Merge branch 'dev' of https://github.com/huderlem/porymap into version-check

This commit is contained in:
GriffinR
2025-04-30 12:03:13 -04:00
113 changed files with 3562 additions and 2148 deletions

View File

@@ -21,20 +21,23 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Add an input field to the Tileset Editor for editing the full metatile attributes value directly, including unused bits.
- An alert will be displayed when attempting to open a seemingly invalid project.
- Add support for defining project values with `enum` where `#define` was expected.
- Add support for referring to object events and warps with named IDs, rather than referring to them with their index number.
- Add a setting to specify the tile values to use for the unused metatile layer.
- Add a setting to specify the maximum number of events in a group. A warning will be shown if too many events are added.
- Add a setting to customize the size and position of the player view distance.
- Add `onLayoutOpened` to the scripting API.
- Add a splash loading screen for project openings.
### Changed
- `Change Dimensions` now has an interactive resizing rectangle.
- Redesigned the new map dialog, including better error checking and a collapsible section for header data.
- New maps/layouts are no longer saved automatically, and can be fully discarded by closing without saving.
- Map groups and ``MAPSEC`` names specified when creating a new map will be added automatically if they don't already exist.
- Custom fields in JSON files that Porymap writes are no longer discarded.
- Edits to map connections now have Undo/Redo and can be viewed in exported timelapses.
- Changes to the "Mirror to Connecting Maps" setting will now be saved between sessions.
- A notice will be displayed when attempting to open the "Dynamic" map, rather than nothing happening.
- The base game version is now auto-detected if the project name contains only one of "emerald", "firered/leafgreen", or "ruby/sapphire".
- The max encounter rate is now read from the project, rather than assuming the default value from RSE.
- It's now possible to cancel quitting if there are unsaved changes in sub-windows.
- The triple-layer metatiles setting can now be set automatically using a project constant.
- `Export Map Stitch Image` and `Export Map Timelapse Image` now show a preview of the full image/gif, not just the current map.
@@ -49,6 +52,10 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- `Script` dropdowns now autocomplete only with scripts from the current map, rather than every script in the project. The old behavior is available via a new setting.
- The options for `Encounter Type` and `Terrain Type` in the Tileset Editor are not hardcoded anymore, they're now read from the project.
- The `symbol_wild_encounters` setting was replaced; this value is now read from the project.
- The max encounter rate is now read from the project, rather than assuming the default value from RSE.
- `MAP_OFFSET_W` and `MAP_OFFSET_H` (used to limit the maximum map size) are now read from the project.
- The rendered area of the map border is now limited to the maximum player view distance (prior to this it included two extra rows on the top and bottom).
- An error message will now be shown when Porymap is unable to save changes (e.g. if Porymap doesn't have write permissions for your project).
- A project may now be opened even if it has no maps or map groups. A minimum of one map layout is required.
- The file extensions that are expected for `.png` and `.pal` data files and the extensions outputted when creating a new tileset can now be customized.
- Miscellaneous performance improvements, especially for opening projects.
@@ -57,12 +64,18 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix `Add Region Map...` not updating the region map settings file.
- Fix some crashes on invalid region map tilesets.
- Improve error reporting for invalid region map editor settings.
- Fix the region map editor's palette resetting between region maps.
- Fix the region map editor's h-flip and v-flip settings being swapped.
- Fix config files being written before the project is opened successfully.
- Fix the map and other project info still displaying if a new project fails to open.
- Fix unsaved changes being ignored when quitting (such as with Cmd+Q on macOS).
- Fix selections with multiple Events not always clearing when making a new selection.
- Fix selections with multiple events not always clearing when making a new selection.
- Fix the new event button not updating correctly when selecting object events.
- Fix duplicated `Hidden Item` events not copying the `Requires Itemfinder` field.
- Fix event sprites disappearing in certain areas outside the map boundaries.
- Fix deselecting an event still allowing you to drag the event around.
- Fix events rendering on top of the ruler at very high y values.
- Fix new map names not appearing in event dropdowns that have already been populated.
- Fix `About porymap` opening a new window each time it's activated.
- Fix the `Edit History` window not raising to the front when reactivated.
- New maps are now always inserted in map dropdowns at the correct position, rather than at the bottom of the list until the project is reloaded.

View File

@@ -97,6 +97,8 @@ In addition to these files, there are some specific symbol and macro names that
``define_pals_total``, ``NUM_PALS_TOTAL``,
``define_tiles_per_metatile``, ``NUM_TILES_PER_METATILE``, to determine if triple-layer metatiles are in use. Values other than 8 or 12 are ignored
``define_map_size``, ``MAX_MAP_DATA_SIZE``, to limit map dimensions
``define_map_offset_width``, ``MAP_OFFSET_W``, to limit map dimensions
``define_map_offset_height``, ``MAP_OFFSET_H``, to limit map dimensions
``define_mask_metatile``, ``MAPGRID_METATILE_ID_MASK``, optionally read to get settings on ``Maps`` tab
``define_mask_collision``, ``MAPGRID_COLLISION_MASK``, optionally read to get settings on ``Maps`` tab
``define_mask_elevation``, ``MAPGRID_ELEVATION_MASK``, optionally read to get settings on ``Maps`` tab

View File

@@ -204,7 +204,7 @@ Callbacks
Called when the mouse exits the map.
.. js:function:: onMapResized(oldWidth, oldHeight, newWidth, newHeight)
.. js:function:: onMapResized(oldWidth, oldHeight, delta)
Called when the dimensions of the map are changed.
@@ -212,10 +212,8 @@ Callbacks
:type oldWidth: number
:param oldHeight: the height of the map before the change
:type oldHeight: number
:param newWidth: the width of the map after the change
:type newWidth: number
:param newHeight: the height of the map after the change
:type newHeight: number
:param delta: the amount the map size changed in each direction. The object's shape is ``{left, right, top, bottom}``
:type prevBlock: delta
.. js:function:: onBorderResized(oldWidth, oldHeight, newWidth, newHeight)

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>178</width>
<width>188</width>
<height>157</height>
</rect>
</property>
@@ -20,7 +20,7 @@
<string notr="true">.ConnectionsListItem { border-width: 1px; }</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
@@ -65,7 +65,7 @@
<item row="3" column="2">
<widget class="QToolButton" name="button_Delete">
<property name="toolTip">
<string>Remove this connection.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Remove this connection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
@@ -79,28 +79,28 @@
<item row="1" column="1" colspan="2">
<widget class="NoScrollComboBox" name="comboBox_Direction">
<property name="toolTip">
<string>Where the connected map should be positioned relative to the current map.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Where the connected map should be positioned relative to the current map.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="NoScrollComboBox" name="comboBox_Map">
<property name="toolTip">
<string>The name of the map to connect to the current map.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The name of the map to connect to the current map.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="NoScrollSpinBox" name="spinBox_Offset">
<property name="toolTip">
<string>The number of spaces to move the connected map perpendicular to its connected direction.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of spaces to move the connected map perpendicular to its connected direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QToolButton" name="button_OpenMap">
<property name="toolTip">
<string>Open the connected map.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Open the connected map.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>

View File

@@ -33,7 +33,7 @@
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_Name">
<property name="toolTip">
<string>The key name for the new JSON field</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The key name for the new JSON field&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -50,7 +50,7 @@
<item row="2" column="1">
<widget class="NoScrollComboBox" name="comboBox_Type">
<property name="toolTip">
<string>The data type for the new JSON field</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The data type for the new JSON field&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -70,7 +70,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>The value for the new JSON field</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The value for the new JSON field&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<widget class="QWidget" name="page_String">
<layout class="QHBoxLayout" name="horizontalLayout">

View File

@@ -60,7 +60,7 @@
<item>
<widget class="QPushButton" name="button_CreateNewScript">
<property name="toolTip">
<string>Create a new Porymap script file with a default template</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Create a new Porymap script file with a default template&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Create New Script...</string>
@@ -74,7 +74,7 @@
<item>
<widget class="QPushButton" name="button_LoadScript">
<property name="toolTip">
<string>Add an existing script file to the list below</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add an existing script file to the list below&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Load Script...</string>
@@ -88,7 +88,7 @@
<item>
<widget class="QPushButton" name="button_RefreshScripts">
<property name="toolTip">
<string>Refresh all loaded scripts to account for any recent edits</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Refresh all loaded scripts to account for any recent edits&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Refresh Scripts</string>

164
forms/loadingscreen.ui Normal file
View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoadingScreen</class>
<widget class="QWidget" name="LoadingScreen">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>255</height>
</rect>
</property>
<property name="cursor">
<cursorShape>BusyCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelPorymap">
<property name="font">
<font>
<pointsize>20</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>porymap</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelVersion">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Version X.x.x</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelPixmap">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string>IMAGE</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelLoading">
<property name="text">
<string>Loading.....</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelText">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -260,9 +260,6 @@
<bool>false</bool>
</property>
<widget class="QWidget" name="tab_Map">
<property name="toolTip">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<property name="leftMargin">
<number>0</number>
@@ -351,12 +348,6 @@
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="transformationAnchor">
<enum>QGraphicsView::ViewportAnchor::AnchorUnderMouse</enum>
</property>
<property name="resizeAnchor">
<enum>QGraphicsView::ViewportAnchor::AnchorUnderMouse</enum>
</property>
</widget>
</item>
</layout>
@@ -2386,7 +2377,7 @@
<item row="0" column="7">
<widget class="QCheckBox" name="checkBox_MirrorConnections">
<property name="toolTip">
<string>If enabled, connections will automatically be updated on the connected map.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, the connected Emerge and/or Dive maps will be displayed with an opacity set using the slider.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Mirror to Connecting Maps</string>
@@ -2435,8 +2426,11 @@
</item>
<item row="1" column="0">
<widget class="QToolButton" name="button_OpenDiveMap">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Open the selected Dive Map</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Open the selected Dive Map&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
@@ -2450,7 +2444,7 @@
<item row="0" column="4" rowspan="2">
<widget class="QGroupBox" name="groupBox_DiveMapOpacity">
<property name="toolTip">
<string>If enabled, the connected Emerge and/or Dive maps will be displayed with an opacity set using the slider.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, the connected Emerge and/or Dive maps will be displayed with an opacity set using the slider.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
<string>Show Emerge/Dive Maps</string>
@@ -2569,8 +2563,11 @@
</item>
<item row="0" column="0">
<widget class="QToolButton" name="button_OpenEmergeMap">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Open the selected Emerge Map</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Open the selected Emerge Map&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
@@ -2589,7 +2586,7 @@
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QGraphicsView" name="graphicsView_Connections">
<widget class="ConnectionsView" name="graphicsView_Connections">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@@ -2863,6 +2860,7 @@
<addaction name="menuOpen_Recent_Project"/>
<addaction name="action_Reload_Project"/>
<addaction name="action_Close_Project"/>
<addaction name="separator"/>
<addaction name="action_Save"/>
<addaction name="action_Save_Project"/>
<addaction name="separator"/>
@@ -2887,8 +2885,10 @@
<addaction name="actionCursor_Tile_Outline"/>
<addaction name="actionPlayer_View_Rectangle"/>
<addaction name="actionBetter_Cursors"/>
<addaction name="separator"/>
<addaction name="actionDive_Emerge_Map"/>
<addaction name="actionShow_Events_In_Map_View"/>
<addaction name="separator"/>
<addaction name="actionShow_Grid"/>
<addaction name="actionGrid_Settings"/>
</widget>
@@ -3076,45 +3076,6 @@
<string>Ctrl+T</string>
</property>
</action>
<action name="actionSort_by_Location">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/sort_alphabet.ico</normaloff>:/icons/sort_alphabet.ico</iconset>
</property>
<property name="text">
<string>Sort by &amp;Location</string>
</property>
</action>
<action name="actionSort_by_Group">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/sort_number.ico</normaloff>:/icons/sort_number.ico</iconset>
</property>
<property name="text">
<string>Sort by &amp;Group</string>
</property>
<property name="toolTip">
<string>Sort by Group</string>
</property>
</action>
<action name="actionSort_by_Layout">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/sort_map.ico</normaloff>:/icons/sort_map.ico</iconset>
</property>
<property name="text">
<string>Sort by &amp;Layout</string>
</property>
</action>
<action name="actionAbout_Porymap">
<property name="text">
<string>About Porymap...</string>
@@ -3310,9 +3271,14 @@
</customwidget>
<customwidget>
<class>MapView</class>
<extends>QWidget</extends>
<extends>QGraphicsView</extends>
<header>mapview.h</header>
</customwidget>
<customwidget>
<class>ConnectionsView</class>
<extends>QGraphicsView</extends>
<header>graphicsview.h</header>
</customwidget>
<customwidget>
<class>MapTree</class>
<extends>QTreeView</extends>
@@ -3321,7 +3287,7 @@
<customwidget>
<class>NoScrollGraphicsView</class>
<extends>QGraphicsView</extends>
<header>mapview.h</header>
<header>graphicsview.h</header>
</customwidget>
<customwidget>
<class>MapListToolBar</class>

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>407</width>
<height>349</height>
<height>380</height>
</rect>
</property>
<property name="windowTitle">
@@ -224,7 +224,11 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_LocationName"/>
<widget class="QLineEdit" name="lineEdit_LocationName">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The name that will be displayed in-game for this Location. This name will be shared with any other map that has the same Location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@@ -32,7 +32,7 @@
<item>
<widget class="QToolButton" name="button_AddFolder">
<property name="toolTip">
<string>Add a new folder to the list.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add a new folder to the list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
@@ -73,7 +73,7 @@
<item>
<widget class="QToolButton" name="button_ExpandAll">
<property name="toolTip">
<string>Expand all folders in the list.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Expand all folders in the list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
@@ -93,7 +93,7 @@
<item>
<widget class="QToolButton" name="button_CollapseAll">
<property name="toolTip">
<string>Collapse all folders in the list.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Collapse all folders in the list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
@@ -113,7 +113,7 @@
<item>
<widget class="QToolButton" name="button_ToggleEdit">
<property name="toolTip">
<string>If enabled, folders may be renamed and items in the list may be rearranged.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, folders may be renamed and items in the list may be rearranged.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>

89
forms/newdefinedialog.ui Normal file
View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewDefineDialog</class>
<widget class="QDialog" name="NewDefineDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>252</width>
<height>124</height>
</rect>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="form">
<layout class="QFormLayout" name="formLayout">
<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="0" column="0">
<widget class="QLabel" name="label_Name">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_Name">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_NameError">
<property name="visible">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(255, 0, 0)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_Value"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_Value">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -17,10 +17,10 @@
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
<enum>QFrame::Shadow::Plain</enum>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="leftMargin">
@@ -45,7 +45,7 @@
<item row="0" column="1">
<widget class="NoScrollComboBox" name="comboBox_Map">
<property name="toolTip">
<string>The name of the map to connect to the current map.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The name of the map to connect to the current map.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -59,7 +59,7 @@
<item row="1" column="1">
<widget class="NoScrollComboBox" name="comboBox_Direction">
<property name="toolTip">
<string>Where the connected map should be positioned relative to the current map.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Where the connected map should be positioned relative to the current map.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -82,10 +82,10 @@
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>

View File

@@ -40,7 +40,7 @@
<item>
<widget class="QCheckBox" name="checkBox_MonitorProjectFiles">
<property name="toolTip">
<string>If checked, a prompt to reload your project will appear if relevant project files are edited</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, a prompt to reload your project will appear if relevant project files are edited&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Monitor project files</string>
@@ -50,7 +50,7 @@
<item>
<widget class="QCheckBox" name="checkBox_OpenRecentProject">
<property name="toolTip">
<string>If checked, Porymap will automatically open your most recently opened project on startup</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, Porymap will automatically open your most recently opened project on startup&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Open recent project on launch</string>
@@ -60,7 +60,7 @@
<item>
<widget class="QCheckBox" name="checkBox_CheckForUpdates">
<property name="toolTip">
<string>If checked, Porymap will automatically alert you on startup if a new release is available</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, Porymap will automatically alert you on startup if a new release is available&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Automatically check for updates</string>
@@ -112,7 +112,7 @@
<item>
<widget class="QCheckBox" name="checkBox_DisableEventWarning">
<property name="toolTip">
<string>If checked, no warning will be shown when deleting an event that has an associated #define that may also be deleted.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, no warning will be shown when deleting an event that has an associated #define that may also be deleted.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Disable warning when deleting events with IDs</string>
@@ -138,7 +138,7 @@
<item>
<widget class="QRadioButton" name="radioButton_OnSprite">
<property name="toolTip">
<string>If enabled, an event can be selected by clicking directly on the opaque pixels of its sprite. This may be preferable when events are overlapping.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, an event can be selected by clicking directly on the opaque pixels of its sprite. This may be preferable when events are overlapping.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Select by clicking on sprite</string>
@@ -148,7 +148,7 @@
<item>
<widget class="QRadioButton" name="radioButton_WithinRect">
<property name="toolTip">
<string>If enabled, an event can be selected by clicking anywhere within its sprite dimensions. This may be preferable for events with small or mostly transparent sprites.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, an event can be selected by clicking anywhere within its sprite dimensions. This may be preferable for events with small or mostly transparent sprites.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Select by clicking within bounding rectangle</string>
@@ -231,7 +231,7 @@
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_TextEditorOpenFolder">
<property name="toolTip">
<string>The shell command for your preferred text editor (possibly an absolute path if the program doesn't exist in your PATH).</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The shell command for your preferred text editor (possibly an absolute path if the program doesn't exist in your PATH).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="placeholderText">
<string>e.g. code %D</string>
@@ -264,7 +264,7 @@
<item row="5" column="1">
<widget class="QLineEdit" name="lineEdit_TextEditorGotoLine">
<property name="toolTip">
<string>The shell command for your preferred text editor to open a file to a specific line number (possibly an absolute path if the program doesn't exist in your PATH).</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The shell command for your preferred text editor to open a file to a specific line number (possibly an absolute path if the program doesn't exist in your PATH).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="placeholderText">
<string>e.g. code --goto %F:%L</string>

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>631</width>
<height>600</height>
<width>642</width>
<height>609</height>
</rect>
</property>
<property name="windowTitle">
@@ -38,8 +38,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>568</height>
<width>570</width>
<height>692</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
@@ -52,7 +52,7 @@
<item>
<widget class="QCheckBox" name="checkBox_UsePoryscript">
<property name="toolTip">
<string>Whether map script files should prefer using .pory</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether map script files should prefer using .pory&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Poryscript</string>
@@ -61,11 +61,24 @@
</item>
<item>
<widget class="QCheckBox" name="checkBox_ShowWildEncounterTables">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, Porymap will display wild encounter data on the Wild Pokémon tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show Wild Encounter Tables</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_PreserveMatchingOnlyData">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled, Porymap will not discard data like &amp;quot;connections_include_order&amp;quot; or &amp;quot;name_clone&amp;quot;, which serve no purpose other than recreating the original game.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Preserve data only needed to match the original game</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -89,7 +102,7 @@
<item row="1" column="0">
<widget class="QPushButton" name="button_ImportDefaultPrefabs">
<property name="toolTip">
<string>Restore the data in the prefabs file to the version defaults. Will create a new file if one doesn't exist.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Restore the data in the prefabs file to the version defaults. Will create a new file if one doesn't exist.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Import Defaults</string>
@@ -99,7 +112,7 @@
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_PrefabsPath">
<property name="toolTip">
<string>The file that will be used to populate the Prefabs tab</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The file that will be used to populate the Prefabs tab&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="placeholderText">
<string>prefabs.json</string>
@@ -138,7 +151,7 @@
<item row="0" column="1" colspan="6">
<widget class="QLineEdit" name="lineEdit_CollisionGraphics">
<property name="toolTip">
<string>The image sheet that will be used to represent elevation and collision on the Collision tab</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The image sheet that will be used to represent elevation and collision on the Collision tab&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -166,7 +179,7 @@
<item row="1" column="5">
<widget class="NoScrollSpinBox" name="spinBox_MaxCollision">
<property name="toolTip">
<string>The maximum collision value represented with an icon on the image sheet</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The maximum collision value represented with an icon on the image sheet&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -187,7 +200,7 @@
<item row="1" column="2">
<widget class="NoScrollSpinBox" name="spinBox_MaxElevation">
<property name="toolTip">
<string>The maximum elevation value represented with an icon on the image sheet</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The maximum elevation value represented with an icon on the image sheet&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -260,7 +273,7 @@
<item row="1" column="2" colspan="3">
<widget class="QLineEdit" name="lineEdit_PokemonIcon">
<property name="toolTip">
<string>The icon that will be displayed on the Wild Pokémon tab for the above species</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The icon that will be displayed on the Wild Pokémon tab for the above species&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -270,6 +283,91 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_PlayerViewDistance">
<property name="title">
<string>Player View Distance</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="formLayout_PlayerViewDistanceCol1">
<item row="0" column="0">
<widget class="QLabel" name="label_PlayerViewDistance_North">
<property name="text">
<string>North</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_PlayerViewDistance_South">
<property name="text">
<string>South</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="NoScrollSpinBox" name="spinBox_PlayerViewDistance_North">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The distance (in pixels) that a player is able to see North of their character's position in-game. By default this is the distance from the center 16x16 to the edge of the 160 pixel tall GBA screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="NoScrollSpinBox" name="spinBox_PlayerViewDistance_South">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The distance (in pixels) that a player is able to see South of their character's position in-game. By default this is the distance from the center 16x16 to the edge of the 160 pixel tall GBA screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_PlayerViewDistanceCol2">
<item row="0" column="0">
<widget class="QLabel" name="label_PlayerViewDistance_West">
<property name="text">
<string>West</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_PlayerViewDistance_East">
<property name="text">
<string>East</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="NoScrollSpinBox" name="spinBox_PlayerViewDistance_West">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The distance (in pixels) that a player is able to see West of their character's position in-game. By default this is the distance from the center 16x16 to the edge of the 240 pixel wide GBA screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="NoScrollSpinBox" name="spinBox_PlayerViewDistance_East">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The distance (in pixels) that a player is able to see East of their character's position in-game. By default this is the distance from the center 16x16 to the edge of the 240 pixel wide GBA screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_Warning">
<property name="styleSheet">
@@ -368,8 +466,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>560</height>
<width>570</width>
<height>622</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
@@ -379,37 +477,6 @@
<string>Map Data Defaults</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="1" colspan="3">
<widget class="QWidget" name="widget_CustomSizeBorderMetatiles" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="NoScrollSpinBox" name="spinBox_FillMetatile">
<property name="toolTip">
<string>The default metatile value that will be used to fill new maps</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_Elevation">
<property name="text">
@@ -417,6 +484,50 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_MapWidth">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="NoScrollSpinBox" name="spinBox_Elevation">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default elevation that will be used to fill new maps&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox_CreateTextFile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether a separate text.inc or text.pory file will be created for new maps, alongside the scripts file&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Create separate text file</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="NoScrollSpinBox" name="spinBox_MapWidth">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default layout width for new maps&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="NoScrollSpinBox" name="spinBox_MapHeight">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default layout height for new maps&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_FillMetatile">
<property name="text">
@@ -424,13 +535,10 @@
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox_CreateTextFile">
<property name="toolTip">
<string>Whether a separate text.inc or text.pory file will be created for new maps, alongside the scripts file</string>
</property>
<item row="4" column="0">
<widget class="QLabel" name="label_Collision">
<property name="text">
<string>Create separate text file</string>
<string>Collision</string>
</property>
</widget>
</item>
@@ -441,6 +549,20 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="UIntHexSpinBox" name="spinBox_FillMetatile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default metatile value that will be used to fill new maps&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="NoScrollSpinBox" name="spinBox_Collision">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default collision that will be used to fill new maps&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QStackedWidget" name="stackedWidget_BorderMetatiles">
<widget class="QWidget" name="page_CustomBorderSize">
@@ -460,7 +582,7 @@
<item>
<widget class="QLineEdit" name="lineEdit_BorderMetatiles">
<property name="toolTip">
<string>A comma-separated list of metatile values that will be used to fill new map borders</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A comma-separated list of metatile values that will be used to fill new map borders&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -481,54 +603,30 @@
<number>0</number>
</property>
<item>
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile1">
<widget class="UIntHexSpinBox" name="spinBox_BorderMetatile1">
<property name="toolTip">
<string>The default metatile value that will be used for the top-left border metatile on new maps.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="displayIntegerBase">
<number>16</number>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default metatile value that will be used for the top-left border metatile on new maps.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile2">
<widget class="UIntHexSpinBox" name="spinBox_BorderMetatile2">
<property name="toolTip">
<string>The default metatile value that will be used for the top-right border metatile on new maps.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="displayIntegerBase">
<number>16</number>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default metatile value that will be used for the top-right border metatile on new maps.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile3">
<widget class="UIntHexSpinBox" name="spinBox_BorderMetatile3">
<property name="toolTip">
<string>The default metatile value that will be used for the bottom-left border metatile on new maps.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="displayIntegerBase">
<number>16</number>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default metatile value that will be used for the bottom-left border metatile on new maps.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile4">
<widget class="UIntHexSpinBox" name="spinBox_BorderMetatile4">
<property name="toolTip">
<string>The default metatile value that will be used for the bottom-right border metatile on new maps.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="displayIntegerBase">
<number>16</number>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default metatile value that will be used for the bottom-right border metatile on new maps.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -536,24 +634,28 @@
</widget>
</widget>
</item>
<item row="5" column="1">
<widget class="NoScrollSpinBox" name="spinBox_Elevation">
<property name="toolTip">
<string>The default elevation that will be used to fill new maps</string>
</property>
<item row="1" column="1" colspan="3">
<widget class="QWidget" name="widget_CustomSizeBorderMetatiles" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_Collision">
<item row="7" column="0">
<widget class="QLabel" name="label_MapHeight">
<property name="text">
<string>Collision</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="NoScrollSpinBox" name="spinBox_Collision">
<property name="toolTip">
<string>The default collision that will be used to fill new maps</string>
<string>Height</string>
</property>
</widget>
</item>
@@ -604,7 +706,7 @@
<item row="0" column="1">
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask">
<property name="toolTip">
<string>The mask used to read/write metatile IDs in map data.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write metatile IDs in map data.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -618,7 +720,7 @@
<item row="1" column="1">
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask">
<property name="toolTip">
<string>The mask used to read/write collision values in map data.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write collision values in map data.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -632,7 +734,7 @@
<item row="2" column="1">
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask">
<property name="toolTip">
<string>The mask used to read/write elevation values in map data.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write elevation values in map data.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -661,7 +763,7 @@
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_EnableAllowFlags">
<property name="toolTip">
<string>Whether &quot;Allow Running&quot;, &quot;Allow Biking&quot; and &quot;Allow Dig &amp; Escape Rope&quot; are default options for Map Headers</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether &amp;quot;Allow Running&amp;quot;, &amp;quot;Allow Biking&amp;quot; and &amp;quot;Allow Dig &amp;amp; Escape Rope&amp;quot; are default options for Map Headers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable 'Allow Running/Biking/Escaping'</string>
@@ -671,7 +773,7 @@
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_EnableFloorNumber">
<property name="toolTip">
<string>Whether &quot;Floor Number&quot; is a default option for Map Headers</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether &amp;quot;Floor Number&amp;quot; is a default option for Map Headers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable 'Floor Number'</string>
@@ -681,7 +783,7 @@
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_EnableCustomBorderSize">
<property name="toolTip">
<string>Whether the dimensions of the border can be changed. If not set, all borders are 2x2</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether the dimensions of the border can be changed. If not set, all borders are 2x2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Custom Border Size</string>
@@ -741,7 +843,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<width>570</width>
<height>798</height>
</rect>
</property>
@@ -760,7 +862,11 @@
</widget>
</item>
<item row="0" column="1">
<widget class="NoScrollComboBox" name="comboBox_DefaultPrimaryTileset"/>
<widget class="NoScrollComboBox" name="comboBox_DefaultPrimaryTileset">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default primary tileset to use for new maps/layouts.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_SecondaryTileset">
@@ -770,7 +876,11 @@
</widget>
</item>
<item row="1" column="1">
<widget class="NoScrollComboBox" name="comboBox_DefaultSecondaryTileset"/>
<widget class="NoScrollComboBox" name="comboBox_DefaultSecondaryTileset">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default secondary tileset to use for new maps/layouts.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
@@ -784,7 +894,7 @@
<item>
<widget class="QRadioButton" name="radioButton_RenderBlack">
<property name="toolTip">
<string>Fully transparent pixels will be rendered as black pixels (the Pokémon games do this by default)</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Fully transparent pixels will be rendered as black pixels (the Pokémon games do this by default)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Render as black</string>
@@ -794,7 +904,7 @@
<item>
<widget class="QRadioButton" name="radioButton_RenderFirstPalColor">
<property name="toolTip">
<string>Fully transparent pixels will be rendered using the first palette color (this the default behavior for the GBA)</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Fully transparent pixels will be rendered using the first palette color (this the default behavior for the GBA)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Render using first palette color</string>
@@ -820,7 +930,7 @@
<item row="0" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileNormal">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused bottom layer of Normal metatiles</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This raw tile value will be used to fill the unused bottom layer of Normal metatiles&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -834,7 +944,7 @@
<item row="1" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileCovered">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused top layer of Covered metatiles</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This raw tile value will be used to fill the unused top layer of Covered metatiles&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -848,7 +958,7 @@
<item row="2" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileSplit">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused middle layer of Split metatiles</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This raw tile value will be used to fill the unused middle layer of Split metatiles&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -892,22 +1002,19 @@
<item row="4" column="1">
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask">
<property name="toolTip">
<string>The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask">
<property name="toolTip">
<string>The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_AttributesSize">
<property name="toolTip">
<string>The number of bytes used per metatile for metatile attributes</string>
</property>
<property name="text">
<string>Attributes size (in bytes)</string>
</property>
@@ -938,6 +1045,9 @@
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_EnableTripleLayerMetatiles">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, metatiles will be interpreted as having 3 layers of 4 tiles each (12 tiles total) as opposed to the default 2 layers of 4 tiles each (8 total).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Triple Layer Metatiles</string>
</property>
@@ -946,7 +1056,7 @@
<item row="7" column="1">
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask">
<property name="toolTip">
<string>The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -973,7 +1083,7 @@
<item row="5" column="1">
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask">
<property name="toolTip">
<string>The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -986,6 +1096,9 @@
</item>
<item row="2" column="1">
<widget class="NoScrollComboBox" name="comboBox_AttributesSize">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of bytes each metatile has for metatile attributes. This is the metadata about each metatile like behvior, layer type, etc.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="editable">
<bool>false</bool>
</property>
@@ -1026,7 +1139,7 @@
<item>
<widget class="QCheckBox" name="checkBox_OutputCallback">
<property name="toolTip">
<string>Whether the C data outputted for new tilesets will include the &quot;callback&quot; field</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether the C data outputted for new tilesets will include the &amp;quot;callback&amp;quot; field&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Output 'callback' field</string>
@@ -1036,7 +1149,7 @@
<item>
<widget class="QCheckBox" name="checkBox_OutputIsCompressed">
<property name="toolTip">
<string>Whether the C data outputted for new tilesets will include the &quot;isCompressed&quot; field</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether the C data outputted for new tilesets will include the &amp;quot;isCompressed&amp;quot; field&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Output 'isCompressed' field</string>
@@ -1083,8 +1196,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>788</height>
<width>570</width>
<height>840</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
@@ -1111,7 +1224,7 @@
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_WarpsIcon">
<property name="toolTip">
<string>The icon that will be used to represent Warp events</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The icon that will be used to represent Warp events&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -1121,7 +1234,7 @@
<item row="4" column="1">
<widget class="QLineEdit" name="lineEdit_HealLocationsIcon">
<property name="toolTip">
<string>The icon that will be used to represent Heal Location events</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The icon that will be used to represent Heal Location events&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -1145,7 +1258,7 @@
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_ObjectsIcon">
<property name="toolTip">
<string>The icon that will be used to represent Object events that don't have their own sprite</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The icon that will be used to represent Object events that don't have their own sprite&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -1162,7 +1275,7 @@
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_TriggersIcon">
<property name="toolTip">
<string>The icon that will be used to represent Trigger events</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The icon that will be used to represent Trigger events&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -1172,7 +1285,7 @@
<item row="3" column="1">
<widget class="QLineEdit" name="lineEdit_BGsIcon">
<property name="toolTip">
<string>The icon that will be used to represent BG events</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The icon that will be used to represent BG events&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -1246,7 +1359,7 @@
<item row="1" column="2">
<widget class="QToolButton" name="button_RemoveWarpBehavior">
<property name="toolTip">
<string>Remove the current text from the list</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Remove the current text from the list&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
@@ -1270,7 +1383,7 @@
<item row="0" column="0" colspan="3">
<widget class="QCheckBox" name="checkBox_DisableWarning">
<property name="toolTip">
<string>If checked, Warp Events will not display a warning about incompatible metatile behaviors</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, Warp Events will not display a warning about incompatible metatile behaviors&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Disable Warning</string>
@@ -1281,9 +1394,9 @@
<widget class="NoScrollComboBox" name="comboBox_WarpBehaviors"/>
</item>
<item row="4" column="0" colspan="3">
<widget class="QTextEdit" name="textEdit_WarpBehaviors">
<widget class="NoScrollTextEdit" name="textEdit_WarpBehaviors">
<property name="toolTip">
<string>Metatile Behaviors on this list won't trigger warnings for Warp Events</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Metatile Behaviors on this list won't trigger warnings for Warp Events&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
@@ -1299,7 +1412,7 @@
<item row="1" column="1">
<widget class="QToolButton" name="button_AddWarpBehavior">
<property name="toolTip">
<string>Add the current text to the list</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add the current text to the list&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
@@ -1465,23 +1578,25 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>490</height>
<width>570</width>
<height>499</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QToolButton" name="button_HelpFiles">
<property name="text">
<string>...</string>
<layout class="QGridLayout" name="gridLayout_10" rowstretch="1,2,8">
<item row="0" column="1">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/help.ico</normaloff>:/icons/help.ico</iconset>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</widget>
</spacer>
</item>
<item>
<item row="2" column="0" colspan="3">
<widget class="QWidget" name="widget_ProjectPaths" native="true">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
@@ -1512,8 +1627,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>533</width>
<height>428</height>
<width>544</width>
<height>338</height>
</rect>
</property>
<layout class="QFormLayout" name="layout_ProjectPaths">
@@ -1533,6 +1648,34 @@
</layout>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="button_AddGlobalConstantsFile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add additional C files containing #defines or enums. These will be used to resolve unknown symbols during project launch.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Add Global Constants File...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="button_HelpFiles">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/help.ico</normaloff>:/icons/help.ico</iconset>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<layout class="QGridLayout" name="gridLayout_GlobalConstantsFiles"/>
</item>
</layout>
</widget>
</widget>
@@ -1554,12 +1697,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>490</height>
<width>570</width>
<height>499</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0">
<widget class="QToolButton" name="button_HelpIdentifiers">
<property name="text">
<string>...</string>
@@ -1570,7 +1713,7 @@
</property>
</widget>
</item>
<item>
<item row="2" column="0" colspan="3">
<widget class="QWidget" name="widget_Identifiers" native="true">
<layout class="QVBoxLayout" name="verticalLayout_20">
<property name="spacing">
@@ -1601,8 +1744,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>533</width>
<height>428</height>
<width>544</width>
<height>421</height>
</rect>
</property>
<layout class="QFormLayout" name="layout_Identifiers">
@@ -1622,6 +1765,36 @@
</layout>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="button_AddGlobalConstant">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add an additional #define name and expression. This may be used to evaluate other #defines during project launch.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Add Global Constant...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_7">
<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 row="1" column="0" colspan="3">
<layout class="QGridLayout" name="gridLayout_GlobalConstants"/>
</item>
</layout>
</widget>
</widget>
@@ -1651,6 +1824,11 @@
<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>

View File

@@ -21,7 +21,7 @@
</property>
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
<enum>QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
@@ -33,7 +33,7 @@
<item row="0" column="1">
<widget class="QLineEdit" name="config_alias">
<property name="toolTip">
<string>A nickname for this region map that will differentiate it from others (should be unique).</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A nickname for this region map that will differentiate it from others (should be unique).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@@ -131,7 +131,7 @@
</size>
</property>
<property name="toolTip">
<string>The height of the tilemap</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The height of the tilemap&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>255</number>
@@ -148,10 +148,10 @@
<item row="6" column="1">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
@@ -218,10 +218,10 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Path to the tilemap binary relative to the project root.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
@@ -269,10 +269,10 @@
<item row="10" column="1">
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
<enum>QFrame::Shadow::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
@@ -392,10 +392,10 @@
<item row="2" column="1">
<widget class="QFrame" name="frame_5">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="leftMargin">
@@ -487,7 +487,7 @@
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -517,7 +517,7 @@
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -590,7 +590,7 @@
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -617,7 +617,7 @@
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -646,7 +646,7 @@
<item row="9" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -659,10 +659,10 @@
<item row="9" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>

View File

@@ -145,7 +145,7 @@
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="minimumContentsLength">
<number>8</number>

View File

@@ -14,19 +14,20 @@
#include <QGraphicsPixmapItem>
#include "events.h"
#include "gridsettings.h"
static const QVersionNumber porymapVersion = QVersionNumber::fromString(PORYMAP_VERSION);
extern const QVersionNumber porymapVersion;
// In both versions the default new map border is a generic tree
#define DEFAULT_BORDER_RSE (QList<uint16_t>{0x1D4, 0x1D5, 0x1DC, 0x1DD})
#define DEFAULT_BORDER_FRLG (QList<uint16_t>{0x14, 0x15, 0x1C, 0x1D})
// Distance in pixels from the edge of a GBA screen (240x160) to the center 16x16 pixels.
#define GBA_H_DIST_TO_CENTER ((240-16)/2)
#define GBA_V_DIST_TO_CENTER ((160-16)/2)
#define CONFIG_BACKWARDS_COMPATABILITY
class KeyValueConfigBase
{
public:
void save();
bool save();
void load();
virtual ~KeyValueConfigBase();
virtual void reset() = 0;
@@ -36,9 +37,11 @@ protected:
virtual QMap<QString, QString> getKeyValueMap() = 0;
virtual void init() = 0;
virtual void setUnreadKeys() = 0;
bool getConfigBool(QString key, QString value);
int getConfigInteger(QString key, QString value, int min = INT_MIN, int max = INT_MAX, int defaultValue = 0);
uint32_t getConfigUint32(QString key, QString value, uint32_t min = 0, uint32_t max = UINT_MAX, uint32_t defaultValue = 0);
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);
};
class PorymapConfig: public KeyValueConfigBase
@@ -92,6 +95,7 @@ public:
this->rateLimitTimes.clear();
this->eventSelectionShapeMode = QGraphicsPixmapItem::MaskShape;
this->shownInGameReloadMessage = false;
this->gridSettings = GridSettings();
}
void addRecentProject(QString project);
void setRecentProjects(QStringList projects);
@@ -156,6 +160,7 @@ public:
QByteArray newMapDialogGeometry;
QByteArray newLayoutDialogGeometry;
bool shownInGameReloadMessage;
GridSettings gridSettings;
protected:
virtual QString getConfigFilepath() override;
@@ -214,6 +219,8 @@ enum ProjectIdentifier {
define_pals_total,
define_tiles_per_metatile,
define_map_size,
define_map_offset_width,
define_map_offset_height,
define_mask_metatile,
define_mask_collision,
define_mask_elevation,
@@ -317,18 +324,20 @@ public:
this->defaultMetatileId = 1;
this->defaultElevation = 3;
this->defaultCollision = 0;
this->defaultMapSize = QSize(20,20);
this->defaultPrimaryTileset = "gTileset_General";
this->prefabFilepath = QString();
this->prefabImportPrompted = false;
this->tilesetsHaveCallback = true;
this->tilesetsHaveIsCompressed = true;
this->setTransparentPixelsBlack = true;
this->preserveMatchingOnlyData = false;
this->filePaths.clear();
this->eventIconPaths.clear();
this->pokemonIconPaths.clear();
this->collisionSheetPath = QString();
this->collisionSheetWidth = 2;
this->collisionSheetHeight = 16;
this->collisionSheetSize = QSize(2, 16);
this->playerViewDistance = QMargins(GBA_H_DIST_TO_CENTER, GBA_V_DIST_TO_CENTER, GBA_H_DIST_TO_CENTER, GBA_V_DIST_TO_CENTER);
this->blockMetatileIdMask = 0x03FF;
this->blockCollisionMask = 0x0C00;
this->blockElevationMask = 0xF000;
@@ -337,6 +346,8 @@ public:
this->unusedTileSplit = 0x0000;
this->maxEventsPerGroup = 255;
this->forcedMajorVersion = 0;
this->globalConstantsFilepaths.clear();
this->globalConstants.clear();
this->identifiers.clear();
this->readKeys.clear();
}
@@ -382,6 +393,7 @@ public:
uint16_t defaultMetatileId;
uint16_t defaultElevation;
uint16_t defaultCollision;
QSize defaultMapSize;
QList<uint16_t> newMapBorderMetatileIds;
QString defaultPrimaryTileset;
QString defaultSecondaryTileset;
@@ -390,6 +402,7 @@ public:
bool tilesetsHaveCallback;
bool tilesetsHaveIsCompressed;
bool setTransparentPixelsBlack;
bool preserveMatchingOnlyData;
int metatileAttributesSize;
uint32_t metatileBehaviorMask;
uint32_t metatileTerrainTypeMask;
@@ -403,11 +416,13 @@ public:
uint16_t unusedTileSplit;
bool mapAllowFlagsEnabled;
QString collisionSheetPath;
int collisionSheetWidth;
int collisionSheetHeight;
QSize collisionSheetSize;
QMargins playerViewDistance;
QList<uint32_t> warpBehaviors;
int maxEventsPerGroup;
int forcedMajorVersion;
QStringList globalConstantsFilepaths;
QMap<QString,QString> globalConstants;
protected:
virtual QString getConfigFilepath() override;

