Merge branch 'master' into label-copy

This commit is contained in:
GriffinR 2022-06-30 00:41:10 -04:00 committed by GitHub
commit 441db4274e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 5372 additions and 2763 deletions

View file

@ -7,7 +7,11 @@ and this project somewhat adheres to [Semantic Versioning](https://semver.org/sp
The **"Breaking Changes"** listed below are changes that have been made in the decompilation projects (e.g. pokeemerald), which porymap requires in order to work properly. If porymap is used on a project that is not up-to-date with the breaking changes, then porymap will likely break or behave improperly.
## [Unreleased]
### Breaking Changes
- Proper support for pokefirered's clone objects was added, which requires the changes made in [pokefirered/#484](https://github.com/pret/pokefirered/pull/484).
### Added
- Add Copy/Paste for metatiles in the Tileset Editor.
- Add ability to set the opacity of the scripting overlay.
- Add ability to get/set map header properties and read tile pixel data via the API.
- Add button to copy the full metatile label to the clipboard in the Tileset Editor.
@ -16,12 +20,15 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- If an object event is inanimate, it will always render using its first frame.
- Only log "Unknown custom script function" when a registered script function is not present in any script.
- Unused metatile attribute bits that are set are preserved instead of being cleared.
- The wild encounter editor is automatically disabled if the encounter JSON data cannot be read
- Overhauled the region map editor, adding support for tilemaps, and significant customization. Also now supports pokefirered.
### Fixed
- Fix cursor tile outline not updating at the end of a dragged selection.
- Fix cursor tile and player view outlines exiting map bounds while painting.
- Fix cursor tile and player view outlines not updating immediately when toggled in Collision view.
- Fix selected space not updating while painting in Collision view.
- Fix the map music dropdown being empty when importing a map from Advance Map.
## [4.5.0] - 2021-12-26
### Added

View file

@ -60,7 +60,7 @@ Object events are typically used for NPCs (non-player-characters). More technic
Object Event Properties
Id
This is the local id of the object in the map. Some script values use this local id to specify object when using scripting commands such as `applymovement`.
This is the local id of the object in the map. Some script values use this local id to specify an object when using scripting commands such as `applymovement`.
Sprite
The sprite that is used by the object.
@ -83,8 +83,27 @@ Trainer Type
Sight Radius or Berry Tree ID
If the object is a trainer, this property control how many tiles the trainer can see to spot the player for battle. If the object is a berry tree, this specifies the global id of the berry tree. Each berry tree in the game has a unique berry tree id.
In Connection
Exclusive to pokefirered. Used to replace objects that are visible in a map's connection with their corresponding object on the connecting map. When checked, these objects will make odd use of other fields; its trainer type value will be the connecting map number, its Sight Radius / Berry Tree Id will be the connecting map group, and its z coordinate will be the object's local id on the connecting map.
Clone Object Events
-------------------
Clone Object events are a special type of object that inherits its properties from another Object event. They are used in-game to load objects that are visible in the connecting area of adjacent maps. The targeted object to clone is specified by id and map name. If the targeted object does not exist, or it's also a clone, the sprite for graphics id 0 will be displayed instead. Double-clicking on a Clone Object will open the targeted map with the targeted object selected. This event type is exclusive to pokefirered projects; the code to process them does not exist in pokeemerald/pokeruby.
.. figure:: images/editing-map-events/event-clone-object.png
:alt: Clone Object Event Properties
Clone Object Event Properties
Id
This is the local id of the object in the map. Some script values use this local id to specify an object when using scripting commands such as `applymovement`.
Sprite
The sprite that is used by the object. Clone Objects inherit their sprite from the targeted object, so this cannot be edited. This field is not actually read by the game.
Target Local Id
The local id of the object to be cloned.
Target Map
The name of the map the object to be cloned is on.
.. _event-warps:

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

View file

@ -2,24 +2,73 @@
The Region Map Editor
*********************
This is where you edit the region map for your game. To open the region map
editor, navigate to *Tools -> Region Map Editor* from porymap's main window.
This is where you edit the region maps for your game. You are able to edit the
background tilemap, the layout of map sections, and the array of map section entries
which determines the dimensions of each section.
.. note::
The region map editor is currently only available for pokeemerald and pokeruby.
To open the region map editor, navigate to *Tools -> Region Map Editor* from
porymap's main window. There is also a keyboard shortcut which is by default ``Ctrl+M``.
When you first open the region map editor, your window will look like this:
When you first open the region map editor, you will need to configure porymap to
read your region map data. There are defaults for every base game project available
which should be sufficient for most users.
.. figure:: images/region-map-editor/rme-new-window.png
:scale: 75%
.. figure:: images/region-map-editor/new-configure-window.png
:align: center
:width: 75%
:alt: RME Window
Region Maps Configurator
Porymap supports multiple region maps for any project.
By default, pokeemerald and pokefirered use this feature.
For a more custom region map, you can use the *Add Region Map...* button to
create a new region map configuration from scratch. You can also double-click on any existing
region map in the list to bring this window up to make changes.
.. figure:: images/region-map-editor/rme-config-properties.png
:align: center
:width: 50%
:alt: RME Config Prop
Region Map Properties Window
This window has many options for users to define:
.. csv-table::
:header: Field,Explanation,Restrictions
:widths: 10, 30, 20
alias,something for porymap to distinguish between your maps,unique & valid json string
**Tilemap Properties**,,
format,format of the tiles,Plain *or* 4bpp *or* 8bpp
width,width *in tiles* of the tilemap,16 *or* 32 *or* 64 *or* 128
height,height *in tiles* of the tilemap,valid corresponding height based on width
tileset path,the relative path to the tile image from project root,valid filepath string
tilemap path,the relative path to the tilemap binary from project root,valid filepath string
palette path,*optional* relative path to ``.pal`` file from project root,valid filepath string
**Layout Properties**,*can be unchecked for maps without layouts*,
format,the format to read the layout file,C array *or* binary
layout path,the relative path from project root to layout file,valid filepath string
width,the width of the layout,non-negative integer
left offset,the position on the tilemap which defines layout x=0,width + left offset < tilemap width
height,the height of the layout,non-negative integer
top offset,the position on the tilemap which defines layout y=0,height + top offset < tilemap height
When you are finished configuring your region maps, you can select *OK*. This will
display the main editor window.
.. figure:: images/region-map-editor/rme-main-window.png
:align: center
:width: 75%
:alt: RME Config Prop
Region Map Editor Window
This window is split vertically--the region map editing is done at the top,
while the zoomed-in city maps are edited at the bottom. You can use the
sliders to zoom in and out on each of the view panes. You will notice
This window has a combobox labeled "Region" which you can use to select the current
region map you want to edit.
You will notice
that there are three different tabs above the image of the region map
(:ref:`Background Image <background-image-tab>`,
:ref:`Map Layout <map-layout-tab>`,
@ -39,24 +88,13 @@ are unhappy with what you have done, you can undo (``Ctrl+Z`` or *Edit -> Undo*)
and redo (``Ctrl+Y`` or *Edit -> Redo*) your changes. Right-clicking on the map
image will select the tile under your mouse from the tile selector.
If your tilemap format is not "Plain", then you can also select the palette,
h-flip, and v-flip of any tile you are painting with.
If you want to clear the background image, *Edit -> Clear Background Image*
will set all tiles to the first tile in the tile selector.
.. figure:: images/region-map-editor/rme-painting-image.gif
:scale: 75%
:align: center
:alt: RME Paint
Drawing on the Region Map Image
It is likely that you will want to use your own tiles for your region map. You
can import a tile image by navigating to *Tools -> Import Region Map Image Tiles*.
There are strict requirements for your region map tile image. It must (1) be
indexed with a 256 color palette\*, (2) be composed of 8x8 pixel tiles, (3) have 256
or fewer tiles.
\* While the region map tile image requires a 256-color palette, the image only
uses the 32 colors beginning at index 112 in the palette.
You can use the sliders to zoom in and out on each of the view panes.
.. _map-layout-tab:
@ -67,9 +105,9 @@ The layout tab is where map sections are placed on the region map. When the
player looks at the region map in-game, the layout determines the map under the
cursor.
.. figure:: images/region-map-editor/rme-layout-tab.png
:scale: 75%
.. figure:: images/region-map-editor/rme-new-layout-tab.png
:align: center
:width: 75%
:alt: RME Layout
RME Layout Tab
@ -78,29 +116,15 @@ To modify the region map layout, select a position by clicking on the map image
and higlighting a single square. The "Map Section" combobox will be populated
with all of the map sections defined in ``include/constants/region_map_sections.h``.
Select the map section you want to associate with the selected position on the
region map. To change the popup name of the map section when you enter the map,
type it into the "Map Name" box. The popup name is tied to the map section, so
each layout square with the same map section will share a name.
region map.
If you want to start from a blank layout, *Edit -> Clear Map Layout* will set
all layout squares to the value of ``MAPSEC_NONE``.
There are a couple of tools which make editing multiple layout squares simultaneously easier.
When adding new region map sections, the layout will be affected. This is
because the layout is stored as a binary file and uses the raw value of each
map section. In order to fix your layout to account for this, you can swap two
values for the entire layout with *Edit -> Swap*.
*Edit -> Clear Map Layout* will set all squares in the layout to ``MAPSEC_NONE``.
In this example, ``MAPSEC_NEW_MAPSEC`` is inserted before ``MAPSEC_NONE``, and
therefore the layout will link the original value of ``MAPSEC_NONE`` to the new
map section ``MAPSEC_NEW_MAPSEC``. Instances of ``MAPSEC_NEW_MAPSEC`` are swapped
with ``MAPSEC_NONE``.
*Edit -> Swap Layout Sections...* will exchange two layout sections with each other.
.. figure:: images/region-map-editor/rme-layout-swap.gif
:scale: 75%
:align: center
:alt: RME Swap
Swapping Map Sections
*Edit -> Replace Layout Section...* will replace all instances of one section with another.
The "Delete Square" button simply resets a single layout square to ``MAPSEC_NONE``.
@ -111,10 +135,10 @@ Map Entries Tab
A region map entry is the area on the region map that spans an entire map section.
This determines, for example, where the player's head appears on the region map
in-game. Entries are stored at ``src/data/region_map/region_map_entries.h``.
in-game. Entries are stored in ``src/data/region_map/region_map_sections.json``.
.. figure:: images/region-map-editor/rme-entries-tab.png
:scale: 75%
.. figure:: images/region-map-editor/rme-new-entries-tab.png
:width: 75%
:align: center
:alt: RME Entries
@ -126,29 +150,8 @@ You can also drag the entry around the map. The "x" and "y" values correspond t
the position of the entry's top-left square on the region map. The "Dimensions"
"width" and "height" spinboxes will change the size of the map entry.
City Maps
---------
In the bottom half of the region map editor window, city maps can be edited.
You paint on this the same way you paint on the region map background image.
.. figure:: images/region-map-editor/rme-painting-city.gif
:scale: 60%
:align: center
:alt: City Paint
Drawing on the City Map
To use custom tiles, there is a tile image importer under
*Tools -> Import City Map Image Tiles*. These images must (1) be indexed with a
16 color palette, (2) be made up of 8x8 pixel tiles, (3) have 256 or fewer tiles.
You can add a new city map by pressing the |new-city-map-button| button.
.. |new-city-map-button|
image:: images/region-map-editor/rme-new-city-map-button.png
Currently, it is not possible to associate a city map to a region map location,
but that functionality will be added in a future update.
To change the popup name of the map section when you enter the map, type it
into the "Map Name" box.

View file

@ -39,10 +39,10 @@ determined by this file.
``use_custom_border_size``, 0, project, yes, Whether to allow variable border sizes
``enable_event_weather_trigger``, 1 if not ``pokefirered``, project, yes, Allows adding Weather Trigger events
``enable_event_secret_base``, 1 if not ``pokefirered``, project, yes, Allows adding Secret Base events
``enable_event_clone_object``, 1 if ``pokefirered``, project, yes, Allows adding Clone Object events
``enable_hidden_item_quantity``, 1 if ``pokefirered``, project, yes, Adds ``Quantity`` to Hidden Item events
``enable_hidden_item_requires_itemfinder``, 1 if ``pokefirered``, project, yes, Adds ``Requires Itemfinder`` to Hidden Item events
``enable_heal_location_respawn_data``, 1 if ``pokefirered``, project, yes, Adds ``Respawn Map`` and ``Respawn NPC`` to Heal Location events
``enable_object_event_in_connection``, 1 if ``pokefirered``, project, yes, Adds ``In Connection`` to Object events
``enable_floor_number``, 1 if ``pokefirered``, project, yes, Adds ``Floor Number`` to map headers
``create_map_text_file``, 1 if not ``pokeemerald``, project, yes, A ``text.inc`` or ``text.pory`` file will be created for any new map
``enable_triple_layer_metatiles``, 0, project, yes, Enables triple-layer metatiles (See https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,706 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RegionMapPropertiesDialog</class>
<widget class="QDialog" name="RegionMapPropertiesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>564</width>
<height>902</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Region Map Properties</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>alias</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="config_alias">
<property name="toolTip">
<string>A nickname for this region map that will differentiate it from others (should be unique).</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="message_alias">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Tilemap Properties</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>format</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="config_tilemapFormat">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The tilemap format can be either:&lt;/p&gt;&lt;p&gt;1) Plain (tilemap is a list of 8-bit tile indexes into the tileset)&lt;/p&gt;&lt;p&gt;2) 4BPP (tilemap entries are 16 bits, with x/y-flip, and palette index for 16 16-color palettes)&lt;/p&gt;&lt;p&gt;3) 8BPP (tilemap entries are 16 bits, with x/y-flip, single 256-color palette)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>plain</string>
</property>
</item>
<item>
<property name="text">
<string>4bpp</string>
</property>
</item>
<item>
<property name="text">
<string>8bpp</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="message_tilemapFormat">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>width</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="message_tilemapWidth">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>height</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="config_tilemapHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The height of the tilemap</string>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>tileset path</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="config_tilemapImagePath">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Path to the tileset image (.png) relative to the project root.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browse_tilesetImagePath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="message_tilemapImagePath">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>tilemap path</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QFrame" name="frame_2">
<property name="toolTip">
<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>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="config_tilemapBinPath"/>
</item>
<item>
<widget class="QPushButton" name="browse_tilemapBinPath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="message_tilemapBinPath">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>palette path</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="config_tilemapPalettePath">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;(optional) Path to the .pal JASC palette file&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browse_tilemapPalettePath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="message_tilemapHeight">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="config_tilemapWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The width of the tilemap&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QGroupBox" name="group_layout">
<property name="title">
<string>Layout Properties</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>format</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="config_layoutFormat">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The format of the layout file, can be a C array in a text file with &amp;quot;MAPSEC_&amp;quot;-prefixed constants, or a binary file where each byte is a value of some &amp;quot;MAPSEC_&amp;quot;-prefixed constant.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>C array</string>
</property>
</item>
<item>
<property name="text">
<string>binary</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="message_layoutFormat">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>layout path</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QFrame" name="frame_5">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="config_layoutPath">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The path to the layout file, relative to the project root.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browse_layoutPath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="message_layoutPath">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_16">
<property name="text">
<string>width</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_layoutWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The layout width.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>left offset</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_leftOffs">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The offset from the left of the tilemap where the layout starts.&lt;/p&gt;&lt;p&gt;(ie, coordinate (0,0) in the layout would be (offset left, offset top) in the tilemap).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>85</width>
<height>2</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="message_layoutWidth">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
<string>height</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_layoutHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The layout height.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>87</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>top offset</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_topOffs">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The offset from the top of the tilemap where the layout starts.&lt;/p&gt;&lt;p&gt;(ie, coordinate (0,0) in the layout would be (offset left, offset top) in the tilemap).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>87</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="message_layoutHeight">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="9" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="9" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RegionMapPropertiesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RegionMapPropertiesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -567,6 +567,8 @@
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionCopy"/>
<addaction name="actionPaste"/>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
</widget>
@ -663,6 +665,22 @@
<string>Import Secondary Metatiles from Advance Map 1.92...</string>
</property>
</action>
<action name="actionCopy">
<property name="text">
<string>Copy</string>
</property>
<property name="shortcut">
<string>Ctrl+C</string>
</property>
</action>
<action name="actionPaste">
<property name="text">
<string>Paste</string>
</property>
<property name="shortcut">
<string>Ctrl+V</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View file

