mirror of
https://github.com/huderlem/porymap.git
synced 2026-08-22 16:46:45 -05:00
Merge pull request #758 from GriffinRichards/metatile-layers
New metatile export options, metatile/tile image fixes
This commit is contained in:
@@ -1222,6 +1222,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 +1863,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
|
||||
|
||||
|
||||
|
||||
526
forms/metatileimageexporter.ui
Normal file
526
forms/metatileimageexporter.ui
Normal file
@@ -0,0 +1,526 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MetatileImageExporter</class>
|
||||
<widget class="QDialog" name="MetatileImageExporter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>649</width>
|
||||
<height>601</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Export Metatiles Image</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="1">
|
||||
<widget class="QFrame" name="frame_Options">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_Options">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_Options">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_Options">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>304</width>
|
||||
<height>532</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Tilesets">
|
||||
<property name="title">
|
||||
<string>Tilesets</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_SecondaryTileset">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If checked, automatically update the metatile range to include the full secondary tileset.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Secondary Tileset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_PrimaryTileset">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_SecondaryTileset">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_PrimaryTileset">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If checked, automatically update the metatile range to include the full primary tileset.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Primary Tileset</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>1</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_MetatileRange">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Metatile Range</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_MetatileStart">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_MetatileStart">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The metatile ID to start the rendered image at.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_MetatileEnd">
|
||||
<property name="text">
|
||||
<string>End</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_MetatileEnd">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The metatile ID to end the rendered image at.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Layers">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Layers</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ReorderableListWidget" name="listWidget_Layers">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow</enum>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragDropMode::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::DropAction::MoveAction</enum>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::ResizeMode::Adjust</enum>
|
||||
</property>
|
||||
<property name="itemAlignment">
|
||||
<set>Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Transparency">
|
||||
<property name="title">
|
||||
<string>Transparency</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_TransparencyNormal">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If checked, transparent pixels in the image will be rendered with alpha of 0.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_TransparencyBlack">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Black</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_TransparencyFirst">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>First palette color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Misc">
|
||||
<property name="title">
|
||||
<string>Miscellaneous</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="checkBox_Placeholders">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Render placeholder metatiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_WidthMetatiles">
|
||||
<property name="text">
|
||||
<string>Width (metatiles)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="UIntSpinBox" name="spinBox_WidthMetatiles">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Width of the output image in metatiles.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_WidthPixels">
|
||||
<property name="text">
|
||||
<string>Width (pixels)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="UIntSpinBox" name="spinBox_WidthPixels">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="minimum" stdset="0">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="maximum" stdset="0">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="singleStep" stdset="0">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="value" stdset="0">
|
||||
<number>128</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_Buttons">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_Reset">
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_Close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_Save">
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_Preview">
|
||||
<property name="title">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QScrollArea" name="scrollArea_Preview">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_Preview">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>285</width>
|
||||
<height>551</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="3">
|
||||
<widget class="QGraphicsView" name="graphicsView_Preview">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="mouseTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored</enum>
|
||||
</property>
|
||||
<property name="dragMode">
|
||||
<enum>QGraphicsView::DragMode::NoDrag</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NoScrollComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>noscrollcombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>UIntSpinBox</class>
|
||||
<extends>QAbstractSpinBox</extends>
|
||||
<header>uintspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>UIntHexSpinBox</class>
|
||||
<extends>UIntSpinBox</extends>
|
||||
<header location="global">uintspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ReorderableListWidget</class>
|
||||
<extends>QListWidget</extends>
|
||||
<header>metatileimageexporter.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -852,7 +852,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>570</width>
|
||||
<height>798</height>
|
||||
<height>837</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
@@ -973,6 +973,27 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_MetatileSelectorWidth">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_MetatileSelectorWidth">
|
||||
<property name="text">
|
||||
<string>Metatile Selector Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_MetatileSelectorWidth">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The width (in metatiles) of the metatile selectors on the Map tab and in the Tileset Editor.</p></body></html></string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_WarningTilesetsTab">
|
||||
<property name="styleSheet">
|
||||
@@ -1873,16 +1894,6 @@
|
||||
<extends>QComboBox</extends>
|
||||
<header>noscrollcombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NoScrollSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>noscrollspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NoScrollTextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>noscrolltextedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>UIntSpinBox</class>
|
||||
<extends>QAbstractSpinBox</extends>
|
||||
@@ -1893,6 +1904,16 @@
|
||||
<extends>UIntSpinBox</extends>
|
||||
<header location="global">uintspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NoScrollSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>noscrollspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NoScrollTextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>noscrolltextedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources/images.qrc"/>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>239</width>
|
||||
<width>253</width>
|
||||
<height>659</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -555,7 +555,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>499</width>
|
||||
<width>446</width>
|
||||
<height>241</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -625,18 +625,42 @@
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuExport_Tiles_Image">
|
||||
<property name="title">
|
||||
<string>Export Tiles Image</string>
|
||||
</property>
|
||||
<addaction name="actionExport_Primary_Tiles_Image"/>
|
||||
<addaction name="actionExport_Secondary_Tiles_Image"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuImport_AdvanceMap_Metatiles">
|
||||
<property name="title">
|
||||
<string>Import Metatiles from Advance Map 1.92</string>
|
||||
</property>
|
||||
<addaction name="actionImport_Primary_AdvanceMap_Metatiles"/>
|
||||
<addaction name="actionImport_Secondary_AdvanceMap_Metatiles"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuImport_Tiles_Image">
|
||||
<property name="title">
|
||||
<string>Import Tiles Image</string>
|
||||
</property>
|
||||
<addaction name="actionImport_Primary_Tiles_Image"/>
|
||||
<addaction name="actionImport_Secondary_Tiles_Image"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuExport_porytiles_Layer_Images">
|
||||
<property name="title">
|
||||
<string>Export Porytiles Layer Images...</string>
|
||||
</property>
|
||||
<addaction name="actionExport_Primary_Porytiles_Layer_Images"/>
|
||||
<addaction name="actionExport_Secondary_Porytiles_Layer_Images"/>
|
||||
</widget>
|
||||
<addaction name="actionSave_Tileset"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionImport_Primary_Tiles"/>
|
||||
<addaction name="actionImport_Secondary_Tiles"/>
|
||||
<addaction name="menuImport_Tiles_Image"/>
|
||||
<addaction name="menuImport_AdvanceMap_Metatiles"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionImport_Primary_Metatiles"/>
|
||||
<addaction name="actionImport_Secondary_Metatiles"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExport_Primary_Tiles_Image"/>
|
||||
<addaction name="actionExport_Secondary_Tiles_Image"/>
|
||||
<addaction name="actionExport_Primary_Metatiles_Image"/>
|
||||
<addaction name="actionExport_Secondary_Metatiles_Image"/>
|
||||
<addaction name="menuExport_Tiles_Image"/>
|
||||
<addaction name="actionExport_Metatiles_Image"/>
|
||||
<addaction name="menuExport_porytiles_Layer_Images"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTools">
|
||||
<property name="title">
|
||||
@@ -685,16 +709,6 @@
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Primary_Tiles">
|
||||
<property name="text">
|
||||
<string>Import Primary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Secondary_Tiles">
|
||||
<property name="text">
|
||||
<string>Import Secondary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChange_Metatiles_Count">
|
||||
<property name="text">
|
||||
<string>Change Number of Metatiles...</string>
|
||||
@@ -745,36 +759,6 @@
|
||||
<string>Ctrl+Y</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Primary_Tiles_Image">
|
||||
<property name="text">
|
||||
<string>Export Primary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Secondary_Tiles_Image">
|
||||
<property name="text">
|
||||
<string>Export Secondary Tiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Primary_Metatiles">
|
||||
<property name="text">
|
||||
<string>Import Primary Metatiles from Advance Map 1.92...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Secondary_Metatiles">
|
||||
<property name="text">
|
||||
<string>Import Secondary Metatiles from Advance Map 1.92...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Primary_Metatiles_Image">
|
||||
<property name="text">
|
||||
<string>Export Primary Metatiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Secondary_Metatiles_Image">
|
||||
<property name="text">
|
||||
<string>Export Secondary Metatiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCut">
|
||||
<property name="text">
|
||||
<string>Cut</string>
|
||||
@@ -834,6 +818,51 @@
|
||||
<string>Show Raw Metatile Attributes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Primary_Tiles_Image">
|
||||
<property name="text">
|
||||
<string>Primary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Secondary_Tiles_Image">
|
||||
<property name="text">
|
||||
<string>Secondary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Primary_AdvanceMap_Metatiles">
|
||||
<property name="text">
|
||||
<string>Primary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Secondary_AdvanceMap_Metatiles">
|
||||
<property name="text">
|
||||
<string>Secondary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Primary_Tiles_Image">
|
||||
<property name="text">
|
||||
<string>Primary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Secondary_Tiles_Image">
|
||||
<property name="text">
|
||||
<string>Secondary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Metatiles_Image">
|
||||
<property name="text">
|
||||
<string>Export Metatiles Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Primary_Porytiles_Layer_Images">
|
||||
<property name="text">
|
||||
<string>Primary...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Secondary_Porytiles_Layer_Images">
|
||||
<property name="text">
|
||||
<string>Secondary...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
@@ -60,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;
|
||||
@@ -71,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 = 45;
|
||||
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->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->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();
|
||||
@@ -357,7 +308,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();
|
||||
@@ -374,6 +325,7 @@ public:
|
||||
this->unusedTileSplit = 0x0000;
|
||||
this->maxEventsPerGroup = 255;
|
||||
this->forcedMajorVersion = 0;
|
||||
this->metatileSelectorWidth = 8;
|
||||
this->globalConstantsFilepaths.clear();
|
||||
this->globalConstants.clear();
|
||||
this->identifiers.clear();
|
||||
@@ -432,7 +384,7 @@ public:
|
||||
bool prefabImportPrompted;
|
||||
bool tilesetsHaveCallback;
|
||||
bool tilesetsHaveIsCompressed;
|
||||
bool setTransparentPixelsBlack;
|
||||
QColor transparencyColor;
|
||||
bool preserveMatchingOnlyData;
|
||||
int metatileAttributesSize;
|
||||
uint32_t metatileBehaviorMask;
|
||||
@@ -453,6 +405,7 @@ public:
|
||||
QList<uint32_t> warpBehaviors;
|
||||
int maxEventsPerGroup;
|
||||
int forcedMajorVersion;
|
||||
int metatileSelectorWidth;
|
||||
QStringList globalConstantsFilepaths;
|
||||
QMap<QString,QString> globalConstants;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
@@ -133,9 +135,6 @@ private:
|
||||
QMap<Event::Group, QList<Event *>> m_events;
|
||||
QSet<Event *> m_ownedEvents; // for memory management
|
||||
|
||||
QList<int> m_metatileLayerOrder;
|
||||
QList<float> m_metatileLayerOpacity;
|
||||
|
||||
void trackConnection(MapConnection*);
|
||||
|
||||
// MapConnections in 'ownedConnections' but not 'connections' persist in the edit history.
|
||||
|
||||
@@ -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> project;
|
||||
static const QMap<QString, QString> oppositeDirections;
|
||||
|
||||
@@ -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<int> metatileLayerOrder;
|
||||
QList<float> metatileLayerOpacity;
|
||||
void setMetatileLayerOrder(const QList<int> &layerOrder) { m_metatileLayerOrder = layerOrder; }
|
||||
const QList<int> &metatileLayerOrder() const {
|
||||
return !m_metatileLayerOrder.isEmpty() ? m_metatileLayerOrder : Layout::globalMetatileLayerOrder();
|
||||
}
|
||||
static void setGlobalMetatileLayerOrder(const QList<int> &layerOrder) { s_globalMetatileLayerOrder = layerOrder; }
|
||||
static const QList<int> &globalMetatileLayerOrder() {
|
||||
static const QList<int> defaultLayerOrder = {0, 1, 2};
|
||||
return !s_globalMetatileLayerOrder.isEmpty() ? s_globalMetatileLayerOrder : defaultLayerOrder;
|
||||
}
|
||||
|
||||
void setMetatileLayerOpacity(const QList<float> &layerOpacity) { m_metatileLayerOpacity = layerOpacity; }
|
||||
const QList<float> &metatileLayerOpacity() const {
|
||||
return !m_metatileLayerOpacity.isEmpty() ? m_metatileLayerOpacity : Layout::globalMetatileLayerOpacity();
|
||||
}
|
||||
static void setGlobalMetatileLayerOpacity(const QList<float> &layerOpacity) { s_globalMetatileLayerOpacity = layerOpacity; }
|
||||
static const QList<float> &globalMetatileLayerOpacity() {
|
||||
static const QList<float> defaultLayerOpacity = {1.0, 1.0, 1.0};
|
||||
return !s_globalMetatileLayerOpacity.isEmpty() ? s_globalMetatileLayerOpacity : defaultLayerOpacity;
|
||||
}
|
||||
|
||||
LayoutPixmapItem *layoutItem = nullptr;
|
||||
CollisionPixmapItem *collisionItem = nullptr;
|
||||
@@ -136,9 +156,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();
|
||||
@@ -147,6 +166,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);
|
||||
@@ -155,6 +176,11 @@ private:
|
||||
|
||||
static int getBorderDrawDistance(int dimension, qreal minimum);
|
||||
|
||||
QList<int> m_metatileLayerOrder;
|
||||
QList<float> m_metatileLayerOpacity;
|
||||
static QList<int> s_globalMetatileLayerOrder;
|
||||
static QList<float> s_globalMetatileLayerOpacity;
|
||||
|
||||
signals:
|
||||
void dimensionsChanged(const QSize &size);
|
||||
void needsRedrawing();
|
||||
|
||||
@@ -62,6 +62,14 @@ public:
|
||||
static void setLayout(Project*);
|
||||
static QString getMetatileIdString(uint16_t metatileId);
|
||||
static QString getMetatileIdStrings(const QList<uint16_t> 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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define TILE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
|
||||
class Tile
|
||||
{
|
||||
@@ -24,6 +25,11 @@ 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 sizeInBytes() { return sizeof(uint16_t); }
|
||||
};
|
||||
|
||||
inline bool operator==(const Tile &a, const Tile &b) {
|
||||
|
||||
@@ -30,14 +30,13 @@ public:
|
||||
QString metatile_attrs_label;
|
||||
QString metatile_attrs_path;
|
||||
QString tilesImagePath;
|
||||
QImage tilesImage;
|
||||
QStringList palettePaths;
|
||||
|
||||
QList<QImage> tiles;
|
||||
QHash<int, QString> metatileLabels;
|
||||
QList<QList<QRgb>> palettes;
|
||||
QList<QList<QRgb>> palettePreviews;
|
||||
|
||||
static QString stripPrefix(const QString &fullName);
|
||||
static Tileset* getMetatileTileset(int, Tileset*, Tileset*);
|
||||
static Tileset* getTileTileset(int, Tileset*, Tileset*);
|
||||
static Metatile* getMetatile(int, Tileset*, Tileset*);
|
||||
@@ -76,15 +75,28 @@ public:
|
||||
void setMetatiles(const QList<Metatile*> &metatiles);
|
||||
void addMetatile(Metatile* metatile);
|
||||
|
||||
QList<Metatile*> metatiles() const { return m_metatiles; }
|
||||
Metatile* metatileAt(unsigned int i) const { return m_metatiles.at(i); }
|
||||
const QList<Metatile*> &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 contains(uint16_t metatileId) const { return metatileId >= firstMetatileId() && metatileId <= lastMetatileId(); }
|
||||
|
||||
int numTiles() const { return m_tiles.length(); }
|
||||
int maxTiles() const;
|
||||
|
||||
QImage tileImage(uint16_t tileId) const { return m_tiles.value(Tile::getIndexInTileset(tileId)); }
|
||||
|
||||
private:
|
||||
QList<Metatile*> m_metatiles;
|
||||
|
||||
QList<QImage> m_tiles;
|
||||
QImage m_tilesImage;
|
||||
bool m_hasUnsavedTilesImage = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -285,7 +285,6 @@ signals:
|
||||
void wildMonTableEdited();
|
||||
void currentMetatilesSelectionChanged();
|
||||
void mapRulerStatusChanged(const QString &);
|
||||
void tilesetUpdated(QString);
|
||||
void gridToggled(bool);
|
||||
void editActionSet(EditAction newEditAction);
|
||||
};
|
||||
|
||||
@@ -145,6 +145,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<int> getMetatileLayerOrder() const;
|
||||
Q_INVOKABLE void setMetatileLayerOrder(const QList<int> &order);
|
||||
Q_INVOKABLE QList<float> getMetatileLayerOpacity() const;
|
||||
Q_INVOKABLE void setMetatileLayerOpacity(const QList<float> &opacities);
|
||||
Q_INVOKABLE QString getSong();
|
||||
Q_INVOKABLE void setSong(QString song);
|
||||
Q_INVOKABLE QString getLocation();
|
||||
|
||||
@@ -255,13 +255,17 @@ 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();
|
||||
|
||||
|
||||
@@ -51,7 +51,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);
|
||||
|
||||
@@ -38,9 +38,9 @@ public:
|
||||
Q_INVOKABLE bool getSmartPathsEnabled();
|
||||
Q_INVOKABLE QList<QString> getCustomScripts();
|
||||
Q_INVOKABLE QList<int> getMetatileLayerOrder();
|
||||
Q_INVOKABLE void setMetatileLayerOrder(QList<int> order);
|
||||
Q_INVOKABLE void setMetatileLayerOrder(const QList<int> &order);
|
||||
Q_INVOKABLE QList<float> getMetatileLayerOpacity();
|
||||
Q_INVOKABLE void setMetatileLayerOpacity(QList<float> order);
|
||||
Q_INVOKABLE void setMetatileLayerOpacity(const QList<float> &order);
|
||||
Q_INVOKABLE QList<QString> getMapNames();
|
||||
Q_INVOKABLE QList<QString> getMapConstants();
|
||||
Q_INVOKABLE QList<QString> getLayoutNames();
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
Q_INVOKABLE bool isPrimaryTileset(QString tilesetName);
|
||||
Q_INVOKABLE bool isSecondaryTileset(QString tilesetName);
|
||||
|
||||
static bool validateMetatileLayerOrder(const QList<int> &order);
|
||||
|
||||
private:
|
||||
void callTimeoutFunction(QJSValue callback);
|
||||
void runMessageBox(QString text, QString informativeText, QString detailedText, QMessageBox::Icon icon);
|
||||
|
||||
39
include/ui/checkeredbgscene.h
Normal file
39
include/ui/checkeredbgscene.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef CHECKEREDBGSCENE_H
|
||||
#define CHECKEREDBGSCENE_H
|
||||
|
||||
#include <QGraphicsScene>
|
||||
|
||||
// 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
|
||||
@@ -2,6 +2,7 @@
|
||||
#define CONNECTIONPIXMAPITEM_H
|
||||
|
||||
#include "mapconnection.h"
|
||||
#include "metatile.h"
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QPainter>
|
||||
#include <QPointer>
|
||||
@@ -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();
|
||||
|
||||
@@ -6,13 +6,40 @@
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
|
||||
class Layout;
|
||||
|
||||
QImage getCollisionMetatileImage(Block);
|
||||
QImage getCollisionMetatileImage(int, int);
|
||||
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
|
||||
|
||||
QImage getMetatileImage(uint16_t, Layout*, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(Metatile*, Layout*, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList<int>&, const QList<float>& = {}, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList<int>&, const QList<float>& = {}, bool useTruePalettes = false);
|
||||
|
||||
QImage getMetatileSheetImage(Layout *layout, int numMetatilesWIde, bool useTruePalettes = false);
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
uint16_t metatileIdStart,
|
||||
uint16_t metatileIdEnd,
|
||||
int numMetatilesWIde,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity = {},
|
||||
const QSize &metatileSize = Metatile::pixelSize(),
|
||||
bool useTruePalettes = false);
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
int numMetatilesWide,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity = {},
|
||||
const QSize &metatileSize = Metatile::pixelSize(),
|
||||
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 getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList<QRgb> &palette);
|
||||
QImage getGreyscaleTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset);
|
||||
|
||||
void flattenTo4bppImage(QImage * image);
|
||||
|
||||
static QList<QRgb> greyscalePalette({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QGraphicsObject>
|
||||
#include <QLine>
|
||||
#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
|
||||
|
||||
103
include/ui/metatileimageexporter.h
Normal file
103
include/ui/metatileimageexporter.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef METATILEIMAGEEXPORTER_H
|
||||
#define METATILEIMAGEEXPORTER_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QShowEvent>
|
||||
#include <QCloseEvent>
|
||||
#include <QListWidget>
|
||||
#include <QDropEvent>
|
||||
#include <QRadioButton>
|
||||
|
||||
#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<int,bool> 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<int> m_layerOrder;
|
||||
ProjectConfig m_savedConfig;
|
||||
QList<QRadioButton*> 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
|
||||
@@ -26,7 +26,7 @@ private:
|
||||
signals:
|
||||
void tileChanged(int, int);
|
||||
void selectedTilesChanged(QPoint, int, int);
|
||||
void hoveredTileChanged(uint16_t);
|
||||
void hoveredTileChanged(const Tile &tile);
|
||||
void hoveredTileCleared();
|
||||
protected:
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent*);
|
||||
|
||||
@@ -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;
|
||||
QPoint 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<uint16_t>, QList<QPair<uint16_t, uint16_t>>);
|
||||
QPoint getMetatileIdCoordsOnWidget(uint16_t);
|
||||
void setExternalSelection(int, int, const QList<uint16_t>&, const QList<QPair<uint16_t, uint16_t>>&);
|
||||
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();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
class MovableRect : public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
MovableRect(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,
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
|
||||
protected:
|
||||
QRectF baseRect;
|
||||
QSize cellSize;
|
||||
QRgb color;
|
||||
|
||||
void updateVisibility();
|
||||
@@ -43,7 +44,7 @@ class ResizableRect : public QObject, public MovableRect
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ResizableRect(QObject *parent, 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));
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#include "maplayout.h"
|
||||
#include "project.h"
|
||||
#include "checkeredbgscene.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPointer>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsLineItem>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QDialogButtonBox>
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,19 @@
|
||||
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 QPoint getSelectionDimensions() const;
|
||||
virtual void draw() = 0;
|
||||
|
||||
protected:
|
||||
@@ -28,7 +33,8 @@ protected:
|
||||
int selectionOffsetY;
|
||||
|
||||
QPoint getSelectionStart();
|
||||
void select(int, int, int, int);
|
||||
void select(int x, int y, int width = 0, int height = 0);
|
||||
void select(const QPoint &pos, const QSize &size = QSize(0,0)) { select(pos.x(), pos.y(), size.width(), size.height()); }
|
||||
void updateSelection(int, int);
|
||||
QPoint getCellPos(QPointF);
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "tileseteditormetatileselector.h"
|
||||
#include "tileseteditortileselector.h"
|
||||
#include "metatilelayersitem.h"
|
||||
#include "metatileimageexporter.h"
|
||||
|
||||
class NoScrollComboBox;
|
||||
class Layout;
|
||||
@@ -61,6 +62,7 @@ private slots:
|
||||
void onWindowActivated();
|
||||
void onHoveredMetatileChanged(uint16_t);
|
||||
void onHoveredMetatileCleared();
|
||||
void onHoveredTileChanged(const Tile&);
|
||||
void onHoveredTileChanged(uint16_t);
|
||||
void onHoveredTileCleared();
|
||||
void onSelectedTilesChanged();
|
||||
@@ -71,10 +73,6 @@ private slots:
|
||||
|
||||
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();
|
||||
|
||||
void on_actionChange_Palettes_triggered();
|
||||
@@ -91,14 +89,6 @@ private slots:
|
||||
|
||||
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_actionCut_triggered();
|
||||
@@ -122,8 +112,11 @@ 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*);
|
||||
@@ -170,6 +163,7 @@ private:
|
||||
QGraphicsScene *metatileLayersScene = nullptr;
|
||||
bool lockSelection = false;
|
||||
QSet<uint16_t> metatileReloadQueue;
|
||||
MetatileImageExporter::Settings *metatileImageExportSettings = nullptr;
|
||||
|
||||
bool save();
|
||||
|
||||
|
||||
@@ -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,9 @@ public:
|
||||
|
||||
bool select(uint16_t metatileId);
|
||||
void setTilesets(Tileset*, Tileset*);
|
||||
uint16_t getSelectedMetatileId();
|
||||
uint16_t getSelectedMetatileId() const { return this->selectedMetatileId; }
|
||||
void updateSelectedMetatile();
|
||||
QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId);
|
||||
QImage buildPrimaryMetatilesImage();
|
||||
QImage buildSecondaryMetatilesImage();
|
||||
QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const;
|
||||
|
||||
QVector<uint16_t> usedMetatiles;
|
||||
bool selectorShowUnused = false;
|
||||
@@ -38,26 +36,24 @@ 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;
|
||||
void updateBasePixmap();
|
||||
uint16_t getMetatileId(int x, int y);
|
||||
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 shouldAcceptEvent(QGraphicsSceneMouseEvent*);
|
||||
int numRows(int numMetatiles);
|
||||
int numRows();
|
||||
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:
|
||||
|
||||
@@ -8,7 +8,7 @@ class TilesetEditorTileSelector: public SelectablePixmapItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, int numLayers)
|
||||
: SelectablePixmapItem(16, 16, numLayers * 2, 2) {
|
||||
: SelectablePixmapItem(16, 16, numLayers * Metatile::tileWidth(), Metatile::tileHeight()) {
|
||||
this->primaryTileset = primaryTileset;
|
||||
this->secondaryTileset = secondaryTileset;
|
||||
this->numTilesWide = 16;
|
||||
@@ -18,8 +18,8 @@ public:
|
||||
this->paletteChanged = false;
|
||||
setAcceptHoverEvents(true);
|
||||
}
|
||||
QPoint getSelectionDimensions();
|
||||
void draw();
|
||||
QPoint getSelectionDimensions() const override;
|
||||
void draw() override;
|
||||
void select(uint16_t metatileId);
|
||||
void highlight(uint16_t metatileId);
|
||||
void setTilesets(Tileset*, Tileset*);
|
||||
@@ -36,13 +36,14 @@ 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;
|
||||
@@ -63,7 +64,7 @@ private:
|
||||
QList<QRgb> getCurPaletteTable();
|
||||
QList<Tile> buildSelectedTiles(int, int, QList<Tile>);
|
||||
QImage buildImage(int tileIdStart, int numTiles);
|
||||
|
||||
void updateBasePixmap();
|
||||
void drawUnused();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -66,6 +66,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 \
|
||||
@@ -124,6 +125,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 +182,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 \
|
||||
@@ -240,6 +243,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 \
|
||||
@@ -289,6 +293,7 @@ FORMS += forms/mainwindow.ui \
|
||||
forms/aboutporymap.ui \
|
||||
forms/newtilesetdialog.ui \
|
||||
forms/mapimageexporter.ui \
|
||||
forms/metatileimageexporter.ui \
|
||||
forms/shortcutseditor.ui \
|
||||
forms/preferenceeditor.ui \
|
||||
forms/regionmappropertiesdialog.ui \
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "map.h"
|
||||
#include "validator.h"
|
||||
#include "utility.h"
|
||||
#include "metatile.h"
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFormLayout>
|
||||
@@ -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,70 @@ 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->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->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);
|
||||
@@ -576,7 +638,7 @@ QMap<QString, QString> 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) {
|
||||
@@ -903,8 +965,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
this->tilesetsHaveCallback = getConfigBool(key, value);
|
||||
} else if (key == "tilesets_have_is_compressed") {
|
||||
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
|
||||
} else if (key == "set_transparent_pixels_black") {
|
||||
this->setTransparentPixelsBlack = getConfigBool(key, value);
|
||||
} 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") {
|
||||
@@ -945,6 +1007,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));
|
||||
}
|
||||
@@ -1010,7 +1074,7 @@ QMap<QString, QString> 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));
|
||||
@@ -1054,6 +1118,7 @@ QMap<QString, QString> 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;
|
||||
}
|
||||
@@ -1187,7 +1252,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) {
|
||||
|
||||
@@ -131,7 +131,7 @@ QList<Metatile*> 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<unsigned char>(in.at(0)) |
|
||||
(static_cast<unsigned char>(in.at(1)) << 8) |
|
||||
(static_cast<unsigned char>(in.at(2)) << 16) |
|
||||
|
||||
@@ -30,8 +30,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++) {
|
||||
@@ -64,44 +62,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
|
||||
@@ -124,7 +106,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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "imageproviders.h"
|
||||
#include "utility.h"
|
||||
|
||||
QList<int> Layout::s_globalMetatileLayerOrder;
|
||||
QList<float> Layout::s_globalMetatileLayerOpacity;
|
||||
|
||||
Layout::Layout(const Layout &other) : Layout() {
|
||||
copyFrom(&other);
|
||||
}
|
||||
@@ -89,12 +92,11 @@ 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) {
|
||||
@@ -341,15 +343,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;
|
||||
}
|
||||
@@ -365,9 +365,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;
|
||||
}
|
||||
|
||||
@@ -380,14 +380,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();
|
||||
@@ -401,13 +399,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;
|
||||
}
|
||||
@@ -419,10 +415,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();
|
||||
@@ -434,14 +429,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()) {
|
||||
@@ -457,10 +452,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) {
|
||||
@@ -535,6 +530,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) {
|
||||
@@ -544,10 +540,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.")
|
||||
@@ -558,6 +550,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;
|
||||
}
|
||||
|
||||
@@ -574,10 +570,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) {
|
||||
@@ -593,6 +586,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ int Metatile::getIndexInTileset(int metatileId) {
|
||||
}
|
||||
|
||||
QPoint Metatile::coordFromPixmapCoord(const QPointF &pixelCoord) {
|
||||
int x = static_cast<int>(pixelCoord.x()) / 16;
|
||||
int y = static_cast<int>(pixelCoord.y()) / 16;
|
||||
int x = static_cast<int>(pixelCoord.x()) / pixelWidth();
|
||||
int y = static_cast<int>(pixelCoord.y()) / pixelHeight();
|
||||
if (pixelCoord.x() < 0) x--;
|
||||
if (pixelCoord.y() < 0) y--;
|
||||
return QPoint(x, y);
|
||||
@@ -55,6 +55,11 @@ QString Metatile::getMetatileIdStrings(const QList<uint16_t> metatileIds) {
|
||||
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;
|
||||
|
||||
@@ -20,15 +20,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 +46,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,6 +94,22 @@ 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();
|
||||
}
|
||||
|
||||
int Tileset::maxTiles() const {
|
||||
return this->is_secondary ? Project::getNumTilesSecondary() : Project::getNumTilesPrimary();
|
||||
}
|
||||
|
||||
Tileset* Tileset::getTileTileset(int tileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
if (tileId < Project::getNumTilesPrimary()) {
|
||||
return primaryTileset;
|
||||
@@ -200,34 +216,34 @@ 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;
|
||||
return (primaryTileset && primaryTileset->contains(metatileId))
|
||||
|| (secondaryTileset && secondaryTileset->contains(metatileId));
|
||||
}
|
||||
|
||||
QList<QList<QRgb>> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) {
|
||||
QList<QList<QRgb>> palettes;
|
||||
auto primaryPalettes = useTruePalettes ? primaryTileset->palettes : primaryTileset->palettePreviews;
|
||||
|
||||
QList<QList<QRgb>> primaryPalettes;
|
||||
if (primaryTileset) {
|
||||
primaryPalettes = useTruePalettes ? primaryTileset->palettes : primaryTileset->palettePreviews;
|
||||
}
|
||||
for (int i = 0; i < Project::getNumPalettesPrimary(); i++) {
|
||||
palettes.append(primaryPalettes.at(i));
|
||||
palettes.append(primaryPalettes.value(i));
|
||||
}
|
||||
|
||||
QList<QList<QRgb>> secondaryPalettes;
|
||||
if (secondaryTileset) {
|
||||
secondaryPalettes = useTruePalettes ? secondaryTileset->palettes : secondaryTileset->palettePreviews;
|
||||
}
|
||||
auto secondaryPalettes = useTruePalettes ? secondaryTileset->palettes : secondaryTileset->palettePreviews;
|
||||
for (int i = Project::getNumPalettesPrimary(); i < Project::getNumPalettesTotal(); i++) {
|
||||
palettes.append(secondaryPalettes.at(i));
|
||||
palettes.append(secondaryPalettes.value(i));
|
||||
}
|
||||
|
||||
return palettes;
|
||||
}
|
||||
|
||||
@@ -236,10 +252,12 @@ QList<QRgb> Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset
|
||||
Tileset *tileset = paletteId < Project::getNumPalettesPrimary()
|
||||
? primaryTileset
|
||||
: secondaryTileset;
|
||||
auto palettes = useTruePalettes ? tileset->palettes : tileset->palettePreviews;
|
||||
if (!tileset) {
|
||||
return paletteTable;
|
||||
}
|
||||
|
||||
if (paletteId < 0 || paletteId >= palettes.length()){
|
||||
logError(QString("Invalid tileset palette id '%1' requested.").arg(paletteId));
|
||||
auto palettes = useTruePalettes ? tileset->palettes : tileset->palettePreviews;
|
||||
if (paletteId < 0 || paletteId >= palettes.length()) {
|
||||
return paletteTable;
|
||||
}
|
||||
|
||||
@@ -373,8 +391,7 @@ QString Tileset::getExpectedDir(QString tilesetName, bool isSecondary)
|
||||
: projectConfig.getFilePath(ProjectFilePath::data_primary_tilesets_folders);
|
||||
|
||||
static const QRegularExpression re("([a-z])([A-Z0-9])");
|
||||
const QString prefix = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix);
|
||||
return basePath + tilesetName.replace(prefix, "").replace(re, "\\1_\\2").toLower();
|
||||
return basePath + Tileset::stripPrefix(tilesetName).replace(re, "\\1_\\2").toLower();
|
||||
}
|
||||
|
||||
// Get the expected positions of the members in struct Tileset.
|
||||
@@ -400,17 +417,25 @@ QHash<int, QString> Tileset::getHeaderMemberMap(bool usingAsm)
|
||||
bool Tileset::loadMetatiles() {
|
||||
clearMetatiles();
|
||||
|
||||
QFile metatiles_file(this->metatiles_path);
|
||||
if (!metatiles_file.open(QIODevice::ReadOnly)) {
|
||||
logError(QString("Could not open '%1' for reading: %2").arg(this->metatiles_path).arg(metatiles_file.errorString()));
|
||||
QFile file(this->metatiles_path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
logError(QString("Could not open '%1' for reading: %2").arg(this->metatiles_path).arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = metatiles_file.readAll();
|
||||
QByteArray data = file.readAll();
|
||||
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
int bytesPerMetatile = 2 * tilesPerMetatile;
|
||||
int num_metatiles = data.length() / bytesPerMetatile;
|
||||
for (int i = 0; i < num_metatiles; i++) {
|
||||
int bytesPerMetatile = Tile::sizeInBytes() * tilesPerMetatile;
|
||||
int numMetatiles = data.length() / bytesPerMetatile;
|
||||
if (numMetatiles > maxMetatiles()) {
|
||||
logWarn(QString("%1 metatile count %2 exceeds limit of %3. Additional metatiles will be ignored.")
|
||||
.arg(this->name)
|
||||
.arg(numMetatiles)
|
||||
.arg(maxMetatiles()));
|
||||
numMetatiles = maxMetatiles();
|
||||
}
|
||||
|
||||
for (int i = 0; i < numMetatiles; i++) {
|
||||
auto metatile = new Metatile;
|
||||
int index = i * bytesPerMetatile;
|
||||
for (int j = 0; j < tilesPerMetatile; j++) {
|
||||
@@ -424,9 +449,9 @@ bool Tileset::loadMetatiles() {
|
||||
}
|
||||
|
||||
bool Tileset::saveMetatiles() {
|
||||
QFile metatiles_file(this->metatiles_path);
|
||||
if (!metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
logError(QString("Could not open '%1' for writing: %2").arg(this->metatiles_path).arg(metatiles_file.errorString()));
|
||||
QFile file(this->metatiles_path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
logError(QString("Could not open '%1' for writing: %2").arg(this->metatiles_path).arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -434,31 +459,40 @@ bool Tileset::saveMetatiles() {
|
||||
int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
for (const auto &metatile : m_metatiles) {
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
uint16_t tile = metatile->tiles.at(i).rawValue();
|
||||
uint16_t tile = metatile->tiles.value(i).rawValue();
|
||||
data.append(static_cast<char>(tile));
|
||||
data.append(static_cast<char>(tile >> 8));
|
||||
}
|
||||
}
|
||||
metatiles_file.write(data);
|
||||
file.write(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tileset::loadMetatileAttributes() {
|
||||
QFile attrs_file(this->metatile_attrs_path);
|
||||
if (!attrs_file.open(QIODevice::ReadOnly)) {
|
||||
logError(QString("Could not open '%1' for reading: %2").arg(this->metatile_attrs_path).arg(attrs_file.errorString()));
|
||||
QFile file(this->metatile_attrs_path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
logError(QString("Could not open '%1' for reading: %2").arg(this->metatile_attrs_path).arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = attrs_file.readAll();
|
||||
QByteArray data = file.readAll();
|
||||
int attrSize = projectConfig.metatileAttributesSize;
|
||||
int numMetatiles = m_metatiles.length();
|
||||
int numMetatileAttrs = data.length() / attrSize;
|
||||
if (numMetatiles != numMetatileAttrs) {
|
||||
logWarn(QString("Metatile count %1 does not match metatile attribute count %2 in %3").arg(numMetatiles).arg(numMetatileAttrs).arg(this->name));
|
||||
if (numMetatileAttrs > numMetatiles) {
|
||||
logWarn(QString("%1 metatile attributes count %2 exceeds metatile count of %3. Additional attributes will be ignored.")
|
||||
.arg(this->name)
|
||||
.arg(numMetatileAttrs)
|
||||
.arg(numMetatiles));
|
||||
numMetatileAttrs = numMetatiles;
|
||||
} else if (numMetatileAttrs < numMetatiles) {
|
||||
logWarn(QString("%1 metatile attributes count %2 is fewer than the metatile count of %3. Missing attributes will default to 0.")
|
||||
.arg(this->name)
|
||||
.arg(numMetatileAttrs)
|
||||
.arg(numMetatiles));
|
||||
}
|
||||
|
||||
for (int i = 0; i < qMin(numMetatiles, numMetatileAttrs); i++) {
|
||||
for (int i = 0; i < numMetatileAttrs; i++) {
|
||||
uint32_t attributes = 0;
|
||||
for (int j = 0; j < attrSize; j++)
|
||||
attributes |= static_cast<unsigned char>(data.at(i * attrSize + j)) << (8 * j);
|
||||
@@ -468,9 +502,9 @@ bool Tileset::loadMetatileAttributes() {
|
||||
}
|
||||
|
||||
bool Tileset::saveMetatileAttributes() {
|
||||
QFile attrs_file(this->metatile_attrs_path);
|
||||
if (!attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
logError(QString("Could not open '%1' for writing: %2").arg(this->metatile_attrs_path).arg(attrs_file.errorString()));
|
||||
QFile file(this->metatile_attrs_path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
logError(QString("Could not open '%1' for writing: %2").arg(this->metatile_attrs_path).arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -480,21 +514,36 @@ bool Tileset::saveMetatileAttributes() {
|
||||
for (int i = 0; i < projectConfig.metatileAttributesSize; i++)
|
||||
data.append(static_cast<char>(attributes >> (8 * i)));
|
||||
}
|
||||
attrs_file.write(data);
|
||||
file.write(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tileset::loadTilesImage(QImage *importedImage) {
|
||||
QImage image;
|
||||
bool imported = false;
|
||||
if (importedImage) {
|
||||
image = *importedImage;
|
||||
m_hasUnsavedTilesImage = true;
|
||||
imported = true;
|
||||
} else if (QFile::exists(this->tilesImagePath)) {
|
||||
// No image provided, load from file path.
|
||||
image = QImage(this->tilesImagePath).convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither);
|
||||
} else {
|
||||
// Use default image
|
||||
image = QImage(8, 8, QImage::Format_Indexed8);
|
||||
}
|
||||
|
||||
if (image.isNull()) {
|
||||
logWarn(QString("Failed to load tiles image for %1. Using default tiles image.").arg(this->name));
|
||||
image = QImage(Tile::pixelWidth(), Tile::pixelHeight(), QImage::Format_Indexed8);
|
||||
image.fill(0);
|
||||
}
|
||||
|
||||
// Validate image dimensions
|
||||
if (image.width() % Tile::pixelWidth() || image.height() % Tile::pixelHeight()) {
|
||||
logError(QString("%1 tiles image has invalid dimensions %2x%3. Dimensions must be a multiple of %4x%5.")
|
||||
.arg(this->name)
|
||||
.arg(image.width())
|
||||
.arg(image.height())
|
||||
.arg(Tile::pixelWidth())
|
||||
.arg(Tile::pixelHeight()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate image contains 16 colors.
|
||||
@@ -508,17 +557,31 @@ bool Tileset::loadTilesImage(QImage *importedImage) {
|
||||
}
|
||||
image.setColorTable(colorTable);
|
||||
}
|
||||
m_tilesImage = image;
|
||||
|
||||
QList<QImage> tiles;
|
||||
int w = 8;
|
||||
int h = 8;
|
||||
for (int y = 0; y < image.height(); y += h)
|
||||
for (int x = 0; x < image.width(); x += w) {
|
||||
QImage tile = image.copy(x, y, w, h);
|
||||
tiles.append(tile);
|
||||
// Cut up the full tiles image into individual tile images.
|
||||
m_tiles.clear();
|
||||
for (int y = 0; y < image.height(); y += Tile::pixelHeight())
|
||||
for (int x = 0; x < image.width(); x += Tile::pixelWidth()) {
|
||||
m_tiles.append(image.copy(x, y, Tile::pixelWidth(), Tile::pixelHeight()));
|
||||
}
|
||||
this->tilesImage = image;
|
||||
this->tiles = tiles;
|
||||
|
||||
if (m_tiles.length() > maxTiles()) {
|
||||
logWarn(QString("%1 tile count of %2 exceeds limit of %3. Additional tiles will not be displayed.")
|
||||
.arg(this->name)
|
||||
.arg(m_tiles.length())
|
||||
.arg(maxTiles()));
|
||||
|
||||
// Just resize m_tiles so that numTiles() reports the correct tile count.
|
||||
// We'll leave m_tilesImage alone (it doesn't get displayed, and we don't want to delete the user's image data).
|
||||
m_tiles = m_tiles.mid(0, maxTiles());
|
||||
}
|
||||
|
||||
if (imported) {
|
||||
// Only set this flag once we've successfully loaded the tiles image.
|
||||
m_hasUnsavedTilesImage = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -528,7 +591,7 @@ bool Tileset::saveTilesImage() {
|
||||
if (!m_hasUnsavedTilesImage)
|
||||
return true;
|
||||
|
||||
if (!this->tilesImage.save(this->tilesImagePath, "PNG")) {
|
||||
if (!m_tilesImage.save(this->tilesImagePath, "PNG")) {
|
||||
logError(QString("Failed to save tiles image '%1'").arg(this->tilesImagePath));
|
||||
return false;
|
||||
}
|
||||
@@ -575,19 +638,23 @@ bool Tileset::savePalettes() {
|
||||
|
||||
bool Tileset::load() {
|
||||
bool success = true;
|
||||
if (!loadPalettes()) success = false;
|
||||
if (!loadTilesImage()) success = false;
|
||||
if (!loadMetatiles()) success = false;
|
||||
if (!loadMetatileAttributes()) success = false;
|
||||
if (!loadTilesImage()) success = false;
|
||||
if (!loadPalettes()) success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Because metatile labels are global (and handled by the project) we don't save them here.
|
||||
bool Tileset::save() {
|
||||
bool success = true;
|
||||
if (!savePalettes()) success = false;
|
||||
if (!saveTilesImage()) success = false;
|
||||
if (!saveMetatiles()) success = false;
|
||||
if (!saveMetatileAttributes()) success = false;
|
||||
if (!saveTilesImage()) success = false;
|
||||
if (!savePalettes()) success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
QString Tileset::stripPrefix(const QString &fullName) {
|
||||
return QString(fullName).replace(projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix), "");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ void Util::numericalModeSort(QStringList &list) {
|
||||
std::sort(list.begin(), list.end(), collator);
|
||||
}
|
||||
|
||||
int Util::roundUp(int numToRound, int multiple) {
|
||||
int Util::roundUpToMultiple(int numToRound, int multiple) {
|
||||
if (multiple <= 0)
|
||||
return numToRound;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Editor::Editor(Ui::MainWindow* ui)
|
||||
{
|
||||
this->ui = ui;
|
||||
this->settings = new Settings();
|
||||
this->cursorMapTileRect = new CursorTileRect(QSize(16,16), qRgb(255, 255, 255));
|
||||
this->cursorMapTileRect = new CursorTileRect(Metatile::pixelSize(), qRgb(255, 255, 255));
|
||||
this->map_ruler = new MapRuler(4);
|
||||
connect(this->map_ruler, &MapRuler::statusChanged, this, &Editor::mapRulerStatusChanged);
|
||||
|
||||
@@ -1171,13 +1171,13 @@ bool Editor::isMouseInMap() const {
|
||||
|
||||
void Editor::setPlayerViewRect(const QRectF &rect) {
|
||||
delete this->playerViewRect;
|
||||
this->playerViewRect = new MovableRect(rect, qRgb(255, 255, 255));
|
||||
this->playerViewRect = new MovableRect(rect, Metatile::pixelSize(), qRgb(255, 255, 255));
|
||||
updateCursorRectVisibility();
|
||||
}
|
||||
|
||||
void Editor::setCursorRectPos(const QPoint &pos) {
|
||||
int x = qMax(0, qMin(pos.x(), this->layout ? this->layout->getWidth() - 1 : 0));
|
||||
int y = qMax(0, qMin(pos.y(), this->layout ? this->layout->getHeight() - 1 : 0));
|
||||
int x = qBound(0, pos.x(), this->layout ? this->layout->getWidth() - 1 : 0);
|
||||
int y = qBound(0, pos.y(), this->layout ? this->layout->getHeight() - 1 : 0);
|
||||
|
||||
if (this->playerViewRect)
|
||||
this->playerViewRect->updateLocation(x, y);
|
||||
@@ -1357,12 +1357,13 @@ bool Editor::setLayout(QString layoutId) {
|
||||
map_ruler->setMapDimensions(QSize(this->layout->getWidth(), this->layout->getHeight()));
|
||||
connect(this->layout, &Layout::dimensionsChanged, map_ruler, &MapRuler::setMapDimensions);
|
||||
|
||||
ui->comboBox_PrimaryTileset->blockSignals(true);
|
||||
ui->comboBox_SecondaryTileset->blockSignals(true);
|
||||
QString prevPrimaryTileset = ui->comboBox_PrimaryTileset->currentText();
|
||||
QString prevSecondaryTileset = ui->comboBox_SecondaryTileset->currentText();
|
||||
|
||||
const QSignalBlocker b_PrimaryTilest(ui->comboBox_PrimaryTileset);
|
||||
const QSignalBlocker b_SecondaryTilest(ui->comboBox_SecondaryTileset);
|
||||
ui->comboBox_PrimaryTileset->setTextItem(this->layout->tileset_primary_label);
|
||||
ui->comboBox_SecondaryTileset->setTextItem(this->layout->tileset_secondary_label);
|
||||
ui->comboBox_PrimaryTileset->blockSignals(false);
|
||||
ui->comboBox_SecondaryTileset->blockSignals(false);
|
||||
|
||||
const QSignalBlocker b0(this->ui->comboBox_LayoutSelector);
|
||||
int index = this->ui->comboBox_LayoutSelector->findText(layoutId);
|
||||
@@ -1371,6 +1372,10 @@ bool Editor::setLayout(QString layoutId) {
|
||||
|
||||
if (this->layout->name != prevLayoutName)
|
||||
Scripting::cb_LayoutOpened(this->layout->name);
|
||||
if (this->layout->tileset_primary_label != prevPrimaryTileset)
|
||||
Scripting::cb_TilesetUpdated(this->layout->tileset_primary_label);
|
||||
if (this->layout->tileset_secondary_label != prevSecondaryTileset)
|
||||
Scripting::cb_TilesetUpdated(this->layout->tileset_secondary_label);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1641,7 +1646,7 @@ void Editor::displayMetatileSelector() {
|
||||
|
||||
scene_metatiles = new QGraphicsScene;
|
||||
if (!metatile_selector_item) {
|
||||
metatile_selector_item = new MetatileSelector(8, this->layout);
|
||||
metatile_selector_item = new MetatileSelector(projectConfig.metatileSelectorWidth, this->layout);
|
||||
connect(metatile_selector_item, &MetatileSelector::hoveredMetatileSelectionChanged,
|
||||
this, &Editor::onHoveredMetatileSelectionChanged);
|
||||
connect(metatile_selector_item, &MetatileSelector::hoveredMetatileSelectionCleared,
|
||||
@@ -1651,13 +1656,6 @@ void Editor::displayMetatileSelector() {
|
||||
metatile_selector_item->select(0);
|
||||
} else {
|
||||
metatile_selector_item->setLayout(this->layout);
|
||||
if (metatile_selector_item->primaryTileset
|
||||
&& metatile_selector_item->primaryTileset != this->layout->tileset_primary)
|
||||
emit tilesetUpdated(this->layout->tileset_primary->name);
|
||||
if (metatile_selector_item->secondaryTileset
|
||||
&& metatile_selector_item->secondaryTileset != this->layout->tileset_secondary)
|
||||
emit tilesetUpdated(this->layout->tileset_secondary->name);
|
||||
metatile_selector_item->setTilesets(this->layout->tileset_primary, this->layout->tileset_secondary);
|
||||
}
|
||||
|
||||
scene_metatiles->addItem(metatile_selector_item);
|
||||
@@ -1917,8 +1915,8 @@ void Editor::displayMapBorder() {
|
||||
for (int y = -borderMargins.top(); y < this->layout->getHeight() + borderMargins.bottom(); y += this->layout->getBorderHeight())
|
||||
for (int x = -borderMargins.left(); x < this->layout->getWidth() + borderMargins.right(); x += this->layout->getBorderWidth()) {
|
||||
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
|
||||
item->setX(x * 16);
|
||||
item->setY(y * 16);
|
||||
item->setX(x * Metatile::pixelWidth());
|
||||
item->setY(y * Metatile::pixelHeight());
|
||||
item->setZValue(ZValue::MapBorder);
|
||||
scene->addItem(item);
|
||||
borderItems.append(item);
|
||||
@@ -1966,8 +1964,8 @@ void Editor::displayMapGrid() {
|
||||
// elements of the scripting API, so they're painted manually in MapView::drawForeground.
|
||||
this->mapGrid = new QGraphicsItemGroup();
|
||||
|
||||
const int pixelMapWidth = this->layout->getWidth() * 16;
|
||||
const int pixelMapHeight = this->layout->getHeight() * 16;
|
||||
const int pixelMapWidth = this->layout->pixelWidth();
|
||||
const int pixelMapHeight = this->layout->pixelHeight();
|
||||
|
||||
// The grid can be moved with a user-specified x/y offset. The grid's dash patterns will only wrap in full pattern increments,
|
||||
// so we draw an additional row/column outside the map that can be revealed as the offset changes.
|
||||
@@ -2488,7 +2486,7 @@ void Editor::setCollisionGraphics() {
|
||||
|
||||
// Use the image sheet to create an icon for each collision/elevation combination.
|
||||
// Any icons for combinations that aren't provided by the image sheet are also created now using default graphics.
|
||||
const int w = 16, h = 16;
|
||||
const int w = Metatile::pixelWidth(), h = Metatile::pixelHeight();
|
||||
imgSheet = imgSheet.scaled(w * imgColumns, h * imgRows);
|
||||
for (int collision = 0; collision <= Block::getMaxCollision(); collision++) {
|
||||
// If (collision >= imgColumns) here, it's a valid collision value, but it is not represented with an icon on the image sheet.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
||||
QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
|
||||
|
||||
QApplication a(argc, argv);
|
||||
a.setStyle("fusion");
|
||||
|
||||
|
||||
@@ -367,7 +367,6 @@ void MainWindow::initEditor() {
|
||||
connect(this->editor, &Editor::currentMetatilesSelectionChanged, this, &MainWindow::currentMetatilesSelectionChanged);
|
||||
connect(this->editor, &Editor::wildMonTableEdited, [this] { markMapEdited(this->editor->map); });
|
||||
connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged);
|
||||
connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated);
|
||||
connect(this->editor, &Editor::editActionSet, this, &MainWindow::setEditActionUi);
|
||||
connect(ui->newEventToolButton, &NewEventToolButton::newEventAdded, this->editor, &Editor::addNewEvent);
|
||||
connect(ui->toolButton_deleteEvent, &QAbstractButton::clicked, this->editor, &Editor::deleteSelectedEvents);
|
||||
@@ -1409,7 +1408,7 @@ bool MainWindow::setProjectUI() {
|
||||
ui->newEventToolButton->setEventTypeVisible(Event::Type::SecretBase, projectConfig.eventSecretBaseEnabled);
|
||||
ui->newEventToolButton->setEventTypeVisible(Event::Type::CloneObject, projectConfig.eventCloneObjectEnabled);
|
||||
|
||||
this->editor->setPlayerViewRect(QRectF(0, 0, 16, 16).marginsAdded(projectConfig.playerViewDistance));
|
||||
this->editor->setPlayerViewRect(QRectF(QPoint(0,0), Metatile::pixelSize()).marginsAdded(projectConfig.playerViewDistance));
|
||||
|
||||
editor->setCollisionGraphics();
|
||||
ui->spinBox_SelectedElevation->setMaximum(Block::getMaxElevation());
|
||||
@@ -1810,7 +1809,7 @@ void MainWindow::scrollMetatileSelectorToSelection() {
|
||||
|
||||
QPoint pos = editor->metatile_selector_item->getMetatileIdCoordsOnWidget(selection.metatileItems.first().metatileId);
|
||||
QPoint size = editor->metatile_selector_item->getSelectionDimensions();
|
||||
pos += QPoint(size.x() - 1, size.y() - 1) * 16 / 2; // We want to focus on the center of the whole selection
|
||||
pos += QPoint((size.x() - 1) * Metatile::pixelWidth(), (size.y() - 1) * Metatile::pixelHeight()) / 2; // We want to focus on the center of the whole selection
|
||||
pos *= getMetatilesZoomScale();
|
||||
|
||||
auto viewport = ui->scrollArea_MetatileSelector->viewport();
|
||||
@@ -1821,7 +1820,9 @@ void MainWindow::currentMetatilesSelectionChanged() {
|
||||
redrawMetatileSelection();
|
||||
if (this->tilesetEditor) {
|
||||
MetatileSelection selection = editor->metatile_selector_item->getMetatileSelection();
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
if (!selection.metatileItems.isEmpty()) {
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't scroll to internal selections here, it will disrupt the user while they make their selection.
|
||||
@@ -2845,7 +2846,9 @@ void MainWindow::on_actionTileset_Editor_triggered()
|
||||
openSubWindow(this->tilesetEditor);
|
||||
|
||||
MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection();
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
if (!selection.metatileItems.isEmpty()) {
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::initTilesetEditor() {
|
||||
|
||||
@@ -1558,9 +1558,8 @@ Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboa
|
||||
tileset->loadTilesImage(&tilesImage);
|
||||
|
||||
// Create default metatiles
|
||||
const int numMetatiles = tileset->is_secondary ? (Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()) : Project::getNumMetatilesPrimary();
|
||||
const int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
for (int i = 0; i < numMetatiles; ++i) {
|
||||
for (int i = 0; i < tileset->maxMetatiles(); ++i) {
|
||||
auto metatile = new Metatile();
|
||||
for(int j = 0; j < tilesPerMetatile; ++j){
|
||||
Tile tile = Tile();
|
||||
@@ -1618,7 +1617,7 @@ Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboa
|
||||
metatilesFilepath.append(projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles));
|
||||
}
|
||||
ignoreWatchedFilesTemporarily({headersFilepath, graphicsFilepath, metatilesFilepath});
|
||||
name.remove(0, prefix.length()); // Strip prefix from name to get base name for use in other symbols.
|
||||
name = Tileset::stripPrefix(name);
|
||||
tileset->appendToHeaders(headersFilepath, name, this->usingAsmTilesets);
|
||||
tileset->appendToGraphics(graphicsFilepath, name, this->usingAsmTilesets);
|
||||
tileset->appendToMetatiles(metatilesFilepath, name, this->usingAsmTilesets);
|
||||
@@ -2400,12 +2399,11 @@ bool Project::readFieldmapProperties() {
|
||||
// We can determine whether triple-layer metatiles are in-use by reading this constant.
|
||||
// If the constant is missing (or is using a value other than 8 or 12) the user must tell
|
||||
// us whether they're using triple-layer metatiles under Project Settings.
|
||||
static const int numTilesPerLayer = 4;
|
||||
int numTilesPerMetatile = it.value();
|
||||
if (numTilesPerMetatile == 2 * numTilesPerLayer) {
|
||||
if (numTilesPerMetatile == 2 * Metatile::tilesPerLayer()) {
|
||||
projectConfig.tripleLayerMetatilesEnabled = false;
|
||||
this->disabledSettingsNames.insert(numTilesPerMetatileName);
|
||||
} else if (numTilesPerMetatile == 3 * numTilesPerLayer) {
|
||||
} else if (numTilesPerMetatile == 3 * Metatile::tilesPerLayer()) {
|
||||
projectConfig.tripleLayerMetatilesEnabled = true;
|
||||
this->disabledSettingsNames.insert(numTilesPerMetatileName);
|
||||
}
|
||||
@@ -2462,6 +2460,7 @@ bool Project::readFieldmapMasks() {
|
||||
projectConfig.blockCollisionMask = blockMask;
|
||||
if (readBlockMask(elevationMaskName, &blockMask))
|
||||
projectConfig.blockElevationMask = blockMask;
|
||||
Block::setLayout();
|
||||
|
||||
// Read RSE metatile attribute masks
|
||||
auto it = defines.find(behaviorMaskName);
|
||||
@@ -3444,14 +3443,25 @@ QString Project::getEmptySpeciesName() {
|
||||
return projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix) + projectConfig.getIdentifier(ProjectIdentifier::define_species_empty);
|
||||
}
|
||||
|
||||
// Get the distance in metatiles (rounded up) that the player is able to see in each direction in-game.
|
||||
// Get the distance in pixels that the player is able to see in each direction in-game,
|
||||
// rounded up to a multiple of a metatile's pixel size.
|
||||
QMargins Project::getPixelViewDistance() {
|
||||
QMargins viewDistance = projectConfig.playerViewDistance;
|
||||
viewDistance.setTop(Util::roundUpToMultiple(viewDistance.top(), Metatile::pixelHeight()));
|
||||
viewDistance.setBottom(Util::roundUpToMultiple(viewDistance.bottom(), Metatile::pixelHeight()));
|
||||
viewDistance.setLeft(Util::roundUpToMultiple(viewDistance.left(), Metatile::pixelWidth()));
|
||||
viewDistance.setRight(Util::roundUpToMultiple(viewDistance.right(), Metatile::pixelWidth()));
|
||||
return viewDistance;
|
||||
}
|
||||
|
||||
// Get the distance in metatiles that the player is able to see in each direction in-game.
|
||||
// For the default view distance (i.e. assuming the player is centered in a 240x160 pixel GBA screen) this is 7x5 metatiles.
|
||||
QMargins Project::getMetatileViewDistance() {
|
||||
QMargins viewDistance = projectConfig.playerViewDistance;
|
||||
viewDistance.setTop(qCeil(viewDistance.top() / 16.0));
|
||||
viewDistance.setBottom(qCeil(viewDistance.bottom() / 16.0));
|
||||
viewDistance.setLeft(qCeil(viewDistance.left() / 16.0));
|
||||
viewDistance.setRight(qCeil(viewDistance.right() / 16.0));
|
||||
QMargins viewDistance = getPixelViewDistance();
|
||||
viewDistance.setTop(viewDistance.top() / Metatile::pixelHeight());
|
||||
viewDistance.setBottom(viewDistance.bottom() / Metatile::pixelHeight());
|
||||
viewDistance.setLeft(viewDistance.left() / Metatile::pixelWidth());
|
||||
viewDistance.setRight(viewDistance.right() / Metatile::pixelWidth());
|
||||
return viewDistance;
|
||||
}
|
||||
|
||||
@@ -3481,15 +3491,14 @@ void Project::applyParsedLimits() {
|
||||
projectConfig.metatileEncounterTypeMask &= maxMask;
|
||||
projectConfig.metatileLayerTypeMask &= maxMask;
|
||||
|
||||
Block::setLayout();
|
||||
Metatile::setLayout(this);
|
||||
|
||||
Project::num_metatiles_primary = qMin(qMax(Project::num_metatiles_primary, 1), Block::getMaxMetatileId() + 1);
|
||||
Project::num_metatiles_primary = qBound(1, Project::num_metatiles_primary, Block::getMaxMetatileId() + 1);
|
||||
projectConfig.defaultMetatileId = qMin(projectConfig.defaultMetatileId, Block::getMaxMetatileId());
|
||||
projectConfig.defaultElevation = qMin(projectConfig.defaultElevation, Block::getMaxElevation());
|
||||
projectConfig.defaultCollision = qMin(projectConfig.defaultCollision, Block::getMaxCollision());
|
||||
projectConfig.collisionSheetSize.setHeight(qMin(qMax(projectConfig.collisionSheetSize.height(), 1), Block::getMaxElevation() + 1));
|
||||
projectConfig.collisionSheetSize.setWidth(qMin(qMax(projectConfig.collisionSheetSize.width(), 1), Block::getMaxCollision() + 1));
|
||||
projectConfig.collisionSheetSize.setHeight(qBound(1, projectConfig.collisionSheetSize.height(), Block::getMaxElevation() + 1));
|
||||
projectConfig.collisionSheetSize.setWidth(qBound(1, projectConfig.collisionSheetSize.width(), Block::getMaxCollision() + 1));
|
||||
}
|
||||
|
||||
bool Project::hasUnsavedChanges() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "editcommands.h"
|
||||
#include "config.h"
|
||||
#include "imageproviders.h"
|
||||
#include "scriptutility.h"
|
||||
|
||||
// TODO: "tilesetNeedsRedraw" is used when redrawing the map after
|
||||
// changing a metatile's tiles via script. It is unnecessarily
|
||||
@@ -26,7 +27,7 @@ void MainWindow::tryRedrawMapArea(bool forceRedraw) {
|
||||
if (this->tilesetEditor)
|
||||
this->tilesetEditor->updateTilesets(this->editor->layout->tileset_primary_label, this->editor->layout->tileset_secondary_label);
|
||||
if (this->editor->metatile_selector_item)
|
||||
this->editor->metatile_selector_item->draw();
|
||||
this->editor->metatile_selector_item->refresh();
|
||||
if (this->editor->selected_border_metatiles_item)
|
||||
this->editor->selected_border_metatiles_item->draw();
|
||||
if (this->editor->current_metatile_selection_item)
|
||||
@@ -332,7 +333,7 @@ void MainWindow::refreshAfterPaletteChange(Tileset *tileset) {
|
||||
if (this->tilesetEditor) {
|
||||
this->tilesetEditor->updateTilesets(this->editor->layout->tileset_primary_label, this->editor->layout->tileset_secondary_label);
|
||||
}
|
||||
this->editor->metatile_selector_item->draw();
|
||||
this->editor->metatile_selector_item->refresh();
|
||||
this->editor->selected_border_metatiles_item->draw();
|
||||
this->editor->map_item->draw(true);
|
||||
this->editor->updateMapBorder();
|
||||
@@ -444,7 +445,7 @@ QJSValue MainWindow::getSecondaryTilesetPalettes() {
|
||||
}
|
||||
|
||||
void MainWindow::refreshAfterPalettePreviewChange() {
|
||||
this->editor->metatile_selector_item->draw();
|
||||
this->editor->metatile_selector_item->refresh();
|
||||
this->editor->selected_border_metatiles_item->draw();
|
||||
this->editor->map_item->draw(true);
|
||||
this->editor->updateMapBorder();
|
||||
@@ -545,13 +546,13 @@ int MainWindow::getNumSecondaryTilesetMetatiles() {
|
||||
int MainWindow::getNumPrimaryTilesetTiles() {
|
||||
if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary)
|
||||
return 0;
|
||||
return this->editor->layout->tileset_primary->tiles.length();
|
||||
return this->editor->layout->tileset_primary->numTiles();
|
||||
}
|
||||
|
||||
int MainWindow::getNumSecondaryTilesetTiles() {
|
||||
if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary)
|
||||
return 0;
|
||||
return this->editor->layout->tileset_secondary->tiles.length();
|
||||
return this->editor->layout->tileset_secondary->numTiles();
|
||||
}
|
||||
|
||||
QString MainWindow::getPrimaryTileset() {
|
||||
@@ -796,17 +797,46 @@ void MainWindow::setMetatileTile(int metatileId, int tileIndex, QJSValue tileObj
|
||||
QJSValue MainWindow::getTilePixels(int tileId) {
|
||||
if (tileId < 0 || !this->editor || !this->editor->layout)
|
||||
return QJSValue();
|
||||
|
||||
const int numPixels = Tile::pixelWidth() * Tile::pixelHeight();
|
||||
QImage tileImage = getTileImage(tileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
|
||||
if (tileImage.isNull() || tileImage.sizeInBytes() < 64)
|
||||
if (tileImage.isNull() || tileImage.sizeInBytes() < numPixels)
|
||||
return QJSValue();
|
||||
|
||||
const uchar * pixels = tileImage.constBits();
|
||||
QJSValue pixelArray = Scripting::getEngine()->newArray(64);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
QJSValue pixelArray = Scripting::getEngine()->newArray(numPixels);
|
||||
for (int i = 0; i < numPixels; i++) {
|
||||
pixelArray.setProperty(i, pixels[i]);
|
||||
}
|
||||
return pixelArray;
|
||||
}
|
||||
|
||||
QList<int> MainWindow::getMetatileLayerOrder() const {
|
||||
if (!this->editor || !this->editor->layout)
|
||||
return QList<int>();
|
||||
return this->editor->layout->metatileLayerOrder();
|
||||
}
|
||||
|
||||
void MainWindow::setMetatileLayerOrder(const QList<int> &order) {
|
||||
if (!this->editor || !this->editor->layout || !ScriptUtility::validateMetatileLayerOrder(order))
|
||||
return;
|
||||
this->editor->layout->setMetatileLayerOrder(order);
|
||||
this->refreshAfterPalettePreviewChange();
|
||||
}
|
||||
|
||||
QList<float> MainWindow::getMetatileLayerOpacity() const {
|
||||
if (!this->editor || !this->editor->layout)
|
||||
return QList<float>();
|
||||
return this->editor->layout->metatileLayerOpacity();
|
||||
}
|
||||
|
||||
void MainWindow::setMetatileLayerOpacity(const QList<float> &opacities) {
|
||||
if (!this->editor || !this->editor->layout)
|
||||
return;
|
||||
this->editor->layout->setMetatileLayerOpacity(opacities);
|
||||
this->refreshAfterPalettePreviewChange();
|
||||
}
|
||||
|
||||
//=====================
|
||||
// Editing map header
|
||||
//=====================
|
||||
|
||||
@@ -294,11 +294,7 @@ void MapView::addTileImage(int x, int y, const Tile &tile, bool setTransparency,
|
||||
void MapView::addMetatileImage(int x, int y, int metatileId, bool setTransparency, int layer) {
|
||||
if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary || !this->editor->layout->tileset_secondary)
|
||||
return;
|
||||
QImage image = getMetatileImage(static_cast<uint16_t>(metatileId),
|
||||
this->editor->layout->tileset_primary,
|
||||
this->editor->layout->tileset_secondary,
|
||||
this->editor->layout->metatileLayerOrder,
|
||||
this->editor->layout->metatileLayerOpacity);
|
||||
QImage image = getMetatileImage(static_cast<uint16_t>(metatileId), this->editor->layout);
|
||||
if (setTransparency)
|
||||
image.setColor(0, qRgba(0, 0, 0, 0));
|
||||
if (this->getOverlay(layer)->addImage(x, y, image))
|
||||
|
||||
@@ -201,46 +201,36 @@ QList<QString> ScriptUtility::getCustomScripts() {
|
||||
}
|
||||
|
||||
QList<int> ScriptUtility::getMetatileLayerOrder() {
|
||||
if (!window || !window->editor || !window->editor->layout)
|
||||
return QList<int>();
|
||||
return window->editor->layout->metatileLayerOrder;
|
||||
return Layout::globalMetatileLayerOrder();
|
||||
}
|
||||
|
||||
void ScriptUtility::setMetatileLayerOrder(QList<int> order) {
|
||||
if (!window || !window->editor || !window->editor->layout)
|
||||
return;
|
||||
|
||||
bool ScriptUtility::validateMetatileLayerOrder(const QList<int> &order) {
|
||||
const int numLayers = 3;
|
||||
int size = order.size();
|
||||
if (size < numLayers) {
|
||||
logError(QString("Metatile layer order has insufficient elements (%1), needs at least %2.").arg(size).arg(numLayers));
|
||||
return;
|
||||
}
|
||||
bool invalid = false;
|
||||
for (int i = 0; i < numLayers; i++) {
|
||||
bool valid = true;
|
||||
for (int i = 0; i < qMin(order.length(), numLayers); i++) {
|
||||
int layer = order.at(i);
|
||||
if (layer < 0 || layer >= numLayers) {
|
||||
logError(QString("'%1' is not a valid metatile layer order value, must be in range 0-%2.").arg(layer).arg(numLayers - 1));
|
||||
invalid = true;
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (invalid) return;
|
||||
return valid;
|
||||
}
|
||||
|
||||
window->editor->layout->metatileLayerOrder = order;
|
||||
window->refreshAfterPalettePreviewChange();
|
||||
void ScriptUtility::setMetatileLayerOrder(const QList<int> &order) {
|
||||
if (!validateMetatileLayerOrder(order))
|
||||
return;
|
||||
Layout::setGlobalMetatileLayerOrder(order);
|
||||
if (window) window->refreshAfterPalettePreviewChange();
|
||||
}
|
||||
|
||||
QList<float> ScriptUtility::getMetatileLayerOpacity() {
|
||||
if (!window || !window->editor || !window->editor->layout)
|
||||
return QList<float>();
|
||||
return window->editor->layout->metatileLayerOpacity;
|
||||
return Layout::globalMetatileLayerOpacity();
|
||||
}
|
||||
|
||||
void ScriptUtility::setMetatileLayerOpacity(QList<float> order) {
|
||||
if (!window || !window->editor || !window->editor->layout)
|
||||
return;
|
||||
window->editor->layout->metatileLayerOpacity = order;
|
||||
window->refreshAfterPalettePreviewChange();
|
||||
void ScriptUtility::setMetatileLayerOpacity(const QList<float> &opacities) {
|
||||
Layout::setGlobalMetatileLayerOpacity(opacities);
|
||||
if (window) window->refreshAfterPalettePreviewChange();
|
||||
}
|
||||
|
||||
QList<QString> ScriptUtility::getMapNames() {
|
||||
|
||||
@@ -99,15 +99,12 @@ void Scripting::populateGlobalObject(MainWindow *mainWindow) {
|
||||
constants.setProperty("version", version);
|
||||
|
||||
// Get basic tileset information
|
||||
int numTilesPrimary = Project::getNumTilesPrimary();
|
||||
int numMetatilesPrimary = Project::getNumMetatilesPrimary();
|
||||
int numPalettesPrimary = Project::getNumPalettesPrimary();
|
||||
constants.setProperty("max_primary_tiles", numTilesPrimary);
|
||||
constants.setProperty("max_secondary_tiles", Project::getNumTilesTotal() - numTilesPrimary);
|
||||
constants.setProperty("max_primary_metatiles", numMetatilesPrimary);
|
||||
constants.setProperty("max_secondary_metatiles", Project::getNumMetatilesTotal() - numMetatilesPrimary);
|
||||
constants.setProperty("num_primary_palettes", numPalettesPrimary);
|
||||
constants.setProperty("num_secondary_palettes", Project::getNumPalettesTotal() - numPalettesPrimary);
|
||||
constants.setProperty("max_primary_tiles", Project::getNumTilesPrimary());
|
||||
constants.setProperty("max_secondary_tiles", Project::getNumTilesSecondary());
|
||||
constants.setProperty("max_primary_metatiles", Project::getNumMetatilesPrimary());
|
||||
constants.setProperty("max_secondary_metatiles", Project::getNumMetatilesSecondary());
|
||||
constants.setProperty("num_primary_palettes", Project::getNumPalettesPrimary());
|
||||
constants.setProperty("num_secondary_palettes", Project::getNumPalettesSecondary());
|
||||
constants.setProperty("layers_per_metatile", projectConfig.getNumLayersInMetatile());
|
||||
constants.setProperty("tiles_per_metatile", projectConfig.getNumTilesInMetatile());
|
||||
|
||||
@@ -301,7 +298,7 @@ void Scripting::cb_MapShifted(int xDelta, int yDelta) {
|
||||
instance->invokeCallback(OnMapShifted, args);
|
||||
}
|
||||
|
||||
void Scripting::cb_TilesetUpdated(QString tilesetName) {
|
||||
void Scripting::cb_TilesetUpdated(const QString &tilesetName) {
|
||||
if (!instance) return;
|
||||
|
||||
QJSValueList args {
|
||||
|
||||
@@ -39,21 +39,15 @@ void BorderMetatilesPixmapItem::draw() {
|
||||
|
||||
int width = layout->getBorderWidth();
|
||||
int height = layout->getBorderHeight();
|
||||
QImage image(16 * width, 16 * height, QImage::Format_RGBA8888);
|
||||
QImage image(width * Metatile::pixelWidth(), height * Metatile::pixelHeight(), QImage::Format_RGBA8888);
|
||||
QPainter painter(&image);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
int x = i * 16;
|
||||
int y = j * 16;
|
||||
QImage metatile_image = getMetatileImage(
|
||||
layout->getBorderMetatileId(i, j),
|
||||
layout->tileset_primary,
|
||||
layout->tileset_secondary,
|
||||
layout->metatileLayerOrder,
|
||||
layout->metatileLayerOpacity);
|
||||
QPoint metatile_origin = QPoint(x, y);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
QImage metatile_image = getMetatileImage(layout->getBorderMetatileId(i, j), layout);
|
||||
int x = i * Metatile::pixelWidth();
|
||||
int y = j * Metatile::pixelHeight();
|
||||
painter.drawImage(x, y, metatile_image);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
src/ui/checkeredbgscene.cpp
Normal file
32
src/ui/checkeredbgscene.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "checkeredbgscene.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
void CheckeredBgScene::drawBackground(QPainter *painter, const QRectF &rect) {
|
||||
QRect r = rect.toRect();
|
||||
int w = this->gridSize.width();
|
||||
int h = this->gridSize.height();
|
||||
int xMin = r.left() - (r.left() % w) - w;
|
||||
int yMin = r.top() - (r.top() % h) - h;
|
||||
int xMax = r.right() - (r.right() % w) + w;
|
||||
int yMax = r.bottom() - (r.bottom() % h) + h;
|
||||
|
||||
// draw grid from top to bottom of scene
|
||||
QColor paintColor;
|
||||
for (int x = xMin; x <= xMax; x += w) {
|
||||
for (int y = yMin; y <= yMax; y += h) {
|
||||
if ((x/w + y/h) % 2) {
|
||||
if (this->validRect.contains(x, y))
|
||||
paintColor = QColor(132, 217, 165); // green light color
|
||||
else
|
||||
paintColor = 0xbcbcbc; // normal light color
|
||||
} else {
|
||||
if (this->validRect.contains(x, y))
|
||||
paintColor = QColor(76, 178, 121); // green dark color
|
||||
else
|
||||
paintColor = 0x969696; // normal dark color
|
||||
}
|
||||
painter->fillRect(QRect(x, y, w, h), paintColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,9 +100,9 @@ void ConnectionPixmapItem::updatePos() {
|
||||
void ConnectionPixmapItem::updateOrigin() {
|
||||
if (this->connection->isVertical()) {
|
||||
this->originX = 0;
|
||||
this->originY = this->connection->relativePos(true).y() * this->mHeight;
|
||||
this->originY = this->connection->relativePixelPos(true).y();
|
||||
} else if (this->connection->isHorizontal()) {
|
||||
this->originX = this->connection->relativePos(true).x() * this->mWidth;
|
||||
this->originX = this->connection->relativePixelPos(true).x();
|
||||
this->originY = 0;
|
||||
}
|
||||
updatePos();
|
||||
|
||||
@@ -3,26 +3,21 @@
|
||||
#include <QPainter>
|
||||
|
||||
QPixmap drawMetatileSelection(MetatileSelection selection, Layout *layout) {
|
||||
int width = selection.dimensions.x() * 16;
|
||||
int height = selection.dimensions.y() * 16;
|
||||
int width = selection.dimensions.x() * Metatile::pixelWidth();
|
||||
int height = selection.dimensions.y() * Metatile::pixelHeight();
|
||||
QImage image(width, height, QImage::Format_RGBA8888);
|
||||
image.fill(QColor(0, 0, 0, 0));
|
||||
QPainter painter(&image);
|
||||
|
||||
for (int i = 0; i < selection.dimensions.x(); i++) {
|
||||
for (int j = 0; j < selection.dimensions.y(); j++) {
|
||||
int x = i * 16;
|
||||
int y = j * 16;
|
||||
int x = i * Metatile::pixelWidth();
|
||||
int y = j * Metatile::pixelHeight();
|
||||
QPoint metatile_origin = QPoint(x, y);
|
||||
int index = j * selection.dimensions.x() + i;
|
||||
MetatileSelectionItem item = selection.metatileItems.at(index);
|
||||
if (item.enabled) {
|
||||
QImage metatile_image = getMetatileImage(
|
||||
item.metatileId,
|
||||
layout->tileset_primary,
|
||||
layout->tileset_secondary,
|
||||
layout->metatileLayerOrder,
|
||||
layout->metatileLayerOpacity);
|
||||
QImage metatile_image = getMetatileImage(item.metatileId, layout);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,6 @@ void CursorTileRect::updateLocation(int coordX, int coordY) {
|
||||
}
|
||||
}
|
||||
|
||||
this->setX(coordX * 16);
|
||||
this->setY(coordY * 16);
|
||||
this->setX(coordX * m_tileSize.width());
|
||||
this->setY(coordY * m_tileSize.height());
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ void MapView::drawForeground(QPainter *painter, const QRectF&) {
|
||||
painter->save();
|
||||
if (editor->layout) {
|
||||
// We're clipping here to hide parts of the grid that are outside the map.
|
||||
const QRectF mapRect(-0.5, -0.5, editor->layout->getWidth() * 16 + 1.5, editor->layout->getHeight() * 16 + 1.5);
|
||||
const QRectF mapRect(-0.5, -0.5, editor->layout->pixelWidth() + 1.5, editor->layout->pixelHeight() + 1.5);
|
||||
painter->setClipping(true);
|
||||
painter->setClipRect(mapRect);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "config.h"
|
||||
#include "imageproviders.h"
|
||||
#include "log.h"
|
||||
#include "editor.h"
|
||||
#include <QPainter>
|
||||
|
||||
@@ -9,10 +8,29 @@ QImage getCollisionMetatileImage(Block block) {
|
||||
}
|
||||
|
||||
QImage getCollisionMetatileImage(int collision, int elevation) {
|
||||
const QImage * image = Editor::collisionIcons.at(collision).at(elevation);
|
||||
const QImage * image = Editor::collisionIcons.value(collision).value(elevation);
|
||||
return image ? *image : QImage();
|
||||
}
|
||||
|
||||
QImage getMetatileImage(uint16_t metatileId, Layout *layout, bool useTruePalettes) {
|
||||
Metatile* metatile = Tileset::getMetatile(metatileId,
|
||||
layout ? layout->tileset_primary : nullptr,
|
||||
layout ? layout->tileset_secondary : nullptr);
|
||||
return getMetatileImage(metatile, layout, useTruePalettes);
|
||||
}
|
||||
|
||||
QImage getMetatileImage(Metatile *metatile, Layout *layout, bool useTruePalettes) {
|
||||
if (!layout) {
|
||||
return getMetatileImage(metatile, nullptr, nullptr, {}, {}, useTruePalettes);
|
||||
}
|
||||
return getMetatileImage(metatile,
|
||||
layout->tileset_primary,
|
||||
layout->tileset_secondary,
|
||||
layout->metatileLayerOrder(),
|
||||
layout->metatileLayerOpacity(),
|
||||
useTruePalettes);
|
||||
}
|
||||
|
||||
QImage getMetatileImage(
|
||||
uint16_t metatileId,
|
||||
Tileset *primaryTileset,
|
||||
@@ -21,13 +39,18 @@ QImage getMetatileImage(
|
||||
const QList<float> &layerOpacity,
|
||||
bool useTruePalettes)
|
||||
{
|
||||
Metatile* metatile = Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset);
|
||||
if (!metatile) {
|
||||
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
|
||||
metatile_image.fill(Qt::magenta);
|
||||
return metatile_image;
|
||||
}
|
||||
return getMetatileImage(metatile, primaryTileset, secondaryTileset, layerOrder, layerOpacity, useTruePalettes);
|
||||
return getMetatileImage(Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset),
|
||||
primaryTileset,
|
||||
secondaryTileset,
|
||||
layerOrder,
|
||||
layerOpacity,
|
||||
useTruePalettes);
|
||||
}
|
||||
|
||||
// The color to use when we want to show some portion of the image request was invalid.
|
||||
// Normally this is Qt::magenta, but we'll use Qt::transparent if we think the image allows it.
|
||||
QColor getInvalidImageColor() {
|
||||
return (projectConfig.transparencyColor == QColor(Qt::transparent)) ? QColor(Qt::transparent) : QColor(Qt::magenta);
|
||||
}
|
||||
|
||||
QImage getMetatileImage(
|
||||
@@ -38,10 +61,10 @@ QImage getMetatileImage(
|
||||
const QList<float> &layerOpacity,
|
||||
bool useTruePalettes)
|
||||
{
|
||||
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
|
||||
QImage metatileImage(Metatile::pixelSize(), QImage::Format_RGBA8888);
|
||||
if (!metatile) {
|
||||
metatile_image.fill(Qt::magenta);
|
||||
return metatile_image;
|
||||
metatileImage.fill(getInvalidImageColor());
|
||||
return metatileImage;
|
||||
}
|
||||
|
||||
QList<QList<QRgb>> palettes = Tileset::getBlockPalettes(primaryTileset, secondaryTileset, useTruePalettes);
|
||||
@@ -50,22 +73,20 @@ QImage getMetatileImage(
|
||||
// tile pixels line up across layers we will still have something to render.
|
||||
// The GBA renders transparent pixels using palette 0 color 0. We have this color,
|
||||
// but all 3 games actually overwrite it with black when loading the tileset palettes,
|
||||
// so we have a setting to choose between these two behaviors.
|
||||
metatile_image.fill(projectConfig.setTransparentPixelsBlack ? QColor("black") : QColor(palettes.value(0).value(0)));
|
||||
// so we have a setting to specify an override transparency color.
|
||||
metatileImage.fill(projectConfig.transparencyColor.isValid() ? projectConfig.transparencyColor : QColor(palettes.value(0).value(0)));
|
||||
|
||||
QPainter painter(&metatileImage);
|
||||
|
||||
QPainter metatile_painter(&metatile_image);
|
||||
const int numLayers = 3; // When rendering, metatiles always have 3 layers
|
||||
uint32_t layerType = metatile->layerType();
|
||||
for (int layer = 0; layer < numLayers; layer++)
|
||||
for (int y = 0; y < 2; y++)
|
||||
for (int x = 0; x < 2; x++) {
|
||||
int l = layerOrder.size() >= numLayers ? layerOrder[layer] : layer;
|
||||
|
||||
for (const auto &layer : layerOrder)
|
||||
for (int y = 0; y < Metatile::tileHeight(); y++)
|
||||
for (int x = 0; x < Metatile::tileWidth(); x++) {
|
||||
// Get the tile to render next
|
||||
Tile tile;
|
||||
int tileOffset = (y * 2) + x;
|
||||
int tileOffset = (y * Metatile::tileWidth()) + x;
|
||||
if (projectConfig.tripleLayerMetatilesEnabled) {
|
||||
tile = metatile->tiles.value(tileOffset + (l * 4));
|
||||
tile = metatile->tiles.value(tileOffset + (layer * Metatile::tilesPerLayer()));
|
||||
} else {
|
||||
// "Vanilla" metatiles only have 8 tiles, but render 12.
|
||||
// The remaining 4 tiles are rendered using user-specified tiles depending on layer type.
|
||||
@@ -73,88 +94,71 @@ QImage getMetatileImage(
|
||||
{
|
||||
default:
|
||||
case Metatile::LayerType::Normal:
|
||||
if (l == 0)
|
||||
if (layer == 0)
|
||||
tile = Tile(projectConfig.unusedTileNormal);
|
||||
else // Tiles are on layers 1 and 2
|
||||
tile = metatile->tiles.value(tileOffset + ((l - 1) * 4));
|
||||
tile = metatile->tiles.value(tileOffset + ((layer - 1) * Metatile::tilesPerLayer()));
|
||||
break;
|
||||
case Metatile::LayerType::Covered:
|
||||
if (l == 2)
|
||||
if (layer == 2)
|
||||
tile = Tile(projectConfig.unusedTileCovered);
|
||||
else // Tiles are on layers 0 and 1
|
||||
tile = metatile->tiles.value(tileOffset + (l * 4));
|
||||
tile = metatile->tiles.value(tileOffset + (layer * Metatile::tilesPerLayer()));
|
||||
break;
|
||||
case Metatile::LayerType::Split:
|
||||
if (l == 1)
|
||||
if (layer == 1)
|
||||
tile = Tile(projectConfig.unusedTileSplit);
|
||||
else // Tiles are on layers 0 and 2
|
||||
tile = metatile->tiles.value(tileOffset + ((l == 0 ? 0 : 1) * 4));
|
||||
tile = metatile->tiles.value(tileOffset + ((layer == 0 ? 0 : 1) * Metatile::tilesPerLayer()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QImage tile_image = getTileImage(tile.tileId, primaryTileset, secondaryTileset);
|
||||
if (tile_image.isNull()) {
|
||||
// Some metatiles specify tiles that are outside the valid range.
|
||||
// The way the GBA will render these depends on what's in memory (which Porymap can't know)
|
||||
// so we treat them as if they were transparent.
|
||||
continue;
|
||||
}
|
||||
QImage tileImage = getColoredTileImage(tile.tileId, primaryTileset, secondaryTileset, palettes.value(tile.palette));
|
||||
|
||||
// Colorize the metatile tiles with its palette.
|
||||
if (tile.palette < palettes.length()) {
|
||||
const QList<QRgb> palette = palettes.value(tile.palette);
|
||||
for (int j = 0; j < palette.length(); j++) {
|
||||
tile_image.setColor(j, palette.value(j));
|
||||
}
|
||||
} else {
|
||||
logWarn(QString("Tile '%1' is referring to invalid palette number: '%2'").arg(tile.tileId).arg(tile.palette));
|
||||
}
|
||||
|
||||
QPoint origin = QPoint(x*8, y*8);
|
||||
float opacity = layerOpacity.size() >= numLayers ? layerOpacity[l] : 1.0;
|
||||
float opacity = layerOpacity.value(layer, 1.0);
|
||||
if (opacity < 1.0) {
|
||||
int alpha = 255 * opacity;
|
||||
for (int c = 0; c < tile_image.colorCount(); c++) {
|
||||
QColor color(tile_image.color(c));
|
||||
for (int c = 0; c < tileImage.colorCount(); c++) {
|
||||
QColor color(tileImage.color(c));
|
||||
color.setAlpha(alpha);
|
||||
tile_image.setColor(c, color.rgba());
|
||||
tileImage.setColor(c, color.rgba());
|
||||
}
|
||||
}
|
||||
|
||||
// Color 0 is displayed as transparent.
|
||||
QColor color(tile_image.color(0));
|
||||
color.setAlpha(0);
|
||||
tile_image.setColor(0, color.rgba());
|
||||
tile.flip(&tile_image);
|
||||
metatile_painter.drawImage(origin, tile_image);
|
||||
}
|
||||
metatile_painter.end();
|
||||
if (tileImage.colorCount()) {
|
||||
QColor color(tileImage.color(0));
|
||||
color.setAlpha(0);
|
||||
tileImage.setColor(0, color.rgba());
|
||||
}
|
||||
|
||||
return metatile_image;
|
||||
tile.flip(&tileImage);
|
||||
painter.drawImage(x * Tile::pixelWidth(), y * Tile::pixelHeight(), tileImage);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
return metatileImage;
|
||||
}
|
||||
|
||||
QImage getTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
Tileset *tileset = Tileset::getTileTileset(tileId, primaryTileset, secondaryTileset);
|
||||
int index = Tile::getIndexInTileset(tileId);
|
||||
if (!tileset) {
|
||||
return QImage();
|
||||
}
|
||||
return tileset->tiles.value(index, QImage());
|
||||
return tileset ? tileset->tileImage(tileId) : QImage();
|
||||
}
|
||||
|
||||
QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList<QRgb> &palette) {
|
||||
QImage tileImage = getTileImage(tileId, primaryTileset, secondaryTileset);
|
||||
if (tileImage.isNull()) {
|
||||
tileImage = QImage(8, 8, QImage::Format_RGBA8888);
|
||||
QPainter painter(&tileImage);
|
||||
painter.fillRect(0, 0, 8, 8, palette.at(0));
|
||||
// Some tiles specify tile IDs or palette IDs that are outside the valid range.
|
||||
// The way the GBA will render these depends on what's in memory (which Porymap can't know)
|
||||
// so we render them using the invalid color
|
||||
tileImage = QImage(Tile::pixelSize(), QImage::Format_RGBA8888);
|
||||
tileImage.fill(getInvalidImageColor());
|
||||
} else {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tileImage.setColor(i, palette.at(i));
|
||||
tileImage.setColor(i, palette.value(i, getInvalidImageColor().rgb()));
|
||||
}
|
||||
}
|
||||
|
||||
return tileImage;
|
||||
}
|
||||
|
||||
@@ -175,3 +179,94 @@ void flattenTo4bppImage(QImage * image) {
|
||||
for (int i = 0; i < image->sizeInBytes(); i++, pixel++)
|
||||
*pixel %= 16;
|
||||
}
|
||||
|
||||
// Constructs a grid image of the metatiles in the specified ID range.
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
uint16_t metatileIdStart,
|
||||
uint16_t metatileIdEnd,
|
||||
int numMetatilesWide,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity,
|
||||
const QSize &metatileSize,
|
||||
bool useTruePalettes)
|
||||
{
|
||||
if (metatileIdEnd < metatileIdStart || numMetatilesWide <= 0)
|
||||
return QImage();
|
||||
|
||||
int numMetatilesToDraw = metatileIdEnd - metatileIdStart + 1;
|
||||
|
||||
// Round up image height for incomplete last row.
|
||||
int numMetatilesTall = Util::roundUpToMultiple(numMetatilesToDraw, numMetatilesWide) / numMetatilesWide;
|
||||
|
||||
QImage image(numMetatilesWide * metatileSize.width(), numMetatilesTall * metatileSize.height(), QImage::Format_RGBA8888);
|
||||
image.fill(getInvalidImageColor());
|
||||
|
||||
QPainter painter(&image);
|
||||
for (int i = 0; i < numMetatilesToDraw; i++) {
|
||||
uint16_t metatileId = i + metatileIdStart;
|
||||
QImage metatileImage = getMetatileImage(metatileId, primaryTileset, secondaryTileset, layerOrder, layerOpacity, useTruePalettes)
|
||||
.scaled(metatileSize);
|
||||
|
||||
int x = (i % numMetatilesWide) * metatileSize.width();
|
||||
int y = (i / numMetatilesWide) * metatileSize.height();
|
||||
painter.drawImage(x, y, metatileImage);
|
||||
}
|
||||
painter.end();
|
||||
return image;
|
||||
}
|
||||
|
||||
// Constructs a grid image of the metatiles in the primary and secondary tileset,
|
||||
// rounding as necessary to keep the two tilesets on separate rows.
|
||||
// The unused metatiles (if any) between the primary and secondary tilesets are skipped.
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
int numMetatilesWide,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity,
|
||||
const QSize &metatileSize,
|
||||
bool useTruePalettes)
|
||||
{
|
||||
auto createSheetImage = [=](uint16_t start, Tileset *tileset) {
|
||||
uint16_t end = start;
|
||||
if (tileset) {
|
||||
if (tileset->numMetatiles() == 0)
|
||||
return QImage();
|
||||
end += tileset->numMetatiles() - 1;
|
||||
}
|
||||
return getMetatileSheetImage(primaryTileset,
|
||||
secondaryTileset,
|
||||
start,
|
||||
end,
|
||||
numMetatilesWide,
|
||||
layerOrder,
|
||||
layerOpacity,
|
||||
metatileSize,
|
||||
useTruePalettes);
|
||||
};
|
||||
|
||||
QImage primaryImage = createSheetImage(0, primaryTileset);
|
||||
QImage secondaryImage = createSheetImage(Project::getNumMetatilesPrimary(), secondaryTileset);
|
||||
|
||||
QImage image(qMax(primaryImage.width(), secondaryImage.width()), primaryImage.height() + secondaryImage.height(), QImage::Format_RGBA8888);
|
||||
image.fill(getInvalidImageColor());
|
||||
|
||||
QPainter painter(&image);
|
||||
painter.drawImage(0, 0, primaryImage);
|
||||
painter.drawImage(0, primaryImage.height(), secondaryImage);
|
||||
painter.end();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
QImage getMetatileSheetImage(Layout *layout, int numMetatilesWide, bool useTruePalettes) {
|
||||
if (!layout)
|
||||
return QImage();
|
||||
return getMetatileSheetImage(layout->tileset_primary,
|
||||
layout->tileset_secondary,
|
||||
numMetatilesWide,
|
||||
layout->metatileLayerOrder(),
|
||||
layout->metatileLayerOpacity(),
|
||||
Metatile::pixelSize(),
|
||||
useTruePalettes);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ MapImageExporter::MapImageExporter(QWidget *parent, Project *project, Map *map,
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
ui->setupUi(this);
|
||||
|
||||
m_scene = new QGraphicsScene(this);
|
||||
m_scene = new CheckeredBgScene(QSize(8,8), this);
|
||||
m_preview = m_scene->addPixmap(QPixmap());
|
||||
ui->graphicsView_Preview->setScene(m_scene);
|
||||
|
||||
@@ -333,8 +333,8 @@ QGifImage* MapImageExporter::createTimelapseGifImage(QProgressDialog *progress)
|
||||
if (currentHistoryAppliesToFrame(step.historyStack) || step.historyStack->index() == step.initialStackIndex) {
|
||||
// Either this is relevant edit history, or it's the final frame (which is always rendered). Record the size of the map at this point.
|
||||
QMargins margins = getMargins(m_map);
|
||||
canvasSize = canvasSize.expandedTo(QSize(m_layout->getWidth() * 16 + margins.left() + margins.right(),
|
||||
m_layout->getHeight() * 16 + margins.top() + margins.bottom()));
|
||||
canvasSize = canvasSize.expandedTo(QSize(m_layout->pixelWidth() + margins.left() + margins.right(),
|
||||
m_layout->pixelHeight() + margins.top() + margins.bottom()));
|
||||
}
|
||||
if (step.historyStack->canUndo()){
|
||||
step.historyStack->undo();
|
||||
@@ -434,8 +434,8 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) {
|
||||
if (!connection->isCardinal()) continue;
|
||||
Map *connectedMap = connection->targetMap();
|
||||
if (!connectedMap) continue;
|
||||
QPoint pos = connection->relativePos();
|
||||
unvisited.append(StitchedMap{cur.x + (pos.x() * 16), cur.y + (pos.y() * 16), connectedMap});
|
||||
QPoint pos = connection->relativePixelPos();
|
||||
unvisited.append(StitchedMap{cur.x + pos.x(), cur.y + pos.y(), connectedMap});
|
||||
}
|
||||
}
|
||||
if (stitchedMaps.isEmpty())
|
||||
@@ -447,7 +447,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) {
|
||||
// Determine the overall dimensions of the stitched maps.
|
||||
QRect dimensions = QRect(0, 0, m_map->getWidth(), m_map->getHeight()) + getMargins(m_map);
|
||||
for (const StitchedMap &map : stitchedMaps) {
|
||||
dimensions |= (QRect(map.x, map.y, map.map->getWidth() * 16, map.map->getHeight() * 16) + getMargins(map.map));
|
||||
dimensions |= (QRect(map.x, map.y, map.map->pixelWidth(), map.map->pixelHeight()) + getMargins(map.map));
|
||||
}
|
||||
|
||||
QPixmap stitchedPixmap(dimensions.width(), dimensions.height());
|
||||
@@ -602,7 +602,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap() {
|
||||
QMargins MapImageExporter::getMargins(const Map *map) {
|
||||
QMargins margins;
|
||||
if (m_settings.showBorder) {
|
||||
margins = m_project->getMetatileViewDistance() * 16;
|
||||
margins = m_project->getPixelViewDistance();
|
||||
} else if (map && connectionsEnabled()) {
|
||||
for (const auto &connection : map->getConnections()) {
|
||||
const QString dir = connection->direction();
|
||||
@@ -612,10 +612,10 @@ QMargins MapImageExporter::getMargins(const Map *map) {
|
||||
if (!targetMap) continue;
|
||||
|
||||
QRect rect = targetMap->getConnectionRect(dir);
|
||||
if (dir == "up") margins.setTop(rect.height() * 16);
|
||||
else if (dir == "down") margins.setBottom(rect.height() * 16);
|
||||
else if (dir == "left") margins.setLeft(rect.width() * 16);
|
||||
else if (dir == "right") margins.setRight(rect.width() * 16);
|
||||
if (dir == "up") margins.setTop(rect.height());
|
||||
else if (dir == "down") margins.setBottom(rect.height());
|
||||
else if (dir == "left") margins.setLeft(rect.width());
|
||||
else if (dir == "right") margins.setRight(rect.width());
|
||||
}
|
||||
}
|
||||
if (m_settings.showGrid) {
|
||||
@@ -652,7 +652,7 @@ void MapImageExporter::paintBorder(QPainter *painter, Layout *layout) {
|
||||
// Skip border painting if it would be fully covered by the rest of the map
|
||||
if (layout->isWithinBounds(QRect(x, y, layout->getBorderWidth(), layout->getBorderHeight())))
|
||||
continue;
|
||||
painter->drawPixmap(x * 16, y * 16, layout->border_pixmap);
|
||||
painter->drawPixmap(x * Metatile::pixelWidth(), y * Metatile::pixelHeight(), layout->border_pixmap);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
@@ -665,7 +665,7 @@ void MapImageExporter::paintConnections(QPainter *painter, const Map *map) {
|
||||
for (const auto &connection : map->getConnections()) {
|
||||
if (!m_settings.showConnections.contains(connection->direction()))
|
||||
continue;
|
||||
painter->drawImage(connection->relativePos(true) * 16, connection->render().toImage());
|
||||
painter->drawImage(connection->relativePixelPos(true), connection->render().toImage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,12 +693,12 @@ void MapImageExporter::paintGrid(QPainter *painter, const Layout *layout) {
|
||||
if (!m_settings.showGrid)
|
||||
return;
|
||||
|
||||
int w = layout->getWidth() * 16;
|
||||
int h = layout->getHeight() * 16;
|
||||
for (int x = 0; x <= w; x += 16) {
|
||||
int w = layout->pixelWidth();
|
||||
int h = layout->pixelHeight();
|
||||
for (int x = 0; x <= w; x += Metatile::pixelWidth()) {
|
||||
painter->drawLine(x, 0, x, h);
|
||||
}
|
||||
for (int y = 0; y <= h; y += 16) {
|
||||
for (int y = 0; y <= h; y += Metatile::pixelHeight()) {
|
||||
painter->drawLine(0, y, w, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ QPainterPath MapRuler::shape() const {
|
||||
ruler.addRect(xRuler);
|
||||
ruler.addRect(yRuler);
|
||||
ruler = ruler.simplified();
|
||||
for (int x = 16; x < pixWidth(); x += 16)
|
||||
int w = Metatile::pixelWidth();
|
||||
int h = Metatile::pixelHeight();
|
||||
for (int x = w; x < pixWidth(); x += w)
|
||||
ruler.addRect(x, xRuler.y(), 0, thickness);
|
||||
for (int y = 16; y < pixHeight(); y += 16)
|
||||
for (int y = h; y < pixHeight(); y += h)
|
||||
ruler.addRect(yRuler.x(), y, thickness, 0);
|
||||
if (deltaX() && deltaY())
|
||||
ruler.addPolygon(QVector<QPointF>({ cornerTick.p1(), cornerTick.p2() }));
|
||||
@@ -131,7 +133,9 @@ QPoint MapRuler::snapToWithinBounds(QPoint pos) const {
|
||||
|
||||
void MapRuler::updateGeometry() {
|
||||
prepareGeometryChange();
|
||||
setPos(QPoint(left() * 16 + 8, top() * 16 + 8));
|
||||
int w = Metatile::pixelWidth();
|
||||
int h = Metatile::pixelHeight();
|
||||
setPos(QPoint(left() * w + w/2, top() * h + h/2));
|
||||
/* Determine what quadrant the end point is in relative to the anchor point. The anchor
|
||||
* point is the top-left corner of the metatile the ruler starts in, so a zero-length
|
||||
* ruler is considered to be in the bottom-right quadrant from the anchor point. */
|
||||
|
||||
364
src/ui/metatileimageexporter.cpp
Normal file
364
src/ui/metatileimageexporter.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#include "metatileimageexporter.h"
|
||||
#include "ui_metatileimageexporter.h"
|
||||
#include "filedialog.h"
|
||||
#include "imageproviders.h"
|
||||
#include "utility.h"
|
||||
#include "project.h"
|
||||
#include "metatile.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
MetatileImageExporter::MetatileImageExporter(QWidget *parent, Tileset *primaryTileset, Tileset *secondaryTileset, Settings *savedSettings) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MetatileImageExporter),
|
||||
m_primaryTileset(primaryTileset),
|
||||
m_secondaryTileset(secondaryTileset),
|
||||
m_savedSettings(savedSettings)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_transparencyButtons = {
|
||||
ui->radioButton_TransparencyNormal,
|
||||
ui->radioButton_TransparencyBlack,
|
||||
ui->radioButton_TransparencyFirst,
|
||||
};
|
||||
|
||||
m_scene = new CheckeredBgScene(QSize(8,8), this);
|
||||
m_preview = m_scene->addPixmap(QPixmap());
|
||||
ui->graphicsView_Preview->setScene(m_scene);
|
||||
|
||||
if (projectConfig.tripleLayerMetatilesEnabled) {
|
||||
// When triple-layer metatiles are enabled there is no unused layer,
|
||||
// so this setting becomes pointless. Disable it.
|
||||
ui->checkBox_Placeholders->setVisible(false);
|
||||
}
|
||||
|
||||
uint16_t maxMetatileId = Block::getMaxMetatileId();
|
||||
ui->spinBox_MetatileStart->setMaximum(maxMetatileId);
|
||||
ui->spinBox_MetatileEnd->setMaximum(maxMetatileId);
|
||||
ui->spinBox_WidthMetatiles->setRange(1, maxMetatileId);
|
||||
ui->spinBox_WidthPixels->setRange(1 * Metatile::pixelWidth(), maxMetatileId * Metatile::pixelWidth());
|
||||
|
||||
if (m_primaryTileset) {
|
||||
ui->comboBox_PrimaryTileset->setTextItem(m_primaryTileset->name);
|
||||
}
|
||||
if (m_secondaryTileset) {
|
||||
ui->comboBox_SecondaryTileset->setTextItem(m_secondaryTileset->name);
|
||||
}
|
||||
|
||||
if (m_savedSettings) {
|
||||
populate(*m_savedSettings);
|
||||
} else {
|
||||
populate({});
|
||||
}
|
||||
|
||||
connect(ui->listWidget_Layers, &ReorderableListWidget::itemChanged, this, &MetatileImageExporter::updatePreview);
|
||||
connect(ui->listWidget_Layers, &ReorderableListWidget::reordered, this, &MetatileImageExporter::updatePreview);
|
||||
|
||||
connect(ui->pushButton_Save, &QPushButton::pressed, [this] { saveImage(); });
|
||||
connect(ui->pushButton_Close, &QPushButton::pressed, this, &MetatileImageExporter::close);
|
||||
connect(ui->pushButton_Reset, &QPushButton::pressed, this, &MetatileImageExporter::reset);
|
||||
|
||||
connect(ui->spinBox_WidthMetatiles, &UIntSpinBox::valueChanged, this, &MetatileImageExporter::syncPixelWidth);
|
||||
connect(ui->spinBox_WidthMetatiles, &UIntSpinBox::valueChanged, this, &MetatileImageExporter::queuePreviewUpdate);
|
||||
connect(ui->spinBox_WidthMetatiles, &UIntSpinBox::editingFinished, this, &MetatileImageExporter::tryUpdatePreview);
|
||||
|
||||
connect(ui->spinBox_WidthPixels, &UIntSpinBox::valueChanged, this, &MetatileImageExporter::syncMetatileWidth);
|
||||
connect(ui->spinBox_WidthPixels, &UIntSpinBox::valueChanged, this, &MetatileImageExporter::queuePreviewUpdate);
|
||||
connect(ui->spinBox_WidthPixels, &UIntSpinBox::editingFinished, this, &MetatileImageExporter::syncPixelWidth); // Round pixel width to multiple of 16
|
||||
connect(ui->spinBox_WidthPixels, &UIntSpinBox::editingFinished, this, &MetatileImageExporter::tryUpdatePreview);
|
||||
|
||||
connect(ui->spinBox_MetatileStart, &UIntHexSpinBox::valueChanged, this, &MetatileImageExporter::validateMetatileEnd);
|
||||
connect(ui->spinBox_MetatileStart, &UIntHexSpinBox::valueChanged, this, &MetatileImageExporter::queuePreviewUpdate);
|
||||
connect(ui->spinBox_MetatileStart, &UIntHexSpinBox::editingFinished, this, &MetatileImageExporter::tryUpdatePreview);
|
||||
|
||||
connect(ui->spinBox_MetatileEnd, &UIntHexSpinBox::valueChanged, this, &MetatileImageExporter::validateMetatileStart);
|
||||
connect(ui->spinBox_MetatileEnd, &UIntHexSpinBox::valueChanged, this, &MetatileImageExporter::queuePreviewUpdate);
|
||||
connect(ui->spinBox_MetatileEnd, &UIntHexSpinBox::editingFinished, this, &MetatileImageExporter::tryUpdatePreview);
|
||||
|
||||
// If we used toggled instead of clicked we'd get two preview updates instead of one when the setting changes.
|
||||
connect(ui->radioButton_TransparencyNormal, &QRadioButton::clicked, this, &MetatileImageExporter::updatePreview);
|
||||
connect(ui->radioButton_TransparencyBlack, &QRadioButton::clicked, this, &MetatileImageExporter::updatePreview);
|
||||
connect(ui->radioButton_TransparencyFirst, &QRadioButton::clicked, this, &MetatileImageExporter::updatePreview);
|
||||
|
||||
connect(ui->checkBox_Placeholders, &QCheckBox::toggled, this, &MetatileImageExporter::updatePreview);
|
||||
connect(ui->checkBox_PrimaryTileset, &QCheckBox::toggled, this, &MetatileImageExporter::tryEnforceMetatileRange);
|
||||
connect(ui->checkBox_PrimaryTileset, &QCheckBox::toggled, this, &MetatileImageExporter::updatePreview);
|
||||
connect(ui->checkBox_SecondaryTileset, &QCheckBox::toggled, this, &MetatileImageExporter::tryEnforceMetatileRange);
|
||||
connect(ui->checkBox_SecondaryTileset, &QCheckBox::toggled, this, &MetatileImageExporter::updatePreview);
|
||||
|
||||
ui->graphicsView_Preview->setFocus();
|
||||
}
|
||||
|
||||
MetatileImageExporter::~MetatileImageExporter() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
// Allow the window to open before displaying the preview.
|
||||
// Metatile sheet image creation is generally quick, so this only
|
||||
// really matters so that the graphics view can adjust the scale properly.
|
||||
void MetatileImageExporter::showEvent(QShowEvent *event) {
|
||||
QDialog::showEvent(event);
|
||||
if (!event->spontaneous()) {
|
||||
QTimer::singleShot(0, this, &MetatileImageExporter::updatePreview);
|
||||
}
|
||||
}
|
||||
|
||||
void MetatileImageExporter::closeEvent(QCloseEvent *event) {
|
||||
if (m_savedSettings) {
|
||||
m_savedSettings->metatileStart = ui->spinBox_MetatileStart->value();
|
||||
m_savedSettings->metatileEnd = ui->spinBox_MetatileEnd->value();
|
||||
m_savedSettings->numMetatilesWide = ui->spinBox_WidthMetatiles->value();
|
||||
m_savedSettings->usePrimaryTileset = ui->checkBox_PrimaryTileset->isChecked();
|
||||
m_savedSettings->useSecondaryTileset = ui->checkBox_SecondaryTileset->isChecked();
|
||||
m_savedSettings->renderPlaceholders = ui->checkBox_Placeholders->isChecked();
|
||||
for (int i = 0; i < m_transparencyButtons.length(); i++) {
|
||||
if (m_transparencyButtons.at(i)->isChecked()) {
|
||||
m_savedSettings->transparencyMode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_savedSettings->layerOrder.clear();
|
||||
for (int i = 0; i < ui->listWidget_Layers->count(); i++) {
|
||||
auto item = ui->listWidget_Layers->item(i);
|
||||
int layerNum = item->data(Qt::UserRole).toInt();
|
||||
m_savedSettings->layerOrder[layerNum] = (item->checkState() == Qt::Checked);
|
||||
}
|
||||
}
|
||||
QDialog::closeEvent(event);
|
||||
}
|
||||
|
||||
void MetatileImageExporter::populate(const Settings &settings) {
|
||||
const QSignalBlocker b_MetatileStart(ui->spinBox_MetatileStart);
|
||||
ui->spinBox_MetatileStart->setValue(settings.metatileStart);
|
||||
|
||||
const QSignalBlocker b_MetatileEnd(ui->spinBox_MetatileStart);
|
||||
ui->spinBox_MetatileEnd->setValue(settings.metatileEnd);
|
||||
|
||||
const QSignalBlocker b_WidthMetatiles(ui->spinBox_MetatileStart);
|
||||
ui->spinBox_WidthMetatiles->setValue(settings.numMetatilesWide);
|
||||
|
||||
const QSignalBlocker b_WidthPixels(ui->spinBox_MetatileStart);
|
||||
ui->spinBox_WidthPixels->setValue(settings.numMetatilesWide * Metatile::pixelWidth());
|
||||
|
||||
const QSignalBlocker b_PrimaryTileset(ui->spinBox_MetatileStart);
|
||||
ui->checkBox_PrimaryTileset->setChecked(settings.usePrimaryTileset);
|
||||
|
||||
const QSignalBlocker b_SecondaryTileset(ui->spinBox_MetatileStart);
|
||||
ui->checkBox_SecondaryTileset->setChecked(settings.useSecondaryTileset);
|
||||
|
||||
const QSignalBlocker b_Placeholders(ui->spinBox_MetatileStart);
|
||||
ui->checkBox_Placeholders->setChecked(settings.renderPlaceholders);
|
||||
|
||||
if (m_transparencyButtons.value(settings.transparencyMode)) {
|
||||
auto button = m_transparencyButtons[settings.transparencyMode];
|
||||
const QSignalBlocker b_Transparency(button);
|
||||
button->setChecked(true);
|
||||
}
|
||||
|
||||
// Build layer list from settings
|
||||
const QSignalBlocker b_Layers(ui->listWidget_Layers);
|
||||
ui->listWidget_Layers->clear();
|
||||
for (auto it = settings.layerOrder.cbegin(); it != settings.layerOrder.cend(); it++) {
|
||||
int layerNum = it.key();
|
||||
bool enabled = it.value();
|
||||
|
||||
auto item = new QListWidgetItem(Metatile::getLayerName(layerNum));
|
||||
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren);
|
||||
item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setData(Qt::UserRole, layerNum); // Save the original index to identify the layer
|
||||
ui->listWidget_Layers->addItem(item);
|
||||
}
|
||||
// Don't give extra unnecessary space to the list
|
||||
ui->listWidget_Layers->setFixedHeight(ui->listWidget_Layers->sizeHintForRow(0) * ui->listWidget_Layers->count() + 4);
|
||||
|
||||
tryEnforceMetatileRange();
|
||||
}
|
||||
|
||||
void MetatileImageExporter::applySettings(const Settings &settings) {
|
||||
populate(settings);
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void MetatileImageExporter::reset() {
|
||||
applySettings({});
|
||||
}
|
||||
|
||||
QImage MetatileImageExporter::getImage() {
|
||||
tryUpdatePreview();
|
||||
return m_preview->pixmap().toImage();
|
||||
}
|
||||
|
||||
bool MetatileImageExporter::saveImage(QString filepath) {
|
||||
tryUpdatePreview();
|
||||
if (filepath.isEmpty()) {
|
||||
QString defaultFilepath = QString("%1/%2").arg(FileDialog::getDirectory()).arg(getDefaultFileName());
|
||||
filepath = FileDialog::getSaveFileName(this, windowTitle(), defaultFilepath, QStringLiteral("Image Files (*.png *.jpg *.bmp)"));
|
||||
if (filepath.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return m_preview->pixmap().save(filepath);
|
||||
}
|
||||
|
||||
QString MetatileImageExporter::getDefaultFileName() const {
|
||||
if (m_layerOrder.length() == 1) {
|
||||
// Exporting a metatile layer image is an expected use case for Porytiles, which expects certain file names.
|
||||
// We can make the process a little easier by setting the default file name to those expected file names.
|
||||
static const QStringList layerFilenames = { "bottom", "middle", "top" };
|
||||
return layerFilenames.at(m_layerOrder.constFirst()) + ".png";
|
||||
}
|
||||
|
||||
QString defaultFilename;
|
||||
if (ui->checkBox_PrimaryTileset->isChecked() && m_primaryTileset) {
|
||||
defaultFilename.append(QString("%1_").arg(Tileset::stripPrefix(m_primaryTileset->name)));
|
||||
}
|
||||
if (ui->checkBox_SecondaryTileset->isChecked() && m_secondaryTileset) {
|
||||
defaultFilename.append(QString("%1_").arg(Tileset::stripPrefix(m_secondaryTileset->name)));
|
||||
}
|
||||
if (!m_layerOrder.isEmpty() && m_layerOrder != QList<int>({0,1,2})) {
|
||||
for (int i = m_layerOrder.length() - 1; i >= 0; i--) {
|
||||
defaultFilename.append(Metatile::getLayerName(m_layerOrder.at(i)));
|
||||
}
|
||||
defaultFilename.append("_");
|
||||
}
|
||||
defaultFilename.append("Metatiles.png");
|
||||
return defaultFilename;
|
||||
}
|
||||
|
||||
void MetatileImageExporter::queuePreviewUpdate() {
|
||||
m_previewUpdateQueued = true;
|
||||
}
|
||||
|
||||
// For updating only when a change has been recorded.
|
||||
// Useful for something that might happen often, like an input widget losing focus.
|
||||
void MetatileImageExporter::tryUpdatePreview() {
|
||||
if (m_preview->pixmap().isNull() || m_previewUpdateQueued) {
|
||||
updatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
void MetatileImageExporter::updatePreview() {
|
||||
copyRenderSettings();
|
||||
|
||||
m_layerOrder.clear();
|
||||
for (int i = 0; i < ui->listWidget_Layers->count(); i++) {
|
||||
auto item = ui->listWidget_Layers->item(i);
|
||||
if (item->checkState() == Qt::Checked) {
|
||||
int layerNum = item->data(Qt::UserRole).toInt();
|
||||
m_layerOrder.prepend(qBound(0, layerNum, 2));
|
||||
}
|
||||
}
|
||||
|
||||
QImage previewImage;
|
||||
if (ui->checkBox_PrimaryTileset->isChecked() && ui->checkBox_SecondaryTileset->isChecked()) {
|
||||
// Special behavior to combine the two tilesets while skipping the unused region between tilesets.
|
||||
previewImage = getMetatileSheetImage(m_primaryTileset,
|
||||
m_secondaryTileset,
|
||||
ui->spinBox_WidthMetatiles->value(),
|
||||
m_layerOrder);
|
||||
} else {
|
||||
previewImage = getMetatileSheetImage(m_primaryTileset,
|
||||
m_secondaryTileset,
|
||||
ui->spinBox_MetatileStart->value(),
|
||||
ui->spinBox_MetatileEnd->value(),
|
||||
ui->spinBox_WidthMetatiles->value(),
|
||||
m_layerOrder);
|
||||
}
|
||||
|
||||
m_preview->setPixmap(QPixmap::fromImage(previewImage));
|
||||
m_scene->setSceneRect(m_scene->itemsBoundingRect());
|
||||
m_previewUpdateQueued = false;
|
||||
|
||||
restoreRenderSettings();
|
||||
}
|
||||
|
||||
void MetatileImageExporter::validateMetatileStart() {
|
||||
const QSignalBlocker b(ui->spinBox_MetatileStart);
|
||||
ui->spinBox_MetatileStart->setValue(qMin(ui->spinBox_MetatileStart->value(),
|
||||
ui->spinBox_MetatileEnd->value()));
|
||||
}
|
||||
|
||||
void MetatileImageExporter::validateMetatileEnd() {
|
||||
const QSignalBlocker b(ui->spinBox_MetatileEnd);
|
||||
ui->spinBox_MetatileEnd->setValue(qMax(ui->spinBox_MetatileStart->value(),
|
||||
ui->spinBox_MetatileEnd->value()));
|
||||
}
|
||||
|
||||
void MetatileImageExporter::updateMetatileRange() {
|
||||
uint16_t min;
|
||||
uint16_t max;
|
||||
if (ui->checkBox_PrimaryTileset->isChecked() && m_primaryTileset) {
|
||||
if (ui->checkBox_SecondaryTileset->isChecked() && m_secondaryTileset) {
|
||||
// Both tilesets enforced
|
||||
min = qMin(m_primaryTileset->firstMetatileId(), m_secondaryTileset->firstMetatileId());
|
||||
max = qMax(m_primaryTileset->lastMetatileId(), m_secondaryTileset->lastMetatileId());
|
||||
} else {
|
||||
// Primary enforced
|
||||
min = m_primaryTileset->firstMetatileId();
|
||||
max = m_primaryTileset->lastMetatileId();
|
||||
}
|
||||
} else if (ui->checkBox_SecondaryTileset->isChecked() && m_secondaryTileset) {
|
||||
// Secondary enforced
|
||||
min = m_secondaryTileset->firstMetatileId();
|
||||
max = m_secondaryTileset->lastMetatileId();
|
||||
} else {
|
||||
// No tilesets enforced
|
||||
return;
|
||||
}
|
||||
|
||||
const QSignalBlocker b_MetatileStart(ui->spinBox_MetatileStart);
|
||||
const QSignalBlocker b_MetatileEnd(ui->spinBox_MetatileEnd);
|
||||
ui->spinBox_MetatileStart->setValue(min);
|
||||
ui->spinBox_MetatileEnd->setValue(max);
|
||||
}
|
||||
|
||||
void MetatileImageExporter::tryEnforceMetatileRange() {
|
||||
// Users can either specify which tileset(s) to render, or specify a range of metatiles, but not both.
|
||||
if (ui->checkBox_PrimaryTileset->isChecked() || ui->checkBox_SecondaryTileset->isChecked()) {
|
||||
updateMetatileRange();
|
||||
ui->groupBox_MetatileRange->setDisabled(true);
|
||||
} else {
|
||||
ui->groupBox_MetatileRange->setDisabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MetatileImageExporter::syncPixelWidth() {
|
||||
const QSignalBlocker b(ui->spinBox_WidthPixels);
|
||||
ui->spinBox_WidthPixels->setValue(ui->spinBox_WidthMetatiles->value() * Metatile::pixelWidth());
|
||||
}
|
||||
|
||||
void MetatileImageExporter::syncMetatileWidth() {
|
||||
const QSignalBlocker b(ui->spinBox_WidthMetatiles);
|
||||
ui->spinBox_WidthMetatiles->setValue(Util::roundUpToMultiple(ui->spinBox_WidthPixels->value(), Metatile::pixelWidth()) / Metatile::pixelWidth());
|
||||
}
|
||||
|
||||
// These settings control some rendering behavior that make metatiles render accurately to their in-game appearance,
|
||||
// which may be undesirable when exporting metatile images for editing.
|
||||
// The settings are buried in getMetatileImage at the moment, to change them we'll temporarily overwrite them.
|
||||
void MetatileImageExporter::copyRenderSettings() {
|
||||
m_savedConfig.transparencyColor = projectConfig.transparencyColor;
|
||||
m_savedConfig.unusedTileNormal = projectConfig.unusedTileNormal;
|
||||
m_savedConfig.unusedTileCovered = projectConfig.unusedTileCovered;
|
||||
m_savedConfig.unusedTileSplit = projectConfig.unusedTileSplit;
|
||||
|
||||
if (ui->radioButton_TransparencyNormal->isChecked()) {
|
||||
projectConfig.transparencyColor = QColor(Qt::transparent);
|
||||
} else if (ui->radioButton_TransparencyBlack->isChecked()) {
|
||||
projectConfig.transparencyColor = QColor(Qt::black);
|
||||
} else {
|
||||
projectConfig.transparencyColor = QColor();
|
||||
}
|
||||
|
||||
if (!ui->checkBox_Placeholders->isChecked()) {
|
||||
projectConfig.unusedTileNormal = 0;
|
||||
projectConfig.unusedTileCovered = 0;
|
||||
projectConfig.unusedTileSplit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MetatileImageExporter::restoreRenderSettings() {
|
||||
projectConfig.transparencyColor = m_savedConfig.transparencyColor;
|
||||
projectConfig.unusedTileNormal = m_savedConfig.unusedTileNormal;
|
||||
projectConfig.unusedTileCovered = m_savedConfig.unusedTileCovered;
|
||||
projectConfig.unusedTileSplit = m_savedConfig.unusedTileSplit;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <QPainter>
|
||||
|
||||
MetatileLayersItem::MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset)
|
||||
: SelectablePixmapItem(16, 16, 2 * projectConfig.getNumLayersInMetatile(), 2),
|
||||
: SelectablePixmapItem(16, 16, Metatile::tileWidth() * projectConfig.getNumLayersInMetatile(), Metatile::tileHeight()),
|
||||
metatile(metatile),
|
||||
primaryTileset(primaryTileset),
|
||||
secondaryTileset(secondaryTileset)
|
||||
@@ -31,8 +31,8 @@ static const QList<QPoint> tilePositions = {
|
||||
|
||||
void MetatileLayersItem::draw() {
|
||||
const int numLayers = projectConfig.getNumLayersInMetatile();
|
||||
const int layerWidth = this->cellWidth * 2;
|
||||
const int layerHeight = this->cellHeight * 2;
|
||||
const int layerWidth = this->cellWidth * Metatile::tileWidth();
|
||||
const int layerHeight = this->cellHeight * Metatile::tileHeight();
|
||||
QPixmap pixmap(numLayers * layerWidth, layerHeight);
|
||||
QPainter painter(&pixmap);
|
||||
|
||||
@@ -129,7 +129,7 @@ void MetatileLayersItem::hoverMoveEvent(QGraphicsSceneHoverEvent * event) {
|
||||
if (tileIndex < 0 || tileIndex >= this->metatile->tiles.length())
|
||||
return;
|
||||
|
||||
emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex).tileId);
|
||||
emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex));
|
||||
}
|
||||
|
||||
void MetatileLayersItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
|
||||
@@ -148,6 +148,6 @@ void MetatileLayersItem::clearLastHoveredCoords() {
|
||||
QPoint MetatileLayersItem::getBoundedPos(const QPointF &pos) {
|
||||
int x = static_cast<int>(pos.x()) / this->cellWidth;
|
||||
int y = static_cast<int>(pos.y()) / this->cellHeight;
|
||||
return QPoint( qMax(0, qMin(x, this->maxSelectionWidth - 1)),
|
||||
qMax(0, qMin(y, this->maxSelectionHeight - 1)) );
|
||||
return QPoint(qBound(0, x, this->maxSelectionWidth - 1),
|
||||
qBound(0, y, this->maxSelectionHeight - 1));
|
||||
}
|
||||
|
||||
@@ -3,40 +3,20 @@
|
||||
#include "project.h"
|
||||
#include <QPainter>
|
||||
|
||||
QPoint MetatileSelector::getSelectionDimensions() {
|
||||
QPoint MetatileSelector::getSelectionDimensions() const {
|
||||
if (this->prefabSelection || this->externalSelection)
|
||||
return selection.dimensions;
|
||||
return SelectablePixmapItem::getSelectionDimensions();
|
||||
}
|
||||
|
||||
int MetatileSelector::numPrimaryMetatilesRounded() const {
|
||||
// We round up the number of primary metatiles to keep the tilesets on separate rows.
|
||||
return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide;
|
||||
if (!primaryTileset())
|
||||
return 0;
|
||||
return Util::roundUpToMultiple(primaryTileset()->numMetatiles(), this->numMetatilesWide);
|
||||
}
|
||||
|
||||
void MetatileSelector::updateBasePixmap() {
|
||||
int primaryLength = this->numPrimaryMetatilesRounded();
|
||||
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
|
||||
int height_ = length_ / this->numMetatilesWide;
|
||||
if (length_ % this->numMetatilesWide != 0) {
|
||||
height_++;
|
||||
}
|
||||
QImage image(this->numMetatilesWide * this->cellWidth, height_ * this->cellHeight, QImage::Format_RGBA8888);
|
||||
image.fill(Qt::magenta);
|
||||
QPainter painter(&image);
|
||||
for (int i = 0; i < length_; i++) {
|
||||
int tile = i;
|
||||
if (i >= primaryLength) {
|
||||
tile += Project::getNumMetatilesPrimary() - primaryLength;
|
||||
}
|
||||
QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset, layout->metatileLayerOrder, layout->metatileLayerOpacity);
|
||||
int map_y = i / this->numMetatilesWide;
|
||||
int map_x = i % this->numMetatilesWide;
|
||||
QPoint metatile_origin = QPoint(map_x * this->cellWidth, map_y * this->cellHeight);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
}
|
||||
painter.end();
|
||||
this->basePixmap = QPixmap::fromImage(image);
|
||||
this->basePixmap = QPixmap::fromImage(getMetatileSheetImage(this->layout, this->numMetatilesWide));
|
||||
}
|
||||
|
||||
void MetatileSelector::draw() {
|
||||
@@ -53,7 +33,11 @@ void MetatileSelector::drawSelection() {
|
||||
}
|
||||
|
||||
bool MetatileSelector::select(uint16_t metatileId) {
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) return false;
|
||||
bool ok;
|
||||
QPoint pos = metatileIdToPos(metatileId, &ok);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
this->externalSelection = false;
|
||||
this->prefabSelection = false;
|
||||
this->selection = MetatileSelection{
|
||||
@@ -62,8 +46,7 @@ bool MetatileSelector::select(uint16_t metatileId) {
|
||||
QList<MetatileSelectionItem>({MetatileSelectionItem{true, metatileId}}),
|
||||
QList<CollisionSelectionItem>(),
|
||||
};
|
||||
QPoint coords = this->getMetatileIdCoords(metatileId);
|
||||
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
|
||||
SelectablePixmapItem::select(pos);
|
||||
this->updateSelectedMetatiles();
|
||||
return true;
|
||||
}
|
||||
@@ -73,9 +56,8 @@ void MetatileSelector::selectFromMap(uint16_t metatileId, uint16_t collision, ui
|
||||
this->setExternalSelection(1, 1, {metatileId}, {movePermissions});
|
||||
}
|
||||
|
||||
void MetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
this->primaryTileset = primaryTileset;
|
||||
this->secondaryTileset = secondaryTileset;
|
||||
void MetatileSelector::setLayout(Layout *layout) {
|
||||
this->layout = layout;
|
||||
if (this->externalSelection)
|
||||
this->updateExternalSelectedMetatiles();
|
||||
else
|
||||
@@ -85,11 +67,11 @@ void MetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTi
|
||||
draw();
|
||||
}
|
||||
|
||||
MetatileSelection MetatileSelector::getMetatileSelection() {
|
||||
return selection;
|
||||
void MetatileSelector::refresh() {
|
||||
setLayout(this->layout);
|
||||
}
|
||||
|
||||
void MetatileSelector::setExternalSelection(int width, int height, QList<uint16_t> metatiles, QList<QPair<uint16_t, uint16_t>> collisions) {
|
||||
void MetatileSelector::setExternalSelection(int width, int height, const QList<uint16_t> &metatiles, const QList<QPair<uint16_t, uint16_t>> &collisions) {
|
||||
this->prefabSelection = false;
|
||||
this->externalSelection = true;
|
||||
this->externalSelectionWidth = width;
|
||||
@@ -99,18 +81,18 @@ void MetatileSelector::setExternalSelection(int width, int height, QList<uint16_
|
||||
this->selection.collisionItems.clear();
|
||||
this->selection.hasCollision = true;
|
||||
this->selection.dimensions = QPoint(width, height);
|
||||
for (int i = 0; i < metatiles.length(); i++) {
|
||||
auto collision = collisions.at(i);
|
||||
this->selection.collisionItems.append(CollisionSelectionItem{true, collision.first, collision.second});
|
||||
for (int i = 0; i < qMin(metatiles.length(), collisions.length()); i++) {
|
||||
uint16_t metatileId = metatiles.at(i);
|
||||
uint16_t collision = collisions.at(i).first;
|
||||
uint16_t elevation = collisions.at(i).second;
|
||||
this->selection.collisionItems.append(CollisionSelectionItem{true, collision, elevation});
|
||||
this->externalSelectedMetatiles.append(metatileId);
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
if (!this->layout->metatileIsValid(metatileId))
|
||||
metatileId = 0;
|
||||
this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId});
|
||||
}
|
||||
if (this->selection.metatileItems.length() == 1) {
|
||||
QPoint coords = this->getMetatileIdCoords(this->selection.metatileItems.first().metatileId);
|
||||
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
|
||||
SelectablePixmapItem::select(metatileIdToPos(this->selection.metatileItems.first().metatileId));
|
||||
}
|
||||
|
||||
this->draw();
|
||||
@@ -127,7 +109,9 @@ void MetatileSelector::setPrefabSelection(MetatileSelection selection) {
|
||||
}
|
||||
|
||||
bool MetatileSelector::positionIsValid(const QPoint &pos) const {
|
||||
return Tileset::metatileIsValid(getMetatileId(pos.x(), pos.y()), this->primaryTileset, this->secondaryTileset);
|
||||
bool ok;
|
||||
posToMetatileId(pos, &ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void MetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
@@ -168,8 +152,14 @@ void MetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
|
||||
}
|
||||
|
||||
void MetatileSelector::hoverChanged() {
|
||||
uint16_t metatileId = this->getMetatileId(this->cellPos.x(), this->cellPos.y());
|
||||
emit this->hoveredMetatileSelectionChanged(metatileId);
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(this->cellPos, &ok);
|
||||
if (ok) {
|
||||
emit this->hoveredMetatileSelectionChanged(metatileId);
|
||||
} else {
|
||||
emit this->hoveredMetatileSelectionCleared();
|
||||
this->cellPos = QPoint(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void MetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
|
||||
@@ -187,9 +177,7 @@ void MetatileSelector::updateSelectedMetatiles() {
|
||||
QPoint origin = this->getSelectionStart();
|
||||
for (int j = 0; j < this->selection.dimensions.y(); j++) {
|
||||
for (int i = 0; i < this->selection.dimensions.x(); i++) {
|
||||
uint16_t metatileId = this->getMetatileId(origin.x() + i, origin.y() + j);
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
metatileId = 0;
|
||||
uint16_t metatileId = posToMetatileId(origin.x() + i, origin.y() + j);
|
||||
this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId});
|
||||
}
|
||||
}
|
||||
@@ -201,43 +189,59 @@ void MetatileSelector::updateExternalSelectedMetatiles() {
|
||||
this->selection.dimensions = QPoint(this->externalSelectionWidth, this->externalSelectionHeight);
|
||||
for (int i = 0; i < this->externalSelectedMetatiles.count(); ++i) {
|
||||
uint16_t metatileId = this->externalSelectedMetatiles.at(i);
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
if (!this->layout->metatileIsValid(metatileId))
|
||||
metatileId = 0;
|
||||
this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId});
|
||||
}
|
||||
emit selectedMetatilesChanged();
|
||||
}
|
||||
|
||||
uint16_t MetatileSelector::getMetatileId(int x, int y) const {
|
||||
uint16_t MetatileSelector::posToMetatileId(const QPoint &pos, bool *ok) const {
|
||||
return posToMetatileId(pos.x(), pos.y(), ok);
|
||||
}
|
||||
|
||||
uint16_t MetatileSelector::posToMetatileId(int x, int y, bool *ok) const {
|
||||
if (ok) *ok = true;
|
||||
int index = y * this->numMetatilesWide + x;
|
||||
int numPrimary = this->numPrimaryMetatilesRounded();
|
||||
if (index < numPrimary) {
|
||||
return static_cast<uint16_t>(index);
|
||||
} else {
|
||||
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
{
|
||||
// Invalid metatile id.
|
||||
return QPoint(0, 0);
|
||||
uint16_t metatileId = static_cast<uint16_t>(index);
|
||||
if (primaryTileset() && primaryTileset()->contains(metatileId)) {
|
||||
return metatileId;
|
||||
}
|
||||
|
||||
int index = metatileId < Project::getNumMetatilesPrimary()
|
||||
? metatileId
|
||||
: metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded();
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
// There's some extra handling here because we round the tilesets to keep them on separate rows.
|
||||
// This means if the maximum number of primary metatiles is not divisible by the metatile width
|
||||
// then the metatiles we used to round the primary tileset would have the index of valid secondary metatiles.
|
||||
// These need to be ignored, or they'll appear to be duplicates of the subseqeunt secondary metatiles.
|
||||
int numPrimaryRounded = numPrimaryMetatilesRounded();
|
||||
int firstSecondaryRow = numPrimaryRounded / this->numMetatilesWide;
|
||||
metatileId = static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimaryRounded);
|
||||
if (secondaryTileset() && secondaryTileset()->contains(metatileId) && y >= firstSecondaryRow) {
|
||||
return metatileId;
|
||||
}
|
||||
|
||||
if (ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QPoint MetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) {
|
||||
QPoint pos = getMetatileIdCoords(metatileId);
|
||||
QPoint MetatileSelector::metatileIdToPos(uint16_t metatileId, bool *ok) const {
|
||||
if (primaryTileset() && primaryTileset()->contains(metatileId)) {
|
||||
if (ok) *ok = true;
|
||||
int index = metatileId;
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
}
|
||||
if (secondaryTileset() && secondaryTileset()->contains(metatileId)) {
|
||||
if (ok) *ok = true;
|
||||
int index = metatileId - Project::getNumMetatilesPrimary() + numPrimaryMetatilesRounded();
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
}
|
||||
|
||||
if (ok) *ok = false;
|
||||
return QPoint(0,0);
|
||||
}
|
||||
|
||||
QPoint MetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) const {
|
||||
QPoint pos = metatileIdToPos(metatileId);
|
||||
pos.rx() = (pos.x() * this->cellWidth) + (this->cellWidth / 2);
|
||||
pos.ry() = (pos.y() * this->cellHeight) + (this->cellHeight / 2);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void MetatileSelector::setLayout(Layout *layout) {
|
||||
this->layout = layout;
|
||||
}
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
#include "movablerect.h"
|
||||
#include "utility.h"
|
||||
|
||||
MovableRect::MovableRect(const QRectF &rect, const QRgb &color)
|
||||
MovableRect::MovableRect(const QRectF &rect, const QSize &cellSize, const QRgb &color)
|
||||
: QGraphicsRectItem(rect),
|
||||
baseRect(rect),
|
||||
cellSize(cellSize),
|
||||
color(color)
|
||||
{ }
|
||||
|
||||
/// Center rect on grid position (x, y)
|
||||
void MovableRect::updateLocation(int x, int y) {
|
||||
setRect(this->baseRect.x() + (x * 16),
|
||||
this->baseRect.y() + (y * 16),
|
||||
setRect(this->baseRect.x() + (x * this->cellSize.width()),
|
||||
this->baseRect.y() + (y * this->cellSize.height()),
|
||||
this->baseRect.width(),
|
||||
this->baseRect.height());
|
||||
}
|
||||
@@ -24,9 +25,9 @@ void MovableRect::updateLocation(int x, int y) {
|
||||
************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
ResizableRect::ResizableRect(QObject *parent, int width, int height, QRgb color)
|
||||
ResizableRect::ResizableRect(QObject *parent, const QSize &cellSize, const QSize &size, const QRgb &color)
|
||||
: QObject(parent),
|
||||
MovableRect(QRect(0, 0, width * 16, height * 16), color)
|
||||
MovableRect(QRect(0, 0, size.width(), size.height()), cellSize, color)
|
||||
{
|
||||
setAcceptHoverEvents(true);
|
||||
setFlags(this->flags() | QGraphicsItem::ItemIsMovable);
|
||||
@@ -114,8 +115,8 @@ void ResizableRect::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
}
|
||||
|
||||
void ResizableRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
|
||||
int dx = Util::roundUp(event->scenePos().x() - this->clickedPos.x(), 16);
|
||||
int dy = Util::roundUp(event->scenePos().y() - this->clickedPos.y(), 16);
|
||||
int dx = Util::roundUpToMultiple(event->scenePos().x() - this->clickedPos.x(), this->cellSize.width());
|
||||
int dy = Util::roundUpToMultiple(event->scenePos().y() - this->clickedPos.y(), this->cellSize.height());
|
||||
|
||||
QRect resizedRect = this->clickedRect;
|
||||
|
||||
@@ -149,20 +150,20 @@ void ResizableRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Lower limits: smallest possible size is 16x16 square
|
||||
if (resizedRect.width() < 16) {
|
||||
// Lower limits: smallest possible size is 1 cell
|
||||
if (resizedRect.width() < this->cellSize.width()) {
|
||||
if (dx < 0) { // right sided adjustment made
|
||||
resizedRect.setWidth(16);
|
||||
resizedRect.setWidth(this->cellSize.width());
|
||||
} else { // left sided adjustment slightly more complicated
|
||||
int dxMax = this->clickedRect.right() - this->clickedRect.left() - 16;
|
||||
int dxMax = this->clickedRect.right() - this->clickedRect.left() - this->cellSize.width();
|
||||
resizedRect.adjust(dxMax - dx, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
if (resizedRect.height() < 16) {
|
||||
if (resizedRect.height() < this->cellSize.height()) {
|
||||
if (dy < 0) { // bottom
|
||||
resizedRect.setHeight(16);
|
||||
resizedRect.setHeight(this->cellSize.height());
|
||||
} else { // top
|
||||
int dyMax = this->clickedRect.bottom() - this->clickedRect.top() - 16;
|
||||
int dyMax = this->clickedRect.bottom() - this->clickedRect.top() - this->cellSize.height();
|
||||
resizedRect.adjust(0, dyMax - dy, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ PrefabCreationDialog::PrefabCreationDialog(QWidget *parent, MetatileSelector *me
|
||||
|
||||
QObject::connect(this->ui->graphicsView_Prefab, &ClickableGraphicsView::clicked, [=](QMouseEvent *event){
|
||||
auto pos = event->pos();
|
||||
int selectionWidth = this->selection.dimensions.x() * 16;
|
||||
int selectionHeight = this->selection.dimensions.y() * 16;
|
||||
int selectionWidth = this->selection.dimensions.x() * Metatile::pixelWidth();
|
||||
int selectionHeight = this->selection.dimensions.y() * Metatile::pixelHeight();
|
||||
if (pos.x() < 0 || pos.x() >= selectionWidth || pos.y() < 0 || pos.y() >= selectionHeight)
|
||||
return;
|
||||
int metatileX = pos.x() / 16;
|
||||
int metatileY = pos.y() / 16;
|
||||
int metatileX = pos.x() / Metatile::pixelWidth();
|
||||
int metatileY = pos.y() / Metatile::pixelHeight();
|
||||
int index = metatileY * this->selection.dimensions.x() + metatileX;
|
||||
bool toggledState = !this->selection.metatileItems[index].enabled;
|
||||
this->selection.metatileItems[index].enabled = toggledState;
|
||||
|
||||
@@ -161,6 +161,7 @@ void ProjectSettingsEditor::initUi() {
|
||||
ui->spinBox_PlayerViewDistance_North->setMaximum(INT_MAX);
|
||||
ui->spinBox_PlayerViewDistance_East->setMaximum(INT_MAX);
|
||||
ui->spinBox_PlayerViewDistance_South->setMaximum(INT_MAX);
|
||||
ui->spinBox_MetatileSelectorWidth->setMaximum(maxMetatileId + 1);
|
||||
|
||||
// The values for some of the settings we provide in this window can be determined using constants in the user's projects.
|
||||
// If the user has these constants we disable these settings in the UI -- they can modify them using their constants.
|
||||
@@ -232,7 +233,7 @@ QList<uint16_t> ProjectSettingsEditor::getBorderMetatileIds(bool customSize) {
|
||||
// Custom border size, read metatiles from line edit
|
||||
for (auto s : ui->lineEdit_BorderMetatiles->text().split(",")) {
|
||||
uint16_t metatileId = s.toUInt(nullptr, 0);
|
||||
metatileIds.append(qMin(metatileId, static_cast<uint16_t>(Project::getNumMetatilesTotal() - 1)));
|
||||
metatileIds.append(qMin(metatileId, Block::getMaxMetatileId()));
|
||||
}
|
||||
} else {
|
||||
// Default border size, read metatiles from spin boxes
|
||||
@@ -475,7 +476,7 @@ void ProjectSettingsEditor::refresh() {
|
||||
ui->checkBox_PreserveMatchingOnlyData->setChecked(projectConfig.preserveMatchingOnlyData);
|
||||
|
||||
// Radio buttons
|
||||
if (projectConfig.setTransparentPixelsBlack)
|
||||
if (projectConfig.transparencyColor == QColor(Qt::black))
|
||||
ui->radioButton_RenderBlack->setChecked(true);
|
||||
else
|
||||
ui->radioButton_RenderFirstPalColor->setChecked(true);
|
||||
@@ -503,6 +504,7 @@ void ProjectSettingsEditor::refresh() {
|
||||
ui->spinBox_PlayerViewDistance_North->setValue(projectConfig.playerViewDistance.top());
|
||||
ui->spinBox_PlayerViewDistance_East->setValue(projectConfig.playerViewDistance.right());
|
||||
ui->spinBox_PlayerViewDistance_South->setValue(projectConfig.playerViewDistance.bottom());
|
||||
ui->spinBox_MetatileSelectorWidth->setValue(projectConfig.metatileSelectorWidth);
|
||||
|
||||
// Set (and sync) border metatile IDs
|
||||
this->setBorderMetatileIds(false, projectConfig.newMapBorderMetatileIds);
|
||||
@@ -574,7 +576,7 @@ void ProjectSettingsEditor::save() {
|
||||
projectConfig.tilesetsHaveCallback = ui->checkBox_OutputCallback->isChecked();
|
||||
projectConfig.tilesetsHaveIsCompressed = ui->checkBox_OutputIsCompressed->isChecked();
|
||||
porymapConfig.warpBehaviorWarningDisabled = ui->checkBox_DisableWarning->isChecked();
|
||||
projectConfig.setTransparentPixelsBlack = ui->radioButton_RenderBlack->isChecked();
|
||||
projectConfig.transparencyColor = ui->radioButton_RenderBlack->isChecked() ? QColor(Qt::black) : QColor();
|
||||
projectConfig.preserveMatchingOnlyData = ui->checkBox_PreserveMatchingOnlyData->isChecked();
|
||||
|
||||
// Save spin box settings
|
||||
@@ -598,6 +600,7 @@ void ProjectSettingsEditor::save() {
|
||||
ui->spinBox_PlayerViewDistance_North->value(),
|
||||
ui->spinBox_PlayerViewDistance_East->value(),
|
||||
ui->spinBox_PlayerViewDistance_South->value());
|
||||
projectConfig.metatileSelectorWidth = ui->spinBox_MetatileSelectorWidth->value();
|
||||
|
||||
// Save line edit settings
|
||||
projectConfig.prefabFilepath = ui->lineEdit_PrefabsPath->text();
|
||||
|
||||
@@ -7,42 +7,9 @@
|
||||
|
||||
#include "ui_resizelayoutpopup.h"
|
||||
|
||||
CheckeredBgScene::CheckeredBgScene(QObject *parent) : QGraphicsScene(parent) { }
|
||||
|
||||
void CheckeredBgScene::drawBackground(QPainter *painter, const QRectF &rect) {
|
||||
QRect r = rect.toRect();
|
||||
int xMin = r.left() - r.left() % this->gridSize - this->gridSize;
|
||||
int yMin = r.top() - r.top() % this->gridSize - this->gridSize;
|
||||
int xMax = r.right() - r.right() % this->gridSize + this->gridSize;
|
||||
int yMax = r.bottom() - r.bottom() % this->gridSize + this->gridSize;
|
||||
|
||||
// draw grid 16x16 from top to bottom of scene
|
||||
QColor paintColor(0x00ff00);
|
||||
for (int x = xMin, xTile = 0; x <= xMax; x += this->gridSize, xTile++) {
|
||||
for (int y = yMin, yTile = 0; y <= yMax; y += this->gridSize, yTile++) {
|
||||
if (!((xTile ^ yTile) & 1)) { // tile numbers have same parity (evenness)
|
||||
if (this->validRect.contains(x, y))
|
||||
paintColor = QColor(132, 217, 165); // green light color
|
||||
else
|
||||
paintColor = 0xbcbcbc; // normal light color
|
||||
}
|
||||
else {
|
||||
if (this->validRect.contains(x, y))
|
||||
paintColor = QColor(76, 178, 121); // green dark color
|
||||
else
|
||||
paintColor = 0x969696; // normal dark color
|
||||
}
|
||||
painter->fillRect(QRect(x, y, this->gridSize, this->gridSize), paintColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
BoundedPixmapItem::BoundedPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent) : QGraphicsPixmapItem(pixmap, parent) {
|
||||
BoundedPixmapItem::BoundedPixmapItem(const QPixmap &pixmap, const QSize &cellSize, QGraphicsItem *parent) : QGraphicsPixmapItem(pixmap, parent) {
|
||||
setFlags(this->flags() | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges | QGraphicsItem::ItemIsSelectable);
|
||||
this->cellSize = cellSize;
|
||||
}
|
||||
|
||||
void BoundedPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {
|
||||
@@ -61,7 +28,8 @@ void BoundedPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem
|
||||
QVariant BoundedPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) {
|
||||
if (change == ItemPositionChange && scene()) {
|
||||
QPointF newPos = value.toPointF();
|
||||
return QPointF(Util::roundUp(newPos.x(), 16), Util::roundUp(newPos.y(), 16));
|
||||
return QPointF(Util::roundUpToMultiple(newPos.x(), this->cellSize.width()),
|
||||
Util::roundUpToMultiple(newPos.y(), this->cellSize.height()));
|
||||
}
|
||||
else
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
@@ -83,7 +51,7 @@ ResizeLayoutPopup::ResizeLayoutPopup(QWidget *parent, Layout *layout, Project *p
|
||||
this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);
|
||||
this->setWindowModality(Qt::ApplicationModal);
|
||||
|
||||
this->scene = new CheckeredBgScene(this);
|
||||
this->scene = new CheckeredBgScene(Metatile::pixelSize(), this);
|
||||
this->ui->graphicsView->setScene(this->scene);
|
||||
this->ui->graphicsView->setRenderHints(QPainter::Antialiasing);
|
||||
this->ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||||
@@ -124,11 +92,13 @@ void ResizeLayoutPopup::setupLayoutView() {
|
||||
this->ui->spinBox_borderHeight->setValue(this->layout->getBorderHeight());
|
||||
|
||||
// Layout stuff
|
||||
this->layoutPixmap = new BoundedPixmapItem(this->layout->pixmap);
|
||||
this->layoutPixmap = new BoundedPixmapItem(this->layout->pixmap, Metatile::pixelSize());
|
||||
this->scene->addItem(layoutPixmap);
|
||||
int maxWidth = this->project->getMaxMapWidth();
|
||||
int maxHeight = this->project->getMaxMapHeight();
|
||||
QGraphicsRectItem *cover = new QGraphicsRectItem(-maxWidth * 8, -maxHeight * 8, maxWidth * 16, maxHeight * 16);
|
||||
int maxPixelWidth = maxWidth * Metatile::pixelWidth() * 2; // *2 to allow reaching max dimension by expanding from 0,0 in either direction
|
||||
int maxPixelHeight = maxHeight * Metatile::pixelHeight() * 2;
|
||||
QGraphicsRectItem *cover = new QGraphicsRectItem(-(maxPixelWidth / 2), -(maxPixelHeight / 2), maxPixelWidth, maxPixelHeight);
|
||||
this->scene->addItem(cover);
|
||||
|
||||
this->ui->spinBox_width->setMinimum(1);
|
||||
@@ -136,12 +106,14 @@ void ResizeLayoutPopup::setupLayoutView() {
|
||||
this->ui->spinBox_height->setMinimum(1);
|
||||
this->ui->spinBox_height->setMaximum(maxHeight);
|
||||
|
||||
this->outline = new ResizableRect(this, this->layout->getWidth(), this->layout->getHeight(), qRgb(255, 0, 255));
|
||||
this->outline = new ResizableRect(this, Metatile::pixelSize(), this->layout->pixelSize(), qRgb(255, 0, 255));
|
||||
this->outline->setZValue(Editor::ZValue::ResizeLayoutPopup); // Ensure on top of view
|
||||
this->outline->setLimit(cover->rect().toAlignedRect());
|
||||
connect(outline, &ResizableRect::rectUpdated, [=](QRect rect){
|
||||
// Note: this extra limit check needs access to the project values, so it is done here and not ResizableRect::mouseMoveEvent
|
||||
int size = this->project->getMapDataSize(rect.width() / 16, rect.height() / 16);
|
||||
int metatilesWide = rect.width() / Metatile::pixelWidth();
|
||||
int metatilesTall = rect.height() / Metatile::pixelHeight();
|
||||
int size = this->project->getMapDataSize(metatilesWide, metatilesTall);
|
||||
int maxSize = this->project->getMaxMapDataSize();
|
||||
if (size > maxSize) {
|
||||
QSize addition = this->project->getMapSizeAddition();
|
||||
@@ -151,8 +123,8 @@ void ResizeLayoutPopup::setupLayoutView() {
|
||||
.arg(addition.width())
|
||||
.arg(addition.height())
|
||||
.arg(maxSize)
|
||||
.arg(rect.width() / 16)
|
||||
.arg(rect.height() / 16)
|
||||
.arg(metatilesWide)
|
||||
.arg(metatilesTall)
|
||||
.arg(size),
|
||||
this);
|
||||
// adjust rect to last accepted size
|
||||
@@ -160,8 +132,11 @@ void ResizeLayoutPopup::setupLayoutView() {
|
||||
}
|
||||
this->scene->setValidRect(rect);
|
||||
this->outline->setRect(rect);
|
||||
this->ui->spinBox_width->setValue(rect.width() / 16);
|
||||
this->ui->spinBox_height->setValue(rect.height() / 16);
|
||||
|
||||
const QSignalBlocker b_Width(this->ui->spinBox_width);
|
||||
const QSignalBlocker b_Height(this->ui->spinBox_height);
|
||||
this->ui->spinBox_width->setValue(metatilesWide);
|
||||
this->ui->spinBox_height->setValue(metatilesTall);
|
||||
});
|
||||
scene->addItem(outline);
|
||||
|
||||
@@ -171,7 +146,7 @@ void ResizeLayoutPopup::setupLayoutView() {
|
||||
this->scale = 1.0;
|
||||
|
||||
QRectF rect = this->outline->rect();
|
||||
const int marginSize = 10 * 16; // Leave a margin of 10 metatiles around the map
|
||||
const int marginSize = 10 * Metatile::pixelWidth(); // Leave a margin of 10 metatiles around the map
|
||||
rect += QMargins(marginSize, marginSize, marginSize, marginSize);
|
||||
this->ui->graphicsView->fitInView(rect, Qt::KeepAspectRatio);
|
||||
}
|
||||
@@ -179,25 +154,23 @@ void ResizeLayoutPopup::setupLayoutView() {
|
||||
void ResizeLayoutPopup::on_spinBox_width_valueChanged(int value) {
|
||||
if (!this->outline) return;
|
||||
QRectF rect = this->outline->rect();
|
||||
this->outline->updatePosFromRect(QRect(rect.x(), rect.y(), value * 16, rect.height()));
|
||||
this->outline->updatePosFromRect(QRect(rect.x(), rect.y(), value * Metatile::pixelWidth(), rect.height()));
|
||||
}
|
||||
|
||||
void ResizeLayoutPopup::on_spinBox_height_valueChanged(int value) {
|
||||
if (!this->outline) return;
|
||||
QRectF rect = this->outline->rect();
|
||||
this->outline->updatePosFromRect(QRect(rect.x(), rect.y(), rect.width(), value * 16));
|
||||
this->outline->updatePosFromRect(QRect(rect.x(), rect.y(), rect.width(), value * Metatile::pixelHeight()));
|
||||
}
|
||||
|
||||
/// Result is the number of metatiles to add (or subtract) to each side of the map after dimension changes
|
||||
QMargins ResizeLayoutPopup::getResult() {
|
||||
QMargins result = QMargins();
|
||||
|
||||
result.setLeft(this->layoutPixmap->x() - this->outline->rect().left());
|
||||
result.setTop(this->layoutPixmap->y() - this->outline->rect().top());
|
||||
result.setRight(this->outline->rect().right() - (this->layoutPixmap->x() + this->layoutPixmap->pixmap().width()));
|
||||
result.setBottom(this->outline->rect().bottom() - (this->layoutPixmap->y() + this->layoutPixmap->pixmap().height()));
|
||||
|
||||
return result / 16;
|
||||
result.setLeft((this->layoutPixmap->x() - this->outline->rect().left()) / Metatile::pixelWidth());
|
||||
result.setTop((this->layoutPixmap->y() - this->outline->rect().top()) / Metatile::pixelHeight());
|
||||
result.setRight((this->outline->rect().right() - (this->layoutPixmap->x() + this->layoutPixmap->pixmap().width())) / Metatile::pixelWidth());
|
||||
result.setBottom((this->outline->rect().bottom() - (this->layoutPixmap->y() + this->layoutPixmap->pixmap().height())) / Metatile::pixelHeight());
|
||||
return result;
|
||||
}
|
||||
|
||||
QSize ResizeLayoutPopup::getBorderResult() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "selectablepixmapitem.h"
|
||||
#include <QPainter>
|
||||
|
||||
QPoint SelectablePixmapItem::getSelectionDimensions()
|
||||
QPoint SelectablePixmapItem::getSelectionDimensions() const
|
||||
{
|
||||
return QPoint(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1);
|
||||
}
|
||||
@@ -19,8 +19,8 @@ void SelectablePixmapItem::select(int x, int y, int width, int height)
|
||||
{
|
||||
this->selectionInitialX = x;
|
||||
this->selectionInitialY = y;
|
||||
this->selectionOffsetX = qMax(0, qMin(width, this->maxSelectionWidth));
|
||||
this->selectionOffsetY = qMax(0, qMin(height, this->maxSelectionHeight));
|
||||
this->selectionOffsetX = qBound(0, width, this->maxSelectionWidth);
|
||||
this->selectionOffsetY = qBound(0, height, this->maxSelectionHeight);
|
||||
this->draw();
|
||||
emit this->selectionChanged(x, y, width, height);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
||||
hasUnsavedChanges(false)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label);
|
||||
ui->setupUi(this);
|
||||
|
||||
setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label);
|
||||
|
||||
connect(ui->checkBox_xFlip, &QCheckBox::toggled, this, &TilesetEditor::setXFlip);
|
||||
connect(ui->checkBox_yFlip, &QCheckBox::toggled, this, &TilesetEditor::setYFlip);
|
||||
|
||||
@@ -35,6 +37,20 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
||||
|
||||
connect(ui->actionSave_Tileset, &QAction::triggered, this, &TilesetEditor::save);
|
||||
|
||||
connect(ui->actionImport_Primary_Tiles_Image, &QAction::triggered, [this] { importTilesetTiles(this->primaryTileset); });
|
||||
connect(ui->actionImport_Secondary_Tiles_Image, &QAction::triggered, [this] { importTilesetTiles(this->secondaryTileset); });
|
||||
|
||||
connect(ui->actionImport_Primary_AdvanceMap_Metatiles, &QAction::triggered, [this] { importAdvanceMapMetatiles(this->primaryTileset); });
|
||||
connect(ui->actionImport_Secondary_AdvanceMap_Metatiles, &QAction::triggered, [this] { importAdvanceMapMetatiles(this->secondaryTileset); });
|
||||
|
||||
connect(ui->actionExport_Primary_Tiles_Image, &QAction::triggered, [this] { exportTilesImage(this->primaryTileset); });
|
||||
connect(ui->actionExport_Secondary_Tiles_Image, &QAction::triggered, [this] { exportTilesImage(this->secondaryTileset); });
|
||||
|
||||
connect(ui->actionExport_Primary_Porytiles_Layer_Images, &QAction::triggered, [this] { exportPorytilesLayerImages(this->primaryTileset); });
|
||||
connect(ui->actionExport_Secondary_Porytiles_Layer_Images, &QAction::triggered, [this] { exportPorytilesLayerImages(this->secondaryTileset); });
|
||||
|
||||
connect(ui->actionExport_Metatiles_Image, &QAction::triggered, [this] { exportMetatilesImage(); });
|
||||
|
||||
ui->actionShow_Tileset_Divider->setChecked(porymapConfig.showTilesetEditorDivider);
|
||||
ui->actionShow_Raw_Metatile_Attributes->setChecked(porymapConfig.showTilesetEditorRawAttributes);
|
||||
|
||||
@@ -74,6 +90,7 @@ TilesetEditor::~TilesetEditor()
|
||||
delete selectedTileScene;
|
||||
delete metatileLayersScene;
|
||||
delete copiedMetatile;
|
||||
delete metatileImageExportSettings;
|
||||
this->metatileHistory.clear();
|
||||
}
|
||||
|
||||
@@ -198,7 +215,7 @@ void TilesetEditor::setRawAttributesVisible(bool visible) {
|
||||
|
||||
void TilesetEditor::initMetatileSelector()
|
||||
{
|
||||
this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset, this->layout);
|
||||
this->metatileSelector = new TilesetEditorMetatileSelector(projectConfig.metatileSelectorWidth, this->primaryTileset, this->secondaryTileset, this->layout);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileChanged,
|
||||
this, &TilesetEditor::onHoveredMetatileChanged);
|
||||
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileCleared,
|
||||
@@ -227,8 +244,9 @@ void TilesetEditor::initMetatileLayersItem() {
|
||||
this, &TilesetEditor::onMetatileLayerTileChanged);
|
||||
connect(this->metatileLayersItem, &MetatileLayersItem::selectedTilesChanged,
|
||||
this, &TilesetEditor::onMetatileLayerSelectionChanged);
|
||||
connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileChanged,
|
||||
this, &TilesetEditor::onHoveredTileChanged);
|
||||
connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileChanged, [this](const Tile &tile) {
|
||||
onHoveredTileChanged(tile);
|
||||
});
|
||||
connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileCleared,
|
||||
this, &TilesetEditor::onHoveredTileCleared);
|
||||
|
||||
@@ -244,8 +262,9 @@ void TilesetEditor::initMetatileLayersItem() {
|
||||
void TilesetEditor::initTileSelector()
|
||||
{
|
||||
this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset, projectConfig.getNumLayersInMetatile());
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileChanged,
|
||||
this, &TilesetEditor::onHoveredTileChanged);
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileChanged, [this](uint16_t tileId) {
|
||||
onHoveredTileChanged(tileId);
|
||||
});
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileCleared,
|
||||
this, &TilesetEditor::onHoveredTileCleared);
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::selectedTilesChanged,
|
||||
@@ -374,19 +393,21 @@ void TilesetEditor::drawSelectedTiles() {
|
||||
return;
|
||||
}
|
||||
|
||||
const int imgTileWidth = 16;
|
||||
const int imgTileHeight = 16;
|
||||
this->selectedTileScene->clear();
|
||||
QList<Tile> tiles = this->tileSelector->getSelectedTiles();
|
||||
QPoint dimensions = this->tileSelector->getSelectionDimensions();
|
||||
QImage selectionImage(16 * dimensions.x(), 16 * dimensions.y(), QImage::Format_RGBA8888);
|
||||
QImage selectionImage(imgTileWidth * dimensions.x(), imgTileHeight * dimensions.y(), QImage::Format_RGBA8888);
|
||||
QPainter painter(&selectionImage);
|
||||
int tileIndex = 0;
|
||||
for (int j = 0; j < dimensions.y(); j++) {
|
||||
for (int i = 0; i < dimensions.x(); i++) {
|
||||
auto tile = tiles.at(tileIndex);
|
||||
QImage tileImage = getPalettedTileImage(tile.tileId, this->primaryTileset, this->secondaryTileset, tile.palette, true).scaled(16, 16);
|
||||
QImage tileImage = getPalettedTileImage(tile.tileId, this->primaryTileset, this->secondaryTileset, tile.palette, true).scaled(imgTileWidth, imgTileHeight);
|
||||
tile.flip(&tileImage);
|
||||
tileIndex++;
|
||||
painter.drawImage(i * 16, j * 16, tileImage);
|
||||
painter.drawImage(i * imgTileWidth, j * imgTileHeight, tileImage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,8 +458,17 @@ void TilesetEditor::queueMetatileReload(uint16_t metatileId) {
|
||||
this->metatileReloadQueue.insert(metatileId);
|
||||
}
|
||||
|
||||
void TilesetEditor::onHoveredTileChanged(uint16_t tile) {
|
||||
this->ui->statusbar->showMessage(QString("Tile: %1").arg(Util::toHexString(tile, 3)));
|
||||
void TilesetEditor::onHoveredTileChanged(const Tile &tile) {
|
||||
this->ui->statusbar->showMessage(QString("Tile: %1, Palette: %2%3%4")
|
||||
.arg(Util::toHexString(tile.tileId, 3))
|
||||
.arg(QString::number(tile.palette))
|
||||
.arg(tile.xflip ? ", X-flipped" : "")
|
||||
.arg(tile.yflip ? ", Y-flipped" : "")
|
||||
);
|
||||
}
|
||||
|
||||
void TilesetEditor::onHoveredTileChanged(uint16_t tileId) {
|
||||
this->ui->statusbar->showMessage(QString("Tile: %1").arg(Util::toHexString(tileId, 3)));
|
||||
}
|
||||
|
||||
void TilesetEditor::onHoveredTileCleared() {
|
||||
@@ -694,17 +724,8 @@ bool TilesetEditor::save() {
|
||||
return success;
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionImport_Primary_Tiles_triggered()
|
||||
{
|
||||
this->importTilesetTiles(this->primaryTileset, true);
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionImport_Secondary_Tiles_triggered()
|
||||
{
|
||||
this->importTilesetTiles(this->secondaryTileset, false);
|
||||
}
|
||||
|
||||
void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
|
||||
void TilesetEditor::importTilesetTiles(Tileset *tileset) {
|
||||
bool primary = !tileset->is_secondary;
|
||||
QString descriptor = primary ? "primary" : "secondary";
|
||||
QString descriptorCaps = primary ? "Primary" : "Secondary";
|
||||
|
||||
@@ -725,18 +746,20 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
|
||||
} else {
|
||||
logError(QString("Failed to open image file: '%1'").arg(filepath));
|
||||
}
|
||||
if (image.width() == 0 || image.height() == 0 || image.width() % 8 != 0 || image.height() % 8 != 0) {
|
||||
if (image.width() == 0 || image.height() == 0 || image.width() % Tile::pixelWidth() != 0 || image.height() % Tile::pixelHeight() != 0) {
|
||||
ErrorMessage::show(QStringLiteral("Failed to import tiles."),
|
||||
QString("The image dimensions (%1 x %2) are invalid. Width and height must be multiples of 8 pixels.")
|
||||
QString("The image dimensions (%1x%2) are invalid. The dimensions must be a multiple of %3x%4 pixels.")
|
||||
.arg(image.width())
|
||||
.arg(image.height()),
|
||||
.arg(image.height())
|
||||
.arg(Tile::pixelWidth())
|
||||
.arg(Tile::pixelHeight()),
|
||||
this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate total number of tiles in image.
|
||||
int numTilesWide = image.width() / 8;
|
||||
int numTilesHigh = image.height() / 8;
|
||||
int numTilesWide = image.width() / Tile::pixelWidth();
|
||||
int numTilesHigh = image.height() / Tile::pixelHeight();
|
||||
int totalTiles = numTilesHigh * numTilesWide;
|
||||
int maxAllowedTiles = primary ? Project::getNumTilesPrimary() : Project::getNumTilesTotal() - Project::getNumTilesPrimary();
|
||||
if (totalTiles > maxAllowedTiles) {
|
||||
@@ -774,7 +797,10 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
|
||||
image = image.convertToFormat(QImage::Format::Format_Indexed8, colorTable);
|
||||
}
|
||||
|
||||
tileset->loadTilesImage(&image);
|
||||
if (!tileset->loadTilesImage(&image)) {
|
||||
RecentErrorMessage::show(QStringLiteral("Failed to import tiles."), this);
|
||||
return;
|
||||
}
|
||||
this->refresh();
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
@@ -822,7 +848,7 @@ void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
|
||||
primarySpinBox->setMinimum(1);
|
||||
secondarySpinBox->setMinimum(1);
|
||||
primarySpinBox->setMaximum(Project::getNumMetatilesPrimary());
|
||||
secondarySpinBox->setMaximum(Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary());
|
||||
secondarySpinBox->setMaximum(Project::getNumMetatilesSecondary());
|
||||
primarySpinBox->setValue(this->primaryTileset->numMetatiles());
|
||||
secondarySpinBox->setValue(this->secondaryTileset->numMetatiles());
|
||||
form.addRow(new QLabel("Primary Tileset"), primarySpinBox);
|
||||
@@ -968,62 +994,70 @@ void TilesetEditor::pasteMetatile(const Metatile * toPaste, QString newLabel)
|
||||
this->commitMetatileAndLabelChange(prevMetatile, prevLabel);
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionExport_Primary_Tiles_Image_triggered()
|
||||
{
|
||||
QString defaultName = QString("%1_Tiles_Pal%2").arg(this->primaryTileset->name).arg(this->paletteId);
|
||||
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
|
||||
QString filepath = FileDialog::getSaveFileName(this, "Export Primary Tiles Image", defaultFilepath, "Image Files (*.png)");
|
||||
void TilesetEditor::exportTilesImage(Tileset *tileset) {
|
||||
bool primary = !tileset->is_secondary;
|
||||
QString defaultFilepath = QString("%1/%2_Tiles_Pal%3.png").arg(FileDialog::getDirectory()).arg(tileset->name).arg(this->paletteId);
|
||||
QString filepath = FileDialog::getSaveFileName(this, QString("Export %1 Tiles Image").arg(primary ? "Primary" : "Secondary"), defaultFilepath, "Image Files (*.png)");
|
||||
if (!filepath.isEmpty()) {
|
||||
QImage image = this->tileSelector->buildPrimaryTilesIndexedImage();
|
||||
QImage image = primary ? this->tileSelector->buildPrimaryTilesIndexedImage() : this->tileSelector->buildSecondaryTilesIndexedImage();
|
||||
exportIndexed4BPPPng(image, filepath);
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionExport_Secondary_Tiles_Image_triggered()
|
||||
{
|
||||
QString defaultName = QString("%1_Tiles_Pal%2").arg(this->secondaryTileset->name).arg(this->paletteId);
|
||||
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
|
||||
QString filepath = FileDialog::getSaveFileName(this, "Export Secondary Tiles Image", defaultFilepath, "Image Files (*.png)");
|
||||
if (!filepath.isEmpty()) {
|
||||
QImage image = this->tileSelector->buildSecondaryTilesIndexedImage();
|
||||
exportIndexed4BPPPng(image, filepath);
|
||||
// There are many more options for exporting metatile images than tile images, so we open a separate dialog to ask the user for settings.
|
||||
void TilesetEditor::exportMetatilesImage() {
|
||||
if (!this->metatileImageExportSettings) {
|
||||
this->metatileImageExportSettings = new MetatileImageExporter::Settings;
|
||||
}
|
||||
auto dialog = new MetatileImageExporter(this, this->primaryTileset, this->secondaryTileset, this->metatileImageExportSettings);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void TilesetEditor::exportPorytilesLayerImages(Tileset *tileset) {
|
||||
QString dir = FileDialog::getExistingDirectory(this, QStringLiteral("Choose Folder to Export Images"));
|
||||
if (dir.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetatileImageExporter layerExporter(this, this->primaryTileset, this->secondaryTileset);
|
||||
MetatileImageExporter::Settings settings = {};
|
||||
settings.usePrimaryTileset = !tileset->is_secondary;
|
||||
settings.useSecondaryTileset = tileset->is_secondary;
|
||||
|
||||
QMap<QString,QImage> images;
|
||||
QStringList pathCollisions;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
settings.layerOrder.clear();
|
||||
settings.layerOrder[i] = true;
|
||||
layerExporter.applySettings(settings);
|
||||
|
||||
QString filename = layerExporter.getDefaultFileName();
|
||||
QString path = QString("%1/%2").arg(dir).arg(filename);
|
||||
if (QFileInfo::exists(path)) {
|
||||
pathCollisions.append(filename);
|
||||
}
|
||||
images[path] = layerExporter.getImage();
|
||||
}
|
||||
|
||||
if (!pathCollisions.isEmpty()) {
|
||||
QString message = QString("The following files will be overwritten, are you sure you want to export?\n\n%1").arg(pathCollisions.join("\n"));
|
||||
auto reply = QuestionMessage::show(message, this);
|
||||
if (reply != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = images.constBegin(); it != images.constEnd(); it++) {
|
||||
QString path = it.key();
|
||||
if (!it.value().save(path)) {
|
||||
logError(QString("Failed to save Porytiles layer image '%1'.").arg(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionExport_Primary_Metatiles_Image_triggered()
|
||||
{
|
||||
QString defaultName = QString("%1_Metatiles").arg(this->primaryTileset->name);
|
||||
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
|
||||
QString filepath = FileDialog::getSaveFileName(this, "Export Primary Metatiles Image", defaultFilepath, "Image Files (*.png)");
|
||||
if (!filepath.isEmpty()) {
|
||||
QImage image = this->metatileSelector->buildPrimaryMetatilesImage();
|
||||
image.save(filepath, "PNG");
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionExport_Secondary_Metatiles_Image_triggered()
|
||||
{
|
||||
QString defaultName = QString("%1_Metatiles").arg(this->secondaryTileset->name);
|
||||
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
|
||||
QString filepath = FileDialog::getSaveFileName(this, "Export Secondary Metatiles Image", defaultFilepath, "Image Files (*.png)");
|
||||
if (!filepath.isEmpty()) {
|
||||
QImage image = this->metatileSelector->buildSecondaryMetatilesImage();
|
||||
image.save(filepath, "PNG");
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionImport_Primary_Metatiles_triggered()
|
||||
{
|
||||
this->importTilesetMetatiles(this->primaryTileset, true);
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionImport_Secondary_Metatiles_triggered()
|
||||
{
|
||||
this->importTilesetMetatiles(this->secondaryTileset, false);
|
||||
}
|
||||
|
||||
void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary)
|
||||
{
|
||||
void TilesetEditor::importAdvanceMapMetatiles(Tileset *tileset) {
|
||||
bool primary = !tileset->is_secondary;
|
||||
QString descriptorCaps = primary ? "Primary" : "Secondary";
|
||||
|
||||
QString filepath = FileDialog::getOpenFileName(this, QString("Import %1 Tileset Metatiles from Advance Map 1.92").arg(descriptorCaps), "", "Advance Map 1.92 Metatile Files (*.bvd)");
|
||||
@@ -1041,7 +1075,7 @@ void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary)
|
||||
|
||||
// TODO: This is crude because it makes a history entry for every newly-imported metatile.
|
||||
// Revisit this when tiles and num metatiles are added to tileset editory history.
|
||||
int metatileIdBase = primary ? 0 : Project::getNumMetatilesPrimary();
|
||||
uint16_t metatileIdBase = tileset->firstMetatileId();
|
||||
for (int i = 0; i < metatiles.length(); i++) {
|
||||
if (i >= tileset->numMetatiles()) {
|
||||
break;
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
#include "project.h"
|
||||
#include <QPainter>
|
||||
|
||||
TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout)
|
||||
: SelectablePixmapItem(16, 16, 1, 1) {
|
||||
// TODO: This class has a decent bit of overlap with the MetatileSelector class.
|
||||
// They should be refactored to inherit from a single parent class.
|
||||
|
||||
TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(int numMetatilesWide, Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout)
|
||||
: SelectablePixmapItem(32, 32, 1, 1),
|
||||
numMetatilesWide(qMax(numMetatilesWide, 1)) {
|
||||
this->primaryTileset = primaryTileset;
|
||||
this->secondaryTileset = secondaryTileset;
|
||||
this->numMetatilesWide = 8;
|
||||
this->layout = layout;
|
||||
setAcceptHoverEvents(true);
|
||||
this->usedMetatiles.resize(Project::getNumMetatilesTotal());
|
||||
}
|
||||
|
||||
int TilesetEditorMetatileSelector::numRows(int numMetatiles) {
|
||||
int TilesetEditorMetatileSelector::numRows(int numMetatiles) const {
|
||||
int numMetatilesHigh = numMetatiles / this->numMetatilesWide;
|
||||
if (numMetatiles % this->numMetatilesWide != 0) {
|
||||
// Round up height for incomplete last row
|
||||
@@ -22,67 +25,29 @@ int TilesetEditorMetatileSelector::numRows(int numMetatiles) {
|
||||
return numMetatilesHigh;
|
||||
}
|
||||
|
||||
int TilesetEditorMetatileSelector::numRows() {
|
||||
int TilesetEditorMetatileSelector::numRows() const {
|
||||
return this->numRows(this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles());
|
||||
}
|
||||
|
||||
int TilesetEditorMetatileSelector::numPrimaryMetatilesRounded() const {
|
||||
// We round up the number of primary metatiles to keep the tilesets on separate rows.
|
||||
return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide;
|
||||
}
|
||||
|
||||
QImage TilesetEditorMetatileSelector::buildAllMetatilesImage() {
|
||||
return this->buildImage(0, this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles());
|
||||
}
|
||||
|
||||
QImage TilesetEditorMetatileSelector::buildPrimaryMetatilesImage() {
|
||||
return this->buildImage(0, this->primaryTileset->numMetatiles());
|
||||
}
|
||||
|
||||
QImage TilesetEditorMetatileSelector::buildSecondaryMetatilesImage() {
|
||||
return this->buildImage(Project::getNumMetatilesPrimary(), this->secondaryTileset->numMetatiles());
|
||||
}
|
||||
|
||||
QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMetatiles) {
|
||||
int numMetatilesHigh = this->numRows(numMetatiles);
|
||||
int numPrimary = this->numPrimaryMetatilesRounded();
|
||||
int maxPrimary = Project::getNumMetatilesPrimary();
|
||||
bool includesPrimary = metatileIdStart < maxPrimary;
|
||||
|
||||
QImage image(this->numMetatilesWide * this->cellWidth, numMetatilesHigh * this->cellHeight, QImage::Format_RGBA8888);
|
||||
image.fill(Qt::magenta);
|
||||
QPainter painter(&image);
|
||||
for (int i = 0; i < numMetatiles; i++) {
|
||||
int metatileId = i + metatileIdStart;
|
||||
if (includesPrimary && metatileId >= numPrimary)
|
||||
metatileId += maxPrimary - numPrimary; // Skip over unused region of primary tileset
|
||||
QImage metatile_image = getMetatileImage(
|
||||
metatileId,
|
||||
this->primaryTileset,
|
||||
this->secondaryTileset,
|
||||
this->layout->metatileLayerOrder,
|
||||
this->layout->metatileLayerOpacity,
|
||||
true)
|
||||
.scaled(this->cellWidth, this->cellHeight);
|
||||
int map_y = i / this->numMetatilesWide;
|
||||
int map_x = i % this->numMetatilesWide;
|
||||
QPoint metatile_origin = QPoint(map_x * this->cellWidth, map_y * this->cellHeight);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
}
|
||||
painter.end();
|
||||
return image;
|
||||
if (!this->primaryTileset)
|
||||
return 0;
|
||||
return Util::roundUpToMultiple(this->primaryTileset->numMetatiles(), this->numMetatilesWide);
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::drawMetatile(uint16_t metatileId) {
|
||||
QPoint pos = getMetatileIdCoords(metatileId);
|
||||
bool ok;
|
||||
QPoint pos = metatileIdToPos(metatileId, &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
QPainter painter(&this->baseImage);
|
||||
QImage metatile_image = getMetatileImage(
|
||||
metatileId,
|
||||
this->primaryTileset,
|
||||
this->secondaryTileset,
|
||||
this->layout->metatileLayerOrder,
|
||||
this->layout->metatileLayerOpacity,
|
||||
this->layout->metatileLayerOrder(),
|
||||
this->layout->metatileLayerOpacity(),
|
||||
true)
|
||||
.scaled(this->cellWidth, this->cellHeight);
|
||||
painter.drawImage(QPoint(pos.x() * this->cellWidth, pos.y() * this->cellHeight), metatile_image);
|
||||
@@ -97,7 +62,13 @@ void TilesetEditorMetatileSelector::drawSelectedMetatile() {
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::updateBasePixmap() {
|
||||
this->baseImage = buildAllMetatilesImage();
|
||||
this->baseImage = getMetatileSheetImage(this->primaryTileset,
|
||||
this->secondaryTileset,
|
||||
this->numMetatilesWide,
|
||||
this->layout->metatileLayerOrder(),
|
||||
this->layout->metatileLayerOpacity(),
|
||||
QSize(this->cellWidth, this->cellHeight),
|
||||
true);
|
||||
this->basePixmap = QPixmap::fromImage(this->baseImage);
|
||||
}
|
||||
|
||||
@@ -114,9 +85,11 @@ void TilesetEditorMetatileSelector::draw() {
|
||||
}
|
||||
|
||||
bool TilesetEditorMetatileSelector::select(uint16_t metatileId) {
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) return false;
|
||||
QPoint coords = this->getMetatileIdCoords(metatileId);
|
||||
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
|
||||
bool ok;
|
||||
QPoint pos = metatileIdToPos(metatileId, &ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
SelectablePixmapItem::select(pos);
|
||||
this->selectedMetatileId = metatileId;
|
||||
emit selectedMetatileChanged(metatileId);
|
||||
return true;
|
||||
@@ -131,32 +104,19 @@ void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::updateSelectedMetatile() {
|
||||
QPoint origin = this->getSelectionStart();
|
||||
uint16_t metatileId = this->getMetatileId(origin.x(), origin.y());
|
||||
if (Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
this->selectedMetatileId = metatileId;
|
||||
else
|
||||
this->selectedMetatileId = Project::getNumMetatilesPrimary() + this->secondaryTileset->numMetatiles() - 1;
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(getSelectionStart(), &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
this->selectedMetatileId = metatileId;
|
||||
emit selectedMetatileChanged(this->selectedMetatileId);
|
||||
}
|
||||
|
||||
uint16_t TilesetEditorMetatileSelector::getSelectedMetatileId() {
|
||||
return this->selectedMetatileId;
|
||||
}
|
||||
|
||||
uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) {
|
||||
int index = y * this->numMetatilesWide + x;
|
||||
int numPrimary = numPrimaryMetatilesRounded();
|
||||
if (index < numPrimary) {
|
||||
return static_cast<uint16_t>(index);
|
||||
} else {
|
||||
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
bool TilesetEditorMetatileSelector::shouldAcceptEvent(QGraphicsSceneMouseEvent *event) {
|
||||
QPoint pos = this->getCellPos(event->pos());
|
||||
return Tileset::metatileIsValid(getMetatileId(pos.x(), pos.y()), this->primaryTileset, this->secondaryTileset);
|
||||
bool ok;
|
||||
posToMetatileId(getCellPos(event->pos()), &ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
@@ -179,29 +139,64 @@ void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
|
||||
QPoint pos = this->getCellPos(event->pos());
|
||||
uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
|
||||
emit this->hoveredMetatileChanged(metatileId);
|
||||
bool ok;
|
||||
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
|
||||
if (ok) {
|
||||
emit this->hoveredMetatileChanged(metatileId);
|
||||
} else {
|
||||
emit this->hoveredMetatileCleared();
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditorMetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
|
||||
emit this->hoveredMetatileCleared();
|
||||
}
|
||||
|
||||
QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
{
|
||||
// Invalid metatile id.
|
||||
return QPoint(0, 0);
|
||||
}
|
||||
int index = metatileId < Project::getNumMetatilesPrimary()
|
||||
? metatileId
|
||||
: metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded();
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
uint16_t TilesetEditorMetatileSelector::posToMetatileId(const QPoint &pos, bool *ok) const {
|
||||
return posToMetatileId(pos.x(), pos.y(), ok);
|
||||
}
|
||||
|
||||
QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) {
|
||||
QPoint pos = getMetatileIdCoords(metatileId);
|
||||
uint16_t TilesetEditorMetatileSelector::posToMetatileId(int x, int y, bool *ok) const {
|
||||
if (ok) *ok = true;
|
||||
int index = y * this->numMetatilesWide + x;
|
||||
uint16_t metatileId = static_cast<uint16_t>(index);
|
||||
if (this->primaryTileset && this->primaryTileset->contains(metatileId)) {
|
||||
return metatileId;
|
||||
}
|
||||
|
||||
// There's some extra handling here because we round the tilesets to keep them on separate rows.
|
||||
// This means if the maximum number of primary metatiles is not divisible by the metatile width
|
||||
// then the metatiles we used to round the primary tileset would have the index of valid secondary metatiles.
|
||||
// These need to be ignored, or they'll appear to be duplicates of the subseqeunt secondary metatiles.
|
||||
int numPrimaryRounded = numPrimaryMetatilesRounded();
|
||||
int firstSecondaryRow = numPrimaryRounded / this->numMetatilesWide;
|
||||
metatileId = static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimaryRounded);
|
||||
if (this->secondaryTileset && this->secondaryTileset->contains(metatileId) && y >= firstSecondaryRow) {
|
||||
return metatileId;
|
||||
}
|
||||
|
||||
if (ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QPoint TilesetEditorMetatileSelector::metatileIdToPos(uint16_t metatileId, bool *ok) const {
|
||||
if (this->primaryTileset && this->primaryTileset->contains(metatileId)) {
|
||||
if (ok) *ok = true;
|
||||
int index = metatileId;
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
}
|
||||
if (this->secondaryTileset && this->secondaryTileset->contains(metatileId)) {
|
||||
if (ok) *ok = true;
|
||||
int index = metatileId - Project::getNumMetatilesPrimary() + numPrimaryMetatilesRounded();
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
}
|
||||
|
||||
if (ok) *ok = false;
|
||||
return QPoint(0,0);
|
||||
}
|
||||
|
||||
QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) const {
|
||||
QPoint pos = metatileIdToPos(metatileId);
|
||||
pos.rx() = (pos.x() * this->cellWidth) + (this->cellWidth / 2);
|
||||
pos.ry() = (pos.y() * this->cellHeight) + (this->cellHeight / 2);
|
||||
return pos;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <QPainter>
|
||||
#include <QVector>
|
||||
|
||||
QPoint TilesetEditorTileSelector::getSelectionDimensions() {
|
||||
QPoint TilesetEditorTileSelector::getSelectionDimensions() const {
|
||||
if (this->externalSelection) {
|
||||
return QPoint(this->externalSelectionWidth, this->externalSelectionHeight);
|
||||
} else {
|
||||
@@ -12,53 +12,40 @@ QPoint TilesetEditorTileSelector::getSelectionDimensions() {
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditorTileSelector::draw() {
|
||||
if (!this->primaryTileset || !this->secondaryTileset) {
|
||||
this->setPixmap(QPixmap());
|
||||
void TilesetEditorTileSelector::updateBasePixmap() {
|
||||
if (!this->primaryTileset || !this->secondaryTileset || this->numTilesWide == 0) {
|
||||
this->basePixmap = QPixmap();
|
||||
return;
|
||||
}
|
||||
|
||||
int totalTiles = Project::getNumTilesTotal();
|
||||
int primaryLength = this->primaryTileset->tiles.length();
|
||||
int secondaryLength = this->secondaryTileset->tiles.length();
|
||||
int height = totalTiles / this->numTilesWide;
|
||||
QList<QRgb> palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset, true);
|
||||
QImage image(this->numTilesWide * this->cellWidth, height * this->cellHeight, QImage::Format_RGBA8888);
|
||||
|
||||
QPainter painter(&image);
|
||||
for (uint16_t tile = 0; tile < totalTiles; tile++) {
|
||||
QImage tileImage;
|
||||
if (tile < primaryLength) {
|
||||
tileImage = getPalettedTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId, true).scaled(this->cellWidth, this->cellHeight);
|
||||
} else if (tile < Project::getNumTilesPrimary()) {
|
||||
tileImage = QImage(this->cellWidth, this->cellHeight, QImage::Format_RGBA8888);
|
||||
tileImage.fill(palette.at(0));
|
||||
} else if (tile < Project::getNumTilesPrimary() + secondaryLength) {
|
||||
tileImage = getPalettedTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId, true).scaled(this->cellWidth, this->cellHeight);
|
||||
} else {
|
||||
tileImage = QImage(this->cellWidth, this->cellHeight, QImage::Format_RGBA8888);
|
||||
QPainter painter(&tileImage);
|
||||
painter.fillRect(0, 0, this->cellWidth, this->cellHeight, palette.at(0));
|
||||
}
|
||||
|
||||
int y = tile / this->numTilesWide;
|
||||
int x = tile % this->numTilesWide;
|
||||
QPoint origin = QPoint(x * this->cellWidth, y * this->cellHeight);
|
||||
painter.drawImage(origin, tileImage);
|
||||
for (uint16_t tileId = 0; tileId < totalTiles; tileId++) {
|
||||
QImage tileImage = getPalettedTileImage(tileId, this->primaryTileset, this->secondaryTileset, this->paletteId, true)
|
||||
.scaled(this->cellWidth, this->cellHeight);
|
||||
int x = (tileId % this->numTilesWide) * this->cellWidth;
|
||||
int y = (tileId / this->numTilesWide) * this->cellHeight;
|
||||
painter.drawImage(x, y, tileImage);
|
||||
}
|
||||
|
||||
if (this->showDivider) {
|
||||
int row = this->primaryTileset->tiles.length() / this->numTilesWide;
|
||||
if (this->primaryTileset->tiles.length() % this->numTilesWide != 0) {
|
||||
// Round up height for incomplete last row
|
||||
row++;
|
||||
}
|
||||
int row = Util::roundUpToMultiple(Project::getNumTilesPrimary(), this->numTilesWide) / this->numTilesWide;
|
||||
const int y = row * this->cellHeight;
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawLine(0, y, this->numTilesWide * this->cellWidth, y);
|
||||
}
|
||||
|
||||
painter.end();
|
||||
this->setPixmap(QPixmap::fromImage(image));
|
||||
this->basePixmap = QPixmap::fromImage(image);
|
||||
}
|
||||
|
||||
void TilesetEditorTileSelector::draw() {
|
||||
if (this->basePixmap.isNull())
|
||||
updateBasePixmap();
|
||||
setPixmap(this->basePixmap);
|
||||
|
||||
if (!this->externalSelection || (this->externalSelectionWidth == 1 && this->externalSelectionHeight == 1)) {
|
||||
this->drawSelection();
|
||||
@@ -82,12 +69,14 @@ void TilesetEditorTileSelector::highlight(uint16_t tile) {
|
||||
void TilesetEditorTileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
this->primaryTileset = primaryTileset;
|
||||
this->secondaryTileset = secondaryTileset;
|
||||
this->updateBasePixmap();
|
||||
this->draw();
|
||||
}
|
||||
|
||||
void TilesetEditorTileSelector::setPaletteId(int paletteId) {
|
||||
this->paletteId = paletteId;
|
||||
this->paletteChanged = true;
|
||||
this->updateBasePixmap();
|
||||
this->draw();
|
||||
}
|
||||
|
||||
@@ -213,7 +202,7 @@ void TilesetEditorTileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
|
||||
}
|
||||
|
||||
QPoint TilesetEditorTileSelector::getTileCoords(uint16_t tile) {
|
||||
if (tile >= Project::getNumTilesTotal())
|
||||
if (tile >= Project::getNumTilesTotal() || this->numTilesWide == 0)
|
||||
{
|
||||
// Invalid tile.
|
||||
return QPoint(0, 0);
|
||||
@@ -233,30 +222,30 @@ QImage TilesetEditorTileSelector::buildPrimaryTilesIndexedImage() {
|
||||
if (!this->primaryTileset)
|
||||
return QImage();
|
||||
|
||||
return buildImage(0, this->primaryTileset->tiles.length());
|
||||
return buildImage(0, this->primaryTileset->numTiles());
|
||||
}
|
||||
|
||||
QImage TilesetEditorTileSelector::buildSecondaryTilesIndexedImage() {
|
||||
if (!this->secondaryTileset)
|
||||
return QImage();
|
||||
|
||||
return buildImage(Project::getNumTilesPrimary(), this->secondaryTileset->tiles.length());
|
||||
return buildImage(Project::getNumTilesPrimary(), this->secondaryTileset->numTiles());
|
||||
}
|
||||
|
||||
QImage TilesetEditorTileSelector::buildImage(int tileIdStart, int numTiles) {
|
||||
const int tileWidth = 8;
|
||||
const int tileHeight = 8;
|
||||
if (this->numTilesWide == 0)
|
||||
return QImage();
|
||||
|
||||
int height = qCeil(numTiles / static_cast<double>(this->numTilesWide));
|
||||
QImage image(this->numTilesWide * tileWidth, height * tileHeight, QImage::Format_RGBA8888);
|
||||
QImage image(this->numTilesWide * Tile::pixelWidth(), height * Tile::pixelHeight(), QImage::Format_RGBA8888);
|
||||
image.fill(0);
|
||||
|
||||
QPainter painter(&image);
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
QImage tileImage = getGreyscaleTileImage(tileIdStart + i, this->primaryTileset, this->secondaryTileset);
|
||||
int y = i / this->numTilesWide;
|
||||
int x = i % this->numTilesWide;
|
||||
QPoint origin = QPoint(x * tileWidth, y * tileHeight);
|
||||
painter.drawImage(origin, tileImage);
|
||||
int x = (i % this->numTilesWide) * Tile::pixelWidth();
|
||||
int y = (i / this->numTilesWide) * Tile::pixelHeight();
|
||||
painter.drawImage(x, y, tileImage);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
|
||||
@@ -2,24 +2,24 @@
|
||||
#include <QWheelEvent>
|
||||
|
||||
UIntSpinBox::UIntSpinBox(QWidget *parent)
|
||||
: QAbstractSpinBox(parent)
|
||||
: QAbstractSpinBox(parent),
|
||||
m_minimum(0),
|
||||
m_maximum(99),
|
||||
m_value(m_minimum),
|
||||
m_singleStep(1),
|
||||
m_displayIntegerBase(10),
|
||||
m_hasPadding(false),
|
||||
m_numChars(2)
|
||||
{
|
||||
// Don't let scrolling hijack focus.
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
m_minimum = 0;
|
||||
m_maximum = 99;
|
||||
m_value = m_minimum;
|
||||
m_displayIntegerBase = 10;
|
||||
m_numChars = 2;
|
||||
m_hasPadding = false;
|
||||
|
||||
this->updateEdit();
|
||||
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onEditFinished()));
|
||||
};
|
||||
|
||||
void UIntSpinBox::setValue(uint32_t val) {
|
||||
val = qMax(m_minimum, qMin(m_maximum, val));
|
||||
val = qBound(m_minimum, val, m_maximum);
|
||||
if (m_value != val) {
|
||||
m_value = val;
|
||||
emit valueChanged(m_value);
|
||||
@@ -69,6 +69,12 @@ void UIntSpinBox::setRange(uint32_t min, uint32_t max) {
|
||||
this->updateEdit();
|
||||
}
|
||||
|
||||
void UIntSpinBox::setSingleStep(uint32_t val) {
|
||||
if (m_singleStep != val) {
|
||||
m_singleStep = val;
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::setPrefix(const QString &prefix) {
|
||||
if (m_prefix != prefix) {
|
||||
m_prefix = prefix;
|
||||
@@ -132,6 +138,7 @@ void UIntSpinBox::onEditFinished() {
|
||||
}
|
||||
|
||||
void UIntSpinBox::stepBy(int steps) {
|
||||
steps *= m_singleStep;
|
||||
auto newValue = m_value;
|
||||
if (steps < 0 && newValue + steps > newValue) {
|
||||
newValue = 0;
|
||||
|
||||
@@ -367,7 +367,7 @@ QChart* WildMonChart::createLevelDistributionChart() {
|
||||
series->attachAxis(axisY);
|
||||
|
||||
// We round the y-axis max up to a multiple of 5.
|
||||
axisY->setMax(Util::roundUp(qCeil(axisY->max()), 5));
|
||||
axisY->setMax(Util::roundUpToMultiple(qCeil(axisY->max()), 5));
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user