View File

@@ -14,7 +14,7 @@ class Map;
class Layout;
class Blockdata;
class Event;
class DraggablePixmapItem;
class EventPixmapItem;
class Editor;
enum CommandId {

View File

@@ -10,6 +10,7 @@
#include <QPointer>
#include "orderedjson.h"
#include "parseutil.h"
class Project;
@@ -18,7 +19,7 @@ class EventFrame;
class ObjectFrame;
class CloneObjectFrame;
class WarpFrame;
class DraggablePixmapItem;
class EventPixmapItem;
class Event;
class ObjectEvent;
@@ -78,9 +79,13 @@ public:
None,
};
// all event groups except warps have IDs that start at 1
// Normally we refer to events using their index in the list of that group's events.
// Object events often get referred to with a special "local ID", which is really just the index + 1.
// We use this local ID number in the index spinner for object events instead of the actual index.
// This distinction is only really important for object and warp events, because these are normally
// the only two groups of events that need to be explicitly referred to.
static int getIndexOffset(Event::Group group) {
return (group == Event::Group::Warp) ? 0 : 1;
return (group == Event::Group::Object) ? 1 : 0;
}
static Event::Group typeToGroup(Event::Type type) {
@@ -139,23 +144,22 @@ public:
Event::Type getEventType() const { return this->eventType; }
virtual OrderedJson::object buildEventJson(Project *project) = 0;
virtual bool loadFromJson(const QJsonObject &json, Project *project) = 0;
virtual bool loadFromJson(QJsonObject json, Project *project) = 0;
virtual void setDefaultValues(Project *project);
virtual QSet<QString> getExpectedFields() = 0;
void readCustomAttributes(const QJsonObject &json);
void addCustomAttributesTo(OrderedJson::object *obj) const;
const QMap<QString, QJsonValue> getCustomAttributes() const { return this->customAttributes; }
void setCustomAttributes(const QMap<QString, QJsonValue> newCustomAttributes) { this->customAttributes = newCustomAttributes; }
virtual void loadPixmap(Project *project);
QJsonObject getCustomAttributes() const { return this->customAttributes; }
void setCustomAttributes(const QJsonObject &newCustomAttributes) { this->customAttributes = newCustomAttributes; }
virtual QPixmap loadPixmap(Project *project);
void setPixmap(QPixmap newPixmap) { this->pixmap = newPixmap; }
QPixmap getPixmap() const { return this->pixmap; }
void setPixmapItem(DraggablePixmapItem *item);
DraggablePixmapItem *getPixmapItem() const { return this->pixmapItem; }
void setPixmapItem(EventPixmapItem *item);
EventPixmapItem *getPixmapItem() const { return this->pixmapItem; }
void setUsesDefaultPixmap(bool newUsesDefaultPixmap) { this->usesDefaultPixmap = newUsesDefaultPixmap; }
bool getUsesDefaultPixmap() const { return this->usesDefaultPixmap; }
@@ -191,12 +195,16 @@ protected:
// When deleting events like this we want to warn the user that the #define may also be deleted.
QString idName;
QMap<QString, QJsonValue> customAttributes;
QJsonObject customAttributes;
QPixmap pixmap;
DraggablePixmapItem *pixmapItem = nullptr;
EventPixmapItem *pixmapItem = nullptr;
QPointer<EventFrame> eventFrame;
static QString readString(QJsonObject *object, const QString &key) { return ParseUtil::jsonToQString(object->take(key)); }
static int readInt(QJsonObject *object, const QString &key) { return ParseUtil::jsonToInt(object->take(key)); }
static bool readBool(QJsonObject *object, const QString &key) { return ParseUtil::jsonToBool(object->take(key)); }
};
@@ -219,13 +227,13 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
virtual QSet<QString> getExpectedFields() override;
virtual void loadPixmap(Project *project) override;
virtual QPixmap loadPixmap(Project *project) override;
void setGfx(QString newGfx) { this->gfx = newGfx; }
QString getGfx() const { return this->gfx; }
@@ -286,23 +294,23 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
virtual QSet<QString> getExpectedFields() override;
virtual void loadPixmap(Project *project) override;
virtual QPixmap loadPixmap(Project *project) override;
void setTargetMap(QString newTargetMap) { this->targetMap = newTargetMap; }
QString getTargetMap() const { return this->targetMap; }
void setTargetID(int newTargetID) { this->targetID = newTargetID; }
int getTargetID() const { return this->targetID; }
void setTargetID(QString newTargetID) { this->targetID = newTargetID; }
QString getTargetID() const { return this->targetID; }
private:
QString targetMap;
int targetID = 0;
QString targetID;
};
@@ -324,7 +332,7 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
@@ -361,7 +369,7 @@ public:
virtual EventFrame *createEventFrame() override = 0;
virtual OrderedJson::object buildEventJson(Project *project) override = 0;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override = 0;
virtual bool loadFromJson(QJsonObject json, Project *project) override = 0;
virtual void setDefaultValues(Project *project) override = 0;
@@ -389,7 +397,7 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
@@ -429,7 +437,7 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
@@ -460,7 +468,7 @@ public:
virtual EventFrame *createEventFrame() override = 0;
virtual OrderedJson::object buildEventJson(Project *project) override = 0;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override = 0;
virtual bool loadFromJson(QJsonObject json, Project *project) override = 0;
virtual void setDefaultValues(Project *project) override = 0;
@@ -487,7 +495,7 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
@@ -522,7 +530,7 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
@@ -567,7 +575,7 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
@@ -599,12 +607,15 @@ public:
virtual EventFrame *createEventFrame() override;
virtual OrderedJson::object buildEventJson(Project *project) override;
virtual bool loadFromJson(const QJsonObject &, Project *) override;
virtual bool loadFromJson(QJsonObject json, Project *project) override;
virtual void setDefaultValues(Project *project) override;
virtual QSet<QString> getExpectedFields() override;
void setHostMapName(QString newHostMapName) { this->hostMapName = newHostMapName; }
QString getHostMapName() const;
void setRespawnMapName(QString newRespawnMapName) { this->respawnMapName = newRespawnMapName; }
QString getRespawnMapName() const { return this->respawnMapName; }
@@ -614,6 +625,7 @@ public:
private:
QString respawnMapName;
QString respawnNPC;
QString hostMapName; // Only needed if the host map fails to load.
};

View File

@@ -22,10 +22,6 @@
#define MAX_BORDER_WIDTH 255
#define MAX_BORDER_HEIGHT 255
// Number of metatiles to draw out from edge of map. Could allow modification of this in the future.
// porymap will reflect changes to it, but the value is hard-coded in the projects at the moment
#define BORDER_DISTANCE 7
class LayoutPixmapItem;
class CollisionPixmapItem;
class BorderMetatilesPixmapItem;
@@ -76,6 +72,8 @@ public:
void resetEvents();
QList<Event *> getEvents(Event::Group group = Event::Group::None) const;
Event* getEvent(Event::Group group, int index) const;
Event* getEvent(Event::Group group, const QString &idName) const;
QStringList getEventIdNames(Event::Group group) const;
int getNumEvents(Event::Group group = Event::Group::None) const;
QStringList getScriptLabels(Event::Group group = Event::Group::None);
QString getScriptsFilePath() const;
@@ -87,6 +85,7 @@ public:
void deleteConnections();
QList<MapConnection*> getConnections() const { return m_connections; }
MapConnection* getConnection(const QString &direction) const;
void removeConnection(MapConnection *);
void addConnection(MapConnection *);
void loadConnection(MapConnection *);
@@ -100,8 +99,8 @@ public:
bool hasUnsavedChanges() const;
void pruneEditHistory();
void setCustomAttributes(const QMap<QString, QJsonValue> &attributes) { m_customAttributes = attributes; }
QMap<QString, QJsonValue> customAttributes() const { return m_customAttributes; }
void setCustomAttributes(const QJsonObject &attributes) { m_customAttributes = attributes; }
QJsonObject customAttributes() const { return m_customAttributes; }
private:
QString m_name;
@@ -110,7 +109,7 @@ private:
QString m_sharedScriptsMap = "";
QStringList m_scriptsFileLabels;
QMap<QString, QJsonValue> m_customAttributes;
QJsonObject m_customAttributes;
MapHeader *m_header = nullptr;
Layout *m_layout = nullptr;

View File

@@ -5,6 +5,7 @@
#include <QString>
#include <QObject>
#include <QMap>
#include <QJsonObject>
class Project;
class Map;
@@ -34,6 +35,9 @@ public:
int offset() const { return m_offset; }
void setOffset(int offset, bool mirror = true);
QJsonObject customData() const { return m_customData; }
void setCustomData(const QJsonObject &customData) { m_customData = customData; }
MapConnection* findMirror();
MapConnection* createMirror();
@@ -55,6 +59,7 @@ private:
QString m_targetMapName;
QString m_direction;
int m_offset;
QJsonObject m_customData;
void markMapEdited();
Map* getMap(const QString& mapName) const;

View File

@@ -42,6 +42,8 @@ public:
Tileset *tileset_primary = nullptr;
Tileset *tileset_secondary = nullptr;
QJsonObject customData;
Blockdata blockdata;
QImage image;
@@ -94,8 +96,8 @@ public:
int getHeight() const { return height; }
int getBorderWidth() const { return border_width; }
int getBorderHeight() const { return border_height; }
int getBorderDrawWidth() const;
int getBorderDrawHeight() const;
QMargins getBorderMargins() const;
QRect getVisibleRect() const;
bool isWithinBounds(int x, int y) const;
bool isWithinBounds(const QRect &rect) const;
@@ -105,8 +107,8 @@ public:
void setBlock(int x, int y, Block block, bool enableScriptCallback = false);
void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false);
void adjustDimensions(QMargins margins, bool setNewBlockdata = true);
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
void adjustDimensions(const QMargins &margins, bool setNewBlockdata = true);
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true);
void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
void cacheBlockdata();
@@ -114,9 +116,12 @@ public:
void clearBorderCache();
void cacheBorder();
void setClean();
bool hasUnsavedChanges() const;
bool save(const QString &root);
bool saveBorder(const QString &root);
bool saveBlockdata(const QString &root);
bool layoutBlockChanged(int i, const Blockdata &cache);
uint16_t getBorderMetatileId(int x, int y);
@@ -141,6 +146,7 @@ public:
private:
void setNewDimensionsBlockdata(int newWidth, int newHeight);
void setNewBorderDimensionsBlockdata(int newWidth, int newHeight);
bool writeBlockdata(const QString &path, const Blockdata &blockdata) const;
static int getBorderDrawDistance(int dimension, qreal minimum);

View File

@@ -7,7 +7,7 @@
namespace PaletteUtil {
QList<QRgb> parse(QString filepath, bool *error);
void writeJASC(QString filepath, QVector<QRgb> colors, int offset, int nColors);
bool writeJASC(const QString &filepath, const QVector<QRgb> &colors, int offset, int nColors);
}
#endif // PALETTEUTIL_H

View File

@@ -43,7 +43,7 @@ class ParseUtil
{
public:
ParseUtil();
void set_root(const QString &dir);
void setRoot(const QString &dir) { this->root = dir; }
static QString readTextFile(const QString &path, QString *error = nullptr);
bool cacheFile(const QString &path, QString *error = nullptr);
void clearFileCache() { this->fileCache.clear(); }
@@ -55,10 +55,14 @@ public:
QString readCIncbin(const QString &text, const QString &label);
QMap<QString, QString> readCIncbinMulti(const QString &filepath);
QStringList readCIncbinArray(const QString &filename, const QString &label);
QMap<QString, int> readCDefinesByRegex(const QString &filename, const QSet<QString> &regexList, QString *error = nullptr);
QMap<QString, int> readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error = nullptr);
QHash<QString, int> readCDefinesByRegex(const QString &filename, const QSet<QString> &regexList, QString *error = nullptr);
QHash<QString, int> readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error = nullptr);
QStringList readCDefineNames(const QString &filename, const QSet<QString> &regexList, QString *error = nullptr);
tsl::ordered_map<QString, QHash<QString, QString>> readCStructs(const QString &, const QString & = "", const QHash<int, QString>& = {});
void loadGlobalCDefinesFromFile(const QString &filename, QString *error = nullptr);
void loadGlobalCDefines(const QMap<QString,QString> &defines);
void loadGlobalCDefines(const QHash<QString,QString> &defines);
void resetCDefines();
OrderedMap<QString, QHash<QString, QString>> readCStructs(const QString &, const QString & = "", const QHash<int, QString>& = {});
QList<QStringList> getLabelMacros(const QList<QStringList>&, const QString&);
QStringList getLabelValues(const QList<QStringList>&, const QString&);
bool tryParseJsonFile(QJsonDocument *out, const QString &filepath, QString *error = nullptr);
@@ -90,8 +94,20 @@ private:
QString curDefine;
QHash<QString, QString> fileCache;
QHash<QString, QStringList> errorMap;
int evaluateDefine(const QString&, const QString &, QMap<QString, int>*, QMap<QString, QString>*);
QList<Token> tokenizeExpression(QString, QMap<QString, int>*, QMap<QString, QString>*);
// The maps of define names to values/expressions that are available while parsing C defines.
// As the parser reads and evaluates more defines it will update these maps accordingly.
QHash<QString, int> knownDefineValues;
QHash<QString, QString> knownDefineExpressions;
// Maps of special define names to values/expressions that take precedence over defines encountered while parsing.
// Some (like 'TRUE'/'FALSE') are always present in these maps, others may be specified by the user with 'loadGlobalCDefines' / 'loadGlobalCDefinesFromFile'.
QHash<QString, int> globalDefineValues;
QHash<QString, QString> globalDefineExpressions;
int evaluateDefine(const QString &identifier, bool *ok = nullptr);
int evaluateExpression(const QString &expression);
QList<Token> tokenizeExpression(QString expression);
QList<Token> generatePostfix(const QList<Token> &tokens);
int evaluatePostfix(const QList<Token> &postfix);
void recordError(const QString &message);
@@ -100,11 +116,11 @@ private:
QString createErrorMessage(const QString &message, const QString &expression);
struct ParsedDefines {
QMap<QString,QString> expressions; // Map of all define names encountered to their expressions
QHash<QString,QString> expressions; // Map of all define names encountered to their expressions
QStringList filteredNames; // List of define names that matched the search text, in the order that they were encountered
};
ParsedDefines readCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
QMap<QString, int> evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
QHash<QString, int> evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
bool defineNameMatchesFilter(const QString &name, const QSet<QString> &filterList) const;
bool defineNameMatchesFilter(const QString &name, const QSet<QRegularExpression> &filterList) const;
QString loadTextFile(const QString &path, QString *error = nullptr);

View File

@@ -56,8 +56,8 @@ public:
bool loadLayout(poryjson::Json);
bool loadEntries();
void setEntries(QMap<QString, MapSectionEntry> *entries) { this->region_map_entries = entries; }
void setEntries(const QMap<QString, MapSectionEntry> &entries) { *(this->region_map_entries) = entries; }
void setEntries(QHash<QString, MapSectionEntry> *entries) { this->region_map_entries = entries; }
void setEntries(const QHash<QString, MapSectionEntry> &entries) { *(this->region_map_entries) = entries; }
void clearEntries() { this->region_map_entries->clear(); }
MapSectionEntry getEntry(QString section);
void setEntry(QString section, MapSectionEntry entry);
@@ -151,7 +151,7 @@ signals:
void mapNeedsDisplaying();
private:
QMap<QString, MapSectionEntry> *region_map_entries = nullptr;
QHash<QString, MapSectionEntry> *region_map_entries = nullptr;
QString alias = "";

View File

@@ -153,7 +153,7 @@ private:
/// ClearEntries
class ClearEntries : public QUndoCommand {
public:
ClearEntries(RegionMap *map, QMap<QString, MapSectionEntry>, QUndoCommand *parent = nullptr);
ClearEntries(RegionMap *map, QHash<QString, MapSectionEntry>, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
@@ -163,7 +163,7 @@ public:
private:
RegionMap *map;
QMap<QString, MapSectionEntry> entries;
QHash<QString, MapSectionEntry> entries;
};
#endif // REGIONMAPEDITCOMMANDS_H

View File

@@ -55,17 +55,17 @@ public:
static QString getExpectedDir(QString tilesetName, bool isSecondary);
QString getExpectedDir();
void load();
void loadMetatiles();
void loadMetatileAttributes();
void loadTilesImage(QImage *importedImage = nullptr);
void loadPalettes();
bool load();
bool loadMetatiles();
bool loadMetatileAttributes();
bool loadTilesImage(QImage *importedImage = nullptr);
bool loadPalettes();
void save();
void saveMetatileAttributes();
void saveMetatiles();
void saveTilesImage();
void savePalettes();
bool save();
bool saveMetatileAttributes();
bool saveMetatiles();
bool saveTilesImage();
bool savePalettes();
bool appendToHeaders(QString root, QString friendlyName, bool usingAsm);
bool appendToGraphics(QString root, QString friendlyName, bool usingAsm);

View File

@@ -9,6 +9,8 @@ namespace Util {
int roundUp(int numToRound, int multiple);
QString toDefineCase(QString input);
QString toHexString(uint32_t value, int minLength = 0);
QString toHtmlParagraph(const QString &text);
Qt::Orientations getOrientation(bool xflip, bool yflip);
}
#endif // UTILITY_H

View File

@@ -3,7 +3,7 @@
#define GUARD_WILDMONINFO_H
#include <QtWidgets>
#include "orderedmap.h"
#include "orderedjson.h"
class WildPokemon {
public:
@@ -13,22 +13,26 @@ public:
int minLevel;
int maxLevel;
QString species;
OrderedJson::object customData;
};
struct WildMonInfo {
bool active = false;
int encounterRate = 0;
QVector<WildPokemon> wildPokemon;
OrderedJson::object customData;
};
struct WildPokemonHeader {
tsl::ordered_map<QString, WildMonInfo> wildMons;
OrderedMap<QString, WildMonInfo> wildMons;
OrderedJson::object customData;
};
struct EncounterField {
QString name; // Ex: "fishing_mons"
QVector<int> encounterRates;
tsl::ordered_map<QString, QVector<int>> groups; // Ex: "good_rod", {2, 3, 4}
OrderedMap<QString, QVector<int>> groups; // Ex: "good_rod", {2, 3, 4}
OrderedJson::object customData;
};
typedef QVector<EncounterField> EncounterFields;

View File

@@ -30,7 +30,7 @@
#include "mapruler.h"
#include "encountertablemodel.h"
class DraggablePixmapItem;
class EventPixmapItem;
class MetatilesPixmapItem;
class Editor : public QObject
@@ -57,8 +57,8 @@ public:
GridSettings gridSettings;
void setProject(Project * project);
void saveAll();
void saveCurrent();
bool saveAll();
bool saveCurrent();
void saveEncounterTabData();
void closeProject();
@@ -92,14 +92,16 @@ public:
void setConnectionsVisibility(bool visible);
void updateDivingMapsVisibility();
void renderDivingConnections();
void addConnection(MapConnection* connection);
void addNewConnection(const QString &mapName, const QString &direction);
void replaceConnection(const QString &mapName, const QString &direction);
void removeConnection(MapConnection* connection);
void removeSelectedConnection();
void addNewWildMonGroup(QWidget *window);
void deleteWildMonGroup();
void configureEncounterJSON(QWidget *);
EncounterTableModel* getCurrentWildMonTable();
void updateDiveMap(QString mapName);
void updateEmergeMap(QString mapName);
bool setDivingMapName(const QString &mapName, const QString &direction);
QString getDivingMapName(const QString &direction) const;
void setSelectedConnection(MapConnection *connection);
void updatePrimaryTileset(QString tilesetLabel, bool forceLoad = false);
@@ -107,7 +109,7 @@ public:
void toggleBorderVisibility(bool visible, bool enableScriptCallback = true);
void updateCustomMapAttributes();
DraggablePixmapItem *addEventPixmapItem(Event *event);
EventPixmapItem *addEventPixmapItem(Event *event);
void removeEventPixmapItem(Event *event);
bool canAddEvents(const QList<Event*> &events);
void selectMapEvent(Event *event, bool toggle = false);
@@ -116,12 +118,16 @@ public:
void duplicateSelectedEvents();
void redrawAllEvents();
void redrawEvents(const QList<Event*> &events);
void redrawEventPixmapItem(DraggablePixmapItem *item);
void redrawEventPixmapItem(EventPixmapItem *item);
void updateEventPixmapItemZValue(EventPixmapItem *item);
qreal getEventOpacity(const Event *event) const;
void setPlayerViewRect(const QRectF &rect);
void updateCursorRectPos(int x, int y);
void setCursorRectVisible(bool visible);
void onEventDragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition);
void onEventReleased(Event *event, const QPoint &position);
void updateWarpEventWarning(Event *event);
void updateWarpEventWarnings();
@@ -172,10 +178,7 @@ public:
static QList<QList<const QImage*>> collisionIcons;
int eventShiftActionId = 0;
void eventsView_onMousePress(QMouseEvent *event);
bool selectingEvent = false;
int eventMoveActionId = 0;
void deleteSelectedEvents();
void shouldReselectEvents();
@@ -183,6 +186,22 @@ public:
static void openInTextEditor(const QString &path, int lineNum = 0);
void setCollisionGraphics();
enum ZValue {
MapBorder = -4,
MapConnectionInactive = -3,
MapConnectionActive = -2,
MapConnectionMask = -1,
// Event pixmaps set their z value to be their y position on the map.
// Their y value is int16_t, so we have enough space to allocate the
// full range + 1 for the selected event (which should always be on top).
EventMinimum = 1,
EventMaximum = EventMinimum + 0x10000,
Ruler,
ResizeLayoutPopup
};
public slots:
void openMapScripts() const;
void openScript(const QString &scriptLabel) const;
@@ -199,7 +218,7 @@ private:
EditMode editMode = EditMode::None;
void save(bool currentOnly);
bool save(bool currentOnly);
void clearMap();
void clearMetatileSelector();
void clearMovementPermissionSelector();
@@ -218,8 +237,9 @@ private:
void removeConnectionPixmap(MapConnection *connection);
void displayConnection(MapConnection *connection);
void displayDivingConnection(MapConnection *connection);
void setDivingMapName(QString mapName, QString direction);
void removeDivingMapPixmap(MapConnection *connection);
void onDivingMapEditingFinished(NoScrollComboBox* combo, const QString &direction);
void updateDivingMapButton(QToolButton* button, const QString &mapName);
void updateEncounterFields(EncounterFields newFields);
QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
QString getMetatileDisplayMessage(uint16_t metatileId);
@@ -249,11 +269,11 @@ private slots:
signals:
void eventsChanged();
void openEventMap(Event*);
void openConnectedMap(MapConnection*);
void wildMonTableOpened(EncounterTableModel*);
void wildMonTableClosed();
void wildMonTableEdited();
void warpEventDoubleClicked(QString, int, Event::Group);
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void tilesetUpdated(QString);

View File

@@ -99,7 +99,7 @@ public:
// Array and object typedefs
typedef QVector<Json> array;
typedef tsl::ordered_map<QString, Json> object;
typedef OrderedMap<QString, Json> object;
// Constructors for the various types of JSON value.
Json() noexcept; // NUL
@@ -132,7 +132,22 @@ public:
int>::type = 0>
Json(const V & v) : Json(array(v.begin(), v.end())) {}
static const Json fromQJsonValue(QJsonValue value);
static Json fromQJsonValue(const QJsonValue &value);
static void append(Json::array *array, const QJsonArray &addendum) {
for (const auto &i : addendum) array->push_back(fromQJsonValue(i));
}
static void append(Json::array *array, const Json::array &addendum) {
for (const auto &i : addendum) array->push_back(i);
}
static void append(Json::object *object, const QJsonObject &addendum) {
for (auto it = addendum.constBegin(); it != addendum.constEnd(); it++)
(*object)[it.key()] = fromQJsonValue(it.value());
}
static void append(Json::object *object, const Json::object &addendum) {
for (auto it = addendum.cbegin(); it != addendum.cend(); it++)
(*object)[it.key()] = it.value();
}
// This prevents Json(some_pointer) from accidentally producing a bool. Use
// Json(bool(some_pointer)) if that behavior is desired.

View File

@@ -1977,6 +1977,14 @@ public:
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
// Naive solution for take, should probably be replaced with one that does a single lookup and no unnecessary insertion.
// We want to mirror the behavior of QMap::take, which returns a default-constructed value if the key is not present.
T take(const key_type& key) {
typename ValueSelect::value_type value = m_ht[key];
m_ht.erase(key);
return value;
}
@@ -2404,4 +2412,7 @@ private:
} // end namespace tsl
template<class Key, class T>
using OrderedMap = tsl::ordered_map<Key, T>;
#endif

View File

@@ -52,6 +52,8 @@ public:
MainWindow(const MainWindow &) = delete;
MainWindow & operator = (const MainWindow &) = delete;
void initialize();
// Scripting API
Q_INVOKABLE QJSValue getBlock(int x, int y);
void tryRedrawMapArea(bool forceRedraw);
@@ -175,9 +177,9 @@ private slots:
void on_action_Reload_Project_triggered();
void on_action_Close_Project_triggered();
void on_action_Save_Project_triggered();
void save(bool currentOnly = false);
bool save(bool currentOnly = false);
void openWarpMap(QString map_name, int event_id, Event::Group event_group);
void openEventMap(Event *event);
void duplicate();
void setClipboardData(poryjson::Json::object);
@@ -197,8 +199,7 @@ private slots:
void onMapLoaded(Map *map);
void onMapRulerStatusChanged(const QString &);
void applyUserShortcuts();
void markMapEdited();
void markSpecificMapEdited(Map*);
void markMapEdited(Map*);
void markLayoutEdited();
void on_actionNew_Tileset_triggered();
@@ -243,13 +244,9 @@ private slots:
void on_pushButton_AddConnection_clicked();
void on_button_OpenDiveMap_clicked();
void on_button_OpenEmergeMap_clicked();
void on_comboBox_DiveMap_currentTextChanged(const QString &mapName);
void on_comboBox_EmergeMap_currentTextChanged(const QString &mapName);
void on_comboBox_PrimaryTileset_currentTextChanged(const QString &arg1);
void on_comboBox_SecondaryTileset_currentTextChanged(const QString &arg1);
void on_pushButton_ChangeDimensions_clicked();
void on_checkBox_smartPaths_stateChanged(int selected);
void on_checkBox_ToggleBorder_stateChanged(int selected);
void resetMapViewScale();
@@ -260,7 +257,6 @@ private slots:
void eventTabChanged(int index);
void on_checkBox_MirrorConnections_stateChanged(int selected);
void on_actionDive_Emerge_Map_triggered();
void on_actionShow_Events_In_Map_View_triggered();
void on_groupBox_DiveMapOpacity_toggled(bool on);
@@ -437,6 +433,10 @@ private:
void checkForUpdates(bool requestedByUser);
void setDivingMapsVisible(bool visible);
void setSmartPathsEnabled(bool enabled);
void setBorderVisibility(bool visible);
void setMirrorConnectionsEnabled(bool enabled);
};
// These are namespaced in a struct to avoid colliding with e.g. class Map.

View File

@@ -62,7 +62,6 @@ public:
QStringList mapSectionIdNames;
QMap<uint32_t, QString> encounterTypeToName;
QMap<uint32_t, QString> terrainTypeToName;
QMap<QString, MapSectionEntry> regionMapEntries;
QMap<QString, QMap<QString, uint16_t>> metatileLabelsMap;
QMap<QString, uint16_t> unusedMetatileLabels;
QMap<QString, uint32_t> metatileBehaviorMap;
@@ -72,13 +71,12 @@ public:
QSet<QString> modifiedFiles;
bool usingAsmTilesets;
QSet<QString> disabledSettingsNames;
QSet<QString> topLevelMapFields;
int pokemonMinLevel;
int pokemonMaxLevel;
int maxEncounterRate;
bool wildEncountersLoaded;
void set_root(QString);
void setRoot(const QString&);
void clearMaps();
void clearTilesetCache();
@@ -111,10 +109,6 @@ public:
bool loadBlockdata(Layout *);
bool loadLayoutBorder(Layout *);
void saveTextFile(QString path, QString text);
void appendTextFile(QString path, QString text);
void deleteFile(QString path);
bool readMapGroups();
void addNewMapGroup(const QString &groupName);
QString mapNameToMapGroup(const QString &mapName) const;
@@ -145,12 +139,11 @@ public:
QString getNewHealLocationName(const Map* map) const;
bool readWildMonData();
tsl::ordered_map<QString, tsl::ordered_map<QString, WildPokemonHeader>> wildMonData;
OrderedMap<QString, OrderedMap<QString, WildPokemonHeader>> wildMonData;
QString wildMonTableName;
QVector<EncounterField> wildMonFields;
QVector<QString> encounterGroupLabels;
QVector<poryjson::Json::object> extraEncounterGroups;
bool readSpeciesIconPaths();
QString getDefaultSpeciesIconPath(const QString &species);
@@ -158,40 +151,34 @@ public:
bool addNewMapsec(const QString &idName, const QString &displayName = QString());
void removeMapsec(const QString &idName);
QString getMapsecDisplayName(const QString &idName) const { return this->mapSectionDisplayNames.value(idName); }
QString getMapsecDisplayName(const QString &idName) const { return this->locationData.value(idName).displayName; }
void setMapsecDisplayName(const QString &idName, const QString &displayName);
bool hasUnsavedChanges();
bool hasUnsavedDataChanges = false;
void initTopLevelMapFields();
bool readMapJson(const QString &mapName, QJsonDocument * out);
bool loadMapEvent(Map *map, const QJsonObject &json, Event::Type defaultType = Event::Type::None);
bool loadMapEvent(Map *map, QJsonObject json, Event::Type defaultType = Event::Type::None);
bool loadMapData(Map*);
bool readMapLayouts();
Layout *loadLayout(QString layoutId);
bool loadLayout(Layout *);
bool loadMapLayout(Map*);
bool loadLayoutTilesets(Layout *);
void loadTilesetAssets(Tileset*);
bool loadTilesetAssets(Tileset*);
void loadTilesetMetatileLabels(Tileset*);
void readTilesetPaths(Tileset* tileset);
void saveAll();
void saveGlobalData();
void saveLayout(Layout *);
void saveLayoutBlockdata(Layout *);
void saveLayoutBorder(Layout *);
void writeBlockdata(QString, const Blockdata &);
void saveMap(Map *map, bool skipLayout = false);
void saveConfig();
void saveMapLayouts();
void saveMapGroups();
void saveRegionMapSections();
void saveWildMonData();
void saveHealLocations();
void saveTilesets(Tileset*, Tileset*);
void saveTilesetMetatileLabels(Tileset*, Tileset*);
bool saveAll();
bool saveGlobalData();
bool saveConfig();
bool saveLayout(Layout *layout);
bool saveMap(Map *map, bool skipLayout = false);
bool saveTextFile(const QString &path, const QString &text);
bool saveRegionMapSections();
bool saveTilesets(Tileset*, Tileset*);
bool saveTilesetMetatileLabels(Tileset*, Tileset*);
void appendTilesetLabel(const QString &label, const QString &isSecondaryStr);
bool readTilesetLabels();
bool readTilesetMetatileLabels();
@@ -217,6 +204,7 @@ public:
bool readEventGraphics();
bool readFieldmapProperties();
bool readFieldmapMasks();
bool readGlobalConstants();
QMap<QString, QMap<QString, QString>> readObjEventGfxInfo();
QPixmap getEventPixmap(const QString &gfxName, const QString &movementName);
@@ -240,35 +228,52 @@ public:
static QString getExistingFilepath(QString filepath);
void applyParsedLimits();
void setRegionMapEntries(const QHash<QString, MapSectionEntry> &entries);
QHash<QString, MapSectionEntry> getRegionMapEntries() const;
QSet<QString> getTopLevelMapFields() const;
int getMapDataSize(int width, int height) const;
int getMaxMapDataSize() const { return this->maxMapDataSize; }
int getMaxMapWidth() const;
int getMaxMapHeight() const;
bool mapDimensionsValid(int width, int height) const;
bool calculateDefaultMapSize();
QSize getDefaultMapSize() const { return this->defaultMapSize; }
QSize getMapSizeAddition() const { return this->mapSizeAddition; }
int getMaxEvents(Event::Group group) const;
static QString getEmptyMapDefineName();
static QString getDynamicMapDefineName();
static QString getDynamicMapName();
static QString getEmptySpeciesName();
static int getNumTilesPrimary();
static int getNumTilesTotal();
static int getNumMetatilesPrimary();
static int getNumMetatilesTotal();
static int getNumPalettesPrimary();
static int getNumPalettesTotal();
static int getMaxMapDataSize();
static int getDefaultMapDimension();
static int getMaxMapWidth();
static int getMaxMapHeight();
static int getMapDataSize(int width, int height);
static bool mapDimensionsValid(int width, int height);
bool calculateDefaultMapSize();
int getMaxEvents(Event::Group group);
static QMargins getMetatileViewDistance();
static int getNumTilesPrimary() { return num_tiles_primary; }
static int getNumTilesTotal() { return num_tiles_total; }
static int getNumMetatilesPrimary() { return num_metatiles_primary; }
static int getNumMetatilesTotal() { return Block::getMaxMetatileId() + 1; }
static int getNumPalettesPrimary(){ return num_pals_primary; }
static int getNumPalettesTotal() { return num_pals_total; }
static QString getEmptyMapsecName();
static QString getMapGroupPrefix();
private:
QHash<QString, QString> mapSectionDisplayNames;
QMap<QString, qint64> modifiedFileTimestamps;
QMap<QString, QString> facingDirections;
QHash<QString, QString> speciesToIconPath;
QHash<QString, Map*> maps;
// Fields for preserving top-level JSON data that Porymap isn't expecting.
QJsonObject customLayoutsData;
QJsonObject customMapSectionsData;
QJsonObject customMapGroupsData;
QJsonObject customHealLocationsData;
OrderedJson::object customWildMonData;
OrderedJson::object customWildMonGroupData;
OrderedJson::array extraEncounterGroups;
// Maps/layouts represented in these sets have been fully loaded from the project.
// If a valid map name / layout id is not in these sets, a Map / Layout object exists
// for it in Project::maps / Project::mapLayouts, but it has been minimally populated
@@ -292,7 +297,14 @@ private:
};
QMap<QString, EventGraphics*> eventGraphicsMap;
void updateLayout(Layout *);
// The extra data that can be associated with each MAPSEC name.
struct LocationData
{
MapSectionEntry map;
QString displayName;
QJsonObject custom;
};
QHash<QString, LocationData> locationData;
void setNewLayoutBlockdata(Layout *layout);
void setNewLayoutBorder(Layout *layout);
@@ -301,17 +313,27 @@ private:
void recordFileChange(const QString &filepath);
void resetFileCache();
bool saveMapLayouts();
bool saveMapGroups();
bool saveWildMonData();
bool saveHealLocations();
bool appendTextFile(const QString &path, const QString &text);
QString findSpeciesIconPath(const QStringList &names) const;
int maxEventsPerGroup;
int maxObjectEvents;
int maxMapDataSize;
QSize defaultMapSize;
QSize mapSizeAddition;
// TODO: These really shouldn't be static, they're specific to a single project.
// We're making an assumption here that we only have one project open at a single time
// (which is true, but then if that's the case we should have some global Project instance instead)
static int num_tiles_primary;
static int num_tiles_total;
static int num_metatiles_primary;
static int num_pals_primary;
static int num_pals_total;
static int max_map_data_size;
static int default_map_dimension;
signals:
void fileChanged(const QString &filepath);