@ -148,7 +148,7 @@ public:
this->enableHiddenItemQuantity = false;
this->enableHiddenItemRequiresItemfinder = false;
this->enableHealLocationRespawnData = false;
this->enableObjectEventInConnection = false;
this->enableEventCloneObject = false;
this->enableFloorNumber = false;
this->createMapTextFile = false;
this->enableTripleLayerMetatiles = false;
@ -178,8 +178,8 @@ public:
bool getHiddenItemRequiresItemfinderEnabled();
void setHealLocationRespawnDataEnabled(bool enable);
bool getHealLocationRespawnDataEnabled();
void setObjectEventInConnectionEnabled(bool enable);
bool getObjectEventInConnectionEnabled();
void setEventCloneObjectEnabled(bool enable);
bool getEventCloneObjectEnabled();
void setFloorNumberEnabled(bool enable);
bool getFloorNumberEnabled();
void setCreateMapTextFileEnabled(bool enable);
@ -206,7 +206,7 @@ private:
bool enableHiddenItemQuantity;
bool enableHiddenItemRequiresItemfinder;
bool enableHealLocationRespawnData;
bool enableObjectEventInConnection;
bool enableEventCloneObject;
bool enableFloorNumber;
bool createMapTextFile;
bool enableTripleLayerMetatiles;

View file

@ -14,6 +14,7 @@ class EventType
{
public:
static QString Object;
static QString CloneObject;
static QString Warp;
static QString Trigger;
static QString WeatherTrigger;
@ -23,6 +24,16 @@ public:
static QString HealLocation;
};
class EventGroup
{
public:
static QString Object;
static QString Warp;
static QString Coord;
static QString Bg;
static QString Heal;
};
class DraggablePixmapItem;
class Project;
class Event
@ -68,6 +79,7 @@ public:
static Event* createNewEvent(QString, QString, Project*);
static Event* createNewObjectEvent(Project*);
static Event* createNewCloneObjectEvent(Project*, QString);
static Event* createNewWarpEvent(QString);
static Event* createNewHealLocationEvent(QString);
static Event* createNewTriggerEvent(Project*);
@ -75,8 +87,12 @@ public:
static Event* createNewSignEvent(Project*);
static Event* createNewHiddenItemEvent(Project*);
static Event* createNewSecretBaseEvent(Project*);
static bool isValidType(QString event_type);
static QString typeToGroup(QString event_type);
static int getIndexOffset(QString group_type);
OrderedJson::object buildObjectEventJSON();
OrderedJson::object buildCloneObjectEventJSON(const QMap<QString, QString> &);
OrderedJson::object buildWarpEventJSON(const QMap<QString, QString> &);
OrderedJson::object buildTriggerEventJSON();
OrderedJson::object buildWeatherTriggerEventJSON();
@ -86,7 +102,7 @@ public:
void setPixmapFromSpritesheet(QImage, int, int, bool);
int getPixelX();
int getPixelY();
QMap<QString, bool> getExpectedFields();
QSet<QString> getExpectedFields();
void readCustomValues(QJsonObject values);
void addCustomValuesTo(OrderedJson::object *obj);
void setFrameFromMovement(QString);

View file

@ -54,4 +54,18 @@ public:
static int getAttributesSize(BaseGameVersion version);
};
inline bool operator==(const Metatile &a, const Metatile &b) {
return a.behavior == b.behavior &&
a.layerType == b.layerType &&
a.encounterType == b.encounterType &&
a.terrainType == b.terrainType &&
a.unusedAttributes == b.unusedAttributes &&
a.label == b.label &&
a.tiles == b.tiles;
}
inline bool operator!=(const Metatile &a, const Metatile &b) {
return !(operator==(a, b));
}
#endif // METATILE_H

View file

@ -3,7 +3,6 @@
#define REGIONMAP_H
#include "map.h"
#include "project.h"
#include "tilemaptileselector.h"
#include "history.h"
@ -15,138 +14,183 @@
#include <QGraphicsScene>
#include <QGraphicsView>
enum RegionMapEditorBox {
BackgroundImage = 1,
CityMapImage = 2,
};
#include <memory>
using std::shared_ptr;
class RegionMapHistoryItem {
public:
int which;
int mapWidth = 0;
int mapHeight = 0;
QVector<uint8_t> tiles;
QString cityMap;
RegionMapHistoryItem(int which, QVector<uint8_t> tiles, QString cityMap) {
this->which = which;
this->tiles = tiles;
this->cityMap = cityMap;
}
RegionMapHistoryItem(int which, QVector<uint8_t> tiles, int width, int height) {
this->which = which;
this->tiles = tiles;
this->mapWidth = width;
this->mapHeight = height;
}
~RegionMapHistoryItem() {}
};
class Project;
class RegionMapEntry
struct LayoutSquare
{
public:
RegionMapEntry()=default;
RegionMapEntry(int x_, int y_, int width_, int height_, QString name_) {
this-> x = x_;
this-> y = y_;
this-> width = width_;
this-> height = height_;
this-> name = name_;
}
LayoutSquare() : map_section("MAPSEC_NONE"), x(-1), y(-1), has_map(false) {}
QString map_section;
int x;
int y;
int width;
int height;
QString name;
void setX(int);
void setY(int);
void setWidth(int);
void setHeight(int);
bool has_map;
};
class RegionMapSquare
struct MapSectionEntry
{
public:
int x = -1;
int y = -1;
uint8_t tile_img_id = 0x00;
uint8_t secid = 0x00;
bool has_map = false;
bool has_city_map = false;
bool duplicated = false;
QString map_name;
QString mapsec;
QString city_map_name;
QString name = "";
int x = 0;
int y = 0;
int width = 1;
int height = 1;
bool valid = false;
};
class RegionMap
class RegionMap : public QObject
{
Q_OBJECT
public:
RegionMap() = default;
RegionMap() = delete;
RegionMap(Project *);
~RegionMap() {}
Project *project = nullptr;
QVector<RegionMapSquare> map_squares;
History<RegionMapHistoryItem*> history;
bool loadMapData(poryjson::Json);
bool loadTilemap(poryjson::Json);
bool loadLayout(poryjson::Json);
bool loadEntries();
QMap<QString, QString> sMapNamesMap;
QMap<QString, RegionMapEntry> mapSecToMapEntry;
QVector<QString> sMapNames;
const int padLeft = 1;
const int padRight = 3;
const int padTop = 2;
const int padBottom = 3;
bool init(Project*);
bool readBkgImgBin();
bool readLayout();
void setEntries(tsl::ordered_map<QString, MapSectionEntry> *entries) { this->region_map_entries = entries; }
void setEntries(tsl::ordered_map<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);
void removeEntry(QString section);
void save();
void saveTileImages();
void saveBkgImgBin();
void saveTilemap();
void saveLayout();
void saveOptions(int id, QString sec, QString name, int x, int y);
void resize(int width, int height);
void resizeTilemap(int width, int height, bool update = true);
void resetSquare(int index);
void clearLayout();
void clearImage();
void replaceSectionId(unsigned oldId, unsigned newId);
void replaceSection(QString oldSection, QString newSection);
void swapSections(QString secA, QString secB);
int width();
int height();
QSize imgSize();
unsigned getTileId(int index);
shared_ptr<TilemapTile> getTile(int index);
unsigned getTileId(int x, int y);
int getMapSquareIndex(int x, int y);
QString pngPath();
void setTemporaryPngPath(QString);
QString cityTilesPath();
void setTemporaryCityTilesPath(QString);
shared_ptr<TilemapTile> getTile(int x, int y);
bool squareHasMap(int index);
QString squareMapSection(int index);
void setSquareMapSection(int index, QString section);
int squareX(int index);
int squareY(int index);
bool squareInLayout(int x, int y);
int firstLayoutIndex() { return this->offset_left + this->offset_top * this->tilemap_width; }
QVector<uint8_t> getTiles();
void setTiles(QVector<uint8_t> tileIds);
void setTileId(int index, unsigned id);
void setTile(int index, TilemapTile &tile);
void setTileData(int index, unsigned id, bool hFlip, bool vFlip, int palette);
int getMapSquareIndex(int x, int y);
QString getAlias() { return this->alias; }
poryjson::Json::object config();
QString palPath();
QString pngPath();
QString entriesPath() { return this->entries_path; }
QByteArray getTilemap();
void setTilemap(QByteArray newTilemap);
QList<LayoutSquare> getLayout(QString layer);
void setLayout(QString layer, QList<LayoutSquare> layout);
bool layoutEnabled() { return this->layout_format != LayoutFormat::None; }
QMap<QString, QList<LayoutSquare>> getAllLayouts();
void setAllLayouts(QMap<QString, QList<LayoutSquare>> newLayouts);
QStringList getLayers() { return this->layout_layers; }
void setLayer(QString layer) { this->current_layer = layer; }
QString getLayer() { return this->current_layer; }
QString fixCase(QString);
int padLeft() { return this->offset_left; }
int padTop() { return this->offset_top; }
int padRight() { return this->tilemap_width - this->layout_width - this->offset_left; }
int padBottom() { return this->tilemap_height - this->layout_height - this->offset_top; }
int tilemapWidth() { return this->tilemap_width; }
int tilemapHeight() { return this->tilemap_height; }
int tilemapSize() { return this->tilemap_width * this->tilemap_height; }
int tilemapBytes();
int layoutWidth() { return this->layout_width; }
int layoutHeight() { return this->layout_height; }
void setLayoutDimensions(int width, int height, bool update = true);
int tilemapToLayoutIndex(int index);
TilemapFormat tilemapFormat() { return this->tilemap_format; }
int pixelWidth() { return this->tilemap_width * 8; }
int pixelHeight() { return this->tilemap_height * 8; }
QString fullPath(QString local);
void commit(QUndoCommand *command);
QUndoStack editHistory;
void undo();
void redo();
void emitDisplay();
signals:
void mapNeedsDisplaying();
private:
int layout_width_;
int layout_height_;
int img_width_;
int img_height_;
// TODO: defaults needed?
tsl::ordered_map<QString, MapSectionEntry> *region_map_entries = nullptr;
QString region_map_png_path;
QString region_map_bin_path;
QString region_map_entries_path;
QString region_map_layout_bin_path;
QString city_map_tiles_path;
QString alias = "";
bool region_map_png_needs_saving = false;
bool city_map_png_needs_saving = false;
int tilemap_width;
int tilemap_height;
int img_index_(int x, int y);
int layout_index_(int x, int y);
int layout_width;
int layout_height;
int offset_left;
int offset_top;
TilemapFormat tilemap_format;
enum class LayoutFormat { None, Binary, CArray };
LayoutFormat layout_format;
QString tileset_path;
QString tilemap_path;
QString palette_path = "";
QString entries_path;
QString layout_path;
QString layout_array_label;
bool layout_uses_layers = false;
QStringList layout_constants;
QString layout_qualifiers;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QVector<shared_ptr<TilemapTile>> tilemap;
#else
QList<shared_ptr<TilemapTile>> tilemap;
#endif
QStringList layout_layers;
QString current_layer;
QMap<QString, QList<LayoutSquare>> layouts;
int get_tilemap_index(int x, int y);
int get_layout_index(int x, int y);
};
#endif // REGIONMAP_H

View file

