diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe1a6fa6..57898b0f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,58 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project somewhat adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The MAJOR version number is bumped when there are **"Breaking Changes"** in the pret projects. For more on this, see [the manual page on breaking changes](https://huderlem.github.io/porymap/manual/breaking-changes.html).
## [Unreleased]
-Nothing, yet.
+### Added
+- Add `View > Show Unused Colors` to the Palette Editor.
+- Add `Tools > Find Color Usage` to the Palette Editor. This opens a dialog showing which metatiles use a particular color.
+- Add `Edit > Swap Metatiles` to the Tileset Editor. While in this mode, selecting two metatiles in the selector will swap their positions. When changes to the tilesets are saved these relocations will be applied to all layouts that use the relevant tileset(s).
+- Add `View > Layer Arrangement` to the Tileset Editor, which changes whether the metatile layer view is oriented vertically (default) or horizontally.
+- Add an `Export Metatiles Image` option to the Tileset Editor that provides many more options for customizing metatile images.
+- Add an `Export Porytiles Layer Images` option to the Tileset Editor, which is a shortcut for individually exporting layer images that Porytiles can use.
+- Add an option under `Preferences` to include common scripts in the autocomplete for Script labels.
+- Add a setting under `Project Settings` to change the width of the metatile selectors.
+- Add versions of the API functions `[get|set]MetatileLayerOrder` and `[get|set]MetatileLayerOpacity` that work globally, rather than on individual layouts.
+- A link to Porymap's manual is now available under `Help`.
+
+### Changed
+- The Player View Rectangle is now visible on the Events tab, as is the Cursor Tile Outline for certain tools.
+- When hovering over tiles in the Tileset Editor their palette and x/yflip are now listed alongside the tile ID.
+- The scroll position of the map view now remains the same between the Connections tab and the Map/Events tabs.
+- The Move tool now behaves more like a traditional pan tool (with no momentum).
+- The map image exporter now uses a checkered background to indicate transparency.
+- Invalid tile IDs are now rendered as magenta (like invalid metatiles), instead of rendering the same as a transparent tile.
+- While holding down `Ctrl` (`Cmd` on macOS) painting on the metatile layer view will now only change the tile's palette.
+- Full menu paths are now listed for shortcuts in the Shortcuts Editor.
+- Adding new event data to a map that has a `shared_events_map` will now remove the `shared_events_map`, rather than discard the event data.
+
+### Fixed
+- Fix crash when rendering tiles with invalid palette numbers.
+- Fix crash when opening the Tileset Editor for tilesets with no metatiles.
+- Fix crash when changing the map/border size in certain API callbacks.
+- Fix metatile images exporting at 2x scale.
+- Fix display errors when a project's metatile limits are not divisible by 8.
+- Fix incorrect dividing line position for primary tiles images that are smaller than the maximum size.
+- Fix the checkered background of the `Change Dimensions` popup shifting while scrolling around.
+- Fix pasting Wild Pokémon data then changing maps resetting the pasted data.
+- Fix click-drag map selections behaving unexpectedly when the cursor is outside the map grid.
+- Fix events being dragged in negative coordinates lagging behind the cursor.
+- Fix the shortcut for duplicating events working while on the Connections tab.
+- Fix the Shortcuts Editor displaying the duplicate shortcut prompt repeatedly.
+- Fix the clear text button on the left in each row of the Shortcuts Editor also clearing the shortcut on the right.
+- Fix Undo/Redo ignoring the automatic resizing that occurs if a layout/border was an unexpected size.
+- Fix Undo/Redo in the Tileset and Palette Editors and Paste in the Tileset Editor appearing enabled even when they don't do anything.
+- Fix `Ctrl+Shift+Z` not being set as a default shortcut for Redo in the Palette Editor like it is for other windows.
+- Fix the Tileset Editor's status bar not updating while selecting tiles in the metatile layer view, or when pasting metatiles.
+- Fix the main window's status bar not immediately reflecting changes made while painting metatiles / movement permissions.
+- Fix cleared metatile labels not updating until the project is reloaded.
+- Fix some changes in the Tileset Editor being discarded if the window is closed too quickly.
+- Fix the Region Map Editor incorrectly displaying whether a `MAPSEC` has region map data.
+- Fix the Primary/Secondary Tileset selectors allowing invalid text, and considering a map unsaved if changed to invalid text then back again.
+- Fix broken error message for the primary tileset on the new map/layout dialogs.
+- Fix the dialog for duplicating/importing a map layout not allowing the tilesets to be changed.
+- Fix warning not appearing when the log file exceeds maximum size.
+- Fix possible lag while using the Tileset Editor's tile selector.
+- Fix unnecessary resources being used to watch files.
+- Fix possible crash on Linux if too many inotify instances are requested.
## [6.1.0] - 2025-06-09
### Added
diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst
index 0651f7d9..e18bd8e3 100644
--- a/docsrc/manual/scripting-capabilities.rst
+++ b/docsrc/manual/scripting-capabilities.rst
@@ -11,6 +11,9 @@ Porymap is extensible via scripting capabilities. This allows the user to write
- Procedurally Generated Maps
- Randomize Grass Patterns
+.. note::
+ If you are compiling Porymap yourself, these features will only be available if Qt's ``qml`` module is installed.
+
Custom Scripts Editor
---------------------
@@ -1222,6 +1225,58 @@ All tileset functions are callable via the global ``map`` object.
:returns: the pixel data
:rtype: array
+.. |describe-metatile-layer-order|
+ replace:: where ``0`` is the bottom layer, ``1`` is the middle layer, and ``2`` is the top layer. The default order is ``[0, 1, 2]``
+
+.. |describe-metatile-layer-order-handling|
+ replace:: If no elements are provided the layer order will be reset to the default. Any layer not listed in the provided ``order`` will not be rendered. Any additional elements after the first 3 are ignored
+
+.. js:function:: map.getMetatileLayerOrder()
+
+ Gets the order that metatile layers are rendered for the current layout, |describe-metatile-layer-order|.
+
+ If you'd like to get the default metatile layer order for all layouts, see :js:func:`utility.getMetatileLayerOrder` instead.
+
+ :returns: array of layers
+ :rtype: array
+
+.. js:function:: map.setMetatileLayerOrder(order)
+
+ Sets the order that metatile layers are rendered for the current layout, |describe-metatile-layer-order|.
+
+ |describe-metatile-layer-order-handling|.
+
+ If you'd like to set the default metatile layer order for all layouts, see :js:func:`utility.setMetatileLayerOrder` instead.
+
+ :param order: array of layers
+ :type order: array
+
+.. |describe-metatile-layer-opacity|
+ replace:: where the first element is the bottom layer, the second element is the middle layer, and the third element is the top layer. The default opacities are ``[1.0, 1.0, 1.0]``
+
+.. |describe-metatile-layer-opacity-handling|
+ replace:: Any additional elements after the first 3 are ignored. Any elements not provided will be rendered with opacity ``1.0``
+
+.. js:function:: map.getMetatileLayerOpacity()
+
+ Gets the opacities that metatile layers are rendered with for the current layout, |describe-metatile-layer-opacity|.
+
+ If you'd like to get the default metatile layer opacities for all layouts, see :js:func:`utility.getMetatileLayerOpacity` instead.
+
+ :returns: array of opacities for each layer
+ :rtype: array
+
+.. js:function:: map.setMetatileLayerOpacity(opacities)
+
+ Sets the opacities that metatile layers are rendered with for the current layout, |describe-metatile-layer-opacity|.
+
+ |describe-metatile-layer-opacity-handling|.
+
+ If you'd like to set the default metatile layer opacities for all layouts, see :js:func:`utility.setMetatileLayerOpacity` instead.
+
+ :param opacities: array of opacities for each layer
+ :type opacities: array
+
Overlay Functions
^^^^^^^^^^^^^^^^^
@@ -1811,30 +1866,42 @@ All settings functions are callable via the global ``utility`` object.
.. js:function:: utility.getMetatileLayerOrder()
- Gets the order that metatile layers are rendered.
+ Gets the order that metatile layers are rendered by default, |describe-metatile-layer-order|.
- :returns: array of layers. The bottom layer is represented as 0.
+ If you'd like to get the metatile layer order for only the current layout, see :js:func:`map.getMetatileLayerOrder` instead.
+
+ :returns: array of layers
:rtype: array
.. js:function:: utility.setMetatileLayerOrder(order)
- Sets the order that metatile layers are rendered.
+ Sets the order that metatile layers are rendered by default, |describe-metatile-layer-order|.
- :param order: array of layers. The bottom layer is represented as 0.
+ |describe-metatile-layer-order-handling|.
+
+ If you'd like to set the metatile layer order for only the current layout, see :js:func:`map.setMetatileLayerOrder` instead.
+
+ :param order: array of layers
:type order: array
.. js:function:: utility.getMetatileLayerOpacity()
- Gets the opacities that metatile layers are rendered with.
+ Gets the opacities that metatile layers are rendered with by default, |describe-metatile-layer-opacity|.
- :returns: array of opacities for each layer. The bottom layer is the first element.
+ If you'd like to get the metatile layer opacities for only the current layout, see :js:func:`map.getMetatileLayerOpacity` instead.
+
+ :returns: array of opacities for each layer
:rtype: array
.. js:function:: utility.setMetatileLayerOpacity(opacities)
- Sets the opacities that metatile layers are rendered with.
+ Sets the opacities that metatile layers are rendered with by default, |describe-metatile-layer-opacity|.
- :param opacities: array of opacities for each layer. The bottom layer is the first element.
+ |describe-metatile-layer-opacity-handling|.
+
+ If you'd like to set the metatile layer opacities for only the current layout, see :js:func:`map.setMetatileLayerOpacity` instead.
+
+ :param opacities: array of opacities for each layer
:type opacities: array
diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui
index 996dff3b..96567305 100644
--- a/forms/mainwindow.ui
+++ b/forms/mainwindow.ui
@@ -1638,9 +1638,6 @@
-
-
- There are no events on the current map.
-
Qt::AlignmentFlag::AlignCenter
@@ -2926,6 +2923,7 @@
Help
+
@@ -3267,6 +3265,11 @@
Alt+Right
+
+
+ Open Manual
+
+
diff --git a/forms/metatileimageexporter.ui b/forms/metatileimageexporter.ui
new file mode 100644
index 00000000..7c6bfe49
--- /dev/null
+++ b/forms/metatileimageexporter.ui
@@ -0,0 +1,526 @@
+
+
+ MetatileImageExporter
+
+
+
+ 0
+ 0
+ 649
+ 601
+
+
+
+ Export Metatiles Image
+
+
+ true
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Qt::FocusPolicy::ClickFocus
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ QFrame::Shape::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 304
+ 532
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Tilesets
+
+
+
+ 6
+
+
+ 6
+
+
-
+
+
+ <html><head/><body><p>If checked, automatically update the metatile range to include the full secondary tileset.</p></body></html>
+
+
+ Secondary Tileset
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+
+ -
+
+
+ <html><head/><body><p>If checked, automatically update the metatile range to include the full primary tileset.</p></body></html>
+
+
+ Primary Tileset
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 1
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ Metatile Range
+
+
+
+ 6
+
+
+ 6
+
+
-
+
+
+ Start
+
+
+
+ -
+
+
+ <html><head/><body><p>The metatile ID to start the rendered image at.</p></body></html>
+
+
+
+ -
+
+
+ End
+
+
+
+ -
+
+
+ <html><head/><body><p>The metatile ID to end the rendered image at.</p></body></html>
+
+
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Each metatile consists of 3 layers of tiles. These layers can be toggled here by clicking the checkbox, or rearranged by clicking and dragging them up or down in the list.</p></body></html>
+
+
+ Layers
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
+
+
+ QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow
+
+
+ true
+
+
+ QAbstractItemView::DragDropMode::InternalMove
+
+
+ Qt::DropAction::MoveAction
+
+
+ QListView::ResizeMode::Adjust
+
+
+ Qt::AlignmentFlag::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Transparency
+
+
+
-
+
+
+ <html><head/><body><p>If checked, transparent pixels in the image will be rendered with alpha of 0.</p></body></html>
+
+
+ Normal
+
+
+
+ -
+
+
+ <html><head/><body><p>If checked, transparent pixels in the image will be rendered as black. This is the default in-game behavior.</p></body></html>
+
+
+ Black
+
+
+
+ -
+
+
+ <html><head/><body><p>If checked, transparent pixels in the image will be rendered using the first color in tileset palette 0. This is the default behavior of the GBA.</p></body></html>
+
+
+ First palette color
+
+
+
+
+
+
+ -
+
+
+ Miscellaneous
+
+
+
+ 6
+
+
+ 6
+
+
-
+
+
+ <html><head/><body><p>If checked, display the placeholder tiles that are rendered for the unused layer in-game. For a given metatile only 2 of the 3 tile layers are used, and the 3rd layer is filled with these placeholder tiles. The unused layer and placeholder tile change depending on the metatile's layer type.</p></body></html>
+
+
+ Render placeholder metatiles
+
+
+
+ -
+
+
+ Width (metatiles)
+
+
+
+ -
+
+
+ <html><head/><body><p>Width of the output image in metatiles.</p></body></html>
+
+
+
+ -
+
+
+ Width (pixels)
+
+
+
+ -
+
+
+ <html><head/><body><p>Width of the output image in pixels. Automatically rounded up to a multiple of a metatile's pixel width.</p></body></html>
+
+
+ 16
+
+
+ 128
+
+
+ 16
+
+
+ 128
+
+
+
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Reset
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Close
+
+
+ false
+
+
+
+ -
+
+
+ Save
+
+
+ false
+
+
+
+
+
+
+
+
+ -
+
+
+ Preview
+
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
-
+
+
+ QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents
+
+
+ true
+
+
+ Qt::AlignmentFlag::AlignCenter
+
+
+
+
+ 0
+ 0
+ 285
+ 551
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ false
+
+
+ false
+
+
+ QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored
+
+
+ QGraphicsView::DragMode::NoDrag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NoScrollComboBox
+ QComboBox
+
+
+
+ UIntSpinBox
+ QAbstractSpinBox
+
+
+
+ UIntHexSpinBox
+ UIntSpinBox
+
+
+
+ ReorderableListWidget
+ QListWidget
+
+
+
+
+
+
diff --git a/forms/palettecolorsearch.ui b/forms/palettecolorsearch.ui
new file mode 100644
index 00000000..b6ae87c8
--- /dev/null
+++ b/forms/palettecolorsearch.ui
@@ -0,0 +1,152 @@
+
+
+ PaletteColorSearch
+
+
+
+ 0
+ 0
+ 547
+ 329
+
+
+
+ Palette Color Search
+
+
+ -
+
+
+
+
+
+ Qt::AlignmentFlag::AlignCenter
+
+
+
+ -
+
+
+ QFrame::Shape::NoFrame
+
+
+ QFrame::Shadow::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+ QFrame::Shadow::Raised
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Color
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Palette
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::StandardButton::Close
+
+
+
+
+
+
+
+ NoScrollSpinBox
+ QSpinBox
+
+
+
+
+
+
diff --git a/forms/paletteeditor.ui b/forms/paletteeditor.ui
index c98d49b6..76374514 100644
--- a/forms/paletteeditor.ui
+++ b/forms/paletteeditor.ui
@@ -38,6 +38,27 @@
+ -
+
+
+ <html><head/><body><p>Opens a search dialog to find which tilesets/metatiles are using certain colors.</p></body></html>
+
+
+ ...
+
+
+
+ :/icons/magnifier.ico:/icons/magnifier.ico
+
+
+
+ -
+
+
+ (All colors used)
+
+
+
-
@@ -89,7 +110,7 @@
0
0
883
- 784
+ 779
@@ -133,11 +154,28 @@
+
+
+
+
+
+ false
+
Undo
@@ -152,6 +190,9 @@
+
+ false
+
Redo
@@ -164,7 +205,22 @@
Import Palette
+
+
+ true
+
+
+ Show Unused Colors
+
+
+
+
+ Find Color Usage...
+
+
-
+
+
+
diff --git a/forms/preferenceeditor.ui b/forms/preferenceeditor.ui
index d3ec7cea..63017212 100644
--- a/forms/preferenceeditor.ui
+++ b/forms/preferenceeditor.ui
@@ -104,7 +104,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
@@ -159,7 +159,7 @@
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -209,7 +209,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
@@ -253,13 +253,42 @@
-
-
-
- <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. Enabling this setting will make Porymap's startup slower.</p></body></html>
-
-
- Autocomplete Script labels using all possible scripts
+
+
+ Script label autocomplete
+
+
-
+
+
+ <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. This is the slowest option for Porymap's project opening.</p></body></html>
+
+
+ All possible scripts
+
+
+
+ -
+
+
+ <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include script labels from the current map's scripts file, scripts in-use by the map's other events, and all script files in the <span style=" font-family:'SFMono-Regular','Menlo','Monaco','Consolas','Liberation Mono','Courier New','Courier','monospace'; font-size:11px; color:#e74c3c; background-color:#ffffff;">data_scripts_folders </span>folder.</p></body></html>
+
+
+ Current map, and global script files
+
+
+
+ -
+
+
+ <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will only include script labels from the current map's scripts file and scripts in-use by the map's other events. This is the fastest option for Porymap's project opening.</p></body></html>
+
+
+ Current map only
+
+
+
+
-
@@ -294,7 +323,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
@@ -323,7 +352,7 @@
-
- QFrame::NoFrame
+ QFrame::Shape::NoFrame
true
@@ -333,13 +362,13 @@
0
0
- 476
- 343
+ 495
+ 376
- QLayout::SetMinimumSize
+ QLayout::SizeConstraint::SetMinimumSize
-
@@ -347,7 +376,7 @@
<html><head/><body><p>When this command is set a button will appear next to the <span style=" font-weight:600; font-style:italic;">Script</span> combo-box in the <span style=" font-weight:600; font-style:italic;">Events</span> tab which executes this command.<span style=" font-weight:600;"> %F</span> will be substituted with the file path of the script and <span style=" font-weight:600;">%L</span> will be substituted with the line number of the script in that file. <span style=" font-weight:600;">%F </span><span style=" font-style:italic;">must</span> be given if <span style=" font-weight:600;">%L</span> is given. If <span style=" font-weight:600;">%F</span> is <span style=" font-style:italic;">not</span> given then the script's file path will be added to the end of the command. If the script can't be found then the current map's scripts file is opened.</p></body></html>
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+ Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop
true
@@ -380,7 +409,7 @@
<html><head/><body><p>This is the command that is executed when clicking <span style=" font-weight:600; font-style:italic;">Open Project in Text Editor</span> in the <span style=" font-weight:600; font-style:italic;">Tools</span> menu. <span style=" font-weight:600;">%D</span> will be substituted with the project's root directory. If <span style=" font-weight:600;">%D</span> is <span style=" font-style:italic;">not</span> specified then the project directory will be added to the end of the command.</p></body></html>
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+ Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop
true
@@ -410,10 +439,10 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
- QSizePolicy::Fixed
+ QSizePolicy::Policy::Fixed
@@ -426,7 +455,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
@@ -447,7 +476,7 @@
-
- QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+ QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok
diff --git a/forms/projectsettingseditor.ui b/forms/projectsettingseditor.ui
index f732f335..f2284c07 100644
--- a/forms/projectsettingseditor.ui
+++ b/forms/projectsettingseditor.ui
@@ -852,7 +852,7 @@
0
0
570
- 798
+ 837
@@ -973,6 +973,27 @@
+ -
+
+
-
+
+
+ Metatile Selector Width
+
+
+
+ -
+
+
+ <html><head/><body><p>The width (in metatiles) of the metatile selectors on the Map tab and in the Tileset Editor.</p></body></html>
+
+
+ 1
+
+
+
+
+
-
@@ -1873,16 +1894,6 @@
QComboBox
-
- NoScrollSpinBox
- QSpinBox
-
-
-
- NoScrollTextEdit
- QTextEdit
-
-
UIntSpinBox
QAbstractSpinBox
@@ -1893,6 +1904,16 @@
UIntSpinBox
+
+ NoScrollSpinBox
+ QSpinBox
+
+
+
+ NoScrollTextEdit
+ QTextEdit
+
+
diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui
index d1817859..eb2580b7 100644
--- a/forms/tileseteditor.ui
+++ b/forms/tileseteditor.ui
@@ -6,7 +6,7 @@
0
0
- 733
+ 748
784
@@ -65,7 +65,7 @@
0
0
- 239
+ 241
659
@@ -158,7 +158,7 @@
0
- 166
+ 190
@@ -194,177 +194,291 @@
false
-
- -
-
-
- true
-
-
-
- -
-
+
+
-
+
-
+
0
0
-
-
- 185
- 0
-
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ Raw Attributes Value
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
- -
-
-
- Metatile Label (Optional)
-
-
-
- -
-
-
- Bottom/Top
-
-
-
- -
-
-
- Terrain Type
-
-
-
- -
-
-
- Layer Type
-
-
-
- -
-
+
-
+
-
+
0
0
-
- QComboBox::InsertPolicy::NoInsert
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Layer Type
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 185
+ 0
+
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
- -
-
+
-
+
-
+
0
0
-
-
- 66
- 34
-
-
-
-
- 96
- 34
-
-
-
- Qt::ScrollBarPolicy::ScrollBarAlwaysOff
-
-
- Qt::ScrollBarPolicy::ScrollBarAlwaysOff
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ <html><head/><body><p>Copies the full metatile label to the clipboard.</p></body></html>
+
+
+ ...
+
+
+
+ :/icons/clipboard.ico:/icons/clipboard.ico
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Metatile Label (Optional)
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
- -
-
+
-
+
-
+
0
0
-
- QComboBox::InsertPolicy::NoInsert
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ Terrain Type
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QComboBox::InsertPolicy::NoInsert
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
- -
-
-
- Qt::Orientation::Horizontal
-
-
- QSizePolicy::Policy::Maximum
-
-
-
- 10
- 20
-
-
-
-
- -
-
+
-
+
-
+
0
0
-
- QComboBox::InsertPolicy::NoInsert
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ Encounter Type
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QComboBox::InsertPolicy::NoInsert
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
- -
-
-
- Metatile Behavior
-
-
-
- -
-
-
- Raw Attributes Value
-
-
-
- -
-
-
- Encounter Type
-
-
-
- -
-
-
- <html><head/><body><p>Copies the full metatile label to the clipboard.</p></body></html>
-
-
- ...
-
-
-
- :/icons/clipboard.ico:/icons/clipboard.ico
-
-
-
- -
+
-
Qt::Orientation::Vertical
@@ -377,8 +491,119 @@
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ Metatile Behavior
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QComboBox::InsertPolicy::NoInsert
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Bottom/Top
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 1
+ 1
+
+
+
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
+
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
+
@@ -457,12 +682,6 @@
-
-
-
- 0
- 0
-
-
98
@@ -503,16 +722,22 @@
-
+
+
+ 0
+ 0
+
+
- 18
- 18
+ 98
+ 98
98
- 34
+ 98
@@ -526,22 +751,22 @@
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
- -
-
-
- Qt::Orientation::Vertical
-
-
-
- 20
- 10
-
-
-
-
@@ -561,8 +786,8 @@
0
0
- 445
- 237
+ 458
+ 203
@@ -623,7 +848,7 @@
0
0
- 733
+ 748
37
@@ -631,18 +856,42 @@
File
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
diff --git a/include/config.h b/include/config.h
index 171e3850..1f2a1a14 100644
--- a/include/config.h
+++ b/include/config.h
@@ -27,6 +27,12 @@ extern const QVersionNumber porymapVersion;
#define CONFIG_BACKWARDS_COMPATABILITY
+enum ScriptAutocompleteMode {
+ MapOnly,
+ MapAndCommon,
+ All,
+};
+
class KeyValueConfigBase
{
public:
@@ -54,7 +60,8 @@ protected:
static bool getConfigBool(const QString &key, const QString &value);
static int getConfigInteger(const QString &key, const QString &value, int min = INT_MIN, int max = INT_MAX, int defaultValue = 0);
static uint32_t getConfigUint32(const QString &key, const QString &value, uint32_t min = 0, uint32_t max = UINT_MAX, uint32_t defaultValue = 0);
- static QColor getConfigColor(const QString &key, const QString &value, const QColor &defaultValue = Qt::black);
+ static QColor getConfigColor(const QString &key, const QString &value, const QColor &defaultValue = QColor(Qt::black));
+ static QString toConfigColor(const QColor &color);
QString m_root;
QString m_filename;
@@ -65,57 +72,7 @@ class PorymapConfig: public KeyValueConfigBase
{
public:
PorymapConfig();
- virtual void reset() override {
- setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
- this->recentProjects.clear();
- this->projectManuallyClosed = false;
- this->reopenOnLaunch = true;
- this->mapListTab = 0;
- this->mapListEditGroupsEnabled = false;
- this->mapListHideEmptyEnabled.clear();
- this->prettyCursors = true;
- this->mirrorConnectingMaps = true;
- this->showDiveEmergeMaps = false;
- this->diveEmergeMapOpacity = 30;
- this->diveMapOpacity = 15;
- this->emergeMapOpacity = 15;
- this->collisionOpacity = 50;
- this->collisionZoom = 30;
- this->metatilesZoom = 30;
- this->tilesetEditorMetatilesZoom = 30;
- this->tilesetEditorTilesZoom = 30;
- this->showPlayerView = false;
- this->showCursorTile = true;
- this->showBorder = true;
- this->showGrid = false;
- this->showTilesetEditorMetatileGrid = false;
- this->showTilesetEditorLayerGrid = true;
- this->showTilesetEditorDivider = false;
- this->showTilesetEditorRawAttributes = false;
- this->monitorFiles = true;
- this->tilesetCheckerboardFill = true;
- this->newMapHeaderSectionExpanded = false;
- this->theme = "default";
- this->wildMonChartTheme = "";
- this->textEditorOpenFolder = "";
- this->textEditorGotoLine = "";
- this->paletteEditorBitDepth = 24;
- this->projectSettingsTab = 0;
- this->loadAllEventScripts = false;
- this->warpBehaviorWarningDisabled = false;
- this->eventDeleteWarningDisabled = false;
- this->eventOverlayEnabled = false;
- this->checkForUpdates = true;
- this->lastUpdateCheckTime = QDateTime();
- this->lastUpdateCheckVersion = porymapVersion;
- this->rateLimitTimes.clear();
- this->eventSelectionShapeMode = QGraphicsPixmapItem::MaskShape;
- this->shownInGameReloadMessage = false;
- this->gridSettings = GridSettings();
- this->statusBarLogTypes = { LogType::LOG_ERROR, LogType::LOG_WARN };
- this->applicationFont = QFont();
- this->mapListFont = PorymapConfig::defaultMapListFont();
- }
+ virtual void reset() override;
void addRecentProject(QString project);
void setRecentProjects(QStringList projects);
QString getRecentProject();
@@ -151,6 +108,7 @@ public:
int metatilesZoom;
int tilesetEditorMetatilesZoom;
int tilesetEditorTilesZoom;
+ Qt::Orientation tilesetEditorLayerOrientation;
bool showPlayerView;
bool showCursorTile;
bool showBorder;
@@ -159,6 +117,7 @@ public:
bool showTilesetEditorLayerGrid;
bool showTilesetEditorDivider;
bool showTilesetEditorRawAttributes;
+ bool showPaletteEditorUnusedColors;
bool monitorFiles;
bool tilesetCheckerboardFill;
bool newMapHeaderSectionExpanded;
@@ -168,7 +127,7 @@ public:
QString textEditorGotoLine;
int paletteEditorBitDepth;
int projectSettingsTab;
- bool loadAllEventScripts;
+ ScriptAutocompleteMode scriptAutocompleteMode;
bool warpBehaviorWarningDisabled;
bool eventDeleteWarningDisabled;
bool eventOverlayEnabled;
@@ -351,7 +310,7 @@ public:
this->prefabImportPrompted = false;
this->tilesetsHaveCallback = true;
this->tilesetsHaveIsCompressed = true;
- this->setTransparentPixelsBlack = true;
+ this->transparencyColor = QColor(Qt::black);
this->preserveMatchingOnlyData = false;
this->filePaths.clear();
this->eventIconPaths.clear();
@@ -368,6 +327,7 @@ public:
this->unusedTileSplit = 0x0000;
this->maxEventsPerGroup = 255;
this->forcedMajorVersion = 0;
+ this->metatileSelectorWidth = 8;
this->globalConstantsFilepaths.clear();
this->globalConstants.clear();
this->identifiers.clear();
@@ -426,7 +386,7 @@ public:
bool prefabImportPrompted;
bool tilesetsHaveCallback;
bool tilesetsHaveIsCompressed;
- bool setTransparentPixelsBlack;
+ QColor transparencyColor;
bool preserveMatchingOnlyData;
int metatileAttributesSize;
uint32_t metatileBehaviorMask;
@@ -447,6 +407,7 @@ public:
QList warpBehaviors;
int maxEventsPerGroup;
int forcedMajorVersion;
+ int metatileSelectorWidth;
QStringList globalConstantsFilepaths;
QMap globalConstants;
diff --git a/include/core/events.h b/include/core/events.h
index 76a58dcf..816cf953 100644
--- a/include/core/events.h
+++ b/include/core/events.h
@@ -122,8 +122,8 @@ public:
int getZ() const { return this->elevation; }
int getElevation() const { return this->elevation; }
- int getPixelX() const { return (this->x * 16) - qMax(0, (pixmap.width() - 16) / 2); }
- int getPixelY() const { return (this->y * 16) - qMax(0, pixmap.height() - 16); }
+ int getPixelX() const;
+ int getPixelY() const;
virtual EventFrame *getEventFrame();
virtual EventFrame *createEventFrame() = 0;
@@ -161,6 +161,7 @@ public:
QString getIdName() const { return this->idName; }
static QString groupToString(Event::Group group);
+ static QString groupToJsonKey(Event::Group group);
static QString typeToString(Event::Type type);
static QString typeToJsonKey(Event::Type type);
static Event::Type typeFromJsonKey(QString type);
diff --git a/include/core/history.h b/include/core/history.h
index c999a4e1..ced8a590 100644
--- a/include/core/history.h
+++ b/include/core/history.h
@@ -25,6 +25,7 @@ public:
if (head > 0) {
return history.at(--head);
}
+ head = -1;
return NULL;
}
@@ -37,9 +38,7 @@ public:
void push(T commit) {
while (head + 1 < history.length()) {
- T item = history.last();
- history.removeLast();
- delete item;
+ delete history.takeLast();
}
if (saved > head) {
saved = -1;
@@ -48,7 +47,7 @@ public:
head++;
}
- T current() {
+ T current() const {
if (head < 0 || history.length() == 0) {
return NULL;
}
@@ -59,10 +58,30 @@ public:
saved = head;
}
- bool isSaved() {
+ bool isSaved() const {
return saved == head;
}
+ int length() const {
+ return history.length();
+ }
+
+ bool isEmpty() const {
+ return history.isEmpty();
+ }
+
+ int index() const {
+ return head;
+ }
+
+ bool canUndo() const {
+ return head >= 0;
+ }
+
+ bool canRedo() const {
+ return (head + 1) < history.length();
+ }
+
private:
QList history;
int head = -1;
diff --git a/include/core/map.h b/include/core/map.h
index d5e28075..f3a377c1 100644
--- a/include/core/map.h
+++ b/include/core/map.h
@@ -51,10 +51,12 @@ public:
void setLayoutId(const QString &layoutId) { m_layoutId = layoutId; }
QString layoutId() const { return layout() ? layout()->id : m_layoutId; }
- int getWidth() const;
- int getHeight() const;
- int getBorderWidth() const;
- int getBorderHeight() const;
+ int getWidth() const { return m_layout ? m_layout->getWidth() : 0; }
+ int getHeight() const { return m_layout ? m_layout->getHeight() : 0; }
+ int getBorderWidth() const { return m_layout ? m_layout->getBorderWidth() : 0; }
+ int getBorderHeight() const { return m_layout ? m_layout->getBorderHeight() : 0; }
+ int pixelWidth() const { return m_layout ? m_layout->pixelWidth() : 0; }
+ int pixelHeight() const { return m_layout ? m_layout->pixelHeight() : 0; }
void setHeader(const MapHeader &header) { *m_header = header; }
MapHeader* header() const { return m_header; }
@@ -62,6 +64,8 @@ public:
void setSharedEventsMap(const QString &sharedEventsMap) { m_sharedEventsMap = sharedEventsMap; }
void setSharedScriptsMap(const QString &sharedScriptsMap);
+ bool isInheritingEvents() const { return !m_sharedEventsMap.isEmpty() && !hasEvents(); }
+ bool isInheritingScripts() const { return !m_sharedScriptsMap.isEmpty(); }
QString sharedEventsMap() const { return m_sharedEventsMap; }
QString sharedScriptsMap() const { return m_sharedScriptsMap; }
@@ -83,6 +87,7 @@ public:
void addEvent(Event *);
int getIndexOfEvent(Event *) const;
bool hasEvent(Event *) const;
+ bool hasEvents() const;
QStringList getScriptLabels(Event::Group group = Event::Group::None);
QString getScriptsFilepath() const;
@@ -133,9 +138,6 @@ private:
QMap> m_events;
QSet m_ownedEvents; // for memory management
- QList m_metatileLayerOrder;
- QList m_metatileLayerOpacity;
-
void trackConnection(MapConnection*);
// MapConnections in 'ownedConnections' but not 'connections' persist in the edit history.
@@ -149,7 +151,7 @@ signals:
void modified();
void scriptsModified();
void mapDimensionsChanged(const QSize &size);
- void openScriptRequested(QString label);
+ void openScriptRequested(const QString &label);
void connectionAdded(MapConnection*);
void connectionRemoved(MapConnection*);
void layoutChanged();
diff --git a/include/core/mapconnection.h b/include/core/mapconnection.h
index 5df071bd..ba7c95a8 100644
--- a/include/core/mapconnection.h
+++ b/include/core/mapconnection.h
@@ -42,7 +42,7 @@ public:
MapConnection* createMirror();
QPixmap render() const;
- QPoint relativePos(bool clipped = false) const;
+ QPoint relativePixelPos(bool clipped = false) const;
static QPointer project;
static const QMap oppositeDirections;
diff --git a/include/core/maplayout.h b/include/core/maplayout.h
index 5a2e23b3..92f0264e 100644
--- a/include/core/maplayout.h
+++ b/include/core/maplayout.h
@@ -17,7 +17,7 @@ class BorderMetatilesPixmapItem;
class Layout : public QObject {
Q_OBJECT
public:
- Layout() {}
+ Layout() {};
Layout(const Layout &other);
static QString layoutConstantFromName(const QString &name);
@@ -32,6 +32,9 @@ public:
int height;
int border_width;
int border_height;
+ int pixelWidth() const { return this->width * Metatile::pixelWidth(); }
+ int pixelHeight() const { return this->height * Metatile::pixelHeight(); }
+ QSize pixelSize() const { return QSize(pixelWidth(), pixelHeight()); }
QString border_path;
QString blockdata_path;
@@ -64,8 +67,25 @@ public:
QSize borderDimensions;
} lastCommitBlocks; // to track map changes
- QList metatileLayerOrder;
- QList metatileLayerOpacity;
+ void setMetatileLayerOrder(const QList &layerOrder) { m_metatileLayerOrder = layerOrder; }
+ const QList &metatileLayerOrder() const {
+ return !m_metatileLayerOrder.isEmpty() ? m_metatileLayerOrder : Layout::globalMetatileLayerOrder();
+ }
+ static void setGlobalMetatileLayerOrder(const QList &layerOrder) { s_globalMetatileLayerOrder = layerOrder; }
+ static const QList &globalMetatileLayerOrder() {
+ static const QList defaultLayerOrder = {0, 1, 2};
+ return !s_globalMetatileLayerOrder.isEmpty() ? s_globalMetatileLayerOrder : defaultLayerOrder;
+ }
+
+ void setMetatileLayerOpacity(const QList &layerOpacity) { m_metatileLayerOpacity = layerOpacity; }
+ const QList &metatileLayerOpacity() const {
+ return !m_metatileLayerOpacity.isEmpty() ? m_metatileLayerOpacity : Layout::globalMetatileLayerOpacity();
+ }
+ static void setGlobalMetatileLayerOpacity(const QList &layerOpacity) { s_globalMetatileLayerOpacity = layerOpacity; }
+ static const QList &globalMetatileLayerOpacity() {
+ static const QList defaultLayerOpacity = {1.0, 1.0, 1.0};
+ return !s_globalMetatileLayerOpacity.isEmpty() ? s_globalMetatileLayerOpacity : defaultLayerOpacity;
+ }
LayoutPixmapItem *layoutItem = nullptr;
CollisionPixmapItem *collisionItem = nullptr;
@@ -100,13 +120,17 @@ public:
QRect getVisibleRect() const;
bool isWithinBounds(int x, int y) const;
+ bool isWithinBounds(const QPoint &pos) const;
bool isWithinBounds(const QRect &rect) const;
bool isWithinBorderBounds(int x, int y) const;
- bool getBlock(int x, int y, Block *out);
+ bool getBlock(int x, int y, Block *out) const;
void setBlock(int x, int y, Block block, bool enableScriptCallback = false);
void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false);
+ uint16_t getMetatileId(int x, int y) const;
+ bool setMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback = false);
+
void adjustDimensions(const QMargins &margins, bool setNewBlockdata = true);
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true);
void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
@@ -135,9 +159,8 @@ public:
void _floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation);
void magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation);
- QPixmap render(bool ignoreCache = false, Layout *fromLayout = nullptr, QRect bounds = QRect(0, 0, -1, -1));
+ QPixmap render(bool ignoreCache = false, Layout *fromLayout = nullptr, const QRect &bounds = QRect(0, 0, -1, -1));
QPixmap renderCollision(bool ignoreCache);
- // QPixmap renderConnection(MapConnection, Layout *);
QPixmap renderBorder(bool ignoreCache = false);
QPixmap getLayoutItemPixmap();
@@ -146,6 +169,8 @@ public:
void setCollisionItem(CollisionPixmapItem *item) { collisionItem = item; }
void setBorderItem(BorderMetatilesPixmapItem *item) { borderItem = item; }
+ bool metatileIsValid(uint16_t metatileId) { return Tileset::metatileIsValid(metatileId, this->tileset_primary, this->tileset_secondary); }
+
private:
void setNewDimensionsBlockdata(int newWidth, int newHeight);
void setNewBorderDimensionsBlockdata(int newWidth, int newHeight);
@@ -154,6 +179,11 @@ private:
static int getBorderDrawDistance(int dimension, qreal minimum);
+ QList m_metatileLayerOrder;
+ QList m_metatileLayerOpacity;
+ static QList s_globalMetatileLayerOrder;
+ static QList s_globalMetatileLayerOpacity;
+
signals:
void dimensionsChanged(const QSize &size);
void needsRedrawing();
diff --git a/include/core/metatile.h b/include/core/metatile.h
index a1c805d1..eba4afed 100644
--- a/include/core/metatile.h
+++ b/include/core/metatile.h
@@ -61,7 +61,15 @@ public:
static int getDefaultAttributesSize(BaseGameVersion version);
static void setLayout(Project*);
static QString getMetatileIdString(uint16_t metatileId);
- static QString getMetatileIdStrings(const QList metatileIds);
+ static QString getMetatileIdStrings(const QList &metatileIds);
+ static QString getLayerName(int layerNum);
+
+ static constexpr int tileWidth() { return 2; }
+ static constexpr int tileHeight() { return 2; }
+ static constexpr int tilesPerLayer() { return Metatile::tileWidth() * Metatile::tileHeight(); }
+ static constexpr int pixelWidth() { return Metatile::tileWidth() * Tile::pixelWidth(); }
+ static constexpr int pixelHeight() { return Metatile::tileHeight() * Tile::pixelHeight(); }
+ static constexpr QSize pixelSize() { return QSize(pixelWidth(), pixelHeight()); }
inline bool operator==(const Metatile &other) {
return this->tiles == other.tiles && this->attributes == other.attributes;
diff --git a/include/core/network.h b/include/core/network.h
index 321b3e99..23dc7eff 100644
--- a/include/core/network.h
+++ b/include/core/network.h
@@ -26,10 +26,14 @@
});
*/
+#if __has_include()
#include
#include
#include
#include
+#endif
+
+#ifdef QT_NETWORK_LIB
class NetworkReplyData : public QObject
{
@@ -84,4 +88,6 @@ private:
const QNetworkRequest getRequest(const QUrl &url);
};
+#endif // QT_NETWORK_LIB
+
#endif // NETWORK_H
diff --git a/include/core/tile.h b/include/core/tile.h
index 1ae7e23d..11fd6a99 100644
--- a/include/core/tile.h
+++ b/include/core/tile.h
@@ -3,6 +3,7 @@
#define TILE_H
#include
+#include
class Tile
{
@@ -24,6 +25,12 @@ public:
static int getIndexInTileset(int);
static const uint16_t maxValue;
+
+ static constexpr int pixelWidth() { return 8; }
+ static constexpr int pixelHeight() { return 8; }
+ static constexpr QSize pixelSize() { return QSize(Tile::pixelWidth(), Tile::pixelHeight()); }
+ static constexpr int numPixels() { return Tile::pixelWidth() * Tile::pixelHeight(); }
+ static constexpr int sizeInBytes() { return sizeof(uint16_t); }
};
inline bool operator==(const Tile &a, const Tile &b) {
diff --git a/include/core/tileset.h b/include/core/tileset.h
index abb31ab1..dcf55ec1 100644
--- a/include/core/tileset.h
+++ b/include/core/tileset.h
@@ -30,17 +30,21 @@ public:
QString metatile_attrs_label;
QString metatile_attrs_path;
QString tilesImagePath;
- QImage tilesImage;
QStringList palettePaths;
- QList tiles;
QHash metatileLabels;
QList> palettes;
QList> palettePreviews;
+ static QString stripPrefix(const QString &fullName);
+ static Tileset* getPaletteTileset(int, Tileset*, Tileset*);
+ static const Tileset* getPaletteTileset(int, const Tileset*, const Tileset*);
static Tileset* getMetatileTileset(int, Tileset*, Tileset*);
+ static const Tileset* getMetatileTileset(int, const Tileset*, const Tileset*);
static Tileset* getTileTileset(int, Tileset*, Tileset*);
+ static const Tileset* getTileTileset(int, const Tileset*, const Tileset*);
static Metatile* getMetatile(int, Tileset*, Tileset*);
+ static const Metatile* getMetatile(int, const Tileset*, const Tileset*);
static Tileset* getMetatileLabelTileset(int, Tileset*, Tileset*);
static QString getMetatileLabel(int, Tileset *, Tileset *);
static QString getOwnedMetatileLabel(int, Tileset *, Tileset *);
@@ -48,9 +52,9 @@ public:
static bool setMetatileLabel(int, QString, Tileset *, Tileset *);
QString getMetatileLabelPrefix();
static QString getMetatileLabelPrefix(const QString &name);
- static QList> getBlockPalettes(Tileset*, Tileset*, bool useTruePalettes = false);
- static QList getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false);
- static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *);
+ static QList> getBlockPalettes(const Tileset*, const Tileset*, bool useTruePalettes = false);
+ static QList getPalette(int, const Tileset*, const Tileset*, bool useTruePalettes = false);
+ static bool metatileIsValid(uint16_t metatileId, const Tileset*, const Tileset*);
static QHash getHeaderMemberMap(bool usingAsm);
static QString getExpectedDir(QString tilesetName, bool isSecondary);
QString getExpectedDir();
@@ -76,15 +80,38 @@ public:
void setMetatiles(const QList &metatiles);
void addMetatile(Metatile* metatile);
- QList metatiles() const { return m_metatiles; }
- Metatile* metatileAt(unsigned int i) const { return m_metatiles.at(i); }
+ const QList &metatiles() const { return m_metatiles; }
+ const Metatile* metatileAt(unsigned int i) const { return m_metatiles.at(i); }
void clearMetatiles();
void resizeMetatiles(int newNumMetatiles);
int numMetatiles() const { return m_metatiles.length(); }
+ int maxMetatiles() const;
+
+ uint16_t firstMetatileId() const;
+ uint16_t lastMetatileId() const;
+ bool containsMetatileId(uint16_t metatileId) const { return metatileId >= firstMetatileId() && metatileId <= lastMetatileId(); }
+
+ uint16_t firstTileId() const;
+ uint16_t lastTileId() const;
+ bool containsTileId(uint16_t tileId) const { return tileId >= firstTileId() && tileId <= lastTileId(); }
+
+ int numTiles() const { return m_tiles.length(); }
+ int maxTiles() const;
+
+ QImage tileImage(uint16_t tileId) const { return m_tiles.value(Tile::getIndexInTileset(tileId)); }
+
+ QSet getUnusedColorIds(int paletteId, const Tileset *pairedTileset, const QSet &searchColors = {}) const;
+ QList findMetatilesUsingColor(int paletteId, int colorId, const Tileset *pairedTileset) const;
+
+ static constexpr int maxPalettes() { return 16; }
+ static constexpr int numColorsPerPalette() { return 16; }
private:
QList m_metatiles;
+
+ QList m_tiles;
+ QImage m_tilesImage;
bool m_hasUnsavedTilesImage = false;
};
diff --git a/include/core/utility.h b/include/core/utility.h
index f7f5da61..7270b552 100644
--- a/include/core/utility.h
+++ b/include/core/utility.h
@@ -7,7 +7,7 @@
namespace Util {
void numericalModeSort(QStringList &list);
- int roundUp(int numToRound, int multiple);
+ int roundUpToMultiple(int numToRound, int multiple);
QString toDefineCase(QString input);
QString toHexString(uint32_t value, int minLength = 0);
QString toHtmlParagraph(const QString &text);
@@ -16,6 +16,7 @@ namespace Util {
QString replaceExtension(const QString &path, const QString &newExtension);
void setErrorStylesheet(QLineEdit *lineEdit, bool isError);
QString toStylesheetString(const QFont &font);
+ void show(QWidget *w);
}
#endif // UTILITY_H
diff --git a/include/editor.h b/include/editor.h
index a5601196..21368f61 100644
--- a/include/editor.h
+++ b/include/editor.h
@@ -67,8 +67,6 @@ public:
bool setLayout(QString layoutName);
void unsetMap();
- Tileset *getCurrentMapPrimaryTileset();
-
bool displayMap();
bool displayLayout();
@@ -122,9 +120,10 @@ public:
void updateEventPixmapItemZValue(EventPixmapItem *item);
qreal getEventOpacity(const Event *event) const;
+ bool isMouseInMap() const;
void setPlayerViewRect(const QRectF &rect);
- void updateCursorRectPos(int x, int y);
- void setCursorRectVisible(bool visible);
+ void setCursorRectPos(const QPoint &pos);
+ void updateCursorRectVisibility();
void onEventDragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition);
void onEventReleased(Event *event, const QPoint &position);
@@ -171,7 +170,7 @@ public:
void setEditMode(EditMode editMode);
EditMode getEditMode() const { return this->editMode; }
- bool getEditingLayout();
+ bool getEditingLayout() const;
void setMapEditingButtonsEnabled(bool enabled);
@@ -208,7 +207,8 @@ public:
public slots:
void openMapScripts() const;
- void openScript(const QString &scriptLabel) const;
+ bool openScript(const QString &scriptLabel) const;
+ bool openScriptInFile(const QString &scriptLabel, const QString &filepath) const;
void openProjectInTextEditor() const;
void maskNonVisibleConnectionTiles();
void onBorderMetatilesChanged();
@@ -251,26 +251,26 @@ private:
QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
QString getMetatileDisplayMessage(uint16_t metatileId);
void setCollisionTabSpinBoxes(uint16_t collision, uint16_t elevation);
+ void adjustStraightPathPos(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item, QPoint *pos) const;
static bool startDetachedProcess(const QString &command,
const QString &workingDirectory = QString(),
qint64 *pid = nullptr);
-
-private slots:
+ bool canPaintMetatiles() const;
void onMapStartPaint(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item);
void onMapEndPaint(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item);
+ void setStatusFromMapPos(const QPoint &pos);
+
+private slots:
void setSmartPathCursorMode(QGraphicsSceneMouseEvent *event);
- void setStraightPathCursorMode(QGraphicsSceneMouseEvent *event);
void mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item);
- void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item);
void setSelectedConnectionItem(ConnectionPixmapItem *connectionItem);
void onHoveredMovementPermissionChanged(uint16_t, uint16_t);
void onHoveredMovementPermissionCleared();
void onHoveredMetatileSelectionChanged(uint16_t);
void onHoveredMetatileSelectionCleared();
- void onHoveredMapMetatileChanged(const QPoint &pos);
- void onHoveredMapMetatileCleared();
- void onHoveredMapMovementPermissionChanged(int, int);
- void onHoveredMapMovementPermissionCleared();
+ void onMapHoverEntered(const QPoint &pos);
+ void onMapHoverChanged(const QPoint &pos);
+ void onMapHoverCleared();
void onSelectedMetatilesChanged();
void onWheelZoom(int);
@@ -283,7 +283,6 @@ signals:
void wildMonTableEdited();
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
- void tilesetUpdated(QString);
void gridToggled(bool);
void editActionSet(EditAction newEditAction);
};
diff --git a/include/mainwindow.h b/include/mainwindow.h
index 3b3003aa..d15bde31 100644
--- a/include/mainwindow.h
+++ b/include/mainwindow.h
@@ -11,7 +11,6 @@
#include
#include
#include
-#include
#include "project.h"
#include "orderedjson.h"
#include "config.h"
@@ -36,6 +35,10 @@
#include "message.h"
#include "resizelayoutpopup.h"
+#if __has_include()
+#include
+#endif
+
namespace Ui {
@@ -56,9 +59,14 @@ public:
void initialize();
+ Q_INVOKABLE void setPrimaryTileset(const QString &tileset);
+ Q_INVOKABLE void setSecondaryTileset(const QString &tileset);
+
// Scripting API
+#ifdef QT_QML_LIB
Q_INVOKABLE QJSValue getBlock(int x, int y);
void tryRedrawMapArea(bool forceRedraw);
+ void redrawResizedMapArea();
void tryCommitMapChanges(bool commitChanges);
Q_INVOKABLE void setBlock(int x, int y, int metatileId, int collision, int elevation, bool forceRedraw = true, bool commitChanges = true);
Q_INVOKABLE void setBlock(int x, int y, int rawValue, bool forceRedraw = true, bool commitChanges = true);
@@ -118,8 +126,6 @@ public:
Q_INVOKABLE int getNumSecondaryTilesetTiles();
Q_INVOKABLE QString getPrimaryTileset();
Q_INVOKABLE QString getSecondaryTileset();
- Q_INVOKABLE void setPrimaryTileset(QString tileset);
- Q_INVOKABLE void setSecondaryTileset(QString tileset);
void saveMetatilesByMetatileId(int metatileId);
void saveMetatileAttributesByMetatileId(int metatileId);
Metatile * getMetatile(int metatileId);
@@ -145,6 +151,10 @@ public:
Q_INVOKABLE void setMetatileTiles(int metatileId, QJSValue tilesObj, int tileStart = 0, int tileEnd = -1, bool forceRedraw = true);
Q_INVOKABLE void setMetatileTiles(int metatileId, int tileId, bool xflip, bool yflip, int palette, int tileStart = 0, int tileEnd = -1, bool forceRedraw = true);
Q_INVOKABLE QJSValue getTilePixels(int tileId);
+ Q_INVOKABLE QList getMetatileLayerOrder() const;
+ Q_INVOKABLE void setMetatileLayerOrder(const QList &order);
+ Q_INVOKABLE QList getMetatileLayerOpacity() const;
+ Q_INVOKABLE void setMetatileLayerOpacity(const QList &opacities);
Q_INVOKABLE QString getSong();
Q_INVOKABLE void setSong(QString song);
Q_INVOKABLE QString getLocation();
@@ -167,6 +177,7 @@ public:
Q_INVOKABLE void setAllowEscaping(bool allow);
Q_INVOKABLE int getFloorNumber();
Q_INVOKABLE void setFloorNumber(int floorNumber);
+#endif // QT_QML_LIB
public slots:
void on_mainTabBar_tabBarClicked(int index);
@@ -246,8 +257,6 @@ private slots:
void on_pushButton_AddConnection_clicked();
void on_button_OpenDiveMap_clicked();
void on_button_OpenEmergeMap_clicked();
- void on_comboBox_PrimaryTileset_currentTextChanged(const QString &arg1);
- void on_comboBox_SecondaryTileset_currentTextChanged(const QString &arg1);
void on_pushButton_ChangeDimensions_clicked();
void resetMapViewScale();
@@ -286,6 +295,7 @@ private slots:
void on_spinBox_SelectedCollision_valueChanged(int collision);
void on_actionRegion_Map_Editor_triggered();
void on_actionPreferences_triggered();
+ void on_actionOpen_Manual_triggered();
void on_actionCheck_for_Updates_triggered();
void togglePreferenceSpecificUi();
void on_actionProject_Settings_triggered();
@@ -321,8 +331,11 @@ private:
QPointer layoutListProxyModel = nullptr;
QPointer layoutTreeModel = nullptr;
+#ifdef QT_NETWORK_LIB
QPointer updatePromoter = nullptr;
QPointer networkAccessManager = nullptr;
+#endif
+
QPointer aboutWindow = nullptr;
QPointer wildMonChart = nullptr;
QPointer wildMonSearch = nullptr;
@@ -380,7 +393,6 @@ private:
void openDuplicateMapOrLayoutDialog();
void openNewMapGroupDialog();
void openNewLocationDialog();
- void openSubWindow(QWidget * window);
void scrollMapList(MapTree *list, const QString &itemName);
void scrollMapListToCurrentMap(MapTree *list);
void scrollMapListToCurrentLayout(MapTree *list);
diff --git a/include/project.h b/include/project.h
index 5c519bc3..0ea25a4a 100644
--- a/include/project.h
+++ b/include/project.h
@@ -61,7 +61,6 @@ public:
QMap metatileBehaviorMap;
QMap metatileBehaviorMapInverse;
ParseUtil parser;
- QFileSystemWatcher fileWatcher;
QSet modifiedFiles;
bool usingAsmTilesets;
QSet disabledSettingsNames;
@@ -105,11 +104,12 @@ public:
bool load();
QMap tilesetCache;
- Tileset* loadTileset(QString, Tileset *tileset = nullptr);
- Tileset* getTileset(QString, bool forceLoad = false);
+ Tileset* getTileset(const QString&, bool forceLoad = false);
QStringList primaryTilesetLabels;
QStringList secondaryTilesetLabels;
QStringList tilesetLabelsOrdered;
+ QSet getPairedTilesetLabels(const Tileset *tileset) const;
+ QSet getTilesetLayoutIds(const Tileset *priamryTileset, const Tileset *secondaryTileset) const;
bool readMapGroups();
void addNewMapGroup(const QString &groupName);
@@ -216,7 +216,11 @@ public:
static QString getScriptFileExtension(bool usePoryScript);
QString getScriptDefaultString(bool usePoryScript, QString mapName) const;
- QStringList getEventScriptsFilepaths() const;
+
+ QStringList getAllEventScriptsFilepaths() const;
+ QStringList getMapScriptsFilepaths() const;
+ QStringList getCommonEventScriptsFilepaths() const;
+ QStringList findScriptsFiles(const QString &searchDir, const QStringList &fileNames = {"*"}) const;
void insertGlobalScriptLabels(QStringList &scriptLabels) const;
QString getDefaultPrimaryTilesetLabel() const;
@@ -252,17 +256,22 @@ public:
static QString getDynamicMapDefineName();
static QString getDynamicMapName();
static QString getEmptySpeciesName();
+ static QMargins getPixelViewDistance();
static QMargins getMetatileViewDistance();
static int getNumTilesPrimary() { return num_tiles_primary; }
static int getNumTilesTotal() { return num_tiles_total; }
+ static int getNumTilesSecondary() { return getNumTilesTotal() - getNumTilesPrimary(); }
static int getNumMetatilesPrimary() { return num_metatiles_primary; }
static int getNumMetatilesTotal() { return Block::getMaxMetatileId() + 1; }
+ static int getNumMetatilesSecondary() { return getNumMetatilesTotal() - getNumMetatilesPrimary(); }
static int getNumPalettesPrimary(){ return num_pals_primary; }
static int getNumPalettesTotal() { return num_pals_total; }
+ static int getNumPalettesSecondary() { return getNumPalettesTotal() - getNumPalettesPrimary(); }
static QString getEmptyMapsecName();
static QString getMapGroupPrefix();
private:
+ QPointer fileWatcher;
QMap modifiedFileTimestamps;
QMap facingDirections;
QHash speciesToIconPath;
@@ -332,6 +341,9 @@ private:
void ignoreWatchedFilesTemporarily(const QStringList &filepaths);
void recordFileChange(const QString &filepath);
void resetFileCache();
+ void resetFileWatcher();
+ void logFileWatchStatus();
+ void cacheTileset(const QString &label, Tileset *tileset);
bool saveMapLayouts();
bool saveMapGroups();
diff --git a/include/scripting.h b/include/scripting.h
index b85e9e26..b700df8f 100644
--- a/include/scripting.h
+++ b/include/scripting.h
@@ -2,12 +2,19 @@
#ifndef SCRIPTING_H
#define SCRIPTING_H
-#include "mainwindow.h"
-#include "block.h"
+#include
#include "scriptutility.h"
-#include
+class Block;
+class Tile;
+class MainWindow;
+
+#if __has_include()
#include
+#endif
+
+
+#ifdef QT_QML_LIB
// !! New callback functions or changes to existing callback function names/arguments
// should be synced to resources/text/script_template.txt and docsrc/manual/scripting-capabilities.rst
@@ -51,7 +58,7 @@ public:
static void cb_MapResized(int oldWidth, int oldHeight, const QMargins &delta);
static void cb_BorderResized(int oldWidth, int oldHeight, int newWidth, int newHeight);
static void cb_MapShifted(int xDelta, int yDelta);
- static void cb_TilesetUpdated(QString tilesetName);
+ static void cb_TilesetUpdated(const QString &tilesetName);
static void cb_MainTabChanged(int oldTab, int newTab);
static void cb_MapViewTabChanged(int oldTab, int newTab);
static void cb_BorderVisibilityToggled(bool visible);
@@ -78,4 +85,34 @@ private:
void invokeCallback(CallbackType type, QJSValueList args);
};
+#else
+
+class Scripting
+{
+public:
+ Scripting(MainWindow *) {}
+ ~Scripting() {}
+ static void init(MainWindow *) {}
+ static void stop() {}
+ static void populateGlobalObject(MainWindow *) {}
+
+ static void cb_ProjectOpened(QString) {};
+ static void cb_ProjectClosed(QString) {};
+ static void cb_MetatileChanged(int, int, Block, Block) {};
+ static void cb_BorderMetatileChanged(int, int, uint16_t, uint16_t) {};
+ static void cb_BlockHoverChanged(int, int) {};
+ static void cb_BlockHoverCleared() {};
+ static void cb_MapOpened(QString) {};
+ static void cb_LayoutOpened(QString) {};
+ static void cb_MapResized(int, int, const QMargins &) {};
+ static void cb_BorderResized(int, int, int, int) {};
+ static void cb_MapShifted(int, int) {};
+ static void cb_TilesetUpdated(const QString &) {};
+ static void cb_MainTabChanged(int, int) {};
+ static void cb_MapViewTabChanged(int, int) {};
+ static void cb_BorderVisibilityToggled(bool) {};
+};
+
+#endif // QT_QML_LIB
+
#endif // SCRIPTING_H
diff --git a/include/scriptutility.h b/include/scriptutility.h
index 78272eec..ae60d316 100644
--- a/include/scriptutility.h
+++ b/include/scriptutility.h
@@ -2,14 +2,24 @@
#ifndef SCRIPTUTILITY_H
#define SCRIPTUTILITY_H
-#include "mainwindow.h"
+#if __has_include()
+#include
+#endif
+
+#ifdef QT_QML_LIB
+
+#include
+#include
+#include
+#include
+
+class MainWindow;
class ScriptUtility : public QObject
{
Q_OBJECT
-
public:
- ScriptUtility(MainWindow *mainWindow);
+ ScriptUtility(MainWindow *mainWindow) : window(mainWindow) {}
~ScriptUtility();
QString getActionFunctionName(int actionIndex);
@@ -38,9 +48,9 @@ public:
Q_INVOKABLE bool getSmartPathsEnabled();
Q_INVOKABLE QList getCustomScripts();
Q_INVOKABLE QList getMetatileLayerOrder();
- Q_INVOKABLE void setMetatileLayerOrder(QList order);
+ Q_INVOKABLE void setMetatileLayerOrder(const QList &order);
Q_INVOKABLE QList getMetatileLayerOpacity();
- Q_INVOKABLE void setMetatileLayerOpacity(QList order);
+ Q_INVOKABLE void setMetatileLayerOpacity(const QList &order);
Q_INVOKABLE QList getMapNames();
Q_INVOKABLE QList getMapConstants();
Q_INVOKABLE QList getLayoutNames();
@@ -57,6 +67,8 @@ public:
Q_INVOKABLE bool isPrimaryTileset(QString tilesetName);
Q_INVOKABLE bool isSecondaryTileset(QString tilesetName);
+ static bool validateMetatileLayerOrder(const QList &order);
+
private:
void callTimeoutFunction(QJSValue callback);
void runMessageBox(QString text, QString informativeText, QString detailedText, QMessageBox::Icon icon);
@@ -67,4 +79,6 @@ private:
QHash actionMap;
};
+#endif // QT_QML_LIB
+
#endif // SCRIPTUTILITY_H
diff --git a/include/settings.h b/include/settings.h
index d37283cd..6e64fd2a 100644
--- a/include/settings.h
+++ b/include/settings.h
@@ -10,7 +10,6 @@ public:
Settings();
bool smartPathsEnabled;
bool betterCursors;
- QCursor mapCursor;
bool playerViewRectEnabled;
bool cursorTileRectEnabled;
};
diff --git a/include/ui/checkeredbgscene.h b/include/ui/checkeredbgscene.h
new file mode 100644
index 00000000..bf0452a7
--- /dev/null
+++ b/include/ui/checkeredbgscene.h
@@ -0,0 +1,39 @@
+#ifndef CHECKEREDBGSCENE_H
+#define CHECKEREDBGSCENE_H
+
+#include
+
+// Custom scene that paints its background a gray checkered pattern.
+// Additionally there is a definable "valid" area which will paint the checkerboard green inside.
+class CheckeredBgScene : public QGraphicsScene {
+ Q_OBJECT
+
+public:
+ CheckeredBgScene(const QSize &gridSize, QObject *parent = nullptr)
+ : QGraphicsScene(parent),
+ gridSize(gridSize)
+ {};
+ CheckeredBgScene(int width, int height, QObject *parent = nullptr)
+ : CheckeredBgScene(QSize(width, height), parent)
+ {};
+
+ void setValidRect(int x, int y, int width, int height) {
+ this->validRect = QRect(x * this->gridSize.width(),
+ y * this->gridSize.height(),
+ width * this->gridSize.width(),
+ height * this->gridSize.height());
+ }
+ void setValidRect(const QRect &rect) {
+ this->validRect = rect;
+ }
+ QRect getValidRect() { return this->validRect; }
+
+protected:
+ void drawBackground(QPainter *painter, const QRectF &rect) override;
+
+private:
+ QSize gridSize;
+ QRect validRect;
+};
+
+#endif // CHECKEREDBGSCENE_H
diff --git a/include/ui/collisionpixmapitem.h b/include/ui/collisionpixmapitem.h
index 1419f2e4..d4f7e182 100644
--- a/include/ui/collisionpixmapitem.h
+++ b/include/ui/collisionpixmapitem.h
@@ -23,29 +23,14 @@ public:
QSpinBox * selectedElevation;
qreal *opacity;
void updateMovementPermissionSelection(QGraphicsSceneMouseEvent *event);
- virtual void paint(QGraphicsSceneMouseEvent*);
- virtual void floodFill(QGraphicsSceneMouseEvent*);
- virtual void magicFill(QGraphicsSceneMouseEvent*);
- virtual void pick(QGraphicsSceneMouseEvent*);
- void draw(bool ignoreCache = false);
+ virtual void paint(QGraphicsSceneMouseEvent*) override;
+ virtual void floodFill(QGraphicsSceneMouseEvent*) override;
+ virtual void magicFill(QGraphicsSceneMouseEvent*) override;
+ virtual void pick(QGraphicsSceneMouseEvent*) override;
+ void draw(bool ignoreCache = false) override;
private:
- unsigned actionId_ = 0;
- QPoint previousPos;
void updateSelection(QPoint pos);
-
-signals:
- void mouseEvent(QGraphicsSceneMouseEvent *, CollisionPixmapItem *);
- void hoveredMapMovementPermissionChanged(int, int);
- void hoveredMapMovementPermissionCleared();
-
-protected:
- void hoverMoveEvent(QGraphicsSceneHoverEvent*);
- void hoverEnterEvent(QGraphicsSceneHoverEvent*);
- void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
- void mousePressEvent(QGraphicsSceneMouseEvent*);
- void mouseMoveEvent(QGraphicsSceneMouseEvent*);
- void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
};
#endif // COLLISIONPIXMAPITEM_H
diff --git a/include/ui/connectionpixmapitem.h b/include/ui/connectionpixmapitem.h
index 32e309f9..5106ba8f 100644
--- a/include/ui/connectionpixmapitem.h
+++ b/include/ui/connectionpixmapitem.h
@@ -2,6 +2,7 @@
#define CONNECTIONPIXMAPITEM_H
#include "mapconnection.h"
+#include "metatile.h"
#include
#include
#include
@@ -31,8 +32,8 @@ private:
bool selected = false;
unsigned actionId = 0;
- static const int mWidth = 16;
- static const int mHeight = 16;
+ static const int mWidth = Metatile::pixelWidth();
+ static const int mHeight = Metatile::pixelHeight();
void updatePos();
void updateOrigin();
diff --git a/include/ui/cursortilerect.h b/include/ui/cursortilerect.h
index f53ae5aa..fd150dc2 100644
--- a/include/ui/cursortilerect.h
+++ b/include/ui/cursortilerect.h
@@ -8,78 +8,56 @@
class CursorTileRect : public QGraphicsItem
{
public:
- CursorTileRect(bool *enabled, QRgb color);
- QRectF boundingRect() const override
- {
- int width = this->width;
- int height = this->height;
- if (this->singleTileMode) {
- width = 16;
- height = 16;
- } else if (!this->rightClickSelectionAnchored && this->smartPathMode && this->selectionHeight == 3 && this->selectionWidth == 3) {
- width = 32;
- height = 32;
- }
+ CursorTileRect(const QSize &tileSize, const QRgb &color, QGraphicsItem *parent = nullptr);
+
+ QSize size() const;
+
+ QRectF boundingRect() const override {
+ auto s = size();
qreal penWidth = 4;
return QRectF(-penWidth,
-penWidth,
- width + penWidth * 2,
- height + penWidth * 2);
+ s.width() + penWidth * 2,
+ s.height() + penWidth * 2);
}
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
- {
- if (!(*enabled)) return;
- int width = this->width;
- int height = this->height;
- if (this->singleTileMode) {
- width = 16;
- height = 16;
- } else if (this->smartPathInEffect()) {
- width = 32;
- height = 32;
- }
-
- painter->setPen(this->color);
- painter->drawRect(x() - 1, y() - 1, width + 2, height + 2);
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
+ if (!isVisible()) return;
+ auto rect = QRectF(pos(), size());
+ painter->setPen(m_color);
+ painter->drawRect(rect + QMargins(1,1,1,1)); // Fill
painter->setPen(QColor(0, 0, 0));
- painter->drawRect(x() - 2, y() - 2, width + 4, height + 4);
- painter->drawRect(x(), y(), width, height);
+ painter->drawRect(rect + QMargins(2,2,2,2)); // Outer border
+ painter->drawRect(rect); // Inner border
}
+
void initAnchor(int coordX, int coordY);
void stopAnchor();
void initRightClickSelectionAnchor(int coordX, int coordY);
void stopRightClickSelectionAnchor();
- void setSmartPathMode(bool enable) { this->smartPathMode = enable; }
- bool getSmartPathMode() const { return this->smartPathMode; }
+ void setSmartPathMode(bool enable) { m_smartPathMode = enable; }
+ bool getSmartPathMode() const { return m_smartPathMode; }
- void setStraightPathMode(bool enable) { this->straightPathMode = enable; }
- bool getStraightPathMode() const { return this->straightPathMode; }
-
- void setSingleTileMode(bool enable) { this->singleTileMode = enable; }
- bool getSingleTileMode() const { return this->singleTileMode; }
+ void setSingleTileMode(bool enable) { m_singleTileMode = enable; }
+ bool getSingleTileMode() const { return m_singleTileMode; }
void updateLocation(int x, int y);
- void updateSelectionSize(int width, int height);
- void setActive(bool active);
- bool getActive();
- bool *enabled;
+ void updateSelectionSize(const QSize &size);
+ void updateSelectionSize(int width, int height) { updateSelectionSize(QSize(width, height)); }
+
private:
- bool active;
- int width;
- int height;
- bool anchored;
- bool rightClickSelectionAnchored;
- bool smartPathMode;
- bool straightPathMode;
- bool singleTileMode;
- int anchorCoordX;
- int anchorCoordY;
- int selectionWidth;
- int selectionHeight;
- QRgb color;
- bool smartPathInEffect();
+ const QSize m_tileSize;
+ QSize m_selectionSize;
+ QPoint m_anchorCoord;
+ QRgb m_color;
+
+ bool m_anchored = false;
+ bool m_rightClickSelectionAnchored = false;
+ bool m_smartPathMode = false;
+ bool m_singleTileMode = false;
+
+ bool smartPathInEffect() const;
};
diff --git a/include/ui/graphicsview.h b/include/ui/graphicsview.h
index c587d2a9..bf3a7e42 100644
--- a/include/ui/graphicsview.h
+++ b/include/ui/graphicsview.h
@@ -4,11 +4,26 @@
#include
#include
-class NoScrollGraphicsView : public QGraphicsView
+// For general utility features that we add to QGraphicsView
+class GraphicsView : public QGraphicsView
{
Q_OBJECT
public:
- NoScrollGraphicsView(QWidget *parent = nullptr) : QGraphicsView(parent) {}
+ GraphicsView(QWidget *parent = nullptr) : QGraphicsView(parent) {}
+
+ void centerOn(const QGraphicsView *other) {
+ if (other && other->viewport()) {
+ QPoint center = other->viewport()->rect().center();
+ QGraphicsView::centerOn(other->mapToScene(center));
+ }
+ }
+};
+
+class NoScrollGraphicsView : public GraphicsView
+{
+ Q_OBJECT
+public:
+ NoScrollGraphicsView(QWidget *parent = nullptr) : GraphicsView(parent) {}
protected:
void wheelEvent(QWheelEvent *event) {
@@ -32,11 +47,11 @@ signals:
void clicked(QMouseEvent *event);
};
-class ConnectionsView : public QGraphicsView
+class ConnectionsView : public GraphicsView
{
Q_OBJECT
public:
- ConnectionsView(QWidget *parent = nullptr) : QGraphicsView(parent) {}
+ ConnectionsView(QWidget *parent = nullptr) : GraphicsView(parent) {}
signals:
void pressedDelete();
diff --git a/include/ui/imageproviders.h b/include/ui/imageproviders.h
index 806ffd5b..f1edc04a 100644
--- a/include/ui/imageproviders.h
+++ b/include/ui/imageproviders.h
@@ -6,13 +6,40 @@
#include
#include
+class Layout;
+
QImage getCollisionMetatileImage(Block);
QImage getCollisionMetatileImage(int, int);
-QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList&, const QList&, bool useTruePalettes = false);
-QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList&, const QList&, bool useTruePalettes = false);
-QImage getTileImage(uint16_t, Tileset*, Tileset*);
-QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false);
-QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset);
+
+QImage getMetatileImage(uint16_t, const Layout*, bool useTruePalettes = false);
+QImage getMetatileImage(const Metatile*, const Layout*, bool useTruePalettes = false);
+QImage getMetatileImage(uint16_t, const Tileset*, const Tileset*, const QList& = {0,1,2}, const QList& = {}, bool useTruePalettes = false);
+QImage getMetatileImage(const Metatile*, const Tileset*, const Tileset*, const QList& = {0,1,2}, const QList& = {}, bool useTruePalettes = false);
+
+QImage getMetatileSheetImage(const Layout *layout, int numMetatilesWIde, bool useTruePalettes = false);
+QImage getMetatileSheetImage(const Tileset *primaryTileset,
+ const Tileset *secondaryTileset,
+ uint16_t metatileIdStart,
+ uint16_t metatileIdEnd,
+ int numMetatilesWIde,
+ const QList &layerOrder,
+ const QList &layerOpacity = {},
+ const QSize &metatileSize = Metatile::pixelSize(),
+ bool useTruePalettes = false);
+QImage getMetatileSheetImage(const Tileset *primaryTileset,
+ const Tileset *secondaryTileset,
+ int numMetatilesWide,
+ const QList &layerOrder,
+ const QList &layerOpacity = {},
+ const QSize &metatileSize = Metatile::pixelSize(),
+ bool useTruePalettes = false);
+
+
+QImage getTileImage(uint16_t, const Tileset*, const Tileset*);
+QImage getPalettedTileImage(uint16_t, const Tileset*, const Tileset*, int, bool useTruePalettes = false);
+QImage getColoredTileImage(uint16_t tileId, const Tileset *, const Tileset *, const QList &palette);
+QImage getGreyscaleTileImage(uint16_t tileId, const Tileset *, const Tileset *);
+
void flattenTo4bppImage(QImage * image);
static QList greyscalePalette({
diff --git a/include/ui/layoutpixmapitem.h b/include/ui/layoutpixmapitem.h
index 695472a5..44970363 100644
--- a/include/ui/layoutpixmapitem.h
+++ b/include/ui/layoutpixmapitem.h
@@ -61,20 +61,22 @@ public:
void magicFill(
int initialX,
int initialY,
- QPoint selectionDimensions,
- QList selectedMetatiles,
- QList selectedCollisions,
+ const QSize &selectionDimensions,
+ const QList &selectedMetatiles,
+ const QList &selectedCollisions,
bool fromScriptCall = false);
void floodFill(int x, int y, bool fromScriptCall = false);
void floodFill(int x, int y, uint16_t metatileId, bool fromScriptCall = false);
void floodFill(int initialX,
int initialY,
- QPoint selectionDimensions,
- QList selectedMetatiles,
- QList selectedCollisions,
+ const QSize &selectionDimensions,
+ const QList &selectedMetatiles,
+ const QList &selectedCollisions,
bool fromScriptCall = false);
void floodFillSmartPath(int initialX, int initialY, bool fromScriptCall = false);
+ static bool isSmartPathSize(const QSize &size) { return size.width() == smartPathWidth && size.height() == smartPathHeight; }
+
virtual void pick(QGraphicsSceneMouseEvent*);
virtual void select(QGraphicsSceneMouseEvent*);
virtual void shift(QGraphicsSceneMouseEvent*);
@@ -86,32 +88,33 @@ public:
void lockNondominantAxis(QGraphicsSceneMouseEvent *event);
QPoint adjustCoords(QPoint pos);
- void setEditsEnabled(bool enabled) { this->editsEnabled = enabled; }
- bool getEditsEnabled() { return this->editsEnabled; }
+protected:
+ unsigned actionId_ = 0;
private:
void paintSmartPath(int x, int y, bool fromScriptCall = false);
+ static bool isValidSmartPathSelection(MetatileSelection selection);
static QList smartPathTable;
+ static constexpr int smartPathWidth = 3;
+ static constexpr int smartPathHeight = 3;
+ static constexpr int smartPathMiddleIndex = (smartPathWidth / 2) + ((smartPathHeight / 2) * smartPathWidth);
QPoint lastMetatileSelectionPos = QPoint(-1,-1);
- unsigned actionId_ = 0;
-
- bool editsEnabled = true;
-
signals:
void startPaint(QGraphicsSceneMouseEvent *, LayoutPixmapItem *);
void endPaint(QGraphicsSceneMouseEvent *, LayoutPixmapItem *);
void mouseEvent(QGraphicsSceneMouseEvent *, LayoutPixmapItem *);
- void hoveredMapMetatileChanged(const QPoint &pos);
- void hoveredMapMetatileCleared();
+ void hoverEntered(const QPoint &pos);
+ void hoverChanged(const QPoint &pos);
+ void hoverCleared();
protected:
- void hoverMoveEvent(QGraphicsSceneHoverEvent*);
- void hoverEnterEvent(QGraphicsSceneHoverEvent*);
- void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
- void mousePressEvent(QGraphicsSceneMouseEvent*);
- void mouseMoveEvent(QGraphicsSceneMouseEvent*);
- void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent*) override;
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent*) override;
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
};
#endif // MAPPIXMAPITEM_H
diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h
index 51c1afe3..789cf9b9 100644
--- a/include/ui/mapimageexporter.h
+++ b/include/ui/mapimageexporter.h
@@ -2,6 +2,7 @@
#define MAPIMAGEEXPORTER_H
#include "project.h"
+#include "checkeredbgscene.h"
class QGifImage;
@@ -52,7 +53,7 @@ private:
Project *m_project = nullptr;
Map *m_map = nullptr;
Layout *m_layout = nullptr;
- QGraphicsScene *m_scene = nullptr;
+ CheckeredBgScene *m_scene = nullptr;
QGifImage *m_timelapseGifImage = nullptr;
QBuffer *m_timelapseBuffer = nullptr;
QMovie *m_timelapseMovie = nullptr;
diff --git a/include/ui/mapruler.h b/include/ui/mapruler.h
index 151492e3..160978cc 100644
--- a/include/ui/mapruler.h
+++ b/include/ui/mapruler.h
@@ -3,6 +3,7 @@
#include
#include
+#include "metatile.h"
class MapRuler : public QGraphicsObject, private QLine
@@ -63,8 +64,8 @@ private:
QPoint snapToWithinBounds(QPoint pos) const;
void updateGeometry();
void updateStatus(Qt::Corner corner);
- int pixWidth() const { return width() * 16; }
- int pixHeight() const { return height() * 16; }
+ int pixWidth() const { return width() * Metatile::pixelWidth(); }
+ int pixHeight() const { return height() * Metatile::pixelHeight(); }
};
#endif // MAPRULER_H
diff --git a/include/ui/mapview.h b/include/ui/mapview.h
index 5520ec80..34610bb1 100644
--- a/include/ui/mapview.h
+++ b/include/ui/mapview.h
@@ -1,20 +1,23 @@
#ifndef MAPVIEW_H
#define MAPVIEW_H
+#if __has_include()
#include
+#endif
+
#include "graphicsview.h"
#include "overlay.h"
#include "tile.h"
class Editor;
-class MapView : public QGraphicsView
+class MapView : public GraphicsView
{
Q_OBJECT
public:
- MapView() : QGraphicsView() {}
- MapView(QWidget *parent) : QGraphicsView(parent) {}
+ MapView() : GraphicsView() {}
+ MapView(QWidget *parent) : GraphicsView(parent) {}
Editor *editor;
@@ -22,6 +25,7 @@ public:
void clearOverlayMap();
// Overlay scripting API
+#ifdef QT_QML_LIB
Q_INVOKABLE void clear(int layer);
Q_INVOKABLE void clear();
Q_INVOKABLE void hide(int layer);
@@ -74,6 +78,7 @@ public:
Q_INVOKABLE void addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int paletteId, bool setTransparency = false, int layer = 0);
Q_INVOKABLE void addTileImage(int x, int y, QJSValue tileObj, bool setTransparency = false, int layer = 0);
Q_INVOKABLE void addMetatileImage(int x, int y, int metatileId, bool setTransparency = false, int layer = 0);
+#endif // QT_QML_LIB
protected:
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
diff --git a/include/ui/metatileimageexporter.h b/include/ui/metatileimageexporter.h
new file mode 100644
index 00000000..1d486bd4
--- /dev/null
+++ b/include/ui/metatileimageexporter.h
@@ -0,0 +1,103 @@
+#ifndef METATILEIMAGEEXPORTER_H
+#define METATILEIMAGEEXPORTER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "config.h"
+#include "checkeredbgscene.h"
+
+class Tileset;
+
+namespace Ui {
+class MetatileImageExporter;
+}
+
+class ReorderableListWidget : public QListWidget
+{
+ Q_OBJECT
+public:
+ explicit ReorderableListWidget(QWidget *parent = nullptr) : QListWidget(parent) {
+ setDragEnabled(true);
+ setDragDropMode(QAbstractItemView::InternalMove);
+ setDefaultDropAction(Qt::MoveAction);
+ };
+
+signals:
+ void reordered();
+
+protected:
+ virtual void dropEvent(QDropEvent *event) override {
+ QListWidget::dropEvent(event);
+ if (event->isAccepted()) {
+ emit reordered();
+ }
+ }
+};
+
+class MetatileImageExporter : public QDialog
+{
+ Q_OBJECT
+
+public:
+ struct Settings {
+ OrderedMap layerOrder = {
+ {2, true},
+ {1, true},
+ {0, true},
+ };
+ uint16_t metatileStart = 0;
+ uint16_t metatileEnd = 0xFFFF;
+ uint16_t numMetatilesWide = projectConfig.metatileSelectorWidth;
+ bool usePrimaryTileset = true;
+ bool useSecondaryTileset = false;
+ bool renderPlaceholders = false;
+ int transparencyMode = 0;
+ };
+
+ explicit MetatileImageExporter(QWidget *parent, Tileset *primaryTileset, Tileset *secondaryTileset, Settings *savedSettings = nullptr);
+ ~MetatileImageExporter();
+
+ bool saveImage(QString filepath = QString());
+ QImage getImage();
+ QString getDefaultFileName() const;
+ void applySettings(const Settings &settings);
+ void reset();
+
+protected:
+ virtual void showEvent(QShowEvent *) override;
+ virtual void closeEvent(QCloseEvent *) override;
+
+private:
+ Ui::MetatileImageExporter *ui;
+
+ Tileset *m_primaryTileset;
+ Tileset *m_secondaryTileset;
+ Settings *m_savedSettings;
+
+ CheckeredBgScene *m_scene = nullptr;
+ QGraphicsPixmapItem *m_preview = nullptr;
+ bool m_previewUpdateQueued = false;
+ QList m_layerOrder;
+ ProjectConfig m_savedConfig;
+ QList m_transparencyButtons;
+
+ void populate(const Settings &settings);
+ void updatePreview();
+ void tryUpdatePreview();
+ void queuePreviewUpdate();
+ void tryEnforceMetatileRange();
+ void syncPixelWidth();
+ void syncMetatileWidth();
+ void validateMetatileStart();
+ void validateMetatileEnd();
+ void updateMetatileRange();
+ void copyRenderSettings();
+ void restoreRenderSettings();
+};
+
+#endif // METATILEIMAGEEXPORTER_H
diff --git a/include/ui/metatilelayersitem.h b/include/ui/metatilelayersitem.h
index ef05a77d..18e799f4 100644
--- a/include/ui/metatilelayersitem.h
+++ b/include/ui/metatilelayersitem.h
@@ -9,38 +9,50 @@
class MetatileLayersItem: public SelectablePixmapItem {
Q_OBJECT
public:
- MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(16, 16, 6, 2) {
- this->metatile = metatile;
- this->primaryTileset = primaryTileset;
- this->secondaryTileset = secondaryTileset;
- this->clearLastModifiedCoords();
- this->clearLastHoveredCoords();
- setAcceptHoverEvents(true);
- }
- void draw();
+ MetatileLayersItem(Metatile *metatile,
+ Tileset *primaryTileset,
+ Tileset *secondaryTileset,
+ Qt::Orientation orientation = Qt::Vertical);
+
+ void draw() override;
void setTilesets(Tileset*, Tileset*);
void setMetatile(Metatile*);
- void clearLastModifiedCoords();
- void clearLastHoveredCoords();
+
+ bool hasCursor() const { return this->cursorCellPos != QPoint(-1,-1); }
+ Tile tileUnderCursor() const;
+
+ QPoint tileIndexToPos(int index) const { return this->tilePositions.value(index); }
+ int posToTileIndex(const QPoint &pos) const { return this->tilePositions.indexOf(pos); }
+ int posToTileIndex(int x, int y) const { return posToTileIndex(QPoint(x, y)); }
+
+ void setOrientation(Qt::Orientation orientation);
+
bool showGrid;
private:
Metatile* metatile;
Tileset *primaryTileset;
Tileset *secondaryTileset;
- QPoint prevChangedPos;
- QPoint prevHoveredPos;
+ Qt::Orientation orientation;
+
+ QPoint cursorCellPos = QPoint(-1,-1);
+
+ QList tilePositions;
+
QPoint getBoundedPos(const QPointF &);
+ void updateSelection();
+ bool setCursorCellPos(const QPoint &pos);
signals:
- void tileChanged(int, int);
- void selectedTilesChanged(QPoint, int, int);
- void hoveredTileChanged(uint16_t);
+ void tileChanged(const QPoint &pos);
+ void paletteChanged(const QPoint &pos);
+ void selectedTilesChanged(const QPoint &pos, const QSize &dimensions);
+ void hoveredTileChanged(const Tile &tile);
void hoveredTileCleared();
protected:
- void mousePressEvent(QGraphicsSceneMouseEvent*);
- void mouseMoveEvent(QGraphicsSceneMouseEvent*);
- void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
- void hoverMoveEvent(QGraphicsSceneHoverEvent*);
- void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
+ void mousePressEvent(QGraphicsSceneMouseEvent*) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
+ void hoverMoveEvent(QGraphicsSceneHoverEvent*) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;
};
#endif // METATILELAYERSITEM_H
diff --git a/include/ui/metatileselector.h b/include/ui/metatileselector.h
index 7bb3f131..663ccd5a 100644
--- a/include/ui/metatileselector.h
+++ b/include/ui/metatileselector.h
@@ -9,20 +9,20 @@
struct MetatileSelectionItem
{
- bool enabled;
- uint16_t metatileId;
+ bool enabled = false;
+ uint16_t metatileId = 0;
};
struct CollisionSelectionItem
{
- bool enabled;
- uint16_t collision;
- uint16_t elevation;
+ bool enabled = false;
+ uint16_t collision = 0;
+ uint16_t elevation = 0;
};
struct MetatileSelection
{
- QPoint dimensions;
+ QSize dimensions;
bool hasCollision;
QList metatileItems;
QList collisionItems;
@@ -31,33 +31,34 @@ struct MetatileSelection
class MetatileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
- MetatileSelector(int numMetatilesWide, Layout *layout): SelectablePixmapItem(16, 16) {
+ MetatileSelector(int numMetatilesWide, Layout *layout)
+ : SelectablePixmapItem(Metatile::pixelSize()),
+ numMetatilesWide(qMax(numMetatilesWide, 1))
+ {
this->externalSelection = false;
this->prefabSelection = false;
- this->numMetatilesWide = numMetatilesWide;
this->layout = layout;
- this->primaryTileset = layout->tileset_primary;
- this->secondaryTileset = layout->tileset_secondary;
this->selection = MetatileSelection{};
this->cellPos = QPoint(-1, -1);
setAcceptHoverEvents(true);
}
- QPoint getSelectionDimensions() override;
+ QSize getSelectionDimensions() const override;
void draw() override;
+ void refresh();
bool select(uint16_t metatile);
void selectFromMap(uint16_t metatileId, uint16_t collision, uint16_t elevation);
- void setTilesets(Tileset*, Tileset*);
- MetatileSelection getMetatileSelection();
+ MetatileSelection getMetatileSelection() const { return this->selection; }
void setPrefabSelection(MetatileSelection selection);
- void setExternalSelection(int, int, QList, QList>);
- QPoint getMetatileIdCoordsOnWidget(uint16_t);
+ void setExternalSelection(int, int, const QList&, const QList>&);
+ QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const;
void setLayout(Layout *layout);
bool isInternalSelection() const { return (!this->externalSelection && !this->prefabSelection); }
- Tileset *primaryTileset;
- Tileset *secondaryTileset;
+ Tileset *primaryTileset() const { return this->layout->tileset_primary; }
+ Tileset *secondaryTileset() const { return this->layout->tileset_secondary; }
+
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
@@ -66,10 +67,10 @@ protected:
void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;
void drawSelection() override;
private:
+ const int numMetatilesWide;
QPixmap basePixmap;
bool externalSelection;
bool prefabSelection;
- int numMetatilesWide;
Layout *layout;
int externalSelectionWidth;
int externalSelectionHeight;
@@ -80,8 +81,9 @@ private:
void updateBasePixmap();
void updateSelectedMetatiles();
void updateExternalSelectedMetatiles();
- uint16_t getMetatileId(int x, int y) const;
- QPoint getMetatileIdCoords(uint16_t);
+ uint16_t posToMetatileId(int x, int y, bool *ok = nullptr) const;
+ uint16_t posToMetatileId(const QPoint &pos, bool *ok = nullptr) const;
+ QPoint metatileIdToPos(uint16_t metatileId, bool *ok = nullptr) const;
bool positionIsValid(const QPoint &pos) const;
bool selectionIsValid();
void hoverChanged();
diff --git a/include/ui/montabwidget.h b/include/ui/montabwidget.h
index 8292d28e..004fa475 100644
--- a/include/ui/montabwidget.h
+++ b/include/ui/montabwidget.h
@@ -33,6 +33,9 @@ public slots:
void setTabActive(int index, bool active = true);
void deactivateTab(int tabIndex);
+signals:
+ void edited();
+
private:
void actionCopyTab(int index);
void actionAddDeleteTab(int index);
diff --git a/include/ui/movablerect.h b/include/ui/movablerect.h
index 92dd43f7..7940e592 100644
--- a/include/ui/movablerect.h
+++ b/include/ui/movablerect.h
@@ -10,7 +10,7 @@
class MovableRect : public QGraphicsRectItem
{
public:
- MovableRect(bool *enabled, const QRectF &rect, const QRgb &color);
+ MovableRect(const QRectF &rect, const QSize &cellSize, const QRgb &color);
QRectF boundingRect() const override {
qreal penWidth = 4;
return QRectF(-penWidth,
@@ -29,13 +29,9 @@ public:
}
void updateLocation(int x, int y);
- void setActive(bool active);
- bool getActive() const { return this->active; }
-
protected:
- bool *enabled = nullptr;
- bool active = true;
QRectF baseRect;
+ QSize cellSize;
QRgb color;
void updateVisibility();
@@ -48,7 +44,7 @@ class ResizableRect : public QObject, public MovableRect
{
Q_OBJECT
public:
- ResizableRect(QObject *parent, bool *enabled, int width, int height, QRgb color);
+ ResizableRect(QObject *parent, const QSize &cellSize, const QSize &size, const QRgb &color);
QRectF boundingRect() const override {
return QRectF(this->rect() + QMargins(lineWidth, lineWidth, lineWidth, lineWidth));
diff --git a/include/ui/newlayoutform.h b/include/ui/newlayoutform.h
index 3e77af18..99640444 100644
--- a/include/ui/newlayoutform.h
+++ b/include/ui/newlayoutform.h
@@ -24,7 +24,7 @@ public:
void setSettings(const Layout::Settings &settings);
Layout::Settings settings() const;
- void setDisabled(bool disabled);
+ void setDimensionsDisabled(bool disabled);
bool validate();
diff --git a/include/ui/numericsorttableitem.h b/include/ui/numericsorttableitem.h
new file mode 100644
index 00000000..e33f48cb
--- /dev/null
+++ b/include/ui/numericsorttableitem.h
@@ -0,0 +1,20 @@
+#ifndef NUMERICSORTTABLEITEM_H
+#define NUMERICSORTTABLEITEM_H
+
+#include
+#include
+
+class NumericSortTableItem : public QTableWidgetItem
+{
+public:
+ explicit NumericSortTableItem(const QString &text) : QTableWidgetItem(text) {};
+
+protected:
+ virtual bool operator<(const QTableWidgetItem &other) const override {
+ QCollator collator;
+ collator.setNumericMode(true);
+ return collator.compare(text(), other.text()) < 0;
+ }
+};
+
+#endif // NUMERICSORTTABLEITEM_H
diff --git a/include/ui/overlay.h b/include/ui/overlay.h
index 86fe0963..9be73ec8 100644
--- a/include/ui/overlay.h
+++ b/include/ui/overlay.h
@@ -8,6 +8,8 @@
#include
#include
+#ifdef QT_QML_LIB
+
class OverlayItem {
public:
OverlayItem() {}
@@ -123,4 +125,17 @@ private:
QRectF *clippingRect;
};
+#else
+
+class Overlay
+{
+public:
+ Overlay() {}
+ ~Overlay() {}
+
+ void renderItems(QPainter *) {}
+};
+
+#endif // QT_QML_LIB
+
#endif // OVERLAY_H
diff --git a/include/ui/palettecolorsearch.h b/include/ui/palettecolorsearch.h
new file mode 100644
index 00000000..967f6efa
--- /dev/null
+++ b/include/ui/palettecolorsearch.h
@@ -0,0 +1,67 @@
+#ifndef PALETTECOLORSEARCH_H
+#define PALETTECOLORSEARCH_H
+
+#include
+#include
+#include
+
+class Tileset;
+class Project;
+
+namespace Ui {
+class PaletteColorSearch;
+}
+
+class PaletteColorSearch : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PaletteColorSearch(Project *project,
+ const Tileset *primaryTileset,
+ const Tileset *secondaryTileset,
+ QWidget *parent = nullptr);
+ ~PaletteColorSearch();
+
+ void setPaletteId(int paletteId);
+ int currentPaletteId() const;
+
+ void setColorId(int colorId);
+ int currentColorId() const;
+
+ void setTilesets(const Tileset *primaryTileset, const Tileset *secondaryTileset);
+ const Tileset* currentTileset() const;
+
+signals:
+ void metatileSelected(uint16_t metatileId);
+ void paletteIdChanged(int paletteId);
+
+private:
+ struct RowData {
+ QString tilesetName;
+ QString pairedTilesetName;
+ QString metatileId;
+ QIcon metatileIcon;
+ };
+
+ enum ResultsColumn {
+ TilesetName,
+ Metatile,
+ };
+
+ Ui::PaletteColorSearch *ui;
+ Project *m_project;
+ const Tileset *m_primaryTileset;
+ const Tileset *m_secondaryTileset;
+
+ QMap> m_resultsCache;
+
+ void addTableEntry(const RowData &rowData);
+ QList search(int colorId) const;
+ QList search(int colorId, const Tileset *tileset, const Tileset *pairedTileset) const;
+ void refresh();
+ void updateResults();
+ void cellDoubleClicked(int row, int col);
+};
+
+#endif // PALETTECOLORSEARCH_H
diff --git a/include/ui/paletteeditor.h b/include/ui/paletteeditor.h
index 63581eb1..2d26cf71 100644
--- a/include/ui/paletteeditor.h
+++ b/include/ui/paletteeditor.h
@@ -2,10 +2,12 @@
#define PALETTEEDITOR_H
#include
+#include
#include "colorinputwidget.h"
#include "project.h"
#include "history.h"
+#include "palettecolorsearch.h"
namespace Ui {
class PaletteEditor;
@@ -24,25 +26,40 @@ class PaletteEditor : public QMainWindow {
public:
explicit PaletteEditor(Project*, Tileset*, Tileset*, int paletteId, QWidget *parent = nullptr);
~PaletteEditor();
+
void setPaletteId(int);
+ int currentPaletteId() const;
+
void setTilesets(Tileset*, Tileset*);
+ bool showingUnusedColors() const;
+
+signals:
+ void metatileSelected(uint16_t metatileId);
+
private:
Ui::PaletteEditor *ui;
- Project *project = nullptr;
- QList colorInputs;
-
+ Project *project;
Tileset *primaryTileset;
Tileset *secondaryTileset;
- QList> palettesHistory;
+ QList colorInputs;
+ QMap> palettesHistory;
+ QMap> unusedColorCache;
+ QPointer colorSearchWindow;
- Tileset* getTileset(int paletteId);
+ Tileset* getTileset(int paletteId) const;
void refreshColorInputs();
+ void refreshPaletteId();
void commitEditHistory();
void commitEditHistory(int paletteId);
+ void updateEditHistoryActions();
void restoreWindowState();
+ void invalidateCache();
void closeEvent(QCloseEvent*);
+ void setColorInputTitles(bool show);
+ QSet getUnusedColorIds();
+ void openColorSearch();
void setRgb(int index, QRgb rgb);
void setPalette(int paletteId, const QList &palette);
@@ -50,14 +67,13 @@ private:
void setBitDepth(int bits);
int bitDepth = 24;
- static const int numColors = 16;
+ static const int numColors = Tileset::numColorsPerPalette();
signals:
void closed();
void changedPaletteColor();
void changedPalette(int);
private slots:
- void on_spinBox_PaletteId_valueChanged(int arg1);
void on_actionUndo_triggered();
void on_actionRedo_triggered();
void on_actionImport_Palette_triggered();
diff --git a/include/ui/preferenceeditor.h b/include/ui/preferenceeditor.h
index 64647f0e..24b6253f 100644
--- a/include/ui/preferenceeditor.h
+++ b/include/ui/preferenceeditor.h
@@ -2,6 +2,7 @@
#define PREFERENCES_H
#include
+#include "config.h"
class NoScrollComboBox;
class QAbstractButton;
@@ -23,7 +24,8 @@ public:
signals:
void preferencesSaved();
void themeChanged(const QString &theme);
- void scriptSettingsChanged(bool on);
+ void scriptSettingsChanged(ScriptAutocompleteMode mode);
+ void reloadProjectRequested();
private:
Ui::PreferenceEditor *ui;
diff --git a/include/ui/resizelayoutpopup.h b/include/ui/resizelayoutpopup.h
index 89ce2e99..b187044f 100644
--- a/include/ui/resizelayoutpopup.h
+++ b/include/ui/resizelayoutpopup.h
@@ -3,10 +3,10 @@
#include "maplayout.h"
#include "project.h"
+#include "checkeredbgscene.h"
#include
#include
-#include
#include
#include
#include
@@ -16,39 +16,12 @@ namespace Ui {
class ResizeLayoutPopup;
}
-
-
-/// Custom scene that paints its background a gray checkered pattern.
-/// Additionally there is a definable "valid" area which will paint the checkerboard green inside.
-class CheckeredBgScene : public QGraphicsScene {
- Q_OBJECT
-
-public:
- CheckeredBgScene(QObject *parent = nullptr);
- void setValidRect(int x, int y, int width, int height) {
- this->validRect = QRect(x * this->gridSize, y * this->gridSize, width * this->gridSize, height * this->gridSize);
- }
- void setValidRect(QRect rect) {
- this->validRect = rect;
- }
- QRect getValidRect() { return this->validRect; }
-
-protected:
- void drawBackground(QPainter *painter, const QRectF &rect) override;
-
-private:
- int gridSize = 16; // virtual pixels
- QRect validRect = QRect();
-};
-
-
-
/// PixmapItem subclass which allows for creating a boundary which determine whether
/// the pixmap paints normally or with a black tint.
-/// This item is movable and snaps on a 16x16 grid.
+/// This item is movable and snaps on a 'cellSize' grid.
class BoundedPixmapItem : public QGraphicsPixmapItem {
public:
- BoundedPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = nullptr);
+ BoundedPixmapItem(const QPixmap &pixmap, const QSize &cellSize, QGraphicsItem *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override;
void setBoundary(ResizableRect *rect) { this->boundary = rect; }
@@ -59,6 +32,7 @@ protected:
private:
ResizableRect *boundary = nullptr;
QPointF clickedPos = QPointF();
+ QSize cellSize;
};
diff --git a/include/ui/selectablepixmapitem.h b/include/ui/selectablepixmapitem.h
index 2681e485..a267291e 100644
--- a/include/ui/selectablepixmapitem.h
+++ b/include/ui/selectablepixmapitem.h
@@ -7,16 +7,27 @@
class SelectablePixmapItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
public:
- SelectablePixmapItem(int cellWidth, int cellHeight): SelectablePixmapItem(cellWidth, cellHeight, INT_MAX, INT_MAX) {}
- SelectablePixmapItem(int cellWidth, int cellHeight, int maxSelectionWidth, int maxSelectionHeight) {
- this->cellWidth = cellWidth;
- this->cellHeight = cellHeight;
- this->maxSelectionWidth = maxSelectionWidth;
- this->maxSelectionHeight = maxSelectionHeight;
- }
- virtual QPoint getSelectionDimensions();
+ SelectablePixmapItem(const QSize &size, const QSize &maxSelectionSize = QSize(INT_MAX, INT_MAX))
+ : SelectablePixmapItem(size.width(), size.height(), maxSelectionSize.width(), maxSelectionSize.height()) {}
+ SelectablePixmapItem(int cellWidth, int cellHeight, int maxSelectionWidth = INT_MAX, int maxSelectionHeight = INT_MAX)
+ : cellWidth(cellWidth),
+ cellHeight(cellHeight),
+ maxSelectionWidth(maxSelectionWidth),
+ maxSelectionHeight(maxSelectionHeight),
+ selectionInitialX(0),
+ selectionInitialY(0),
+ selectionOffsetX(0),
+ selectionOffsetY(0)
+ {}
+ virtual QSize getSelectionDimensions() const { return QSize(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1); }
virtual void draw() = 0;
+ virtual void setMaxSelectionSize(const QSize &size) { setMaxSelectionSize(size.width(), size.height()); }
+ virtual void setMaxSelectionSize(int width, int height);
+ QSize maxSelectionSize() { return QSize(this->maxSelectionWidth, this->maxSelectionHeight); }
+
+ void setSelectionStyle(Qt::PenStyle style);
+
protected:
int cellWidth;
int cellHeight;
@@ -28,16 +39,28 @@ protected:
int selectionOffsetY;
QPoint getSelectionStart();
- void select(int, int, int, int);
- void updateSelection(int, int);
- QPoint getCellPos(QPointF);
+ void select(const QPoint &pos, const QSize &size = QSize(1,1));
+ void select(int x, int y, int width = 1, int height = 1) { select(QPoint(x, y), QSize(width, height)); }
+ void updateSelection(const QPoint &pos);
+ QPoint getCellPos(const QPointF &itemPos);
+ int getBoundedWidth(int width) const { return qBound(1, width, this->maxSelectionWidth); }
+ int getBoundedHeight(int height) const { return qBound(1, height, this->maxSelectionHeight); }
virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
+ virtual void drawSelectionRect(const QPoint &, const QSize &, Qt::PenStyle style = Qt::SolidLine);
virtual void drawSelection();
+ virtual int cellsWide() const { return this->cellWidth ? (pixmap().width() / this->cellWidth) : 0; }
+ virtual int cellsTall() const { return this->cellHeight ? (pixmap().height() / this->cellHeight) : 0; }
signals:
- void selectionChanged(int, int, int, int);
+ void selectionChanged(const QPoint&, const QSize&);
+
+private:
+ QPoint prevCellPos = QPoint(-1,-1);
+ Qt::PenStyle selectionStyle = Qt::SolidLine;
+
+ void setSelection(const QPoint &pos, const QSize &size);
};
#endif // SELECTABLEPIXMAPITEM_H
diff --git a/include/ui/shortcutseditor.h b/include/ui/shortcutseditor.h
index e44c8896..6f136773 100644
--- a/include/ui/shortcutseditor.h
+++ b/include/ui/shortcutseditor.h
@@ -40,6 +40,7 @@ private:
QHash> multiKeyEdits_objects;
void parseObjectList(const QObjectList &objectList);
+ void parseObject(const QObject *object, QMap *objects_labels, QMap *objects_prefixes);
QString getLabel(const QObject *object) const;
bool stringPropertyIsNotEmpty(const QObject *object, const char *name) const;
void populateMainContainer();
@@ -48,7 +49,6 @@ private:
void addNewMultiKeyEdit(const QObject *object, const QString &shortcutContext);
QList siblings(MultiKeyEdit *multiKeyEdit) const;
void promptUserOnDuplicateFound(MultiKeyEdit *current, MultiKeyEdit *sender);
- void removeKeySequence(const QKeySequence &keySequence, MultiKeyEdit *multiKeyEdit);
void saveShortcuts();
void resetShortcuts();
diff --git a/include/ui/tilemaptileselector.h b/include/ui/tilemaptileselector.h
index 155957a6..558b6dc6 100644
--- a/include/ui/tilemaptileselector.h
+++ b/include/ui/tilemaptileselector.h
@@ -136,7 +136,7 @@ public:
this->palette = PaletteUtil::parse(palFilepath, &err);
}
this->setPixmap(QPixmap::fromImage(this->tileset));
- this->numTilesWide = this->tileset.width() / 8;
+ this->numTilesWide = this->tileset.width() / this->cellWidth;
this->selectedTile = 0x00;
setAcceptHoverEvents(true);
}
diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h
index b6a60a61..fc974d10 100644
--- a/include/ui/tileseteditor.h
+++ b/include/ui/tileseteditor.h
@@ -2,12 +2,15 @@
#define TILESETEDITOR_H
#include
+#include
+#include
#include "project.h"
#include "history.h"
#include "paletteeditor.h"
#include "tileseteditormetatileselector.h"
#include "tileseteditortileselector.h"
#include "metatilelayersitem.h"
+#include "metatileimageexporter.h"
class NoScrollComboBox;
class Layout;
@@ -18,6 +21,7 @@ class TilesetEditor;
class MetatileHistoryItem {
public:
+ MetatileHistoryItem() {};
MetatileHistoryItem(uint16_t metatileId, Metatile *prevMetatile, Metatile *newMetatile, QString prevLabel, QString newLabel) {
this->metatileId = metatileId;
this->prevMetatile = prevMetatile;
@@ -25,15 +29,24 @@ public:
this->prevLabel = prevLabel;
this->newLabel = newLabel;
}
+ MetatileHistoryItem(uint16_t metatileIdA, uint16_t metatileIdB) {
+ this->metatileId = metatileIdA;
+ this->swapMetatileId = metatileIdB;
+ this->isSwap = true;
+ }
~MetatileHistoryItem() {
delete this->prevMetatile;
delete this->newMetatile;
}
- uint16_t metatileId;
- Metatile *prevMetatile;
- Metatile *newMetatile;
+
+ uint16_t metatileId = 0;
+ Metatile *prevMetatile = nullptr;
+ Metatile *newMetatile = nullptr;
QString prevLabel;
QString newLabel;
+
+ uint16_t swapMetatileId = 0;
+ bool isSwap = false;
};
class TilesetEditor : public QMainWindow
@@ -53,27 +66,19 @@ public:
QObjectList shortcutableObjects() const;
+ void setPaletteId(int paletteId);
+ int paletteId() const;
+
public slots:
void applyUserShortcuts();
void onSelectedMetatileChanged(uint16_t);
private slots:
void onWindowActivated();
- void onHoveredMetatileChanged(uint16_t);
void onHoveredMetatileCleared();
- void onHoveredTileChanged(uint16_t);
void onHoveredTileCleared();
- void onSelectedTilesChanged();
- void onMetatileLayerTileChanged(int, int);
- void onMetatileLayerSelectionChanged(QPoint, int, int);
+ void onMetatileLayerSelectionChanged(const QPoint&, const QSize&);
void onPaletteEditorChangedPaletteColor();
- void onPaletteEditorChangedPalette(int);
-
- void on_spinBox_paletteSelector_valueChanged(int arg1);
-
- void on_actionImport_Primary_Tiles_triggered();
-
- void on_actionImport_Secondary_Tiles_triggered();
void on_actionChange_Metatiles_Count_triggered();
@@ -89,17 +94,7 @@ private slots:
void on_actionUndo_triggered();
void on_actionRedo_triggered();
- void on_lineEdit_metatileLabel_editingFinished();
-
- void on_actionExport_Primary_Tiles_Image_triggered();
- void on_actionExport_Secondary_Tiles_Image_triggered();
- void on_actionExport_Primary_Metatiles_Image_triggered();
- void on_actionExport_Secondary_Metatiles_Image_triggered();
-
- void on_actionImport_Primary_Metatiles_triggered();
- void on_actionImport_Secondary_Metatiles_triggered();
-
- void on_copyButton_metatileLabel_clicked();
+ void on_copyButton_MetatileLabel_clicked();
void on_actionCut_triggered();
void on_actionCopy_triggered();
@@ -107,6 +102,10 @@ private slots:
void on_horizontalSlider_MetatilesZoom_valueChanged(int value);
void on_horizontalSlider_TilesZoom_valueChanged(int value);
+protected:
+ void keyPressEvent(QKeyEvent *event) override;
+ void closeEvent(QCloseEvent*) override;
+
private:
void initAttributesUi();
void initMetatileSelector();
@@ -122,16 +121,18 @@ private:
void drawSelectedTiles();
void redrawTileSelector();
void redrawMetatileSelector();
- void importTilesetTiles(Tileset*, bool);
- void importTilesetMetatiles(Tileset*, bool);
+ void importTilesetTiles(Tileset*);
+ void importAdvanceMapMetatiles(Tileset*);
+ void exportTilesImage(Tileset*);
+ void exportPorytilesLayerImages(Tileset*);
+ void exportMetatilesImage();
void refresh();
void commitMetatileLabel();
- void closeEvent(QCloseEvent*);
void countMetatileUsage();
void countTileUsage();
void copyMetatile(bool cut);
- void pasteMetatile(const Metatile * toPaste, QString label);
- bool replaceMetatile(uint16_t metatileId, const Metatile * src, QString label);
+ void pasteMetatile(const Metatile &toPaste, QString label);
+ bool replaceMetatile(uint16_t metatileId, const Metatile &src, QString label);
void commitMetatileChange(Metatile * prevMetatile);
void commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel);
uint32_t attributeNameToValue(Metatile::Attr attribute, const QString &text, bool *ok);
@@ -142,24 +143,36 @@ private:
void commitEncounterType();
void commitTerrainType();
void commitLayerType();
+ void commit(MetatileHistoryItem *item);
+ void updateEditHistoryActions();
void setRawAttributesVisible(bool visible);
- void setXFlip(bool enabled);
- void setYFlip(bool enabled);
+ void refreshTileFlips();
+ void refreshPaletteId();
+ void paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly = false);
+ void setMetatileLayerOrientation(Qt::Orientation orientation);
+ void commitMetatileSwap(uint16_t metatileIdA, uint16_t metatileIdB);
+ bool swapMetatiles(uint16_t metatileIdA, uint16_t metatileIdB);
+ void applyMetatileSwapToLayouts(uint16_t metatileIdA, uint16_t metatileIdB);
+ void applyMetatileSwapsToLayouts();
+ void rebuildMetatilePropertiesFrame();
+ void addWidgetToMetatileProperties(QWidget *w, int *row, int rowSpan);
+ void updateLayerTileStatus();
+ void showTileStatus(const Tile &tile);
+ void showTileStatus(uint16_t tileId);
+ void updateMetatileStatus();
+ void showMetatileStatus(uint16_t metatileId);
Ui::TilesetEditor *ui;
History metatileHistory;
TilesetEditorMetatileSelector *metatileSelector = nullptr;
TilesetEditorTileSelector *tileSelector = nullptr;
MetatileLayersItem *metatileLayersItem = nullptr;
- PaletteEditor *paletteEditor = nullptr;
+ QPointer paletteEditor = nullptr;
Project *project = nullptr;
Layout *layout = nullptr;
Metatile *metatile = nullptr;
Metatile *copiedMetatile = nullptr;
QString copiedMetatileLabel;
- int paletteId;
- bool tileXFlip;
- bool tileYFlip;
bool hasUnsavedChanges;
Tileset *primaryTileset = nullptr;
Tileset *secondaryTileset = nullptr;
@@ -170,6 +183,9 @@ private:
QGraphicsScene *metatileLayersScene = nullptr;
bool lockSelection = false;
QSet metatileReloadQueue;
+ MetatileImageExporter::Settings *metatileImageExportSettings = nullptr;
+ QList> metatileIdSwaps;
+ int numLayerViewRows;
bool save();
diff --git a/include/ui/tileseteditormetatileselector.h b/include/ui/tileseteditormetatileselector.h
index 8a3fdfa7..3b38d9b2 100644
--- a/include/ui/tileseteditormetatileselector.h
+++ b/include/ui/tileseteditormetatileselector.h
@@ -9,7 +9,7 @@ class Layout;
class TilesetEditorMetatileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
- TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout);
+ TilesetEditorMetatileSelector(int numMetatilesWide, Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout);
Layout *layout = nullptr;
void draw() override;
@@ -18,11 +18,16 @@ public:
bool select(uint16_t metatileId);
void setTilesets(Tileset*, Tileset*);
- uint16_t getSelectedMetatileId();
- void updateSelectedMetatile();
- QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId);
- QImage buildPrimaryMetatilesImage();
- QImage buildSecondaryMetatilesImage();
+ uint16_t getSelectedMetatileId() const { return this->selectedMetatileId; }
+ QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const;
+
+ void setSwapMode(bool enabled);
+ void addToSwapSelection(uint16_t metatileId);
+ void removeFromSwapSelection(uint16_t metatileId);
+ void clearSwapSelection();
+
+ bool hasCursor() const { return this->prevCellPos != QPoint(-1,-1); }
+ uint16_t metatileIdUnderCursor() const { return this->lastHoveredMetatileId; }
QVector usedMetatiles;
bool selectorShowUnused = false;
@@ -38,32 +43,37 @@ protected:
void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;
private:
+ const int numMetatilesWide;
QImage baseImage;
QPixmap basePixmap;
Tileset *primaryTileset = nullptr;
Tileset *secondaryTileset = nullptr;
- uint16_t selectedMetatileId;
- int numMetatilesWide;
- int numMetatilesHigh;
+ uint16_t selectedMetatileId = 0;
+ QPoint prevCellPos = QPoint(-1,-1);
+
+ QList swapMetatileIds;
+ uint16_t lastHoveredMetatileId = 0;
+ bool inSwapMode = false;
+
void updateBasePixmap();
- uint16_t getMetatileId(int x, int y);
- QPoint getMetatileIdCoords(uint16_t);
- bool shouldAcceptEvent(QGraphicsSceneMouseEvent*);
- int numRows(int numMetatiles);
- int numRows();
+ uint16_t posToMetatileId(int x, int y, bool *ok = nullptr) const;
+ uint16_t posToMetatileId(const QPoint &pos, bool *ok = nullptr) const;
+ QPoint metatileIdToPos(uint16_t metatileId, bool *ok = nullptr) const;
+ bool isValidMetatileId(uint16_t metatileId) const;
+ int numRows(int numMetatiles) const;
+ int numRows() const;
void drawGrid();
void drawDivider();
void drawFilters();
void drawUnused();
void drawCounts();
- QImage buildAllMetatilesImage();
- QImage buildImage(int metatileIdStart, int numMetatiles);
int numPrimaryMetatilesRounded() const;
signals:
void hoveredMetatileChanged(uint16_t);
void hoveredMetatileCleared();
void selectedMetatileChanged(uint16_t);
+ void swapRequested(uint16_t, uint16_t);
};
#endif // TILESETEDITORMETATILESELECTOR_H
diff --git a/include/ui/tileseteditortileselector.h b/include/ui/tileseteditortileselector.h
index aa2a1923..c98239be 100644
--- a/include/ui/tileseteditortileselector.h
+++ b/include/ui/tileseteditortileselector.h
@@ -7,8 +7,8 @@
class TilesetEditorTileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
- TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, int numLayers)
- : SelectablePixmapItem(16, 16, numLayers * 2, 2) {
+ TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset)
+ : SelectablePixmapItem(16, 16, Metatile::tileWidth(), Metatile::tileWidth()) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->numTilesWide = 16;
@@ -18,15 +18,16 @@ public:
this->paletteChanged = false;
setAcceptHoverEvents(true);
}
- QPoint getSelectionDimensions();
- void draw();
+ QSize getSelectionDimensions() const override;
+ void setMaxSelectionSize(int width, int height) override;
+ void draw() override;
void select(uint16_t metatileId);
void highlight(uint16_t metatileId);
void setTilesets(Tileset*, Tileset*);
void setPaletteId(int);
void setTileFlips(bool, bool);
QList getSelectedTiles();
- void setExternalSelection(int, int, QList, QList);
+ void setExternalSelection(int, int, const QList&);
QPoint getTileCoordsOnWidget(uint16_t);
QImage buildPrimaryTilesIndexedImage();
QImage buildSecondaryTilesIndexedImage();
@@ -36,18 +37,19 @@ public:
bool showDivider = false;
protected:
- void mousePressEvent(QGraphicsSceneMouseEvent*);
- void mouseMoveEvent(QGraphicsSceneMouseEvent*);
- void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
- void hoverMoveEvent(QGraphicsSceneHoverEvent*);
- void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
+ void mousePressEvent(QGraphicsSceneMouseEvent*) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
+ void hoverMoveEvent(QGraphicsSceneHoverEvent*) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;
private:
+ QPixmap basePixmap;
bool externalSelection;
int externalSelectionWidth;
int externalSelectionHeight;
QList externalSelectedTiles;
- QList externalSelectedPos;
+ QPoint prevCellPos = QPoint(-1,-1);
Tileset *primaryTileset;
Tileset *secondaryTileset;
@@ -61,9 +63,9 @@ private:
uint16_t getTileId(int x, int y);
QPoint getTileCoords(uint16_t);
QList getCurPaletteTable();
- QList buildSelectedTiles(int, int, QList);
+ QList buildSelectedTiles(int, int, const QList&);
QImage buildImage(int tileIdStart, int numTiles);
-
+ void updateBasePixmap();
void drawUnused();
signals:
diff --git a/include/ui/uintspinbox.h b/include/ui/uintspinbox.h
index bc217ec2..aa8bcb77 100644
--- a/include/ui/uintspinbox.h
+++ b/include/ui/uintspinbox.h
@@ -21,6 +21,7 @@ public:
uint32_t value() const { return m_value; }
uint32_t minimum() const { return m_minimum; }
uint32_t maximum() const { return m_maximum; }
+ uint32_t singleStep() const { return m_singleStep; }
QString prefix() const { return m_prefix; }
int displayIntegerBase() const { return m_displayIntegerBase; }
bool hasPadding() const { return m_hasPadding; }
@@ -28,6 +29,7 @@ public:
void setMinimum(uint32_t min);
void setMaximum(uint32_t max);
void setRange(uint32_t min, uint32_t max);
+ void setSingleStep(uint32_t val);
void setPrefix(const QString &prefix);
void setDisplayIntegerBase(int base);
void setHasPadding(bool enabled);
@@ -36,6 +38,7 @@ private:
uint32_t m_minimum;
uint32_t m_maximum;
uint32_t m_value;
+ uint32_t m_singleStep;
QString m_prefix;
int m_displayIntegerBase;
bool m_hasPadding;
diff --git a/include/ui/updatepromoter.h b/include/ui/updatepromoter.h
index de73bcdd..76157c4d 100644
--- a/include/ui/updatepromoter.h
+++ b/include/ui/updatepromoter.h
@@ -1,6 +1,8 @@
#ifndef UPDATEPROMOTER_H
#define UPDATEPROMOTER_H
+#ifdef QT_NETWORK_LIB
+
#include "network.h"
#include
@@ -47,4 +49,6 @@ signals:
void changedPreferences();
};
+#endif // QT_NETWORK_LIB
+
#endif // UPDATEPROMOTER_H
diff --git a/include/ui/wildmonsearch.h b/include/ui/wildmonsearch.h
index ccf0fe94..4fe608b8 100644
--- a/include/ui/wildmonsearch.h
+++ b/include/ui/wildmonsearch.h
@@ -2,24 +2,11 @@
#define WILDMONSEARCH_H
#include
-#include
-#include
+
+#include "numericsorttableitem.h"
class Project;
-class NumericSortTableItem : public QTableWidgetItem
-{
-public:
- explicit NumericSortTableItem(const QString &text) : QTableWidgetItem(text) {};
-
-protected:
- virtual bool operator<(const QTableWidgetItem &other) const override {
- QCollator collator;
- collator.setNumericMode(true);
- return collator.compare(text(), other.text()) < 0;
- }
-};
-
namespace Ui {
class WildMonSearch;
}
diff --git a/porymap.pro b/porymap.pro
index 4045e230..50aa83e2 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -4,13 +4,23 @@
#
#-------------------------------------------------
-QT += core gui qml network
+QT += core gui
qtHaveModule(charts) {
QT += charts
} else {
warning("Qt module 'charts' not found, disabling chart features.")
}
+qtHaveModule(qml) {
+ QT += qml
+} else {
+ warning("Qt module 'qml' not found, disabling plug-in features.")
+}
+qtHaveModule(network) {
+ QT += network
+} else {
+ warning("Qt module 'network' not found, disabling network features.")
+}
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
@@ -66,6 +76,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/scriptapi/apiutility.cpp \
src/scriptapi/scripting.cpp \
src/ui/aboutporymap.cpp \
+ src/ui/checkeredbgscene.cpp \
src/ui/colorinputwidget.cpp \
src/ui/connectionslistitem.cpp \
src/ui/customattributesdialog.cpp \
@@ -115,6 +126,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/montabwidget.cpp \
src/ui/encountertablemodel.cpp \
src/ui/encountertabledelegates.cpp \
+ src/ui/palettecolorsearch.cpp \
src/ui/paletteeditor.cpp \
src/ui/selectablepixmapitem.cpp \
src/ui/tileseteditor.cpp \
@@ -124,6 +136,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/regionmapeditor.cpp \
src/ui/newmapdialog.cpp \
src/ui/mapimageexporter.cpp \
+ src/ui/metatileimageexporter.cpp \
src/ui/newtilesetdialog.cpp \
src/ui/flowlayout.cpp \
src/ui/mapruler.cpp \
@@ -180,6 +193,7 @@ HEADERS += include/core/advancemapparser.h \
include/lib/orderedmap.h \
include/lib/orderedjson.h \
include/ui/aboutporymap.h \
+ include/ui/checkeredbgscene.h \
include/ui/connectionslistitem.h \
include/ui/customattributesdialog.h \
include/ui/customattributestable.h \
@@ -231,6 +245,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/encountertablemodel.h \
include/ui/encountertabledelegates.h \
include/ui/adjustingstackedwidget.h \
+ include/ui/palettecolorsearch.h \
include/ui/paletteeditor.h \
include/ui/selectablepixmapitem.h \
include/ui/tileseteditor.h \
@@ -240,6 +255,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/regionmapeditor.h \
include/ui/newmapdialog.h \
include/ui/mapimageexporter.h \
+ include/ui/metatileimageexporter.h \
include/ui/newtilesetdialog.h \
include/ui/overlay.h \
include/ui/flowlayout.h \
@@ -283,12 +299,14 @@ FORMS += forms/mainwindow.ui \
forms/prefabcreationdialog.ui \
forms/prefabframe.ui \
forms/tileseteditor.ui \
+ forms/palettecolorsearch.ui \
forms/paletteeditor.ui \
forms/regionmapeditor.ui \
forms/newmapdialog.ui \
forms/aboutporymap.ui \
forms/newtilesetdialog.ui \
forms/mapimageexporter.ui \
+ forms/metatileimageexporter.ui \
forms/shortcutseditor.ui \
forms/preferenceeditor.ui \
forms/regionmappropertiesdialog.ui \
diff --git a/resources/icons/swap_cursor.ico b/resources/icons/swap_cursor.ico
new file mode 100644
index 00000000..d352b1f5
Binary files /dev/null and b/resources/icons/swap_cursor.ico differ
diff --git a/resources/images.qrc b/resources/images.qrc
index 2891ff3c..856a3fbe 100644
--- a/resources/images.qrc
+++ b/resources/images.qrc
@@ -44,6 +44,7 @@
icons/refresh.ico
icons/shift_cursor.ico
icons/shift.ico
+ icons/swap_cursor.ico
icons/tall_grass.ico
icons/warning.ico
icons/minimap.ico
diff --git a/src/config.cpp b/src/config.cpp
index 4f5188bd..4322e4cd 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -4,6 +4,7 @@
#include "map.h"
#include "validator.h"
#include "utility.h"
+#include "metatile.h"
#include
#include
#include
@@ -283,7 +284,7 @@ int KeyValueConfigBase::getConfigInteger(const QString &key, const QString &valu
logWarn(QString("Invalid config value for %1: '%2'. Must be an integer. Using default value '%3'.").arg(key).arg(value).arg(defaultValue));
result = defaultValue;
}
- return qMin(max, qMax(min, result));
+ return qBound(min, result, max);
}
uint32_t KeyValueConfigBase::getConfigUint32(const QString &key, const QString &value, uint32_t min, uint32_t max, uint32_t defaultValue) {
@@ -293,10 +294,13 @@ uint32_t KeyValueConfigBase::getConfigUint32(const QString &key, const QString &
logWarn(QString("Invalid config value for %1: '%2'. Must be an integer. Using default value '%3'.").arg(key).arg(value).arg(defaultValue));
result = defaultValue;
}
- return qMin(max, qMax(min, result));
+ return qBound(min, result, max);
}
QColor KeyValueConfigBase::getConfigColor(const QString &key, const QString &value, const QColor &defaultValue) {
+ if (value.isEmpty())
+ return QColor();
+
QColor color = QColor("#" + value);
if (!color.isValid()) {
logWarn(QString("Invalid config value for %1: '%2'. Must be a color in the format 'RRGGBB'. Using default value '%3'.").arg(key).arg(value).arg(defaultValue.name()));
@@ -305,12 +309,72 @@ QColor KeyValueConfigBase::getConfigColor(const QString &key, const QString &val
return color;
}
+QString KeyValueConfigBase::toConfigColor(const QColor &color) {
+ return color.isValid() ? color.name().remove("#") : QString(); // Our text config treats '#' as the start of a comment.
+}
+
PorymapConfig porymapConfig;
PorymapConfig::PorymapConfig() : KeyValueConfigBase(QStringLiteral("porymap.cfg")) {
reset();
}
+void PorymapConfig::reset() {
+ setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ this->recentProjects.clear();
+ this->projectManuallyClosed = false;
+ this->reopenOnLaunch = true;
+ this->mapListTab = 0;
+ this->mapListEditGroupsEnabled = false;
+ this->mapListHideEmptyEnabled.clear();
+ this->prettyCursors = true;
+ this->mirrorConnectingMaps = true;
+ this->showDiveEmergeMaps = false;
+ this->diveEmergeMapOpacity = 30;
+ this->diveMapOpacity = 15;
+ this->emergeMapOpacity = 15;
+ this->collisionOpacity = 50;
+ this->collisionZoom = 30;
+ this->metatilesZoom = 30;
+ this->tilesetEditorMetatilesZoom = 30;
+ this->tilesetEditorTilesZoom = 30;
+ this->tilesetEditorLayerOrientation = Qt::Vertical;
+ this->showPlayerView = false;
+ this->showCursorTile = true;
+ this->showBorder = true;
+ this->showGrid = false;
+ this->showTilesetEditorMetatileGrid = false;
+ this->showTilesetEditorLayerGrid = true;
+ this->showTilesetEditorDivider = false;
+ this->showTilesetEditorRawAttributes = false;
+ this->showPaletteEditorUnusedColors = false;
+ this->monitorFiles = true;
+ this->tilesetCheckerboardFill = true;
+ this->newMapHeaderSectionExpanded = false;
+ this->theme = "default";
+ this->wildMonChartTheme = "";
+ this->textEditorOpenFolder = "";
+ this->textEditorGotoLine = "";
+ this->paletteEditorBitDepth = 24;
+ this->projectSettingsTab = 0;
+ this->scriptAutocompleteMode = ScriptAutocompleteMode::MapOnly;
+ this->warpBehaviorWarningDisabled = false;
+ this->eventDeleteWarningDisabled = false;
+ this->eventOverlayEnabled = false;
+ this->checkForUpdates = true;
+ this->lastUpdateCheckTime = QDateTime();
+ this->lastUpdateCheckVersion = porymapVersion;
+ this->rateLimitTimes.clear();
+ this->eventSelectionShapeMode = QGraphicsPixmapItem::MaskShape;
+ this->shownInGameReloadMessage = false;
+ this->gridSettings = GridSettings();
+ this->gridSettings.width = Metatile::pixelWidth();
+ this->gridSettings.height = Metatile::pixelHeight();
+ this->statusBarLogTypes = { LogType::LOG_ERROR, LogType::LOG_WARN };
+ this->applicationFont = QFont();
+ this->mapListFont = PorymapConfig::defaultMapListFont();
+}
+
void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
if (key == "recent_project") {
this->recentProjects = value.split(",", Qt::SkipEmptyParts);
@@ -391,6 +455,9 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
this->tilesetEditorMetatilesZoom = getConfigInteger(key, value, 10, 100, 30);
} else if (key == "tileset_editor_tiles_zoom") {
this->tilesetEditorTilesZoom = getConfigInteger(key, value, 10, 100, 30);
+ } else if (key == "tileset_editor_layer_orientation") {
+ // Being explicit here to avoid casting out-of-range values.
+ this->tilesetEditorLayerOrientation = (getConfigInteger(key, value) == static_cast(Qt::Horizontal)) ? Qt::Horizontal : Qt::Vertical;
} else if (key == "show_player_view") {
this->showPlayerView = getConfigBool(key, value);
} else if (key == "show_cursor_tile") {
@@ -407,6 +474,8 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
this->showTilesetEditorDivider = getConfigBool(key, value);
} else if (key == "show_tileset_editor_raw_attributes") {
this->showTilesetEditorRawAttributes = getConfigBool(key, value);
+ } else if (key == "show_palette_editor_unused_colors") {
+ this->showPaletteEditorUnusedColors = getConfigBool(key, value);
} else if (key == "monitor_files") {
this->monitorFiles = getConfigBool(key, value);
} else if (key == "tileset_checkerboard_fill") {
@@ -428,8 +497,13 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
}
} else if (key == "project_settings_tab") {
this->projectSettingsTab = getConfigInteger(key, value, 0);
+#ifdef CONFIG_BACKWARDS_COMPATABILITY
+ // Old setting replaced by script_autocomplete_mode
} else if (key == "load_all_event_scripts") {
- this->loadAllEventScripts = getConfigBool(key, value);
+ this->scriptAutocompleteMode = getConfigBool(key, value) ? ScriptAutocompleteMode::All : ScriptAutocompleteMode::MapOnly;
+#endif
+ } else if (key == "script_autocomplete_mode") {
+ this->scriptAutocompleteMode = static_cast(getConfigInteger(key, value, ScriptAutocompleteMode::MapOnly, ScriptAutocompleteMode::All));
} else if (key == "warp_behavior_warning_disabled") {
this->warpBehaviorWarningDisabled = getConfigBool(key, value);
} else if (key == "event_delete_warning_disabled") {
@@ -534,6 +608,7 @@ QMap PorymapConfig::getKeyValueMap() {
map.insert("metatiles_zoom", QString::number(this->metatilesZoom));
map.insert("tileset_editor_metatiles_zoom", QString::number(this->tilesetEditorMetatilesZoom));
map.insert("tileset_editor_tiles_zoom", QString::number(this->tilesetEditorTilesZoom));
+ map.insert("tileset_editor_layer_orientation", QString::number(this->tilesetEditorLayerOrientation));
map.insert("show_player_view", this->showPlayerView ? "1" : "0");
map.insert("show_cursor_tile", this->showCursorTile ? "1" : "0");
map.insert("show_border", this->showBorder ? "1" : "0");
@@ -542,6 +617,7 @@ QMap PorymapConfig::getKeyValueMap() {
map.insert("show_tileset_editor_layer_grid", this->showTilesetEditorLayerGrid ? "1" : "0");
map.insert("show_tileset_editor_divider", this->showTilesetEditorDivider ? "1" : "0");
map.insert("show_tileset_editor_raw_attributes", this->showTilesetEditorRawAttributes ? "1" : "0");
+ map.insert("show_palette_editor_unused_colors", this->showPaletteEditorUnusedColors ? "1" : "0");
map.insert("monitor_files", this->monitorFiles ? "1" : "0");
map.insert("tileset_checkerboard_fill", this->tilesetCheckerboardFill ? "1" : "0");
map.insert("new_map_header_section_expanded", this->newMapHeaderSectionExpanded ? "1" : "0");
@@ -551,7 +627,7 @@ QMap PorymapConfig::getKeyValueMap() {
map.insert("text_editor_goto_line", this->textEditorGotoLine);
map.insert("palette_editor_bit_depth", QString::number(this->paletteEditorBitDepth));
map.insert("project_settings_tab", QString::number(this->projectSettingsTab));
- map.insert("load_all_event_scripts", QString::number(this->loadAllEventScripts));
+ map.insert("script_autocomplete_mode", QString::number(this->scriptAutocompleteMode));
map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled));
map.insert("event_delete_warning_disabled", QString::number(this->eventDeleteWarningDisabled));
map.insert("event_overlay_enabled", QString::number(this->eventOverlayEnabled));
@@ -571,7 +647,7 @@ QMap PorymapConfig::getKeyValueMap() {
map.insert("grid_x", QString::number(this->gridSettings.offsetX));
map.insert("grid_y", QString::number(this->gridSettings.offsetY));
map.insert("grid_style", GridSettings::getStyleName(this->gridSettings.style));
- map.insert("grid_color", this->gridSettings.color.name().remove("#")); // Our text config treats '#' as the start of a comment.
+ map.insert("grid_color", toConfigColor(this->gridSettings.color));
QStringList logTypesStrings;
for (const auto &type : this->statusBarLogTypes) {
@@ -898,8 +974,13 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->tilesetsHaveCallback = getConfigBool(key, value);
} else if (key == "tilesets_have_is_compressed") {
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
+#ifdef CONFIG_BACKWARDS_COMPATABILITY
+ // Old setting replaced by transparency_color
} else if (key == "set_transparent_pixels_black") {
- this->setTransparentPixelsBlack = getConfigBool(key, value);
+ this->transparencyColor = getConfigBool(key, value) ? QColor(Qt::black) : QColor();
+#endif
+ } else if (key == "transparency_color") {
+ this->transparencyColor = getConfigColor(key, value);
} else if (key == "preserve_matching_only_data") {
this->preserveMatchingOnlyData = getConfigBool(key, value);
} else if (key == "event_icon_path_object") {
@@ -940,6 +1021,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->maxEventsPerGroup = getConfigInteger(key, value, 1, INT_MAX, 255);
} else if (key == "forced_major_version") {
this->forcedMajorVersion = getConfigInteger(key, value);
+ } else if (key == "metatile_selector_width") {
+ this->metatileSelectorWidth = getConfigInteger(key, value, 1, INT_MAX, 8);
} else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key));
}
@@ -1005,7 +1088,7 @@ QMap ProjectConfig::getKeyValueMap() {
}
map.insert("tilesets_have_callback", QString::number(this->tilesetsHaveCallback));
map.insert("tilesets_have_is_compressed", QString::number(this->tilesetsHaveIsCompressed));
- map.insert("set_transparent_pixels_black", QString::number(this->setTransparentPixelsBlack));
+ map.insert("transparency_color", toConfigColor(this->transparencyColor));
map.insert("preserve_matching_only_data", QString::number(this->preserveMatchingOnlyData));
map.insert("metatile_attributes_size", QString::number(this->metatileAttributesSize));
map.insert("metatile_behavior_mask", Util::toHexString(this->metatileBehaviorMask));
@@ -1049,6 +1132,7 @@ QMap ProjectConfig::getKeyValueMap() {
map.insert("warp_behaviors", warpBehaviorStrs.join(","));
map.insert("max_events_per_group", QString::number(this->maxEventsPerGroup));
map.insert("forced_major_version", QString::number(this->forcedMajorVersion));
+ map.insert("metatile_selector_width", QString::number(this->metatileSelectorWidth));
return map;
}
@@ -1182,7 +1266,7 @@ int ProjectConfig::getNumLayersInMetatile() {
}
int ProjectConfig::getNumTilesInMetatile() {
- return this->tripleLayerMetatilesEnabled ? 12 : 8;
+ return getNumLayersInMetatile() * Metatile::tilesPerLayer();
}
void ProjectConfig::setEventIconPath(Event::Group group, const QString &path) {
diff --git a/src/core/advancemapparser.cpp b/src/core/advancemapparser.cpp
index 6a8428ec..04348e96 100644
--- a/src/core/advancemapparser.cpp
+++ b/src/core/advancemapparser.cpp
@@ -73,15 +73,21 @@ Layout *AdvanceMapParser::parseLayout(const QString &filepath, bool *error, cons
const QList tilesets = project->tilesetLabelsOrdered;
- if (mapPrimaryTilesetNum > tilesets.size())
- mapLayout->tileset_primary_label = project->getDefaultPrimaryTilesetLabel();
- else
- mapLayout->tileset_primary_label = tilesets.at(mapPrimaryTilesetNum);
+ const QString defaultPrimaryTileset = project->getDefaultPrimaryTilesetLabel();
+ QString primaryTilesetLabel = tilesets.value(mapPrimaryTilesetNum, defaultPrimaryTileset);
+ if (!project->primaryTilesetLabels.contains(primaryTilesetLabel)) {
+ // AdvanceMap's primary tileset value points to a secondary tileset. Ignore it.
+ primaryTilesetLabel = defaultPrimaryTileset;
+ }
+ const QString defaultSecondaryTileset = project->getDefaultSecondaryTilesetLabel();
+ QString secondaryTilesetLabel = tilesets.value(mapSecondaryTilesetNum, defaultSecondaryTileset);
+ if (!project->secondaryTilesetLabels.contains(secondaryTilesetLabel)) {
+ // AdvanceMap's secondary tileset value points to a primary tileset. Ignore it.
+ secondaryTilesetLabel = defaultSecondaryTileset;
+ }
- if (mapSecondaryTilesetNum > tilesets.size())
- mapLayout->tileset_secondary_label = project->getDefaultSecondaryTilesetLabel();
- else
- mapLayout->tileset_secondary_label = tilesets.at(mapSecondaryTilesetNum);
+ mapLayout->tileset_primary_label = primaryTilesetLabel;
+ mapLayout->tileset_secondary_label = secondaryTilesetLabel;
mapLayout->blockdata = blockdata;
@@ -131,7 +137,7 @@ QList AdvanceMapParser::parseMetatiles(const QString &filepath, bool
}
int attrSize = Metatile::getDefaultAttributesSize(version);
- int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary();
+ int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesSecondary();
int numMetatiles = static_cast(in.at(0)) |
(static_cast(in.at(1)) << 8) |
(static_cast(in.at(2)) << 16) |
diff --git a/src/core/events.cpp b/src/core/events.cpp
index a4ab33a9..194a7ab1 100644
--- a/src/core/events.cpp
+++ b/src/core/events.cpp
@@ -3,6 +3,7 @@
#include "eventframes.h"
#include "project.h"
#include "config.h"
+#include "metatile.h"
Event* Event::create(Event::Type type) {
switch (type) {
@@ -23,6 +24,14 @@ Event::~Event() {
delete this->eventFrame;
}
+int Event::getPixelX() const {
+ return (this->x * Metatile::pixelWidth()) - qMax(0, (this->pixmap.width() - Metatile::pixelWidth()) / 2);
+}
+
+int Event::getPixelY() const {
+ return (this->y * Metatile::pixelHeight()) - qMax(0, this->pixmap.height() - Metatile::pixelHeight());
+}
+
EventFrame *Event::getEventFrame() {
if (!this->eventFrame) createEventFrame();
return this->eventFrame;
@@ -54,6 +63,16 @@ void Event::modify() {
this->map->modify();
}
+QString Event::groupToJsonKey(Event::Group group) {
+ static const QMap map = {
+ {Event::Group::Object, "object_events"},
+ {Event::Group::Warp, "warp_events"},
+ {Event::Group::Coord, "coord_events"},
+ {Event::Group::Bg, "bg_events"},
+ };
+ return map.value(group);
+}
+
const QMap groupToStringMap = {
{Event::Group::Object, "Object"},
{Event::Group::Warp, "Warp"},
diff --git a/src/core/map.cpp b/src/core/map.cpp
index 6ced8d4c..1f3b5916 100644
--- a/src/core/map.cpp
+++ b/src/core/map.cpp
@@ -4,6 +4,7 @@
#include "scripting.h"
#include "utility.h"
#include "editcommands.h"
+#include "project.h"
#include
#include
@@ -15,9 +16,6 @@ Map::Map(QObject *parent) : QObject(parent)
{
m_editHistory = new QUndoStack(this);
- m_scriptFileWatcher = new QFileSystemWatcher(this);
- connect(m_scriptFileWatcher, &QFileSystemWatcher::fileChanged, this, &Map::invalidateScripts);
-
resetEvents();
m_header = new MapHeader(this);
@@ -33,8 +31,6 @@ Map::Map(const Map &other, QObject *parent) : Map(parent) {
*m_header = *other.m_header;
m_layout = other.m_layout;
m_isPersistedToFile = false;
- m_metatileLayerOrder = other.m_metatileLayerOrder;
- m_metatileLayerOpacity = other.m_metatileLayerOpacity;
// Copy events
for (auto i = other.m_events.constBegin(); i != other.m_events.constEnd(); i++) {
@@ -67,44 +63,28 @@ QString Map::mapConstantFromName(const QString &name) {
return projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix) + Util::toDefineCase(name);
}
-int Map::getWidth() const {
- return m_layout ? m_layout->getWidth() : 0;
-}
-
-int Map::getHeight() const {
- return m_layout ? m_layout->getHeight() : 0;
-}
-
-int Map::getBorderWidth() const {
- return m_layout ? m_layout->getBorderWidth() : 0;
-}
-
-int Map::getBorderHeight() const {
- return m_layout ? m_layout->getBorderHeight() : 0;
-}
-
// Get the portion of the map that can be rendered when rendered as a map connection.
// Cardinal connections render the nearest segment of their map and within the bounds of the border draw distance,
// Dive/Emerge connections are rendered normally within the bounds of their parent map.
QRect Map::getConnectionRect(const QString &direction, Layout * fromLayout) const {
int x = 0, y = 0;
- int w = getWidth(), h = getHeight();
+ int w = pixelWidth(), h = pixelHeight();
- QMargins viewDistance = Project::getMetatileViewDistance();
+ QMargins viewDistance = Project::getPixelViewDistance();
if (direction == "up") {
h = qMin(h, viewDistance.top());
- y = getHeight() - h;
+ y = pixelHeight() - h;
} else if (direction == "down") {
h = qMin(h, viewDistance.bottom());
} else if (direction == "left") {
w = qMin(w, viewDistance.left());
- x = getWidth() - w;
+ x = pixelWidth() - w;
} else if (direction == "right") {
w = qMin(w, viewDistance.right());
} else if (MapConnection::isDiving(direction)) {
if (fromLayout) {
- w = qMin(w, fromLayout->getWidth());
- h = qMin(h, fromLayout->getHeight());
+ w = qMin(w, fromLayout->pixelWidth());
+ h = qMin(h, fromLayout->pixelHeight());
}
} else {
// Unknown direction
@@ -127,7 +107,7 @@ QPixmap Map::renderConnection(const QString &direction, Layout * fromLayout) {
fromLayout = nullptr;
QPixmap connectionPixmap = m_layout->render(true, fromLayout, bounds);
- return connectionPixmap.copy(bounds.x() * 16, bounds.y() * 16, bounds.width() * 16, bounds.height() * 16);
+ return connectionPixmap.copy(bounds);
}
void Map::openScript(const QString &label) {
@@ -143,7 +123,12 @@ void Map::setSharedScriptsMap(const QString &sharedScriptsMap) {
void Map::invalidateScripts() {
m_scriptsLoaded = false;
- m_scriptFileWatcher->removePaths(m_scriptFileWatcher->files());
+
+ // m_scriptFileWatcher is a QPointer so clearing it shouldn't be necessary,
+ // but it's possible that Map::getScriptLabels will be called before events are processed.
+ delete m_scriptFileWatcher;
+ m_scriptFileWatcher = nullptr;
+
emit scriptsModified();
}
@@ -158,14 +143,32 @@ QStringList Map::getScriptLabels(Event::Group group) {
.arg(Util::stripPrefix(scriptsFilepath, projectConfig.projectDir() + "/"))
.arg(m_name)
.arg(error));
+
+ // Setting this flag here (and below) lets us skip some steps and logging if we already know it failed.
+ // Script labels may be re-requested often, so we don't want to fill the log with warnings.
m_loggedScriptsFileError = true;
}
- if (!m_scriptFileWatcher->files().contains(scriptsFilepath) && !m_scriptFileWatcher->addPath(scriptsFilepath) && !m_loggedScriptsFileError) {
- logWarn(QString("Failed to add scripts file '%1' to file watcher for %2.")
- .arg(Util::stripPrefix(scriptsFilepath, projectConfig.projectDir() + "/"))
- .arg(m_name));
- m_loggedScriptsFileError = true;
+ if (porymapConfig.monitorFiles && !m_loggedScriptsFileError) {
+ if (!m_scriptFileWatcher) {
+ // Only create the file watcher when it's first needed (even an empty QFileSystemWatcher will consume system resources).
+ // The other option would be for Porymap to have a single global QFileSystemWatcher, but that has complications of its own.
+ m_scriptFileWatcher = new QFileSystemWatcher(this);
+ connect(m_scriptFileWatcher, &QFileSystemWatcher::fileChanged, this, &Map::invalidateScripts);
+ }
+ // m_scriptFileWatcher can stil be nullptr here if the inotify limit was reached on Linux.
+ // Porymap isn't using enough resources in general for this to be a problem, but the user may have lowered the inotify limit.
+ if (!m_scriptFileWatcher) {
+ logWarn(QString("Failed to add scripts file '%1' to file watcher for %2: Reached system resource limit.")
+ .arg(Util::stripPrefix(scriptsFilepath, projectConfig.projectDir() + "/"))
+ .arg(m_name));
+ m_loggedScriptsFileError = true;
+ } else if (!m_scriptFileWatcher->files().contains(scriptsFilepath) && !m_scriptFileWatcher->addPath(scriptsFilepath)) {
+ logWarn(QString("Failed to add scripts file '%1' to file watcher for %2.")
+ .arg(Util::stripPrefix(scriptsFilepath, projectConfig.projectDir() + "/"))
+ .arg(m_name));
+ m_loggedScriptsFileError = true;
+ }
}
m_scriptsLoaded = true;
@@ -278,14 +281,23 @@ int Map::getNumEvents(Event::Group group) const {
if (group == Event::Group::None) {
// Total number of events
int numEvents = 0;
- for (auto i = m_events.constBegin(); i != m_events.constEnd(); i++) {
- numEvents += i.value().length();
+ for (auto it = m_events.constBegin(); it != m_events.constEnd(); it++) {
+ numEvents += it.value().length();
}
return numEvents;
}
return m_events[group].length();
}
+bool Map::hasEvents() const {
+ for (auto it = m_events.constBegin(); it != m_events.constEnd(); it++) {
+ if (!it.value().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
void Map::removeEvent(Event *event) {
for (auto i = m_events.begin(); i != m_events.end(); i++) {
i.value().removeAll(event);
diff --git a/src/core/mapconnection.cpp b/src/core/mapconnection.cpp
index d37a8c74..e1478fad 100644
--- a/src/core/mapconnection.cpp
+++ b/src/core/mapconnection.cpp
@@ -72,20 +72,20 @@ QPixmap MapConnection::render() const {
// For right/down connections this is offset by the dimensions of the parent map.
// For left/up connections this is offset by the dimensions of the target map.
// If 'clipped' is true, only the rendered dimensions of the target map will be used, rather than its full dimensions.
-QPoint MapConnection::relativePos(bool clipped) const {
+QPoint MapConnection::relativePixelPos(bool clipped) const {
int x = 0, y = 0;
if (m_direction == "right") {
- if (m_parentMap) x = m_parentMap->getWidth();
- y = m_offset;
+ if (m_parentMap) x = m_parentMap->pixelWidth();
+ y = m_offset * Metatile::pixelHeight();
} else if (m_direction == "down") {
- x = m_offset;
- if (m_parentMap) y = m_parentMap->getHeight();
+ x = m_offset * Metatile::pixelWidth();
+ if (m_parentMap) y = m_parentMap->pixelHeight();
} else if (m_direction == "left") {
- if (targetMap()) x = !clipped ? -targetMap()->getWidth() : -targetMap()->getConnectionRect(m_direction).width();
- y = m_offset;
+ if (targetMap()) x = !clipped ? -targetMap()->pixelWidth() : -targetMap()->getConnectionRect(m_direction).width();
+ y = m_offset * Metatile::pixelHeight();
} else if (m_direction == "up") {
- x = m_offset;
- if (targetMap()) y = !clipped ? -targetMap()->getHeight() : -targetMap()->getConnectionRect(m_direction).height();
+ x = m_offset * Metatile::pixelWidth();
+ if (targetMap()) y = !clipped ? -targetMap()->pixelHeight() : -targetMap()->getConnectionRect(m_direction).height();
}
return QPoint(x, y);
}
diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp
index 4108400f..44616ab6 100644
--- a/src/core/maplayout.cpp
+++ b/src/core/maplayout.cpp
@@ -5,6 +5,11 @@
#include "scripting.h"
#include "imageproviders.h"
#include "utility.h"
+#include "project.h"
+#include "layoutpixmapitem.h"
+
+QList Layout::s_globalMetatileLayerOrder;
+QList Layout::s_globalMetatileLayerOpacity;
Layout::Layout(const Layout &other) : Layout() {
copyFrom(&other);
@@ -55,6 +60,10 @@ bool Layout::isWithinBounds(int x, int y) const {
return (x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight());
}
+bool Layout::isWithinBounds(const QPoint &pos) const {
+ return isWithinBounds(pos.x(), pos.y());
+}
+
bool Layout::isWithinBounds(const QRect &rect) const {
return rect.left() >= 0 && rect.right() < this->getWidth() && rect.top() >= 0 && rect.bottom() < this->getHeight();
}
@@ -85,15 +94,14 @@ QMargins Layout::getBorderMargins() const {
return distance;
}
-// Get a rectangle that represents (in pixels) the layout's map area and the visible area of its border.
-// At maximum, this is equal to the map size plus the border margins.
-// If the border is large (and so beyond player the view) it may be smaller than that.
+// Get a rectangle that represents (in pixels) the layout's map area + the distance the player can see.
+// Note that this may be smaller than the map area + the size of the border for layouts with large border dimensions.
QRect Layout::getVisibleRect() const {
- QRect area = QRect(0, 0, this->width * 16, this->height * 16);
- return area += (Project::getMetatileViewDistance() * 16);
+ QRect area = QRect(0, 0, this->pixelWidth(), this->pixelHeight());
+ return area += Project::getPixelViewDistance();
}
-bool Layout::getBlock(int x, int y, Block *out) {
+bool Layout::getBlock(int x, int y, Block *out) const {
if (isWithinBounds(x, y)) {
int i = y * getWidth() + x;
*out = this->blockdata.value(i);
@@ -128,6 +136,20 @@ void Layout::setBlockdata(Blockdata newBlockdata, bool enableScriptCallback) {
}
}
+uint16_t Layout::getMetatileId(int x, int y) const {
+ Block block;
+ return getBlock(x, y, &block) ? block.metatileId() : 0;
+}
+
+bool Layout::setMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback) {
+ Block block;
+ if (!getBlock(x, y, &block)) {
+ return false;
+ }
+ setBlock(x, y, Block(metatileId, block.collision(), block.elevation()), enableScriptCallback);
+ return true;
+}
+
void Layout::clearBorderCache() {
this->cached_border.clear();
}
@@ -337,15 +359,13 @@ void Layout::magicFillCollisionElevation(int initialX, int initialY, uint16_t co
}
}
-QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, QRect bounds) {
+QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, const QRect &bounds) {
bool changed_any = false;
- int width_ = getWidth();
- int height_ = getHeight();
- if (this->image.isNull() || this->image.width() != width_ * 16 || this->image.height() != height_ * 16) {
- this->image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888);
+ if (this->image.isNull() || this->image.width() != pixelWidth() || this->image.height() != pixelHeight()) {
+ this->image = QImage(pixelWidth(), pixelHeight(), QImage::Format_RGBA8888);
changed_any = true;
}
- if (this->blockdata.isEmpty() || !width_ || !height_) {
+ if (this->blockdata.isEmpty() || this->width == 0 || this->height == 0) {
this->pixmap = this->pixmap.fromImage(this->image);
return this->pixmap;
}
@@ -361,9 +381,9 @@ QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, QRect bounds) {
if (!ignoreCache && !layoutBlockChanged(i, this->blockdata, this->cached_blockdata)) {
continue;
}
- int map_y = width_ ? i / width_ : 0;
- int map_x = width_ ? i % width_ : 0;
- if (bounds.isValid() && !bounds.contains(map_x, map_y)) {
+ int x = this->width ? ((i % this->width) * Metatile::pixelWidth()) : 0;
+ int y = this->width ? ((i / this->width) * Metatile::pixelHeight()) : 0;
+ if (bounds.isValid() && !bounds.contains(x, y)) {
continue;
}
@@ -376,14 +396,12 @@ QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, QRect bounds) {
metatileId,
fromLayout ? fromLayout->tileset_primary : this->tileset_primary,
fromLayout ? fromLayout->tileset_secondary : this->tileset_secondary,
- metatileLayerOrder,
- metatileLayerOpacity
+ metatileLayerOrder(),
+ metatileLayerOpacity()
);
imageCache.insert(metatileId, metatileImage);
}
-
- QPoint metatileOrigin = QPoint(map_x * 16, map_y * 16);
- painter.drawImage(metatileOrigin, metatileImage);
+ painter.drawImage(x, y, metatileImage);
changed_any = true;
}
painter.end();
@@ -397,13 +415,11 @@ QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, QRect bounds) {
QPixmap Layout::renderCollision(bool ignoreCache) {
bool changed_any = false;
- int width_ = getWidth();
- int height_ = getHeight();
- if (collision_image.isNull() || collision_image.width() != width_ * 16 || collision_image.height() != height_ * 16) {
- collision_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888);
+ if (collision_image.isNull() || collision_image.width() != pixelWidth() || collision_image.height() != pixelHeight()) {
+ collision_image = QImage(pixelWidth(), pixelHeight(), QImage::Format_RGBA8888);
changed_any = true;
}
- if (this->blockdata.isEmpty() || !width_ || !height_) {
+ if (this->blockdata.isEmpty() || this->width == 0 || this->height == 0) {
collision_pixmap = collision_pixmap.fromImage(collision_image);
return collision_pixmap;
}
@@ -415,10 +431,9 @@ QPixmap Layout::renderCollision(bool ignoreCache) {
changed_any = true;
Block block = this->blockdata.at(i);
QImage collision_metatile_image = getCollisionMetatileImage(block);
- int map_y = width_ ? i / width_ : 0;
- int map_x = width_ ? i % width_ : 0;
- QPoint metatile_origin = QPoint(map_x * 16, map_y * 16);
- painter.drawImage(metatile_origin, collision_metatile_image);
+ int x = this->width ? ((i % this->width) * Metatile::pixelWidth()) : 0;
+ int y = this->width ? ((i / this->width) * Metatile::pixelHeight()) : 0;
+ painter.drawImage(x, y, collision_metatile_image);
}
painter.end();
cacheCollision();
@@ -430,14 +445,14 @@ QPixmap Layout::renderCollision(bool ignoreCache) {
QPixmap Layout::renderBorder(bool ignoreCache) {
bool changed_any = false, border_resized = false;
- int width_ = getBorderWidth();
- int height_ = getBorderHeight();
+ int pixelWidth = this->border_width * Metatile::pixelWidth();
+ int pixelHeight = this->border_height * Metatile::pixelHeight();
if (this->border_image.isNull()) {
- this->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888);
+ this->border_image = QImage(pixelWidth, pixelHeight, QImage::Format_RGBA8888);
changed_any = true;
}
- if (this->border_image.width() != width_ * 16 || this->border_image.height() != height_ * 16) {
- this->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888);
+ if (this->border_image.width() != pixelWidth || this->border_image.height() != pixelHeight) {
+ this->border_image = QImage(pixelWidth, pixelHeight, QImage::Format_RGBA8888);
border_resized = true;
}
if (this->border.isEmpty()) {
@@ -453,10 +468,10 @@ QPixmap Layout::renderBorder(bool ignoreCache) {
changed_any = true;
Block block = this->border.at(i);
uint16_t metatileId = block.metatileId();
- QImage metatile_image = getMetatileImage(metatileId, this->tileset_primary, this->tileset_secondary, metatileLayerOrder, metatileLayerOpacity);
- int map_y = width_ ? i / width_ : 0;
- int map_x = width_ ? i % width_ : 0;
- painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image);
+ QImage metatile_image = getMetatileImage(metatileId, this);
+ int x = this->border_width ? ((i % this->border_width) * Metatile::pixelWidth()) : 0;
+ int y = this->border_width ? ((i / this->border_width) * Metatile::pixelHeight()) : 0;
+ painter.drawImage(x, y, metatile_image);
}
painter.end();
if (changed_any) {
@@ -531,6 +546,7 @@ bool Layout::loadBorder(const QString &root) {
logError(QString("Failed to load border for %1 from '%2': %3").arg(this->name).arg(path).arg(error));
return false;
}
+ this->border = blockdata;
// 0 is an expected border width/height that should be handled, GF used it for the RS layouts in FRLG
if (this->border_width <= 0) {
@@ -540,10 +556,6 @@ bool Layout::loadBorder(const QString &root) {
this->border_height = DEFAULT_BORDER_HEIGHT;
}
- this->border = blockdata;
- this->lastCommitBlocks.border = blockdata;
- this->lastCommitBlocks.borderDimensions = QSize(this->border_width, this->border_height);
-
int expectedSize = this->border_width * this->border_height;
if (this->border.count() != expectedSize) {
logWarn(QString("%1 border blockdata length %2 does not match dimensions %3x%4 (should be %5). Resizing border blockdata.")
@@ -554,6 +566,10 @@ bool Layout::loadBorder(const QString &root) {
.arg(expectedSize));
this->border.resize(expectedSize);
}
+
+ this->lastCommitBlocks.border = this->border;
+ this->lastCommitBlocks.borderDimensions = QSize(this->border_width, this->border_height);
+
return true;
}
@@ -570,10 +586,7 @@ bool Layout::loadBlockdata(const QString &root) {
logError(QString("Failed to load blockdata for %1 from '%2': %3").arg(this->name).arg(path).arg(error));
return false;
}
-
this->blockdata = blockdata;
- this->lastCommitBlocks.blocks = blockdata;
- this->lastCommitBlocks.layoutDimensions = QSize(this->width, this->height);
int expectedSize = this->width * this->height;
if (expectedSize <= 0) {
@@ -589,6 +602,10 @@ bool Layout::loadBlockdata(const QString &root) {
.arg(expectedSize));
this->blockdata.resize(expectedSize);
}
+
+ this->lastCommitBlocks.blocks = this->blockdata;
+ this->lastCommitBlocks.layoutDimensions = QSize(this->width, this->height);
+
return true;
}
diff --git a/src/core/metatile.cpp b/src/core/metatile.cpp
index 073fd867..00fff2ba 100644
--- a/src/core/metatile.cpp
+++ b/src/core/metatile.cpp
@@ -36,8 +36,10 @@ int Metatile::getIndexInTileset(int metatileId) {
}
QPoint Metatile::coordFromPixmapCoord(const QPointF &pixelCoord) {
- int x = static_cast(pixelCoord.x()) / 16;
- int y = static_cast(pixelCoord.y()) / 16;
+ int x = static_cast(pixelCoord.x()) / pixelWidth();
+ int y = static_cast(pixelCoord.y()) / pixelHeight();
+ if (pixelCoord.x() < 0) x--;
+ if (pixelCoord.y() < 0) y--;
return QPoint(x, y);
}
@@ -46,13 +48,18 @@ QString Metatile::getMetatileIdString(uint16_t metatileId) {
return Util::toHexString(metatileId, numMetatileIdChars);
};
-QString Metatile::getMetatileIdStrings(const QList metatileIds) {
+QString Metatile::getMetatileIdStrings(const QList &metatileIds) {
QStringList metatiles;
for (auto metatileId : metatileIds)
metatiles << Metatile::getMetatileIdString(metatileId);
return metatiles.join(",");
};
+QString Metatile::getLayerName(int layerNum) {
+ static const QStringList layerTitles = { "Bottom", "Middle", "Top"};
+ return layerTitles.value(layerNum);
+}
+
// Read and pack together this metatile's attributes.
uint32_t Metatile::getAttributes() const {
uint32_t data = 0;
diff --git a/src/core/network.cpp b/src/core/network.cpp
index 4f2a1c19..ce0d5124 100644
--- a/src/core/network.cpp
+++ b/src/core/network.cpp
@@ -1,3 +1,4 @@
+#ifdef QT_NETWORK_LIB
#include "network.h"
#include "config.h"
@@ -148,3 +149,5 @@ void NetworkAccessManager::processReply(QNetworkReply * reply, NetworkReplyData
cacheEntry->data = data->m_body = reply->readAll();
}
+
+#endif // QT_NETWORK_LIB
diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp
index cc7efb55..0aa1f7d6 100644
--- a/src/core/tileset.cpp
+++ b/src/core/tileset.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
Tileset::Tileset(const Tileset &other)
@@ -20,15 +21,15 @@ Tileset::Tileset(const Tileset &other)
metatile_attrs_label(other.metatile_attrs_label),
metatile_attrs_path(other.metatile_attrs_path),
tilesImagePath(other.tilesImagePath),
- tilesImage(other.tilesImage.copy()),
palettePaths(other.palettePaths),
metatileLabels(other.metatileLabels),
palettes(other.palettes),
palettePreviews(other.palettePreviews),
+ m_tilesImage(other.m_tilesImage.copy()),
m_hasUnsavedTilesImage(other.m_hasUnsavedTilesImage)
{
- for (auto tile : other.tiles) {
- tiles.append(tile.copy());
+ for (auto tile : other.m_tiles) {
+ m_tiles.append(tile.copy());
}
for (auto *metatile : other.m_metatiles) {
@@ -46,15 +47,15 @@ Tileset &Tileset::operator=(const Tileset &other) {
metatile_attrs_label = other.metatile_attrs_label;
metatile_attrs_path = other.metatile_attrs_path;
tilesImagePath = other.tilesImagePath;
- tilesImage = other.tilesImage.copy();
+ m_tilesImage = other.m_tilesImage.copy();
palettePaths = other.palettePaths;
metatileLabels = other.metatileLabels;
palettes = other.palettes;
palettePreviews = other.palettePreviews;
- tiles.clear();
- for (auto tile : other.tiles) {
- tiles.append(tile.copy());
+ m_tiles.clear();
+ for (auto tile : other.m_tiles) {
+ m_tiles.append(tile.copy());
}
clearMetatiles();
@@ -94,7 +95,50 @@ void Tileset::resizeMetatiles(int newNumMetatiles) {
}
}
+uint16_t Tileset::firstMetatileId() const {
+ return this->is_secondary ? Project::getNumMetatilesPrimary() : 0;
+}
+
+uint16_t Tileset::lastMetatileId() const {
+ return qMax(1, firstMetatileId() + m_metatiles.length()) - 1;
+}
+
+int Tileset::maxMetatiles() const {
+ return this->is_secondary ? Project::getNumMetatilesSecondary() : Project::getNumMetatilesPrimary();
+}
+
+uint16_t Tileset::firstTileId() const {
+ return this->is_secondary ? Project::getNumTilesPrimary() : 0;
+}
+
+uint16_t Tileset::lastTileId() const {
+ return qMax(1, firstMetatileId() + m_tiles.length()) - 1;
+}
+
+int Tileset::maxTiles() const {
+ return this->is_secondary ? Project::getNumTilesSecondary() : Project::getNumTilesPrimary();
+}
+
+Tileset* Tileset::getPaletteTileset(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset) {
+ return const_cast(getPaletteTileset(paletteId, static_cast(primaryTileset), static_cast(secondaryTileset)));
+}
+
+const Tileset* Tileset::getPaletteTileset(int paletteId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
+ if (paletteId < Project::getNumPalettesPrimary()) {
+ return primaryTileset;
+ } else if (paletteId < Project::getNumPalettesTotal()) {
+ return secondaryTileset;
+ } else {
+ return nullptr;
+ }
+}
+
Tileset* Tileset::getTileTileset(int tileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
+ return const_cast(getTileTileset(tileId, static_cast(primaryTileset), static_cast(secondaryTileset)));
+}
+
+// Get the tileset *expected* to contain the given 'tileId'. Note that this does not mean the tile actually exists in that tileset.
+const Tileset* Tileset::getTileTileset(int tileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
if (tileId < Project::getNumTilesPrimary()) {
return primaryTileset;
} else if (tileId < Project::getNumTilesTotal()) {
@@ -105,6 +149,11 @@ Tileset* Tileset::getTileTileset(int tileId, Tileset *primaryTileset, Tileset *s
}
Tileset* Tileset::getMetatileTileset(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
+ return const_cast(getMetatileTileset(metatileId, static_cast(primaryTileset), static_cast(secondaryTileset)));
+}
+
+// Get the tileset *expected* to contain the given 'metatileId'. Note that this does not mean the metatile actually exists in that tileset.
+const Tileset* Tileset::getMetatileTileset(int metatileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
if (metatileId < Project::getNumMetatilesPrimary()) {
return primaryTileset;
} else if (metatileId < Project::getNumMetatilesTotal()) {
@@ -115,7 +164,11 @@ Tileset* Tileset::getMetatileTileset(int metatileId, Tileset *primaryTileset, Ti
}
Metatile* Tileset::getMetatile(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
- Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset);
+ return const_cast(getMetatile(metatileId, static_cast(primaryTileset), static_cast(secondaryTileset)));
+}
+
+const Metatile* Tileset::getMetatile(int metatileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
+ const Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset);
if (!tileset) {
return nullptr;
}
@@ -200,46 +253,48 @@ QString Tileset::getMetatileLabelPrefix()
QString Tileset::getMetatileLabelPrefix(const QString &name)
{
// Default is "gTileset_Name" --> "METATILE_Name_"
- const QString tilesetPrefix = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix);
const QString labelPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_metatile_label_prefix);
- return QString("%1%2_").arg(labelPrefix).arg(QString(name).replace(tilesetPrefix, ""));
+ return QString("%1%2_").arg(labelPrefix).arg(Tileset::stripPrefix(name));
}
-bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
- if (metatileId >= Project::getNumMetatilesTotal())
- return false;
-
- if (metatileId < Project::getNumMetatilesPrimary() && metatileId >= primaryTileset->numMetatiles())
- return false;
-
- if (metatileId >= Project::getNumMetatilesPrimary() + secondaryTileset->numMetatiles())
- return false;
-
- return true;
+bool Tileset::metatileIsValid(uint16_t metatileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
+ return (primaryTileset && primaryTileset->containsMetatileId(metatileId))
+ || (secondaryTileset && secondaryTileset->containsMetatileId(metatileId));
}
-QList> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) {
+QList