View File

@@ -39,6 +39,7 @@ public:
static void populateGlobalObject(MainWindow *mainWindow);
static QJSEngine *getEngine();
static void invokeAction(int actionIndex);
static void cb_ProjectOpened(QString projectPath);
static void cb_ProjectClosed(QString projectPath);
static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock);
@@ -47,18 +48,20 @@ public:
static void cb_BlockHoverCleared();
static void cb_MapOpened(QString mapName);
static void cb_LayoutOpened(QString layoutName);
static void cb_MapResized(int oldWidth, int oldHeight, int newWidth, int newHeight);
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_MainTabChanged(int oldTab, int newTab);
static void cb_MapViewTabChanged(int oldTab, int newTab);
static void cb_BorderVisibilityToggled(bool visible);
static bool tryErrorJS(QJSValue js);
static QJSValue fromBlock(Block block);
static QJSValue fromTile(Tile tile);
static Tile toTile(QJSValue obj);
static QJSValue dimensions(int width, int height);
static QJSValue margins(const QMargins &margins);
static QJSValue position(int x, int y);
static const QImage * getImage(const QString &filepath, bool useCache);
static QJSValue dialogInput(QJSValue input, bool selectedOk);

View File

@@ -13,6 +13,8 @@ class AboutPorymap : public QDialog
public:
explicit AboutPorymap(QWidget *parent = nullptr);
~AboutPorymap();
static QString getVersionString();
private:
Ui::AboutPorymap *ui;
};

View File

@@ -43,8 +43,6 @@ protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override;
virtual void keyPressEvent(QKeyEvent*) override;
virtual void focusInEvent(QFocusEvent*) override;
signals:
void connectionItemDoubleClicked(MapConnection*);

View File

@@ -36,19 +36,17 @@ private:
protected:
virtual void mousePressEvent(QMouseEvent*) override;
virtual void focusInEvent(QFocusEvent*) override;
virtual void keyPressEvent(QKeyEvent*) override;
virtual bool eventFilter(QObject*, QEvent *event) override;
signals:
void selected();
void openMapClicked(MapConnection*);
private slots:
void on_comboBox_Direction_currentTextChanged(QString direction);
void on_comboBox_Map_currentTextChanged(QString mapName);
void on_spinBox_Offset_valueChanged(int offset);
void on_button_Delete_clicked();
void on_button_OpenMap_clicked();
private:
void commitDirection();
void commitMap(const QString &mapName);
void commitMove(int offset);
void commitRemove();
};
#endif // CONNECTIONSLISTITEM_H

View File

@@ -13,8 +13,8 @@ public:
explicit CustomAttributesTable(QWidget *parent = nullptr);
~CustomAttributesTable() {};
QMap<QString, QJsonValue> getAttributes() const;
void setAttributes(const QMap<QString, QJsonValue> &attributes);
QJsonObject getAttributes() const;
void setAttributes(const QJsonObject &attributes);
void addNewAttribute(const QString &key, const QJsonValue &value);
bool deleteSelectedAttributes();

View File

@@ -1,70 +0,0 @@
#ifndef DRAGGABLEPIXMAPITEM_H
#define DRAGGABLEPIXMAPITEM_H
#include <QString>
#include <QGraphicsItemGroup>
#include <QGraphicsPixmapItem>
#include <QGraphicsItemAnimation>
#include <QtWidgets>
#include "events.h"
class Editor;
class DraggablePixmapItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
public:
DraggablePixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) {}
DraggablePixmapItem(Event *event, Editor *editor) : QGraphicsPixmapItem(event->getPixmap()) {
this->event = event;
event->setPixmapItem(this);
this->editor = editor;
updatePosition();
}
Event *event = nullptr;
void updatePosition();
void move(int dx, int dy);
void moveTo(const QPoint &pos);
void emitPositionChanged();
void updatePixmap();
private:
Editor *editor = nullptr;
QPoint lastPos;
bool active = false;
bool releaseSelectionQueued = false;
signals:
void positionChanged(Event *event);
void xChanged(int);
void yChanged(int);
void elevationChanged(int);
void spriteChanged(QPixmap pixmap);
void onPropertyChanged(QString key, QString value);
public slots:
void set_x(int x) {
event->setX(x);
updatePosition();
}
void set_y(int y) {
event->setY(y);
updatePosition();
}
void set_elevation(int z) {
event->setElevation(z);
updatePosition();
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*);
};
#endif // DRAGGABLEPIXMAPITEM_H

View File

@@ -57,7 +57,9 @@ protected:
bool initialized = false;
bool connected = false;
void populateDropdown(NoScrollComboBox * combo, const QStringList &items);
void populateScriptDropdown(NoScrollComboBox * combo, Project * project);
void populateIdNameDropdown(NoScrollComboBox * combo, Project * project, const QString &mapName, Event::Group group);
private:
Event *event;
@@ -78,6 +80,7 @@ public:
virtual void populate(Project *project) override;
public:
QLineEdit *line_edit_local_id;
NoScrollComboBox *combo_sprite;
NoScrollComboBox *combo_movement;
NoScrollSpinBox *spinner_radius_x;
@@ -108,12 +111,15 @@ public:
virtual void populate(Project *project) override;
public:
QLineEdit *line_edit_local_id;
NoScrollComboBox *combo_sprite;
NoScrollSpinBox *spinner_target_id;
NoScrollComboBox *combo_target_id;
NoScrollComboBox *combo_target_map;
private:
CloneObjectEvent *clone;
void tryInvalidateIdDropdown(Map *map);
};
@@ -131,12 +137,15 @@ public:
virtual void populate(Project *project) override;
public:
QLineEdit *line_edit_id;
NoScrollComboBox *combo_dest_map;
NoScrollComboBox *combo_dest_warp;
QPushButton *warning;
private:
WarpEvent *warp;
void tryInvalidateIdDropdown(Map *map);
};
@@ -275,6 +284,8 @@ public:
private:
HealLocationEvent *healLocation;
void tryInvalidateIdDropdown(Map *map);
};
#endif // EVENTRAMES_H

View File

@@ -0,0 +1,58 @@
#ifndef EVENTPIXMAPITEM_H
#define EVENTPIXMAPITEM_H
#include <QString>
#include <QGraphicsItemGroup>
#include <QGraphicsPixmapItem>
#include <QGraphicsItemAnimation>
#include <QtWidgets>
#include "events.h"
class Project;
class EventPixmapItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
public:
explicit EventPixmapItem(Event *event);
void render(Project *project);
bool isSelected() const { return m_selected; }
void setSelected(bool selected) { m_selected = selected; }
Event * getEvent() const { return m_event; }
void move(int dx, int dy);
void moveTo(int x, int y);
void moveTo(const QPoint &pos);
private:
QPixmap m_basePixmap;
Event *const m_event = nullptr;
QPoint m_lastPos;
bool m_active = false;
bool m_selected = false;
bool m_releaseSelectionQueued = false;
void updatePixelPosition();
signals:
void xChanged(int x);
void yChanged(int y);
void posChanged(int x, int y);
void rendered(const QPixmap &pixmap);
void selected(Event *event, bool toggle);
void dragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition);
void released(Event *event, const QPoint &position);
void doubleClicked(Event *event);
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override { emit doubleClicked(m_event); }
};
#endif // EVENTPIXMAPITEM_H

View File

@@ -32,25 +32,17 @@ signals:
void clicked(QMouseEvent *event);
};
class Editor;
// TODO: This should just be MapView. It makes map-based assumptions, and no other class inherits GraphicsView.
class GraphicsView : public QGraphicsView
class ConnectionsView : public QGraphicsView
{
Q_OBJECT
public:
GraphicsView() : QGraphicsView() {}
GraphicsView(QWidget *parent) : QGraphicsView(parent) {}
ConnectionsView(QWidget *parent = nullptr) : QGraphicsView(parent) {}
signals:
void pressedDelete();
public:
// GraphicsView_Object object;
Editor *editor;
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void moveEvent(QMoveEvent *event) override;
virtual void keyPressEvent(QKeyEvent *event) override;
};
//Q_DECLARE_METATYPE(GraphicsView)
#endif // GRAPHICSVIEW_H

View File

@@ -0,0 +1,41 @@
#include "qgifimage.h"
#include <QSplashScreen>
#include <QTimer>
#include <QWidget>
namespace Ui {
class LoadingScreen;
}
class PorymapLoadingScreen : public QWidget {
Q_OBJECT
public:
explicit PorymapLoadingScreen(QWidget *parent = nullptr);
~PorymapLoadingScreen();
void setPixmap(QPixmap pixmap);
void showMessage(QString text);
void start();
void stop ();
private:
void setupUi();
public slots:
void updateFrame();
private:
Ui::LoadingScreen *ui;
QGifImage splashImage;
int frame = 0;
QTimer timer;
};
extern PorymapLoadingScreen *porysplash;

View File

@@ -64,6 +64,8 @@ private:
QPointer<Project> m_project = nullptr;
bool m_allowProjectChanges = true;
void setText(QComboBox *combo, const QString &text) const;
void setText(QLineEdit *lineEdit, const QString &text) const;
void setLocations(const QStringList &locations);
void updateLocationName();
@@ -73,11 +75,11 @@ private:
void onWeatherChanged(const QString &weather);
void onTypeChanged(const QString &type);
void onBattleSceneChanged(const QString &battleScene);
void onRequiresFlashChanged(int selected);
void onShowLocationNameChanged(int selected);
void onAllowRunningChanged(int selected);
void onAllowBikingChanged(int selected);
void onAllowEscapingChanged(int selected);
void onRequiresFlashChanged(bool enabled);
void onShowLocationNameChanged(bool enabled);
void onAllowRunningChanged(bool enabled);
void onAllowBikingChanged(bool enabled);
void onAllowEscapingChanged(bool enabled);
void onFloorNumberChanged(int offset);
};

View File

@@ -91,30 +91,27 @@ protected:
virtual void showEvent(QShowEvent *) override;
virtual void resizeEvent(QResizeEvent *) override;
private slots:
void on_checkBox_Objects_stateChanged(int state);
void on_checkBox_Warps_stateChanged(int state);
void on_checkBox_BGs_stateChanged(int state);
void on_checkBox_Triggers_stateChanged(int state);
void on_checkBox_HealLocations_stateChanged(int state);
void on_checkBox_AllEvents_stateChanged(int state);
void on_checkBox_ConnectionUp_stateChanged(int state);
void on_checkBox_ConnectionDown_stateChanged(int state);
void on_checkBox_ConnectionLeft_stateChanged(int state);
void on_checkBox_ConnectionRight_stateChanged(int state);
void on_checkBox_AllConnections_stateChanged(int state);
void on_checkBox_Collision_stateChanged(int state);
void on_checkBox_Grid_stateChanged(int state);
void on_checkBox_Border_stateChanged(int state);
private:
void setShowGrid(bool checked);
void setShowBorder(bool checked);
void setShowObjects(bool checked);
void setShowWarps(bool checked);
void setShowBgs(bool checked);
void setShowTriggers(bool checked);
void setShowHealLocations(bool checked);
void setShowAllEvents(bool checked);
void setShowConnectionUp(bool checked);
void setShowConnectionDown(bool checked);
void setShowConnectionLeft(bool checked);
void setShowConnectionRight(bool checked);
void setShowAllConnections(bool checked);
void setShowCollision(bool checked);
void setDisablePreviewScaling(bool checked);
void setDisablePreviewUpdates(bool checked);
void on_pushButton_Reset_pressed();
void on_spinBox_TimelapseDelay_editingFinished();
void on_spinBox_FrameSkip_editingFinished();
void on_checkBox_DisablePreviewScaling_stateChanged(int state);
void on_checkBox_DisablePreviewUpdates_stateChanged(int state);
};
#endif // MAPIMAGEEXPORTER_H

View File

@@ -5,13 +5,17 @@
#include "graphicsview.h"
#include "overlay.h"
class MapView : public GraphicsView
class Editor;
class MapView : public QGraphicsView
{
Q_OBJECT
public:
MapView() : GraphicsView() {}
MapView(QWidget *parent) : GraphicsView(parent) {}
MapView() : QGraphicsView() {}
MapView(QWidget *parent) : QGraphicsView(parent) {}
Editor *editor;
Overlay * getOverlay(int layer);
void clearOverlayMap();
@@ -73,6 +77,7 @@ public:
protected:
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
virtual void keyPressEvent(QKeyEvent*) override;
virtual void moveEvent(QMoveEvent *event) override;
private:
QMap<int, Overlay*> overlayMap;

View File

@@ -24,21 +24,21 @@ public:
class ErrorMessage : public Message {
public:
ErrorMessage(const QString &message, QWidget *parent);
static int show(const QString &message, QWidget *parent);
static void show(const QString &message, QWidget *parent);
};
// Basic warning message with an 'Ok' button.
class WarningMessage : public Message {
public:
WarningMessage(const QString &message, QWidget *parent);
static int show(const QString &message, QWidget *parent);
static void show(const QString &message, QWidget *parent);
};
// Basic informational message with a 'Close' button.
class InfoMessage : public Message {
public:
InfoMessage(const QString &message, QWidget *parent);
static int show(const QString &message, QWidget *parent);
static void show(const QString &message, QWidget *parent);
};
// Basic question message with a 'Yes' and 'No' button.
@@ -53,7 +53,7 @@ public:
class RecentErrorMessage : public ErrorMessage {
public:
RecentErrorMessage(const QString &message, QWidget *parent);
static int show(const QString &message, QWidget *parent);
static void show(const QString &message, QWidget *parent);
};

View File

@@ -10,28 +10,35 @@
class MovableRect : public QGraphicsRectItem
{
public:
MovableRect(bool *enabled, int width, int height, QRgb color);
MovableRect(bool *enabled, const QRectF &rect, const QRgb &color);
QRectF boundingRect() const override {
qreal penWidth = 4;
return QRectF(-penWidth,
-penWidth,
30 * 8 + penWidth * 2,
20 * 8 + penWidth * 2);
this->rect().width() + penWidth * 2,
this->rect().height() + penWidth * 2);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
if (!(*enabled)) return;
if (!isVisible()) return;
painter->setPen(this->color);
painter->drawRect(this->rect().x() - 2, this->rect().y() - 2, this->rect().width() + 3, this->rect().height() + 3);
painter->setPen(QColor(0, 0, 0));
painter->drawRect(this->rect().x() - 3, this->rect().y() - 3, this->rect().width() + 5, this->rect().height() + 5);
painter->drawRect(this->rect().x() - 1, this->rect().y() - 1, this->rect().width() + 1, this->rect().height() + 1);
painter->drawRect(this->rect() + QMargins(1,1,1,1)); // Fill
painter->setPen(Qt::black);
painter->drawRect(this->rect() + QMargins(2,2,2,2)); // Outer border
painter->drawRect(this->rect()); // Inner border
}
void updateLocation(int x, int y);
bool *enabled;
void setActive(bool active);
bool getActive() const { return this->active; }
protected:
bool *enabled = nullptr;
bool active = true;
QRectF baseRect;
QRgb color;
void updateVisibility();
};

View File

@@ -0,0 +1,32 @@
#ifndef NEWDEFINEDIALOG_H
#define NEWDEFINEDIALOG_H
#include <QDialog>
#include <QAbstractButton>
namespace Ui {
class NewDefineDialog;
}
class NewDefineDialog : public QDialog
{
Q_OBJECT
public:
explicit NewDefineDialog(QWidget *parent = nullptr);
~NewDefineDialog();
virtual void accept() override;
signals:
void createdDefine(const QString &name, const QString &expression);
private:
Ui::NewDefineDialog *ui;
bool validateName(bool allowEmpty = false);
void onNameChanged(const QString &name);
void dialogButtonClicked(QAbstractButton *button);
};
#endif // NEWDEFINEDIALOG_H

View File

@@ -20,13 +20,16 @@ public:
virtual void accept() override;
signals:
void accepted(MapConnection *result);
void newConnectionedAdded(const QString &mapName, const QString &direction);
void connectionReplaced(const QString &mapName, const QString &direction);
private:
Ui::NewMapConnectionDialog *ui;
Map *m_map;
bool mapNameIsValid();
void setWarningVisible(bool visible);
bool askReplaceConnection(MapConnection *connection, const QString &newMapName);
};
#endif // NEWMAPCONNECTIONDIALOG_H

View File

@@ -18,6 +18,9 @@ public:
void setLineEdit(QLineEdit *edit);
void setFocusedScrollingEnabled(bool enabled);
signals:
void editingFinished();
private:
void setItem(int index, const QString &text);

View File

@@ -0,0 +1,25 @@
#ifndef NOSCROLLTEXTEDIT_H
#define NOSCROLLTEXTEDIT_H
#include <QTextEdit>
#include <QWheelEvent>
class NoScrollTextEdit : public QTextEdit
{
Q_OBJECT
public:
explicit NoScrollTextEdit(const QString &text, QWidget *parent = nullptr) : QTextEdit(text, parent) {
setFocusPolicy(Qt::StrongFocus);
};
explicit NoScrollTextEdit(QWidget *parent = nullptr) : NoScrollTextEdit(QString(), parent) {};
virtual void wheelEvent(QWheelEvent *event) override {
if (hasFocus()) {
QTextEdit::wheelEvent(event);
} else {
event->ignore();
}
};
};
#endif // NOSCROLLTEXTEDIT_H

View File

@@ -67,6 +67,12 @@ private:
void setWarpBehaviorsList(QStringList list);
void openFilesHelp();
void openIdentifiersHelp();
void addNewGlobalConstantsFilepath();
void addGlobalConstantsFilepath(const QString &filepath);
QStringList getGlobalConstantsFilepaths();
void addNewGlobalConstant();
void addGlobalConstant(const QString &name, const QString &expression);
QMap<QString,QString> getGlobalConstants();
private slots:
void dialogButtonClicked(QAbstractButton *button);

View File

@@ -54,7 +54,7 @@ private:
Project *project;
RegionMap *region_map = nullptr;
tsl::ordered_map<QString, RegionMap *> region_maps;
OrderedMap<QString, RegionMap *> region_maps;
QString configFilepath;
@@ -95,7 +95,7 @@ private:
void saveConfig();
bool loadRegionMapEntries();
bool saveRegionMapEntries();
QMap<QString, MapSectionEntry> region_map_entries;
QHash<QString, MapSectionEntry> region_map_entries;
bool buildConfigDialog();
poryjson::Json configRegionMapDialog();
@@ -121,6 +121,9 @@ private:
void restoreWindowState();
void closeEvent(QCloseEvent* event);
void setTileHFlip(bool enabled);
void setTileVFlip(bool enabled);
private slots:
void on_action_RegionMap_Save_triggered();
void on_actionSave_All_triggered();
@@ -145,8 +148,6 @@ private slots:
void on_spinBox_RM_LayoutWidth_valueChanged(int);
void on_spinBox_RM_LayoutHeight_valueChanged(int);
void on_spinBox_tilePalette_valueChanged(int);
void on_checkBox_tileHFlip_stateChanged(int);
void on_checkBox_tileVFlip_stateChanged(int);
void on_verticalSlider_Zoom_Map_Image_valueChanged(int);
void on_verticalSlider_Zoom_Image_Tiles_valueChanged(int);
void onHoveredRegionMapTileChanged(int x, int y);

View File

@@ -49,9 +49,6 @@ public:
void setAutoRepeat(bool on);
bool autoRepeat() const;
int id() const;
QList<int> ids() const;
inline QWidget *parentWidget() const
{ return static_cast<QWidget *>(QObject::parent()); }

View File

@@ -149,10 +149,10 @@ public:
void select(unsigned tileId);
unsigned selectedTile = 0;
void selectVFlip(bool hFlip) { this->tile_hFlip = hFlip; }
void selectHFlip(bool hFlip) { this->tile_hFlip = hFlip; }
bool tile_hFlip = false;
void selectHFlip(bool vFlip) { this->tile_vFlip = vFlip; }
void selectVFlip(bool vFlip) { this->tile_vFlip = vFlip; }
bool tile_vFlip = false;
void selectPalette(int palette) {

View File

@@ -71,12 +71,6 @@ private slots:
void on_spinBox_paletteSelector_valueChanged(int arg1);
void on_checkBox_xFlip_stateChanged(int arg1);
void on_checkBox_yFlip_stateChanged(int arg1);
void on_actionSave_Tileset_triggered();
void on_actionImport_Primary_Tiles_triggered();
void on_actionImport_Secondary_Tiles_triggered();
@@ -149,6 +143,8 @@ private:
void commitTerrainType();
void commitLayerType();
void setRawAttributesVisible(bool visible);
void setXFlip(bool enabled);
void setYFlip(bool enabled);
Ui::TilesetEditor *ui;
History<MetatileHistoryItem*> metatileHistory;
@@ -175,6 +171,8 @@ private:
bool lockSelection = false;
QSet<uint16_t> metatileReloadQueue;
bool save();
signals:
void tilesetsSaved(QString, QString);
};

View File

@@ -73,7 +73,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/customscriptseditor.cpp \
src/ui/customscriptslistitem.cpp \
src/ui/divingmappixmapitem.cpp \
src/ui/draggablepixmapitem.cpp \
src/ui/eventpixmapitem.cpp \
src/ui/bordermetatilespixmapitem.cpp \
src/ui/collisionpixmapitem.cpp \
src/ui/connectionpixmapitem.cpp \
@@ -104,6 +104,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/metatileselector.cpp \
src/ui/movablerect.cpp \
src/ui/movementpermissionsselector.cpp \
src/ui/newdefinedialog.cpp \
src/ui/neweventtoolbutton.cpp \
src/ui/newlayoutdialog.cpp \
src/ui/newlayoutform.cpp \
@@ -133,6 +134,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/preferenceeditor.cpp \
src/ui/regionmappropertiesdialog.cpp \
src/ui/colorpicker.cpp \
src/ui/loadingscreen.cpp \
src/config.cpp \
src/editor.cpp \
src/main.cpp \
@@ -184,7 +186,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/customscriptseditor.h \
include/ui/customscriptslistitem.h \
include/ui/divingmappixmapitem.h \
include/ui/draggablepixmapitem.h \
include/ui/eventpixmapitem.h \
include/ui/bordermetatilespixmapitem.h \
include/ui/collisionpixmapitem.h \
include/ui/connectionpixmapitem.h \
@@ -216,6 +218,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/metatileselector.h \
include/ui/movablerect.h \
include/ui/movementpermissionsselector.h \
include/ui/newdefinedialog.h \
include/ui/neweventtoolbutton.h \
include/ui/newlayoutdialog.h \
include/ui/newlayoutform.h \
@@ -223,6 +226,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/newmapgroupdialog.h \
include/ui/noscrollcombobox.h \
include/ui/noscrollspinbox.h \
include/ui/noscrolltextedit.h \
include/ui/montabwidget.h \
include/ui/encountertablemodel.h \
include/ui/encountertabledelegates.h \
@@ -247,6 +251,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/preferenceeditor.h \
include/ui/regionmappropertiesdialog.h \
include/ui/colorpicker.h \
include/ui/loadingscreen.h \
include/config.h \
include/editor.h \
include/mainwindow.h \
@@ -266,8 +271,10 @@ FORMS += forms/mainwindow.ui \
forms/connectionslistitem.ui \
forms/customattributesframe.ui \
forms/gridsettingsdialog.ui \
forms/loadingscreen.ui \
forms/mapheaderform.ui \
forms/maplisttoolbar.ui \
forms/newdefinedialog.ui \
forms/newlayoutdialog.ui \
forms/newlayoutform.ui \
forms/newlocationdialog.ui \

View File