@ -0,0 +1,169 @@
#pragma once
#ifndef REGIONMAPEDITCOMMANDS_H
#define REGIONMAPEDITCOMMANDS_H
#include "regionmap.h"
#include <QUndoCommand>
#include <QList>
class RegionMap;
enum RMCommandId {
ID_EditTilemap = 0,
ID_EditLayout,
ID_ResizeLayout,
ID_EditEntry,
ID_RemoveEntry,
ID_AddEntry,
ID_ResizeTilemap,
ID_ClearEntries,
};
/// Implements a command to commit tilemap paint actions
class EditTilemap : public QUndoCommand {
public:
EditTilemap(RegionMap *map, QByteArray oldTilemap, QByteArray newTilemap, unsigned actionId, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
bool mergeWith(const QUndoCommand *command) override;
int id() const override { return RMCommandId::ID_EditTilemap; }
protected:
RegionMap *map;
QByteArray oldTilemap;
QByteArray newTilemap;
unsigned actionId;
};
/// Edit region map section layout
class EditLayout : public QUndoCommand {
public:
EditLayout(RegionMap *map, QString layer, int index, QList<LayoutSquare> oldLayout, QList<LayoutSquare> newLayout, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
bool mergeWith(const QUndoCommand *command) override;
int id() const override { return RMCommandId::ID_EditLayout; }
private:
RegionMap *map;
int index;
QString layer;
QList<LayoutSquare> oldLayout;
QList<LayoutSquare> newLayout;
};
/// Edit Layout Dimensions
class ResizeLayout : public QUndoCommand {
public:
ResizeLayout(RegionMap *map, int oldWidth, int oldHeight, int newWidth, int newHeight,
QMap<QString, QList<LayoutSquare>> oldLayouts, QMap<QString, QList<LayoutSquare>> newLayouts, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
bool mergeWith(const QUndoCommand *command) override;
int id() const override { return RMCommandId::ID_ResizeLayout; }
private:
RegionMap *map;
int oldWidth;
int oldHeight;
int newWidth;
int newHeight;
QMap<QString, QList<LayoutSquare>> oldLayouts;
QMap<QString, QList<LayoutSquare>> newLayouts;
};
/// Edit Entry Value
class EditEntry : public QUndoCommand {
public:
EditEntry(RegionMap *map, QString section, MapSectionEntry oldEntry, MapSectionEntry newEntry, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
bool mergeWith(const QUndoCommand *command) override;
int id() const override { return RMCommandId::ID_EditEntry; }
protected:
RegionMap *map;
QString section;
MapSectionEntry oldEntry;
MapSectionEntry newEntry;
};
/// Remove Entry
class RemoveEntry : public EditEntry {
public:
RemoveEntry(RegionMap *map, QString section, MapSectionEntry oldEntry, MapSectionEntry newEntry, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
int id() const override { return RMCommandId::ID_RemoveEntry; }
};
/// Add Entry
class AddEntry : public EditEntry {
public:
AddEntry(RegionMap *map, QString section, MapSectionEntry oldEntry, MapSectionEntry newEntry, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
int id() const override { return RMCommandId::ID_AddEntry; }
};
/// ResizeTilemap
class ResizeTilemap : public EditTilemap {
public:
ResizeTilemap(RegionMap *map, QByteArray oldTilemap, QByteArray newTilemap,
int oldWidth, int oldHeight, int newWidth, int newHeight, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
int id() const override { return RMCommandId::ID_ResizeTilemap; }
private:
int oldWidth;
int oldHeight;
int newWidth;
int newHeight;
};
/// ClearEntries
class ClearEntries : public QUndoCommand {
public:
ClearEntries(RegionMap *map, tsl::ordered_map<QString, MapSectionEntry>, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
bool mergeWith(const QUndoCommand *command) override { return false; }
int id() const override { return RMCommandId::ID_ClearEntries; }
private:
RegionMap *map;
tsl::ordered_map<QString, MapSectionEntry> entries;
};
#endif // REGIONMAPEDITCOMMANDS_H

View file

@ -22,4 +22,15 @@ public:
static int getIndexInTileset(int);
};
inline bool operator==(const Tile &a, const Tile &b) {
return a.tileId == b.tileId &&
a.xflip == b.xflip &&
a.yflip == b.yflip &&
a.palette == b.palette;
}
inline bool operator!=(const Tile &a, const Tile &b) {
return !(operator==(a, b));
}
#endif // TILE_H

View file

@ -210,7 +210,7 @@ signals:
void selectedObjectsChanged();
void loadMapRequested(QString, QString);
void wildMonDataChanged();
void warpEventDoubleClicked(QString mapName, QString warpNum);
void warpEventDoubleClicked(QString, QString, QString);
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void editedMapData();

View file

@ -212,7 +212,7 @@ private slots:
void on_action_Reload_Project_triggered();
void on_mapList_activated(const QModelIndex &index);
void on_action_Save_Project_triggered();
void openWarpMap(QString map_name, QString warp_num);
void openWarpMap(QString map_name, QString event_id, QString event_group);
void duplicate();
void setClipboardData(poryjson::Json::object);
@ -345,7 +345,7 @@ private:
QPointer<RegionMapEditor> regionMapEditor = nullptr;
QPointer<ShortcutsEditor> shortcutsEditor = nullptr;
QPointer<MapImageExporter> mapImageExporter = nullptr;
QPointer<NewMapPopup> newmapprompt = nullptr;
QPointer<NewMapPopup> newMapPrompt = nullptr;
QPointer<PreferenceEditor> preferenceEditor = nullptr;
FilterChildrenProxyModel *mapListProxyModel;
QStandardItemModel *mapListModel;
@ -418,6 +418,7 @@ private:
void initShortcuts();
void initExtraShortcuts();
void setProjectSpecificUIVisibility();
void setWildEncountersUIEnabled(bool enabled);
void loadUserSettings();
void applyMapListFilter(QString filterText);
void restoreWindowState();

View file

@ -9,6 +9,7 @@
#include "wildmoninfo.h"
#include "parseutil.h"
#include "orderedjson.h"
#include "regionmap.h"
#include <QStringList>
#include <QList>
@ -60,7 +61,7 @@ public:
QMap<QString, int> mapSectionNameToValue;
QMap<int, QString> mapSectionValueToName;
QMap<QString, EventGraphics*> eventGraphicsMap;
QStringList gfxNames;
QMap<QString, int> gfxDefines;
QStringList songNames;
QStringList itemNames;
QStringList flagNames;
@ -244,6 +245,7 @@ signals:
void reloadProject();
void uncheckMonitorFilesAction();
void mapCacheCleared();
void disableWildEncountersUI();
};
#endif // PROJECT_H

View file

@ -11,6 +11,7 @@ public:
explicit NewEventToolButton(QWidget *parent = nullptr);
QString getSelectedEventType();
QAction *newObjectAction;
QAction *newCloneObjectAction;
QAction *newWarpAction;
QAction *newHealLocationAction;
QAction *newTriggerAction;
@ -20,6 +21,7 @@ public:
QAction *newSecretBaseAction;
public slots:
void newObject();
void newCloneObject();
void newWarp();
void newHealLocation();
void newTrigger();

View file

@ -6,6 +6,8 @@
#include "regionmaplayoutpixmapitem.h"
#include "regionmapentriespixmapitem.h"
#include "regionmap.h"
#include "orderedjson.h"
#include "project.h"
#include <QMainWindow>
#include <QGraphicsSceneMouseEvent>
@ -24,14 +26,9 @@ public:
explicit RegionMapEditor(QWidget *parent = 0, Project *pro = nullptr);
~RegionMapEditor();
RegionMap *region_map;
bool loadRegionMapData();
bool loadCityMaps();
void setCurrentSquareOptions();
bool load();
void onRegionMapTileSelectorSelectedTileChanged(unsigned id);
void onCityMapTileSelectorSelectedTileChanged(unsigned id);
void onRegionMapTileSelectorHoveredTileChanged(unsigned id);
void onRegionMapTileSelectorHoveredTileCleared();
@ -42,10 +39,7 @@ public:
void onRegionMapEntriesSelectedTileChanged(QString) {};
void onRegionMapEntryDragged(int, int);
void undo();
void redo();
void resize(int width, int height);
void resizeTilemap(int width, int height);
QObjectList shortcutableObjects() const;
@ -56,14 +50,19 @@ private:
Ui::RegionMapEditor *ui;
Project *project;
History<RegionMapHistoryItem*> history;
RegionMap *region_map = nullptr;
tsl::ordered_map<QString, RegionMap *> region_maps;
int currIndex;
unsigned selectedCityTile;
unsigned selectedImageTile;
poryjson::Json rmConfigJson;
bool configSaved = false;
QUndoGroup history;
int currIndex = 0;
unsigned selectedImageTile = 0;
QString activeEntry;
bool hasUnsavedChanges = false;
bool cityMapFirstDraw = true;
bool regionMapFirstDraw = true;
bool entriesFirstDraw = true;
@ -72,19 +71,32 @@ private:
double initialScale = 30.0;
QGraphicsScene *scene_region_map_image = nullptr;
QGraphicsScene *scene_city_map_image = nullptr;
QGraphicsScene *scene_region_map_layout = nullptr;
QGraphicsScene *scene_region_map_entries = nullptr;
QGraphicsScene *scene_region_map_tiles = nullptr;
QGraphicsScene *scene_city_map_tiles = nullptr;
TilemapTileSelector *mapsquare_selector_item = nullptr;
TilemapTileSelector *city_map_selector_item = nullptr;
RegionMapEntriesPixmapItem *region_map_entries_item = nullptr;
RegionMapLayoutPixmapItem *region_map_layout_item = nullptr;
RegionMapPixmapItem *region_map_item = nullptr;
CityMapPixmapItem *city_map_item = nullptr;
bool reload();
bool setup();
void clear();
bool saveRegionMap(RegionMap *map);
void saveConfig();
bool loadRegionMapEntries();
bool saveRegionMapEntries();
tsl::ordered_map<QString, MapSectionEntry> region_map_entries;
bool buildConfigDialog();
poryjson::Json configRegionMapDialog();
poryjson::Json buildDefaultJson();
bool verifyConfig(poryjson::Json cfg);
bool modified();
void initShortcuts();
void displayRegionMap();
@ -94,47 +106,46 @@ private:
void displayRegionMapLayoutOptions();
void updateRegionMapLayoutOptions(int index);
void displayRegionMapTileSelector();
void displayCityMapTileSelector();
void displayCityMap(QString name);
void updateLayerDisplayed();
void displayRegionMapEntryOptions();
void updateRegionMapEntryOptions(QString);
void importTileImage(bool city = false);
bool createCityMap(QString name);
bool tryInsertNewMapEntry(QString);
void setRegionMap(RegionMap *map);
void restoreWindowState();
void closeEvent(QCloseEvent* event);
private slots:
void on_action_RegionMap_Save_triggered();
void on_action_RegionMap_Undo_triggered();
void on_action_RegionMap_Redo_triggered();
void on_actionSave_All_triggered();
void on_action_RegionMap_Resize_triggered();
void on_action_RegionMap_ClearImage_triggered();
void on_action_RegionMap_ClearLayout_triggered();
void on_action_RegionMap_ClearEntries_triggered();
void on_action_Swap_triggered();
void on_action_Import_RegionMap_ImageTiles_triggered();
void on_action_Import_CityMap_ImageTiles_triggered();
void on_action_Replace_triggered();
void on_action_Configure_triggered();
void on_tabWidget_Region_Map_currentChanged(int);
void on_pushButton_RM_Options_delete_clicked();
void on_comboBox_RM_ConnectedMap_textActivated(const QString &);
void on_comboBox_RM_Entry_MapSection_textActivated(const QString &);
void on_comboBox_regionSelector_textActivated(const QString &);
void on_comboBox_layoutLayer_textActivated(const QString &);
void on_spinBox_RM_Entry_x_valueChanged(int);
void on_spinBox_RM_Entry_y_valueChanged(int);
void on_spinBox_RM_Entry_width_valueChanged(int);
void on_spinBox_RM_Entry_height_valueChanged(int);
void on_pushButton_CityMap_add_clicked();
void on_pushButton_entryActivate_clicked();
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 on_verticalSlider_Zoom_City_Map_valueChanged(int);
void on_verticalSlider_Zoom_City_Tiles_valueChanged(int);
void on_comboBox_CityMap_picker_currentTextChanged(const QString &);
void on_lineEdit_RM_MapName_textEdited(const QString &);
void onHoveredRegionMapTileChanged(int x, int y);
void onHoveredRegionMapTileCleared();
void mouseEvent_region_map(QGraphicsSceneMouseEvent *event, RegionMapPixmapItem *item);
void mouseEvent_city_map(QGraphicsSceneMouseEvent *event, CityMapPixmapItem *item);
};
#endif // REGIONMAPEDITOR_H

View file

@ -17,12 +17,14 @@ public:
this->tile_selector = tile_selector;
setAcceptHoverEvents(true);
}
RegionMap *region_map;
RegionMap *region_map = nullptr;
TilemapTileSelector *tile_selector;
virtual void paint(QGraphicsSceneMouseEvent *);
virtual void fill(QGraphicsSceneMouseEvent *);
virtual void select(QGraphicsSceneMouseEvent *);
virtual void draw();
void floodFill(int x, int y, std::shared_ptr<TilemapTile> oldTile, std::shared_ptr<TilemapTile> newTile);
signals:
void mouseEvent(QGraphicsSceneMouseEvent *, RegionMapPixmapItem *);

View file

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

View file

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

View file

@ -41,7 +41,7 @@ public:
void updateMap(Map *map);
void updateTilesets(QString primaryTilsetLabel, QString secondaryTilesetLabel);
bool selectMetatile(uint16_t metatileId);
uint16_t getSelectedMetatile();
uint16_t getSelectedMetatileId();
void setMetatileLabel(QString label);
QObjectList shortcutableObjects() const;
@ -105,6 +105,10 @@ private slots:
void on_copyButton_metatileLabel_clicked();
void on_actionCopy_triggered();
void on_actionPaste_triggered();
private:
void initUi();
void setMetatileBehaviors();
@ -129,6 +133,7 @@ private:
void closeEvent(QCloseEvent*);
void countMetatileUsage();
void countTileUsage();
bool replaceMetatile(uint16_t metatileId, Metatile * src);
Ui::TilesetEditor *ui;
History<MetatileHistoryItem*> metatileHistory;
@ -139,6 +144,7 @@ private:
Project *project = nullptr;
Map *map = nullptr;
Metatile *metatile = nullptr;
Metatile *copiedMetatile = nullptr;
int paletteId;
bool tileXFlip;
bool tileYFlip;

View file

@ -13,7 +13,7 @@ public:
void draw();
bool select(uint16_t metatileId);
void setTilesets(Tileset*, Tileset*, bool draw = true);
uint16_t getSelectedMetatile();
uint16_t getSelectedMetatileId();
void updateSelectedMetatile();
QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId);

View file

@ -12,7 +12,7 @@ TARGET = porymap
TEMPLATE = app
RC_ICONS = resources/icons/porymap-icon-2.ico
ICON = resources/icons/porymap.icns
QMAKE_CXXFLAGS += -std=c++11 -Wall
QMAKE_CXXFLAGS += -std=c++17 -Wall
QMAKE_TARGET_BUNDLE_PREFIX = com.pret
SOURCES += src/core/block.cpp \
@ -33,6 +33,7 @@ SOURCES += src/core/block.cpp \
src/core/wildmoninfo.cpp \
src/core/editcommands.cpp \
src/lib/orderedjson.cpp \
src/core/regionmapeditcommands.cpp \
src/mainwindow_scriptapi.cpp \
src/ui/aboutporymap.cpp \
src/ui/draggablepixmapitem.cpp \
@ -77,6 +78,7 @@ SOURCES += src/core/block.cpp \
src/ui/shortcutseditor.cpp \
src/ui/multikeyedit.cpp \
src/ui/preferenceeditor.cpp \
src/ui/regionmappropertiesdialog.cpp \
src/config.cpp \
src/editor.cpp \
src/main.cpp \
@ -105,6 +107,7 @@ HEADERS += include/core/block.h \
include/core/regionmap.h \
include/core/wildmoninfo.h \
include/core/editcommands.h \
include/core/regionmapeditcommands.h \
include/lib/orderedmap.h \
include/lib/orderedjson.h \
include/ui/aboutporymap.h \
@ -151,6 +154,7 @@ HEADERS += include/core/block.h \
include/ui/shortcutseditor.h \
include/ui/multikeyedit.h \
include/ui/preferenceeditor.h \
include/ui/regionmappropertiesdialog.h \
include/config.h \
include/editor.h \
include/mainwindow.h \
@ -169,11 +173,13 @@ FORMS += forms/mainwindow.ui \
forms/newtilesetdialog.ui \
forms/mapimageexporter.ui \
forms/shortcutseditor.ui \
forms/preferenceeditor.ui
forms/preferenceeditor.ui \
forms/regionmappropertiesdialog.ui
RESOURCES += \
resources/images.qrc \
resources/themes.qrc
resources/themes.qrc \
resources/text.qrc
INCLUDEPATH += include
INCLUDEPATH += include/core

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 4.3 KiB

7
resources/text.qrc Normal file
View file

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

View file

@ -0,0 +1,33 @@
{
"region_maps": [
{
"alias": "hoenn",
"tilemap": {
"width": 64,
"height": 64,
"format": "plain",
"tileset_path": "graphics/pokenav/region_map.png",
"tilemap_path": "graphics/pokenav/region_map_map.bin"
},
"layout": {
"width": 28,
"height": 15,
"offset_left": 1,
"offset_top": 2,
"format": "C array",
"path": "src/data/region_map/region_map_layout.h"
}
},
{
"alias": "pokedex area screen",
"tilemap": {
"width": 32,
"height": 32,
"format": "8bpp",
"tileset_path": "graphics/pokedex/region_map.png",
"tilemap_path": "graphics/pokedex/region_map.bin"
},
"layout": null
}
]
}

View file

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

View file

@ -0,0 +1,22 @@
{
"region_maps": [
{
"alias": "hoenn",
"tilemap": {
"width": 64,
"height": 64,
"format": "plain",
"tileset_path": "graphics/pokenav/region_map.png",
"tilemap_path": "graphics/pokenav/region_map_map.bin"
},
"layout": {
"width": 28,
"height": 15,
"offset_left": 1,
"offset_top": 2,
"format": "C array",
"path": "src/data/region_map/region_map_layout.h"
}
}
]
}

View file

@ -522,11 +522,11 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
if (!ok) {
logWarn(QString("Invalid config value for enable_heal_location_respawn_data: '%1'. Must be 0 or 1.").arg(value));
}
} else if (key == "enable_object_event_in_connection") {
} else if (key == "enable_event_clone_object") {
bool ok;
this->enableObjectEventInConnection = value.toInt(&ok);
this->enableEventCloneObject = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_object_event_in_connection: '%1'. Must be 0 or 1.").arg(value));
logWarn(QString("Invalid config value for enable_event_clone_object: '%1'. Must be 0 or 1.").arg(value));
}
} else if (key == "enable_floor_number") {
bool ok;
@ -570,7 +570,7 @@ void ProjectConfig::setUnreadKeys() {
if (!readKeys.contains("enable_hidden_item_quantity")) this->enableHiddenItemQuantity = isPokefirered;
if (!readKeys.contains("enable_hidden_item_requires_itemfinder")) this->enableHiddenItemRequiresItemfinder = isPokefirered;
if (!readKeys.contains("enable_heal_location_respawn_data")) this->enableHealLocationRespawnData = isPokefirered;
if (!readKeys.contains("enable_object_event_in_connection")) this->enableObjectEventInConnection = isPokefirered;
if (!readKeys.contains("enable_event_clone_object")) this->enableEventCloneObject = isPokefirered;
if (!readKeys.contains("enable_floor_number")) this->enableFloorNumber = isPokefirered;
if (!readKeys.contains("create_map_text_file")) this->createMapTextFile = (this->baseGameVersion != BaseGameVersion::pokeemerald);
}
@ -587,7 +587,7 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("enable_hidden_item_quantity", QString::number(this->enableHiddenItemQuantity));
map.insert("enable_hidden_item_requires_itemfinder", QString::number(this->enableHiddenItemRequiresItemfinder));
map.insert("enable_heal_location_respawn_data", QString::number(this->enableHealLocationRespawnData));
map.insert("enable_object_event_in_connection", QString::number(this->enableObjectEventInConnection));
map.insert("enable_event_clone_object", QString::number(this->enableEventCloneObject));
map.insert("enable_floor_number", QString::number(this->enableFloorNumber));
map.insert("create_map_text_file", QString::number(this->createMapTextFile));
map.insert("enable_triple_layer_metatiles", QString::number(this->enableTripleLayerMetatiles));
@ -628,7 +628,7 @@ void ProjectConfig::onNewConfigFileCreated() {
this->enableHiddenItemQuantity = isPokefirered;
this->enableHiddenItemRequiresItemfinder = isPokefirered;
this->enableHealLocationRespawnData = isPokefirered;
this->enableObjectEventInConnection = isPokefirered;
this->enableEventCloneObject = isPokefirered;
this->enableFloorNumber = isPokefirered;
this->createMapTextFile = (this->baseGameVersion != BaseGameVersion::pokeemerald);
this->useEncounterJson = true;
@ -739,13 +739,13 @@ bool ProjectConfig::getHealLocationRespawnDataEnabled() {
return this->enableHealLocationRespawnData;
}
void ProjectConfig::setObjectEventInConnectionEnabled(bool enable) {
this->enableObjectEventInConnection = enable;
void ProjectConfig::setEventCloneObjectEnabled(bool enable) {
this->enableEventCloneObject = enable;
this->save();
}
bool ProjectConfig::getObjectEventInConnectionEnabled() {
return this->enableObjectEventInConnection;
bool ProjectConfig::getEventCloneObjectEnabled() {
return this->enableEventCloneObject;
}
void ProjectConfig::setFloorNumberEnabled(bool enable) {

View file

@ -9,18 +9,16 @@
int getEventTypeMask(QList<Event *> events) {
int eventTypeMask = 0;
for (auto event : events) {
if (event->get("event_type") == EventType::Object) {
QString groupType = event->get("event_group_type");
if (groupType == EventGroup::Object) {
eventTypeMask |= IDMask_EventType_Object;
} else if (event->get("event_type") == EventType::Warp) {
} else if (groupType == EventGroup::Warp) {
eventTypeMask |= IDMask_EventType_Warp;
} else if (event->get("event_type") == EventType::Trigger ||
event->get("event_type") == EventType::WeatherTrigger) {
} else if (groupType == EventGroup::Coord) {
eventTypeMask |= IDMask_EventType_Trigger;
} else if (event->get("event_type") == EventType::Sign ||
event->get("event_type") == EventType::HiddenItem ||
event->get("event_type") == EventType::SecretBase) {
} else if (groupType == EventGroup::Bg) {
eventTypeMask |= IDMask_EventType_BG;
} else if (event->get("event_type") == EventType::HealLocation) {
} else if (groupType == EventGroup::Heal) {
eventTypeMask |= IDMask_EventType_Heal;
}
}

View file

@ -3,14 +3,33 @@
#include "project.h"
#include "config.h"
QString EventGroup::Object = "object_event_group";
QString EventGroup::Warp = "warp_event_group";
QString EventGroup::Heal = "heal_event_group";
QString EventGroup::Coord = "coord_event_group";
QString EventGroup::Bg = "bg_event_group";
QString EventType::Object = "event_object";
QString EventType::CloneObject = "event_clone_object";
QString EventType::Warp = "event_warp";
QString EventType::Trigger = "event_trigger";
QString EventType::WeatherTrigger = "event_weather_trigger";
QString EventType::Sign = "event_sign";
QString EventType::HiddenItem = "event_hidden_item";
QString EventType::SecretBase = "event_secret_base";
QString EventType::HealLocation = "event_heal_location";
QString EventType::HealLocation = "event_healspot";
const QMap<QString, QString> EventTypeTable = {
{EventType::Object, EventGroup::Object},
{EventType::CloneObject, EventGroup::Object},
{EventType::Warp, EventGroup::Warp},
{EventType::Trigger, EventGroup::Coord},
{EventType::WeatherTrigger, EventGroup::Coord},
{EventType::Sign, EventGroup::Bg},
{EventType::HiddenItem, EventGroup::Bg},
{EventType::SecretBase, EventGroup::Bg},
{EventType::HealLocation, EventGroup::Heal},
};
Event::Event() :
spriteWidth(16),
@ -41,6 +60,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name, Project *proj
if (event_type == EventType::Object) {
event = createNewObjectEvent(project);
event->setFrameFromMovement(event->get("movement_type"));
} else if (event_type == EventType::CloneObject) {
event = createNewCloneObjectEvent(project, map_name);
} else if (event_type == EventType::Warp) {
event = createNewWarpEvent(map_name);
} else if (event_type == EventType::HealLocation) {
@ -60,6 +81,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name, Project *proj
event = new Event;
}
event->put("event_type", event_type);
event->put("event_group_type", typeToGroup(event_type));
event->setX(0);
event->setY(0);
return event;
@ -68,13 +91,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name, Project *proj
Event* Event::createNewObjectEvent(Project *project)
{
Event *event = new Event;
event->put("event_group_type", "object_event_group");
event->put("event_type", EventType::Object);
event->put("sprite", project->gfxNames.first());
event->put("sprite", project->gfxDefines.keys().first());
event->put("movement_type", project->movementTypes.first());
if (projectConfig.getObjectEventInConnectionEnabled()) {
event->put("in_connection", false);
}
event->put("radius_x", 0);
event->put("radius_y", 0);
event->put("script_label", "NULL");
@ -86,11 +104,18 @@ Event* Event::createNewObjectEvent(Project *project)
return event;
}
Event* Event::createNewCloneObjectEvent(Project *project, QString map_name)
{
Event *event = new Event;
event->put("sprite", project->gfxDefines.keys().first());
event->put("target_local_id", 1);
event->put("target_map", map_name);
return event;
}
Event* Event::createNewWarpEvent(QString map_name)
{
Event *event = new Event;
event->put("event_group_type", "warp_event_group");
event->put("event_type", EventType::Warp);
event->put("destination_warp", 0);
event->put("destination_map_name", map_name);
event->put("elevation", 0);
@ -100,8 +125,6 @@ Event* Event::createNewWarpEvent(QString map_name)
Event* Event::createNewHealLocationEvent(QString map_name)
{
Event *event = new Event;
event->put("event_group_type", "heal_event_group");
event->put("event_type", EventType::HealLocation);
event->put("loc_name", QString(Map::mapConstantFromName(map_name)).remove(0,4));
event->put("id_name", map_name.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2").toUpper());
event->put("elevation", 3);
@ -115,8 +138,6 @@ Event* Event::createNewHealLocationEvent(QString map_name)
Event* Event::createNewTriggerEvent(Project *project)
{
Event *event = new Event;
event->put("event_group_type", "coord_event_group");
event->put("event_type", EventType::Trigger);
event->put("script_label", "NULL");
event->put("script_var", project->varNames.first());
event->put("script_var_value", "0");
@ -127,8 +148,6 @@ Event* Event::createNewTriggerEvent(Project *project)
Event* Event::createNewWeatherTriggerEvent(Project *project)
{
Event *event = new Event;
event->put("event_group_type", "coord_event_group");
event->put("event_type", EventType::WeatherTrigger);
event->put("weather", project->coordEventWeatherNames.first());
event->put("elevation", 0);
return event;
@ -137,8 +156,6 @@ Event* Event::createNewWeatherTriggerEvent(Project *project)
Event* Event::createNewSignEvent(Project *project)
{
Event *event = new Event;
event->put("event_group_type", "bg_event_group");
event->put("event_type", EventType::Sign);
event->put("player_facing_direction", project->bgEventFacingDirections.first());
event->put("script_label", "NULL");
event->put("elevation", 0);
@ -148,8 +165,6 @@ Event* Event::createNewSignEvent(Project *project)
Event* Event::createNewHiddenItemEvent(Project *project)
{
Event *event = new Event;
event->put("event_group_type", "bg_event_group");
event->put("event_type", EventType::HiddenItem);
event->put("item", project->itemNames.first());
event->put("flag", project->flagNames.first());
event->put("elevation", 3);
@ -165,8 +180,6 @@ Event* Event::createNewHiddenItemEvent(Project *project)
Event* Event::createNewSecretBaseEvent(Project *project)
{
Event *event = new Event;
event->put("event_group_type", "bg_event_group");
event->put("event_type", EventType::SecretBase);
event->put("secret_base_id", project->secretBaseIds.first());
event->put("elevation", 0);
return event;
@ -182,81 +195,76 @@ int Event::getPixelY()
return (this->y() * 16) - qMax(0, this->spriteHeight - 16);
}
static QMap<QString, bool> expectedObjectFields {
{"graphics_id", true},
{"x", true},
{"y", true},
{"elevation", true},
{"movement_type", true},
{"movement_range_x", true},
{"movement_range_y", true},
{"trainer_type", true},
{"trainer_sight_or_berry_tree_id", true},
{"script", true},
{"flag", true},
const QSet<QString> expectedObjectFields = {
"graphics_id",
"elevation",
"movement_type",
"movement_range_x",
"movement_range_y",
"trainer_type",
"trainer_sight_or_berry_tree_id",
"script",
"flag",
};
static QMap<QString, bool> expectedWarpFields {
{"x", true},
{"y", true},
{"elevation", true},
{"dest_map", true},
{"dest_warp_id", true},
const QSet<QString> expectedCloneObjectFields = {
"type",
"graphics_id",
"target_local_id",
"target_map",
};
static QMap<QString, bool> expectedTriggerFields {
{"type", true},
{"x", true},
{"y", true},
{"elevation", true},
{"var", true},
{"var_value", true},
{"script", true},
const QSet<QString> expectedWarpFields = {
"elevation",
"dest_map",
"dest_warp_id",
};
static QMap<QString, bool> expectedWeatherTriggerFields {
{"type", true},
{"x", true},
{"y", true},
{"elevation", true},
{"weather", true},
const QSet<QString> expectedTriggerFields = {
"type",
"elevation",
"var",
"var_value",
"script",
};
static QMap<QString, bool> expectedSignFields {
{"type", true},
{"x", true},
{"y", true},
{"elevation", true},
{"player_facing_dir", true},
{"script", true},
const QSet<QString> expectedWeatherTriggerFields = {
"type",
"elevation",
"weather",
};
static QMap<QString, bool> expectedHiddenItemFields {
{"type", true},
{"x", true},
{"y", true},
{"elevation", true},
{"item", true},
{"flag", true},
const QSet<QString> expectedSignFields = {
"type",
"elevation",
"player_facing_dir",
"script",
};
static QMap<QString, bool> expectedSecretBaseFields {
{"type", true},
{"x", true},
{"y", true},
{"elevation", true},
{"secret_base_id", true},
const QSet<QString> expectedHiddenItemFields = {
"type",
"elevation",
"item",
"flag",
};
QMap<QString, bool> Event::getExpectedFields()
const QSet<QString> expectedSecretBaseFields = {
"type",
"elevation",
"secret_base_id",
};
QSet<QString> Event::getExpectedFields()
{
QString type = this->get("event_type");
QMap<QString, bool> expectedFields = QMap<QString, bool>();
QSet<QString> expectedFields = QSet<QString>();
if (type == EventType::Object) {
expectedFields = expectedObjectFields;
if (projectConfig.getObjectEventInConnectionEnabled()) {
expectedFields.insert("in_connection", true);
if (projectConfig.getEventCloneObjectEnabled()) {
expectedFields.insert("type");
}
} else if (type == EventType::CloneObject) {
expectedFields = expectedCloneObjectFields;
} else if (type == EventType::Warp) {
expectedFields = expectedWarpFields;
} else if (type == EventType::Trigger) {
@ -268,23 +276,24 @@ QMap<QString, bool> Event::getExpectedFields()
} else if (type == EventType::HiddenItem) {
expectedFields = expectedHiddenItemFields;
if (projectConfig.getHiddenItemQuantityEnabled()) {
expectedFields.insert("quantity", true);
expectedFields.insert("quantity");
}
if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) {
expectedFields.insert("underfoot", true);
expectedFields.insert("underfoot");
}
} else if (type == EventType::SecretBase) {
expectedFields = expectedSecretBaseFields;
}
expectedFields << "x" << "y";
return expectedFields;
};
void Event::readCustomValues(QJsonObject values)
{
this->customValues.clear();
QMap<QString, bool> expectedValues = this->getExpectedFields();
QSet<QString> expectedFields = this->getExpectedFields();
for (QString key : values.keys()) {
if (!expectedValues.contains(key)) {
if (!expectedFields.contains(key)) {
this->customValues[key] = values[key].toString();
}
}
@ -301,24 +310,38 @@ void Event::addCustomValuesTo(OrderedJson::object *obj)
OrderedJson::object Event::buildObjectEventJSON()
{
OrderedJson::object eventObj;
eventObj["graphics_id"] = this->get("sprite");
if (projectConfig.getObjectEventInConnectionEnabled()) {
eventObj["in_connection"] = this->getInt("in_connection") > 0 || this->get("in_connection") == "TRUE";
OrderedJson::object objectObj;
if (projectConfig.getEventCloneObjectEnabled()) {
objectObj["type"] = "object";
}
eventObj["x"] = this->getS16("x");
eventObj["y"] = this->getS16("y");
eventObj["elevation"] = this->getInt("elevation");
eventObj["movement_type"] = this->get("movement_type");
eventObj["movement_range_x"] = this->getInt("radius_x");
eventObj["movement_range_y"] = this->getInt("radius_y");
eventObj["trainer_type"] = this->get("trainer_type");
eventObj["trainer_sight_or_berry_tree_id"] = this->get("sight_radius_tree_id");
eventObj["script"] = this->get("script_label");
eventObj["flag"] = this->get("event_flag");
this->addCustomValuesTo(&eventObj);
objectObj["graphics_id"] = this->get("sprite");
objectObj["x"] = this->getS16("x");
objectObj["y"] = this->getS16("y");
objectObj["elevation"] = this->getInt("elevation");
objectObj["movement_type"] = this->get("movement_type");
objectObj["movement_range_x"] = this->getInt("radius_x");
objectObj["movement_range_y"] = this->getInt("radius_y");
objectObj["trainer_type"] = this->get("trainer_type");
objectObj["trainer_sight_or_berry_tree_id"] = this->get("sight_radius_tree_id");
objectObj["script"] = this->get("script_label");
objectObj["flag"] = this->get("event_flag");
this->addCustomValuesTo(&objectObj);
return eventObj;
return objectObj;
}
OrderedJson::object Event::buildCloneObjectEventJSON(const QMap<QString, QString> &mapNamesToMapConstants)
{
OrderedJson::object cloneObj;
cloneObj["type"] = "clone";
cloneObj["graphics_id"] = this->get("sprite");
cloneObj["x"] = this->getS16("x");
cloneObj["y"] = this->getS16("y");
cloneObj["target_local_id"] = this->getInt("target_local_id");
cloneObj["target_map"] = mapNamesToMapConstants.value(this->get("target_map"));
this->addCustomValuesTo(&cloneObj);
return cloneObj;
}
OrderedJson::object Event::buildWarpEventJSON(const QMap<QString, QString> &mapNamesToMapConstants)
@ -442,3 +465,16 @@ void Event::setFrameFromMovement(QString facingDir) {
this->hFlip = true;
}
}
// All event groups excepts warps have IDs that start at 1
int Event::getIndexOffset(QString group_type) {
return (group_type == EventGroup::Warp) ? 0 : 1;
}
bool Event::isValidType(QString event_type) {
return EventTypeTable.contains(event_type);
}
QString Event::typeToGroup(QString event_type) {
return EventTypeTable.value(event_type, QString());
}

View file

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,286 @@
#include "regionmapeditcommands.h"
EditTilemap::EditTilemap(RegionMap *map, QByteArray oldTilemap, QByteArray newTilemap, unsigned actionId, QUndoCommand *parent)
: QUndoCommand(parent) {
setText("Edit Tilemap");
this->map = map;
this->oldTilemap = oldTilemap;
this->newTilemap = newTilemap;
this->actionId = actionId;
}
void EditTilemap::redo() {
QUndoCommand::redo();
if (!map) return;
map->setTilemap(this->newTilemap);
}
void EditTilemap::undo() {
if (!map) return;
map->setTilemap(this->oldTilemap);
QUndoCommand::undo();
}
bool EditTilemap::mergeWith(const QUndoCommand *command) {
const EditTilemap *other = static_cast<const EditTilemap *>(command);
if (this->map != other->map)
return false;
if (this->actionId != other->actionId)
return false;
newTilemap = other->newTilemap;
return true;
}
///
EditLayout::EditLayout(RegionMap *map, QString layer, int index, QList<LayoutSquare> oldLayout, QList<LayoutSquare> newLayout, QUndoCommand *parent)
: QUndoCommand(parent) {
setText("Edit Layout");
this->map = map;
this->layer = layer;
this->index = index;
this->oldLayout = oldLayout;
this->newLayout = newLayout;
}
void EditLayout::redo() {
QUndoCommand::redo();
if (!map) return;
map->setLayout(this->layer, this->newLayout);
}
void EditLayout::undo() {
if (!map) return;
map->setLayout(this->layer, this->oldLayout);
QUndoCommand::undo();
}
bool EditLayout::mergeWith(const QUndoCommand *command) {
const EditLayout *other = static_cast<const EditLayout *>(command);
if (this->map != other->map)
return false;
if (this->text() != other->text())
return false;
if (this->index != other->index)
return false;
this->newLayout = other->newLayout;
return true;
}
///
ResizeLayout::ResizeLayout(RegionMap *map, int oldWidth, int oldHeight, int newWidth, int newHeight,
QMap<QString, QList<LayoutSquare>> oldLayouts, QMap<QString, QList<LayoutSquare>> newLayouts, QUndoCommand *parent)
: QUndoCommand(parent) {
setText("Change Layout Dimensions");
this->map = map;
this->oldWidth = oldWidth;
this->oldHeight = oldHeight;
this->newWidth = newWidth;
this->newHeight = newHeight;
this->oldLayouts = oldLayouts;
this->newLayouts = newLayouts;
}
void ResizeLayout::redo() {
QUndoCommand::redo();
if (!map) return;
map->setLayoutDimensions(newWidth, newHeight, false);
map->setAllLayouts(this->newLayouts);
}
void ResizeLayout::undo() {
if (!map) return;
map->setLayoutDimensions(oldWidth, oldHeight, false);
map->setAllLayouts(this->oldLayouts);
QUndoCommand::undo();
}
bool ResizeLayout::mergeWith(const QUndoCommand *command) {
const ResizeLayout *other = static_cast<const ResizeLayout *>(command);
if (this->map != other->map)
return false;
// always allow consecutive dimension changes to merge
this->newWidth = other->newWidth;
this->newHeight = other->newHeight;
this->newLayouts = other->newLayouts;
return true;
}
///
EditEntry::EditEntry(RegionMap *map, QString section, MapSectionEntry oldEntry, MapSectionEntry newEntry, QUndoCommand *parent)
: QUndoCommand(parent) {
setText("Change Entry for " + section);
this->map = map;
this->section = section;
this->oldEntry = oldEntry;
this->newEntry = newEntry;
}
void EditEntry::redo() {
QUndoCommand::redo();
if (!map) return;
map->setEntry(section, newEntry);
}
void EditEntry::undo() {
if (!map) return;
map->setEntry(section, oldEntry);
QUndoCommand::undo();
}
bool EditEntry::mergeWith(const QUndoCommand *command) {
const EditEntry *other = static_cast<const EditEntry *>(command);
if (this->map != other->map)
return false;
if (this->section != other->section)
return false;
this->newEntry = other->newEntry;
return true;
}
///
AddEntry::AddEntry(RegionMap *map, QString section, MapSectionEntry oldEntry, MapSectionEntry newEntry, QUndoCommand *parent)
: EditEntry(map, section, oldEntry, newEntry, parent) {
setText("Add Entry for " + section);
}
void AddEntry::redo() {
QUndoCommand::redo();
if (!map) return;
map->setEntry(section, newEntry);
}
void AddEntry::undo() {
if (!map) return;
map->removeEntry(section);
QUndoCommand::undo();
}
///
RemoveEntry::RemoveEntry(RegionMap *map, QString section, MapSectionEntry oldEntry, MapSectionEntry newEntry, QUndoCommand *parent)
: EditEntry(map, section, oldEntry, newEntry, parent) {
setText("Remove Entry for " + section);
}
void RemoveEntry::redo() {
QUndoCommand::redo();
if (!map) return;
map->removeEntry(section);
}
void RemoveEntry::undo() {
if (!map) return;
map->setEntry(section, oldEntry);
QUndoCommand::undo();
}
///
ResizeTilemap::ResizeTilemap(RegionMap *map, QByteArray oldTilemap, QByteArray newTilemap,
int oldWidth, int oldHeight, int newWidth, int newHeight, QUndoCommand *parent)
: EditTilemap(map, oldTilemap, newTilemap, -1, parent) {
setText("Resize Tilemap");
this->oldWidth = oldWidth;
this->oldHeight = oldHeight;
this->newWidth = newWidth;
this->newHeight = newHeight;
}
void ResizeTilemap::redo() {
QUndoCommand::redo();
if (!map) return;
map->resizeTilemap(this->newWidth, this->newHeight, false);
map->setTilemap(this->newTilemap);
map->emitDisplay();
}
void ResizeTilemap::undo() {
if (!map) return;
map->resizeTilemap(this->oldWidth, this->oldHeight, false);
map->setTilemap(this->oldTilemap);
map->emitDisplay();
QUndoCommand::undo();
}
///
ClearEntries::ClearEntries(RegionMap *map, tsl::ordered_map<QString, MapSectionEntry> entries, QUndoCommand *parent) {
setText("Clear Entries");
this->map = map;
this->entries = entries;
}
void ClearEntries::redo() {
QUndoCommand::redo();
if (!map) return;
map->clearEntries();
}
void ClearEntries::undo() {
if (!map) return;
map->setEntries(entries);
QUndoCommand::undo();
}

View file

@ -188,7 +188,7 @@ bool Tileset::appendToMetatiles(QString metatileFile, QString friendlyName, bool
dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/metatiles.bin\"\n").arg(primaryString, friendlyName.toLower()));
dataString.append(QString("\n\t.align 1\n"));
dataString.append(QString("gMetatileAttributes_%1::\n").arg(friendlyName));
dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/metatile_attributes.bin\"").arg(primaryString, friendlyName.toLower()));
dataString.append(QString("\t.incbin \"data/tilesets/%1/%2/metatile_attributes.bin\"\n").arg(primaryString, friendlyName.toLower()));
file.write(dataString.toUtf8());
file.flush();
file.close();

View file

@ -2064,8 +2064,8 @@ DraggablePixmapItem* Editor::addNewEvent(QString event_type) {
bool Editor::eventLimitReached(Map *map, QString event_type)
{
if (project && map && !event_type.isEmpty()) {
if (event_type == EventType::Object)
return map->events.value("object_event_group").length() >= project->getMaxObjectEvents();
if (Event::typeToGroup(event_type) == EventGroup::Object)
return map->events.value(EventGroup::Object).length() >= project->getMaxObjectEvents();
}
return false;
}

View file

@ -349,12 +349,15 @@ void MainWindow::markMapEdited() {
}
}
void MainWindow::setWildEncountersUIEnabled(bool enabled) {
ui->actionUse_Encounter_Json->setChecked(enabled);
ui->mainTabBar->setTabEnabled(4, enabled);
}
void MainWindow::setProjectSpecificUIVisibility()
{
ui->actionUse_Encounter_Json->setChecked(projectConfig.getEncounterJsonActive());
ui->actionUse_Poryscript->setChecked(projectConfig.getUsePoryScript());
ui->mainTabBar->setTabEnabled(4, projectConfig.getEncounterJsonActive());
this->setWildEncountersUIEnabled(projectConfig.getEncounterJsonActive());
switch (projectConfig.getBaseGameVersion())
{
@ -385,27 +388,17 @@ void MainWindow::setProjectSpecificUIVisibility()
ui->label_AllowEscapeRope->setVisible(true);
// TODO: pokefirered is not set up for the Region Map Editor and vice versa.
// porymap will crash on attempt. Remove below once resolved
ui->actionRegion_Map_Editor->setVisible(false);
ui->actionRegion_Map_Editor->setVisible(true);
break;
}
if (projectConfig.getEventWeatherTriggerEnabled()) {
ui->newEventToolButton->newWeatherTriggerAction->setVisible(true);
} else {
ui->newEventToolButton->newWeatherTriggerAction->setVisible(false);
}
if (projectConfig.getEventSecretBaseEnabled()) {
ui->newEventToolButton->newSecretBaseAction->setVisible(true);
} else {
ui->newEventToolButton->newSecretBaseAction->setVisible(false);
}
if (projectConfig.getFloorNumberEnabled()) {
ui->spinBox_FloorNumber->setVisible(true);
ui->label_FloorNumber->setVisible(true);
} else {
ui->spinBox_FloorNumber->setVisible(false);
ui->label_FloorNumber->setVisible(false);
}
ui->newEventToolButton->newWeatherTriggerAction->setVisible(projectConfig.getEventWeatherTriggerEnabled());
ui->newEventToolButton->newSecretBaseAction->setVisible(projectConfig.getEventSecretBaseEnabled());
ui->newEventToolButton->newCloneObjectAction->setVisible(projectConfig.getEventCloneObjectEnabled());
bool floorNumEnabled = projectConfig.getFloorNumberEnabled();
ui->spinBox_FloorNumber->setVisible(floorNumEnabled);
ui->label_FloorNumber->setVisible(floorNumEnabled);
}
void MainWindow::mapSortOrder_changed(QAction *action)
@ -525,6 +518,7 @@ bool MainWindow::openProject(QString dir) {
editor->project = new Project(this);
QObject::connect(editor->project, &Project::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered);
QObject::connect(editor->project, &Project::mapCacheCleared, this, &MainWindow::onMapCacheCleared);
QObject::connect(editor->project, &Project::disableWildEncountersUI, [this]() { this->setWildEncountersUIEnabled(false); });
QObject::connect(editor->project, &Project::uncheckMonitorFilesAction, [this]() { ui->actionMonitor_Project_Files->setChecked(false); });
on_actionMonitor_Project_Files_triggered(porymapConfig.getMonitorFiles());
editor->project->set_root(dir);
@ -702,22 +696,22 @@ void MainWindow::refreshMapScene()
on_horizontalSlider_MetatileZoom_valueChanged(ui->horizontalSlider_MetatileZoom->value());
}
void MainWindow::openWarpMap(QString map_name, QString warp_num) {
void MainWindow::openWarpMap(QString map_name, QString event_id, QString event_group) {
// Ensure valid destination map name.
if (!editor->project->mapNames.contains(map_name)) {
logError(QString("Invalid warp destination map name '%1'").arg(map_name));
logError(QString("Invalid map name '%1'").arg(map_name));
return;
}
// Ensure valid destination warp number.
// Ensure valid event number.
bool ok;
int warpNum = warp_num.toInt(&ok, 0);
int event_index = event_id.toInt(&ok, 0);
if (!ok) {
logError(QString("Invalid warp number '%1' for destination map '%2'").arg(warp_num).arg(map_name));
logError(QString("Invalid event number '%1' for map '%2'").arg(event_id).arg(map_name));
return;
}
// Open the destination map, and select the target warp event.
// Open the destination map.
if (!setMap(map_name, true)) {
QMessageBox msgBox(this);
QString errorMsg = QString("There was an error opening map %1. Please see %2 for full error details.\n\n%3")
@ -728,11 +722,13 @@ void MainWindow::openWarpMap(QString map_name, QString warp_num) {
return;
}
QList<Event*> warp_events = editor->map->events["warp_event_group"];
if (warp_events.length() > warpNum) {
Event *warp_event = warp_events.at(warpNum);
// Select the target event.
event_index -= Event::getIndexOffset(event_group);
QList<Event*> events = editor->map->events[event_group];
if (event_index < events.length() && event_index >= 0) {
Event *event = events.at(event_index);
for (DraggablePixmapItem *item : editor->getObjects()) {
if (item->event == warp_event) {
if (item->event == event) {
editor->selected_events->clear();
editor->selected_events->append(item);
editor->updateSelectedEvents();
@ -1170,11 +1166,11 @@ void MainWindow::onAddNewMapToLayoutClick(QAction* triggeredAction)
}
void MainWindow::onNewMapCreated() {
QString newMapName = this->newmapprompt->map->name;
int newMapGroup = this->newmapprompt->group;
Map *newMap = this->newmapprompt->map;
bool existingLayout = this->newmapprompt->existingLayout;
bool importedMap = this->newmapprompt->importedMap;
QString newMapName = this->newMapPrompt->map->name;
int newMapGroup = this->newMapPrompt->group;
Map *newMap = this->newMapPrompt->map;
bool existingLayout = this->newMapPrompt->existingLayout;
bool importedMap = this->newMapPrompt->importedMap;
newMap = editor->project->addNewMapToGroup(newMapName, newMapGroup, newMap, existingLayout, importedMap);
@ -1199,52 +1195,52 @@ void MainWindow::onNewMapCreated() {
editor->save();// required
}
disconnect(this->newmapprompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated);
disconnect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated);
delete newMap;
}
void MainWindow::openNewMapPopupWindow(int type, QVariant data) {
if (!this->newmapprompt) {
this->newmapprompt = new NewMapPopup(this, this->editor->project);
if (!this->newMapPrompt) {
this->newMapPrompt = new NewMapPopup(this, this->editor->project);
}
if (!this->newmapprompt->isVisible()) {
this->newmapprompt->show();
if (!this->newMapPrompt->isVisible()) {
this->newMapPrompt->show();
} else {
this->newmapprompt->raise();
this->newmapprompt->activateWindow();
this->newMapPrompt->raise();
this->newMapPrompt->activateWindow();
}
switch (type)
{
case MapSortOrder::Group:
this->newmapprompt->init(type, data.toInt(), QString(), QString());
this->newMapPrompt->init(type, data.toInt(), QString(), QString());
break;
case MapSortOrder::Area:
this->newmapprompt->init(type, 0, data.toString(), QString());
this->newMapPrompt->init(type, 0, data.toString(), QString());
break;
case MapSortOrder::Layout:
this->newmapprompt->init(type, 0, QString(), data.toString());
this->newMapPrompt->init(type, 0, QString(), data.toString());
break;
}
connect(this->newmapprompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated);
this->newmapprompt->setAttribute(Qt::WA_DeleteOnClose);
connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated);
this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose);
}
void MainWindow::openNewMapPopupWindowImportMap(MapLayout *mapLayout) {
if (!this->newmapprompt) {
this->newmapprompt = new NewMapPopup(this, this->editor->project);
if (!this->newMapPrompt) {
this->newMapPrompt = new NewMapPopup(this, this->editor->project);
}
if (!this->newmapprompt->isVisible()) {
this->newmapprompt->show();
if (!this->newMapPrompt->isVisible()) {
this->newMapPrompt->show();
} else {
this->newmapprompt->raise();
this->newmapprompt->activateWindow();
this->newMapPrompt->raise();
this->newMapPrompt->activateWindow();
}
this->newmapprompt->initImportMap(mapLayout);
this->newMapPrompt->initImportMap(mapLayout);
connect(this->newmapprompt, SIGNAL(applied()), this, SLOT(onNewMapCreated()));
connect(this->newmapprompt, &QObject::destroyed, [=](QObject *) { this->newmapprompt = nullptr; });
this->newmapprompt->setAttribute(Qt::WA_DeleteOnClose);
connect(this->newMapPrompt, SIGNAL(applied()), this, SLOT(onNewMapCreated()));
connect(this->newMapPrompt, &QObject::destroyed, [=](QObject *) { this->newMapPrompt = nullptr; });
this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose);
}
void MainWindow::on_action_NewMap_triggered() {
@ -1802,15 +1798,12 @@ void MainWindow::connectSubEditorsToShortcutsEditor() {
connect(shortcutsEditor, &ShortcutsEditor::shortcutsSaved,
tilesetEditor, &TilesetEditor::applyUserShortcuts);
// TODO: Remove this check when the region map editor supports pokefirered.
if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokefirered) {
if (!regionMapEditor)
initRegionMapEditor();
if (regionMapEditor)
connect(shortcutsEditor, &ShortcutsEditor::shortcutsSaved,
regionMapEditor, &RegionMapEditor::applyUserShortcuts);
}
}
void MainWindow::on_actionPencil_triggered()
{
@ -1859,7 +1852,7 @@ void MainWindow::addNewEvent(QString event_type)
} else {
QMessageBox msgBox(this);
msgBox.setText("Failed to add new event");
if (event_type == EventType::Object) {
if (Event::typeToGroup(event_type) == EventGroup::Object) {
msgBox.setInformativeText(QString("The limit for object events (%1) has been reached.\n\n"
"This limit can be adjusted with OBJECT_EVENT_TEMPLATES_COUNT in 'include/constants/global.h'.")
.arg(editor->project->getMaxObjectEvents()));
@ -1879,57 +1872,20 @@ void MainWindow::updateObjects() {
selectedHealspot = nullptr;
ui->tabWidget_EventType->clear();
bool hasObjects = false;
bool hasWarps = false;
bool hasTriggers = false;
bool hasBGs = false;
bool hasHealspots = false;
for (DraggablePixmapItem *item : editor->getObjects())
{
QString event_type = item->event->get("event_type");
if (event_type == EventType::Object) {
hasObjects = true;
}
else if (event_type == EventType::Warp) {
hasWarps = true;
}
else if (event_type == EventType::Trigger || event_type == EventType::WeatherTrigger) {
hasTriggers = true;
}
else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) {
hasBGs = true;
}
else if (event_type == EventType::HealLocation) {
hasHealspots = true;
}
}
if (hasObjects)
{
if (editor->map->events.value(EventGroup::Object).length())
ui->tabWidget_EventType->addTab(eventTabObjectWidget, "Objects");
}
if (hasWarps)
{
if (editor->map->events.value(EventGroup::Warp).length())
ui->tabWidget_EventType->addTab(eventTabWarpWidget, "Warps");
}
if (hasTriggers)
{
if (editor->map->events.value(EventGroup::Coord).length())
ui->tabWidget_EventType->addTab(eventTabTriggerWidget, "Triggers");
}
if (hasBGs)
{
if (editor->map->events.value(EventGroup::Bg).length())
ui->tabWidget_EventType->addTab(eventTabBGWidget, "BGs");
}
if (hasHealspots)
{
if (editor->map->events.value(EventGroup::Heal).length())
ui->tabWidget_EventType->addTab(eventTabHealspotWidget, "Healspots");
}
updateSelectedObjects();
}
@ -1956,7 +1912,6 @@ void MainWindow::updateSelectedObjects() {
QList<EventPropertiesFrame *> frames;
bool inConnectionEnabled = projectConfig.getObjectEventInConnectionEnabled();
bool quantityEnabled = projectConfig.getHiddenItemQuantityEnabled();
bool underfootEnabled = projectConfig.getHiddenItemRequiresItemfinderEnabled();
bool respawnDataEnabled = projectConfig.getHealLocationRespawnDataEnabled();
@ -1991,9 +1946,7 @@ void MainWindow::updateSelectedObjects() {
QString event_type = item->event->get("event_type");
QString event_group_type = item->event->get("event_group_type");
QString map_name = item->event->get("map_name");
int event_offs;
if (event_type == EventType::Warp) { event_offs = 0; }
else { event_offs = 1; }
int event_offs = Event::getIndexOffset(event_group_type);
frame->ui->label_name->setText(QString("%1 Id").arg(event_type));
if (events.count() == 1)
@ -2021,7 +1974,6 @@ void MainWindow::updateSelectedObjects() {
field_labels["radius_y"] = "Movement Radius Y";
field_labels["trainer_type"] = "Trainer Type";
field_labels["sight_radius_tree_id"] = "Sight Radius / Berry Tree ID";
field_labels["in_connection"] = "In Connection";
field_labels["destination_warp"] = "Destination Warp";
field_labels["destination_map_name"] = "Destination Map";
field_labels["script_var"] = "Var";
@ -2035,13 +1987,15 @@ void MainWindow::updateSelectedObjects() {
field_labels["secret_base_id"] = "Secret Base Id";
field_labels["respawn_map"] = "Respawn Map";
field_labels["respawn_npc"] = "Respawn NPC";
field_labels["target_local_id"] = "Target Local Id";
field_labels["target_map"] = "Target Map";
QStringList fields;
if (event_type == EventType::Object) {
frame->ui->sprite->setVisible(true);
frame->ui->comboBox_sprite->addItems(editor->project->gfxNames);
frame->ui->comboBox_sprite->addItems(editor->project->gfxDefines.keys());
frame->ui->comboBox_sprite->setCurrentIndex(frame->ui->comboBox_sprite->findText(item->event->get("sprite")));
connect(frame->ui->comboBox_sprite, &QComboBox::currentTextChanged, item, &DraggablePixmapItem::set_sprite);
connect(frame->ui->comboBox_sprite, &QComboBox::currentTextChanged, this, &MainWindow::markMapEdited);
@ -2063,7 +2017,17 @@ void MainWindow::updateSelectedObjects() {
fields << "event_flag";
fields << "trainer_type";
fields << "sight_radius_tree_id";
if (inConnectionEnabled) fields << "in_connection";
}
else if (event_type == EventType::CloneObject) {
frame->ui->sprite->setVisible(true);
frame->ui->comboBox_sprite->setEnabled(false);
frame->ui->comboBox_sprite->addItem(item->event->get("sprite"));
frame->ui->spinBox_z->setVisible(false);
frame->ui->label_z->setVisible(false);
fields << "target_local_id";
fields << "target_map";
}
else if (event_type == EventType::Warp) {
fields << "destination_map_name";
@ -2101,8 +2065,8 @@ void MainWindow::updateSelectedObjects() {
}
// Some keys shouldn't use a combobox
QStringList spinKeys = {"quantity", "respawn_npc"};
QStringList checkKeys = {"underfoot", "in_connection"};
QStringList spinKeys = {"quantity", "respawn_npc", "target_local_id"};
QStringList checkKeys = {"underfoot"};
for (QString key : fields) {
QString value = item->event->get(key);
QWidget *widget = new QWidget(frame);
@ -2226,8 +2190,6 @@ void MainWindow::updateSelectedObjects() {
combo->setToolTip("The maximum sight range of a trainer,\n"
"OR the unique id of the berry tree.");
combo->setMinimumContentsLength(4);
} else if (key == "in_connection") {
check->setToolTip("Check if object is positioned in the connection to another map.");
} else if (key == "respawn_map") {
if (!editor->project->mapNames.contains(value)) {
combo->addItem(value);
@ -2239,6 +2201,27 @@ void MainWindow::updateSelectedObjects() {
"upon respawning after whiteout.");
spin->setMinimum(1);
spin->setMaximum(126);
} else if (key == "target_local_id") {
spin->setToolTip("event_object ID of the object being cloned.");
spin->setMinimum(1);
spin->setMaximum(126);
connect(spin, QOverload<int>::of(&NoScrollSpinBox::valueChanged), [item, frame](int value) {
item->event->put("target_local_id", value);
item->updatePixmap();
frame->ui->comboBox_sprite->setItemText(0, item->event->get("sprite"));
});
} else if (key == "target_map") {
if (!editor->project->mapNames.contains(value)) {
combo->addItem(value);
}
combo->addItems(editor->project->mapNames);
combo->setCurrentIndex(combo->findText(value));
combo->setToolTip("The name of the map that the object being cloned is on.");
connect(combo, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), this, [item, frame](QString value){
item->event->put("target_map", value);
item->updatePixmap();
frame->ui->comboBox_sprite->setItemText(0, item->event->get("sprite"));
});
} else {
combo->addItem(value);
}
@ -2323,27 +2306,27 @@ void MainWindow::updateSelectedObjects() {
{
QString event_group_type = events[0]->event->get("event_group_type");
if (event_group_type == "object_event_group") {
if (event_group_type == EventGroup::Object) {
scrollTarget = ui->scrollArea_Objects;
target = ui->scrollAreaWidgetContents_Objects;
ui->tabWidget_EventType->setCurrentWidget(ui->tab_Objects);
}
else if (event_group_type == "warp_event_group") {
else if (event_group_type == EventGroup::Warp) {
scrollTarget = ui->scrollArea_Warps;
target = ui->scrollAreaWidgetContents_Warps;
ui->tabWidget_EventType->setCurrentWidget(ui->tab_Warps);
}
else if (event_group_type == "coord_event_group") {
else if (event_group_type == EventGroup::Coord) {
scrollTarget = ui->scrollArea_Triggers;
target = ui->scrollAreaWidgetContents_Triggers;
ui->tabWidget_EventType->setCurrentWidget(ui->tab_Triggers);
}
else if (event_group_type == "bg_event_group") {
else if (event_group_type == EventGroup::Bg) {
scrollTarget = ui->scrollArea_BGs;
target = ui->scrollAreaWidgetContents_BGs;
ui->tabWidget_EventType->setCurrentWidget(ui->tab_BGs);
}
else if (event_group_type == "heal_event_group") {
else if (event_group_type == EventGroup::Heal) {
scrollTarget = ui->scrollArea_Healspots;
target = ui->scrollAreaWidgetContents_Healspots;
ui->tabWidget_EventType->setCurrentWidget(ui->tab_Healspots);
@ -2400,23 +2383,23 @@ QString MainWindow::getEventGroupFromTabWidget(QWidget *tab)
QString ret = "";
if (tab == eventTabObjectWidget)
{
ret = "object_event_group";
ret = EventGroup::Object;
}
else if (tab == eventTabWarpWidget)
{
ret = "warp_event_group";
ret = EventGroup::Warp;
}
else if (tab == eventTabTriggerWidget)
{
ret = "coord_event_group";
ret = EventGroup::Coord;
}
else if (tab == eventTabBGWidget)
{
ret = "bg_event_group";
ret = EventGroup::Bg;
}
else if (tab == eventTabHealspotWidget)
{
ret = "heal_event_group";
ret = EventGroup::Heal;
}
return ret;
}
@ -2426,23 +2409,23 @@ void MainWindow::eventTabChanged(int index) {
QString group = getEventGroupFromTabWidget(ui->tabWidget_EventType->widget(index));
DraggablePixmapItem *selectedEvent = nullptr;
if (group == "object_event_group") {
if (group == EventGroup::Object) {
selectedEvent = selectedObject;
ui->newEventToolButton->setDefaultAction(ui->newEventToolButton->newObjectAction);
}
else if (group == "warp_event_group") {
else if (group == EventGroup::Warp) {
selectedEvent = selectedWarp;
ui->newEventToolButton->setDefaultAction(ui->newEventToolButton->newWarpAction);
}
else if (group == "coord_event_group") {
else if (group == EventGroup::Coord) {
selectedEvent = selectedTrigger;
ui->newEventToolButton->setDefaultAction(ui->newEventToolButton->newTriggerAction);
}
else if (group == "bg_event_group") {
else if (group == EventGroup::Bg) {
selectedEvent = selectedBG;
ui->newEventToolButton->setDefaultAction(ui->newEventToolButton->newSignAction);
}
else if (group == "heal_event_group") {
else if (group == EventGroup::Heal) {
selectedEvent = selectedHealspot;
}
@ -2468,7 +2451,7 @@ void MainWindow::eventTabChanged(int index) {
void MainWindow::selectedEventIndexChanged(int index)
{
QString group = getEventGroupFromTabWidget(ui->tabWidget_EventType->currentWidget());
int event_offs = group == "warp_event_group" ? 0 : 1;
int event_offs = Event::getIndexOffset(group);
Event *event = editor->map->events.value(group).at(index - event_offs);
DraggablePixmapItem *selectedEvent = nullptr;
for (QGraphicsItem *child : editor->events_group->childItems()) {
@ -2502,7 +2485,7 @@ void MainWindow::on_toolButton_deleteObject_clicked() {
int numDeleted = 0;
for (DraggablePixmapItem *item : *editor->selected_events) {
QString event_group = item->event->get("event_group_type");
if (event_group != "heal_event_group") {
if (event_group != EventGroup::Heal) {
// Get the index for the event that should be selected after this event has been deleted.
// If it's at the end of the list, select the previous event, otherwise select the next one.
// Don't bother getting the event to select if there are still more events to delete
@ -3164,8 +3147,12 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
bool MainWindow::initRegionMapEditor() {
this->regionMapEditor = new RegionMapEditor(this, this->editor->project);
bool success = this->regionMapEditor->loadRegionMapData()
&& this->regionMapEditor->loadCityMaps();
this->regionMapEditor->setAttribute(Qt::WA_DeleteOnClose);
connect(this->regionMapEditor, &QObject::destroyed, [this](){
this->regionMapEditor = nullptr;
});
bool success = this->regionMapEditor->load();
if (!success) {
delete this->regionMapEditor;
this->regionMapEditor = nullptr;
@ -3182,16 +3169,26 @@ bool MainWindow::initRegionMapEditor() {
}
void MainWindow::closeSupplementaryWindows() {
if (this->tilesetEditor)
if (this->tilesetEditor) {
delete this->tilesetEditor;
if (this->regionMapEditor)
this->tilesetEditor = nullptr;
}
if (this->regionMapEditor) {
delete this->regionMapEditor;
if (this->mapImageExporter)
this->regionMapEditor = nullptr;
}
if (this->mapImageExporter) {
delete this->mapImageExporter;
if (this->newmapprompt)
delete this->newmapprompt;
if (this->shortcutsEditor)
this->mapImageExporter = nullptr;
}
if (this->newMapPrompt) {
delete this->newMapPrompt;
this->newMapPrompt = nullptr;
}
if (this->shortcutsEditor) {
delete this->shortcutsEditor;
this->shortcutsEditor = nullptr;
}
}
void MainWindow::closeEvent(QCloseEvent *event) {

View file

@ -833,7 +833,7 @@ void MainWindow::saveMetatileAttributesByMetatileId(int metatileId) {
this->editor->project->saveTilesetMetatileAttributes(tileset);
// If the Tileset Editor is currently displaying the updated metatile, refresh it
if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatile() == metatileId)
if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatileId() == metatileId)
this->tilesetEditor->updateTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label);
}
@ -863,7 +863,7 @@ void MainWindow::setMetatileLabel(int metatileId, QString label) {
return;
}
if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatile() == metatileId) {
if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatileId() == metatileId) {
this->tilesetEditor->setMetatileLabel(label);
} else if (metatile->label != label) {
metatile->label = label;

View file

@ -211,16 +211,17 @@ bool Project::loadMapData(Map* map) {
map->sharedScriptsMap = mapObj["shared_scripts_map"].toString();
// Events
map->events["object_event_group"].clear();
map->events[EventGroup::Object].clear();
QJsonArray objectEventsArr = mapObj["object_events"].toArray();
bool hasCloneObjects = projectConfig.getEventCloneObjectEnabled();
for (int i = 0; i < objectEventsArr.size(); i++) {
QJsonObject event = objectEventsArr[i].toObject();
// If clone objects are not enabled then no type field is present
QString type = hasCloneObjects ? event["type"].toString() : "object";
if (type.isEmpty() || type == "object") {
Event *object = new Event(event, EventType::Object);
object->put("map_name", map->name);
object->put("sprite", event["graphics_id"].toString());
if (projectConfig.getObjectEventInConnectionEnabled()) {
object->put("in_connection", event["in_connection"].toBool());
}
object->put("x", QString::number(event["x"].toInt()));
object->put("y", QString::number(event["y"].toInt()));
object->put("elevation", QString::number(event["elevation"].toInt()));
@ -231,11 +232,35 @@ bool Project::loadMapData(Map* map) {
object->put("sight_radius_tree_id", event["trainer_sight_or_berry_tree_id"].toString());
object->put("script_label", event["script"].toString());
object->put("event_flag", event["flag"].toString());
object->put("event_group_type", "object_event_group");
map->events["object_event_group"].append(object);
object->put("event_group_type", EventGroup::Object);
map->events[EventGroup::Object].append(object);
} else if (type == "clone") {
Event *object = new Event(event, EventType::CloneObject);
object->put("map_name", map->name);
object->put("sprite", event["graphics_id"].toString());
object->put("x", QString::number(event["x"].toInt()));
object->put("y", QString::number(event["y"].toInt()));
object->put("target_local_id", QString::number(event["target_local_id"].toInt()));
// Ensure the target map constant is valid before adding it to the events.
QString mapConstant = event["target_map"].toString();
if (mapConstantsToMapNames.contains(mapConstant)) {
object->put("target_map", mapConstantsToMapNames.value(mapConstant));
object->put("event_group_type", EventGroup::Object);
map->events[EventGroup::Object].append(object);
} else if (mapConstant == NONE_MAP_CONSTANT) {
object->put("target_map", NONE_MAP_NAME);
object->put("event_group_type", EventGroup::Object);
map->events[EventGroup::Object].append(object);
} else {
logError(QString("Destination map constant '%1' is invalid").arg(mapConstant));
}
} else {
logError(QString("Map %1 object_event %2 has invalid type '%3'. Must be 'object' or 'clone'.").arg(map->name).arg(i).arg(type));
}
}
map->events["warp_event_group"].clear();
map->events[EventGroup::Warp].clear();
QJsonArray warpEventsArr = mapObj["warp_events"].toArray();
for (int i = 0; i < warpEventsArr.size(); i++) {
QJsonObject event = warpEventsArr[i].toObject();
@ -250,18 +275,18 @@ bool Project::loadMapData(Map* map) {
QString mapConstant = event["dest_map"].toString();
if (mapConstantsToMapNames.contains(mapConstant)) {
warp->put("destination_map_name", mapConstantsToMapNames.value(mapConstant));
warp->put("event_group_type", "warp_event_group");
map->events["warp_event_group"].append(warp);
warp->put("event_group_type", EventGroup::Warp);
map->events[EventGroup::Warp].append(warp);
} else if (mapConstant == NONE_MAP_CONSTANT) {
warp->put("destination_map_name", NONE_MAP_NAME);
warp->put("event_group_type", "warp_event_group");
map->events["warp_event_group"].append(warp);
warp->put("event_group_type", EventGroup::Warp);
map->events[EventGroup::Warp].append(warp);
} else {
logError(QString("Destination map constant '%1' is invalid for warp").arg(mapConstant));
}
}
map->events["heal_event_group"].clear();
map->events[EventGroup::Heal].clear();
for (auto it = healLocations.begin(); it != healLocations.end(); it++) {
HealLocation loc = *it;
@ -277,18 +302,18 @@ bool Project::loadMapData(Map* map) {
heal->put("index", loc.index);
heal->put("elevation", 3); // TODO: change this?
heal->put("destination_map_name", mapConstantsToMapNames.value(map->name));
heal->put("event_group_type", "heal_event_group");
heal->put("event_group_type", EventGroup::Heal);
heal->put("event_type", EventType::HealLocation);
if (projectConfig.getHealLocationRespawnDataEnabled()) {
heal->put("respawn_map", mapConstantsToMapNames.value(QString("MAP_" + loc.respawnMap)));
heal->put("respawn_npc", loc.respawnNPC);
}
map->events["heal_event_group"].append(heal);
map->events[EventGroup::Heal].append(heal);
}
}
map->events["coord_event_group"].clear();
map->events[EventGroup::Coord].clear();
QJsonArray coordEventsArr = mapObj["coord_events"].toArray();
for (int i = 0; i < coordEventsArr.size(); i++) {
QJsonObject event = coordEventsArr[i].toObject();
@ -302,8 +327,8 @@ bool Project::loadMapData(Map* map) {
coord->put("script_var", event["var"].toString());
coord->put("script_var_value", event["var_value"].toString());
coord->put("script_label", event["script"].toString());
coord->put("event_group_type", "coord_event_group");
map->events["coord_event_group"].append(coord);
coord->put("event_group_type", EventGroup::Coord);
map->events[EventGroup::Coord].append(coord);
} else if (type == "weather") {
Event *coord = new Event(event, EventType::WeatherTrigger);
coord->put("map_name", map->name);
@ -311,15 +336,15 @@ bool Project::loadMapData(Map* map) {
coord->put("y", QString::number(event["y"].toInt()));
coord->put("elevation", QString::number(event["elevation"].toInt()));
coord->put("weather", event["weather"].toString());
coord->put("event_group_type", "coord_event_group");
coord->put("event_group_type", EventGroup::Coord);
coord->put("event_type", EventType::WeatherTrigger);
map->events["coord_event_group"].append(coord);
map->events[EventGroup::Coord].append(coord);
} else {
logError(QString("Map %1 coord_event %2 has invalid type '%3'. Must be 'trigger' or 'weather'.").arg(map->name).arg(i).arg(type));
}
}
map->events["bg_event_group"].clear();
map->events[EventGroup::Bg].clear();
QJsonArray bgEventsArr = mapObj["bg_events"].toArray();
for (int i = 0; i < bgEventsArr.size(); i++) {
QJsonObject event = bgEventsArr[i].toObject();
@ -332,8 +357,8 @@ bool Project::loadMapData(Map* map) {
bg->put("elevation", QString::number(event["elevation"].toInt()));
bg->put("player_facing_direction", event["player_facing_dir"].toString());
bg->put("script_label", event["script"].toString());
bg->put("event_group_type", "bg_event_group");
map->events["bg_event_group"].append(bg);
bg->put("event_group_type", EventGroup::Bg);
map->events[EventGroup::Bg].append(bg);
} else if (type == "hidden_item") {
Event *bg = new Event(event, EventType::HiddenItem);
bg->put("map_name", map->name);
@ -348,8 +373,8 @@ bool Project::loadMapData(Map* map) {
if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) {
bg->put("underfoot", event["underfoot"].toBool());
}
bg->put("event_group_type", "bg_event_group");
map->events["bg_event_group"].append(bg);
bg->put("event_group_type", EventGroup::Bg);
map->events[EventGroup::Bg].append(bg);
} else if (type == "secret_base") {
Event *bg = new Event(event, EventType::SecretBase);
bg->put("map_name", map->name);
@ -357,8 +382,8 @@ bool Project::loadMapData(Map* map) {
bg->put("y", QString::number(event["y"].toInt()));
bg->put("elevation", QString::number(event["elevation"].toInt()));
bg->put("secret_base_id", event["secret_base_id"].toString());
bg->put("event_group_type", "bg_event_group");
map->events["bg_event_group"].append(bg);
bg->put("event_group_type", EventGroup::Bg);
map->events[EventGroup::Bg].append(bg);
} else {
logError(QString("Map %1 bg_event %2 has invalid type '%3'. Must be 'sign', 'hidden_item', or 'secret_base'.").arg(map->name).arg(i).arg(type));
}
@ -861,8 +886,8 @@ void Project::saveHealLocationStruct(Map *map) {
}
// set new location in healLocations list
if (map->events["heal_event_group"].length() > 0) {
for (Event *healEvent : map->events["heal_event_group"]) {
if (map->events[EventGroup::Heal].length() > 0) {
for (Event *healEvent : map->events[EventGroup::Heal]) {
HealLocation hl = HealLocation::fromEvent(healEvent);
healLocations[hl.index - 1] = hl;
}
@ -1333,17 +1358,23 @@ void Project::saveMap(Map *map) {
if (map->sharedEventsMap.isEmpty()) {
// Object events
OrderedJson::array objectEventsArr;
for (int i = 0; i < map->events["object_event_group"].length(); i++) {
Event *object_event = map->events["object_event_group"].value(i);
OrderedJson::object eventObj = object_event->buildObjectEventJSON();
objectEventsArr.push_back(eventObj);
for (int i = 0; i < map->events[EventGroup::Object].length(); i++) {
Event *event = map->events[EventGroup::Object].value(i);
QString event_type = event->get("event_type");
OrderedJson::object jsonObj;
if (event_type == EventType::Object) {
jsonObj = event->buildObjectEventJSON();
} else if (event_type == EventType::CloneObject) {
jsonObj = event->buildCloneObjectEventJSON(mapNamesToMapConstants);
}
objectEventsArr.push_back(jsonObj);
}
mapObj["object_events"] = objectEventsArr;
// Warp events
OrderedJson::array warpEventsArr;
for (int i = 0; i < map->events["warp_event_group"].length(); i++) {
Event *warp_event = map->events["warp_event_group"].value(i);
for (int i = 0; i < map->events[EventGroup::Warp].length(); i++) {
Event *warp_event = map->events[EventGroup::Warp].value(i);
OrderedJson::object warpObj = warp_event->buildWarpEventJSON(mapNamesToMapConstants);
warpEventsArr.append(warpObj);
}
@ -1351,8 +1382,8 @@ void Project::saveMap(Map *map) {
// Coord events
OrderedJson::array coordEventsArr;
for (int i = 0; i < map->events["coord_event_group"].length(); i++) {
Event *event = map->events["coord_event_group"].value(i);
for (int i = 0; i < map->events[EventGroup::Coord].length(); i++) {
Event *event = map->events[EventGroup::Coord].value(i);
QString event_type = event->get("event_type");
if (event_type == EventType::Trigger) {
OrderedJson::object triggerObj = event->buildTriggerEventJSON();
@ -1366,8 +1397,8 @@ void Project::saveMap(Map *map) {
// Bg Events
OrderedJson::array bgEventsArr;
for (int i = 0; i < map->events["bg_event_group"].length(); i++) {
Event *event = map->events["bg_event_group"].value(i);
for (int i = 0; i < map->events[EventGroup::Bg].length(); i++) {
Event *event = map->events[EventGroup::Bg].value(i);
QString event_type = event->get("event_type");
if (event_type == EventType::Sign) {
OrderedJson::object signObj = event->buildSignEventJSON();
@ -1692,8 +1723,12 @@ bool Project::readWildMonData() {
OrderedJson::object wildMonObj;
if (!parser.tryParseOrderedJsonFile(&wildMonObj, wildMonJsonFilepath)) {
logError(QString("Failed to read wild encounters from %1").arg(wildMonJsonFilepath));
return false;
// Failing to read wild encounters data is not a critical error, just disable the
// encounter editor and log a warning in case the user intended to have this data.
projectConfig.setEncounterJsonActive(false);
emit disableWildEncountersUI();
logWarn(QString("Failed to read wild encounters from %1").arg(wildMonJsonFilepath));
return true;
}
for (OrderedJson subObjectRef : wildMonObj["wild_encounter_groups"].array_items()) {
@ -2248,9 +2283,8 @@ bool Project::readObjEventGfxConstants() {
QStringList objEventGfxPrefixes("\\bOBJ_EVENT_GFX_");
QString filename = "include/constants/event_objects.h";
fileWatcher.addPath(root + "/" + filename);
QMap<QString, int> gfxDefines = parser.readCDefines(filename, objEventGfxPrefixes);
this->gfxNames = gfxDefines.keys();
if (this->gfxNames.isEmpty()) {
this->gfxDefines = parser.readCDefines(filename, objEventGfxPrefixes);
if (this->gfxDefines.isEmpty()) {
logError(QString("Failed to read object event graphics constants from %1.").arg(filename));
return false;
}
@ -2380,25 +2414,48 @@ void Project::setEventPixmap(Event * event, bool forceLoad) {
event->spriteHeight = 16;
event->usingSprite = false;
QString group_type = event->get("event_group_type");
if (group_type == EventGroup::Object) {
QString gfxName;
QString movement;
QString event_type = event->get("event_type");
if (event_type == EventType::Object) {
QString gfxName = event->get("sprite");
if (event_type == EventType::CloneObject) {
// Try to get the targeted object to clone
int eventIndex = event->getInt("target_local_id") - 1;
Map * clonedMap = getMap(event->get("target_map"));
Event * clonedEvent = clonedMap ? clonedMap->events[EventGroup::Object].value(eventIndex, nullptr) : nullptr;
if (clonedEvent && clonedEvent->get("event_type") == EventType::Object) {
// Get graphics data from cloned object
gfxName = clonedEvent->get("sprite");
movement = clonedEvent->get("movement_type");
} else {
// Invalid object specified, use default graphics data (as would be shown in-game)
gfxName = gfxDefines.key(0);
movement = movementTypes.first();
}
// Update clone object's sprite text to match target object
event->put("sprite", gfxName);
} else {
// Get graphics data of regular object
gfxName = event->get("sprite");
movement = event->get("movement_type");
}
EventGraphics * eventGfx = eventGraphicsMap.value(gfxName, nullptr);
if (!eventGfx || eventGfx->spritesheet.isNull()) {
// No sprite associated with this gfx constant.
// Use default sprite instead.
event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(0, 0, 16, 16);
} else {
event->setFrameFromMovement(facingDirections.value(event->get("movement_type")));
event->setFrameFromMovement(facingDirections.value(movement));
event->setPixmapFromSpritesheet(eventGfx->spritesheet, eventGfx->spriteWidth, eventGfx->spriteHeight, eventGfx->inanimate);
}
} else if (event_type == EventType::Warp) {
} else if (group_type == EventGroup::Warp) {
event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(16, 0, 16, 16);
} else if (event_type == EventType::Trigger || event_type == EventType::WeatherTrigger) {
} else if (group_type == EventGroup::Coord) {
event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(32, 0, 16, 16);
} else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) {
} else if (group_type == EventGroup::Bg) {
event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(48, 0, 16, 16);
} else if (event_type == EventType::HealLocation) {
} else if (group_type == EventGroup::Heal) {
event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(64, 0, 16, 16);
}
}
@ -2413,7 +2470,8 @@ bool Project::readEventGraphics() {
qDeleteAll(eventGraphicsMap);
eventGraphicsMap.clear();
for (QString gfxName : this->gfxNames) {
QStringList gfxNames = gfxDefines.keys();
for (QString gfxName : gfxNames) {
EventGraphics * eventGraphics = new EventGraphics;
QString info_label = pointerHash[gfxName].replace("&", "");
@ -2473,8 +2531,8 @@ bool Project::readSpeciesIconPaths() {
void Project::saveMapHealEvents(Map *map) {
// save heal event changes
if (map->events["heal_event_group"].length() > 0) {
for (Event *healEvent : map->events["heal_event_group"]) {
if (map->events[EventGroup::Heal].length() > 0) {
for (Event *healEvent : map->events[EventGroup::Heal]) {
HealLocation hl = HealLocation::fromEvent(healEvent);
healLocations[hl.index - 1] = hl;
}
@ -2483,11 +2541,11 @@ void Project::saveMapHealEvents(Map *map) {
}
void Project::setNewMapEvents(Map *map) {
map->events["object_event_group"].clear();
map->events["warp_event_group"].clear();
map->events["heal_event_group"].clear();
map->events["coord_event_group"].clear();
map->events["bg_event_group"].clear();
map->events[EventGroup::Object].clear();
map->events[EventGroup::Warp].clear();
map->events[EventGroup::Heal].clear();
map->events[EventGroup::Coord].clear();
map->events[EventGroup::Bg].clear();
}
int Project::getNumTilesPrimary()

View file

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

View file

@ -102,17 +102,24 @@ void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) {
}
void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
if (this->event->get("event_type") == EventType::Warp) {
QString eventType = this->event->get("event_type");
if (eventType == EventType::Warp) {
QString destMap = this->event->get("destination_map_name");
if (destMap != NONE_MAP_NAME) {
emit editor->warpEventDoubleClicked(this->event->get("destination_map_name"), this->event->get("destination_warp"));
emit editor->warpEventDoubleClicked(destMap, this->event->get("destination_warp"), EventGroup::Warp);
}
}
else if (this->event->get("event_type") == EventType::SecretBase) {
else if (eventType == EventType::CloneObject) {
QString destMap = this->event->get("target_map");
if (destMap != NONE_MAP_NAME) {
emit editor->warpEventDoubleClicked(destMap, this->event->get("target_local_id"), EventGroup::Object);
}
}
else if (eventType == EventType::SecretBase) {
QString baseId = this->event->get("secret_base_id");
QString destMap = editor->project->mapConstantsToMapNames.value("MAP_" + baseId.left(baseId.lastIndexOf("_")));
if (destMap != NONE_MAP_NAME) {
emit editor->warpEventDoubleClicked(destMap, "0");
emit editor->warpEventDoubleClicked(destMap, "0", EventGroup::Warp);
}
}
}

View file

@ -371,11 +371,11 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
for (Event *event : events) {
editor->project->setEventPixmap(event);
QString group = event->get("event_group_type");
if ((showObjects && group == "object_event_group")
|| (showWarps && group == "warp_event_group")
|| (showBGs && group == "bg_event_group")
|| (showTriggers && group == "coord_event_group")
|| (showHealSpots && group == "heal_event_group"))
if ((showObjects && group == EventGroup::Object)
|| (showWarps && group == EventGroup::Warp)
|| (showBGs && group == EventGroup::Bg)
|| (showTriggers && group == EventGroup::Coord)
|| (showHealSpots && group == EventGroup::Heal))
eventPainter.drawImage(QPoint(event->getPixelX(), event->getPixelY()), event->pixmap.toImage());
}
eventPainter.end();

View file

@ -18,6 +18,10 @@ void NewEventToolButton::init()
this->newObjectAction->setIcon(QIcon(":/icons/add.ico"));
connect(this->newObjectAction, &QAction::triggered, this, &NewEventToolButton::newObject);
this->newCloneObjectAction = new QAction("New Clone Object", this);
this->newCloneObjectAction->setIcon(QIcon(":/icons/add.ico"));
connect(this->newCloneObjectAction, &QAction::triggered, this, &NewEventToolButton::newCloneObject);
this->newWarpAction = new QAction("New Warp", this);
this->newWarpAction->setIcon(QIcon(":/icons/add.ico"));
connect(this->newWarpAction, &QAction::triggered, this, &NewEventToolButton::newWarp);
@ -50,6 +54,7 @@ void NewEventToolButton::init()
QMenu *alignMenu = new QMenu();
alignMenu->addAction(this->newObjectAction);
alignMenu->addAction(this->newCloneObjectAction);
alignMenu->addAction(this->newWarpAction);
//alignMenu->addAction(this->newHealLocationAction);
alignMenu->addAction(this->newTriggerAction);
@ -72,6 +77,12 @@ void NewEventToolButton::newObject()
emit newEventAdded(this->selectedEventType);
}
void NewEventToolButton::newCloneObject()
{
this->selectedEventType = EventType::CloneObject;
emit newEventAdded(this->selectedEventType);
}
void NewEventToolButton::newWarp()
{
this->selectedEventType = EventType::Warp;

View file

@ -156,6 +156,8 @@ void NewMapPopup::setDefaultValuesImportMap(MapLayout *mapLayout) {
ui->comboBox_NewMap_Group->addItems(project->groupNames);
ui->comboBox_NewMap_Group->setCurrentText(project->groupNames.at(0));
ui->comboBox_Song->addItems(project->songNames);
ui->spinBox_NewMap_Width->setValue(mapLayout->width.toInt(nullptr, 0));
ui->spinBox_NewMap_Height->setValue(mapLayout->height.toInt(nullptr, 0));
ui->comboBox_NewMap_Primary_Tileset->setCurrentText(mapLayout->tileset_primary_label);

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -1,15 +1,16 @@
#include "regionmappixmapitem.h"
#include "regionmapeditcommands.h"
void RegionMapPixmapItem::draw() {
if (!region_map) return;
QImage image(region_map->width() * 8, region_map->height() * 8, QImage::Format_RGBA8888);
QImage image(region_map->pixelWidth(), region_map->pixelHeight(), QImage::Format_RGBA8888);
QPainter painter(&image);
for (int i = 0; i < region_map->map_squares.size(); i++) {
QImage img = this->tile_selector->tileImg(region_map->map_squares[i].tile_img_id);
int x = i % region_map->width();
int y = i / region_map->width();
for (int i = 0; i < region_map->tilemapSize(); i++) {
QImage img = this->tile_selector->tileImg(region_map->getTile(i));
int x = i % region_map->tilemapWidth();
int y = i / region_map->tilemapWidth();
QPoint pos = QPoint(x * 8, y * 8);
painter.drawImage(pos, img);
}
@ -23,8 +24,77 @@ void RegionMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
QPointF pos = event->pos();
int x = static_cast<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
int index = x + y * region_map->width();
this->region_map->map_squares[index].tile_img_id = this->tile_selector->selectedTile;
int index = x + y * region_map->tilemapWidth();
this->region_map->setTileData(index,
this->tile_selector->selectedTile,
this->tile_selector->tile_hFlip,
this->tile_selector->tile_vFlip,
this->tile_selector->tile_palette
);
draw();
}
}
void RegionMapPixmapItem::floodFill(int x, int y, std::shared_ptr<TilemapTile> oldTile, std::shared_ptr<TilemapTile> newTile) {
// out of bounds
if (x < 0
|| y < 0
|| x >= this->region_map->tilemapWidth()
|| y >= this->region_map->tilemapHeight()) {
return;
}
auto tile = this->region_map->getTile(x, y);
if (!tile->operator==(*oldTile) || tile->operator==(*newTile)) {
return;
}
int index = x + y * this->region_map->tilemapWidth();
this->region_map->setTileData(index,
newTile->id(),
newTile->hFlip(),
newTile->vFlip(),
newTile->palette()
);
floodFill(x + 1, y, oldTile, newTile);
floodFill(x - 1, y, oldTile, newTile);
floodFill(x, y + 1, oldTile, newTile);
floodFill(x, y - 1, oldTile, newTile);
}
void RegionMapPixmapItem::fill(QGraphicsSceneMouseEvent *event) {
if (region_map) {
QPointF pos = event->pos();
int x = static_cast<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
int index = x + y * this->region_map->tilemapWidth();
std::shared_ptr<TilemapTile> currentTile, newTile, oldTile;
currentTile = this->region_map->getTile(index);
switch(this->tile_selector->format) {
case TilemapFormat::Plain:
oldTile = std::make_shared<PlainTile>(currentTile->raw());
newTile = std::make_shared<PlainTile>(currentTile->raw());
newTile->setId(this->tile_selector->selectedTile);
break;
case TilemapFormat::BPP_4:
oldTile = std::make_shared<BPP4Tile>(currentTile->raw());
newTile = std::make_shared<BPP4Tile>(currentTile->raw());
newTile->setId(this->tile_selector->selectedTile);
newTile->setHFlip(this->tile_selector->tile_hFlip);
newTile->setVFlip(this->tile_selector->tile_vFlip);
newTile->setPalette(this->tile_selector->tile_palette);
break;
case TilemapFormat::BPP_8:
oldTile = std::make_shared<BPP8Tile>(currentTile->raw());
newTile = std::make_shared<BPP8Tile>(currentTile->raw());
newTile->setId(this->tile_selector->selectedTile);
newTile->setId(this->tile_selector->selectedTile);
newTile->setHFlip(this->tile_selector->tile_hFlip);
newTile->setVFlip(this->tile_selector->tile_vFlip);
break;
}
floodFill(x, y, oldTile, newTile);
draw();
}
}
@ -54,8 +124,8 @@ void RegionMapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
QPointF pos = event->pos();
int x = static_cast<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
if (x < this->region_map->width() && x >= 0
&& y < this->region_map->height() && y >= 0) {
if (x < this->region_map->tilemapWidth() && x >= 0
&& y < this->region_map->tilemapHeight() && y >= 0) {
emit this->hoveredRegionMapTileChanged(x, y);
emit mouseEvent(event, this);
}

View file

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

View file

@ -3,16 +3,16 @@
#include <QDebug>
void TilemapTileSelector::draw() {
size_t width_ = this->tilemap.width();
size_t width_ = this->tileset.width();
this->pixelWidth = width_;
size_t height_ = this->tilemap.height();
size_t height_ = this->tileset.height();
this->pixelHeight = height_;
size_t ntiles_ = (width_/8) * (height_/8);
this->numTilesWide = width_ / 8;
this->numTiles = ntiles_;
this->setPixmap(tilemap);
this->setPixmap(QPixmap::fromImage(this->setPalette(this->tile_palette)));
this->drawSelection();
}
@ -20,6 +20,7 @@ void TilemapTileSelector::select(unsigned tileId) {
QPoint coords = this->getTileIdCoords(tileId);
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
this->selectedTile = tileId;
this->drawSelection();
emit selectedTileChanged(tileId);
}
@ -28,10 +29,6 @@ void TilemapTileSelector::updateSelectedTile() {
this->selectedTile = this->getTileId(origin.x(), origin.y());
}
unsigned TilemapTileSelector::getSelectedTile() {
return this->selectedTile;
}
unsigned TilemapTileSelector::getTileId(int x, int y) {
unsigned index = y * this->numTilesWide + x;
return index < this->numTiles ? index : this->numTiles % index;
@ -42,9 +39,63 @@ QPoint TilemapTileSelector::getTileIdCoords(unsigned tileId) {
return QPoint(index % this->numTilesWide, index / this->numTilesWide);
}
QImage TilemapTileSelector::tileImg(unsigned tileId) {
QImage TilemapTileSelector::setPalette(int paletteIndex) {
QImage tilesetImage = this->tileset;
tilesetImage.convertTo(QImage::Format::Format_Indexed8);
// TODO: bounds check on the palette copying
switch(this->format) {
case TilemapFormat::Plain:
break;
case TilemapFormat::BPP_4:
{
QVector<QRgb> newColorTable;
int palMinLength = paletteIndex * 16 + 16;
if ((this->palette.count() < palMinLength) || (tilesetImage.colorTable().count() != 16)) {
// either a) the palette has less than 256 colors, or b) the image is improperly indexed
for (QRgb color : tilesetImage.colorTable()) {
int gray = qGray(color);
newColorTable.append(qRgb(gray, gray, gray));
}
} else {
// use actual pal
// before Qt 6, the color table is a QVector which is deprecated now, and this method does not exits
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
newColorTable = this->palette.toVector().mid(paletteIndex * 16, 16);
#else
newColorTable = this->palette.mid(paletteIndex * 16, 16);
#endif
}
tilesetImage.setColorTable(newColorTable);
break;
}
case TilemapFormat::BPP_8:
{
if (tilesetImage.colorTable().count() == this->palette.count()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
tilesetImage.setColorTable(this->palette.toVector());
#else
tilesetImage.setColorTable(this->palette);
#endif
}
break;
}
default: break;
}
return tilesetImage;
}
QImage TilemapTileSelector::tileImg(shared_ptr<TilemapTile> tile) {
unsigned tileId = tile->id();
QPoint pos = getTileIdCoords(tileId);
return this->tilemap.copy(pos.x() * 8, pos.y() * 8, 8, 8).toImage();
QImage tilesetImage = setPalette(tile->palette());
// take a tile from the tileset
QImage img = tilesetImage.copy(pos.x() * 8, pos.y() * 8, 8, 8);
img = img.mirrored(tile->hFlip(), tile->vFlip());
return img;
}
void TilemapTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {

View file

@ -40,6 +40,7 @@ TilesetEditor::~TilesetEditor()
delete selectedTilePixmapItem;
delete selectedTileScene;
delete metatileLayersScene;
delete copiedMetatile;
}
void TilesetEditor::update(Map *map, QString primaryTilesetLabel, QString secondaryTilesetLabel) {
@ -76,8 +77,8 @@ bool TilesetEditor::selectMetatile(uint16_t metatileId) {
return true;
}
uint16_t TilesetEditor::getSelectedMetatile() {
return this->metatileSelector->getSelectedMetatile();
uint16_t TilesetEditor::getSelectedMetatileId() {
return this->metatileSelector->getSelectedMetatileId();
}
void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
@ -177,7 +178,7 @@ void TilesetEditor::initMetatileSelector()
}
void TilesetEditor::initMetatileLayersItem() {
Metatile *metatile = Tileset::getMetatile(this->getSelectedMetatile(), this->primaryTileset, this->secondaryTileset);
Metatile *metatile = Tileset::getMetatile(this->getSelectedMetatileId(), this->primaryTileset, this->secondaryTileset);
this->metatileLayersItem = new MetatileLayersItem(metatile, this->primaryTileset, this->secondaryTileset);
connect(this->metatileLayersItem, &MetatileLayersItem::tileChanged,
this, &TilesetEditor::onMetatileLayerTileChanged);
@ -282,7 +283,7 @@ void TilesetEditor::refresh() {
this->metatileLayersItem->setTilesets(this->primaryTileset, this->secondaryTileset);
this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
this->metatileSelector->select(this->getSelectedMetatile());
this->metatileSelector->select(this->getSelectedMetatileId());
this->drawSelectedTiles();
if (metatileSelector) {
@ -419,7 +420,7 @@ void TilesetEditor::onMetatileLayerTileChanged(int x, int y) {
this->metatileLayersItem->draw();
this->hasUnsavedChanges = true;
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
}
@ -486,7 +487,7 @@ void TilesetEditor::on_comboBox_metatileBehaviors_textActivated(const QString &m
if (this->metatile) {
Metatile *prevMetatile = new Metatile(*this->metatile);
this->metatile->behavior = static_cast<uint16_t>(project->metatileBehaviorMap[metatileBehavior]);
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
this->hasUnsavedChanges = true;
@ -510,7 +511,7 @@ void TilesetEditor::saveMetatileLabel()
if (this->metatile && this->metatile->label != this->ui->lineEdit_metatileLabel->text()) {
Metatile *prevMetatile = new Metatile(*this->metatile);
this->metatile->label = this->ui->lineEdit_metatileLabel->text();
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
this->hasUnsavedChanges = true;
@ -522,7 +523,7 @@ void TilesetEditor::on_comboBox_layerType_activated(int layerType)
if (this->metatile) {
Metatile *prevMetatile = new Metatile(*this->metatile);
this->metatile->layerType = static_cast<uint8_t>(layerType);
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
this->hasUnsavedChanges = true;
@ -534,7 +535,7 @@ void TilesetEditor::on_comboBox_encounterType_activated(int encounterType)
if (this->metatile) {
Metatile *prevMetatile = new Metatile(*this->metatile);
this->metatile->encounterType = static_cast<uint8_t>(encounterType);
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
this->hasUnsavedChanges = true;
@ -546,7 +547,7 @@ void TilesetEditor::on_comboBox_terrainType_activated(int terrainType)
if (this->metatile) {
Metatile *prevMetatile = new Metatile(*this->metatile);
this->metatile->terrainType = static_cast<uint8_t>(terrainType);
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
this->hasUnsavedChanges = true;
@ -811,6 +812,21 @@ void TilesetEditor::onPaletteEditorChangedPalette(int paletteId) {
this->on_spinBox_paletteSelector_valueChanged(paletteId);
}
bool TilesetEditor::replaceMetatile(uint16_t metatileId, Metatile * src)
{
Metatile * dest = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
if (!dest || !src || *dest == *src)
return false;
this->metatile = dest;
*this->metatile = *src;
this->metatileSelector->select(metatileId);
this->metatileSelector->draw();
this->metatileLayersItem->draw();
this->metatileLayersItem->clearLastModifiedCoords();
return true;
}
void TilesetEditor::on_actionUndo_triggered()
{
MetatileHistoryItem *commit = this->metatileHistory.current();
@ -818,34 +834,39 @@ void TilesetEditor::on_actionUndo_triggered()
Metatile *prev = commit->prevMetatile;
if (!prev) return;
this->metatileHistory.back();
Metatile *temp = Tileset::getMetatile(commit->metatileId, this->primaryTileset, this->secondaryTileset);
if (temp) {
this->metatile = temp;
*this->metatile = *prev;
this->metatileSelector->select(commit->metatileId);
this->metatileSelector->draw();
this->metatileLayersItem->draw();
this->metatileLayersItem->clearLastModifiedCoords();
}
this->replaceMetatile(commit->metatileId, prev);
}
void TilesetEditor::on_actionRedo_triggered()
{
MetatileHistoryItem *commit = this->metatileHistory.next();
if (!commit) return;
Metatile *next = commit->newMetatile;
if (!next) return;
Metatile *temp = Tileset::getMetatile(commit->metatileId, this->primaryTileset, this->secondaryTileset);
if (temp) {
this->metatile = temp;
*this->metatile = *next;
this->metatileSelector->select(commit->metatileId);
this->metatileSelector->draw();
this->metatileLayersItem->draw();
this->metatileLayersItem->clearLastModifiedCoords();
this->replaceMetatile(commit->metatileId, commit->newMetatile);
}
void TilesetEditor::on_actionCopy_triggered()
{
Metatile * toCopy = Tileset::getMetatile(this->getSelectedMetatileId(), this->primaryTileset, this->secondaryTileset);
if (!toCopy) return;
if (!this->copiedMetatile)
this->copiedMetatile = new Metatile(*toCopy);
else
*this->copiedMetatile = *toCopy;
this->copiedMetatile->label = ""; // Don't copy the label, these should be unique to each metatile
}
void TilesetEditor::on_actionPaste_triggered()
{
Metatile *prevMetatile = new Metatile(*this->metatile);
uint16_t metatileId = this->getSelectedMetatileId();
if (!this->replaceMetatile(metatileId, this->copiedMetatile)) {
delete prevMetatile;
return;
}
MetatileHistoryItem *commit = new MetatileHistoryItem(metatileId, prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);
}
void TilesetEditor::on_actionExport_Primary_Tiles_Image_triggered()

View file

@ -78,7 +78,7 @@ void TilesetEditorMetatileSelector::updateSelectedMetatile() {
emit selectedMetatileChanged(this->selectedMetatile);
}
uint16_t TilesetEditorMetatileSelector::getSelectedMetatile() {
uint16_t TilesetEditorMetatileSelector::getSelectedMetatileId() {
return this->selectedMetatile;
}