@@ -94,6 +94,7 @@
<file>images/collisions_unknown.png</file>
<file>images/Entities_16x16.png</file>
<file>images/pokemon_icon_placeholder.png</file>
<file>images/porysplash.gif</file>
<file>icons/clipboard.ico</file>
<file>icons/map_go.ico</file>
</qresource>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -39,7 +39,7 @@ export function onBlockHoverCleared() {
}
// Called when the dimensions of the map are changed.
export function onMapResized(oldWidth, oldHeight, newWidth, newHeight) {
export function onMapResized(oldWidth, oldHeight, delta) {
}

View File

@@ -18,6 +18,12 @@
#include <QAction>
#include <QAbstractButton>
const QVersionNumber porymapVersion = QVersionNumber::fromString(PORYMAP_VERSION);
// In both versions the default new map border is a generic tree
const QList<uint16_t> defaultBorder_RSE = {0x1D4, 0x1D5, 0x1DC, 0x1DD};
const QList<uint16_t> defaultBorder_FRLG = {0x14, 0x15, 0x1C, 0x1D};
const QList<uint32_t> defaultWarpBehaviors_RSE = {
0x0E, // MB_MOSSDEEP_GYM_WARP
0x0F, // MB_MT_PYRE_HOLE
@@ -89,6 +95,8 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{ProjectIdentifier::define_pals_total, {"define_pals_total", "NUM_PALS_TOTAL"}},
{ProjectIdentifier::define_tiles_per_metatile, {"define_tiles_per_metatile", "NUM_TILES_PER_METATILE"}},
{ProjectIdentifier::define_map_size, {"define_map_size", "MAX_MAP_DATA_SIZE"}},
{ProjectIdentifier::define_map_offset_width, {"define_map_offset_width", "MAP_OFFSET_W"}},
{ProjectIdentifier::define_map_offset_height, {"define_map_offset_height", "MAP_OFFSET_H"}},
{ProjectIdentifier::define_mask_metatile, {"define_mask_metatile", "MAPGRID_METATILE_ID_MASK"}},
{ProjectIdentifier::define_mask_collision, {"define_mask_collision", "MAPGRID_COLLISION_MASK"}},
{ProjectIdentifier::define_mask_elevation, {"define_mask_elevation", "MAPGRID_ELEVATION_MASK"}},
@@ -226,14 +234,14 @@ void KeyValueConfigBase::load() {
continue;
}
this->parseConfigKeyValue(match.captured("key").trimmed().toLower(), match.captured("value").trimmed());
this->parseConfigKeyValue(match.captured("key").trimmed(), match.captured("value").trimmed());
}
this->setUnreadKeys();
file.close();
}
void KeyValueConfigBase::save() {
bool KeyValueConfigBase::save() {
QString text = "";
QMap<QString, QString> map = this->getKeyValueMap();
for (QMap<QString, QString>::iterator it = map.begin(); it != map.end(); it++) {
@@ -241,15 +249,17 @@ void KeyValueConfigBase::save() {
}
QFile file(this->getConfigFilepath());
if (file.open(QIODevice::WriteOnly)) {
file.write(text.toUtf8());
file.close();
} else {
if (!file.open(QIODevice::WriteOnly)) {
logError(QString("Could not open config file '%1' for writing: ").arg(this->getConfigFilepath()) + file.errorString());
return false;
}
file.write(text.toUtf8());
file.close();
return true;
}
bool KeyValueConfigBase::getConfigBool(QString key, QString value) {
bool KeyValueConfigBase::getConfigBool(const QString &key, const QString &value) {
bool ok;
int result = value.toInt(&ok, 0);
if (!ok || (result != 0 && result != 1)) {
@@ -258,26 +268,35 @@ bool KeyValueConfigBase::getConfigBool(QString key, QString value) {
return (result != 0);
}
int KeyValueConfigBase::getConfigInteger(QString key, QString value, int min, int max, int defaultValue) {
int KeyValueConfigBase::getConfigInteger(const QString &key, const QString &value, int min, int max, int defaultValue) {
bool ok;
int result = value.toInt(&ok, 0);
if (!ok) {
logWarn(QString("Invalid config value for %1: '%2'. Must be an integer.").arg(key).arg(value));
return defaultValue;
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));
}
uint32_t KeyValueConfigBase::getConfigUint32(QString key, QString value, uint32_t min, uint32_t max, uint32_t defaultValue) {
uint32_t KeyValueConfigBase::getConfigUint32(const QString &key, const QString &value, uint32_t min, uint32_t max, uint32_t defaultValue) {
bool ok;
uint32_t result = value.toUInt(&ok, 0);
if (!ok) {
logWarn(QString("Invalid config value for %1: '%2'. Must be an integer.").arg(key).arg(value));
return defaultValue;
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));
}
QColor KeyValueConfigBase::getConfigColor(const QString &key, const QString &value, const QColor &defaultValue) {
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()));
color = defaultValue;
}
return color;
}
PorymapConfig porymapConfig;
QString PorymapConfig::getConfigFilepath() {
@@ -445,6 +464,18 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
}
} else if (key == "shown_in_game_reload_message") {
this->shownInGameReloadMessage = getConfigBool(key, value);
} else if (key == "grid_width") {
this->gridSettings.width = getConfigUint32(key, value);
} else if (key == "grid_height") {
this->gridSettings.height = getConfigUint32(key, value);
} else if (key == "grid_x") {
this->gridSettings.offsetX = getConfigInteger(key, value, 0, 999);
} else if (key == "grid_y") {
this->gridSettings.offsetY = getConfigInteger(key, value, 0, 999);
} else if (key == "grid_style") {
this->gridSettings.style = GridSettings::getStyleFromName(value);
} else if (key == "grid_color") {
this->gridSettings.color = getConfigColor(key, value);
} else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
}
@@ -522,6 +553,12 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
}
map.insert("event_selection_shape_mode", (this->eventSelectionShapeMode == QGraphicsPixmapItem::MaskShape) ? "mask" : "bounding_rect");
map.insert("shown_in_game_reload_message", this->shownInGameReloadMessage ? "1" : "0");
map.insert("grid_width", QString::number(this->gridSettings.width));
map.insert("grid_height", QString::number(this->gridSettings.height));
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.
return map;
}
@@ -737,6 +774,10 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->defaultElevation = getConfigUint32(key, value, 0, Block::maxValue);
} else if (key == "default_collision") {
this->defaultCollision = getConfigUint32(key, value, 0, Block::maxValue);
} else if (key == "default_map_width") {
this->defaultMapSize.setWidth(getConfigInteger(key, value, 1));
} else if (key == "default_map_height") {
this->defaultMapSize.setHeight(getConfigInteger(key, value, 1));
} else if (key == "new_map_border_metatiles") {
this->newMapBorderMetatileIds.clear();
QList<QString> metatileIds = value.split(",");
@@ -799,6 +840,10 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
} else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
}
} else if (key.startsWith("global_constant/")) {
this->globalConstants.insert(key.mid(QStringLiteral("global_constant/").length()), value);
} else if (key == "global_constants_filepaths") {
this->globalConstantsFilepaths = value.split(",", Qt::SkipEmptyParts);
} else if (key == "prefabs_filepath") {
this->prefabFilepath = value;
} else if (key == "prefabs_import_prompted") {
@@ -809,6 +854,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
} else if (key == "set_transparent_pixels_black") {
this->setTransparentPixelsBlack = getConfigBool(key, value);
} else if (key == "preserve_matching_only_data") {
this->preserveMatchingOnlyData = getConfigBool(key, value);
} else if (key == "event_icon_path_object") {
this->eventIconPaths[Event::Group::Object] = value;
} else if (key == "event_icon_path_warp") {
@@ -820,13 +867,21 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
} else if (key == "event_icon_path_heal") {
this->eventIconPaths[Event::Group::Heal] = value;
} else if (key.startsWith("pokemon_icon_path/")) {
this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()).toUpper(), value);
this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()), value);
} else if (key == "collision_sheet_path") {
this->collisionSheetPath = value;
} else if (key == "collision_sheet_width") {
this->collisionSheetWidth = getConfigUint32(key, value, 1, Block::maxValue);
this->collisionSheetSize.setWidth(getConfigInteger(key, value, 1, Block::maxValue));
} else if (key == "collision_sheet_height") {
this->collisionSheetHeight = getConfigUint32(key, value, 1, Block::maxValue);
this->collisionSheetSize.setHeight(getConfigInteger(key, value, 1, Block::maxValue));
} else if (key == "player_view_north") {
this->playerViewDistance.setTop(getConfigInteger(key, value, 0, INT_MAX, GBA_V_DIST_TO_CENTER));
} else if (key == "player_view_south") {
this->playerViewDistance.setBottom(getConfigInteger(key, value, 0, INT_MAX, GBA_V_DIST_TO_CENTER));
} else if (key == "player_view_west") {
this->playerViewDistance.setLeft(getConfigInteger(key, value, 0, INT_MAX, GBA_H_DIST_TO_CENTER));
} else if (key == "player_view_east") {
this->playerViewDistance.setRight(getConfigInteger(key, value, 0, INT_MAX, GBA_H_DIST_TO_CENTER));
} else if (key == "warp_behaviors") {
this->warpBehaviors.clear();
value.remove(" ");
@@ -862,7 +917,7 @@ void ProjectConfig::setUnreadKeys() {
if (!readKeys.contains("enable_event_clone_object")) this->eventCloneObjectEnabled = isPokefirered;
if (!readKeys.contains("enable_floor_number")) this->floorNumberEnabled = isPokefirered;
if (!readKeys.contains("create_map_text_file")) this->createMapTextFileEnabled = (this->baseGameVersion != BaseGameVersion::pokeemerald);
if (!readKeys.contains("new_map_border_metatiles")) this->newMapBorderMetatileIds = isPokefirered ? DEFAULT_BORDER_FRLG : DEFAULT_BORDER_RSE;
if (!readKeys.contains("new_map_border_metatiles")) this->newMapBorderMetatileIds = isPokefirered ? defaultBorder_FRLG : defaultBorder_RSE;
if (!readKeys.contains("default_secondary_tileset")) this->defaultSecondaryTileset = isPokefirered ? "gTileset_PalletTown" : "gTileset_Petalburg";
if (!readKeys.contains("metatile_attributes_size")) this->metatileAttributesSize = Metatile::getDefaultAttributesSize(this->baseGameVersion);
if (!readKeys.contains("metatile_behavior_mask")) this->metatileBehaviorMask = Metatile::getDefaultAttributesMask(this->baseGameVersion, Metatile::Attr::Behavior);
@@ -890,6 +945,8 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("default_metatile", Metatile::getMetatileIdString(this->defaultMetatileId));
map.insert("default_elevation", QString::number(this->defaultElevation));
map.insert("default_collision", QString::number(this->defaultCollision));
map.insert("default_map_width", QString::number(this->defaultMapSize.width()));
map.insert("default_map_height", QString::number(this->defaultMapSize.height()));
map.insert("new_map_border_metatiles", Metatile::getMetatileIdStrings(this->newMapBorderMetatileIds));
map.insert("default_primary_tileset", this->defaultPrimaryTileset);
map.insert("default_secondary_tileset", this->defaultSecondaryTileset);
@@ -901,6 +958,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("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));
map.insert("metatile_terrain_type_mask", Util::toHexString(this->metatileTerrainTypeMask));
@@ -918,16 +976,24 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("event_icon_path_coord", this->eventIconPaths[Event::Group::Coord]);
map.insert("event_icon_path_bg", this->eventIconPaths[Event::Group::Bg]);
map.insert("event_icon_path_heal", this->eventIconPaths[Event::Group::Heal]);
for (auto i = this->pokemonIconPaths.cbegin(), end = this->pokemonIconPaths.cend(); i != end; i++){
const QString path = i.value();
if (!path.isEmpty()) map.insert("pokemon_icon_path/" + i.key(), path);
for (auto it = this->pokemonIconPaths.constBegin(); it != this->pokemonIconPaths.constEnd(); it++) {
const QString path = it.value();
if (!path.isEmpty()) map.insert("pokemon_icon_path/" + it.key(), path);
}
for (auto i = this->identifiers.cbegin(), end = this->identifiers.cend(); i != end; i++) {
map.insert("ident/"+defaultIdentifiers.value(i.key()).first, i.value());
for (auto it = this->globalConstants.constBegin(); it != this->globalConstants.constEnd(); it++) {
map.insert("global_constant/" + it.key(), it.value());
}
map.insert("global_constants_filepaths", this->globalConstantsFilepaths.join(","));
for (auto it = this->identifiers.constBegin(); it != this->identifiers.constEnd(); it++) {
map.insert("ident/"+defaultIdentifiers.value(it.key()).first, it.value());
}
map.insert("collision_sheet_path", this->collisionSheetPath);
map.insert("collision_sheet_width", QString::number(this->collisionSheetWidth));
map.insert("collision_sheet_height", QString::number(this->collisionSheetHeight));
map.insert("collision_sheet_width", QString::number(this->collisionSheetSize.width()));
map.insert("collision_sheet_height", QString::number(this->collisionSheetSize.height()));
map.insert("player_view_north", QString::number(this->playerViewDistance.top()));
map.insert("player_view_south", QString::number(this->playerViewDistance.bottom()));
map.insert("player_view_west", QString::number(this->playerViewDistance.left()));
map.insert("player_view_east", QString::number(this->playerViewDistance.right()));
QStringList warpBehaviorStrs;
for (const auto &value : this->warpBehaviors)
warpBehaviorStrs.append("0x" + QString("%1").arg(value, 2, 16, QChar('0')).toUpper());

View File

@@ -1,5 +1,5 @@
#include "editcommands.h"
#include "draggablepixmapitem.h"
#include "eventpixmapitem.h"
#include "bordermetatilespixmapitem.h"
#include "editor.h"

View File

@@ -20,8 +20,7 @@ Event* Event::create(Event::Type type) {
}
Event::~Event() {
if (this->eventFrame)
this->eventFrame->deleteLater();
delete this->eventFrame;
}
EventFrame *Event::getEventFrame() {
@@ -34,7 +33,7 @@ void Event::destroyEventFrame() {
this->eventFrame = nullptr;
}
void Event::setPixmapItem(DraggablePixmapItem *item) {
void Event::setPixmapItem(EventPixmapItem *item) {
this->pixmapItem = item;
if (this->eventFrame) {
this->eventFrame->invalidateConnections();
@@ -51,24 +50,6 @@ void Event::setDefaultValues(Project *) {
this->setElevation(projectConfig.defaultElevation);
}
void Event::readCustomAttributes(const QJsonObject &json) {
this->customAttributes.clear();
const QSet<QString> expectedFields = this->getExpectedFields();
for (auto i = json.constBegin(); i != json.constEnd(); i++) {
if (!expectedFields.contains(i.key())) {
this->customAttributes[i.key()] = i.value();
}
}
}
void Event::addCustomAttributesTo(OrderedJson::object *obj) const {
for (auto i = this->customAttributes.constBegin(); i != this->customAttributes.constEnd(); i++) {
if (!obj->contains(i.key())) {
(*obj)[i.key()] = OrderedJson::fromQJsonValue(i.value());
}
}
}
void Event::modify() {
this->map->modify();
}
@@ -123,7 +104,7 @@ QString Event::typeToString(Event::Type type) {
{Event::Type::CloneObject, "Clone Object"},
{Event::Type::Warp, "Warp"},
{Event::Type::Trigger, "Trigger"},
{Event::Type::WeatherTrigger, "Weather"},
{Event::Type::WeatherTrigger, "Weather Trigger"},
{Event::Type::Sign, "Sign"},
{Event::Type::HiddenItem, "Hidden Item"},
{Event::Type::SecretBase, "Secret Base"},
@@ -132,9 +113,10 @@ QString Event::typeToString(Event::Type type) {
return typeToStringMap.value(type);
}
void Event::loadPixmap(Project *project) {
QPixmap Event::loadPixmap(Project *project) {
this->pixmap = project->getEventPixmap(this->getEventGroup());
this->usesDefaultPixmap = true;
return this->pixmap;
}
@@ -170,12 +152,13 @@ EventFrame *ObjectEvent::createEventFrame() {
OrderedJson::object ObjectEvent::buildEventJson(Project *) {
OrderedJson::object objectJson;
if (projectConfig.eventCloneObjectEnabled) {
objectJson["type"] = Event::typeToJsonKey(Event::Type::Object);
}
QString idName = this->getIdName();
if (!idName.isEmpty())
objectJson["local_id"] = idName;
if (projectConfig.eventCloneObjectEnabled) {
objectJson["type"] = Event::typeToJsonKey(Event::Type::Object);
}
objectJson["graphics_id"] = this->getGfx();
objectJson["x"] = this->getX();
objectJson["y"] = this->getY();
@@ -187,27 +170,26 @@ OrderedJson::object ObjectEvent::buildEventJson(Project *) {
objectJson["trainer_sight_or_berry_tree_id"] = this->getSightRadiusBerryTreeID();
objectJson["script"] = this->getScript();
objectJson["flag"] = this->getFlag();
this->addCustomAttributesTo(&objectJson);
OrderedJson::append(&objectJson, this->getCustomAttributes());
return objectJson;
}
bool ObjectEvent::loadFromJson(const QJsonObject &json, Project *) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setIdName(ParseUtil::jsonToQString(json["local_id"]));
this->setGfx(ParseUtil::jsonToQString(json["graphics_id"]));
this->setMovement(ParseUtil::jsonToQString(json["movement_type"]));
this->setRadiusX(ParseUtil::jsonToInt(json["movement_range_x"]));
this->setRadiusY(ParseUtil::jsonToInt(json["movement_range_y"]));
this->setTrainerType(ParseUtil::jsonToQString(json["trainer_type"]));
this->setSightRadiusBerryTreeID(ParseUtil::jsonToQString(json["trainer_sight_or_berry_tree_id"]));
this->setScript(ParseUtil::jsonToQString(json["script"]));
this->setFlag(ParseUtil::jsonToQString(json["flag"]));
bool ObjectEvent::loadFromJson(QJsonObject json, Project *) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setIdName(readString(&json, "local_id"));
this->setGfx(readString(&json, "graphics_id"));
this->setMovement(readString(&json, "movement_type"));
this->setRadiusX(readInt(&json, "movement_range_x"));
this->setRadiusY(readInt(&json, "movement_range_y"));
this->setTrainerType(readString(&json, "trainer_type"));
this->setSightRadiusBerryTreeID(readString(&json, "trainer_sight_or_berry_tree_id"));
this->setScript(readString(&json, "script"));
this->setFlag(readString(&json, "flag"));
this->readCustomAttributes(json);
this->setCustomAttributes(json);
return true;
}
@@ -222,36 +204,34 @@ void ObjectEvent::setDefaultValues(Project *project) {
this->setSightRadiusBerryTreeID("0");
}
const QSet<QString> expectedObjectFields = {
"local_id",
"graphics_id",
"elevation",
"movement_type",
"movement_range_x",
"movement_range_y",
"trainer_type",
"trainer_sight_or_berry_tree_id",
"script",
"flag",
};
QSet<QString> ObjectEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedObjectFields;
QSet<QString> expectedFields = {
"x",
"y",
"local_id",
"graphics_id",
"elevation",
"movement_type",
"movement_range_x",
"movement_range_y",
"trainer_type",
"trainer_sight_or_berry_tree_id",
"script",
"flag",
};
if (projectConfig.eventCloneObjectEnabled) {
expectedFields.insert("type");
}
expectedFields << "x" << "y";
return expectedFields;
}
void ObjectEvent::loadPixmap(Project *project) {
QPixmap ObjectEvent::loadPixmap(Project *project) {
this->pixmap = project->getEventPixmap(this->gfx, this->movement);
if (!this->pixmap.isNull()) {
this->usesDefaultPixmap = false;
} else {
Event::loadPixmap(project);
return this->pixmap;
}
return Event::loadPixmap(project);
}
@@ -282,65 +262,62 @@ EventFrame *CloneObjectEvent::createEventFrame() {
OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) {
OrderedJson::object cloneJson;
cloneJson["type"] = Event::typeToJsonKey(Event::Type::CloneObject);
QString idName = this->getIdName();
if (!idName.isEmpty())
cloneJson["local_id"] = idName;
cloneJson["type"] = Event::typeToJsonKey(Event::Type::CloneObject);
cloneJson["graphics_id"] = this->getGfx();
cloneJson["x"] = this->getX();
cloneJson["y"] = this->getY();
cloneJson["target_local_id"] = this->getTargetID();
const QString mapName = this->getTargetMap();
cloneJson["target_map"] = project->getMapConstant(mapName, mapName);
this->addCustomAttributesTo(&cloneJson);
OrderedJson::append(&cloneJson, this->getCustomAttributes());
return cloneJson;
}
bool CloneObjectEvent::loadFromJson(const QJsonObject &json, Project *project) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setIdName(ParseUtil::jsonToQString(json["local_id"]));
this->setGfx(ParseUtil::jsonToQString(json["graphics_id"]));
this->setTargetID(ParseUtil::jsonToInt(json["target_local_id"]));
bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setIdName(readString(&json, "local_id"));
this->setGfx(readString(&json, "graphics_id"));
this->setTargetID(readString(&json, "target_local_id"));
// Log a warning if "target_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["target_map"]);
const QString mapConstant = readString(&json, "target_map");
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Unknown Target Map constant '%1'.").arg(mapConstant));
this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomAttributes(json);
this->setCustomAttributes(json);
return true;
}
void CloneObjectEvent::setDefaultValues(Project *project) {
this->setGfx(project->gfxDefines.key(0, "0"));
this->setTargetID(1);
this->setTargetID(QString::number(Event::getIndexOffset(Event::Group::Object)));
if (this->getMap()) this->setTargetMap(this->getMap()->name());
}
const QSet<QString> expectedCloneObjectFields = {
"type",
"local_id",
"graphics_id",
"target_local_id",
"target_map",
};
QSet<QString> CloneObjectEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedCloneObjectFields;
expectedFields << "x" << "y";
static const QSet<QString> expectedFields = {
"x",
"y",
"type",
"local_id",
"graphics_id",
"target_local_id",
"target_map",
};
return expectedFields;
}
void CloneObjectEvent::loadPixmap(Project *project) {
QPixmap CloneObjectEvent::loadPixmap(Project *project) {
// Try to get the targeted object to clone
int eventIndex = this->targetID - 1;
Map *clonedMap = project->loadMap(this->targetMap);
Event *clonedEvent = clonedMap ? clonedMap->getEvent(Event::Group::Object, eventIndex) : nullptr;
Event *clonedEvent = clonedMap ? clonedMap->getEvent(Event::Group::Object, this->targetID) : nullptr;
if (clonedEvent && clonedEvent->getEventType() == Event::Type::Object) {
// Get graphics data from cloned object
@@ -352,7 +329,7 @@ void CloneObjectEvent::loadPixmap(Project *project) {
this->gfx = project->gfxDefines.key(0, "0");
this->movement = project->movementTypes.value(0, "0");
}
ObjectEvent::loadPixmap(project);
return ObjectEvent::loadPixmap(project);
}
@@ -389,25 +366,23 @@ OrderedJson::object WarpEvent::buildEventJson(Project *project) {
warpJson["dest_map"] = project->getMapConstant(mapName, mapName);
warpJson["dest_warp_id"] = this->getDestinationWarpID();
this->addCustomAttributesTo(&warpJson);
OrderedJson::append(&warpJson, this->getCustomAttributes());
return warpJson;
}
bool WarpEvent::loadFromJson(const QJsonObject &json, Project *project) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setDestinationWarpID(ParseUtil::jsonToQString(json["dest_warp_id"]));
bool WarpEvent::loadFromJson(QJsonObject json, Project *project) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setDestinationWarpID(readString(&json, "dest_warp_id"));
// Log a warning if "dest_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["dest_map"]);
const QString mapConstant = readString(&json, "dest_map");
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Unknown Destination Map constant '%1'.").arg(mapConstant));
this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomAttributes(json);
this->setCustomAttributes(json);
return true;
}
@@ -417,16 +392,14 @@ void WarpEvent::setDefaultValues(Project *) {
this->setElevation(0);
}
const QSet<QString> expectedWarpFields = {
"elevation",
"dest_map",
"dest_warp_id",
};
QSet<QString> WarpEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedWarpFields;
expectedFields << "x" << "y";
static const QSet<QString> expectedFields = {
"x",
"y",
"elevation",
"dest_map",
"dest_warp_id",
};
return expectedFields;
}
@@ -476,21 +449,19 @@ OrderedJson::object TriggerEvent::buildEventJson(Project *) {
triggerJson["var_value"] = this->getScriptVarValue();
triggerJson["script"] = this->getScriptLabel();
this->addCustomAttributesTo(&triggerJson);
OrderedJson::append(&triggerJson, this->getCustomAttributes());
return triggerJson;
}
bool TriggerEvent::loadFromJson(const QJsonObject &json, Project *) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setScriptVar(ParseUtil::jsonToQString(json["var"]));
this->setScriptVarValue(ParseUtil::jsonToQString(json["var_value"]));
this->setScriptLabel(ParseUtil::jsonToQString(json["script"]));
this->readCustomAttributes(json);
bool TriggerEvent::loadFromJson(QJsonObject json, Project *) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setScriptVar(readString(&json, "var"));
this->setScriptVarValue(readString(&json, "var_value"));
this->setScriptLabel(readString(&json, "script"));
this->setCustomAttributes(json);
return true;
}
@@ -501,18 +472,16 @@ void TriggerEvent::setDefaultValues(Project *project) {
this->setElevation(0);
}
const QSet<QString> expectedTriggerFields = {
"type",
"elevation",
"var",
"var_value",
"script",
};
QSet<QString> TriggerEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedTriggerFields;
expectedFields << "x" << "y";
static const QSet<QString> expectedFields = {
"x",
"y",
"type",
"elevation",
"var",
"var_value",
"script",
};
return expectedFields;
}
@@ -548,19 +517,17 @@ OrderedJson::object WeatherTriggerEvent::buildEventJson(Project *) {
weatherJson["elevation"] = this->getElevation();
weatherJson["weather"] = this->getWeather();
this->addCustomAttributesTo(&weatherJson);
OrderedJson::append(&weatherJson, this->getCustomAttributes());
return weatherJson;
}
bool WeatherTriggerEvent::loadFromJson(const QJsonObject &json, Project *) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setWeather(ParseUtil::jsonToQString(json["weather"]));
this->readCustomAttributes(json);
bool WeatherTriggerEvent::loadFromJson(QJsonObject json, Project *) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setWeather(readString(&json, "weather"));
this->setCustomAttributes(json);
return true;
}
@@ -569,16 +536,14 @@ void WeatherTriggerEvent::setDefaultValues(Project *project) {
this->setElevation(0);
}
const QSet<QString> expectedWeatherTriggerFields = {
"type",
"elevation",
"weather",
};
QSet<QString> WeatherTriggerEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedWeatherTriggerFields;
expectedFields << "x" << "y";
static const QSet<QString> expectedFields = {
"x",
"y",
"type",
"elevation",
"weather",
};
return expectedFields;
}
@@ -616,20 +581,18 @@ OrderedJson::object SignEvent::buildEventJson(Project *) {
signJson["player_facing_dir"] = this->getFacingDirection();
signJson["script"] = this->getScriptLabel();
this->addCustomAttributesTo(&signJson);
OrderedJson::append(&signJson, this->getCustomAttributes());
return signJson;
}
bool SignEvent::loadFromJson(const QJsonObject &json, Project *) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setFacingDirection(ParseUtil::jsonToQString(json["player_facing_dir"]));
this->setScriptLabel(ParseUtil::jsonToQString(json["script"]));
this->readCustomAttributes(json);
bool SignEvent::loadFromJson(QJsonObject json, Project *) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setFacingDirection(readString(&json, "player_facing_dir"));
this->setScriptLabel(readString(&json, "script"));
this->setCustomAttributes(json);
return true;
}
@@ -639,17 +602,15 @@ void SignEvent::setDefaultValues(Project *project) {
this->setElevation(0);
}
const QSet<QString> expectedSignFields = {
"type",
"elevation",
"player_facing_dir",
"script",
};
QSet<QString> SignEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedSignFields;
expectedFields << "x" << "y";
static const QSet<QString> expectedFields = {
"x",
"y",
"type",
"elevation",
"player_facing_dir",
"script",
};
return expectedFields;
}
@@ -695,26 +656,24 @@ OrderedJson::object HiddenItemEvent::buildEventJson(Project *) {
hiddenItemJson["underfoot"] = this->getUnderfoot();
}
this->addCustomAttributesTo(&hiddenItemJson);
OrderedJson::append(&hiddenItemJson, this->getCustomAttributes());
return hiddenItemJson;
}
bool HiddenItemEvent::loadFromJson(const QJsonObject &json, Project *) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setItem(ParseUtil::jsonToQString(json["item"]));
this->setFlag(ParseUtil::jsonToQString(json["flag"]));
bool HiddenItemEvent::loadFromJson(QJsonObject json, Project *) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setItem(readString(&json, "item"));
this->setFlag(readString(&json, "flag"));
if (projectConfig.hiddenItemQuantityEnabled) {
this->setQuantity(ParseUtil::jsonToInt(json["quantity"]));
this->setQuantity(readInt(&json, "quantity"));
}
if (projectConfig.hiddenItemRequiresItemfinderEnabled) {
this->setUnderfoot(ParseUtil::jsonToBool(json["underfoot"]));
this->setUnderfoot(readBool(&json, "underfoot"));
}
this->readCustomAttributes(json);
this->setCustomAttributes(json);
return true;
}
@@ -729,23 +688,21 @@ void HiddenItemEvent::setDefaultValues(Project *project) {
}
}
const QSet<QString> expectedHiddenItemFields = {
"type",
"elevation",
"item",
"flag",
};
QSet<QString> HiddenItemEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedHiddenItemFields;
QSet<QString> expectedFields = {
"x",
"y",
"type",
"elevation",
"item",
"flag",
};
if (projectConfig.hiddenItemQuantityEnabled) {
expectedFields << "quantity";
}
if (projectConfig.hiddenItemRequiresItemfinderEnabled) {
expectedFields << "underfoot";
}
expectedFields << "x" << "y";
return expectedFields;
}
@@ -781,19 +738,17 @@ OrderedJson::object SecretBaseEvent::buildEventJson(Project *) {
secretBaseJson["elevation"] = this->getElevation();
secretBaseJson["secret_base_id"] = this->getBaseID();
this->addCustomAttributesTo(&secretBaseJson);
OrderedJson::append(&secretBaseJson, this->getCustomAttributes());
return secretBaseJson;
}
bool SecretBaseEvent::loadFromJson(const QJsonObject &json, Project *) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setBaseID(ParseUtil::jsonToQString(json["secret_base_id"]));
this->readCustomAttributes(json);
bool SecretBaseEvent::loadFromJson(QJsonObject json, Project *) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setElevation(readInt(&json, "elevation"));
this->setBaseID(readString(&json, "secret_base_id"));
this->setCustomAttributes(json);
return true;
}
@@ -802,16 +757,14 @@ void SecretBaseEvent::setDefaultValues(Project *project) {
this->setElevation(0);
}
const QSet<QString> expectedSecretBaseFields = {
"type",
"elevation",
"secret_base_id",
};
QSet<QString> SecretBaseEvent::getExpectedFields() {
QSet<QString> expectedFields = QSet<QString>();
expectedFields = expectedSecretBaseFields;
expectedFields << "x" << "y";
static const QSet<QString> expectedFields = {
"x",
"y",
"type",
"elevation",
"secret_base_id",
};
return expectedFields;
}
@@ -823,6 +776,7 @@ Event *HealLocationEvent::duplicate() const {
copy->setX(this->getX());
copy->setY(this->getY());
copy->setIdName(this->getIdName());
copy->setHostMapName(this->getHostMapName());
copy->setRespawnMapName(this->getRespawnMapName());
copy->setRespawnNPC(this->getRespawnNPC());
@@ -839,12 +793,15 @@ EventFrame *HealLocationEvent::createEventFrame() {
return this->eventFrame;
}
QString HealLocationEvent::getHostMapName() const {
return this->getMap() ? this->getMap()->constantName() : this->hostMapName;
}
OrderedJson::object HealLocationEvent::buildEventJson(Project *project) {
OrderedJson::object healLocationJson;
healLocationJson["id"] = this->getIdName();
// This field doesn't need to be stored in the Event itself, so it's output only.
healLocationJson["map"] = this->getMap() ? this->getMap()->constantName() : QString();
healLocationJson["map"] = this->getHostMapName();
healLocationJson["x"] = this->getX();
healLocationJson["y"] = this->getY();
if (projectConfig.healLocationRespawnDataEnabled) {
@@ -853,26 +810,26 @@ OrderedJson::object HealLocationEvent::buildEventJson(Project *project) {
healLocationJson["respawn_npc"] = this->getRespawnNPC();
}
this->addCustomAttributesTo(&healLocationJson);
OrderedJson::append(&healLocationJson, this->getCustomAttributes());
return healLocationJson;
}
bool HealLocationEvent::loadFromJson(const QJsonObject &json, Project *project) {
this->setX(ParseUtil::jsonToInt(json["x"]));
this->setY(ParseUtil::jsonToInt(json["y"]));
this->setIdName(ParseUtil::jsonToQString(json["id"]));
bool HealLocationEvent::loadFromJson(QJsonObject json, Project *project) {
this->setX(readInt(&json, "x"));
this->setY(readInt(&json, "y"));
this->setIdName(readString(&json, "id"));
this->setHostMapName(readString(&json, "map"));
if (projectConfig.healLocationRespawnDataEnabled) {
// Log a warning if "respawn_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["respawn_map"]);
const QString mapConstant = readString(&json, "respawn_map");
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Unknown Respawn Map constant '%1'.").arg(mapConstant));
this->setRespawnMapName(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->setRespawnNPC(ParseUtil::jsonToQString(json["respawn_npc"]));
this->setRespawnNPC(readString(&json, "respawn_npc"));
}
this->readCustomAttributes(json);
this->setCustomAttributes(json);
return true;
}
@@ -885,16 +842,19 @@ void HealLocationEvent::setDefaultValues(Project *project) {
}
const QSet<QString> expectedHealLocationFields = {
"id",
"map"
};
QSet<QString> HealLocationEvent::getExpectedFields() {
QSet<QString> expectedFields = expectedHealLocationFields;
QSet<QString> expectedFields = {
"x",
"y",
"id",
"map",
};
if (projectConfig.healLocationRespawnDataEnabled) {
expectedFields.insert("respawn_map");
expectedFields.insert("respawn_npc");
}
expectedFields << "x" << "y";
return expectedFields;
}

View File

@@ -83,16 +83,17 @@ QRect Map::getConnectionRect(const QString &direction, Layout * fromLayout) cons
int x = 0, y = 0;
int w = getWidth(), h = getHeight();
QMargins viewDistance = Project::getMetatileViewDistance();
if (direction == "up") {
h = qMin(h, BORDER_DISTANCE);
h = qMin(h, viewDistance.top());
y = getHeight() - h;
} else if (direction == "down") {
h = qMin(h, BORDER_DISTANCE);
h = qMin(h, viewDistance.bottom());
} else if (direction == "left") {
w = qMin(w, BORDER_DISTANCE);
w = qMin(w, viewDistance.left());
x = getWidth() - w;
} else if (direction == "right") {
w = qMin(w, BORDER_DISTANCE);
w = qMin(w, viewDistance.right());
} else if (MapConnection::isDiving(direction)) {
if (fromLayout) {
w = qMin(w, fromLayout->getWidth());
@@ -194,6 +195,49 @@ Event* Map::getEvent(Event::Group group, int index) const {
return m_events[group].value(index, nullptr);
}
Event* Map::getEvent(Event::Group group, const QString &idName) const {
if (idName.isEmpty())
return nullptr;
bool idIsNumber;
int id = idName.toInt(&idIsNumber, 0);
if (idIsNumber)
return getEvent(group, id - Event::getIndexOffset(group));
auto events = getEvents(group);
for (const auto &event : events) {
if (event->getIdName() == idName) {
return event;
}
}
return nullptr;
}
// Returns a list of ID names for the given event group (or all events, if no group is given).
// For events with no explicit ID name, their index string is given instead.
QStringList Map::getEventIdNames(Event::Group group) const {
QList<Event::Group> groups;
if (group == Event::Group::None) {
groups = Event::groups();
} else {
groups.append(group);
}
QStringList idNames;
for (const auto &group : groups) {
const auto events = m_events[group];
int indexOffset = Event::getIndexOffset(group);
for (int i = 0; i < events.length(); i++) {
QString idName = events.at(i)->getIdName();
if (idName.isEmpty()) {
idName = QString::number(i + indexOffset);
}
idNames.append(idName);
}
}
return idNames;
}
int Map::getNumEvents(Event::Group group) const {
if (group == Event::Group::None) {
// Total number of events
@@ -288,6 +332,15 @@ void Map::removeConnection(MapConnection *connection) {
emit connectionRemoved(connection);
}
// Return the first map connection that has the given direction.
MapConnection* Map::getConnection(const QString &direction) const {
for (const auto &connection : m_connections) {
if (connection->direction() == direction)
return connection;
}
return nullptr;
}
void Map::commit(QUndoCommand *cmd) {
m_editHistory->push(cmd);
}

View File

@@ -31,6 +31,7 @@ void Layout::copyFrom(const Layout *other) {
this->tileset_secondary = other->tileset_secondary;
this->blockdata = other->blockdata;
this->border = other->border;
this->customData = other->customData;
}
QString Layout::layoutConstantFromName(const QString &name) {
@@ -62,17 +63,11 @@ bool Layout::isWithinBorderBounds(int x, int y) const {
return (x >= 0 && x < this->getBorderWidth() && y >= 0 && y < this->getBorderHeight());
}
int Layout::getBorderDrawWidth() const {
return getBorderDrawDistance(border_width, BORDER_DISTANCE);
}
int Layout::getBorderDrawHeight() const {
return getBorderDrawDistance(border_height, BORDER_DISTANCE);
}
// We need to draw sufficient border blocks to fill the area that gets loaded around the player in-game (BORDER_DISTANCE).
// Note that this is not the same as the player's view distance.
// The result will be some multiple of the input dimension, because we only draw the border in increments of its full width/height.
// Calculate the distance away from the layout's edge that we need to start drawing border blocks.
// We need to fulfill two requirements here:
// - We should draw enough to fill the player's in-game view
// - The value should be some multiple of the border's dimension
// (otherwise the border won't be positioned the same as it would in-game).
int Layout::getBorderDrawDistance(int dimension, qreal minimum) {
if (dimension >= minimum)
return dimension;
@@ -80,6 +75,23 @@ int Layout::getBorderDrawDistance(int dimension, qreal minimum) {
// Get first multiple of dimension >= the minimum
return dimension * qCeil(minimum / qMax(dimension, 1));
}
QMargins Layout::getBorderMargins() const {
QMargins minimum = Project::getMetatileViewDistance();
QMargins distance;
distance.setTop(getBorderDrawDistance(this->border_height, minimum.top()));
distance.setBottom(getBorderDrawDistance(this->border_height, minimum.bottom()));
distance.setLeft(getBorderDrawDistance(this->border_width, minimum.left()));
distance.setRight(getBorderDrawDistance(this->border_width, minimum.right()));
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.
QRect Layout::getVisibleRect() const {
QRect area = QRect(0, 0, this->width * 16, this->height * 16);
return area += (Project::getMetatileViewDistance() * 16);
}
bool Layout::getBlock(int x, int y, Block *out) {
if (isWithinBounds(x, y)) {
@@ -177,46 +189,38 @@ void Layout::setBorderBlockData(Blockdata newBlockdata, bool enableScriptCallbac
}
}
void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) {
void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata) {
if (setNewBlockdata) {
setNewDimensionsBlockdata(newWidth, newHeight);
}
int oldWidth = this->width;
int oldHeight = this->height;
this->width = newWidth;
this->height = newHeight;
if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) {
Scripting::cb_MapResized(oldWidth, oldHeight, newWidth, newHeight);
}
emit dimensionsChanged(QSize(getWidth(), getHeight()));
emit dimensionsChanged(QSize(this->width, this->height));
}
void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata) {
int newWidth = this->width + margins.left() + margins.right();
int newHeight = this->height + margins.top() + margins.bottom();
void Layout::adjustDimensions(const QMargins &margins, bool setNewBlockdata) {
int oldWidth = this->width;
int oldHeight = this->height;
this->width = oldWidth + margins.left() + margins.right();
this->height = oldHeight + margins.top() + margins.bottom();
if (setNewBlockdata) {
// Fill new blockdata
Blockdata newBlockdata;
for (int y = 0; y < newHeight; y++)
for (int x = 0; x < newWidth; x++) {
if ((x < margins.left()) || (x >= newWidth - margins.right()) || (y < margins.top()) || (y >= newHeight - margins.bottom())) {
for (int y = 0; y < this->height; y++)
for (int x = 0; x < this->width; x++) {
if ((x < margins.left()) || (x >= this->width - margins.right()) || (y < margins.top()) || (y >= this->height - margins.bottom())) {
newBlockdata.append(0);
} else {
int index = (y - margins.top()) * this->width + (x - margins.left());
int index = (y - margins.top()) * oldWidth + (x - margins.left());
newBlockdata.append(this->blockdata.value(index));
}
}
this->blockdata = newBlockdata;
}
this->width = newWidth;
this->height = newHeight;
emit dimensionsChanged(QSize(getWidth(), getHeight()));
Scripting::cb_MapResized(oldWidth, oldHeight, margins);
emit dimensionsChanged(QSize(this->width, this->height));
}
void Layout::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) {
@@ -466,11 +470,50 @@ QPixmap Layout::getLayoutItemPixmap() {
return this->layoutItem ? this->layoutItem->pixmap() : QPixmap();
}
void Layout::setClean() {
this->editHistory.setClean();
this->hasUnsavedDataChanges = false;
}
bool Layout::hasUnsavedChanges() const {
return !this->editHistory.isClean() || this->hasUnsavedDataChanges || !this->newFolderPath.isEmpty();
}
bool Layout::save(const QString &root) {
if (!this->newFolderPath.isEmpty()) {
// Layout directory doesn't exist yet, create it now.
const QString fullPath = QString("%1/%2").arg(root).arg(this->newFolderPath);
if (!QDir::root().mkpath(fullPath)) {
logError(QString("Failed to create directory for new layout: '%1'").arg(fullPath));
return false;
}
this->newFolderPath = QString();
}
bool success = true;
if (!saveBorder(root)) success = false;
if (!saveBlockdata(root)) success = false;
if (!success)
return false;
this->editHistory.setClean();
this->hasUnsavedDataChanges = false;
return true;
}
bool Layout::saveBorder(const QString &root) {
QString path = QString("%1/%2").arg(root).arg(this->border_path);
return writeBlockdata(path, this->border);
}
bool Layout::saveBlockdata(const QString &root) {
QString path = QString("%1/%2").arg(root).arg(this->blockdata_path);
return writeBlockdata(path, this->blockdata);
}
bool Layout::writeBlockdata(const QString &path, const Blockdata &blockdata) const {
QFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
logError(QString("Could not open '%1' for writing: %2").arg(path).arg(file.errorString()));
return false;
}
QByteArray data = blockdata.serialize();
file.write(data);
return true;
}

View File

@@ -38,14 +38,14 @@ QList<QRgb> PaletteUtil::parse(QString filepath, bool *error) {
return QList<QRgb>();
}
void PaletteUtil::writeJASC(QString filepath, QVector<QRgb> palette, int offset, int nColors) {
bool PaletteUtil::writeJASC(const QString &filepath, const QVector<QRgb> &palette, int offset, int nColors) {
if (!nColors) {
logWarn(QString("Cannot save a palette with no colors."));
return;
logError(QString("Cannot save a palette with no colors."));
return false;
}
if (offset > palette.size() || offset + nColors > palette.size()) {
logWarn("Palette offset out of range for color table.");
return;
logError("Palette offset out of range for color table.");
return false;
}
QString text = "JASC-PAL\r\n0100\r\n";
@@ -59,11 +59,13 @@ void PaletteUtil::writeJASC(QString filepath, QVector<QRgb> palette, int offset,
}
QFile file(filepath);
if (file.open(QIODevice::WriteOnly)) {
file.write(text.toUtf8());
} else {
logWarn(QString("Could not write to file '%1': ").arg(filepath) + file.errorString());
if (!file.open(QIODevice::WriteOnly)) {
logError(QString("Could not write to file '%1': ").arg(filepath) + file.errorString());
return false;
}
file.write(text.toUtf8());
return true;
}
QList<QRgb> parsePal(QString filepath, bool *error) {

View File

@@ -1,5 +1,6 @@
#include "log.h"
#include "parseutil.h"
#include "loadingscreen.h"
#include <QRegularExpression>
#include <QJsonDocument>
@@ -15,26 +16,8 @@ const QRegularExpression ParseUtil::re_poryScriptLabel("\\b(script)(\\((global|l
const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?<label>[\\w_][\\w\\d_]*)");
const QRegularExpression ParseUtil::re_poryRawSection("\\b(raw)\\s*`(?<raw_script>[^`]*)");
static const QMap<QString, int> globalDefineValues = {
{"FALSE", 0},
{"TRUE", 1},
{"SCHAR_MIN", SCHAR_MIN},
{"SCHAR_MAX", SCHAR_MAX},
{"CHAR_MIN", CHAR_MIN},
{"CHAR_MAX", CHAR_MAX},
{"UCHAR_MAX", UCHAR_MAX},
{"SHRT_MIN", SHRT_MIN},
{"SHRT_MAX", SHRT_MAX},
{"USHRT_MAX", USHRT_MAX},
{"INT_MIN", INT_MIN},
{"INT_MAX", INT_MAX},
{"UINT_MAX", UINT_MAX},
};
ParseUtil::ParseUtil() { }
void ParseUtil::set_root(const QString &dir) {
this->root = dir;
ParseUtil::ParseUtil() {
resetCDefines();
}
QString ParseUtil::pathWithRoot(const QString &path) {
@@ -74,6 +57,8 @@ QString ParseUtil::createErrorMessage(const QString &message, const QString &exp
}
QString ParseUtil::readTextFile(const QString &path, QString *error) {
// splash screen message
porysplash->showMessage(path);
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
if (error) *error = file.errorString();
@@ -143,28 +128,48 @@ QList<QStringList> ParseUtil::parseAsm(const QString &filename) {
return parsed;
}
// 'identifier' is the name of the #define to evaluate, e.g. 'FOO' in '#define FOO (BAR+1)'
// 'expression' is the text of the #define to evaluate, e.g. '(BAR+1)' in '#define FOO (BAR+1)'
// 'knownValues' is a pointer to a map of identifier->values for defines that have already been evaluated.
// 'unevaluatedExpressions' is a pointer to a map of identifier->expressions for defines that have not been evaluated. If this map contains any
// identifiers found in 'expression' then this function will be called recursively to evaluate that define first.
// This function will maintain the passed maps appropriately as new #defines are evaluated.
int ParseUtil::evaluateDefine(const QString &identifier, const QString &expression, QMap<QString, int> *knownValues, QMap<QString, QString> *unevaluatedExpressions) {
if (unevaluatedExpressions->contains(identifier))
unevaluatedExpressions->remove(identifier);
// Try to evaluate the given #define/enum 'identifier' name using the information the parser has.
// If it recognizes the name as an identifier it's aware of (either from having parsed it or having been told about
// it using 'loadGlobalCDefines') it will evaluate it if necessary then return the resulting value and set 'ok' to true.
// Evaluated identifiers are cached, and will only be re-evaluated if the parser encounters a new expression for that identifier.
// If it doesn't recognize it, 'ok' will be set to false and it will return 0.
int ParseUtil::evaluateDefine(const QString &identifier, bool *ok) {
if (ok) *ok = true;
if (knownValues->contains(identifier))
return knownValues->value(identifier);
// Global defines take precedence
if (this->globalDefineExpressions.contains(identifier)) {
int value = evaluateExpression(this->globalDefineExpressions.take(identifier));
this->globalDefineValues.insert(identifier, value);
return value;
}
auto it = this->globalDefineValues.constFind(identifier);
if (it != this->globalDefineValues.constEnd()) {
return it.value();
}
QList<Token> tokens = tokenizeExpression(expression, knownValues, unevaluatedExpressions);
QList<Token> postfixExpression = generatePostfix(tokens);
int value = evaluatePostfix(postfixExpression);
// Check known expressions before checking known values.
// If an identifier is redefined then we'll receive a new expression for it, and we want to make sure we re-evaluate it.
if (this->knownDefineExpressions.contains(identifier)) {
int value = evaluateExpression(this->knownDefineExpressions.take(identifier));
this->knownDefineValues.insert(identifier, value);
return value;
}
it = this->knownDefineValues.constFind(identifier);
if (it != this->knownDefineValues.constEnd()) {
return it.value();
}
knownValues->insert(identifier, value);
return value;
if (ok) *ok = false;
return 0;
}
QList<Token> ParseUtil::tokenizeExpression(QString expression, QMap<QString, int> *knownValues, QMap<QString, QString> *unevaluatedExpressions) {
int ParseUtil::evaluateExpression(const QString &expression) {
QList<Token> tokens = tokenizeExpression(expression);
QList<Token> postfixExpression = generatePostfix(tokens);
return evaluatePostfix(postfixExpression);
}
QList<Token> ParseUtil::tokenizeExpression(QString expression) {
QList<Token> tokens;
static const QStringList tokenTypes = {"hex", "decimal", "identifier", "operator", "leftparen", "rightparen"};
@@ -181,14 +186,14 @@ QList<Token> ParseUtil::tokenizeExpression(QString expression, QMap<QString, int
QString token = match.captured(tokenType);
if (!token.isEmpty()) {
if (tokenType == "identifier") {
if (unevaluatedExpressions->contains(token)) {
// This expression depends on a define we know of but haven't evaluated. Evaluate it now
evaluateDefine(token, unevaluatedExpressions->value(token), knownValues, unevaluatedExpressions);
}
if (knownValues->contains(token)) {
bool ok;
int tokenValue = evaluateDefine(token, &ok);
if (ok) {
// Any errors encountered when this identifier was evaluated should be recorded for this expression as well.
recordErrors(this->errorMap.value(token));
QString actualToken = QString("%1").arg(knownValues->value(token));
// Replace token with evaluated expression
QString actualToken = QString::number(tokenValue);
expression = expression.replace(0, token.length(), actualToken);
token = actualToken;
tokenType = "decimal";
@@ -481,23 +486,27 @@ ParseUtil::ParsedDefines ParseUtil::readCDefines(const QString &filename, const
result.filteredNames.append(name);
}
}
// QHash::insert(const QHash<K, V> &other) was introduced in 5.15.
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
this->knownDefineExpressions.insert(result.expressions);
#else
for (auto it = result.expressions.constBegin(); it != result.expressions.constEnd(); it++) {
this->knownDefineExpressions.insert(it.key(), it.value());
}
#endif
return result;
}
// Read all the define names and their expressions in the specified file, then evaluate the ones matching the search text (and any they depend on).
QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error) {
QHash<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error) {
ParsedDefines defines = readCDefines(filename, filterList, useRegex, error);
// Evaluate defines
QMap<QString, int> filteredValues;
QMap<QString, int> allValues = globalDefineValues;
QHash<QString, int> filteredValues;
this->errorMap.clear();
while (!defines.filteredNames.isEmpty()) {
const QString name = defines.filteredNames.takeFirst();
const QString expression = defines.expressions.take(name);
if (expression == " ") continue;
this->curDefine = name;
filteredValues.insert(name, evaluateDefine(name, expression, &allValues, &defines.expressions));
this->curDefine = defines.filteredNames.takeFirst();
filteredValues.insert(this->curDefine, evaluateDefine(this->curDefine));
logRecordedErrors(); // Only log errors for defines that Porymap is looking for
}
@@ -505,12 +514,12 @@ QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QS
}
// Find and evaluate a specific set of defines with known names.
QMap<QString, int> ParseUtil::readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error) {
QHash<QString, int> ParseUtil::readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error) {
return evaluateCDefines(filename, names, false, error);
}
// Find and evaluate an unknown list of defines with a known name pattern.
QMap<QString, int> ParseUtil::readCDefinesByRegex(const QString &filename, const QSet<QString> &regexList, QString *error) {
QHash<QString, int> ParseUtil::readCDefinesByRegex(const QString &filename, const QSet<QString> &regexList, QString *error) {
return evaluateCDefines(filename, regexList, true, error);
}
@@ -521,6 +530,50 @@ QStringList ParseUtil::readCDefineNames(const QString &filename, const QSet<QStr
return readCDefines(filename, regexList, true, error).filteredNames;
}
// Find any defines in the specified file and save their expressions.
// If any of these defines are encountered later by other define parsing functions then they'll be recognized and evaluated.
void ParseUtil::loadGlobalCDefinesFromFile(const QString &filename, QString *error) {
loadGlobalCDefines(readCDefines(filename, {}, false, error).expressions);
}
void ParseUtil::loadGlobalCDefines(const QHash<QString,QString> &defines) {
// QHash::insert(const QHash<K, V> &other) was introduced in 5.15.
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
this->globalDefineExpressions.insert(defines);
#else
for (auto it = defines.constBegin(); it != defines.constEnd(); it++) {
this->globalDefineExpressions.insert(it.key(), it.value());
}
#endif
}
void ParseUtil::loadGlobalCDefines(const QMap<QString,QString> &defines) {
for (auto it = defines.constBegin(); it != defines.constEnd(); it++)
this->globalDefineExpressions.insert(it.key(), it.value());
}
void ParseUtil::resetCDefines() {
static const QHash<QString, int> defaultDefineValues = {
{"FALSE", 0},
{"TRUE", 1},
{"SCHAR_MIN", SCHAR_MIN},
{"SCHAR_MAX", SCHAR_MAX},
{"CHAR_MIN", CHAR_MIN},
{"CHAR_MAX", CHAR_MAX},
{"UCHAR_MAX", UCHAR_MAX},
{"SHRT_MIN", SHRT_MIN},
{"SHRT_MAX", SHRT_MAX},
{"USHRT_MAX", USHRT_MAX},
{"INT_MIN", INT_MIN},
{"INT_MAX", INT_MAX},
{"UINT_MAX", UINT_MAX},
};
this->globalDefineValues = defaultDefineValues;
this->globalDefineExpressions.clear();
this->knownDefineValues.clear();
this->knownDefineExpressions.clear();
}
QStringList ParseUtil::readCArray(const QString &filename, const QString &label) {
QStringList list;
@@ -610,12 +663,12 @@ bool ParseUtil::gameStringToBool(const QString &gameString, bool * ok) {
return gameStringToInt(gameString, ok) != 0;
}
tsl::ordered_map<QString, QHash<QString, QString>> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash<int, QString> &memberMap) {
OrderedMap<QString, QHash<QString, QString>> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash<int, QString> &memberMap) {
QString filePath = pathWithRoot(filename);
auto cParser = fex::Parser();
auto tokens = fex::Lexer().LexFile(filePath);
auto topLevelObjects = cParser.ParseTopLevelObjects(tokens);
tsl::ordered_map<QString, QHash<QString, QString>> structs;
OrderedMap<QString, QHash<QString, QString>> structs;
for (auto it = topLevelObjects.begin(); it != topLevelObjects.end(); it++) {
QString structLabel = QString::fromStdString(it->first);
if (structLabel.isEmpty()) continue;

View File

@@ -260,7 +260,7 @@ void ResizeTilemap::undo() {
///
ClearEntries::ClearEntries(RegionMap *map, QMap<QString, MapSectionEntry> entries, QUndoCommand *parent)
ClearEntries::ClearEntries(RegionMap *map, QHash<QString, MapSectionEntry> entries, QUndoCommand *parent)
: QUndoCommand(parent) {
setText("Clear Entries");

View File

@@ -402,13 +402,13 @@ QHash<int, QString> Tileset::getHeaderMemberMap(bool usingAsm)
return map;
}
void Tileset::loadMetatiles() {
bool Tileset::loadMetatiles() {
clearMetatiles();
QFile metatiles_file(this->metatiles_path);
if (!metatiles_file.open(QIODevice::ReadOnly)) {
logError(QString("Could not open '%1' for reading.").arg(this->metatiles_path));
return;
logError(QString("Could not open '%1' for reading: %2").arg(this->metatiles_path).arg(metatiles_file.errorString()));
return false;
}
QByteArray data = metatiles_file.readAll();
@@ -425,13 +425,14 @@ void Tileset::loadMetatiles() {
}
m_metatiles.append(metatile);
}
return true;
}
void Tileset::saveMetatiles() {
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.").arg(this->metatiles_path));
return;
logError(QString("Could not open '%1' for writing: %2").arg(this->metatiles_path).arg(metatiles_file.errorString()));
return false;
}
QByteArray data;
@@ -444,13 +445,14 @@ void Tileset::saveMetatiles() {
}
}
metatiles_file.write(data);
return true;
}
void Tileset::loadMetatileAttributes() {
bool Tileset::loadMetatileAttributes() {
QFile attrs_file(this->metatile_attrs_path);
if (!attrs_file.open(QIODevice::ReadOnly)) {
logError(QString("Could not open '%1' for reading.").arg(this->metatile_attrs_path));
return;
logError(QString("Could not open '%1' for reading: %2").arg(this->metatile_attrs_path).arg(attrs_file.errorString()));
return false;
}
QByteArray data = attrs_file.readAll();
@@ -467,13 +469,14 @@ void Tileset::loadMetatileAttributes() {
attributes |= static_cast<unsigned char>(data.at(i * attrSize + j)) << (8 * j);
m_metatiles.at(i)->setAttributes(attributes);
}
return true;
}
void Tileset::saveMetatileAttributes() {
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.").arg(this->metatile_attrs_path));
return;
logError(QString("Could not open '%1' for writing: %2").arg(this->metatile_attrs_path).arg(attrs_file.errorString()));
return false;
}
QByteArray data;
@@ -483,9 +486,10 @@ void Tileset::saveMetatileAttributes() {
data.append(static_cast<char>(attributes >> (8 * i)));
}
attrs_file.write(data);
return true;
}
void Tileset::loadTilesImage(QImage *importedImage) {
bool Tileset::loadTilesImage(QImage *importedImage) {
QImage image;
if (importedImage) {
image = *importedImage;
@@ -520,23 +524,25 @@ void Tileset::loadTilesImage(QImage *importedImage) {
}
this->tilesImage = image;
this->tiles = tiles;
return true;
}
void Tileset::saveTilesImage() {
bool Tileset::saveTilesImage() {
// Only write the tiles image if it was changed.
// Porymap will only ever change an existing tiles image by importing a new one.
if (!m_hasUnsavedTilesImage)
return;
return true;
if (!this->tilesImage.save(this->tilesImagePath, "PNG")) {
logError(QString("Failed to save tiles image '%1'").arg(this->tilesImagePath));
return;
return false;
}
m_hasUnsavedTilesImage = false;
return true;
}
void Tileset::loadPalettes() {
bool Tileset::loadPalettes() {
this->palettes.clear();
this->palettePreviews.clear();
@@ -559,26 +565,34 @@ void Tileset::loadPalettes() {
this->palettes.append(palette);
this->palettePreviews.append(palette);
}
return true;
}
void Tileset::savePalettes() {
bool Tileset::savePalettes() {
bool success = true;
int numPalettes = qMin(this->palettePaths.length(), this->palettes.length());
for (int i = 0; i < numPalettes; i++) {
PaletteUtil::writeJASC(this->palettePaths.at(i), this->palettes.at(i).toVector(), 0, 16);
if (!PaletteUtil::writeJASC(this->palettePaths.at(i), this->palettes.at(i).toVector(), 0, 16))
success = false;
}
return success;
}
void Tileset::load() {
loadMetatiles();
loadMetatileAttributes();
loadTilesImage();
loadPalettes();
bool Tileset::load() {
bool success = true;
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.
void Tileset::save() {
saveMetatiles();
saveMetatileAttributes();
saveTilesImage();
savePalettes();
bool Tileset::save() {
bool success = true;
if (!saveMetatiles()) success = false;
if (!saveMetatileAttributes()) success = false;
if (!saveTilesImage()) success = false;
if (!savePalettes()) success = false;
return success;
}

View File

@@ -42,3 +42,14 @@ QString Util::toDefineCase(QString input) {
QString Util::toHexString(uint32_t value, int minLength) {
return "0x" + QString("%1").arg(value, minLength, 16, QChar('0')).toUpper();
}
QString Util::toHtmlParagraph(const QString &text) {
return QString("<html><head/><body><p>%1</p></body></html>").arg(text);
}
Qt::Orientations Util::getOrientation(bool xflip, bool yflip) {
Qt::Orientations flags;
if (xflip) flags |= Qt::Orientation::Horizontal;
if (yflip) flags |= Qt::Orientation::Vertical;
return flags;
}

View File

@@ -1,5 +1,5 @@
#include "editor.h"
#include "draggablepixmapitem.h"
#include "eventpixmapitem.h"
#include "imageproviders.h"
#include "log.h"
#include "connectionslistitem.h"
@@ -30,7 +30,6 @@ Editor::Editor(Ui::MainWindow* ui)
{
this->ui = ui;
this->settings = new Settings();
this->playerViewRect = new MovableRect(&this->settings->playerViewRectEnabled, 30 * 8, 20 * 8, qRgb(255, 255, 255));
this->cursorMapTileRect = new CursorTileRect(&this->settings->cursorTileRectEnabled, qRgb(255, 255, 255));
this->map_ruler = new MapRuler(4);
connect(this->map_ruler, &MapRuler::statusChanged, this, &Editor::mapRulerStatusChanged);
@@ -54,6 +53,19 @@ Editor::Editor(Ui::MainWindow* ui)
connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this, &Editor::openProjectInTextEditor);
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::toggleGrid);
connect(ui->mapCustomAttributesFrame->table(), &CustomAttributesTable::edited, this, &Editor::updateCustomMapAttributes);
connect(ui->comboBox_DiveMap, &NoScrollComboBox::editingFinished, [this] {
onDivingMapEditingFinished(this->ui->comboBox_DiveMap, "dive");
});
connect(ui->comboBox_EmergeMap, &NoScrollComboBox::editingFinished, [this] {
onDivingMapEditingFinished(this->ui->comboBox_EmergeMap, "emerge");
});
connect(ui->comboBox_DiveMap, &NoScrollComboBox::currentTextChanged, [this] {
updateDivingMapButton(this->ui->button_OpenDiveMap, this->ui->comboBox_DiveMap->currentText());
});
connect(ui->comboBox_EmergeMap, &NoScrollComboBox::currentTextChanged, [this] {
updateDivingMapButton(this->ui->button_OpenEmergeMap, this->ui->comboBox_EmergeMap->currentText());
});
}
Editor::~Editor()
@@ -68,30 +80,33 @@ Editor::~Editor()
closeProject();
}
void Editor::saveCurrent() {
save(true);
bool Editor::saveCurrent() {
return save(true);
}
void Editor::saveAll() {
save(false);
bool Editor::saveAll() {
return save(false);
}
void Editor::save(bool currentOnly) {
bool Editor::save(bool currentOnly) {
if (!this->project)
return;
return true;
saveEncounterTabData();
bool success = true;
if (currentOnly) {
if (this->map) {
this->project->saveMap(this->map);
success = this->project->saveMap(this->map);
} else if (this->layout) {
this->project->saveLayout(this->layout);
success = this->project->saveLayout(this->layout);
}
this->project->saveGlobalData();
if (!this->project->saveGlobalData())
success = false;
} else {
this->project->saveAll();
success = this->project->saveAll();
}
return success;
}
void Editor::setProject(Project * project) {
@@ -155,6 +170,7 @@ void Editor::setEditMode(EditMode editMode) {
}
this->cursorMapTileRect->setSingleTileMode();
this->cursorMapTileRect->setActive(editingLayout);
this->playerViewRect->setActive(editingLayout);
this->editGroup.setActiveStack(editStack);
setMapEditingButtonsEnabled(editingLayout);
@@ -296,8 +312,8 @@ void Editor::addNewWildMonGroup(QWidget *window) {
form.addRow(new QLabel(monField.name), fieldCheckbox);
}
// Reading from ui here so not saving to disk before user.
connect(copyCheckbox, &QCheckBox::stateChanged, [=](int state){
if (state == Qt::Checked) {
connect(copyCheckbox, &QCheckBox::toggled, [=](bool checked){
if (checked) {
int fieldIndex = 0;
MonTabWidget *monWidget = static_cast<MonTabWidget *>(stack->widget(stack->currentIndex()));
for (EncounterField monField : project->wildMonFields) {
@@ -305,7 +321,7 @@ void Editor::addNewWildMonGroup(QWidget *window) {
fieldCheckboxes[fieldIndex]->setEnabled(false);
fieldIndex++;
}
} else if (state == Qt::Unchecked) {
} else {
int fieldIndex = 0;
for (EncounterField monField : project->wildMonFields) {
fieldCheckboxes[fieldIndex]->setEnabled(true);
@@ -596,7 +612,7 @@ void Editor::configureEncounterJSON(QWidget *window) {
if (newNameDialog.exec() == QDialog::Accepted) {
QString newFieldName = newNameEdit->text();
QVector<int> newFieldRates(1, 100);
tempFields.append({newFieldName, newFieldRates, {}});
tempFields.append({newFieldName, newFieldRates, {}, {}});
fieldChoices->addItem(newFieldName);
fieldChoices->setCurrentIndex(fieldChoices->count() - 1);
}
@@ -675,7 +691,7 @@ void Editor::saveEncounterTabData() {
if (!stack->count()) return;
tsl::ordered_map<QString, WildPokemonHeader> &encounterMap = project->wildMonData[map->constantName()];
OrderedMap<QString, WildPokemonHeader> &encounterMap = project->wildMonData[map->constantName()];
for (int groupIndex = 0; groupIndex < stack->count(); groupIndex++) {
MonTabWidget *tabWidget = static_cast<MonTabWidget *>(stack->widget(groupIndex));
@@ -805,23 +821,41 @@ void Editor::displayConnection(MapConnection *connection) {
}
}
void Editor::addConnection(MapConnection *connection) {
if (!connection)
void Editor::addNewConnection(const QString &mapName, const QString &direction) {
if (!this->map)
return;
MapConnection *connection = new MapConnection(mapName, direction);
// Mark this connection to be selected once its display elements have been created.
// It's possible this is a Dive/Emerge connection, but that's ok (no selection will occur).
connection_to_select = connection;
this->connection_to_select = connection;
this->map->commit(new MapConnectionAdd(this->map, connection));
}
void Editor::replaceConnection(const QString &mapName, const QString &direction) {
if (!this->map)
return;
MapConnection *connection = this->map->getConnection(direction);
if (!connection || connection->targetMapName() == mapName)
return;
this->map->commit(new MapConnectionChangeMap(connection, mapName));
}
void Editor::removeConnection(MapConnection *connection) {
if (!connection)
if (!this->map || !connection)
return;
this->map->commit(new MapConnectionRemove(this->map, connection));
}
void Editor::removeSelectedConnection() {
if (selected_connection_item)
removeConnection(selected_connection_item->connection);
}
void Editor::removeConnectionPixmap(MapConnection *connection) {
if (!connection)
return;
@@ -914,21 +948,18 @@ void Editor::removeDivingMapPixmap(MapConnection *connection) {
updateDivingMapsVisibility();
}
void Editor::updateDiveMap(QString mapName) {
setDivingMapName(mapName, "dive");
}
bool Editor::setDivingMapName(const QString &mapName, const QString &direction) {
if (!mapName.isEmpty() && !this->project->mapNames.contains(mapName))
return false;
if (!MapConnection::isDiving(direction))
return false;
void Editor::updateEmergeMap(QString mapName) {
setDivingMapName(mapName, "emerge");
}
void Editor::setDivingMapName(QString mapName, QString direction) {
auto pixmapItem = diving_map_items.value(direction);
MapConnection *connection = pixmapItem ? pixmapItem->connection() : nullptr;
if (connection) {
if (mapName == connection->targetMapName())
return; // No change
return true; // No change
// Update existing connection
if (mapName.isEmpty()) {
@@ -938,8 +969,25 @@ void Editor::setDivingMapName(QString mapName, QString direction) {
}
} else if (!mapName.isEmpty()) {
// Create new connection
addConnection(new MapConnection(mapName, direction));
addNewConnection(mapName, direction);
}
return true;
}
QString Editor::getDivingMapName(const QString &direction) const {
auto pixmapItem = diving_map_items.value(direction);
return (pixmapItem && pixmapItem->connection()) ? pixmapItem->connection()->targetMapName() : QString();
}
void Editor::onDivingMapEditingFinished(NoScrollComboBox *combo, const QString &direction) {
if (!setDivingMapName(combo->currentText(), direction)) {
// If user input was invalid, restore the combo to the previously-valid text.
combo->setCurrentText(getDivingMapName(direction));
}
}
void Editor::updateDivingMapButton(QToolButton* button, const QString &mapName) {
if (this->project) button->setDisabled(!this->project->mapNames.contains(mapName));
}
void Editor::updateDivingMapsVisibility() {
@@ -1061,6 +1109,14 @@ void Editor::scaleMapView(int s) {
ui->graphicsView_Connections->setTransform(transform);
}
void Editor::setPlayerViewRect(const QRectF &rect) {
delete this->playerViewRect;
this->playerViewRect = new MovableRect(&this->settings->playerViewRectEnabled, rect, qRgb(255, 255, 255));
this->playerViewRect->setActive(getEditingLayout());
if (ui->graphicsView_Map->scene())
ui->graphicsView_Map->scene()->update();
}
void Editor::updateCursorRectPos(int x, int y) {
if (this->playerViewRect)
this->playerViewRect->updateLocation(x, y);
@@ -1289,7 +1345,6 @@ void Editor::setStraightPathCursorMode(QGraphicsSceneMouseEvent *event) {
}
void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item) {
// TODO: add event tab event painting tool buttons stuff here
if (!item->getEditsEnabled()) {
return;
}
@@ -1363,8 +1418,11 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *i
if (event && event->getPixmapItem())
event->getPixmapItem()->moveTo(pos);
}
} else if (eventEditAction == EditAction::Select) {
// do nothing here, at least for now
} else if (eventEditAction == EditAction::Select && event->type() == QEvent::GraphicsSceneMousePress) {
if (!(event->modifiers() & Qt::ControlModifier) && this->selectedEvents.length() > 1) {
// User is clearing group selection by clicking on the background
selectMapEvent(this->selectedEvents.first());
}
} else if (eventEditAction == EditAction::Shift) {
static QPoint selection_origin;
@@ -1482,7 +1540,7 @@ bool Editor::displayLayout() {
scene->installEventFilter(filter);
connect(filter, &MapSceneEventFilter::wheelZoom, this, &Editor::onWheelZoom);
scene->installEventFilter(this->map_ruler);
this->map_ruler->setZValue(1000);
this->map_ruler->setZValue(ZValue::Ruler);
scene->addItem(this->map_ruler);
}
@@ -1560,14 +1618,8 @@ void Editor::displayMapMetatiles() {
map_item->draw(true);
scene->addItem(map_item);
int tw = 16;
int th = 16;
scene->setSceneRect(
-BORDER_DISTANCE * tw,
-BORDER_DISTANCE * th,
map_item->pixmap().width() + BORDER_DISTANCE * 2 * tw,
map_item->pixmap().height() + BORDER_DISTANCE * 2 * th
);
// Scene rect is the map plus a margin that gives enough space to scroll and see the edge of the player view rectangle.
scene->setSceneRect(this->layout->getVisibleRect() + QMargins(3,3,3,3));
}
void Editor::clearMapMovementPermissions() {
@@ -1673,9 +1725,6 @@ void Editor::clearMapEvents() {
if (events_group->scene()) {
events_group->scene()->removeItem(events_group);
}
// events_group does not own its children, the childrens' parent
// is set to the group's parent (and our group has no parent).
qDeleteAll(events_group->childItems());
delete events_group;
events_group = nullptr;
}
@@ -1695,9 +1744,16 @@ void Editor::displayMapEvents() {
events_group->setHandlesChildEvents(false);
}
DraggablePixmapItem *Editor::addEventPixmapItem(Event *event) {
EventPixmapItem *Editor::addEventPixmapItem(Event *event) {
this->project->loadEventPixmap(event);
auto item = new DraggablePixmapItem(event, this);
auto item = new EventPixmapItem(event);
connect(item, &EventPixmapItem::doubleClicked, this, &Editor::openEventMap);
connect(item, &EventPixmapItem::dragged, this, &Editor::onEventDragged);
connect(item, &EventPixmapItem::released, this, &Editor::onEventReleased);
connect(item, &EventPixmapItem::selected, this, &Editor::selectMapEvent);
connect(item, &EventPixmapItem::posChanged, [this, event] { updateWarpEventWarning(event); });
connect(item, &EventPixmapItem::yChanged, [this, item] { updateEventPixmapItemZValue(item); });
updateWarpEventWarning(event);
redrawEventPixmapItem(item);
this->events_group->addToGroup(item);
return item;
@@ -1722,8 +1778,6 @@ void Editor::clearMapConnections() {
}
connection_items.clear();
const QSignalBlocker blocker1(ui->comboBox_DiveMap);
const QSignalBlocker blocker2(ui->comboBox_EmergeMap);
ui->comboBox_DiveMap->setCurrentText("");
ui->comboBox_EmergeMap->setCurrentText("");
@@ -1760,24 +1814,20 @@ void Editor::clearConnectionMask() {
}
}
// Hides connected map tiles that cannot be seen from the current map (beyond BORDER_DISTANCE).
// Hides connected map tiles that cannot be seen from the current map
void Editor::maskNonVisibleConnectionTiles() {
clearConnectionMask();
QPainterPath mask;
mask.addRect(scene->itemsBoundingRect().toRect());
mask.addRect(
-BORDER_DISTANCE * 16,
-BORDER_DISTANCE * 16,
(layout->getWidth() + BORDER_DISTANCE * 2) * 16,
(layout->getHeight() + BORDER_DISTANCE * 2) * 16
);
mask.addRect(layout->getVisibleRect());
// Mask the tiles with the current theme's background color.
QPen pen(ui->graphicsView_Map->palette().color(QPalette::Active, QPalette::Base));
QBrush brush(ui->graphicsView_Map->palette().color(QPalette::Active, QPalette::Base));
connection_mask = scene->addPath(mask, pen, brush);
connection_mask->setZValue(ZValue::MapConnectionMask);
}
void Editor::clearMapBorder() {
@@ -1793,17 +1843,14 @@ void Editor::clearMapBorder() {
void Editor::displayMapBorder() {
clearMapBorder();
int borderWidth = this->layout->getBorderWidth();
int borderHeight = this->layout->getBorderHeight();
int borderHorzDist = this->layout->getBorderDrawWidth();
int borderVertDist = this->layout->getBorderDrawHeight();
QPixmap pixmap = this->layout->renderBorder();
for (int y = -borderVertDist; y < this->layout->getHeight() + borderVertDist; y += borderHeight)
for (int x = -borderHorzDist; x < this->layout->getWidth() + borderHorzDist; x += borderWidth) {
const QMargins borderMargins = layout->getBorderMargins();
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->setZValue(-3);
item->setZValue(ZValue::MapBorder);
scene->addItem(item);
borderItems.append(item);
}
@@ -1973,32 +2020,59 @@ qreal Editor::getEventOpacity(const Event *event) const {
return event->getUsesDefaultPixmap() ? 0.7 : 1.0;
}
void Editor::redrawEventPixmapItem(DraggablePixmapItem *item) {
if (item && item->event && !item->event->getPixmap().isNull()) {
item->setOpacity(getEventOpacity(item->event));
project->loadEventPixmap(item->event, true);
item->setPixmap(item->event->getPixmap());
item->setShapeMode(porymapConfig.eventSelectionShapeMode);
void Editor::redrawEventPixmapItem(EventPixmapItem *item) {
if (!item) return;
Event *event = item->getEvent();
if (!event) return;
if (this->editMode == EditMode::Events) {
if (this->selectedEvents.contains(item->event)) {
// Draw the selection rectangle
QImage image = item->pixmap().toImage();
QPainter painter(&image);
painter.setPen(QColor(255, 0, 255));
painter.drawRect(0, 0, image.width() - 1, image.height() - 1);
painter.end();
item->setPixmap(QPixmap::fromImage(image));
}
item->setAcceptedMouseButtons(Qt::AllButtons);
} else {
// Can't interact with event pixmaps outside of event editing mode.
// We could do setEnabled(false), but rather than ignoring the mouse events this
// would reject them, which would prevent painting on the map behind the events.
item->setAcceptedMouseButtons(Qt::NoButton);
}
item->updatePosition();
if (this->editMode == EditMode::Events) {
item->setAcceptedMouseButtons(Qt::AllButtons);
item->setSelected(this->selectedEvents.contains(event));
} else {
// Can't interact with event pixmaps outside of event editing mode.
// We could do setEnabled(false), but rather than ignoring the mouse events this
// would reject them, which would prevent painting on the map behind the events.
item->setAcceptedMouseButtons(Qt::NoButton);
item->setSelected(false);
}
updateEventPixmapItemZValue(item);
item->setOpacity(getEventOpacity(event));
item->setShapeMode(porymapConfig.eventSelectionShapeMode);
item->render(project);
}
void Editor::updateEventPixmapItemZValue(EventPixmapItem *item) {
if (!item) return;
Event *event = item->getEvent();
if (!event) return;
if (item->isSelected()) {
item->setZValue(ZValue::EventMaximum);
} else {
item->setZValue(event->getY() + ((ZValue::EventMaximum - ZValue::EventMinimum) / 2));
}
}
void Editor::onEventDragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition) {
if (!this->map || !this->map_item)
return;
this->map_item->hoveredMapMetatileChanged(newPosition);
// Drag all the other selected events (if any) with it
QList<Event*> draggedEvents;
if (this->selectedEvents.contains(event)) {
draggedEvents = this->selectedEvents;
} else {
draggedEvents.append(event);
}
QPoint moveDistance = newPosition - oldPosition;
this->map->commit(new EventMove(draggedEvents, moveDistance.x(), moveDistance.y(), this->eventMoveActionId));
}
void Editor::onEventReleased(Event *, const QPoint &) {
this->eventMoveActionId++;
}
// Warp events display a warning if they're not positioned on a metatile with a warp behavior.
@@ -2283,32 +2357,6 @@ bool Editor::startDetachedProcess(const QString &command, const QString &working
return process.startDetached(pid);
}
// It doesn't seem to be possible to prevent the mousePress event
// from triggering both event's DraggablePixmapItem and the background mousePress.
// Since the DraggablePixmapItem's event fires first, we can set a temp
// variable "selectingEvent" so that we can detect whether or not the user
// is clicking on the background instead of an event.
void Editor::eventsView_onMousePress(QMouseEvent *event) {
// make sure we are in event editing mode
if (map_item && this->editMode != EditMode::Events) {
return;
}
if (this->eventEditAction == EditAction::Paint && event->buttons() & Qt::RightButton) {
this->eventEditAction = EditAction::Select;
this->settings->mapCursor = QCursor();
this->cursorMapTileRect->setSingleTileMode();
this->ui->toolButton_Paint->setChecked(false);
this->ui->toolButton_Select->setChecked(true);
}
bool multiSelect = event->modifiers() & Qt::ControlModifier;
if (!selectingEvent && !multiSelect && this->selectedEvents.length() > 1) {
// User is clearing group selection by clicking on the background
this->selectMapEvent(this->selectedEvents.first());
}
selectingEvent = false;
}
void Editor::setCollisionTabSpinBoxes(uint16_t collision, uint16_t elevation) {
const QSignalBlocker blocker1(ui->spinBox_SelectedCollision);
const QSignalBlocker blocker2(ui->spinBox_SelectedElevation);
@@ -2338,8 +2386,8 @@ void Editor::setCollisionGraphics() {
// Users are not required to provide an image that gives an icon for every elevation/collision combination.
// Instead they tell us how many are provided in their image by specifying the number of columns and rows.
const int imgColumns = projectConfig.collisionSheetWidth;
const int imgRows = projectConfig.collisionSheetHeight;
const int imgColumns = projectConfig.collisionSheetSize.width();
const int imgRows = projectConfig.collisionSheetSize.height();
// Create a pixmap for the selector on the Collision tab. If a project was previously opened we'll also need to refresh the selector.
this->collisionSheetPixmap = QPixmap::fromImage(imgSheet).scaled(MovementPermissionsSelector::CellWidth * imgColumns,

View File

@@ -311,33 +311,26 @@ const Json & JsonArray::operator[] (int i) const {
else return m_value[i];
}
const Json Json::fromQJsonValue(QJsonValue value) {
Json Json::fromQJsonValue(const QJsonValue &value) {
switch (value.type())
{
case QJsonValue::String: return value.toString();
case QJsonValue::Double: return value.toInt();
case QJsonValue::Bool: return value.toBool();
case QJsonValue::Array:
{
QJsonArray qArr = value.toArray();
Json::array arr;
for (const auto &i: qArr)
arr.push_back(Json::fromQJsonValue(i));
return arr;
case QJsonValue::Array: {
Json::array array;
Json::append(&array, value.toArray());
return array;
}
case QJsonValue::Object:
{
QJsonObject qObj = value.toObject();
Json::object obj;
for (auto it = qObj.constBegin(); it != qObj.constEnd(); it++)
obj[it.key()] = Json::fromQJsonValue(it.value());
return obj;
case QJsonValue::Object: {
Json::object object;
Json::append(&object, value.toObject());
return object;
}
default: return static_null();
}
}
/* * * * * * * * * * * * * * * * * * * *
* Comparison
*/

View File

@@ -1,4 +1,6 @@
#include "mainwindow.h"
#include "loadingscreen.h"
#include <QApplication>
int main(int argc, char *argv[])
@@ -6,8 +8,13 @@ int main(int argc, char *argv[])
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
QApplication a(argc, argv);
a.setStyle("fusion");
porysplash = new PorymapLoadingScreen;
QObject::connect(&a, &QCoreApplication::aboutToQuit, [=]() { delete porysplash; });
MainWindow w(nullptr);
w.show();
w.initialize();
return a.exec();
}

View File

@@ -10,7 +10,7 @@
#include "customattributesframe.h"
#include "scripting.h"
#include "adjustingstackedwidget.h"
#include "draggablepixmapitem.h"
#include "eventpixmapitem.h"
#include "editcommands.h"
#include "flowlayout.h"
#include "shortcut.h"
@@ -29,6 +29,7 @@
#include "newmapgroupdialog.h"
#include "newlocationdialog.h"
#include "message.h"
#include "loadingscreen.h"
#include <QClipboard>
#include <QDirIterator>
@@ -75,10 +76,13 @@ MainWindow::MainWindow(QWidget *parent) :
cleanupLargeLog();
logInfo(QString("Launching Porymap v%1").arg(QCoreApplication::applicationVersion()));
}
void MainWindow::initialize() {
this->initWindow();
if (porymapConfig.reopenOnLaunch && !porymapConfig.projectManuallyClosed && this->openProject(porymapConfig.getRecentProject(), true))
if (porymapConfig.reopenOnLaunch && !porymapConfig.projectManuallyClosed && this->openProject(porymapConfig.getRecentProject(), true)) {
on_toolButton_Paint_clicked();
}
// there is a bug affecting macOS users, where the trackpad deilveres a bad touch-release gesture
// the warning is a bit annoying, so it is disabled here
@@ -86,6 +90,9 @@ MainWindow::MainWindow(QWidget *parent) :
if (porymapConfig.checkForUpdates)
this->checkForUpdates(false);
this->restoreWindowState();
this->show();
}
MainWindow::~MainWindow()
@@ -142,7 +149,6 @@ void MainWindow::initWindow() {
this->initMiscHeapObjects();
this->initMapList();
this->initShortcuts();
this->restoreWindowState();
#ifndef RELEASE_PLATFORM
ui->actionCheck_for_Updates->setVisible(false);
@@ -153,7 +159,6 @@ void MainWindow::initWindow() {
#endif
setWindowDisabled(true);
show();
}
void MainWindow::initShortcuts() {
@@ -261,6 +266,10 @@ void MainWindow::initCustomUI() {
// Create map header data widget
this->mapHeaderForm = new MapHeaderForm();
ui->layout_HeaderData->addWidget(this->mapHeaderForm);
// Center zooming on the mouse
ui->graphicsView_Map->setTransformationAnchor(QGraphicsView::ViewportAnchor::AnchorUnderMouse);
ui->graphicsView_Map->setResizeAnchor(QGraphicsView::ViewportAnchor::AnchorUnderMouse);
}
void MainWindow::initExtraSignals() {
@@ -299,6 +308,9 @@ void MainWindow::initExtraSignals() {
connect(ui->action_NewLayout, &QAction::triggered, this, &MainWindow::openNewLayoutDialog);
connect(ui->actionDuplicate_Current_Map_Layout, &QAction::triggered, this, &MainWindow::openDuplicateMapOrLayoutDialog);
connect(ui->comboBox_LayoutSelector->lineEdit(), &QLineEdit::editingFinished, this, &MainWindow::onLayoutSelectorEditingFinished);
connect(ui->checkBox_smartPaths, &QCheckBox::toggled, this, &MainWindow::setSmartPathsEnabled);
connect(ui->checkBox_ToggleBorder, &QCheckBox::toggled, this, &MainWindow::setBorderVisibility);
connect(ui->checkBox_MirrorConnections, &QCheckBox::toggled, this, &MainWindow::setMirrorConnectionsEnabled);
}
void MainWindow::on_actionCheck_for_Updates_triggered() {
@@ -338,13 +350,14 @@ void MainWindow::initEditor() {
this->editor = new Editor(ui);
connect(this->editor, &Editor::eventsChanged, this, &MainWindow::updateEvents);
connect(this->editor, &Editor::openConnectedMap, this, &MainWindow::onOpenConnectedMap);
connect(this->editor, &Editor::warpEventDoubleClicked, this, &MainWindow::openWarpMap);
connect(this->editor, &Editor::openEventMap, this, &MainWindow::openEventMap);
connect(this->editor, &Editor::currentMetatilesSelectionChanged, this, &MainWindow::currentMetatilesSelectionChanged);
connect(this->editor, &Editor::wildMonTableEdited, this, &MainWindow::markMapEdited);
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(ui->newEventToolButton, &NewEventToolButton::newEventAdded, this->editor, &Editor::addNewEvent);
connect(ui->toolButton_deleteEvent, &QAbstractButton::clicked, this->editor, &Editor::deleteSelectedEvents);
connect(ui->graphicsView_Connections, &ConnectionsView::pressedDelete, this->editor, &Editor::removeSelectedConnection);
this->loadUserSettings();
@@ -520,11 +533,7 @@ void MainWindow::updateWindowTitle() {
}
}
void MainWindow::markMapEdited() {
if (editor) markSpecificMapEdited(editor->map);
}
void MainWindow::markSpecificMapEdited(Map* map) {
void MainWindow::markMapEdited(Map* map) {
if (!map)
return;
map->setHasUnsavedDataChanges(true);
@@ -584,6 +593,8 @@ void MainWindow::loadUserSettings() {
ui->checkBox_ToggleBorder->setChecked(porymapConfig.showBorder);
ui->actionShow_Events_In_Map_View->setChecked(porymapConfig.eventOverlayEnabled);
this->editor->gridSettings = porymapConfig.gridSettings;
setTheme(porymapConfig.theme);
setDivingMapsVisible(porymapConfig.showDiveEmergeMaps);
}
@@ -651,11 +662,13 @@ bool MainWindow::openProject(QString dir, bool initial) {
projectConfig.projectDir = dir;
projectConfig.load();
porysplash->start();
Scripting::init(this);
// Create the project
auto project = new Project(editor);
project->set_root(dir);
project->setRoot(dir);
connect(project, &Project::fileChanged, this, &MainWindow::showFileWatcherWarning);
connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded);
connect(project, &Project::mapCreated, this, &MainWindow::onNewMapCreated);
@@ -670,6 +683,7 @@ bool MainWindow::openProject(QString dir, bool initial) {
// Make sure project looks reasonable before attempting to load it
if (isInvalidProject(this->editor->project)) {
delete this->editor->project;
porysplash->stop();
return false;
}
@@ -679,6 +693,7 @@ bool MainWindow::openProject(QString dir, bool initial) {
showProjectOpenFailure();
delete this->editor->project;
// TODO: Allow changing project settings at this point
porysplash->stop();
return false;
}
@@ -699,6 +714,7 @@ bool MainWindow::openProject(QString dir, bool initial) {
editor->layout);
Scripting::cb_ProjectOpened(dir);
setWindowDisabled(false);
porysplash->stop();
return true;
}
@@ -712,7 +728,7 @@ bool MainWindow::isInvalidProject(Project *project) {
if (!project->sanityCheck()) {
logWarn(QString("The directory '%1' failed the project sanity check.").arg(project->root));
ErrorMessage msgBox(QStringLiteral("The selected directory appears to be invalid."), this);
ErrorMessage msgBox(QStringLiteral("The selected directory appears to be invalid."), porysplash);
msgBox.setInformativeText(QString("The directory '%1' is missing key files.\n\n"
"Make sure you selected the correct project directory "
"(the one used to make your .gba file, e.g. 'pokeemerald').").arg(project->root));
@@ -764,14 +780,15 @@ void MainWindow::showProjectOpenFailure() {
// Alert the user that one or more maps have been excluded while loading the project.
void MainWindow::showMapsExcludedAlert(const QStringList &excludedMapNames) {
RecentErrorMessage msgBox("", this);
auto msgBox = new RecentErrorMessage("", this);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
if (excludedMapNames.length() == 1) {
msgBox.setText(QString("Failed to load map '%1'. Saving will exclude this map from your project.").arg(excludedMapNames.first()));
msgBox->setText(QString("Failed to load map '%1'. Saving will exclude this map from your project.").arg(excludedMapNames.first()));
} else {
msgBox.setText(QStringLiteral("Failed to load the maps listed below. Saving will exclude these maps from your project."));
msgBox.setDetailedText(excludedMapNames.join("\n")); // Overwrites error details text, user will need to check the log.
msgBox->setText(QStringLiteral("Failed to load the maps listed below. Saving will exclude these maps from your project."));
msgBox->setDetailedText(excludedMapNames.join("\n")); // Overwrites error details text, user will need to check the log.
}
msgBox.exec();
msgBox->open();
}
bool MainWindow::isProjectOpen() {
@@ -881,28 +898,28 @@ void MainWindow::showFileWatcherWarning() {
path.remove(root);
}
QuestionMessage msgBox("", this);
QPointer msgBox = new QuestionMessage("", this);
if (modifiedFiles.count() == 1) {
msgBox.setText(QString("The file %1 has changed on disk. Would you like to reload the project?").arg(modifiedFiles.first()));
msgBox->setText(QString("The file %1 has changed on disk. Would you like to reload the project?").arg(modifiedFiles.first()));
} else {
msgBox.setText(QStringLiteral("Some project files have changed on disk. Would you like to reload the project?"));
msgBox.setDetailedText(QStringLiteral("The following files have changed:\n") + modifiedFiles.join("\n"));
msgBox->setText(QStringLiteral("Some project files have changed on disk. Would you like to reload the project?"));
msgBox->setDetailedText(QStringLiteral("The following files have changed:\n") + modifiedFiles.join("\n"));
}
msgBox->setCheckBox(new QCheckBox("Do not ask again."));
QCheckBox showAgainCheck("Do not ask again.");
msgBox.setCheckBox(&showAgainCheck);
auto reply = msgBox.exec();
if (reply == QMessageBox::Yes) {
on_action_Reload_Project_triggered();
} else if (reply == QMessageBox::No) {
if (showAgainCheck.isChecked()) {
porymapConfig.monitorFiles = false;
if (this->preferenceEditor)
this->preferenceEditor->updateFields();
connect(msgBox, &QuestionMessage::accepted, this, &MainWindow::on_action_Reload_Project_triggered);
connect(msgBox, &QuestionMessage::finished, [this, msgBox] {
if (msgBox) {
if (msgBox->checkBox() && msgBox->checkBox()->isChecked()) {
porymapConfig.monitorFiles = false;
if (this->preferenceEditor)
this->preferenceEditor->updateFields();
}
msgBox->deleteLater();
}
}
showing = false;
showing = false;
});
msgBox->open();
}
QString MainWindow::getExistingDirectory(QString dir) {
@@ -917,7 +934,8 @@ void MainWindow::on_action_Open_Project_triggered()
}
void MainWindow::on_action_Reload_Project_triggered() {
openProject(editor->project->root);
if (this->editor && this->editor->project)
openProject(this->editor->project->root);
}
void MainWindow::on_action_Close_Project_triggered() {
@@ -942,9 +960,10 @@ bool MainWindow::userSetMap(QString map_name) {
}
if (map_name == editor->project->getDynamicMapName()) {
WarningMessage msgBox(QString("Cannot open map '%1'.").arg(map_name), this);
msgBox.setInformativeText(QStringLiteral("This map name is a placeholder to indicate that the warp's map will be set programmatically."));
msgBox.exec();
auto msgBox = new WarningMessage(QString("Cannot open map '%1'.").arg(map_name), this);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setInformativeText(QStringLiteral("This map name is a placeholder to indicate that the warp's map will be set programmatically."));
msgBox->open();
return false;
}
@@ -976,8 +995,6 @@ bool MainWindow::setMap(QString map_name) {
updateMapList();
resetMapListFilters();
connect(editor->map, &Map::modified, this, &MainWindow::markMapEdited, Qt::UniqueConnection);
// If the map's MAPSEC / layout changes, update the map's position in the map list.
// These are doing more work than necessary, rather than rebuilding the entire list they should find and relocate the appropriate row.
connect(editor->map, &Map::layoutChanged, this, &MainWindow::rebuildMapList_Layouts, Qt::UniqueConnection);
@@ -1090,20 +1107,57 @@ void MainWindow::refreshCollisionSelector() {
on_horizontalSlider_CollisionZoom_valueChanged(ui->horizontalSlider_CollisionZoom->value());
}
void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_group) {
// Open the destination map.
if (!userSetMap(map_name))
// Some events (like warps) have data that refers to an event on a different map.
// This function opens that map, and selects the event it's referring to.
void MainWindow::openEventMap(Event *sourceEvent) {
if (!sourceEvent || !this->editor->map) return;
QString targetMapName;
QString targetEventIdName;
Event::Group targetEventGroup;
Event::Type eventType = sourceEvent->getEventType();
if (eventType == Event::Type::Warp) {
// Warp events open to their destination warp event.
WarpEvent *warp = dynamic_cast<WarpEvent *>(sourceEvent);
targetMapName = warp->getDestinationMap();
targetEventIdName = warp->getDestinationWarpID();
targetEventGroup = Event::Group::Warp;
} else if (eventType == Event::Type::CloneObject) {
// Clone object events open to their target object event.
CloneObjectEvent *clone = dynamic_cast<CloneObjectEvent *>(sourceEvent);
targetMapName = clone->getTargetMap();
targetEventIdName = clone->getTargetID();
targetEventGroup = Event::Group::Object;
} else if (eventType == Event::Type::SecretBase) {
// Secret Bases open to their secret base entrance
const QString mapPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
SecretBaseEvent *base = dynamic_cast<SecretBaseEvent *>(sourceEvent);
// Extract the map name from the secret base ID.
QString baseId = base->getBaseID();
targetMapName = this->editor->project->mapConstantsToMapNames.value(mapPrefix + baseId.left(baseId.lastIndexOf("_")));
// Just select the first warp. Normally the only warp event on every secret base map is the entrance/exit, so this is usually correct.
// The warp IDs for secret bases are specified in the project's C code, not in the map data, so we don't have an easy way to read the actual IDs.
targetEventIdName = "0";
targetEventGroup = Event::Group::Warp;
} else if (eventType == Event::Type::HealLocation && projectConfig.healLocationRespawnDataEnabled) {
// Heal location events open to their respawn NPC
HealLocationEvent *heal = dynamic_cast<HealLocationEvent *>(sourceEvent);
targetMapName = heal->getRespawnMapName();
targetEventIdName = heal->getRespawnNPC();
targetEventGroup = Event::Group::Object;
} else {
// Other event types have no target map to open.
return;
}
if (!userSetMap(targetMapName))
return;
// Select the target event.
int index = event_id - Event::getIndexOffset(event_group);
Event* event = this->editor->map->getEvent(event_group, index);
if (event) {
this->editor->selectMapEvent(event);
} else {
// Can still warp to this map, but can't select the specified event
logWarn(QString("%1 %2 doesn't exist on map '%3'").arg(Event::groupToString(event_group)).arg(event_id).arg(map_name));
}
// Map opened successfully, now try to select the targeted event on that map.
Event *targetEvent = this->editor->map->getEvent(targetEventGroup, targetEventIdName);
this->editor->selectMapEvent(targetEvent);
}
void MainWindow::displayMapProperties() {
@@ -1143,7 +1197,7 @@ void MainWindow::on_comboBox_LayoutSelector_currentTextChanged(const QString &te
}
this->editor->map->setLayout(layout);
setMap(this->editor->map->name());
markMapEdited();
markMapEdited(this->editor->map);
}
void MainWindow::onLayoutSelectorEditingFinished() {
@@ -1198,6 +1252,8 @@ 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));
editor->setCollisionGraphics();
ui->spinBox_SelectedElevation->setMaximum(Block::getMaxElevation());
ui->spinBox_SelectedCollision->setMaximum(Block::getMaxCollision());
@@ -1227,7 +1283,7 @@ bool MainWindow::setProjectUI() {
ui->layoutList->setModel(layoutListProxyModel);
ui->layoutList->sortByColumn(0, Qt::SortOrder::AscendingOrder);
ui->mapCustomAttributesFrame->table()->setRestrictedKeys(project->topLevelMapFields);
ui->mapCustomAttributesFrame->table()->setRestrictedKeys(project->getTopLevelMapFields());
return true;
}
@@ -1240,10 +1296,7 @@ void MainWindow::clearProjectUI() {
const QSignalBlocker b_SecondaryTileset(ui->comboBox_SecondaryTileset);
ui->comboBox_SecondaryTileset->clear();
const QSignalBlocker b_DiveMap(ui->comboBox_DiveMap);
ui->comboBox_DiveMap->clear();
const QSignalBlocker b_EmergeMap(ui->comboBox_EmergeMap);
ui->comboBox_EmergeMap->clear();
const QSignalBlocker b_LayoutSelector(ui->comboBox_LayoutSelector);
@@ -1404,8 +1457,6 @@ void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) {
// (other combo boxes like for warp destinations are repopulated when the map changes).
int mapIndex = this->editor->project->mapNames.indexOf(newMap->name());
if (mapIndex >= 0) {
const QSignalBlocker b_DiveMap(ui->comboBox_DiveMap);
const QSignalBlocker b_EmergeMap(ui->comboBox_EmergeMap);
ui->comboBox_DiveMap->insertItem(mapIndex, newMap->name());
ui->comboBox_EmergeMap->insertItem(mapIndex, newMap->name());
}
@@ -1661,16 +1712,15 @@ void MainWindow::on_action_Save_triggered() {
save(true);
}
void MainWindow::save(bool currentOnly) {
if (currentOnly) {
this->editor->saveCurrent();
} else {
this->editor->saveAll();
bool MainWindow::save(bool currentOnly) {
bool success = currentOnly ? this->editor->saveCurrent() : this->editor->saveAll();
if (!success) {
RecentErrorMessage::show(QStringLiteral("Failed to save some project changes."), this);
}
updateWindowTitle();
updateMapList();
if (!porymapConfig.shownInGameReloadMessage) {
if (success && !porymapConfig.shownInGameReloadMessage) {
// Show a one-time warning that the user may need to reload their map to see their new changes.
InfoMessage::show(QStringLiteral("Reload your map in-game!\n\nIf your game is currently saved on a map you have edited, "
"the changes may not appear until you leave the map and return."),
@@ -1679,6 +1729,7 @@ void MainWindow::save(bool currentOnly) {
}
saveGlobalConfigs();
return success;
}
void MainWindow::duplicate() {
@@ -1688,6 +1739,13 @@ void MainWindow::duplicate() {
void MainWindow::copy() {
auto focused = QApplication::focusWidget();
if (focused) {
// Allow copying text from selectable QLabels.
auto label = dynamic_cast<QLabel*>(focused);
if (label && !label->selectedText().isEmpty()) {
setClipboardData(label->selectedText());
return;
}
QString objectName = focused->objectName();
if (objectName == "graphicsView_currentMetatileSelection") {
// copy the current metatile selection as json data
@@ -1984,7 +2042,7 @@ void MainWindow::on_actionPlayer_View_Rectangle_triggered()
this->editor->settings->playerViewRectEnabled = enabled;
if ((this->editor->map_item && this->editor->map_item->has_mouse)
|| (this->editor->collision_item && this->editor->collision_item->has_mouse)) {
this->editor->playerViewRect->setVisible(enabled);
this->editor->playerViewRect->setVisible(enabled && this->editor->playerViewRect->getActive());
ui->graphicsView_Map->scene()->update();
}
}
@@ -2014,6 +2072,7 @@ void MainWindow::on_actionGrid_Settings_triggered() {
if (!this->gridSettingsDialog) {
this->gridSettingsDialog = new GridSettingsDialog(&this->editor->gridSettings, this);
connect(this->gridSettingsDialog, &GridSettingsDialog::changedGridSettings, this->editor, &Editor::updateMapGrid);
connect(this->gridSettingsDialog, &GridSettingsDialog::accepted, [this] { porymapConfig.gridSettings = this->editor->gridSettings; });
}
openSubWindow(this->gridSettingsDialog);
}
@@ -2510,7 +2569,7 @@ void MainWindow::onOpenConnectedMap(MapConnection *connection) {
}
void MainWindow::onMapLoaded(Map *map) {
connect(map, &Map::modified, [this, map] { this->markSpecificMapEdited(map); });
connect(map, &Map::modified, [this, map] { markMapEdited(map); });
}
void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
@@ -2616,7 +2675,8 @@ void MainWindow::on_pushButton_AddConnection_clicked() {
return;
auto dialog = new NewMapConnectionDialog(this, this->editor->map, this->editor->project->mapNames);
connect(dialog, &NewMapConnectionDialog::accepted, this->editor, &Editor::addConnection);
connect(dialog, &NewMapConnectionDialog::newConnectionedAdded, this->editor, &Editor::addNewConnection);
connect(dialog, &NewMapConnectionDialog::connectionReplaced, this->editor, &Editor::replaceConnection);
dialog->open();
}
@@ -2669,17 +2729,6 @@ void MainWindow::on_button_OpenEmergeMap_clicked() {
userSetMap(ui->comboBox_EmergeMap->currentText());
}
void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) {
// Include empty names as an update (user is deleting the connection)
if (mapName.isEmpty() || editor->project->mapNames.contains(mapName))
editor->updateDiveMap(mapName);
}
void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName) {
if (mapName.isEmpty() || editor->project->mapNames.contains(mapName))
editor->updateEmergeMap(mapName);
}
void MainWindow::on_comboBox_PrimaryTileset_currentTextChanged(const QString &tilesetLabel)
{
if (editor->project->primaryTilesetLabels.contains(tilesetLabel) && editor->layout) {
@@ -2741,25 +2790,20 @@ void MainWindow::on_pushButton_ChangeDimensions_clicked() {
}
}
void MainWindow::on_checkBox_smartPaths_stateChanged(int selected)
void MainWindow::setSmartPathsEnabled(bool enabled)
{
bool enabled = selected == Qt::Checked;
editor->settings->smartPathsEnabled = enabled;
if (enabled) {
editor->cursorMapTileRect->setSmartPathMode(true);
} else {
editor->cursorMapTileRect->setSmartPathMode(false);
}
this->editor->settings->smartPathsEnabled = enabled;
this->editor->cursorMapTileRect->setSmartPathMode(enabled);
}
void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected)
void MainWindow::setBorderVisibility(bool visible)
{
editor->toggleBorderVisibility(selected != 0);
editor->toggleBorderVisibility(visible);
}
void MainWindow::on_checkBox_MirrorConnections_stateChanged(int selected)
void MainWindow::setMirrorConnectionsEnabled(bool enabled)
{
porymapConfig.mirrorConnectingMaps = (selected == Qt::Checked);
porymapConfig.mirrorConnectingMaps = enabled;
}
void MainWindow::on_actionTileset_Editor_triggered()
@@ -3080,7 +3124,8 @@ bool MainWindow::closeProject() {
auto reply = msgBox.exec();
if (reply == QMessageBox::Yes) {
save();
if (!save())
return false;
} else if (reply == QMessageBox::No) {
logWarn("Closing project with unsaved changes.");
} else if (reply == QMessageBox::Cancel) {

File diff suppressed because it is too large Load Diff

View File

@@ -227,7 +227,7 @@ int MainWindow::getHeight() {
void MainWindow::setDimensions(int width, int height) {
if (!this->editor || !this->editor->layout)
return;
if (!Project::mapDimensionsValid(width, height))
if (this->editor->project && !this->editor->project->mapDimensionsValid(width, height))
return;
this->editor->layout->setDimensions(width, height);
this->tryCommitMapChanges(true);
@@ -237,7 +237,7 @@ void MainWindow::setDimensions(int width, int height) {
void MainWindow::setWidth(int width) {
if (!this->editor || !this->editor->layout)
return;
if (!Project::mapDimensionsValid(width, this->editor->layout->getHeight()))
if (this->editor->project && !this->editor->project->mapDimensionsValid(width, this->editor->layout->getHeight()))
return;
this->editor->layout->setDimensions(width, this->editor->layout->getHeight());
this->tryCommitMapChanges(true);
@@ -247,7 +247,7 @@ void MainWindow::setWidth(int width) {
void MainWindow::setHeight(int height) {
if (!this->editor || !this->editor->layout)
return;
if (!Project::mapDimensionsValid(this->editor->layout->getWidth(), height))
if (this->editor->project && !this->editor->project->mapDimensionsValid(this->editor->layout->getWidth(), height))
return;
this->editor->layout->setDimensions(this->editor->layout->getWidth(), height);
this->tryCommitMapChanges(true);

View File

@@ -276,7 +276,12 @@ void MapView::addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int
this->editor->layout->tileset_primary,
this->editor->layout->tileset_secondary,
paletteId)
#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
.flipped(Util::getOrientation(xflip, yflip));
#else
.mirrored(xflip, yflip);
#endif
if (setTransparency)
image.setColor(0, qRgba(0, 0, 0, 0));
if (this->getOverlay(layer)->addImage(x, y, image))

View File

@@ -268,14 +268,13 @@ void Scripting::cb_LayoutOpened(QString layoutName) {
instance->invokeCallback(OnLayoutOpened, args);
}
void Scripting::cb_MapResized(int oldWidth, int oldHeight, int newWidth, int newHeight) {
void Scripting::cb_MapResized(int oldWidth, int oldHeight, const QMargins &delta) {
if (!instance) return;
QJSValueList args {
oldWidth,
oldHeight,
newWidth,
newHeight,
Scripting::margins(delta),
};
instance->invokeCallback(OnMapResized, args);
}
@@ -356,6 +355,15 @@ QJSValue Scripting::dimensions(int width, int height) {
return obj;
}
QJSValue Scripting::margins(const QMargins &margins) {
QJSValue obj = instance->engine->newObject();
obj.setProperty("left", margins.left());
obj.setProperty("right", margins.right());
obj.setProperty("top", margins.top());
obj.setProperty("bottom", margins.bottom());
return obj;
}
QJSValue Scripting::position(int x, int y) {
QJSValue obj = instance->engine->newObject();
obj.setProperty("x", x);

View File

@@ -9,15 +9,19 @@ AboutPorymap::AboutPorymap(QWidget *parent) :
setAttribute(Qt::WA_DeleteOnClose);
static const QString commitHash = PORYMAP_LATEST_COMMIT;
this->ui->label_Version->setText(QString("Version %1%2\nQt %3 (%4)\n%5")
this->ui->label_Version->setText(getVersionString());
layout()->setSizeConstraint(QLayout::SetFixedSize);
}
QString AboutPorymap::getVersionString() {
static const QString commitHash = PORYMAP_LATEST_COMMIT;
return QString("Version %1%2\nQt %3 (%4)\n%5")
.arg(QCoreApplication::applicationVersion())
.arg(commitHash.isEmpty() ? "" : QString(" (%1)").arg(commitHash))
.arg(QStringLiteral(QT_VERSION_STR))
.arg(QSysInfo::buildCpuArchitecture())
.arg(QStringLiteral(__DATE__))
);
layout()->setSizeConstraint(QLayout::SetFixedSize);
.arg(QStringLiteral(__DATE__));
}
AboutPorymap::~AboutPorymap()

View File

@@ -1,6 +1,7 @@
#include "connectionpixmapitem.h"
#include "editcommands.h"
#include "map.h"
#include "editor.h"
#include <math.h>
@@ -9,7 +10,6 @@ ConnectionPixmapItem::ConnectionPixmapItem(MapConnection* connection)
connection(connection)
{
this->setEditable(true);
setFlag(ItemIsFocusable, true);
this->basePixmap = pixmap();
updateOrigin();
render(false);
@@ -31,7 +31,7 @@ void ConnectionPixmapItem::render(bool ignoreCache) {
this->basePixmap = this->connection->render();
QPixmap pixmap = this->basePixmap.copy(0, 0, this->basePixmap.width(), this->basePixmap.height());
this->setZValue(-1);
this->setZValue(Editor::ZValue::MapConnectionActive);
// When editing is inactive the current selection is ignored, all connections should appear normal.
if (this->getEditable()) {
@@ -43,7 +43,7 @@ void ConnectionPixmapItem::render(bool ignoreCache) {
painter.end();
} else {
// Darken the image
this->setZValue(-2);
this->setZValue(Editor::ZValue::MapConnectionInactive);
QPainter painter(&pixmap);
int alpha = static_cast<int>(255 * 0.25);
painter.fillRect(0, 0, pixmap.width(), pixmap.height(), QColor(0, 0, 0, alpha));
@@ -118,10 +118,6 @@ bool ConnectionPixmapItem::getEditable() {
}
void ConnectionPixmapItem::setSelected(bool selected) {
if (selected && !hasFocus()) {
setFocus(Qt::OtherFocusReason);
}
if (this->selected == selected)
return;
this->selected = selected;
@@ -131,7 +127,7 @@ void ConnectionPixmapItem::setSelected(bool selected) {
}
void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *) {
setFocus(Qt::MouseFocusReason);
this->setSelected(true);
}
void ConnectionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
@@ -142,18 +138,3 @@ void ConnectionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
emit connectionItemDoubleClicked(this->connection);
}
void ConnectionPixmapItem::keyPressEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) {
emit deleteRequested(this->connection);
} else {
QGraphicsPixmapItem::keyPressEvent(event);
}
}
void ConnectionPixmapItem::focusInEvent(QFocusEvent* event) {
if (!this->getEditable())
return;
this->setSelected(true);
QGraphicsPixmapItem::focusInEvent(event);
}

View File

@@ -7,44 +7,55 @@
ConnectionsListItem::ConnectionsListItem(QWidget *parent, MapConnection * connection, const QStringList &mapNames) :
QFrame(parent),
ui(new Ui::ConnectionsListItem)
ui(new Ui::ConnectionsListItem),
connection(connection),
map(connection->parentMap())
{
ui->setupUi(this);
setFocusPolicy(Qt::StrongFocus);
const QSignalBlocker blocker1(ui->comboBox_Direction);
const QSignalBlocker blocker2(ui->comboBox_Map);
const QSignalBlocker blocker3(ui->spinBox_Offset);
ui->comboBox_Direction->setEditable(false);
// Direction
const QSignalBlocker b_Direction(ui->comboBox_Direction);
ui->comboBox_Direction->setMinimumContentsLength(0);
ui->comboBox_Direction->addItems(MapConnection::cardinalDirections);
ui->comboBox_Direction->installEventFilter(this);
connect(ui->comboBox_Direction, &NoScrollComboBox::editingFinished, this, &ConnectionsListItem::commitDirection);
// Map
const QSignalBlocker b_Map(ui->comboBox_Map);
ui->comboBox_Map->setMinimumContentsLength(6);
ui->comboBox_Map->addItems(mapNames);
ui->comboBox_Map->setFocusedScrollingEnabled(false); // Scrolling could cause rapid changes to many different maps
ui->comboBox_Map->setInsertPolicy(QComboBox::NoInsert);
ui->comboBox_Map->installEventFilter(this);
ui->spinBox_Offset->setMinimum(INT_MIN);
ui->spinBox_Offset->setMaximum(INT_MAX);
connect(ui->comboBox_Map, &QComboBox::currentTextChanged, this, &ConnectionsListItem::commitMap);
// Invalid map names are not considered a change. If editing finishes with an invalid name, restore the previous name.
connect(ui->comboBox_Map->lineEdit(), &QLineEdit::editingFinished, [this] {
const QSignalBlocker blocker(ui->comboBox_Map);
if (ui->comboBox_Map->findText(ui->comboBox_Map->currentText()) < 0)
const QSignalBlocker b(ui->comboBox_Map);
if (this->connection && ui->comboBox_Map->findText(ui->comboBox_Map->currentText()) < 0)
ui->comboBox_Map->setTextItem(this->connection->targetMapName());
});
// Distinguish between move actions for the edit history
connect(ui->spinBox_Offset, &QSpinBox::editingFinished, [this] { this->actionId++; });
// Offset
const QSignalBlocker b_Offset(ui->spinBox_Offset);
ui->spinBox_Offset->setMinimum(INT_MIN);
ui->spinBox_Offset->setMaximum(INT_MAX);
ui->spinBox_Offset->installEventFilter(this);
connect(ui->spinBox_Offset, &QSpinBox::editingFinished, [this] { this->actionId++; }); // Distinguish between move actions for the edit history
connect(ui->spinBox_Offset, QOverload<int>::of(&QSpinBox::valueChanged), this, &ConnectionsListItem::commitMove);
// If the connection changes externally we want to update to reflect the change.
connect(connection, &MapConnection::offsetChanged, this, &ConnectionsListItem::updateUI);
connect(connection, &MapConnection::directionChanged, this, &ConnectionsListItem::updateUI);
connect(connection, &MapConnection::targetMapNameChanged, this, &ConnectionsListItem::updateUI);
this->connection = connection;
this->map = connection->parentMap();
connect(ui->button_Delete, &QToolButton::clicked, this, &ConnectionsListItem::commitRemove);
connect(ui->button_OpenMap, &QToolButton::clicked, [this] { emit openMapClicked(this->connection); });
this->updateUI();
}
@@ -66,13 +77,19 @@ void ConnectionsListItem::updateUI() {
ui->spinBox_Offset->setValue(this->connection->offset());
}
bool ConnectionsListItem::eventFilter(QObject*, QEvent *event) {
if (event->type() == QEvent::FocusIn)
this->setSelected(true);
return false;
}
void ConnectionsListItem::setSelected(bool selected) {
if (selected == this->isSelected)
return;
this->isSelected = selected;
this->setStyleSheet(selected ? ".ConnectionsListItem { border: 1px solid rgb(255, 0, 255); }"
: ".ConnectionsListItem { border-width: 1px; }");
this->setStyleSheet(selected ? QStringLiteral(".ConnectionsListItem { border: 1px solid rgb(255, 0, 255); }")
: QStringLiteral(".ConnectionsListItem { border-width: 1px; }"));
if (selected)
emit this->selected();
}
@@ -81,42 +98,34 @@ void ConnectionsListItem::mousePressEvent(QMouseEvent *) {
this->setSelected(true);
}
void ConnectionsListItem::on_comboBox_Direction_currentTextChanged(QString direction) {
this->setSelected(true);
if (this->map)
void ConnectionsListItem::commitDirection() {
const QString direction = ui->comboBox_Direction->currentText();
if (!this->connection || this->connection->direction() == direction)
return;
if (MapConnection::isDiving(direction)) {
// Diving maps are displayed separately, no support right now for replacing a list item with a diving map.
// For now just restore the original direction.
ui->comboBox_Direction->setCurrentText(this->connection->direction());
return;
}
if (this->map) {
this->map->commit(new MapConnectionChangeDirection(this->connection, direction));
}
}
void ConnectionsListItem::on_comboBox_Map_currentTextChanged(QString mapName) {
this->setSelected(true);
void ConnectionsListItem::commitMap(const QString &mapName) {
if (this->map && ui->comboBox_Map->findText(mapName) >= 0)
this->map->commit(new MapConnectionChangeMap(this->connection, mapName));
}
void ConnectionsListItem::on_spinBox_Offset_valueChanged(int offset) {
this->setSelected(true);
void ConnectionsListItem::commitMove(int offset) {
if (this->map)
this->map->commit(new MapConnectionMove(this->connection, offset, this->actionId));
}
void ConnectionsListItem::on_button_Delete_clicked() {
void ConnectionsListItem::commitRemove() {
if (this->map)
this->map->commit(new MapConnectionRemove(this->map, this->connection));
}
void ConnectionsListItem::on_button_OpenMap_clicked() {
emit openMapClicked(this->connection);
}
void ConnectionsListItem::focusInEvent(QFocusEvent* event) {
this->setSelected(true);
QFrame::focusInEvent(event);
}
void ConnectionsListItem::keyPressEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) {
on_button_Delete_clicked();
} else {
QFrame::keyPressEvent(event);
}
}

View File

@@ -1,6 +1,7 @@
#include "customattributestable.h"
#include "parseutil.h"
#include "noscrollspinbox.h"
#include "utility.h"
#include <QHeaderView>
#include <QScrollBar>
@@ -39,8 +40,8 @@ CustomAttributesTable::CustomAttributesTable(QWidget *parent) :
});
}
QMap<QString, QJsonValue> CustomAttributesTable::getAttributes() const {
QMap<QString, QJsonValue> fields;
QJsonObject CustomAttributesTable::getAttributes() const {
QJsonObject fields;
for (int row = 0; row < this->rowCount(); row++) {
auto keyValuePair = this->getAttribute(row);
if (!keyValuePair.first.isEmpty())
@@ -96,7 +97,7 @@ int CustomAttributesTable::addAttribute(const QString &key, const QJsonValue &va
keyItem->setFlags(Qt::ItemIsEnabled);
keyItem->setData(DataRole::JsonType, type); // Record the type for writing to the file
keyItem->setTextAlignment(Qt::AlignCenter);
keyItem->setToolTip(key); // Display name as tool tip in case it's too long to see in the cell
keyItem->setToolTip(Util::toHtmlParagraph(key)); // Display name as tool tip in case it's too long to see in the cell
this->setItem(rowIndex, Column::Key, keyItem);
// Add value to table
@@ -145,10 +146,10 @@ void CustomAttributesTable::addNewAttribute(const QString &key, const QJsonValue
}
// For programmatically populating the table
void CustomAttributesTable::setAttributes(const QMap<QString, QJsonValue> &attributes) {
void CustomAttributesTable::setAttributes(const QJsonObject &attributes) {
m_keys.clear();
this->setRowCount(0); // Clear old values
for (auto it = attributes.cbegin(); it != attributes.cend(); it++)
for (auto it = attributes.constBegin(); it != attributes.constEnd(); it++)
this->addAttribute(it.key(), it.value());
this->resizeVertically();
}

View File

@@ -105,7 +105,7 @@ void CustomScriptsEditor::displayScript(const QString &filepath, bool enabled) {
connect(widget->ui->b_Choose, &QAbstractButton::clicked, [this, item](bool) { this->replaceScript(item); });
connect(widget->ui->b_Edit, &QAbstractButton::clicked, [this, item](bool) { this->openScript(item); });
connect(widget->ui->b_Delete, &QAbstractButton::clicked, [this, item](bool) { this->removeScript(item); });
connect(widget->ui->checkBox_Enable, &QCheckBox::stateChanged, this, &CustomScriptsEditor::markEdited);
connect(widget->ui->checkBox_Enable, &QCheckBox::toggled, this, &CustomScriptsEditor::markEdited);
connect(widget->ui->lineEdit_filepath, &QLineEdit::textEdited, this, &CustomScriptsEditor::markEdited);
// Per the Qt manual, for performance reasons QListWidget::setItemWidget shouldn't be used with non-static items.

View File

@@ -38,9 +38,5 @@ void DivingMapPixmapItem::onTargetMapChanged() {
}
void DivingMapPixmapItem::setComboText(const QString &text) {
if (!m_combo)
return;
const QSignalBlocker blocker(m_combo);
m_combo->setCurrentText(text);
if (m_combo) m_combo->setCurrentText(text);
}

View File

@@ -1,134 +0,0 @@
#include "draggablepixmapitem.h"
#include "editor.h"
#include "editcommands.h"
#include "mapruler.h"
#include "metatile.h"
static unsigned currentActionId = 0;
void DraggablePixmapItem::updatePosition() {
int x = this->event->getPixelX();
int y = this->event->getPixelY();
setX(x);
setY(y);
if (this->editor->selectedEvents.contains(this->event)) {
setZValue(event->getY() + 1);
} else {
setZValue(event->getY());
}
editor->updateWarpEventWarning(event);
}
void DraggablePixmapItem::emitPositionChanged() {
emit xChanged(event->getX());
emit yChanged(event->getY());
emit elevationChanged(event->getElevation());
}
void DraggablePixmapItem::updatePixmap() {
editor->project->loadEventPixmap(event, true);
this->updatePosition();
editor->redrawEventPixmapItem(this);
emit spriteChanged(event->getPixmap());
}
void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) {
if (this->active)
return;
this->active = true;
this->lastPos = Metatile::coordFromPixmapCoord(mouse->scenePos());
bool selectionToggle = mouse->modifiers() & Qt::ControlModifier;
if (selectionToggle || !this->editor->selectedEvents.contains(this->event)) {
// User is either toggling this selection on/off as part of a group selection,
// or they're newly selecting just this item.
this->editor->selectMapEvent(this->event, selectionToggle);
} else {
// This item is already selected and the user isn't toggling the selection, so there are 4 possibilities:
// 1. This is the only selected event, and the selection is pointless.
// 2. This is the only selected event, and they want to drag the item around.
// 3. There's a group selection, and they want to start a new selection with just this item.
// 4. There's a group selection, and they want to drag the group around.
// 'selectMapEvent' will immediately clear the rest of the selection, which supports #1-3 but prevents #4.
// To support #4 we set the flag below, and we only call 'selectMapEvent' on mouse release if no move occurred.
this->releaseSelectionQueued = true;
}
this->editor->selectingEvent = true;
}
void DraggablePixmapItem::move(int dx, int dy) {
event->setX(event->getX() + dx);
event->setY(event->getY() + dy);
updatePosition();
emitPositionChanged();
}
void DraggablePixmapItem::moveTo(const QPoint &pos) {
event->setX(pos.x());
event->setY(pos.y());
updatePosition();
emitPositionChanged();
}
void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) {
if (!this->active)
return;
QPoint pos = Metatile::coordFromPixmapCoord(mouse->scenePos());
if (pos == this->lastPos)
return;
QPoint moveDistance = pos - this->lastPos;
this->lastPos = pos;
emit this->editor->map_item->hoveredMapMetatileChanged(pos);
QList <Event *> selectedEvents;
if (this->editor->selectedEvents.contains(this->event)) {
selectedEvents = this->editor->selectedEvents;
} else {
selectedEvents.append(this->event);
}
editor->map->commit(new EventMove(selectedEvents, moveDistance.x(), moveDistance.y(), currentActionId));
this->releaseSelectionQueued = false;
}
void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) {
if (!this->active)
return;
this->active = false;
currentActionId++;
if (this->releaseSelectionQueued) {
this->releaseSelectionQueued = false;
if (Metatile::coordFromPixmapCoord(mouse->scenePos()) == this->lastPos)
this->editor->selectMapEvent(this->event);
}
}
// Events with properties that specify a map will open that map when double-clicked.
void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
Event::Type eventType = this->event->getEventType();
if (eventType == Event::Type::Warp) {
WarpEvent *warp = dynamic_cast<WarpEvent *>(this->event);
QString destMap = warp->getDestinationMap();
int warpId = ParseUtil::gameStringToInt(warp->getDestinationWarpID());
emit editor->warpEventDoubleClicked(destMap, warpId, Event::Group::Warp);
}
else if (eventType == Event::Type::CloneObject) {
CloneObjectEvent *clone = dynamic_cast<CloneObjectEvent *>(this->event);
emit editor->warpEventDoubleClicked(clone->getTargetMap(), clone->getTargetID(), Event::Group::Object);
}
else if (eventType == Event::Type::SecretBase) {
const QString mapPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
SecretBaseEvent *base = dynamic_cast<SecretBaseEvent *>(this->event);
QString baseId = base->getBaseID();
QString destMap = editor->project->mapConstantsToMapNames.value(mapPrefix + baseId.left(baseId.lastIndexOf("_")));
emit editor->warpEventDoubleClicked(destMap, 0, Event::Group::Warp);
}
else if (eventType == Event::Type::HealLocation && projectConfig.healLocationRespawnDataEnabled) {
HealLocationEvent *heal = dynamic_cast<HealLocationEvent *>(this->event);
const QString localIdName = heal->getRespawnNPC();
int localId = 0; // TODO: Get value from localIdName
emit editor->warpEventDoubleClicked(heal->getRespawnMapName(), localId, Event::Group::Object);
}
}

View File

@@ -1,7 +1,7 @@
#include "eventframes.h"
#include "customattributesframe.h"
#include "editcommands.h"
#include "draggablepixmapitem.h"
#include "eventpixmapitem.h"
#include <limits>
using std::numeric_limits;
@@ -74,6 +74,7 @@ void EventFrame::setup() {
this->label_id = new QLabel("event_type");
l_vbox_1->addWidget(this->label_id);
l_vbox_1->addLayout(l_layout_xyz);
this->label_id->setText(Event::typeToString(this->event->getEventType()));
// icon / pixmap label
this->label_icon = new QLabel(this);
@@ -113,7 +114,7 @@ void EventFrame::connectSignals(MainWindow *) {
}
});
connect(this->event->getPixmapItem(), &DraggablePixmapItem::xChanged, this->spinner_x, &NoScrollSpinBox::setValue);
connect(this->event->getPixmapItem(), &EventPixmapItem::xChanged, this->spinner_x, &NoScrollSpinBox::setValue);
this->spinner_y->disconnect();
connect(this->spinner_y, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
@@ -122,7 +123,7 @@ void EventFrame::connectSignals(MainWindow *) {
this->event->getMap()->commit(new EventMove(QList<Event *>() << this->event, 0, delta, this->spinner_y->getActionId()));
}
});
connect(this->event->getPixmapItem(), &DraggablePixmapItem::yChanged, this->spinner_y, &NoScrollSpinBox::setValue);
connect(this->event->getPixmapItem(), &EventPixmapItem::yChanged, this->spinner_y, &NoScrollSpinBox::setValue);
this->spinner_z->disconnect();
connect(this->spinner_z, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
@@ -172,19 +173,31 @@ void EventFrame::setActive(bool active) {
this->blockSignals(!active);
}
void EventFrame::populateDropdown(NoScrollComboBox * combo, const QStringList &items) {
// Set the items in the combo box. This may be called after the frame is initialized
// if the frame needs to be repopulated, so ensure the text in the combo is preserved
// and that we don't accidentally fire 'currentTextChanged'.
const QSignalBlocker b(combo);
const QString savedText = combo->currentText();
combo->clear();
combo->addItems(items);
combo->setCurrentText(savedText);
}
void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * project) {
// The script dropdown and autocomplete are populated with scripts used by the map's events and from its scripts file.
if (!this->event->getMap())
return;
QStringList scripts = this->event->getMap()->getScriptLabels(this->event->getEventGroup());
combo->addItems(scripts);
populateDropdown(combo, scripts);
// Depending on the settings, the autocomplete may also contain all global scripts.
if (porymapConfig.loadAllEventScripts) {
project->insertGlobalScriptLabels(scripts);
}
// Note: Because 'combo' is the parent, the old QCompleter will be deleted when a new one is set.
auto completer = new QCompleter(scripts, combo);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
@@ -197,27 +210,46 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj
combo->setCompleter(completer);
// If the project changes the script labels, update the EventFrame.
// TODO: At the moment this only happens when the user changes script settings (i.e. when 'porymapConfig.loadAllEventScripts' changes).
// This should ultimately be connected to a file watcher so that we can also update the dropdown when the scripts file changes.
connect(project, &Project::eventScriptLabelsRead, this, &EventFrame::invalidateValues, Qt::UniqueConnection);
}
void EventFrame::populateIdNameDropdown(NoScrollComboBox * combo, Project * project, const QString &mapName, Event::Group group) {
if (!project->mapNames.contains(mapName))
return;
Map *map = project->loadMap(mapName);
if (map) populateDropdown(combo, map->getEventIdNames(group));
}
void ObjectFrame::setup() {
EventFrame::setup();
this->label_id->setText("Object");
// local id
QFormLayout *l_form_local_id = new QFormLayout();
this->line_edit_local_id = new QLineEdit(this);
static const QString line_edit_local_id_toolTip = Util::toHtmlParagraph("An optional, unique name to use to refer to this object in scripts. "
"If no name is given you can refer to this object using its 'object id' number.");
this->line_edit_local_id->setToolTip(line_edit_local_id_toolTip);
this->line_edit_local_id->setPlaceholderText("LOCALID_MY_NPC");
l_form_local_id->addRow("Local ID", this->line_edit_local_id);
this->layout_contents->addLayout(l_form_local_id);
// sprite combo
QFormLayout *l_form_sprite = new QFormLayout();
this->combo_sprite = new NoScrollComboBox(this);
this->combo_sprite->setToolTip("The sprite graphics to use for this object.");
static const QString combo_sprite_toolTip = Util::toHtmlParagraph("The sprite graphics to use for this object.");
this->combo_sprite->setToolTip(combo_sprite_toolTip);
l_form_sprite->addRow("Sprite", this->combo_sprite);
this->layout_contents->addLayout(l_form_sprite);
// movement
QFormLayout *l_form_movement = new QFormLayout();
this->combo_movement = new NoScrollComboBox(this);
this->combo_movement->setToolTip("The object's natural movement behavior when\n"
"the player is not interacting with it.");
static const QString combo_movement_toolTip = Util::toHtmlParagraph("The object's natural movement behavior when the player is not interacting with it.");
this->combo_movement->setToolTip(combo_movement_toolTip);
l_form_movement->addRow("Movement", this->combo_movement);
this->layout_contents->addLayout(l_form_movement);
@@ -226,15 +258,15 @@ void ObjectFrame::setup() {
this->spinner_radius_x = new NoScrollSpinBox(this);
this->spinner_radius_x->setMinimum(0);
this->spinner_radius_x->setMaximum(255);
this->spinner_radius_x->setToolTip("The maximum number of metatiles this object\n"
"is allowed to move left or right during its\n"
"normal movement behavior actions.");
static const QString spinner_radius_x_toolTip = Util::toHtmlParagraph("The maximum number of metatiles this object is allowed to move left "
"or right during its normal movement behavior actions.");
this->spinner_radius_x->setToolTip(spinner_radius_x_toolTip);
this->spinner_radius_y = new NoScrollSpinBox(this);
this->spinner_radius_y->setMinimum(0);
this->spinner_radius_y->setMaximum(255);
this->spinner_radius_y->setToolTip("The maximum number of metatiles this object\n"
"is allowed to move up or down during its\n"
"normal movement behavior actions.");
static const QString spinner_radius_y_toolTip = Util::toHtmlParagraph("The maximum number of metatiles this object is allowed to move up "
"or down during its normal movement behavior actions.");
this->spinner_radius_y->setToolTip(spinner_radius_y_toolTip);
l_form_radii_xy->addRow("Movement Radius X", this->spinner_radius_x);
l_form_radii_xy->addRow("Movement Radius Y", this->spinner_radius_y);
this->layout_contents->addLayout(l_form_radii_xy);
@@ -242,11 +274,13 @@ void ObjectFrame::setup() {
// script
QFormLayout *l_form_script = new QFormLayout();
this->combo_script = new NoScrollComboBox(this);
this->combo_script->setToolTip("The script which is executed with this event.");
static const QString combo_script_toolTip = Util::toHtmlParagraph("The script that is executed with this event.");
this->combo_script->setToolTip(combo_script_toolTip);
// Add button next to combo which opens combo's current script.
this->button_script = new QToolButton(this);
this->button_script->setToolTip("Go to this script definition in text editor.");
static const QString button_script_toolTip = Util::toHtmlParagraph("Go to this script definition in text editor.");
this->button_script->setToolTip(button_script_toolTip);
this->button_script->setFixedSize(this->combo_script->height(), this->combo_script->height());
this->button_script->setIcon(QFileIconProvider().icon(QFileIconProvider::File));
@@ -261,24 +295,25 @@ void ObjectFrame::setup() {
// event flag
QFormLayout *l_form_flag = new QFormLayout();
this->combo_flag = new NoScrollComboBox(this);
this->combo_flag->setToolTip("The flag which hides the object when set.");
static const QString combo_flag_toolTip = Util::toHtmlParagraph("The flag that hides the object when set.");
this->combo_flag->setToolTip(combo_flag_toolTip);
l_form_flag->addRow("Event Flag", this->combo_flag);
this->layout_contents->addLayout(l_form_flag);
// trainer type
QFormLayout *l_form_trainer = new QFormLayout();
this->combo_trainer_type = new NoScrollComboBox(this);
this->combo_trainer_type->setToolTip("The trainer type of this object event.\n"
"If it is not a trainer, use NONE. SEE ALL DIRECTIONS\n"
"should only be used with a sight radius of 1.");
static const QString combo_trainer_type_toolTip = Util::toHtmlParagraph("The trainer type of this object event. If it is not a trainer, use NONE. "
"SEE ALL DIRECTIONS should only be used with a sight radius of 1.");
this->combo_trainer_type->setToolTip(combo_trainer_type_toolTip);
l_form_trainer->addRow("Trainer Type", this->combo_trainer_type);
this->layout_contents->addLayout(l_form_trainer);
// sight radius / berry tree id
QFormLayout *l_form_radius_treeid = new QFormLayout();
this->combo_radius_treeid = new NoScrollComboBox(this);
this->combo_radius_treeid->setToolTip("The maximum sight range of a trainer,\n"
"OR the unique id of the berry tree.");
static const QString combo_radius_treeid_toolTip = Util::toHtmlParagraph("The maximum sight range of a trainer, OR the unique id of the berry tree.");
this->combo_radius_treeid->setToolTip(combo_radius_treeid_toolTip);
l_form_radius_treeid->addRow("Sight Radius / Berry Tree ID", this->combo_radius_treeid);
this->layout_contents->addLayout(l_form_radius_treeid);
@@ -290,21 +325,29 @@ void ObjectFrame::connectSignals(MainWindow *window) {
if (this->connected) return;
EventFrame::connectSignals(window);
Project *project = window->editor->project;
// local id
this->line_edit_local_id->disconnect();
connect(this->line_edit_local_id, &QLineEdit::textChanged, [this](const QString &text) {
this->object->setIdName(text);
this->object->modify();
});
// sprite update
this->combo_sprite->disconnect();
connect(this->combo_sprite, &QComboBox::currentTextChanged, [this](const QString &text) {
connect(this->combo_sprite, &QComboBox::currentTextChanged, [this, project](const QString &text) {
this->object->setGfx(text);
this->object->getPixmapItem()->updatePixmap();
this->object->getPixmapItem()->render(project);
this->object->modify();
});
connect(this->object->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap);
connect(this->object->getPixmapItem(), &EventPixmapItem::rendered, this->label_icon, &QLabel::setPixmap);
// movement
this->combo_movement->disconnect();
connect(this->combo_movement, &QComboBox::currentTextChanged, [this](const QString &text) {
connect(this->combo_movement, &QComboBox::currentTextChanged, [this, project](const QString &text) {
this->object->setMovement(text);
this->object->getPixmapItem()->updatePixmap();
this->object->getPixmapItem()->render(project);
this->object->modify();
});
@@ -362,6 +405,9 @@ void ObjectFrame::initialize() {
const QSignalBlocker blocker(this);
EventFrame::initialize();
// local id
this->line_edit_local_id->setText(this->object->getIdName());
// sprite
this->combo_sprite->setTextItem(this->object->getGfx());
@@ -393,12 +439,11 @@ void ObjectFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
this->combo_sprite->addItems(project->gfxDefines.keys());
this->combo_movement->addItems(project->movementTypes);
this->combo_flag->addItems(project->flagNames);
this->combo_trainer_type->addItems(project->trainerTypes);
this->populateScriptDropdown(this->combo_script, project);
populateDropdown(this->combo_sprite, project->gfxDefines.keys());
populateDropdown(this->combo_movement, project->movementTypes);
populateDropdown(this->combo_flag, project->flagNames);
populateDropdown(this->combo_trainer_type, project->trainerTypes);
populateScriptDropdown(this->combo_script, project);
}
@@ -406,13 +451,25 @@ void ObjectFrame::populate(Project *project) {
void CloneObjectFrame::setup() {
EventFrame::setup();
this->label_id->setText("Clone Object");
this->spinner_z->setEnabled(false);
// local id
QFormLayout *l_form_local_id = new QFormLayout();
this->line_edit_local_id = new QLineEdit(this);
static const QString line_edit_local_id_toolTip = Util::toHtmlParagraph("An optional, unique name to use to refer to this object in scripts. "
"If no name is given you can refer to this object using its 'object id' number.");
this->line_edit_local_id->setToolTip(line_edit_local_id_toolTip);
this->line_edit_local_id->setPlaceholderText("LOCALID_MY_CLONE_NPC");
l_form_local_id->addRow("Local ID", this->line_edit_local_id);
this->layout_contents->addLayout(l_form_local_id);
// sprite combo (edits disabled)
QFormLayout *l_form_sprite = new QFormLayout();
this->combo_sprite = new NoScrollComboBox(this);
static const QString combo_sprite_toolTip = Util::toHtmlParagraph("The sprite graphics to use for this object. This is updated automatically "
"to match the target object, and so can't be edited. By default the games "
"will get the graphics directly from the target object, so this field is ignored.");
this->combo_sprite->setToolTip(combo_sprite_toolTip);
l_form_sprite->addRow("Sprite", this->combo_sprite);
this->combo_sprite->setEnabled(false);
this->layout_contents->addLayout(l_form_sprite);
@@ -420,15 +477,17 @@ void CloneObjectFrame::setup() {
// clone map id combo
QFormLayout *l_form_dest_map = new QFormLayout();
this->combo_target_map = new NoScrollComboBox(this);
this->combo_target_map->setToolTip("The name of the map that the object being cloned is on.");
static const QString combo_target_map_toolTip = Util::toHtmlParagraph("The name of the map that the object being cloned is on.");
this->combo_target_map->setToolTip(combo_target_map_toolTip);
l_form_dest_map->addRow("Target Map", this->combo_target_map);
this->layout_contents->addLayout(l_form_dest_map);
// clone local id spinbox
// clone local id combo
QFormLayout *l_form_dest_id = new QFormLayout();
this->spinner_target_id = new NoScrollSpinBox(this);
this->spinner_target_id->setToolTip("event_object ID of the object being cloned.");
l_form_dest_id->addRow("Target Local ID", this->spinner_target_id);
this->combo_target_id = new NoScrollComboBox(this);
static const QString combo_target_id_toolTip = Util::toHtmlParagraph("The Local ID name or number of the object being cloned.");
this->combo_target_id->setToolTip(combo_target_id_toolTip);
l_form_dest_id->addRow("Target Local ID", this->combo_target_id);
this->layout_contents->addLayout(l_form_dest_id);
// custom attributes
@@ -439,27 +498,48 @@ void CloneObjectFrame::connectSignals(MainWindow *window) {
if (this->connected) return;
EventFrame::connectSignals(window);
Project *project = window->editor->project;
// local id
this->line_edit_local_id->disconnect();
connect(this->line_edit_local_id, &QLineEdit::textChanged, [this](const QString &text) {
this->clone->setIdName(text);
this->clone->modify();
});
// update icon displayed in frame with target
connect(this->clone->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap);
connect(this->clone->getPixmapItem(), &EventPixmapItem::rendered, this->label_icon, &QLabel::setPixmap);
// target map
this->combo_target_map->disconnect();
connect(this->combo_target_map, &QComboBox::currentTextChanged, [this](const QString &text) {
this->clone->setTargetMap(text);
this->clone->getPixmapItem()->updatePixmap();
connect(this->combo_target_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) {
this->clone->setTargetMap(mapName);
this->clone->getPixmapItem()->render(project);
this->combo_sprite->setCurrentText(this->clone->getGfx());
this->clone->modify();
populateIdNameDropdown(this->combo_target_id, project, mapName, Event::Group::Object);
});
connect(window, &MainWindow::mapOpened, this, &CloneObjectFrame::tryInvalidateIdDropdown, Qt::UniqueConnection);
// target id
this->combo_target_id->disconnect();
connect(this->combo_target_id, &QComboBox::currentTextChanged, [this, project](const QString &text) {
this->clone->setTargetID(text);
this->clone->getPixmapItem()->render(project);
this->combo_sprite->setCurrentText(this->clone->getGfx());
this->clone->modify();
});
// target id
this->spinner_target_id->disconnect();
connect(this->spinner_target_id, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
this->clone->setTargetID(value);
this->clone->getPixmapItem()->updatePixmap();
this->combo_sprite->setCurrentText(this->clone->getGfx());
this->clone->modify();
});
// This frame type displays map names, so when a new map is created we need to repopulate it.
connect(project, &Project::mapCreated, this, &EventFrame::invalidateValues, Qt::UniqueConnection);
}
void CloneObjectFrame::tryInvalidateIdDropdown(Map *map) {
// If the clone's target map is opened then the names in this frame's ID dropdown may be changed.
// Make sure we update the frame next time it's opened.
if (map && this->clone && map->name() == this->clone->getTargetMap()) {
invalidateValues();
}
}
void CloneObjectFrame::initialize() {
@@ -468,13 +548,14 @@ void CloneObjectFrame::initialize() {
const QSignalBlocker blocker(this);
EventFrame::initialize();
// local id
this->line_edit_local_id->setText(this->clone->getIdName());
// sprite
this->combo_sprite->setCurrentText(this->clone->getGfx());
// target id
this->spinner_target_id->setMinimum(1);
this->spinner_target_id->setMaximum(126);
this->spinner_target_id->setValue(this->clone->getTargetID());
this->combo_target_id->setCurrentText(this->clone->getTargetID());
// target map
this->combo_target_map->setTextItem(this->clone->getTargetMap());
@@ -486,32 +567,41 @@ void CloneObjectFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
this->combo_target_map->addItems(project->mapNames);
populateDropdown(this->combo_target_map, project->mapNames);
populateIdNameDropdown(this->combo_target_id, project, this->clone->getTargetMap(), Event::Group::Object);
}
void WarpFrame::setup() {
EventFrame::setup();
this->label_id->setText("Warp");
// ID
QFormLayout *l_form_id = new QFormLayout();
this->line_edit_id = new QLineEdit(this);
static const QString line_edit_id_toolTip = Util::toHtmlParagraph("An optional, unique name to use to refer to this warp from other warps. "
"If no name is given you can refer to this warp using its 'warp id' number.");
this->line_edit_id->setToolTip(line_edit_id_toolTip);
this->line_edit_id->setPlaceholderText("WARP_ID_MY_WARP");
l_form_id->addRow("ID", this->line_edit_id);
this->layout_contents->addLayout(l_form_id);
// desination map combo
QFormLayout *l_form_dest_map = new QFormLayout();
this->combo_dest_map = new NoScrollComboBox(this);
this->combo_dest_map->setToolTip("The destination map name of the warp.");
static const QString combo_dest_map_toolTip = Util::toHtmlParagraph("The destination map name of the warp.");
this->combo_dest_map->setToolTip(combo_dest_map_toolTip);
l_form_dest_map->addRow("Destination Map", this->combo_dest_map);
this->layout_contents->addLayout(l_form_dest_map);
// desination warp id
QFormLayout *l_form_dest_warp = new QFormLayout();
this->combo_dest_warp = new NoScrollComboBox(this);
this->combo_dest_warp->setToolTip("The warp id on the destination map.");
static const QString combo_dest_warp_toolTip = Util::toHtmlParagraph("The warp id on the destination map.");
this->combo_dest_warp->setToolTip(combo_dest_warp_toolTip);
l_form_dest_warp->addRow("Destination Warp", this->combo_dest_warp);
this->layout_contents->addLayout(l_form_dest_warp);
// warning
static const QString warningText = "Warning:\n"
"This warp event is not positioned on a metatile with a warp behavior.\n"
"Click this warning for more details.";
auto warningText = QStringLiteral("Warning:\nThis warp event is not positioned on a metatile with a warp behavior.\nClick this warning for more details.");
QVBoxLayout *l_vbox_warning = new QVBoxLayout();
this->warning = new QPushButton(warningText, this);
this->warning->setFlat(true);
@@ -528,13 +618,23 @@ void WarpFrame::connectSignals(MainWindow *window) {
if (this->connected) return;
EventFrame::connectSignals(window);
Project *project = window->editor->project;
// id
this->line_edit_id->disconnect();
connect(this->line_edit_id, &QLineEdit::textChanged, [this](const QString &text) {
this->warp->setIdName(text);
this->warp->modify();
});
// dest map
this->combo_dest_map->disconnect();
connect(this->combo_dest_map, &QComboBox::currentTextChanged, [this](const QString &text) {
this->warp->setDestinationMap(text);
connect(this->combo_dest_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) {
this->warp->setDestinationMap(mapName);
this->warp->modify();
populateIdNameDropdown(this->combo_dest_warp, project, mapName, Event::Group::Warp);
});
connect(window, &MainWindow::mapOpened, this, &WarpFrame::tryInvalidateIdDropdown, Qt::UniqueConnection);
// dest id
this->combo_dest_warp->disconnect();
@@ -546,6 +646,17 @@ void WarpFrame::connectSignals(MainWindow *window) {
// warning
this->warning->disconnect();
connect(this->warning, &QPushButton::clicked, window, &MainWindow::onWarpBehaviorWarningClicked);
// This frame type displays map names, so when a new map is created we need to repopulate it.
connect(project, &Project::mapCreated, this, &EventFrame::invalidateValues, Qt::UniqueConnection);
}
void WarpFrame::tryInvalidateIdDropdown(Map *map) {
// If the warps's target map is opened then the names in this frame's ID dropdown may be changed.
// Make sure we update the frame next time it's opened.
if (map && this->warp && map->name() == this->warp->getDestinationMap()) {
invalidateValues();
}
}
void WarpFrame::initialize() {
@@ -554,6 +665,9 @@ void WarpFrame::initialize() {
const QSignalBlocker blocker(this);
EventFrame::initialize();
// id
this->line_edit_id->setText(this->warp->getIdName());
// dest map
this->combo_dest_map->setTextItem(this->warp->getDestinationMap());
@@ -567,7 +681,8 @@ void WarpFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
this->combo_dest_map->addItems(project->mapNames);
populateDropdown(this->combo_dest_map, project->mapNames);
populateIdNameDropdown(this->combo_dest_warp, project, this->warp->getDestinationMap(), Event::Group::Warp);
}
@@ -575,27 +690,28 @@ void WarpFrame::populate(Project *project) {
void TriggerFrame::setup() {
EventFrame::setup();
this->label_id->setText("Trigger");
// script combo
QFormLayout *l_form_script = new QFormLayout();
this->combo_script = new NoScrollComboBox(this);
this->combo_script->setToolTip("The script which is executed with this event.");
static const QString combo_script_toolTip = Util::toHtmlParagraph("The script that is executed with this event.");
this->combo_script->setToolTip(combo_script_toolTip);
l_form_script->addRow("Script", this->combo_script);
this->layout_contents->addLayout(l_form_script);
// var combo
QFormLayout *l_form_var = new QFormLayout();
this->combo_var = new NoScrollComboBox(this);
this->combo_var->setToolTip("The variable by which the script is triggered.\n"
"The script is triggered when this variable's value matches 'Var Value'.");
static const QString combo_var_toolTip = Util::toHtmlParagraph("The variable by which the script is triggered. "
"The script is triggered when this variable's value matches 'Var Value'.");
this->combo_var->setToolTip(combo_var_toolTip);
l_form_var->addRow("Var", this->combo_var);
this->layout_contents->addLayout(l_form_var);
// var value combo
QFormLayout *l_form_var_val = new QFormLayout();
this->combo_var_value = new NoScrollComboBox(this);
this->combo_var_value->setToolTip("The variable's value which triggers the script.");
static const QString combo_var_value_toolTip = Util::toHtmlParagraph("The variable's value that triggers the script.");
this->combo_var_value->setToolTip(combo_var_value_toolTip);
l_form_var_val->addRow("Var Value", this->combo_var_value);
this->layout_contents->addLayout(l_form_var_val);
@@ -652,10 +768,8 @@ void TriggerFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
// var combo
this->combo_var->addItems(project->varNames);
this->populateScriptDropdown(this->combo_script, project);
populateDropdown(this->combo_var, project->varNames);
populateScriptDropdown(this->combo_script, project);
}
@@ -663,12 +777,11 @@ void TriggerFrame::populate(Project *project) {
void WeatherTriggerFrame::setup() {
EventFrame::setup();
this->label_id->setText("Weather Trigger");
// weather combo
QFormLayout *l_form_weather = new QFormLayout();
this->combo_weather = new NoScrollComboBox(this);
this->combo_weather->setToolTip("The weather that starts when the player steps on this spot.");
static const QString combo_weather_toolTip = Util::toHtmlParagraph("The weather that starts when the player steps on this spot.");
this->combo_weather->setToolTip(combo_weather_toolTip);
l_form_weather->addRow("Weather", this->combo_weather);
this->layout_contents->addLayout(l_form_weather);
@@ -705,8 +818,7 @@ void WeatherTriggerFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
// weather
this->combo_weather->addItems(project->coordEventWeatherNames);
populateDropdown(this->combo_weather, project->coordEventWeatherNames);
}
@@ -714,20 +826,19 @@ void WeatherTriggerFrame::populate(Project *project) {
void SignFrame::setup() {
EventFrame::setup();
this->label_id->setText("Sign");
// facing dir combo
QFormLayout *l_form_facing_dir = new QFormLayout();
this->combo_facing_dir = new NoScrollComboBox(this);
this->combo_facing_dir->setToolTip("The direction which the player must be facing\n"
"to be able to interact with this event.");
static const QString combo_facing_dir_toolTip = Util::toHtmlParagraph("The direction that the player must be facing to be able to interact with this event.");
this->combo_facing_dir->setToolTip(combo_facing_dir_toolTip);
l_form_facing_dir->addRow("Player Facing Direction", this->combo_facing_dir);
this->layout_contents->addLayout(l_form_facing_dir);
// script combo
QFormLayout *l_form_script = new QFormLayout();
this->combo_script = new NoScrollComboBox(this);
this->combo_script->setToolTip("The script which is executed with this event.");
static const QString combo_script_toolTip = Util::toHtmlParagraph("The script that is executed with this event.");
this->combo_script->setToolTip(combo_script_toolTip);
l_form_script->addRow("Script", this->combo_script);
this->layout_contents->addLayout(l_form_script);
@@ -774,10 +885,8 @@ void SignFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
// facing dir
this->combo_facing_dir->addItems(project->bgEventFacingDirections);
this->populateScriptDropdown(this->combo_script, project);
populateDropdown(this->combo_facing_dir, project->bgEventFacingDirections);
populateScriptDropdown(this->combo_script, project);
}
@@ -785,19 +894,19 @@ void SignFrame::populate(Project *project) {
void HiddenItemFrame::setup() {
EventFrame::setup();
this->label_id->setText("Hidden Item");
// item combo
QFormLayout *l_form_item = new QFormLayout();
this->combo_item = new NoScrollComboBox(this);
this->combo_item->setToolTip("The item to be given.");
static const QString combo_item_toolTip = Util::toHtmlParagraph("The item to be given.");
this->combo_item->setToolTip(combo_item_toolTip);
l_form_item->addRow("Item", this->combo_item);
this->layout_contents->addLayout(l_form_item);
// flag combo
QFormLayout *l_form_flag = new QFormLayout();
this->combo_flag = new NoScrollComboBox(this);
this->combo_flag->setToolTip("The flag which is set when the hidden item is picked up.");
static const QString combo_flag_toolTip = Util::toHtmlParagraph("The flag that is set when the hidden item is picked up.");
this->combo_flag->setToolTip(combo_flag_toolTip);
l_form_flag->addRow("Flag", this->combo_flag);
this->layout_contents->addLayout(l_form_flag);
@@ -806,7 +915,8 @@ void HiddenItemFrame::setup() {
QFormLayout *l_form_quantity = new QFormLayout(hideable_quantity);
l_form_quantity->setContentsMargins(0, 0, 0, 0);
this->spinner_quantity = new NoScrollSpinBox(hideable_quantity);
this->spinner_quantity->setToolTip("The number of items received when the hidden item is picked up.");
static const QString spinner_quantity_toolTip = Util::toHtmlParagraph("The number of items received when the hidden item is picked up.");
this->spinner_quantity->setToolTip(spinner_quantity_toolTip);
this->spinner_quantity->setMinimum(0x01);
this->spinner_quantity->setMaximum(0xFF);
l_form_quantity->addRow("Quantity", this->spinner_quantity);
@@ -817,7 +927,8 @@ void HiddenItemFrame::setup() {
QFormLayout *l_form_itemfinder = new QFormLayout(hideable_itemfinder);
l_form_itemfinder->setContentsMargins(0, 0, 0, 0);
this->check_itemfinder = new QCheckBox(hideable_itemfinder);
this->check_itemfinder->setToolTip("If checked, hidden item can only be picked up using the Itemfinder");
static const QString check_itemfinder_toolTip = Util::toHtmlParagraph("If checked, hidden item can only be picked up using the Itemfinder");
this->check_itemfinder->setToolTip(check_itemfinder_toolTip);
l_form_itemfinder->addRow("Requires Itemfinder", this->check_itemfinder);
this->layout_contents->addWidget(hideable_itemfinder);
@@ -853,8 +964,8 @@ void HiddenItemFrame::connectSignals(MainWindow *window) {
// underfoot
this->check_itemfinder->disconnect();
connect(this->check_itemfinder, &QCheckBox::stateChanged, [=](int state) {
this->hiddenItem->setUnderfoot(state == Qt::Checked);
connect(this->check_itemfinder, &QCheckBox::toggled, [=](bool checked) {
this->hiddenItem->setUnderfoot(checked);
this->hiddenItem->modify();
});
}
@@ -890,8 +1001,8 @@ void HiddenItemFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
this->combo_item->addItems(project->itemNames);
this->combo_flag->addItems(project->flagNames);
populateDropdown(this->combo_item, project->itemNames);
populateDropdown(this->combo_flag, project->flagNames);
}
@@ -899,16 +1010,14 @@ void HiddenItemFrame::populate(Project *project) {
void SecretBaseFrame::setup() {
EventFrame::setup();
this->label_id->setText("Secret Base");
this->spinner_z->setEnabled(false);
// item combo
QFormLayout *l_form_base_id = new QFormLayout();
this->combo_base_id = new NoScrollComboBox(this);
this->combo_base_id->setToolTip("The secret base id which is inside this secret\n"
"base entrance. Secret base ids are meant to be\n"
"unique to each and every secret base entrance.");
static const QString combo_base_id_toolTip = Util::toHtmlParagraph("The secret base id that is inside this secret base entrance. "
"Secret base ids are meant to be unique to each and every secret base entrance.");
this->combo_base_id->setToolTip(combo_base_id_toolTip);
l_form_base_id->addRow("Secret Base", this->combo_base_id);
this->layout_contents->addLayout(l_form_base_id);
@@ -944,7 +1053,7 @@ void SecretBaseFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
this->combo_base_id->addItems(project->secretBaseIds);
populateDropdown(this->combo_base_id, project->secretBaseIds);
}
@@ -952,15 +1061,14 @@ void SecretBaseFrame::populate(Project *project) {
void HealLocationFrame::setup() {
EventFrame::setup();
this->label_id->setText("Heal Location");
this->hideable_label_z->setVisible(false);
this->spinner_z->setVisible(false);
// ID
QFormLayout *l_form_id = new QFormLayout();
this->line_edit_id = new QLineEdit(this);
this->line_edit_id->setToolTip("The unique identifier for this heal location.");
static const QString line_edit_id_toolTip = Util::toHtmlParagraph("The unique identifier for this heal location.");
this->line_edit_id->setToolTip(line_edit_id_toolTip);
this->line_edit_id->setPlaceholderText(projectConfig.getIdentifier(ProjectIdentifier::define_heal_locations_prefix) + "MY_MAP");
l_form_id->addRow("ID", this->line_edit_id);
this->layout_contents->addLayout(l_form_id);
@@ -970,7 +1078,8 @@ void HealLocationFrame::setup() {
QFormLayout *l_form_respawn_map = new QFormLayout(hideable_respawn_map);
l_form_respawn_map->setContentsMargins(0, 0, 0, 0);
this->combo_respawn_map = new NoScrollComboBox(hideable_respawn_map);
this->combo_respawn_map->setToolTip("The map where the player will respawn after whiteout.");
static const QString combo_respawn_map_toolTip = Util::toHtmlParagraph("The map where the player will respawn after whiteout.");
this->combo_respawn_map->setToolTip(combo_respawn_map_toolTip);
l_form_respawn_map->addRow("Respawn Map", this->combo_respawn_map);
this->layout_contents->addWidget(hideable_respawn_map);
@@ -979,8 +1088,9 @@ void HealLocationFrame::setup() {
QFormLayout *l_form_respawn_npc = new QFormLayout(hideable_respawn_npc);
l_form_respawn_npc->setContentsMargins(0, 0, 0, 0);
this->combo_respawn_npc = new NoScrollComboBox(hideable_respawn_npc);
this->combo_respawn_npc->setToolTip("event_object ID of the NPC the player interacts with\n"
"upon respawning after whiteout.");
static const QString combo_respawn_npc_toolTip = Util::toHtmlParagraph("The Local ID name or number of the NPC the player "
"interacts with upon respawning after whiteout.");
this->combo_respawn_npc->setToolTip(combo_respawn_npc_toolTip);
l_form_respawn_npc->addRow("Respawn NPC", this->combo_respawn_npc);
this->layout_contents->addWidget(hideable_respawn_npc);
@@ -992,6 +1102,7 @@ void HealLocationFrame::connectSignals(MainWindow *window) {
if (this->connected) return;
EventFrame::connectSignals(window);
Project *project = window->editor->project;
this->line_edit_id->disconnect();
connect(this->line_edit_id, &QLineEdit::textChanged, [this](const QString &text) {
@@ -1000,16 +1111,29 @@ void HealLocationFrame::connectSignals(MainWindow *window) {
});
this->combo_respawn_map->disconnect();
connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this](const QString &text) {
this->healLocation->setRespawnMapName(text);
connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) {
this->healLocation->setRespawnMapName(mapName);
this->healLocation->modify();
populateIdNameDropdown(this->combo_respawn_npc, project, mapName, Event::Group::Object);
});
connect(window, &MainWindow::mapOpened, this, &HealLocationFrame::tryInvalidateIdDropdown, Qt::UniqueConnection);
this->combo_respawn_npc->disconnect();
connect(this->combo_respawn_npc, &QComboBox::currentTextChanged, [this](const QString &text) {
this->healLocation->setRespawnNPC(text);
this->healLocation->modify();
});
// This frame type displays map names, so when a new map is created we need to repopulate it.
connect(project, &Project::mapCreated, this, &EventFrame::invalidateValues, Qt::UniqueConnection);
}
void HealLocationFrame::tryInvalidateIdDropdown(Map *map) {
// If the heal locations's target map is opened then the names in this frame's ID dropdown may be changed.
// Make sure we update the frame next time it's opened.
if (map && this->healLocation && map->name() == this->healLocation->getRespawnMapName()) {
invalidateValues();
}
}
void HealLocationFrame::initialize() {
@@ -1033,7 +1157,8 @@ void HealLocationFrame::populate(Project *project) {
const QSignalBlocker blocker(this);
EventFrame::populate(project);
this->combo_respawn_map->addItems(project->mapNames);
// TODO: We should dynamically populate combo_respawn_npc with the local IDs of the respawn_map
// Same for warp IDs.
if (projectConfig.healLocationRespawnDataEnabled) {
populateDropdown(this->combo_respawn_map, project->mapNames);
populateIdNameDropdown(this->combo_respawn_npc, project, this->healLocation->getRespawnMapName(), Event::Group::Object);
}
}

115
src/ui/eventpixmapitem.cpp Normal file
View File

@@ -0,0 +1,115 @@
#include "eventpixmapitem.h"
#include "project.h"
#include "editcommands.h"
#include "mapruler.h"
#include "metatile.h"
EventPixmapItem::EventPixmapItem(Event *event)
: QGraphicsPixmapItem(event->getPixmap()),
m_basePixmap(pixmap()),
m_event(event)
{
m_event->setPixmapItem(this);
updatePixelPosition();
}
void EventPixmapItem::render(Project *project) {
if (!m_event)
return;
m_basePixmap = m_event->loadPixmap(project);
// If the base pixmap changes, the event's pixel position may change.
updatePixelPosition();
QPixmap pixmap = m_basePixmap;
if (m_selected) {
// Draw the selection rectangle
QPainter painter(&pixmap);
painter.setPen(Qt::magenta);
painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1);
}
setPixmap(pixmap);
emit rendered(m_basePixmap);
}
void EventPixmapItem::move(int dx, int dy) {
moveTo(m_event->getX() + dx,
m_event->getY() + dy);
}
void EventPixmapItem::moveTo(const QPoint &pos) {
moveTo(pos.x(), pos.y());
}
void EventPixmapItem::moveTo(int x, int y) {
bool changed = false;
if (m_event->getX() != x) {
m_event->setX(x);
emit xChanged(x);
changed = true;
}
if (m_event->getY() != y) {
m_event->setY(y);
emit yChanged(y);
changed = true;
}
if (changed) {
updatePixelPosition();
emit posChanged(x, y);
}
}
void EventPixmapItem::updatePixelPosition() {
setPos(m_event->getPixelX(), m_event->getPixelY());
}
void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {
if (m_active)
return;
m_active = true;
m_lastPos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos());
bool selectionToggle = mouseEvent->modifiers() & Qt::ControlModifier;
if (selectionToggle || !m_selected) {
// User is either toggling this selection on/off as part of a group selection,
// or they're newly selecting just this item.
m_selected = (selectionToggle) ? !m_selected : true;
emit selected(m_event, selectionToggle);
} else {
// This item is already selected and the user isn't toggling the selection, so there are 4 possibilities:
// 1. This is the only selected event, and the selection is pointless.
// 2. This is the only selected event, and they want to drag the item around.
// 3. There's a group selection, and they want to start a new selection with just this item.
// 4. There's a group selection, and they want to drag the group around.
// 'selectMapEvent' will immediately clear the rest of the selection, which supports #1-3 but prevents #4.
// To support #4 we set the flag below, and we only call 'selectMapEvent' on mouse release if no move occurred.
m_releaseSelectionQueued = true;
}
mouseEvent->accept();
}
void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) {
if (!m_active || !m_selected)
return;
QPoint pos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos());
if (pos == m_lastPos)
return;
m_releaseSelectionQueued = false;
emit dragged(m_event, m_lastPos, pos);
m_lastPos = pos;
}
void EventPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {
if (!m_active)
return;
m_active = false;
if (m_releaseSelectionQueued) {
m_releaseSelectionQueued = false;
if (Metatile::coordFromPixmapCoord(mouseEvent->scenePos()) == m_lastPos)
emit selected(m_event, false);
}
emit released(m_event, m_lastPos);
}

View File

@@ -2,22 +2,7 @@
#include "mapview.h"
#include "editor.h"
void GraphicsView::mousePressEvent(QMouseEvent *event) {
QGraphicsView::mousePressEvent(event);
if (editor) {
editor->eventsView_onMousePress(event);
}
}
void GraphicsView::mouseMoveEvent(QMouseEvent *event) {
QGraphicsView::mouseMoveEvent(event);
}
void GraphicsView::mouseReleaseEvent(QMouseEvent *event) {
QGraphicsView::mouseReleaseEvent(event);
}
void GraphicsView::moveEvent(QMoveEvent *event) {
void MapView::moveEvent(QMoveEvent *event) {
QGraphicsView::moveEvent(event);
QLabel *label_MapRulerStatus = findChild<QLabel *>("label_MapRulerStatus", Qt::FindDirectChildrenOnly);
if (label_MapRulerStatus && label_MapRulerStatus->isVisible())
@@ -79,3 +64,12 @@ Overlay * MapView::getOverlay(int layer) {
}
return overlay;
}
void ConnectionsView::keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) {
emit pressedDelete();
event->accept();
} else {
QGraphicsView::keyPressEvent(event);
}
}

View File

@@ -1,8 +1,6 @@
#include "ui_gridsettingsdialog.h"
#include "gridsettings.h"
// TODO: Save settings in config
const QMap<GridSettings::Style, QString> GridSettings::styleToName = {
{Style::Solid, "Solid"},
{Style::LargeDashes, "Large Dashes"},

View File

@@ -127,7 +127,12 @@ QImage getMetatileImage(
color.setAlpha(0);
tile_image.setColor(0, color.rgba());
metatile_painter.drawImage(origin, tile_image.mirrored(tile.xflip, tile.yflip));
#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
tile_image.flip(Util::getOrientation(tile.xflip, tile.yflip));
#else
tile_image = tile_image.mirrored(tile.xflip, tile.yflip);
#endif
metatile_painter.drawImage(origin, tile_image);
}
metatile_painter.end();

View File

@@ -714,19 +714,20 @@ void LayoutPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
}
void LayoutPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QPoint pos = Metatile::coordFromPixmapCoord(event->pos());
this->paint_tile_initial_x = this->straight_path_initial_x = pos.x();
this->paint_tile_initial_y = this->straight_path_initial_y = pos.y();
this->metatilePos = Metatile::coordFromPixmapCoord(event->pos());
this->paint_tile_initial_x = this->straight_path_initial_x = this->metatilePos.x();
this->paint_tile_initial_y = this->straight_path_initial_y = this->metatilePos.y();
emit startPaint(event, this);
emit mouseEvent(event, this);
}
void LayoutPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
QPoint pos = Metatile::coordFromPixmapCoord(event->pos());
if (pos != this->metatilePos) {
this->metatilePos = pos;
emit this->hoveredMapMetatileChanged(pos);
}
if (pos == this->metatilePos)
return;
this->metatilePos = pos;
emit hoveredMapMetatileChanged(pos);
emit mouseEvent(event, this);
}

63
src/ui/loadingscreen.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "loadingscreen.h"
#include "aboutporymap.h"
#include "ui_loadingscreen.h"
#include "qgifimage.h"
#include <QApplication>
PorymapLoadingScreen *porysplash = nullptr;
PorymapLoadingScreen::~PorymapLoadingScreen() {
delete ui;
}
PorymapLoadingScreen::PorymapLoadingScreen(QWidget *parent) : QWidget(parent), ui(new Ui::LoadingScreen) {
ui->setupUi(this);
this->setWindowFlags(Qt::FramelessWindowHint);
this->splashImage.load(":/images/porysplash.gif");
connect(&this->timer, &QTimer::timeout, this, &PorymapLoadingScreen::updateFrame);
}
void PorymapLoadingScreen::start() {
static bool shownVersion = false;
if (!shownVersion) {
this->ui->labelVersion->setText(AboutPorymap::getVersionString());
shownVersion = true;
}
this->frame = 0;
this->ui->labelPixmap->setPixmap(QPixmap::fromImage(this->splashImage.frame(this->frame)));
this->ui->labelText->setText("");
this->timer.start(120);
this->show();
}
void PorymapLoadingScreen::stop () {
this->timer.stop();
this->hide();
}
void PorymapLoadingScreen::setPixmap(QPixmap pixmap) {
if (!this->isVisible()) return;
this->ui->labelPixmap->setPixmap(pixmap);
}
void PorymapLoadingScreen::showMessage(QString text) {
if (!this->isVisible()) return;
this->ui->labelText->setText(text.mid(text.lastIndexOf("/") + 1));
QApplication::processEvents();
}
void PorymapLoadingScreen::updateFrame() {
this->frame = (this->frame + 1) % this->splashImage.frameCount();
this->setPixmap(QPixmap::fromImage(this->splashImage.frame(this->frame)));
QApplication::processEvents();
}

View File

@@ -20,12 +20,11 @@ MapHeaderForm::MapHeaderForm(QWidget *parent)
connect(ui->comboBox_Weather, &QComboBox::currentTextChanged, this, &MapHeaderForm::onWeatherChanged);
connect(ui->comboBox_Type, &QComboBox::currentTextChanged, this, &MapHeaderForm::onTypeChanged);
connect(ui->comboBox_BattleScene, &QComboBox::currentTextChanged, this, &MapHeaderForm::onBattleSceneChanged);
connect(ui->checkBox_RequiresFlash, &QCheckBox::stateChanged, this, &MapHeaderForm::onRequiresFlashChanged);
connect(ui->checkBox_ShowLocationName, &QCheckBox::stateChanged, this, &MapHeaderForm::onShowLocationNameChanged);
connect(ui->checkBox_AllowRunning, &QCheckBox::stateChanged, this, &MapHeaderForm::onAllowRunningChanged);
connect(ui->checkBox_AllowBiking, &QCheckBox::stateChanged, this, &MapHeaderForm::onAllowBikingChanged);
connect(ui->checkBox_AllowEscaping, &QCheckBox::stateChanged, this, &MapHeaderForm::onAllowEscapingChanged);
connect(ui->checkBox_RequiresFlash, &QCheckBox::toggled, this, &MapHeaderForm::onRequiresFlashChanged);
connect(ui->checkBox_ShowLocationName, &QCheckBox::toggled, this, &MapHeaderForm::onShowLocationNameChanged);
connect(ui->checkBox_AllowRunning, &QCheckBox::toggled, this, &MapHeaderForm::onAllowRunningChanged);
connect(ui->checkBox_AllowBiking, &QCheckBox::toggled, this, &MapHeaderForm::onAllowBikingChanged);
connect(ui->checkBox_AllowEscaping, &QCheckBox::toggled, this, &MapHeaderForm::onAllowEscapingChanged);
connect(ui->spinBox_FloorNumber, QOverload<int>::of(&QSpinBox::valueChanged), this, &MapHeaderForm::onFloorNumberChanged);
@@ -175,19 +174,29 @@ void MapHeaderForm::updateLocationName() {
}
// Set data in UI
void MapHeaderForm::setSong(const QString &song) { ui->comboBox_Song->setCurrentText(song); }
void MapHeaderForm::setLocation(const QString &location) { ui->comboBox_Location->setCurrentText(location); }
void MapHeaderForm::setLocationName(const QString &locationName) { ui->lineEdit_LocationName->setText(locationName); }
void MapHeaderForm::setSong(const QString &song) { setText(ui->comboBox_Song, song); }
void MapHeaderForm::setLocation(const QString &location) { setText(ui->comboBox_Location, location); }
void MapHeaderForm::setLocationName(const QString &locationName) { setText(ui->lineEdit_LocationName, locationName); }
void MapHeaderForm::setRequiresFlash(bool requiresFlash) { ui->checkBox_RequiresFlash->setChecked(requiresFlash); }
void MapHeaderForm::setWeather(const QString &weather) { ui->comboBox_Weather->setCurrentText(weather); }
void MapHeaderForm::setType(const QString &type) { ui->comboBox_Type->setCurrentText(type); }
void MapHeaderForm::setBattleScene(const QString &battleScene) { ui->comboBox_BattleScene->setCurrentText(battleScene); }
void MapHeaderForm::setWeather(const QString &weather) { setText(ui->comboBox_Weather, weather); }
void MapHeaderForm::setType(const QString &type) { setText(ui->comboBox_Type, type); }
void MapHeaderForm::setBattleScene(const QString &battleScene) { setText(ui->comboBox_BattleScene, battleScene); }
void MapHeaderForm::setShowsLocationName(bool showsLocationName) { ui->checkBox_ShowLocationName->setChecked(showsLocationName); }
void MapHeaderForm::setAllowsRunning(bool allowsRunning) { ui->checkBox_AllowRunning->setChecked(allowsRunning); }
void MapHeaderForm::setAllowsBiking(bool allowsBiking) { ui->checkBox_AllowBiking->setChecked(allowsBiking); }
void MapHeaderForm::setAllowsEscaping(bool allowsEscaping) { ui->checkBox_AllowEscaping->setChecked(allowsEscaping); }
void MapHeaderForm::setFloorNumber(int floorNumber) { ui->spinBox_FloorNumber->setValue(floorNumber); }
// If we always call setText / setCurrentText the user's cursor may move to the end of the text while they're typing.
void MapHeaderForm::setText(QComboBox *combo, const QString &text) const {
if (combo->currentText() != text)
combo->setCurrentText(text);
}
void MapHeaderForm::setText(QLineEdit *lineEdit, const QString &text) const {
if (lineEdit->text() != text)
lineEdit->setText(text);
}
// Read data from UI
QString MapHeaderForm::song() const { return ui->comboBox_Song->currentText(); }
QString MapHeaderForm::location() const { return ui->comboBox_Location->currentText(); }
@@ -207,11 +216,11 @@ void MapHeaderForm::onSongUpdated(const QString &song) { if (m_hea
void MapHeaderForm::onWeatherChanged(const QString &weather) { if (m_header) m_header->setWeather(weather); }
void MapHeaderForm::onTypeChanged(const QString &type) { if (m_header) m_header->setType(type); }
void MapHeaderForm::onBattleSceneChanged(const QString &battleScene) { if (m_header) m_header->setBattleScene(battleScene); }
void MapHeaderForm::onRequiresFlashChanged(int selected) { if (m_header) m_header->setRequiresFlash(selected == Qt::Checked); }
void MapHeaderForm::onShowLocationNameChanged(int selected) { if (m_header) m_header->setShowsLocationName(selected == Qt::Checked); }
void MapHeaderForm::onAllowRunningChanged(int selected) { if (m_header) m_header->setAllowsRunning(selected == Qt::Checked); }
void MapHeaderForm::onAllowBikingChanged(int selected) { if (m_header) m_header->setAllowsBiking(selected == Qt::Checked); }
void MapHeaderForm::onAllowEscapingChanged(int selected) { if (m_header) m_header->setAllowsEscaping(selected == Qt::Checked); }
void MapHeaderForm::onRequiresFlashChanged(bool enabled) { if (m_header) m_header->setRequiresFlash(enabled); }
void MapHeaderForm::onShowLocationNameChanged(bool enabled) { if (m_header) m_header->setShowsLocationName(enabled); }
void MapHeaderForm::onAllowRunningChanged(bool enabled) { if (m_header) m_header->setAllowsRunning(enabled); }
void MapHeaderForm::onAllowBikingChanged(bool enabled) { if (m_header) m_header->setAllowsBiking(enabled); }
void MapHeaderForm::onAllowEscapingChanged(bool enabled) { if (m_header) m_header->setAllowsEscaping(enabled); }
void MapHeaderForm::onFloorNumberChanged(int offset) { if (m_header) m_header->setFloorNumber(offset); }
void MapHeaderForm::onLocationChanged(const QString &location) {
if (m_header) m_header->setLocation(location);

View File

@@ -55,10 +55,24 @@ MapImageExporter::MapImageExporter(QWidget *parent, Project *project, Map *map,
connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage);
connect(ui->pushButton_Cancel, &QPushButton::pressed, this, &MapImageExporter::close);
// Update the map selector when the text changes.
// We don't use QComboBox::currentTextChanged to avoid unnecessary re-rendering.
connect(ui->comboBox_MapSelection, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MapImageExporter::updateMapSelection);
connect(ui->comboBox_MapSelection->lineEdit(), &QLineEdit::editingFinished, this, &MapImageExporter::updateMapSelection);
connect(ui->comboBox_MapSelection, &NoScrollComboBox::editingFinished, this, &MapImageExporter::updateMapSelection);
connect(ui->checkBox_Objects, &QCheckBox::toggled, this, &MapImageExporter::setShowObjects);
connect(ui->checkBox_Warps, &QCheckBox::toggled, this, &MapImageExporter::setShowWarps);
connect(ui->checkBox_BGs, &QCheckBox::toggled, this, &MapImageExporter::setShowBgs);
connect(ui->checkBox_Triggers, &QCheckBox::toggled, this, &MapImageExporter::setShowTriggers);
connect(ui->checkBox_HealLocations, &QCheckBox::toggled, this, &MapImageExporter::setShowHealLocations);
connect(ui->checkBox_AllEvents, &QCheckBox::toggled, this, &MapImageExporter::setShowAllEvents);
connect(ui->checkBox_ConnectionUp, &QCheckBox::toggled, this, &MapImageExporter::setShowConnectionUp);
connect(ui->checkBox_ConnectionDown, &QCheckBox::toggled, this, &MapImageExporter::setShowConnectionDown);
connect(ui->checkBox_ConnectionLeft, &QCheckBox::toggled, this, &MapImageExporter::setShowConnectionLeft);
connect(ui->checkBox_ConnectionRight, &QCheckBox::toggled, this, &MapImageExporter::setShowConnectionRight);
connect(ui->checkBox_AllConnections, &QCheckBox::toggled, this, &MapImageExporter::setShowAllConnections);
connect(ui->checkBox_Collision, &QCheckBox::toggled, this, &MapImageExporter::setShowCollision);
connect(ui->checkBox_Grid, &QCheckBox::toggled, this, &MapImageExporter::setShowGrid);
connect(ui->checkBox_Border, &QCheckBox::toggled, this, &MapImageExporter::setShowBorder);
connect(ui->checkBox_DisablePreviewScaling, &QCheckBox::toggled, this, &MapImageExporter::setDisablePreviewScaling);
connect(ui->checkBox_DisablePreviewUpdates, &QCheckBox::toggled, this, &MapImageExporter::setDisablePreviewUpdates);
ui->graphicsView_Preview->setFocus();
}
@@ -96,8 +110,7 @@ void MapImageExporter::setModeSpecificUi() {
}
if (m_mode == ImageExporterMode::Timelapse) {
// TODO: At the moment edit history for events (and the DraggablePixmapItem class)
// explicitly depend on the editor and assume their map is currently open.
// TODO: At the moment edit history for events explicitly depend on the editor and assume their map is currently open.
// Other edit commands rely on this more subtly, like triggering API callbacks or
// spending time rendering their layout (which can make creating timelapses very slow).
// Until this is resolved, the selected map/layout must remain the same as in the editor.
@@ -589,9 +602,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap() {
QMargins MapImageExporter::getMargins(const Map *map) {
QMargins margins;
if (m_settings.showBorder) {
// The border may technically extend beyond BORDER_DISTANCE, but when the border is painted
// we will be limiting it to the visible sight range.
margins = QMargins(BORDER_DISTANCE, BORDER_DISTANCE, BORDER_DISTANCE, BORDER_DISTANCE) * 16;
margins = m_project->getMetatileViewDistance() * 16;
} else if (map && connectionsEnabled()) {
for (const auto &connection : map->getConnections()) {
const QString dir = connection->direction();
@@ -632,15 +643,12 @@ void MapImageExporter::paintBorder(QPainter *painter, Layout *layout) {
layout->renderBorder(true);
// Clip parts of the border that would be beyond player visibility.
QRect visibleArea(0, 0, layout->getWidth() * 16, layout->getHeight() * 16);
visibleArea += (QMargins(BORDER_DISTANCE, BORDER_DISTANCE, BORDER_DISTANCE, BORDER_DISTANCE) * 16);
painter->save();
painter->setClipRect(visibleArea);
painter->setClipRect(layout->getVisibleRect());
int borderHorzDist = layout->getBorderDrawWidth();
int borderVertDist = layout->getBorderDrawHeight();
for (int y = -borderVertDist; y < layout->getHeight() + borderVertDist; y += layout->getBorderHeight())
for (int x = -borderHorzDist; x < layout->getWidth() + borderHorzDist; x += layout->getBorderWidth()) {
const QMargins borderMargins = layout->getBorderMargins();
for (int y = -borderMargins.top(); y < layout->getHeight() + borderMargins.bottom(); y += layout->getBorderHeight())
for (int x = -borderMargins.left(); x < layout->getWidth() + borderMargins.right(); x += layout->getBorderWidth()) {
// 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;
@@ -719,127 +727,123 @@ void MapImageExporter::setConnectionDirectionEnabled(const QString &dir, bool en
}
}
void MapImageExporter::on_checkBox_Collision_stateChanged(int state) {
m_settings.showCollision = (state == Qt::Checked);
void MapImageExporter::setShowCollision(bool checked) {
m_settings.showCollision = checked;
updatePreview();
}
void MapImageExporter::on_checkBox_Grid_stateChanged(int state) {
m_settings.showGrid = (state == Qt::Checked);
void MapImageExporter::setShowGrid(bool checked) {
m_settings.showGrid = checked;
updatePreview();
}
void MapImageExporter::on_checkBox_Border_stateChanged(int state) {
m_settings.showBorder = (state == Qt::Checked);
void MapImageExporter::setShowBorder(bool checked) {
m_settings.showBorder = checked;
updatePreview();
}
void MapImageExporter::on_checkBox_Objects_stateChanged(int state) {
setEventGroupEnabled(Event::Group::Object, state == Qt::Checked);
void MapImageExporter::setShowObjects(bool checked) {
setEventGroupEnabled(Event::Group::Object, checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Warps_stateChanged(int state) {
setEventGroupEnabled(Event::Group::Warp, state == Qt::Checked);
void MapImageExporter::setShowWarps(bool checked) {
setEventGroupEnabled(Event::Group::Warp, checked);
updatePreview();
}
void MapImageExporter::on_checkBox_BGs_stateChanged(int state) {
setEventGroupEnabled(Event::Group::Bg, state == Qt::Checked);
void MapImageExporter::setShowBgs(bool checked) {
setEventGroupEnabled(Event::Group::Bg, checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Triggers_stateChanged(int state) {
setEventGroupEnabled(Event::Group::Coord, state == Qt::Checked);
void MapImageExporter::setShowTriggers(bool checked) {
setEventGroupEnabled(Event::Group::Coord, checked);
updatePreview();
}
void MapImageExporter::on_checkBox_HealLocations_stateChanged(int state) {
setEventGroupEnabled(Event::Group::Heal, state == Qt::Checked);
void MapImageExporter::setShowHealLocations(bool checked) {
setEventGroupEnabled(Event::Group::Heal, checked);
updatePreview();
}
// Shortcut setting for enabling all events
void MapImageExporter::on_checkBox_AllEvents_stateChanged(int state) {
bool on = (state == Qt::Checked);
void MapImageExporter::setShowAllEvents(bool checked) {
const QSignalBlocker b_Objects(ui->checkBox_Objects);
ui->checkBox_Objects->setChecked(on);
ui->checkBox_Objects->setDisabled(on);
setEventGroupEnabled(Event::Group::Object, on);
ui->checkBox_Objects->setChecked(checked);
ui->checkBox_Objects->setDisabled(checked);
setEventGroupEnabled(Event::Group::Object, checked);
const QSignalBlocker b_Warps(ui->checkBox_Warps);
ui->checkBox_Warps->setChecked(on);
ui->checkBox_Warps->setDisabled(on);
setEventGroupEnabled(Event::Group::Warp, on);
ui->checkBox_Warps->setChecked(checked);
ui->checkBox_Warps->setDisabled(checked);
setEventGroupEnabled(Event::Group::Warp, checked);
const QSignalBlocker b_BGs(ui->checkBox_BGs);
ui->checkBox_BGs->setChecked(on);
ui->checkBox_BGs->setDisabled(on);
setEventGroupEnabled(Event::Group::Bg, on);
ui->checkBox_BGs->setChecked(checked);
ui->checkBox_BGs->setDisabled(checked);
setEventGroupEnabled(Event::Group::Bg, checked);
const QSignalBlocker b_Triggers(ui->checkBox_Triggers);
ui->checkBox_Triggers->setChecked(on);
ui->checkBox_Triggers->setDisabled(on);
setEventGroupEnabled(Event::Group::Coord, on);
ui->checkBox_Triggers->setChecked(checked);
ui->checkBox_Triggers->setDisabled(checked);
setEventGroupEnabled(Event::Group::Coord, checked);
const QSignalBlocker b_HealLocations(ui->checkBox_HealLocations);
ui->checkBox_HealLocations->setChecked(on);
ui->checkBox_HealLocations->setDisabled(on);
setEventGroupEnabled(Event::Group::Heal, on);
ui->checkBox_HealLocations->setChecked(checked);
ui->checkBox_HealLocations->setDisabled(checked);
setEventGroupEnabled(Event::Group::Heal, checked);
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionUp_stateChanged(int state) {
setConnectionDirectionEnabled("up", state == Qt::Checked);
void MapImageExporter::setShowConnectionUp(bool checked) {
setConnectionDirectionEnabled("up", checked);
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionDown_stateChanged(int state) {
setConnectionDirectionEnabled("down", state == Qt::Checked);
void MapImageExporter::setShowConnectionDown(bool checked) {
setConnectionDirectionEnabled("down", checked);
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionLeft_stateChanged(int state) {
setConnectionDirectionEnabled("left", state == Qt::Checked);
void MapImageExporter::setShowConnectionLeft(bool checked) {
setConnectionDirectionEnabled("left", checked);
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionRight_stateChanged(int state) {
setConnectionDirectionEnabled("right", state == Qt::Checked);
void MapImageExporter::setShowConnectionRight(bool checked) {
setConnectionDirectionEnabled("right", checked);
updatePreview();
}
// Shortcut setting for enabling all connection directions
void MapImageExporter::on_checkBox_AllConnections_stateChanged(int state) {
bool on = (state == Qt::Checked);
void MapImageExporter::setShowAllConnections(bool checked) {
const QSignalBlocker b_Up(ui->checkBox_ConnectionUp);
ui->checkBox_ConnectionUp->setChecked(on);
ui->checkBox_ConnectionUp->setDisabled(on);
setConnectionDirectionEnabled("up", on);
ui->checkBox_ConnectionUp->setChecked(checked);
ui->checkBox_ConnectionUp->setDisabled(checked);
setConnectionDirectionEnabled("up", checked);
const QSignalBlocker b_Down(ui->checkBox_ConnectionDown);
ui->checkBox_ConnectionDown->setChecked(on);
ui->checkBox_ConnectionDown->setDisabled(on);
setConnectionDirectionEnabled("down", on);
ui->checkBox_ConnectionDown->setChecked(checked);
ui->checkBox_ConnectionDown->setDisabled(checked);
setConnectionDirectionEnabled("down", checked);
const QSignalBlocker b_Left(ui->checkBox_ConnectionLeft);
ui->checkBox_ConnectionLeft->setChecked(on);
ui->checkBox_ConnectionLeft->setDisabled(on);
setConnectionDirectionEnabled("left", on);
ui->checkBox_ConnectionLeft->setChecked(checked);
ui->checkBox_ConnectionLeft->setDisabled(checked);
setConnectionDirectionEnabled("left", checked);
const QSignalBlocker b_Right(ui->checkBox_ConnectionRight);
ui->checkBox_ConnectionRight->setChecked(on);
ui->checkBox_ConnectionRight->setDisabled(on);
setConnectionDirectionEnabled("right", on);
ui->checkBox_ConnectionRight->setChecked(checked);
ui->checkBox_ConnectionRight->setDisabled(checked);
setConnectionDirectionEnabled("right", checked);
updatePreview();
}
void MapImageExporter::on_checkBox_DisablePreviewScaling_stateChanged(int state) {
m_settings.disablePreviewScaling = (state == Qt::Checked);
void MapImageExporter::setDisablePreviewScaling(bool checked) {
m_settings.disablePreviewScaling = checked;
if (m_settings.disablePreviewScaling) {
ui->graphicsView_Preview->resetTransform();
} else {
@@ -847,8 +851,8 @@ void MapImageExporter::on_checkBox_DisablePreviewScaling_stateChanged(int state)
}
}
void MapImageExporter::on_checkBox_DisablePreviewUpdates_stateChanged(int state) {
m_settings.disablePreviewUpdates = (state == Qt::Checked);
void MapImageExporter::setDisablePreviewUpdates(bool checked) {
m_settings.disablePreviewUpdates = checked;
if (m_settings.disablePreviewUpdates) {
if (m_timelapseMovie) {
m_timelapseMovie->stop();

View File

@@ -93,7 +93,7 @@ void MapListToolBar::setEmptyFoldersVisible(bool visible) {
}
// Update tool tip to reflect what will happen if the button is pressed.
const QString toolTip = QString("%1 empty folders in the list.").arg(visible ? "Hide" : "Show");
const QString toolTip = Util::toHtmlParagraph(QString("%1 empty folders in the list.").arg(visible ? "Hide" : "Show"));
ui->button_ToggleEmptyFolders->setToolTip(toolTip);
const QSignalBlocker b(ui->button_ToggleEmptyFolders);
@@ -121,7 +121,9 @@ void MapListToolBar::applyFilter(const QString &filterText) {
return;
const QSignalBlocker b(ui->lineEdit_filterBox);
ui->lineEdit_filterBox->setText(filterText);
if (ui->lineEdit_filterBox->text() != filterText) {
ui->lineEdit_filterBox->setText(filterText);
}
// The clear button does not properly disappear when filterText is empty.
// It seems like this is because blocking the QLineEdit's signals prevents

View File

@@ -52,19 +52,22 @@ RecentErrorMessage::RecentErrorMessage(const QString &message, QWidget *parent)
setDetailedText(getMostRecentError());
}
int RecentErrorMessage::show(const QString &message, QWidget *parent) {
RecentErrorMessage msgBox(message, parent);
return msgBox.exec();
void RecentErrorMessage::show(const QString &message, QWidget *parent) {
auto msgBox = new RecentErrorMessage(message, parent);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->open();
};
int ErrorMessage::show(const QString &message, QWidget *parent) {
ErrorMessage msgBox(message, parent);
return msgBox.exec();
void ErrorMessage::show(const QString &message, QWidget *parent) {
auto msgBox = new ErrorMessage(message, parent);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->open();
};
int WarningMessage::show(const QString &message, QWidget *parent) {
WarningMessage msgBox(message, parent);
return msgBox.exec();
void WarningMessage::show(const QString &message, QWidget *parent) {
auto msgBox = new WarningMessage(message, parent);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->open();
};
int QuestionMessage::show(const QString &message, QWidget *parent) {
@@ -72,7 +75,8 @@ int QuestionMessage::show(const QString &message, QWidget *parent) {
return msgBox.exec();
};
int InfoMessage::show(const QString &message, QWidget *parent) {
InfoMessage msgBox(message, parent);
return msgBox.exec();
void InfoMessage::show(const QString &message, QWidget *parent) {
auto msgBox = new InfoMessage(message, parent);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->open();
};

View File

@@ -1,6 +1,7 @@
#include "config.h"
#include "metatilelayersitem.h"
#include "imageproviders.h"
#include "utility.h"
#include <QPainter>
static const QList<QPoint> tilePositions = {
@@ -28,7 +29,11 @@ void MetatileLayersItem::draw() {
for (int i = 0; i < numTiles; i++) {
Tile tile = this->metatile->tiles.at(i);
QImage tileImage = getPalettedTileImage(tile.tileId, this->primaryTileset, this->secondaryTileset, tile.palette, true)
#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
.flipped(Util::getOrientation(tile.xflip, tile.yflip))
#else
.mirrored(tile.xflip, tile.yflip)
#endif
.scaled(16, 16);
painter.drawImage(tilePositions.at(i) * 16, tileImage);
}

View File

@@ -5,18 +5,31 @@
#include "movablerect.h"
#include "utility.h"
MovableRect::MovableRect(bool *enabled, int width, int height, QRgb color)
: QGraphicsRectItem(0, 0, width, height)
MovableRect::MovableRect(bool *enabled, const QRectF &rect, const QRgb &color)
: QGraphicsRectItem(rect),
enabled(enabled),
baseRect(rect),
color(color)
{
this->enabled = enabled;
this->color = color;
this->setVisible(*enabled);
updateVisibility();
}
/// Center rect on grid position (x, y)
void MovableRect::updateLocation(int x, int y) {
this->setRect((x * 16) - this->rect().width() / 2 + 8, (y * 16) - this->rect().height() / 2 + 8, this->rect().width(), this->rect().height());
this->setVisible(*this->enabled);
setRect(this->baseRect.x() + (x * 16),
this->baseRect.y() + (y * 16),
this->baseRect.width(),
this->baseRect.height());
updateVisibility();
}
void MovableRect::setActive(bool active) {
this->active = active;
updateVisibility();
}
void MovableRect::updateVisibility() {
setVisible(*this->enabled && this->active);
}
/******************************************************************************
@@ -25,9 +38,8 @@ void MovableRect::updateLocation(int x, int y) {
ResizableRect::ResizableRect(QObject *parent, bool *enabled, int width, int height, QRgb color)
: QObject(parent),
MovableRect(enabled, width * 16, height * 16, color)
MovableRect(enabled, QRect(0, 0, width * 16, height * 16), color)
{
setZValue(0xFFFFFFFF); // ensure on top of view
setAcceptHoverEvents(true);
setFlags(this->flags() | QGraphicsItem::ItemIsMovable);
}

Some files were not shown because too many files have changed in this diff Show More