Merge pull request #543 from GriffinRichards/options
Add Project Settings Editor
|
@ -8,12 +8,16 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
|
|||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Adds an editor window under `Options -> Project Settings...` to customize the project-specific settings in `porymap.project.cfg` and `porymap.user.cfg`.
|
||||
- Adds an editor window under `Options -> Custom Scripts...` for Porymap's API scripts.
|
||||
- Support for 8BPP tileset tile images.
|
||||
|
||||
### Changed
|
||||
- The Palette Editor now remembers the Bit Depth setting.
|
||||
- The min/max levels on the Wild Pokémon tab will now adjust automatically if they invalidate each other.
|
||||
- If the recent project directory doesn't exist Porymap will open an empty project instead of failing with a misleading error message.
|
||||
- Settings under `Options` were relocated either to the `Preferences` window or `Options -> Project Settings`.
|
||||
- Secret Base and Weather Trigger events are automatically disabled if their respective constants files fail to parse, instead of not opening the project.
|
||||
|
||||
### Fixed
|
||||
- Fix text boxes in the Palette Editor calculating color incorrectly.
|
||||
|
@ -24,6 +28,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
|
|||
- Stop the Tileset Editor from scrolling to the initially selected metatile when saving.
|
||||
- Fix `0x0`/`NULL` appearing more than once in the scripts dropdown.
|
||||
- Fix the selection outline sticking in single-tile mode on the Prefab tab.
|
||||
- Fix bad URL color contrast on dark themes.
|
||||
|
||||
## [5.1.1] - 2023-02-20
|
||||
### Added
|
||||
|
|
BIN
docsrc/manual/images/project-files/settings.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
docsrc/manual/images/settings-and-options/base-game-version.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docsrc/manual/images/settings-and-options/default-tilesets.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docsrc/manual/images/settings-and-options/events.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
docsrc/manual/images/settings-and-options/maps.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docsrc/manual/images/settings-and-options/new-map-defaults.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
docsrc/manual/images/settings-and-options/prefabs.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docsrc/manual/images/settings-and-options/preferences.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
docsrc/manual/images/settings-and-options/tilesets-metatiles.png
Normal file
After Width: | Height: | Size: 80 KiB |
|
@ -6,8 +6,12 @@ Porymap relies on the user maintaining a certain level of integrity with their p
|
|||
This is a list of files that porymap reads from and writes to. Generally, if porymap writes
|
||||
to a file, it probably is not a good idea to edit yourself unless otherwise noted.
|
||||
|
||||
The filepath that Porymap expects for each file can be overridden with config options. The name of each config override is listed in the table, and should begin with ``path/``.
|
||||
For example if you wanted to rename ``include/constants/items.h`` to ``headers/defines/stuff.h``, you would add ``path/constants_items=headers/defines/stuff.h`` to your project's ``porymap.project.cfg`` file.
|
||||
The filepath that Porymap expects for each file can be overridden under the ``Project Files`` section of ``Options -> Project Settings``. A new path can be specified by entering it in the text box or choosing it with the folder button. Paths are expected to be relative to the root project folder. If no path is specified, or if the file/folder specified does not exist, then the default path will be used instead. The name of each setting in this section is listed in the table below under ``Override``.
|
||||
|
||||
.. figure:: images/project-files/settings.png
|
||||
:align: center
|
||||
:width: 75%
|
||||
:alt: Settings
|
||||
|
||||
|
||||
.. csv-table::
|
||||
|
|
|
@ -5,66 +5,307 @@ Porymap Settings
|
|||
****************
|
||||
|
||||
Porymap uses config files to read and store user and project settings.
|
||||
|
||||
===============
|
||||
Global settings
|
||||
===============
|
||||
|
||||
A global settings file is stored in a platform-dependent location for app configuration files
|
||||
(``%Appdata%\pret\porymap\porymap.cfg`` on Windows, ``~/Library/Application\ Support/pret/porymap/porymap.cfg`` on macOS).
|
||||
|
||||
A config file is also created when opening a project in porymap for the first time. It is stored in
|
||||
your project root as ``porymap.project.cfg``. There are several project-specific settings that are
|
||||
determined by this file. You may want to force commit this file so that other users will automatically have access to your project settings.
|
||||
A selection of the settings in this file can be edited under ``Preferences...``, and the rest are updated automatically while using Porymap.
|
||||
|
||||
A second config file is created for user-specific settings. It is stored in
|
||||
your project root as ``porymap.user.cfg``. You should add this file to your gitignore.
|
||||
================
|
||||
Project settings
|
||||
================
|
||||
|
||||
.. csv-table::
|
||||
:header: Setting,Default,Location,Can Edit?,Description
|
||||
:widths: 10, 3, 5, 5, 20
|
||||
A config file for project-specific settings is also created when opening a project in porymap for the first time. It is stored in your project root as ``porymap.project.cfg``. You may want to force commit this file so that other users will automatically have access to your project settings.
|
||||
|
||||
``recent_project``, , global, yes, The project that will be opened on launch
|
||||
``reopen_on_launch``, 1, global, yes, Whether the most recent project should be opened on launch
|
||||
``recent_map``, , user, yes, The map that will be opened on launch
|
||||
``pretty_cursors``, 1, global, yes, Whether to use custom crosshair cursors
|
||||
``map_sort_order``, group, global, yes, The order map list is sorted in
|
||||
``window_geometry``, , global, no, For restoring window sizes
|
||||
``window_state``, , global, no, For restoring window sizes
|
||||
``map_splitter_state``, , global, no, For restoring window sizes
|
||||
``main_splitter_state``, , global, no, For restoring window sizes
|
||||
``collision_opacity``, 50, global, yes, Opacity of collision overlay
|
||||
``metatiles_zoom``, 30, global, yes, Scale of map metatiles
|
||||
``show_player_view``, 0, global, yes, Display a rectangle for the GBA screen radius
|
||||
``show_cursor_tile``, 0, global, yes, Display a rectangle around the hovered metatile(s)
|
||||
``monitor_files``, 1, global, yes, Whether porymap will monitor changes to project files
|
||||
``tileset_checkerboard_fill``, 1, global, yes, Whether new tilesets will be filled with a checkerboard pattern of metatiles.
|
||||
``theme``, default, global, yes, The color theme for porymap windows and widgets
|
||||
``text_editor_goto_line``, , global, yes, The command that will be executed when clicking the button next the ``Script`` combo-box.
|
||||
``text_editor_open_directory``, , global, yes, The command that will be executed when clicking ``Open Project in Text Editor``.
|
||||
``base_game_version``, , project, no, The base pret repo for this project
|
||||
``use_encounter_json``, 1, user, yes, Enables wild encounter table editing
|
||||
``use_poryscript``, 0, project, yes, Whether to open .pory files for scripts
|
||||
``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_floor_number``, 1 if ``pokefirered``, project, yes, Adds ``Floor Number`` to map headers
|
||||
``enable_map_allow_flags``, 1 if not ``pokeruby``, project, yes, "Adds ``Allow Running``, ``Allow Biking``, and ``Allow Dig & Escape Rope`` 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)
|
||||
``new_map_metatile``, 1, project, yes, The metatile id that will be used to fill new maps
|
||||
``new_map_elevation``, 3, project, yes, The elevation that will be used to fill new maps
|
||||
``new_map_border_metatiles``, "``468,469,476,477`` or ``20,21,28,29``", project, yes, The list of metatile ids that will be used to fill the 2x2 border of new maps
|
||||
``default_primary_tileset``, ``gTileset_General``, project, yes, The label of the default primary tileset
|
||||
``default_secondary_tileset``, ``gTileset_Petalburg`` or ``gTileset_PalletTown``, project, yes, The label of the default secondary tileset
|
||||
``custom_scripts``, , user, yes, A list of script files to load into the scripting engine
|
||||
``prefabs_filepath``, ``<project_root>/prefabs.json``, project, yes, The filepath containing prefab JSON data
|
||||
``prefabs_import_prompted``, 0, project, no, Keeps track of whether or not the project was prompted for importing default prefabs
|
||||
``tilesets_have_callback``, 1, project, yes, Whether new tileset headers should have the ``callback`` field
|
||||
``tilesets_have_is_compressed``, 1, project, yes, Whether new tileset headers should have the ``isCompressed`` field
|
||||
``metatile_attributes_size``, 2 or 4, project, yes, The number of attribute bytes each metatile has
|
||||
``metatile_behavior_mask``, ``0xFF`` or ``0x1FF``, project, yes, The mask for the metatile Behavior attribute
|
||||
``metatile_encounter_type_mask``, ``0x0`` or ``0x7000000``, project, yes, The mask for the metatile Encounter Type attribute
|
||||
``metatile_layer_type_mask``, ``0xF000`` or ``0x60000000``, project, yes, The mask for the metatile Layer Type attribute
|
||||
``metatile_terrain_type_mask``, ``0x0`` or ``0x3E00``, project, yes, The mask for the metatile Terrain Type attribute
|
||||
A second config file is created for user-specific settings. It is stored in your project root as ``porymap.user.cfg``. You should add this file to your gitignore.
|
||||
|
||||
Some of these settings can be toggled manually in porymap via the *Options* menu.
|
||||
The settings in ``porymap.project.cfg`` and ``porymap.user.cfg`` can be edited under ``Options -> Project Settings...``. Any changes made in this window will not take effect unless confirmed by selecting ``OK`` and then reloading the project.
|
||||
|
||||
Each of the settings in the ``Project Settings...`` window are described below.
|
||||
|
||||
.. warning::
|
||||
Changing any of the settings in the Project Settings Editor's red ``Warning`` box will require additional changes to your project to function correctly. Investigate the repository versions that have a setting natively supported to see what changes to your project are necessary.
|
||||
|
||||
|
||||
Preferences
|
||||
-----------
|
||||
|
||||
.. figure:: images/settings-and-options/preferences.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Preferences
|
||||
|
||||
Use Poryscript
|
||||
If this is checked, a ``scripts.pory`` (and ``text.pory``, if applicable) file will be created alongside new maps, instead of a ``scripts.inc`` file. Additionally, ``.pory`` files will be considered when searching for scripts labels and when opening scripts files (in addition to the regular ``.inc`` files).
|
||||
|
||||
Defaults to ``unchecked``.
|
||||
|
||||
Field name: ``use_poryscript``
|
||||
|
||||
Show Wild Encounter Tables
|
||||
If this is checked, the ``Wild Pokemon`` tab will be enabled and wild encounter data will be read from the project's encounters JSON file.
|
||||
|
||||
If no encounters JSON file is found this will be automatically unchecked.
|
||||
|
||||
Field name: ``use_encounter_json``
|
||||
|
||||
|
||||
Default Tilesets
|
||||
----------------
|
||||
|
||||
.. figure:: images/settings-and-options/default-tilesets.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Default Tilesets
|
||||
|
||||
Default Primary/Secondary Tilesest
|
||||
These will be the initially-selected tilesets when creating a new map, and will be used if a layout's tileset fails to load. If a default tileset is not found then the first tileset in the respective list will be used instead.
|
||||
|
||||
The default primary tileset is ``gTileset_General``.
|
||||
|
||||
The default secondary tileset is ``gTileset_PalletTown`` for ``pokefirered``, and ``gTileset_Petalburg`` for other versions.
|
||||
|
||||
Field names: ``default_primary_tileset`` and ``default_secondary_tileset``
|
||||
|
||||
|
||||
New Map Defaults
|
||||
----------------
|
||||
|
||||
.. figure:: images/settings-and-options/new-map-defaults.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: New Map Defaults
|
||||
|
||||
Border Metatiles
|
||||
This is list of metatile ID values that will be used to fill the border on new maps. The spin boxes correspond to the top-left, top-right, bottom-left, and bottom-right border metatiles respectively.
|
||||
|
||||
If ``Enable Custom Border Size`` is checked, this will instead be a comma-separated list of metatile ID values that will be used to fill the border on new maps. Values in the list will be read sequentially to fill the new border left-to-right top-to-bottom. If the number of metatiles in the border for a new map is not the same as the number of values in the list then the border will be filled with metatile ID ``0x000`` instead.
|
||||
|
||||
Defaults to ``0x014``, ``0x015``, ``0x01C``, ``0x01D`` for ``pokefirered``, and ``0x1D4``, ``0x1D5``, ``0x1DC``, ``0x1DD`` for other versions.
|
||||
|
||||
Field name: ``new_map_border_metatiles``
|
||||
|
||||
Fill Metatile
|
||||
This is the metatile ID value that will be used to fill new maps.
|
||||
|
||||
Defaults to ``0x1``.
|
||||
|
||||
Field name: ``new_map_metatile``
|
||||
|
||||
Elevation
|
||||
This is the elevation that will be used to fill new maps. New maps will be filled with passable collision.
|
||||
|
||||
Defaults to ``3``.
|
||||
|
||||
Field name: ``new_map_elevation``
|
||||
|
||||
Create separate text file
|
||||
If this is checked, a ``text.inc`` (or ``text.pory``) file will be created alongside new maps.
|
||||
|
||||
Defaults to ``unchecked`` for ``pokeemerald`` and ``checked`` for other versions.
|
||||
|
||||
Field name: ``create_map_text_file``
|
||||
|
||||
|
||||
Prefabs
|
||||
-------
|
||||
|
||||
.. figure:: images/settings-and-options/prefabs.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Prefabs
|
||||
|
||||
Prefabs Path
|
||||
This is the file path to a ``.json`` file that contains definitions of prefabs. This will be used to populate the ``Prefabs`` panel on the ``Map`` tab. If no path is specified prefabs will be saved to a new ``prefabs.json`` file in the root project folder. A new file can be selected with the folder button.
|
||||
|
||||
The ``Import Defaults`` button will populate the specified file with version-specific prefabs constructed using the vanilla tilesets. This will overwrite any existing prefabs.
|
||||
|
||||
Field name: ``prefabs_filepath``.
|
||||
|
||||
Additionally, there is a ``prefabs_import_prompted`` field that should not be edited.
|
||||
|
||||
|
||||
Base game version
|
||||
-----------------
|
||||
|
||||
.. figure:: images/settings-and-options/base-game-version.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Base Game Version
|
||||
|
||||
This is the name of base pret repository for this project. The options are ``pokeruby``, ``pokefirered``, and ``pokeemerald``, and can be selected (or automatically from the project folder name) when the project is first opened. Changing the base game version setting will prompt you to restore the default project settings for any of the three versions. You can also do this for the currently-selected base game version by selecting ``Restore Defaults`` at the bottom. For up-to-date projects changing this setting has no other effect.
|
||||
|
||||
Field name: ``base_game_version``
|
||||
|
||||
|
||||
Tilesets / Metatiles
|
||||
--------------------
|
||||
|
||||
.. figure:: images/settings-and-options/tilesets-metatiles.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Tilesets / Metatiles
|
||||
|
||||
Enable Triple Layer Metatiles
|
||||
Metatile data normally consists of 2 layers with 4 tiles each. If this is checked, they should instead consist of 3 layers with 4 tiles each. Additionally, the ``Layer Type`` option in the ``Tileset Editor`` will be removed. Note that layer type data will still be read and written according to your ``Layer Type mask`` setting.
|
||||
|
||||
For details on supporting this setting in your project, see https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles.
|
||||
|
||||
Defaults to ``unchecked``
|
||||
|
||||
Field name: ``enable_triple_layer_metatiles``
|
||||
|
||||
Attributes size
|
||||
The number of bytes used per metatile for metatile attributes. The data in each of your project's ``metatile_attributes.bin`` files will be expected to be ``s * n``, where ``s`` is this size and ``n`` is the number of metatiles in the tileset. Additionally, new ``metatile_attributes.bin`` will be included in the project with a corresponding ``INCBIN_U8``, ``INCBIN_U16``, or ``INCBIN_U32`` directive.
|
||||
|
||||
Changing this setting will automatically enforce the new limit on the metatile attribute mask settings below.
|
||||
|
||||
Defaults to ``4`` for ``pokefirered`` and ``2`` for other versions.
|
||||
|
||||
Field name: ``metatile_attributes_size``
|
||||
|
||||
Attribute masks
|
||||
Each of the following four settings are bit masks that will be used to read and write a specific metatile attribute from the metatile attributes data. If you are instead importing metatile attribute data from AdvanceMap, a default mask value will be used to read the data, and the mask value specified here will be used to write the new file.
|
||||
|
||||
If any of the mask values are set to ``0x0``, the corresponding option in the Tileset Editor will be removed. The maximum for all the attribute masks is determined by the Attributes size setting.
|
||||
|
||||
.. warning::
|
||||
If any of the metatile attribute masks have overlapping bits they may behave in unexpected ways. A warning will be logged in the Porymap log file if this happens
|
||||
|
||||
|
||||
Metatile Behavior mask
|
||||
See Attribute masks. This is the mask value for the ``Metatile Behavior`` metatile attribute.
|
||||
|
||||
Defaults to ``0x1FF`` for ``pokefirered``, and ``0xFF`` for other versions.
|
||||
|
||||
Field name: ``metatile_behavior_mask``
|
||||
|
||||
Layer Type mask
|
||||
See Attribute masks. This is the mask value for the ``Layer Type`` metatile attribute. If the value is set to ``0x0`` the ``Layer Type`` option will be disabled in the Tileset Editor, and all metatiles will be treated in the editor as if they had the ``Normal`` layer type.
|
||||
|
||||
Defaults to ``0x60000000`` for ``pokefirered`` and ``0xF000`` for other versions.
|
||||
|
||||
Field name: ``metatile_layer_type_mask``
|
||||
|
||||
Encounter Type mask
|
||||
See Attribute masks. This is the mask value for the ``Encounter Type`` metatile attribute.
|
||||
|
||||
Defaults to ``0x7000000`` for ``pokefirered`` and ``0x0`` for other versions.
|
||||
|
||||
Field name: ``metatile_encounter_type_mask``
|
||||
|
||||
Terrain Type mask
|
||||
See Attribute masks. This is the mask value for the ``Terrain Type`` metatile attribute.
|
||||
|
||||
Defaults to ``0x3E00`` for ``pokefirered`` and ``0x0`` for other versions.
|
||||
|
||||
Field name: ``metatile_terrain_type_mask``
|
||||
|
||||
Output 'callback' and 'isCompressed' fields
|
||||
If these are checked, then ``callback`` and ``isCompressed`` fields will be output in the C data for new tilesets. Their default values will be ``NULL`` and ``TRUE``, respectively.
|
||||
|
||||
Defaults to ``checked`` for both.
|
||||
|
||||
Field names: ``tilesets_have_callback`` and ``tilesets_have_is_compressed``
|
||||
|
||||
|
||||
Project Files
|
||||
-------------
|
||||
This is a list of the files and folders Porymap expects from your project. Each can be overridden by typing a new path or selecting a file/folder with the folder button. If the file/folder doesn't exist when the project is loaded then the default path will be used instead.
|
||||
|
||||
For more information on each of these files/folders, see https://huderlem.github.io/porymap/manual/project-files.html
|
||||
|
||||
Field name: ``path/<identifier>``
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
.. figure:: images/settings-and-options/events.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Events
|
||||
|
||||
Enable Clone Objects
|
||||
If this is checked Clone Object Events will be available on the ``Events`` tab. For more information see https://huderlem.github.io/porymap/manual/editing-map-events.html#clone-object-events
|
||||
|
||||
Defaults to ``checked`` for ``pokefirered`` and ``unchecked`` for other versions.
|
||||
|
||||
Field name: ``enable_event_clone_object``
|
||||
|
||||
Enable Secret Bases
|
||||
If this is checked Secret Base Events will be available on the ``Events`` tab. For more information see https://huderlem.github.io/porymap/manual/editing-map-events.html#secret-base-event
|
||||
|
||||
Defaults to ``unchecked`` for ``pokefirered`` and ``checked`` for other versions.
|
||||
|
||||
Field name: ``enable_event_secret_base``
|
||||
|
||||
Enable Weather Triggers
|
||||
If this is checked Weather Trigger Events will be available on the ``Events`` tab. For more information see https://huderlem.github.io/porymap/manual/editing-map-events.html#weather-trigger-events
|
||||
|
||||
Defaults to ``unchecked`` for ``pokefirered`` and ``checked`` for other versions.
|
||||
|
||||
Field name: ``enable_event_weather_trigger``
|
||||
|
||||
Enable 'Quantity' for Hidden Items
|
||||
If this is checked the ``Quantity`` property will be available for Hidden Item Events. For more information see https://huderlem.github.io/porymap/manual/editing-map-events.html#hidden-item-event
|
||||
|
||||
Defaults to ``checked`` for ``pokefirered`` and ``unchecked`` for other versions.
|
||||
|
||||
Field name: ``enable_hidden_item_quantity``
|
||||
|
||||
Enable 'Requires Itemfinder' for Hidden Items
|
||||
If this is checked the ``Requires Itemfinder`` property will be available for Hidden Item Events. For more information see https://huderlem.github.io/porymap/manual/editing-map-events.html#hidden-item-event
|
||||
|
||||
Defaults to ``checked`` for ``pokefirered`` and ``unchecked`` for other versions.
|
||||
|
||||
Field name: ``enable_hidden_item_requires_itemfinder``
|
||||
|
||||
Enable 'Repsawn Map/NPC' for Heal Locations
|
||||
If this is checked the ``Respawn Map`` and ``Respawn NPC`` properties will be available for Heal Location events. For more information see https://huderlem.github.io/porymap/manual/editing-map-events.html#heal-location-healspots
|
||||
|
||||
Defaults to ``checked`` for ``pokefirered`` and ``unchecked`` for other versions.
|
||||
|
||||
Field name: ``enable_heal_location_respawn_data``
|
||||
|
||||
|
||||
Maps
|
||||
----
|
||||
|
||||
.. figure:: images/settings-and-options/maps.png
|
||||
:align: left
|
||||
:width: 60%
|
||||
:alt: Maps
|
||||
|
||||
Enable 'Floor Number'
|
||||
If this is checked, a ``Floor Number`` option will become available on the ``Header`` tab and on the new map prompt. For more information see https://huderlem.github.io/porymap/manual/editing-map-header.html
|
||||
|
||||
Defaults to ``checked`` for ``pokefirered`` and ``unchecked`` for other versions.
|
||||
|
||||
Field name: ``enable_floor_number``
|
||||
|
||||
Enable 'Allow Running/Biking/Escaping'
|
||||
If this is checked, ``Allow Running``, ``Allow Biking``, and ``Allow Dig & Escape Rope`` options will become available on the ``Header`` tab and on the new map prompt. For more information see https://huderlem.github.io/porymap/manual/editing-map-header.html
|
||||
|
||||
Defaults to ``unchecked`` for ``pokeruby`` and ``checked`` for other versions.
|
||||
|
||||
Field name: ``enable_map_allow_flags``
|
||||
|
||||
Enable Custom Border Size
|
||||
If this is checked, ``Border Width`` and ``Border Height`` options will become available under the ``Change Dimensions`` button and on the new map prompt. If it is unchecked all maps will use the default 2x2 dimensions.
|
||||
|
||||
Defaults to ``checked`` for ``pokefirered`` and ``unchecked`` for other versions.
|
||||
|
||||
Field name: ``use_custom_border_size``
|
||||
|
||||
|
||||
Additional Fields
|
||||
-----------------
|
||||
There are two additional fields in ``porymap.user.cfg`` that aren't described above.
|
||||
|
||||
``recent_map`` is the name of the most recently opened map and is updated automatically. This is the map that will be opened when the project is opened. If no map is found with this name (or if the field is empty) then the first map in the map list will be used instead.
|
||||
|
||||
``custom_scripts`` is a comma-separated list of filepaths to scripts for Porymap's API. These can be edited under ``Options -> Custom Scripts...``. For more information see https://huderlem.github.io/porymap/manual/scripting-capabilities.html
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
<item>
|
||||
<widget class="QLabel" name="label_Manual">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Official Documentation: <a href="https://huderlem.github.io/porymap/"><span style=" text-decoration: underline; color:#0000ff;">https://huderlem.github.io/porymap/</span></a></p></body></html></string>
|
||||
<string><html><head/><body><p>Official Documentation: <a href="https://huderlem.github.io/porymap/"><span style=" text-decoration: underline;">https://huderlem.github.io/porymap/</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
|
|
164
forms/customscriptseditor.ui
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CustomScriptsEditor</class>
|
||||
<widget class="QMainWindow" name="CustomScriptsEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>374</width>
|
||||
<height>355</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Custom Scripts Editor</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<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="QFrame" name="window">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QFrame" name="header">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="button_AddNewScript">
|
||||
<property name="text">
|
||||
<string>Add New Script...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="button_ReloadScripts">
|
||||
<property name="text">
|
||||
<string>Reload Scripts</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/refresh.ico</normaloff>:/icons/refresh.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_header">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_Manual">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="https://huderlem.github.io/porymap/manual/scripting-capabilities.html"><span style=" text-decoration: underline;">What are custom scripts?</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_ListHeader">
|
||||
<property name="text">
|
||||
<string>Scripts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="list">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::NoDragDrop</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::IgnoreAction</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="textElideMode">
|
||||
<enum>Qt::ElideLeft</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Free</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
97
forms/customscriptslistitem.ui
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CustomScriptsListItem</class>
|
||||
<widget class="QFrame" name="CustomScriptsListItem">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>129</width>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="b_Choose">
|
||||
<property name="toolTip">
|
||||
<string>Choose a new filepath for this script</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/folder.ico</normaloff>:/icons/folder.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_filepath">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_Enable">
|
||||
<property name="toolTip">
|
||||
<string>If unchecked this script will be ignored</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="b_Edit">
|
||||
<property name="toolTip">
|
||||
<string>Open this script file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/edit_document.ico</normaloff>:/icons/edit_document.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="b_Delete">
|
||||
<property name="toolTip">
|
||||
<string>Remove this script</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/delete.ico</normaloff>:/icons/delete.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -3012,13 +3012,10 @@
|
|||
<property name="title">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
<addaction name="actionUse_Encounter_Json"/>
|
||||
<addaction name="actionMonitor_Project_Files"/>
|
||||
<addaction name="actionUse_Poryscript"/>
|
||||
<addaction name="actionOpen_Recent_Project_On_Launch"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEdit_Preferences"/>
|
||||
<addaction name="actionEdit_Shortcuts"/>
|
||||
<addaction name="actionProject_Settings"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="actionShortcuts"/>
|
||||
<addaction name="actionCustom_Scripts"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
|
@ -3064,38 +3061,6 @@
|
|||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUse_Encounter_Json">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Wild Encounter Tables</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMonitor_Project_Files">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Monitor Project Files</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUse_Poryscript">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Poryscript</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpen_Recent_Project_On_Launch">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Recent Project On Launch</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_NewMap">
|
||||
<property name="text">
|
||||
<string>New Map...</string>
|
||||
|
@ -3288,9 +3253,9 @@
|
|||
<string>Export Map Stitch Image...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Preferences">
|
||||
<action name="actionPreferences">
|
||||
<property name="text">
|
||||
<string>Edit Preferences...</string>
|
||||
<string>Preferences...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+,</string>
|
||||
|
@ -3301,9 +3266,9 @@
|
|||
<string>Open Project in Text Editor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Shortcuts">
|
||||
<action name="actionShortcuts">
|
||||
<property name="text">
|
||||
<string>Edit Shortcuts...</string>
|
||||
<string>Shortcuts...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_Map_Timelapse_Image">
|
||||
|
@ -3326,6 +3291,16 @@
|
|||
<string>Import Map from Advance Map 1.92...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProject_Settings">
|
||||
<property name="text">
|
||||
<string>Project Settings...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCustom_Scripts">
|
||||
<property name="text">
|
||||
<string>Custom Scripts...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>480</height>
|
||||
<width>522</width>
|
||||
<height>493</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -18,6 +18,29 @@
|
|||
<property name="spacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Miscellaneous</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_MonitorProjectFiles">
|
||||
<property name="text">
|
||||
<string>Monitor project files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_OpenRecentProject">
|
||||
<property name="text">
|
||||
<string>Open recent project on launch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Themes">
|
||||
<property name="sizePolicy">
|
||||
|
@ -65,8 +88,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>582</width>
|
||||
<height>372</height>
|
||||
<width>492</width>
|
||||
<height>327</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
|
|
765
forms/projectsettingseditor.ui
Normal file
|
@ -0,0 +1,765 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProjectSettingsEditor</class>
|
||||
<widget class="QMainWindow" name="ProjectSettingsEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Project Settings</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>585</width>
|
||||
<height>585</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_UserConfig">
|
||||
<property name="title">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_UsePoryscript">
|
||||
<property name="toolTip">
|
||||
<string>Whether map script files should prefer using .pory</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Poryscript</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_ShowWildEncounterTables">
|
||||
<property name="text">
|
||||
<string>Show Wild Encounter Tables</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_DefaultTilesets">
|
||||
<property name="title">
|
||||
<string>Default Tilesets</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_PrimaryTileset">
|
||||
<property name="text">
|
||||
<string>Primary Tileset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_DefaultPrimaryTileset"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_SecondaryTileset">
|
||||
<property name="text">
|
||||
<string>Secondary Tileset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_DefaultSecondaryTileset"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_NewMapDefaults">
|
||||
<property name="title">
|
||||
<string>New Map Defaults</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="2" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_FillMetatile">
|
||||
<property name="toolTip">
|
||||
<string>The default metatile value that will be used to fill new maps</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_Elevation">
|
||||
<property name="text">
|
||||
<string>Elevation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_Elevation">
|
||||
<property name="toolTip">
|
||||
<string>The default elevation that will be used to fill new maps</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_FillMetatile">
|
||||
<property name="text">
|
||||
<string>Fill Metatile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="6">
|
||||
<widget class="QCheckBox" name="checkBox_CreateTextFile">
|
||||
<property name="toolTip">
|
||||
<string>Whether a separate text.inc or text.pory file will be created for new maps, alongside the scripts file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create separate text file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="6">
|
||||
<widget class="QWidget" name="widget_CustomSizeBorderMetatiles" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_BorderMetatiles_2">
|
||||
<property name="text">
|
||||
<string>Border Metatiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_BorderMetatiles">
|
||||
<property name="toolTip">
|
||||
<string>A comma-separated list of metatile values that will be used to fill new map borders</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="5">
|
||||
<widget class="QWidget" name="widget_DefaultSizeBorderMetatiles" native="true">
|
||||
<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="QLabel" name="label_BorderMetatiles">
|
||||
<property name="text">
|
||||
<string>Border Metatiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile1">
|
||||
<property name="toolTip">
|
||||
<string>The default metatile value that will be used for the top-left border metatile on new maps.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile2">
|
||||
<property name="toolTip">
|
||||
<string>The default metatile value that will be used for the top-right border metatile on new maps.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile3">
|
||||
<property name="toolTip">
|
||||
<string>The default metatile value that will be used for the bottom-left border metatile on new maps.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NoScrollSpinBox" name="spinBox_BorderMetatile4">
|
||||
<property name="toolTip">
|
||||
<string>The default metatile value that will be used for the bottom-right border metatile on new maps.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Prefabs">
|
||||
<property name="title">
|
||||
<string>Prefabs</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="2">
|
||||
<widget class="QToolButton" name="button_ChoosePrefabs">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/folder.ico</normaloff>:/icons/folder.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="button_ImportDefaultPrefabs">
|
||||
<property name="toolTip">
|
||||
<string>Restore the data in the prefabs file to the version defaults. Will create a new file if one doesn't exist.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import Defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_PrefabsPath">
|
||||
<property name="toolTip">
|
||||
<string>The file that will be used to populate the Prefabs tab</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_PrefabsPath">
|
||||
<property name="text">
|
||||
<string>Prefabs Path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_Warning">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_Warning">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">.QFrame { border: 1px solid red; }</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_Warning">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:13pt; color:#d7000c;">WARNING: </span><span style=" font-weight:400;">The settings from this point below require project changes to function properly. Do not modify these settings without the necessary changes. </span></p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_BaseGameVersion" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_BaseGameVersion">
|
||||
<property name="text">
|
||||
<string>Base game version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NoScrollComboBox" name="comboBox_BaseGameVersion">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</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>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_TilesetsMetatiles">
|
||||
<property name="title">
|
||||
<string>Tilesets / Metatiles</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="8" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableTripleLayerMetatiles">
|
||||
<property name="text">
|
||||
<string>Enable Triple Layer Metatiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="UIntSpinBox" name="spinBox_TerrainTypeMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="hasPadding">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_AttributesSize">
|
||||
<property name="toolTip">
|
||||
<string>The number of bytes used per metatile for metatile attributes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Attributes size (in bytes)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_TerrainTypeMask">
|
||||
<property name="text">
|
||||
<string>Terrain Type mask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_AttributesSize">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>15</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_BehaviorMask">
|
||||
<property name="text">
|
||||
<string>Behavior mask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="UIntSpinBox" name="spinBox_BehaviorMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="hasPadding">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_OutputCallback">
|
||||
<property name="toolTip">
|
||||
<string>Whether the C data outputted for new tilesets will include the "callback" field</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output 'callback' field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_OutputIsCompressed">
|
||||
<property name="toolTip">
|
||||
<string>Whether the C data outputted for new tilesets will include the "isCompressed" field</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output 'isCompressed' field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="UIntSpinBox" name="spinBox_EncounterTypeMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="hasPadding">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_EncounterTypeMask">
|
||||
<property name="text">
|
||||
<string>Encounter Type mask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_LayerTypeMask">
|
||||
<property name="text">
|
||||
<string>Layer Type mask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="UIntSpinBox" name="spinBox_LayerTypeMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="hasPadding">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_ProjectPaths" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<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_ProjectFiles">
|
||||
<property name="text">
|
||||
<string> <html><head/><body><p><a href="https://huderlem.github.io/porymap/manual/project-files.html"><span style=" text-decoration: underline;">Project Files</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_ProjectPaths">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>320</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaContents_ProjectPaths">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>533</width>
|
||||
<height>318</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="layout_ProjectPaths">
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Events">
|
||||
<property name="title">
|
||||
<string>Events</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableWeatherTriggers">
|
||||
<property name="text">
|
||||
<string>Enable Weather Triggers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableSecretBases">
|
||||
<property name="text">
|
||||
<string>Enable Secret Bases</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableCloneObjects">
|
||||
<property name="text">
|
||||
<string>Enable Clone Objects</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_EnableRequiresItemfinder">
|
||||
<property name="text">
|
||||
<string>Enable 'Requires Itemfinder' for Hidden Items</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_EnableQuantity">
|
||||
<property name="text">
|
||||
<string>Enable 'Quantity' for Hidden Items</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_EnableRespawn">
|
||||
<property name="text">
|
||||
<string>Enable 'Respawn Map/NPC' for Heal Locations</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Maps">
|
||||
<property name="title">
|
||||
<string>Maps</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_EnableAllowFlags">
|
||||
<property name="toolTip">
|
||||
<string>Whether "Allow Running", "Allow Biking" and "Allow Dig & Escape Rope" are default options for Map Headers</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable 'Allow Running/Biking/Escaping'</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableFloorNumber">
|
||||
<property name="toolTip">
|
||||
<string>Whether "Floor Number" is a default option for Map Headers</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable 'Floor Number'</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableCustomBorderSize">
|
||||
<property name="toolTip">
|
||||
<string>Whether the dimensions of the border can be changed. If not set, all borders are 2x2</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Custom Border Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NoScrollComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>noscrollcombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NoScrollSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>noscrollspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>UIntSpinBox</class>
|
||||
<extends>QAbstractSpinBox</extends>
|
||||
<header>uintspinbox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -169,7 +169,7 @@
|
|||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
|
@ -186,8 +186,8 @@
|
|||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
|
@ -201,7 +201,44 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="13" column="3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_BottomTop">
|
||||
<property name="text">
|
||||
<string>Bottom/Top</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<widget class="NoScrollComboBox" name="comboBox_metatileBehaviors"/>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_encounterType">
|
||||
<property name="text">
|
||||
<string>Encounter Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_terrainType">
|
||||
<property name="text">
|
||||
<string>Terrain Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="NoScrollComboBox" name="comboBox_terrainType"/>
|
||||
</item>
|
||||
<item row="12" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_metatileLabel">
|
||||
<property name="text">
|
||||
<string>Metatile Label (Optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="NoScrollComboBox" name="comboBox_layerType"/>
|
||||
</item>
|
||||
<item row="13" column="2">
|
||||
<widget class="QToolButton" name="copyButton_metatileLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Copies the full metatile label to the clipboard.</p></body></html></string>
|
||||
|
@ -215,46 +252,29 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0" colspan="3">
|
||||
<item row="13" column="0" colspan="2">
|
||||
<widget class="QLineEdit" name="lineEdit_metatileLabel">
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label_metatileLabel">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_layerType">
|
||||
<property name="text">
|
||||
<string>Metatile Label (Optional)</string>
|
||||
<string>Layer Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_BottomTop">
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_metatileBehavior">
|
||||
<property name="text">
|
||||
<string>Bottom/Top</string>
|
||||
<string>Metatile Behavior</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<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="8" column="0">
|
||||
<widget class="QLabel" name="label_encounterType">
|
||||
<property name="text">
|
||||
<string>Encounter Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
<item row="9" column="0">
|
||||
<widget class="NoScrollComboBox" name="comboBox_encounterType"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGraphicsView" name="graphicsView_metatileLayers">
|
||||
|
@ -278,44 +298,17 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="3">
|
||||
<widget class="NoScrollComboBox" name="comboBox_metatileBehaviors"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_layerType">
|
||||
<property name="text">
|
||||
<string>Layer Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QComboBox" name="comboBox_encounterType"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label_metatileBehavior">
|
||||
<property name="text">
|
||||
<string>Metatile Behavior</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QComboBox" name="comboBox_terrainType"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QComboBox" name="comboBox_layerType"/>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_terrainType">
|
||||
<property name="text">
|
||||
<string>Terrain Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Tile Properties</string>
|
||||
</property>
|
||||
|
@ -331,7 +324,14 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="spinBox_paletteSelector"/>
|
||||
<widget class="QSpinBox" name="spinBox_paletteSelector">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
|
@ -364,7 +364,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
|
@ -377,7 +377,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QGraphicsView" name="graphicsView_selectedTile">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
|
@ -402,6 +402,32 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -410,6 +436,12 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_Tiles">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -419,7 +451,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>411</width>
|
||||
<height>247</height>
|
||||
<height>272</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include <QMultiMap>
|
||||
|
||||
// In both versions the default new map border is a generic tree
|
||||
#define DEFAULT_BORDER_RSE (QList<int>{468, 469, 476, 477})
|
||||
#define DEFAULT_BORDER_FRLG (QList<int>{20, 21, 28, 29})
|
||||
#define DEFAULT_BORDER_RSE (QList<uint16_t>{0x1D4, 0x1D5, 0x1DC, 0x1DD})
|
||||
#define DEFAULT_BORDER_FRLG (QList<uint16_t>{0x14, 0x15, 0x1C, 0x1D})
|
||||
|
||||
#define CONFIG_BACKWARDS_COMPATABILITY
|
||||
|
||||
|
@ -26,6 +26,7 @@ class KeyValueConfigBase
|
|||
public:
|
||||
void save();
|
||||
void load();
|
||||
void setSaveDisabled(bool disabled);
|
||||
virtual ~KeyValueConfigBase();
|
||||
virtual void reset() = 0;
|
||||
protected:
|
||||
|
@ -37,6 +38,8 @@ protected:
|
|||
bool getConfigBool(QString key, QString value);
|
||||
int getConfigInteger(QString key, QString value, int min, int max, int defaultValue);
|
||||
uint32_t getConfigUint32(QString key, QString value, uint32_t min, uint32_t max, uint32_t defaultValue);
|
||||
private:
|
||||
bool saveDisabled = false;
|
||||
};
|
||||
|
||||
class PorymapConfig: public KeyValueConfigBase
|
||||
|
@ -70,6 +73,8 @@ public:
|
|||
void setTilesetEditorGeometry(QByteArray, QByteArray);
|
||||
void setPaletteEditorGeometry(QByteArray, QByteArray);
|
||||
void setRegionMapEditorGeometry(QByteArray, QByteArray);
|
||||
void setProjectSettingsEditorGeometry(QByteArray, QByteArray);
|
||||
void setCustomScriptsEditorGeometry(QByteArray, QByteArray);
|
||||
void setCollisionOpacity(int opacity);
|
||||
void setMetatilesZoom(int zoom);
|
||||
void setShowPlayerView(bool enabled);
|
||||
|
@ -90,6 +95,8 @@ public:
|
|||
QMap<QString, QByteArray> getTilesetEditorGeometry();
|
||||
QMap<QString, QByteArray> getPaletteEditorGeometry();
|
||||
QMap<QString, QByteArray> getRegionMapEditorGeometry();
|
||||
QMap<QString, QByteArray> getProjectSettingsEditorGeometry();
|
||||
QMap<QString, QByteArray> getCustomScriptsEditorGeometry();
|
||||
int getCollisionOpacity();
|
||||
int getMetatilesZoom();
|
||||
bool getShowPlayerView();
|
||||
|
@ -126,6 +133,10 @@ private:
|
|||
QByteArray paletteEditorState;
|
||||
QByteArray regionMapEditorGeometry;
|
||||
QByteArray regionMapEditorState;
|
||||
QByteArray projectSettingsEditorGeometry;
|
||||
QByteArray projectSettingsEditorState;
|
||||
QByteArray customScriptsEditorGeometry;
|
||||
QByteArray customScriptsEditorState;
|
||||
int collisionOpacity;
|
||||
int metatilesZoom;
|
||||
bool showPlayerView;
|
||||
|
@ -203,20 +214,11 @@ public:
|
|||
}
|
||||
virtual void reset() override {
|
||||
this->baseGameVersion = BaseGameVersion::pokeemerald;
|
||||
this->useCustomBorderSize = false;
|
||||
this->enableEventWeatherTrigger = true;
|
||||
this->enableEventSecretBase = true;
|
||||
this->enableHiddenItemQuantity = false;
|
||||
this->enableHiddenItemRequiresItemfinder = false;
|
||||
this->enableHealLocationRespawnData = false;
|
||||
this->enableEventCloneObject = false;
|
||||
this->enableFloorNumber = false;
|
||||
this->createMapTextFile = false;
|
||||
// Reset non-version-specific settings
|
||||
this->usePoryScript = false;
|
||||
this->enableTripleLayerMetatiles = false;
|
||||
this->newMapMetatileId = 1;
|
||||
this->newMapElevation = 3;
|
||||
this->newMapBorderMetatileIds = DEFAULT_BORDER_RSE;
|
||||
this->defaultPrimaryTileset = "gTileset_General";
|
||||
this->prefabFilepath = QString();
|
||||
this->prefabImportPrompted = false;
|
||||
|
@ -225,9 +227,14 @@ public:
|
|||
this->filePaths.clear();
|
||||
this->readKeys.clear();
|
||||
}
|
||||
static const QMap<ProjectFilePath, std::pair<QString, QString>> defaultPaths;
|
||||
static const QStringList versionStrings;
|
||||
void reset(BaseGameVersion baseGameVersion);
|
||||
void setBaseGameVersion(BaseGameVersion baseGameVersion);
|
||||
BaseGameVersion getBaseGameVersion();
|
||||
QString getBaseGameVersionString();
|
||||
QString getBaseGameVersionString(BaseGameVersion version);
|
||||
BaseGameVersion stringToBaseGameVersion(QString string, bool * ok = nullptr);
|
||||
void setUsePoryScript(bool usePoryScript);
|
||||
bool getUsePoryScript();
|
||||
void setProjectDir(QString projectDir);
|
||||
|
@ -254,18 +261,22 @@ public:
|
|||
bool getTripleLayerMetatilesEnabled();
|
||||
int getNumLayersInMetatile();
|
||||
int getNumTilesInMetatile();
|
||||
void setNewMapMetatileId(int metatileId);
|
||||
int getNewMapMetatileId();
|
||||
void setNewMapMetatileId(uint16_t metatileId);
|
||||
uint16_t getNewMapMetatileId();
|
||||
void setNewMapElevation(int elevation);
|
||||
int getNewMapElevation();
|
||||
void setNewMapBorderMetatileIds(QList<int> metatileIds);
|
||||
QList<int> getNewMapBorderMetatileIds();
|
||||
void setNewMapBorderMetatileIds(QList<uint16_t> metatileIds);
|
||||
QList<uint16_t> getNewMapBorderMetatileIds();
|
||||
QString getDefaultPrimaryTileset();
|
||||
QString getDefaultSecondaryTileset();
|
||||
void setDefaultPrimaryTileset(QString tilesetName);
|
||||
void setDefaultSecondaryTileset(QString tilesetName);
|
||||
void setFilePath(QString pathId, QString path);
|
||||
void setFilePath(ProjectFilePath pathId, QString path);
|
||||
QString getFilePath(ProjectFilePath pathId);
|
||||
QString getFilePath(QString defaultPath, bool customOnly = false);
|
||||
QString getFilePath(ProjectFilePath pathId, bool customOnly = false);
|
||||
void setPrefabFilepath(QString filepath);
|
||||
QString getPrefabFilepath(bool setIfEmpty);
|
||||
QString getPrefabFilepath();
|
||||
void setPrefabImportPrompted(bool prompted);
|
||||
bool getPrefabImportPrompted();
|
||||
void setTilesetsHaveCallback(bool has);
|
||||
|
@ -273,11 +284,17 @@ public:
|
|||
void setTilesetsHaveIsCompressed(bool has);
|
||||
bool getTilesetsHaveIsCompressed();
|
||||
int getMetatileAttributesSize();
|
||||
void setMetatileAttributesSize(int size);
|
||||
uint32_t getMetatileBehaviorMask();
|
||||
uint32_t getMetatileTerrainTypeMask();
|
||||
uint32_t getMetatileEncounterTypeMask();
|
||||
uint32_t getMetatileLayerTypeMask();
|
||||
void setMetatileBehaviorMask(uint32_t mask);
|
||||
void setMetatileTerrainTypeMask(uint32_t mask);
|
||||
void setMetatileEncounterTypeMask(uint32_t mask);
|
||||
void setMetatileLayerTypeMask(uint32_t mask);
|
||||
bool getMapAllowFlagsEnabled();
|
||||
void setMapAllowFlagsEnabled(bool enabled);
|
||||
protected:
|
||||
virtual QString getConfigFilepath() override;
|
||||
virtual void parseConfigKeyValue(QString key, QString value) override;
|
||||
|
@ -299,9 +316,9 @@ private:
|
|||
bool enableFloorNumber;
|
||||
bool createMapTextFile;
|
||||
bool enableTripleLayerMetatiles;
|
||||
int newMapMetatileId;
|
||||
uint16_t newMapMetatileId;
|
||||
int newMapElevation;
|
||||
QList<int> newMapBorderMetatileIds;
|
||||
QList<uint16_t> newMapBorderMetatileIds;
|
||||
QString defaultPrimaryTileset;
|
||||
QString defaultSecondaryTileset;
|
||||
QStringList readKeys;
|
||||
|
@ -337,8 +354,11 @@ public:
|
|||
bool getEncounterJsonActive();
|
||||
void setProjectDir(QString projectDir);
|
||||
QString getProjectDir();
|
||||
void setCustomScripts(QList<QString> scripts);
|
||||
QList<QString> getCustomScripts();
|
||||
void setCustomScripts(QStringList scripts, QList<bool> enabled);
|
||||
QStringList getCustomScriptPaths();
|
||||
QList<bool> getCustomScriptsEnabled();
|
||||
void parseCustomScripts(QString input);
|
||||
QString outputCustomScripts();
|
||||
protected:
|
||||
virtual QString getConfigFilepath() override;
|
||||
virtual void parseConfigKeyValue(QString key, QString value) override;
|
||||
|
@ -352,7 +372,7 @@ private:
|
|||
QString projectDir;
|
||||
QString recentMap;
|
||||
bool useEncounterJson;
|
||||
QList<QString> customScripts;
|
||||
QMap<QString, bool> customScripts;
|
||||
QStringList readKeys;
|
||||
};
|
||||
|
||||
|
|
|
@ -93,6 +93,15 @@ public:
|
|||
static QPoint coordFromPixmapCoord(const QPointF &pixelCoord);
|
||||
static int getDefaultAttributesSize(BaseGameVersion version);
|
||||
static void setCustomLayout(Project*);
|
||||
static QString getMetatileIdString(uint16_t metatileId) {
|
||||
return "0x" + QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper();
|
||||
};
|
||||
static QString getMetatileIdStringList(const QList<uint16_t> metatileIds) {
|
||||
QStringList metatiles;
|
||||
for (auto metatileId : metatileIds)
|
||||
metatiles << Metatile::getMetatileIdString(metatileId);
|
||||
return metatiles.join(",");
|
||||
};
|
||||
|
||||
private:
|
||||
// Stores how each attribute should be laid out for all metatiles, according to the user's config
|
||||
|
|
|
@ -149,7 +149,7 @@ public:
|
|||
|
||||
void shouldReselectEvents();
|
||||
void scaleMapView(int);
|
||||
void openInTextEditor(const QString &path, int lineNum = 0) const;
|
||||
static void openInTextEditor(const QString &path, int lineNum = 0);
|
||||
bool eventLimitReached(Event::Type type);
|
||||
|
||||
public slots:
|
||||
|
@ -179,9 +179,9 @@ private:
|
|||
void updateEncounterFields(EncounterFields newFields);
|
||||
QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
|
||||
QString getMetatileDisplayMessage(uint16_t metatileId);
|
||||
bool startDetachedProcess(const QString &command,
|
||||
const QString &workingDirectory = QString(),
|
||||
qint64 *pid = nullptr) const;
|
||||
static bool startDetachedProcess(const QString &command,
|
||||
const QString &workingDirectory = QString(),
|
||||
qint64 *pid = nullptr);
|
||||
|
||||
private slots:
|
||||
void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "newtilesetdialog.h"
|
||||
#include "shortcutseditor.h"
|
||||
#include "preferenceeditor.h"
|
||||
#include "projectsettingseditor.h"
|
||||
#include "customscriptseditor.h"
|
||||
|
||||
|
||||
|
||||
|
@ -197,11 +199,7 @@ private slots:
|
|||
void on_checkBox_AllowBiking_stateChanged(int selected);
|
||||
void on_checkBox_AllowEscaping_stateChanged(int selected);
|
||||
void on_spinBox_FloorNumber_valueChanged(int offset);
|
||||
void on_actionUse_Encounter_Json_triggered(bool checked);
|
||||
void on_actionMonitor_Project_Files_triggered(bool checked);
|
||||
void on_actionUse_Poryscript_triggered(bool checked);
|
||||
void on_actionOpen_Recent_Project_On_Launch_triggered(bool checked);
|
||||
void on_actionEdit_Shortcuts_triggered();
|
||||
void on_actionShortcuts_triggered();
|
||||
|
||||
void on_actionZoom_In_triggered();
|
||||
void on_actionZoom_Out_triggered();
|
||||
|
@ -284,8 +282,11 @@ private slots:
|
|||
void on_pushButton_CreatePrefab_clicked();
|
||||
|
||||
void on_actionRegion_Map_Editor_triggered();
|
||||
void on_actionEdit_Preferences_triggered();
|
||||
void on_actionPreferences_triggered();
|
||||
void togglePreferenceSpecificUi();
|
||||
void on_actionProject_Settings_triggered();
|
||||
void on_actionCustom_Scripts_triggered();
|
||||
void reloadScriptEngine();
|
||||
|
||||
public:
|
||||
Ui::MainWindow *ui;
|
||||
|
@ -299,6 +300,8 @@ private:
|
|||
QPointer<MapImageExporter> mapImageExporter = nullptr;
|
||||
QPointer<NewMapPopup> newMapPrompt = nullptr;
|
||||
QPointer<PreferenceEditor> preferenceEditor = nullptr;
|
||||
QPointer<ProjectSettingsEditor> projectSettingsEditor = nullptr;
|
||||
QPointer<CustomScriptsEditor> customScriptsEditor = nullptr;
|
||||
FilterChildrenProxyModel *mapListProxyModel;
|
||||
QStandardItemModel *mapListModel;
|
||||
QList<QStandardItem*> *mapGroupItemsList;
|
||||
|
@ -342,6 +345,7 @@ private:
|
|||
bool loadProjectCombos();
|
||||
bool populateMapList();
|
||||
void sortMapList();
|
||||
void openSubWindow(QWidget * window);
|
||||
QString getExistingDirectory(QString);
|
||||
bool openProject(QString dir);
|
||||
QString getDefaultMap();
|
||||
|
@ -381,6 +385,7 @@ private:
|
|||
void initTilesetEditor();
|
||||
bool initRegionMapEditor(bool silent = false);
|
||||
void initShortcutsEditor();
|
||||
void initCustomScriptsEditor();
|
||||
void connectSubEditorsToShortcutsEditor();
|
||||
|
||||
bool isProjectOpen();
|
||||
|
|
61
include/ui/customscriptseditor.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef CUSTOMSCRIPTSEDITOR_H
|
||||
#define CUSTOMSCRIPTSEDITOR_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QListWidget>
|
||||
#include <QAbstractButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "customscriptslistitem.h"
|
||||
|
||||
namespace Ui {
|
||||
class CustomScriptsEditor;
|
||||
}
|
||||
|
||||
|
||||
class CustomScriptsEditor : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomScriptsEditor(QWidget *parent = nullptr);
|
||||
~CustomScriptsEditor();
|
||||
|
||||
signals:
|
||||
void reloadScriptEngine();
|
||||
|
||||
public slots:
|
||||
void applyUserShortcuts();
|
||||
|
||||
private:
|
||||
Ui::CustomScriptsEditor *ui;
|
||||
|
||||
bool hasUnsavedChanges = false;
|
||||
QString importDir;
|
||||
const QString baseDir;
|
||||
|
||||
void displayScript(const QString &filepath, bool enabled);
|
||||
QString chooseScript(QString dir);
|
||||
void removeScript(QListWidgetItem * item);
|
||||
void replaceScript(QListWidgetItem * item);
|
||||
void openScript(QListWidgetItem * item);
|
||||
QString getScriptFilepath(QListWidgetItem * item, bool absolutePath = true) const;
|
||||
void setScriptFilepath(QListWidgetItem * item, QString filepath) const;
|
||||
bool getScriptEnabled(QListWidgetItem * item) const;
|
||||
void markEdited();
|
||||
int prompt(const QString &text, QMessageBox::StandardButton defaultButton);
|
||||
void save();
|
||||
void closeEvent(QCloseEvent*);
|
||||
void restoreWindowState();
|
||||
void initShortcuts();
|
||||
QObjectList shortcutableObjects() const;
|
||||
|
||||
private slots:
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
void addNewScript();
|
||||
void reloadScripts();
|
||||
void removeSelectedScripts();
|
||||
void openSelectedScripts();
|
||||
};
|
||||
|
||||
#endif // CUSTOMSCRIPTSEDITOR_H
|
22
include/ui/customscriptslistitem.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef CUSTOMSCRIPTSLISTITEM_H
|
||||
#define CUSTOMSCRIPTSLISTITEM_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
namespace Ui {
|
||||
class CustomScriptsListItem;
|
||||
}
|
||||
|
||||
class CustomScriptsListItem : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomScriptsListItem(QWidget *parent = nullptr);
|
||||
~CustomScriptsListItem();
|
||||
|
||||
public:
|
||||
Ui::CustomScriptsListItem *ui;
|
||||
};
|
||||
|
||||
#endif // CUSTOMSCRIPTSLISTITEM_H
|
|
@ -11,8 +11,10 @@ public:
|
|||
explicit NoScrollComboBox(QWidget *parent = nullptr);
|
||||
void wheelEvent(QWheelEvent *event);
|
||||
void setTextItem(const QString &text);
|
||||
void setNumberItem(int value);
|
||||
|
||||
private:
|
||||
void setItem(int index, const QString &text);
|
||||
};
|
||||
|
||||
#endif // NOSCROLLCOMBOBOX_H
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
void initPrefabUI(MetatileSelector *selector, QWidget *prefabWidget, QLabel *emptyPrefabLabel, Map *map);
|
||||
void addPrefab(MetatileSelection selection, Map *map, QString name);
|
||||
void updatePrefabUi(Map *map);
|
||||
void tryImportDefaultPrefabs(Map *map);
|
||||
bool tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, QString filepath = "");
|
||||
|
||||
private:
|
||||
MetatileSelector *selector;
|
||||
|
|
|
@ -18,6 +18,7 @@ class PreferenceEditor : public QMainWindow
|
|||
public:
|
||||
explicit PreferenceEditor(QWidget *parent = nullptr);
|
||||
~PreferenceEditor();
|
||||
void updateFields();
|
||||
|
||||
signals:
|
||||
void preferencesSaved();
|
||||
|
@ -27,9 +28,10 @@ private:
|
|||
Ui::PreferenceEditor *ui;
|
||||
NoScrollComboBox *themeSelector;
|
||||
|
||||
void populateFields();
|
||||
void initFields();
|
||||
void saveFields();
|
||||
|
||||
|
||||
private slots:
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
};
|
||||
|
|
60
include/ui/projectsettingseditor.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef PROJECTSETTINGSEDITOR_H
|
||||
#define PROJECTSETTINGSEDITOR_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "project.h"
|
||||
|
||||
class NoScrollComboBox;
|
||||
class QAbstractButton;
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class ProjectSettingsEditor;
|
||||
}
|
||||
|
||||
class ProjectSettingsEditor : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ProjectSettingsEditor(QWidget *parent = nullptr, Project *project = nullptr);
|
||||
~ProjectSettingsEditor();
|
||||
|
||||
signals:
|
||||
void reloadProject();
|
||||
|
||||
private:
|
||||
Ui::ProjectSettingsEditor *ui;
|
||||
Project *project;
|
||||
|
||||
bool hasUnsavedChanges = false;
|
||||
bool projectNeedsReload = false;
|
||||
bool refreshing = false;
|
||||
const QString baseDir;
|
||||
|
||||
void initUi();
|
||||
void connectSignals();
|
||||
void restoreWindowState();
|
||||
void save();
|
||||
void refresh();
|
||||
void closeEvent(QCloseEvent*);
|
||||
int prompt(const QString &, QMessageBox::StandardButton = QMessageBox::Yes);
|
||||
bool promptSaveChanges();
|
||||
bool promptRestoreDefaults();
|
||||
|
||||
void setBorderMetatilesUi(bool customSize);
|
||||
void setBorderMetatileIds(bool customSize, QList<uint16_t> metatileIds);
|
||||
QList<uint16_t> getBorderMetatileIds(bool customSize);
|
||||
|
||||
void createProjectPathsTable();
|
||||
QString chooseProjectFile(const QString &defaultFilepath);
|
||||
|
||||
private slots:
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
void choosePrefabsFileClicked(bool);
|
||||
void importDefaultPrefabsClicked(bool);
|
||||
void updateAttributeLimits(const QString &attrSize);
|
||||
void markEdited();
|
||||
};
|
||||
|
||||
#endif // PROJECTSETTINGSEDITOR_H
|
|
@ -138,7 +138,6 @@ private:
|
|||
void copyMetatile(bool cut);
|
||||
void pasteMetatile(const Metatile * toPaste, QString label);
|
||||
bool replaceMetatile(uint16_t metatileId, const Metatile * src, QString label);
|
||||
void setComboValue(QComboBox * combo, int value);
|
||||
void commitMetatileChange(Metatile * prevMetatile);
|
||||
void commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel);
|
||||
|
||||
|
|
67
include/ui/uintspinbox.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
#ifndef UINTSPINBOX_H
|
||||
#define UINTSPINBOX_H
|
||||
|
||||
#include <QAbstractSpinBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
/*
|
||||
QSpinBox stores minimum/maximum/value as ints. This class is a version of QAbstractSpinBox for values above 0x7FFFFFFF.
|
||||
It minimally implements some QSpinBox-specific features like prefixes to simplify support for hex value input.
|
||||
It also implements the scroll focus requirements of NoScrollSpinBox.
|
||||
*/
|
||||
|
||||
class UIntSpinBox : public QAbstractSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UIntSpinBox(QWidget *parent = nullptr);
|
||||
~UIntSpinBox() {};
|
||||
|
||||
uint32_t value() const { return m_value; }
|
||||
uint32_t minimum() const { return m_minimum; }
|
||||
uint32_t maximum() const { return m_maximum; }
|
||||
QString prefix() const { return m_prefix; }
|
||||
int displayIntegerBase() const { return m_displayIntegerBase; }
|
||||
bool hasPadding() const { return m_hasPadding; }
|
||||
|
||||
void setMinimum(uint32_t min);
|
||||
void setMaximum(uint32_t max);
|
||||
void setRange(uint32_t min, uint32_t max);
|
||||
void setPrefix(const QString &prefix);
|
||||
void setDisplayIntegerBase(int base);
|
||||
void setHasPadding(bool enabled);
|
||||
|
||||
private:
|
||||
uint32_t m_minimum;
|
||||
uint32_t m_maximum;
|
||||
uint32_t m_value;
|
||||
QString m_prefix;
|
||||
int m_displayIntegerBase;
|
||||
bool m_hasPadding;
|
||||
int m_numChars;
|
||||
|
||||
void updateEdit();
|
||||
QString stripped(QString input) const;
|
||||
|
||||
virtual void stepBy(int steps) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
virtual void focusOutEvent(QFocusEvent *event) override;
|
||||
|
||||
protected:
|
||||
virtual uint32_t valueFromText(const QString &text) const;
|
||||
virtual QString textFromValue(uint32_t val) const;
|
||||
virtual QValidator::State validate(QString &input, int &pos) const override;
|
||||
virtual QAbstractSpinBox::StepEnabled stepEnabled() const override;
|
||||
|
||||
|
||||
public slots:
|
||||
void setValue(uint32_t val);
|
||||
void onEditFinished();
|
||||
|
||||
signals:
|
||||
void valueChanged(uint32_t val);
|
||||
void textChanged(const QString &text);
|
||||
};
|
||||
|
||||
#endif // UINTSPINBOX_H
|
17
porymap.pro
|
@ -42,6 +42,8 @@ SOURCES += src/core/block.cpp \
|
|||
src/scriptapi/apiutility.cpp \
|
||||
src/scriptapi/scripting.cpp \
|
||||
src/ui/aboutporymap.cpp \
|
||||
src/ui/customscriptseditor.cpp \
|
||||
src/ui/customscriptslistitem.cpp \
|
||||
src/ui/draggablepixmapitem.cpp \
|
||||
src/ui/bordermetatilespixmapitem.cpp \
|
||||
src/ui/collisionpixmapitem.cpp \
|
||||
|
@ -49,6 +51,7 @@ SOURCES += src/core/block.cpp \
|
|||
src/ui/currentselectedmetatilespixmapitem.cpp \
|
||||
src/ui/overlay.cpp \
|
||||
src/ui/prefab.cpp \
|
||||
src/ui/projectsettingseditor.cpp \
|
||||
src/ui/regionmaplayoutpixmapitem.cpp \
|
||||
src/ui/regionmapentriespixmapitem.cpp \
|
||||
src/ui/cursortilerect.cpp \
|
||||
|
@ -97,7 +100,8 @@ SOURCES += src/core/block.cpp \
|
|||
src/mainwindow.cpp \
|
||||
src/project.cpp \
|
||||
src/settings.cpp \
|
||||
src/log.cpp
|
||||
src/log.cpp \
|
||||
src/ui/uintspinbox.cpp
|
||||
|
||||
HEADERS += include/core/block.h \
|
||||
include/core/blockdata.h \
|
||||
|
@ -128,12 +132,15 @@ HEADERS += include/core/block.h \
|
|||
include/lib/orderedmap.h \
|
||||
include/lib/orderedjson.h \
|
||||
include/ui/aboutporymap.h \
|
||||
include/ui/customscriptseditor.h \
|
||||
include/ui/customscriptslistitem.h \
|
||||
include/ui/draggablepixmapitem.h \
|
||||
include/ui/bordermetatilespixmapitem.h \
|
||||
include/ui/collisionpixmapitem.h \
|
||||
include/ui/connectionpixmapitem.h \
|
||||
include/ui/currentselectedmetatilespixmapitem.h \
|
||||
include/ui/prefabframe.h \
|
||||
include/ui/projectsettingseditor.h \
|
||||
include/ui/regionmaplayoutpixmapitem.h \
|
||||
include/ui/regionmapentriespixmapitem.h \
|
||||
include/ui/cursortilerect.h \
|
||||
|
@ -186,7 +193,8 @@ HEADERS += include/core/block.h \
|
|||
include/scripting.h \
|
||||
include/scriptutility.h \
|
||||
include/settings.h \
|
||||
include/log.h
|
||||
include/log.h \
|
||||
include/ui/uintspinbox.h
|
||||
|
||||
FORMS += forms/mainwindow.ui \
|
||||
forms/prefabcreationdialog.ui \
|
||||
|
@ -201,7 +209,10 @@ FORMS += forms/mainwindow.ui \
|
|||
forms/shortcutseditor.ui \
|
||||
forms/preferenceeditor.ui \
|
||||
forms/regionmappropertiesdialog.ui \
|
||||
forms/colorpicker.ui
|
||||
forms/colorpicker.ui \
|
||||
forms/projectsettingseditor.ui \
|
||||
forms/customscriptseditor.ui \
|
||||
forms/customscriptslistitem.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources/images.qrc \
|
||||
|
|
BIN
resources/icons/edit_document.ico
Executable file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/icons/refresh.ico
Executable file
After Width: | Height: | Size: 1.6 KiB |
|
@ -4,6 +4,7 @@
|
|||
<file>icons/collapse_all.ico</file>
|
||||
<file>icons/cursor.ico</file>
|
||||
<file>icons/delete.ico</file>
|
||||
<file>icons/edit_document.ico</file>
|
||||
<file>icons/expand_all.ico</file>
|
||||
<file>icons/fill_color_cursor.ico</file>
|
||||
<file>icons/fill_color.ico</file>
|
||||
|
@ -24,6 +25,7 @@
|
|||
<file>icons/porymap-icon-1.ico</file>
|
||||
<file>icons/porymap-icon-2.ico</file>
|
||||
<file>icons/porymap.icns</file>
|
||||
<file>icons/refresh.ico</file>
|
||||
<file>icons/shift_cursor.ico</file>
|
||||
<file>icons/shift.ico</file>
|
||||
<file>icons/sort_alphabet.ico</file>
|
||||
|
|
289
src/config.cpp
|
@ -16,7 +16,7 @@
|
|||
#include <QAction>
|
||||
#include <QAbstractButton>
|
||||
|
||||
const QMap<ProjectFilePath, std::pair<QString, QString>> defaultPaths = {
|
||||
const QMap<ProjectFilePath, std::pair<QString, QString>> ProjectConfig::defaultPaths = {
|
||||
{ProjectFilePath::data_map_folders, { "data_map_folders", "data/maps/"}},
|
||||
{ProjectFilePath::data_scripts_folders, { "data_scripts_folders", "data/scripts/"}},
|
||||
{ProjectFilePath::data_layouts_folders, { "data_layouts_folders", "data/layouts/"}},
|
||||
|
@ -64,7 +64,7 @@ const QMap<ProjectFilePath, std::pair<QString, QString>> defaultPaths = {
|
|||
};
|
||||
|
||||
ProjectFilePath reverseDefaultPaths(QString str) {
|
||||
for (auto it = defaultPaths.constKeyValueBegin(); it != defaultPaths.constKeyValueEnd(); ++it) {
|
||||
for (auto it = ProjectConfig::defaultPaths.constKeyValueBegin(); it != ProjectConfig::defaultPaths.constKeyValueEnd(); ++it) {
|
||||
if ((*it).second.first == str) return (*it).first;
|
||||
}
|
||||
return static_cast<ProjectFilePath>(-1);
|
||||
|
@ -119,6 +119,9 @@ void KeyValueConfigBase::load() {
|
|||
}
|
||||
|
||||
void KeyValueConfigBase::save() {
|
||||
if (this->saveDisabled)
|
||||
return;
|
||||
|
||||
QString text = "";
|
||||
QMap<QString, QString> map = this->getKeyValueMap();
|
||||
for (QMap<QString, QString>::iterator it = map.begin(); it != map.end(); it++) {
|
||||
|
@ -163,6 +166,11 @@ uint32_t KeyValueConfigBase::getConfigUint32(QString key, QString value, uint32_
|
|||
return qMin(max, qMax(min, result));
|
||||
}
|
||||
|
||||
// For temporarily disabling saving during frequent config changes.
|
||||
void KeyValueConfigBase::setSaveDisabled(bool disabled) {
|
||||
this->saveDisabled = disabled;
|
||||
}
|
||||
|
||||
const QMap<MapSortOrder, QString> mapSortOrderMap = {
|
||||
{MapSortOrder::Group, "group"},
|
||||
{MapSortOrder::Layout, "layout"},
|
||||
|
@ -226,6 +234,14 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
this->regionMapEditorGeometry = bytesFromString(value);
|
||||
} else if (key == "region_map_editor_state") {
|
||||
this->regionMapEditorState = bytesFromString(value);
|
||||
} else if (key == "project_settings_editor_geometry") {
|
||||
this->projectSettingsEditorGeometry = bytesFromString(value);
|
||||
} else if (key == "project_settings_editor_state") {
|
||||
this->projectSettingsEditorState = bytesFromString(value);
|
||||
} else if (key == "custom_scripts_editor_geometry") {
|
||||
this->customScriptsEditorGeometry = bytesFromString(value);
|
||||
} else if (key == "custom_scripts_editor_state") {
|
||||
this->customScriptsEditorState = bytesFromString(value);
|
||||
} else if (key == "metatiles_zoom") {
|
||||
this->metatilesZoom = getConfigInteger(key, value, 10, 100, 30);
|
||||
} else if (key == "show_player_view") {
|
||||
|
@ -272,6 +288,10 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
|
|||
map.insert("palette_editor_state", stringFromByteArray(this->paletteEditorState));
|
||||
map.insert("region_map_editor_geometry", stringFromByteArray(this->regionMapEditorGeometry));
|
||||
map.insert("region_map_editor_state", stringFromByteArray(this->regionMapEditorState));
|
||||
map.insert("project_settings_editor_geometry", stringFromByteArray(this->projectSettingsEditorGeometry));
|
||||
map.insert("project_settings_editor_state", stringFromByteArray(this->projectSettingsEditorState));
|
||||
map.insert("custom_scripts_editor_geometry", stringFromByteArray(this->customScriptsEditorGeometry));
|
||||
map.insert("custom_scripts_editor_state", stringFromByteArray(this->customScriptsEditorState));
|
||||
map.insert("collision_opacity", QString("%1").arg(this->collisionOpacity));
|
||||
map.insert("metatiles_zoom", QString("%1").arg(this->metatilesZoom));
|
||||
map.insert("show_player_view", this->showPlayerView ? "1" : "0");
|
||||
|
@ -362,6 +382,18 @@ void PorymapConfig::setRegionMapEditorGeometry(QByteArray regionMapEditorGeometr
|
|||
this->save();
|
||||
}
|
||||
|
||||
void PorymapConfig::setProjectSettingsEditorGeometry(QByteArray projectSettingsEditorGeometry_, QByteArray projectSettingsEditorState_) {
|
||||
this->projectSettingsEditorGeometry = projectSettingsEditorGeometry_;
|
||||
this->projectSettingsEditorState = projectSettingsEditorState_;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void PorymapConfig::setCustomScriptsEditorGeometry(QByteArray customScriptsEditorGeometry_, QByteArray customScriptsEditorState_) {
|
||||
this->customScriptsEditorGeometry = customScriptsEditorGeometry_;
|
||||
this->customScriptsEditorState = customScriptsEditorState_;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void PorymapConfig::setCollisionOpacity(int opacity) {
|
||||
this->collisionOpacity = opacity;
|
||||
// don't auto-save here because this can be called very frequently.
|
||||
|
@ -465,6 +497,24 @@ QMap<QString, QByteArray> PorymapConfig::getRegionMapEditorGeometry() {
|
|||
return geometry;
|
||||
}
|
||||
|
||||
QMap<QString, QByteArray> PorymapConfig::getProjectSettingsEditorGeometry() {
|
||||
QMap<QString, QByteArray> geometry;
|
||||
|
||||
geometry.insert("project_settings_editor_geometry", this->projectSettingsEditorGeometry);
|
||||
geometry.insert("project_settings_editor_state", this->projectSettingsEditorState);
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
QMap<QString, QByteArray> PorymapConfig::getCustomScriptsEditorGeometry() {
|
||||
QMap<QString, QByteArray> geometry;
|
||||
|
||||
geometry.insert("custom_scripts_editor_geometry", this->customScriptsEditorGeometry);
|
||||
geometry.insert("custom_scripts_editor_state", this->customScriptsEditorState);
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
int PorymapConfig::getCollisionOpacity() {
|
||||
return this->collisionOpacity;
|
||||
}
|
||||
|
@ -513,18 +563,34 @@ int PorymapConfig::getPaletteEditorBitDepth() {
|
|||
return this->paletteEditorBitDepth;
|
||||
}
|
||||
|
||||
const QStringList ProjectConfig::versionStrings = {
|
||||
"pokeruby",
|
||||
"pokefirered",
|
||||
"pokeemerald",
|
||||
};
|
||||
|
||||
const QMap<BaseGameVersion, QString> baseGameVersionMap = {
|
||||
{BaseGameVersion::pokeruby, "pokeruby"},
|
||||
{BaseGameVersion::pokefirered, "pokefirered"},
|
||||
{BaseGameVersion::pokeemerald, "pokeemerald"},
|
||||
{BaseGameVersion::pokeruby, ProjectConfig::versionStrings[0]},
|
||||
{BaseGameVersion::pokefirered, ProjectConfig::versionStrings[1]},
|
||||
{BaseGameVersion::pokeemerald, ProjectConfig::versionStrings[2]},
|
||||
};
|
||||
|
||||
const QMap<QString, BaseGameVersion> baseGameVersionReverseMap = {
|
||||
{"pokeruby", BaseGameVersion::pokeruby},
|
||||
{"pokefirered", BaseGameVersion::pokefirered},
|
||||
{"pokeemerald", BaseGameVersion::pokeemerald},
|
||||
{ProjectConfig::versionStrings[0], BaseGameVersion::pokeruby},
|
||||
{ProjectConfig::versionStrings[1], BaseGameVersion::pokefirered},
|
||||
{ProjectConfig::versionStrings[2], BaseGameVersion::pokeemerald},
|
||||
};
|
||||
|
||||
BaseGameVersion ProjectConfig::stringToBaseGameVersion(QString string, bool * ok) {
|
||||
if (baseGameVersionReverseMap.contains(string)) {
|
||||
if (ok) *ok = true;
|
||||
return baseGameVersionReverseMap.value(string);
|
||||
} else {
|
||||
if (ok) *ok = false;
|
||||
return BaseGameVersion::pokeemerald;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectConfig projectConfig;
|
||||
|
||||
QString ProjectConfig::getConfigFilepath() {
|
||||
|
@ -534,13 +600,10 @@ QString ProjectConfig::getConfigFilepath() {
|
|||
|
||||
void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
if (key == "base_game_version") {
|
||||
QString baseGameVersion = value.toLower();
|
||||
if (baseGameVersionReverseMap.contains(baseGameVersion)) {
|
||||
this->baseGameVersion = baseGameVersionReverseMap.value(baseGameVersion);
|
||||
} else {
|
||||
this->baseGameVersion = BaseGameVersion::pokeemerald;
|
||||
bool ok;
|
||||
this->baseGameVersion = this->stringToBaseGameVersion(value.toLower(), &ok);
|
||||
if (!ok)
|
||||
logWarn(QString("Invalid config value for base_game_version: '%1'. Must be 'pokeruby', 'pokefirered' or 'pokeemerald'.").arg(value));
|
||||
}
|
||||
} else if (key == "use_poryscript") {
|
||||
this->usePoryScript = getConfigBool(key, value);
|
||||
} else if (key == "use_custom_border_size") {
|
||||
|
@ -565,23 +628,18 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
} else if (key == "enable_triple_layer_metatiles") {
|
||||
this->enableTripleLayerMetatiles = getConfigBool(key, value);
|
||||
} else if (key == "new_map_metatile") {
|
||||
this->newMapMetatileId = getConfigInteger(key, value, 0, 1023, 0);
|
||||
this->newMapMetatileId = getConfigUint32(key, value, 0, 1023, 0);
|
||||
} else if (key == "new_map_elevation") {
|
||||
this->newMapElevation = getConfigInteger(key, value, 0, 15, 3);
|
||||
} else if (key == "new_map_border_metatiles") {
|
||||
this->newMapBorderMetatileIds.clear();
|
||||
QList<QString> metatileIds = value.split(",");
|
||||
const int maxSize = DEFAULT_BORDER_WIDTH * DEFAULT_BORDER_HEIGHT;
|
||||
const int size = qMin(metatileIds.size(), maxSize);
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
int metatileId = getConfigInteger(key, metatileIds.at(i), 0, 1023, 0);
|
||||
for (int i = 0; i < metatileIds.size(); i++) {
|
||||
// TODO: The max of 1023 here should eventually reflect Project::num_metatiles_total-1,
|
||||
// but the config is parsed well before that constant is.
|
||||
int metatileId = getConfigUint32(key, metatileIds.at(i), 0, 1023, 0);
|
||||
this->newMapBorderMetatileIds.append(metatileId);
|
||||
}
|
||||
for (; i < maxSize; i++) {
|
||||
// Set any metatiles not provided to 0
|
||||
this->newMapBorderMetatileIds.append(0);
|
||||
}
|
||||
} else if (key == "default_primary_tileset") {
|
||||
this->defaultPrimaryTileset = value;
|
||||
} else if (key == "default_secondary_tileset") {
|
||||
|
@ -609,14 +667,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
} else if (key == "use_encounter_json") {
|
||||
userConfig.useEncounterJson = getConfigBool(key, value);
|
||||
} else if (key == "custom_scripts") {
|
||||
userConfig.customScripts.clear();
|
||||
QList<QString> paths = value.split(",");
|
||||
paths.removeDuplicates();
|
||||
for (QString script : paths) {
|
||||
if (!script.isEmpty()) {
|
||||
userConfig.customScripts.append(script);
|
||||
}
|
||||
}
|
||||
userConfig.parseCustomScripts(value);
|
||||
#endif
|
||||
} else if (key.startsWith("path/")) {
|
||||
auto k = reverseDefaultPaths(key.mid(5));
|
||||
|
@ -639,6 +690,13 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
readKeys.append(key);
|
||||
}
|
||||
|
||||
// Restore config to version-specific defaults
|
||||
void::ProjectConfig::reset(BaseGameVersion baseGameVersion) {
|
||||
this->reset();
|
||||
this->baseGameVersion = baseGameVersion;
|
||||
this->setUnreadKeys();
|
||||
}
|
||||
|
||||
void ProjectConfig::setUnreadKeys() {
|
||||
// Set game-version specific defaults for any config field that wasn't read
|
||||
bool isPokefirered = this->baseGameVersion == BaseGameVersion::pokefirered;
|
||||
|
@ -657,7 +715,7 @@ void ProjectConfig::setUnreadKeys() {
|
|||
if (!readKeys.contains("metatile_behavior_mask")) this->metatileBehaviorMask = Metatile::getBehaviorMask(this->baseGameVersion);
|
||||
if (!readKeys.contains("metatile_terrain_type_mask")) this->metatileTerrainTypeMask = Metatile::getTerrainTypeMask(this->baseGameVersion);
|
||||
if (!readKeys.contains("metatile_encounter_type_mask")) this->metatileEncounterTypeMask = Metatile::getEncounterTypeMask(this->baseGameVersion);
|
||||
if (!readKeys.contains("metatile_layer_type_mask")) this-> metatileLayerTypeMask = Metatile::getLayerTypeMask(this->baseGameVersion);
|
||||
if (!readKeys.contains("metatile_layer_type_mask")) this->metatileLayerTypeMask = Metatile::getLayerTypeMask(this->baseGameVersion);
|
||||
if (!readKeys.contains("enable_map_allow_flags")) this->enableMapAllowFlags = (this->baseGameVersion != BaseGameVersion::pokeruby);
|
||||
}
|
||||
|
||||
|
@ -675,12 +733,9 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
|
|||
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));
|
||||
map.insert("new_map_metatile", QString::number(this->newMapMetatileId));
|
||||
map.insert("new_map_metatile", Metatile::getMetatileIdString(this->newMapMetatileId));
|
||||
map.insert("new_map_elevation", QString::number(this->newMapElevation));
|
||||
QStringList metatiles;
|
||||
for (auto metatile : this->newMapBorderMetatileIds)
|
||||
metatiles << QString::number(metatile);
|
||||
map.insert("new_map_border_metatiles", metatiles.join(","));
|
||||
map.insert("new_map_border_metatiles", Metatile::getMetatileIdStringList(this->newMapBorderMetatileIds));
|
||||
map.insert("default_primary_tileset", this->defaultPrimaryTileset);
|
||||
map.insert("default_secondary_tileset", this->defaultSecondaryTileset);
|
||||
map.insert("prefabs_filepath", this->prefabFilepath);
|
||||
|
@ -738,17 +793,40 @@ QString ProjectConfig::getProjectDir() {
|
|||
|
||||
void ProjectConfig::setFilePath(ProjectFilePath pathId, QString path) {
|
||||
if (!defaultPaths.contains(pathId)) return;
|
||||
this->filePaths[pathId] = path;
|
||||
if (path.isEmpty()) {
|
||||
this->filePaths.remove(pathId);
|
||||
} else {
|
||||
this->filePaths[pathId] = path;
|
||||
}
|
||||
}
|
||||
|
||||
QString ProjectConfig::getFilePath(ProjectFilePath pathId) {
|
||||
if (this->filePaths.contains(pathId)) {
|
||||
return this->filePaths[pathId];
|
||||
} else if (defaultPaths.contains(pathId)) {
|
||||
return defaultPaths[pathId].second;
|
||||
} else {
|
||||
return QString();
|
||||
void ProjectConfig::setFilePath(QString defaultPath, QString newPath) {
|
||||
this->setFilePath(reverseDefaultPaths(defaultPath), newPath);
|
||||
}
|
||||
|
||||
QString ProjectConfig::getFilePath(ProjectFilePath pathId, bool customOnly) {
|
||||
const QString customPath = this->filePaths.value(pathId);
|
||||
|
||||
// When reading custom filepaths for the settings editor we don't care
|
||||
// about the default path or whether the custom path exists.
|
||||
if (customOnly)
|
||||
return customPath;
|
||||
|
||||
if (!customPath.isEmpty()) {
|
||||
// A custom filepath has been specified. If the file/folder exists, use that.
|
||||
const QString absCustomPath = this->projectDir + QDir::separator() + customPath;
|
||||
if (QFileInfo::exists(absCustomPath)) {
|
||||
return customPath;
|
||||
} else {
|
||||
logError(QString("Custom project filepath '%1' not found. Using default.").arg(absCustomPath));
|
||||
}
|
||||
}
|
||||
return defaultPaths.contains(pathId) ? defaultPaths[pathId].second : QString();
|
||||
|
||||
}
|
||||
|
||||
QString ProjectConfig::getFilePath(QString defaultPath, bool customOnly) {
|
||||
return this->getFilePath(reverseDefaultPaths(defaultPath), customOnly);
|
||||
}
|
||||
|
||||
void ProjectConfig::setBaseGameVersion(BaseGameVersion baseGameVersion) {
|
||||
|
@ -760,8 +838,15 @@ BaseGameVersion ProjectConfig::getBaseGameVersion() {
|
|||
return this->baseGameVersion;
|
||||
}
|
||||
|
||||
QString ProjectConfig::getBaseGameVersionString(BaseGameVersion version) {
|
||||
if (!baseGameVersionMap.contains(version)) {
|
||||
version = BaseGameVersion::pokeemerald;
|
||||
}
|
||||
return baseGameVersionMap.value(version);
|
||||
}
|
||||
|
||||
QString ProjectConfig::getBaseGameVersionString() {
|
||||
return baseGameVersionMap.value(this->baseGameVersion);
|
||||
return this->getBaseGameVersionString(this->baseGameVersion);
|
||||
}
|
||||
|
||||
void ProjectConfig::setUsePoryScript(bool usePoryScript) {
|
||||
|
@ -871,12 +956,12 @@ int ProjectConfig::getNumTilesInMetatile() {
|
|||
return this->enableTripleLayerMetatiles ? 12 : 8;
|
||||
}
|
||||
|
||||
void ProjectConfig::setNewMapMetatileId(int metatileId) {
|
||||
void ProjectConfig::setNewMapMetatileId(uint16_t metatileId) {
|
||||
this->newMapMetatileId = metatileId;
|
||||
this->save();
|
||||
}
|
||||
|
||||
int ProjectConfig::getNewMapMetatileId() {
|
||||
uint16_t ProjectConfig::getNewMapMetatileId() {
|
||||
return this->newMapMetatileId;
|
||||
}
|
||||
|
||||
|
@ -889,12 +974,12 @@ int ProjectConfig::getNewMapElevation() {
|
|||
return this->newMapElevation;
|
||||
}
|
||||
|
||||
void ProjectConfig::setNewMapBorderMetatileIds(QList<int> metatileIds) {
|
||||
void ProjectConfig::setNewMapBorderMetatileIds(QList<uint16_t> metatileIds) {
|
||||
this->newMapBorderMetatileIds = metatileIds;
|
||||
this->save();
|
||||
}
|
||||
|
||||
QList<int> ProjectConfig::getNewMapBorderMetatileIds() {
|
||||
QList<uint16_t> ProjectConfig::getNewMapBorderMetatileIds() {
|
||||
return this->newMapBorderMetatileIds;
|
||||
}
|
||||
|
||||
|
@ -906,15 +991,22 @@ QString ProjectConfig::getDefaultSecondaryTileset() {
|
|||
return this->defaultSecondaryTileset;
|
||||
}
|
||||
|
||||
void ProjectConfig::setDefaultPrimaryTileset(QString tilesetName) {
|
||||
this->defaultPrimaryTileset = tilesetName;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setDefaultSecondaryTileset(QString tilesetName) {
|
||||
this->defaultSecondaryTileset = tilesetName;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setPrefabFilepath(QString filepath) {
|
||||
this->prefabFilepath = filepath;
|
||||
this->save();
|
||||
}
|
||||
|
||||
QString ProjectConfig::getPrefabFilepath(bool setIfEmpty) {
|
||||
if (setIfEmpty && this->prefabFilepath.isEmpty()) {
|
||||
this->setPrefabFilepath("prefabs.json");
|
||||
}
|
||||
QString ProjectConfig::getPrefabFilepath() {
|
||||
return this->prefabFilepath;
|
||||
}
|
||||
|
||||
|
@ -949,6 +1041,11 @@ int ProjectConfig::getMetatileAttributesSize() {
|
|||
return this->metatileAttributesSize;
|
||||
}
|
||||
|
||||
void ProjectConfig::setMetatileAttributesSize(int size) {
|
||||
this->metatileAttributesSize = size;
|
||||
this->save();
|
||||
}
|
||||
|
||||
uint32_t ProjectConfig::getMetatileBehaviorMask() {
|
||||
return this->metatileBehaviorMask;
|
||||
}
|
||||
|
@ -965,10 +1062,35 @@ uint32_t ProjectConfig::getMetatileLayerTypeMask() {
|
|||
return this->metatileLayerTypeMask;
|
||||
}
|
||||
|
||||
void ProjectConfig::setMetatileBehaviorMask(uint32_t mask) {
|
||||
this->metatileBehaviorMask = mask;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setMetatileTerrainTypeMask(uint32_t mask) {
|
||||
this->metatileTerrainTypeMask = mask;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setMetatileEncounterTypeMask(uint32_t mask) {
|
||||
this->metatileEncounterTypeMask = mask;
|
||||
this->save();
|
||||
}
|
||||
|
||||
void ProjectConfig::setMetatileLayerTypeMask(uint32_t mask) {
|
||||
this->metatileLayerTypeMask = mask;
|
||||
this->save();
|
||||
}
|
||||
|
||||
bool ProjectConfig::getMapAllowFlagsEnabled() {
|
||||
return this->enableMapAllowFlags;
|
||||
}
|
||||
|
||||
void ProjectConfig::setMapAllowFlagsEnabled(bool enabled) {
|
||||
this->enableMapAllowFlags = enabled;
|
||||
this->save();
|
||||
}
|
||||
|
||||
|
||||
UserConfig userConfig;
|
||||
|
||||
|
@ -983,14 +1105,7 @@ void UserConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
} else if (key == "use_encounter_json") {
|
||||
this->useEncounterJson = getConfigBool(key, value);
|
||||
} else if (key == "custom_scripts") {
|
||||
this->customScripts.clear();
|
||||
QList<QString> paths = value.split(",");
|
||||
paths.removeDuplicates();
|
||||
for (QString script : paths) {
|
||||
if (!script.isEmpty()) {
|
||||
this->customScripts.append(script);
|
||||
}
|
||||
}
|
||||
this->parseCustomScripts(value);
|
||||
} else {
|
||||
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
|
||||
}
|
||||
|
@ -1004,7 +1119,7 @@ QMap<QString, QString> UserConfig::getKeyValueMap() {
|
|||
QMap<QString, QString> map;
|
||||
map.insert("recent_map", this->recentMap);
|
||||
map.insert("use_encounter_json", QString::number(this->useEncounterJson));
|
||||
map.insert("custom_scripts", this->customScripts.join(","));
|
||||
map.insert("custom_scripts", this->outputCustomScripts());
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -1040,13 +1155,51 @@ bool UserConfig::getEncounterJsonActive() {
|
|||
return this->useEncounterJson;
|
||||
}
|
||||
|
||||
void UserConfig::setCustomScripts(QList<QString> scripts) {
|
||||
this->customScripts = scripts;
|
||||
// Read input from the config to get the script paths and whether each is enabled or disbled.
|
||||
// The format is a comma-separated list of paths. Each path can be followed (before the comma)
|
||||
// by a :0 or :1 to indicate whether it should be disabled or enabled, respectively. If neither
|
||||
// follow, it's assumed the script should be enabled.
|
||||
void UserConfig::parseCustomScripts(QString input) {
|
||||
this->customScripts.clear();
|
||||
QList<QString> paths = input.split(",", Qt::SkipEmptyParts);
|
||||
for (QString path : paths) {
|
||||
// Read and remove suffix
|
||||
bool enabled = !path.endsWith(":0");
|
||||
if (!enabled || path.endsWith(":1"))
|
||||
path.chop(2);
|
||||
|
||||
if (!path.isEmpty()) {
|
||||
// If a path is repeated only its last instance will be considered.
|
||||
this->customScripts.insert(path, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inverse of UserConfig::parseCustomScripts
|
||||
QString UserConfig::outputCustomScripts() {
|
||||
QStringList list;
|
||||
QMapIterator<QString, bool> i(this->customScripts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
list.append(QString("%1:%2").arg(i.key()).arg(i.value() ? "1" : "0"));
|
||||
}
|
||||
return list.join(",");
|
||||
}
|
||||
|
||||
void UserConfig::setCustomScripts(QStringList scripts, QList<bool> enabled) {
|
||||
this->customScripts.clear();
|
||||
size_t size = qMin(scripts.length(), enabled.length());
|
||||
for (size_t i = 0; i < size; i++)
|
||||
this->customScripts.insert(scripts.at(i), enabled.at(i));
|
||||
this->save();
|
||||
}
|
||||
|
||||
QList<QString> UserConfig::getCustomScripts() {
|
||||
return this->customScripts;
|
||||
QStringList UserConfig::getCustomScriptPaths() {
|
||||
return this->customScripts.keys();
|
||||
}
|
||||
|
||||
QList<bool> UserConfig::getCustomScriptsEnabled() {
|
||||
return this->customScripts.values();
|
||||
}
|
||||
|
||||
ShortcutsConfig shortcutsConfig;
|
||||
|
|
|
@ -933,8 +933,7 @@ void Editor::onHoveredMovementPermissionCleared() {
|
|||
QString Editor::getMetatileDisplayMessage(uint16_t metatileId) {
|
||||
Metatile *metatile = Tileset::getMetatile(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary);
|
||||
QString label = Tileset::getMetatileLabel(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary);
|
||||
QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper();
|
||||
QString message = QString("Metatile: 0x%1").arg(hexString);
|
||||
QString message = QString("Metatile: %1").arg(Metatile::getMetatileIdString(metatileId));
|
||||
if (label.size())
|
||||
message += QString(" \"%1\"").arg(label);
|
||||
if (metatile && metatile->behavior) // Skip MB_NORMAL
|
||||
|
@ -2147,7 +2146,7 @@ void Editor::openScript(const QString &scriptLabel) const {
|
|||
openInTextEditor(scriptPath, lineNum);
|
||||
}
|
||||
|
||||
void Editor::openInTextEditor(const QString &path, int lineNum) const {
|
||||
void Editor::openInTextEditor(const QString &path, int lineNum) {
|
||||
QString command = porymapConfig.getTextEditorGotoLine();
|
||||
if (command.isEmpty()) {
|
||||
// Open map scripts in the system's default editor.
|
||||
|
@ -2160,7 +2159,7 @@ void Editor::openInTextEditor(const QString &path, int lineNum) const {
|
|||
} else {
|
||||
command += " \"" + path + '\"';
|
||||
}
|
||||
startDetachedProcess(command);
|
||||
Editor::startDetachedProcess(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2173,7 +2172,7 @@ void Editor::openProjectInTextEditor() const {
|
|||
startDetachedProcess(command);
|
||||
}
|
||||
|
||||
bool Editor::startDetachedProcess(const QString &command, const QString &workingDirectory, qint64 *pid) const {
|
||||
bool Editor::startDetachedProcess(const QString &command, const QString &workingDirectory, qint64 *pid) {
|
||||
logInfo("Executing command: " + command);
|
||||
QProcess process;
|
||||
#ifdef Q_OS_WIN
|
||||
|
|
|
@ -369,13 +369,11 @@ void MainWindow::markMapEdited() {
|
|||
}
|
||||
|
||||
void MainWindow::setWildEncountersUIEnabled(bool enabled) {
|
||||
ui->actionUse_Encounter_Json->setChecked(enabled);
|
||||
ui->mainTabBar->setTabEnabled(4, enabled);
|
||||
}
|
||||
|
||||
void MainWindow::setProjectSpecificUIVisibility()
|
||||
{
|
||||
ui->actionUse_Poryscript->setChecked(projectConfig.getUsePoryScript());
|
||||
this->setWildEncountersUIEnabled(userConfig.getEncounterJsonActive());
|
||||
|
||||
bool hasFlags = projectConfig.getMapAllowFlagsEnabled();
|
||||
|
@ -454,8 +452,6 @@ void MainWindow::loadUserSettings() {
|
|||
ui->horizontalSlider_MetatileZoom->blockSignals(true);
|
||||
ui->horizontalSlider_MetatileZoom->setValue(porymapConfig.getMetatilesZoom());
|
||||
ui->horizontalSlider_MetatileZoom->blockSignals(false);
|
||||
ui->actionMonitor_Project_Files->setChecked(porymapConfig.getMonitorFiles());
|
||||
ui->actionOpen_Recent_Project_On_Launch->setChecked(porymapConfig.getReopenOnLaunch());
|
||||
setTheme(porymapConfig.getTheme());
|
||||
}
|
||||
|
||||
|
@ -526,8 +522,11 @@ bool MainWindow::openProject(QString dir) {
|
|||
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());
|
||||
QObject::connect(editor->project, &Project::uncheckMonitorFilesAction, [this]() {
|
||||
porymapConfig.setMonitorFiles(false);
|
||||
if (this->preferenceEditor)
|
||||
this->preferenceEditor->updateFields();
|
||||
});
|
||||
editor->project->set_root(dir);
|
||||
success = loadDataStructures()
|
||||
&& populateMapList()
|
||||
|
@ -592,6 +591,19 @@ QString MainWindow::getDefaultMap() {
|
|||
return QString();
|
||||
}
|
||||
|
||||
void MainWindow::openSubWindow(QWidget * window) {
|
||||
if (!window) return;
|
||||
|
||||
if (!window->isVisible()) {
|
||||
window->show();
|
||||
} else if (window->isMinimized()) {
|
||||
window->showNormal();
|
||||
} else {
|
||||
window->raise();
|
||||
window->activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
QString MainWindow::getExistingDirectory(QString dir) {
|
||||
return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly);
|
||||
}
|
||||
|
@ -1186,12 +1198,9 @@ void MainWindow::openNewMapPopupWindow() {
|
|||
if (!this->newMapPrompt) {
|
||||
this->newMapPrompt = new NewMapPopup(this, this->editor->project);
|
||||
}
|
||||
if (!this->newMapPrompt->isVisible()) {
|
||||
this->newMapPrompt->show();
|
||||
} else {
|
||||
this->newMapPrompt->raise();
|
||||
this->newMapPrompt->activateWindow();
|
||||
}
|
||||
|
||||
openSubWindow(this->newMapPrompt);
|
||||
|
||||
connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated);
|
||||
this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
@ -1680,7 +1689,12 @@ void MainWindow::on_mapViewTab_tabBarClicked(int index)
|
|||
editor->setEditingCollision();
|
||||
} else if (index == 2) {
|
||||
editor->setEditingMap();
|
||||
prefab.tryImportDefaultPrefabs(this->editor->map);
|
||||
if (projectConfig.getPrefabFilepath().isEmpty() && !projectConfig.getPrefabImportPrompted()) {
|
||||
// User hasn't set up prefabs and hasn't been prompted before.
|
||||
// Ask if they'd like to import the default prefabs file.
|
||||
if (prefab.tryImportDefaultPrefabs(this, projectConfig.getBaseGameVersion()))
|
||||
prefab.updatePrefabUi(this->editor->map);
|
||||
}
|
||||
}
|
||||
editor->setCursorRectVisible(false);
|
||||
}
|
||||
|
@ -1757,43 +1771,12 @@ void MainWindow::on_actionCursor_Tile_Outline_triggered()
|
|||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionUse_Encounter_Json_triggered(bool checked)
|
||||
{
|
||||
QMessageBox warning(this);
|
||||
warning.setText("You must reload the project for this setting to take effect.");
|
||||
warning.setIcon(QMessageBox::Information);
|
||||
warning.exec();
|
||||
userConfig.setEncounterJsonActive(checked);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionMonitor_Project_Files_triggered(bool checked)
|
||||
{
|
||||
porymapConfig.setMonitorFiles(checked);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionUse_Poryscript_triggered(bool checked)
|
||||
{
|
||||
projectConfig.setUsePoryScript(checked);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionOpen_Recent_Project_On_Launch_triggered(bool checked)
|
||||
{
|
||||
porymapConfig.setReopenOnLaunch(checked);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionEdit_Shortcuts_triggered()
|
||||
void MainWindow::on_actionShortcuts_triggered()
|
||||
{
|
||||
if (!shortcutsEditor)
|
||||
initShortcutsEditor();
|
||||
|
||||
if (shortcutsEditor->isHidden()) {
|
||||
shortcutsEditor->show();
|
||||
} else if (shortcutsEditor->isMinimized()) {
|
||||
shortcutsEditor->showNormal();
|
||||
} else {
|
||||
shortcutsEditor->raise();
|
||||
shortcutsEditor->activateWindow();
|
||||
}
|
||||
openSubWindow(shortcutsEditor);
|
||||
}
|
||||
|
||||
void MainWindow::initShortcutsEditor() {
|
||||
|
@ -1819,6 +1802,11 @@ void MainWindow::connectSubEditorsToShortcutsEditor() {
|
|||
if (regionMapEditor)
|
||||
connect(shortcutsEditor, &ShortcutsEditor::shortcutsSaved,
|
||||
regionMapEditor, &RegionMapEditor::applyUserShortcuts);
|
||||
|
||||
if (!customScriptsEditor)
|
||||
initCustomScriptsEditor();
|
||||
connect(shortcutsEditor, &ShortcutsEditor::shortcutsSaved,
|
||||
customScriptsEditor, &CustomScriptsEditor::applyUserShortcuts);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionPencil_triggered()
|
||||
|
@ -2472,13 +2460,7 @@ void MainWindow::showExportMapImageWindow(ImageExporterMode mode) {
|
|||
this->mapImageExporter = new MapImageExporter(this, this->editor, mode);
|
||||
this->mapImageExporter->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (!this->mapImageExporter->isVisible()) {
|
||||
this->mapImageExporter->show();
|
||||
} else if (this->mapImageExporter->isMinimized()) {
|
||||
this->mapImageExporter->showNormal();
|
||||
} else {
|
||||
this->mapImageExporter->activateWindow();
|
||||
}
|
||||
openSubWindow(this->mapImageExporter);
|
||||
}
|
||||
|
||||
void MainWindow::on_comboBox_ConnectionDirection_currentTextChanged(const QString &direction)
|
||||
|
@ -2674,14 +2656,7 @@ void MainWindow::on_actionTileset_Editor_triggered()
|
|||
initTilesetEditor();
|
||||
}
|
||||
|
||||
if (!this->tilesetEditor->isVisible()) {
|
||||
this->tilesetEditor->show();
|
||||
} else if (this->tilesetEditor->isMinimized()) {
|
||||
this->tilesetEditor->showNormal();
|
||||
} else {
|
||||
this->tilesetEditor->raise();
|
||||
this->tilesetEditor->activateWindow();
|
||||
}
|
||||
openSubWindow(this->tilesetEditor);
|
||||
|
||||
MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection();
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
|
@ -2723,7 +2698,7 @@ void MainWindow::on_actionOpen_Config_Folder_triggered() {
|
|||
QDesktopServices::openUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionEdit_Preferences_triggered() {
|
||||
void MainWindow::on_actionPreferences_triggered() {
|
||||
if (!preferenceEditor) {
|
||||
preferenceEditor = new PreferenceEditor(this);
|
||||
connect(preferenceEditor, &PreferenceEditor::themeChanged,
|
||||
|
@ -2734,14 +2709,7 @@ void MainWindow::on_actionEdit_Preferences_triggered() {
|
|||
this, &MainWindow::togglePreferenceSpecificUi);
|
||||
}
|
||||
|
||||
if (!preferenceEditor->isVisible()) {
|
||||
preferenceEditor->show();
|
||||
} else if (preferenceEditor->isMinimized()) {
|
||||
preferenceEditor->showNormal();
|
||||
} else {
|
||||
preferenceEditor->raise();
|
||||
preferenceEditor->activateWindow();
|
||||
}
|
||||
openSubWindow(preferenceEditor);
|
||||
}
|
||||
|
||||
void MainWindow::togglePreferenceSpecificUi() {
|
||||
|
@ -2751,6 +2719,39 @@ void MainWindow::togglePreferenceSpecificUi() {
|
|||
ui->actionOpen_Project_in_Text_Editor->setEnabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionProject_Settings_triggered() {
|
||||
if (!this->projectSettingsEditor) {
|
||||
this->projectSettingsEditor = new ProjectSettingsEditor(this, this->editor->project);
|
||||
connect(this->projectSettingsEditor, &ProjectSettingsEditor::reloadProject,
|
||||
this, &MainWindow::on_action_Reload_Project_triggered);
|
||||
}
|
||||
|
||||
openSubWindow(this->projectSettingsEditor);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionCustom_Scripts_triggered() {
|
||||
if (!this->customScriptsEditor)
|
||||
initCustomScriptsEditor();
|
||||
|
||||
openSubWindow(this->customScriptsEditor);
|
||||
}
|
||||
|
||||
void MainWindow::initCustomScriptsEditor() {
|
||||
this->customScriptsEditor = new CustomScriptsEditor(this);
|
||||
connect(this->customScriptsEditor, &CustomScriptsEditor::reloadScriptEngine,
|
||||
this, &MainWindow::reloadScriptEngine);
|
||||
}
|
||||
|
||||
void MainWindow::reloadScriptEngine() {
|
||||
Scripting::init(this);
|
||||
this->ui->graphicsView_Map->clearOverlayMap();
|
||||
Scripting::populateGlobalObject(this);
|
||||
// Lying to the scripts here, simulating a project reload
|
||||
Scripting::cb_ProjectOpened(projectConfig.getProjectDir());
|
||||
if (editor && editor->map)
|
||||
Scripting::cb_MapOpened(editor->map->name);
|
||||
}
|
||||
|
||||
void MainWindow::on_pushButton_AddCustomHeaderField_clicked()
|
||||
{
|
||||
bool ok;
|
||||
|
@ -2800,14 +2801,7 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!this->regionMapEditor->isVisible()) {
|
||||
this->regionMapEditor->show();
|
||||
} else if (this->regionMapEditor->isMinimized()) {
|
||||
this->regionMapEditor->showNormal();
|
||||
} else {
|
||||
this->regionMapEditor->raise();
|
||||
this->regionMapEditor->activateWindow();
|
||||
}
|
||||
openSubWindow(this->regionMapEditor);
|
||||
}
|
||||
|
||||
void MainWindow::on_pushButton_CreatePrefab_clicked() {
|
||||
|
|
|
@ -444,10 +444,6 @@ bool Project::readMapLayouts() {
|
|||
"blockdata_filepath",
|
||||
};
|
||||
bool useCustomBorderSize = projectConfig.getUseCustomBorderSize();
|
||||
if (useCustomBorderSize) {
|
||||
requiredFields.append("border_width");
|
||||
requiredFields.append("border_height");
|
||||
}
|
||||
for (int i = 0; i < layouts.size(); i++) {
|
||||
QJsonObject layoutObj = layouts[i].toObject();
|
||||
if (layoutObj.isEmpty())
|
||||
|
@ -957,9 +953,9 @@ void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *second
|
|||
}
|
||||
for (QString defineName : definesOut.keys()) {
|
||||
int value = defines[defineName];
|
||||
QString line = QString("#define %1 0x%2\n")
|
||||
QString line = QString("#define %1 %2\n")
|
||||
.arg(defineName, -1 * longestLength)
|
||||
.arg(QString("%1").arg(value, 3, 16, QChar('0')).toUpper());
|
||||
.arg(Metatile::getMetatileIdString(value));
|
||||
outputText += line;
|
||||
}
|
||||
i += j;
|
||||
|
@ -1142,16 +1138,21 @@ void Project::setNewMapBorder(Map *map) {
|
|||
map->layout->border.clear();
|
||||
int width = map->getBorderWidth();
|
||||
int height = map->getBorderHeight();
|
||||
if (width != DEFAULT_BORDER_WIDTH || height != DEFAULT_BORDER_HEIGHT) {
|
||||
|
||||
const QList<uint16_t> configMetatileIds = projectConfig.getNewMapBorderMetatileIds();
|
||||
if (configMetatileIds.length() != width * height) {
|
||||
// Border size doesn't match the number of default border metatiles.
|
||||
// Fill the border with empty metatiles.
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
map->layout->border.append(0);
|
||||
}
|
||||
} else {
|
||||
QList<int> metatileIds = projectConfig.getNewMapBorderMetatileIds();
|
||||
for (int i = 0; i < DEFAULT_BORDER_WIDTH * DEFAULT_BORDER_HEIGHT; i++) {
|
||||
map->layout->border.append(qint16(metatileIds.at(i)));
|
||||
// Fill the border with the default metatiles from the config.
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
map->layout->border.append(configMetatileIds.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
map->layout->lastCommitBlocks.border = map->layout->border;
|
||||
map->layout->lastCommitBlocks.borderDimensions = QSize(width, height);
|
||||
}
|
||||
|
@ -2174,8 +2175,8 @@ bool Project::readCoordEventWeatherNames() {
|
|||
fileWatcher.addPath(root + "/" + filename);
|
||||
coordEventWeatherNames = parser.readCDefinesSorted(filename, prefixes);
|
||||
if (coordEventWeatherNames.isEmpty()) {
|
||||
logError(QString("Failed to read coord event weather constants from %1").arg(filename));
|
||||
return false;
|
||||
logWarn(QString("Failed to read coord event weather constants from %1. Disabling Weather Trigger events.").arg(filename));
|
||||
projectConfig.setEventWeatherTriggerEnabled(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2189,8 +2190,8 @@ bool Project::readSecretBaseIds() {
|
|||
fileWatcher.addPath(root + "/" + filename);
|
||||
secretBaseIds = parser.readCDefinesSorted(filename, prefixes);
|
||||
if (secretBaseIds.isEmpty()) {
|
||||
logError(QString("Failed to read secret base id constants from %1").arg(filename));
|
||||
return false;
|
||||
logWarn(QString("Failed to read secret base id constants from '%1'. Disabling Secret Base events.").arg(filename));
|
||||
projectConfig.setEventSecretBaseEnabled(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ bool ScriptUtility::getSmartPathsEnabled() {
|
|||
}
|
||||
|
||||
QList<QString> ScriptUtility::getCustomScripts() {
|
||||
return userConfig.getCustomScripts();
|
||||
return userConfig.getCustomScriptPaths();
|
||||
}
|
||||
|
||||
QList<int> ScriptUtility::getMetatileLayerOrder() {
|
||||
|
|
|
@ -36,8 +36,11 @@ Scripting::Scripting(MainWindow *mainWindow) {
|
|||
this->mainWindow = mainWindow;
|
||||
this->engine = new QJSEngine(mainWindow);
|
||||
this->engine->installExtensions(QJSEngine::ConsoleExtension);
|
||||
for (QString script : userConfig.getCustomScripts()) {
|
||||
this->filepaths.append(script);
|
||||
const QStringList paths = userConfig.getCustomScriptPaths();
|
||||
const QList<bool> enabled = userConfig.getCustomScriptsEnabled();
|
||||
for (int i = 0; i < paths.length(); i++) {
|
||||
if (enabled.at(i))
|
||||
this->filepaths.append(paths.at(i));
|
||||
}
|
||||
this->loadModules(this->filepaths);
|
||||
this->scriptUtility = new ScriptUtility(mainWindow);
|
||||
|
|
254
src/ui/customscriptseditor.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
#include "customscriptseditor.h"
|
||||
#include "ui_customscriptseditor.h"
|
||||
#include "ui_customscriptslistitem.h"
|
||||
#include "config.h"
|
||||
#include "editor.h"
|
||||
#include "shortcut.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
|
||||
CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::CustomScriptsEditor),
|
||||
baseDir(userConfig.getProjectDir() + QDir::separator())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
// This property seems to be reset if we don't set it programmatically
|
||||
ui->list->setDragDropMode(QAbstractItemView::NoDragDrop);
|
||||
|
||||
const QStringList paths = userConfig.getCustomScriptPaths();
|
||||
const QList<bool> enabled = userConfig.getCustomScriptsEnabled();
|
||||
for (int i = 0; i < paths.length(); i++)
|
||||
this->displayScript(paths.at(i), enabled.at(i));
|
||||
|
||||
this->importDir = userConfig.getProjectDir();
|
||||
|
||||
connect(ui->button_AddNewScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::addNewScript);
|
||||
connect(ui->button_ReloadScripts, &QAbstractButton::clicked, this, &CustomScriptsEditor::reloadScripts);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &CustomScriptsEditor::dialogButtonClicked);
|
||||
|
||||
this->initShortcuts();
|
||||
this->restoreWindowState();
|
||||
}
|
||||
|
||||
CustomScriptsEditor::~CustomScriptsEditor()
|
||||
{
|
||||
ui->list->clear();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::initShortcuts() {
|
||||
auto *shortcut_remove = new Shortcut({QKeySequence("Del"), QKeySequence("Backspace")}, this, SLOT(removeSelectedScripts()));
|
||||
shortcut_remove->setObjectName("shortcut_remove");
|
||||
shortcut_remove->setWhatsThis("Remove Selected Scripts");
|
||||
|
||||
auto *shortcut_open = new Shortcut(QKeySequence(), this, SLOT(openSelectedScripts()));
|
||||
shortcut_open->setObjectName("shortcut_open");
|
||||
shortcut_open->setWhatsThis("Open Selected Scripts");
|
||||
|
||||
auto *shortcut_addNew = new Shortcut(QKeySequence(), this, SLOT(addNewScript()));
|
||||
shortcut_addNew->setObjectName("shortcut_addNew");
|
||||
shortcut_addNew->setWhatsThis("Add New Script...");
|
||||
|
||||
auto *shortcut_reload = new Shortcut(QKeySequence(), this, SLOT(reloadScripts()));
|
||||
shortcut_reload->setObjectName("shortcut_reload");
|
||||
shortcut_reload->setWhatsThis("Reload Scripts");
|
||||
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
|
||||
applyUserShortcuts();
|
||||
}
|
||||
|
||||
QObjectList CustomScriptsEditor::shortcutableObjects() const {
|
||||
QObjectList shortcutable_objects;
|
||||
|
||||
for (auto *action : findChildren<QAction *>())
|
||||
if (!action->objectName().isEmpty())
|
||||
shortcutable_objects.append(qobject_cast<QObject *>(action));
|
||||
for (auto *shortcut : findChildren<Shortcut *>())
|
||||
if (!shortcut->objectName().isEmpty())
|
||||
shortcutable_objects.append(qobject_cast<QObject *>(shortcut));
|
||||
|
||||
return shortcutable_objects;
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::applyUserShortcuts() {
|
||||
for (auto *action : findChildren<QAction *>())
|
||||
if (!action->objectName().isEmpty())
|
||||
action->setShortcuts(shortcutsConfig.userShortcuts(action));
|
||||
for (auto *shortcut : findChildren<Shortcut *>())
|
||||
if (!shortcut->objectName().isEmpty())
|
||||
shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut));
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::restoreWindowState() {
|
||||
logInfo("Restoring custom scripts editor geometry from previous session.");
|
||||
const QMap<QString, QByteArray> geometry = porymapConfig.getCustomScriptsEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("custom_scripts_editor_geometry"));
|
||||
this->restoreState(geometry.value("custom_scripts_editor_state"));
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::displayScript(const QString &filepath, bool enabled) {
|
||||
auto item = new QListWidgetItem();
|
||||
auto widget = new CustomScriptsListItem();
|
||||
|
||||
widget->ui->checkBox_Enable->setChecked(enabled);
|
||||
widget->ui->lineEdit_filepath->setText(filepath);
|
||||
item->setSizeHint(widget->sizeHint());
|
||||
|
||||
connect(widget->ui->b_Choose, &QAbstractButton::clicked, [this, item](bool) { this->replaceScript(item); });
|
||||
connect(widget->ui->b_Edit, &QAbstractButton::clicked, [this, item](bool) { this->openScript(item); });
|
||||
connect(widget->ui->b_Delete, &QAbstractButton::clicked, [this, item](bool) { this->removeScript(item); });
|
||||
connect(widget->ui->checkBox_Enable, &QCheckBox::stateChanged, this, &CustomScriptsEditor::markEdited);
|
||||
connect(widget->ui->lineEdit_filepath, &QLineEdit::textEdited, this, &CustomScriptsEditor::markEdited);
|
||||
|
||||
// Per the Qt manual, for performance reasons QListWidget::setItemWidget shouldn't be used with non-static items.
|
||||
// There's an assumption here that users won't have enough scripts for that to be a problem.
|
||||
ui->list->addItem(item);
|
||||
ui->list->setItemWidget(item, widget);
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::markEdited() {
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
QString CustomScriptsEditor::getScriptFilepath(QListWidgetItem * item, bool absolutePath) const {
|
||||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
if (!widget) return QString();
|
||||
|
||||
QString path = widget->ui->lineEdit_filepath->text();
|
||||
if (absolutePath) {
|
||||
QFileInfo fileInfo(path);
|
||||
if (fileInfo.isRelative())
|
||||
path.prepend(this->baseDir);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::setScriptFilepath(QListWidgetItem * item, QString filepath) const {
|
||||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
if (!widget) return;
|
||||
|
||||
if (filepath.startsWith(this->baseDir))
|
||||
filepath.remove(0, this->baseDir.length());
|
||||
widget->ui->lineEdit_filepath->setText(filepath);
|
||||
}
|
||||
|
||||
bool CustomScriptsEditor::getScriptEnabled(QListWidgetItem * item) const {
|
||||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
return widget && widget->ui->checkBox_Enable->isChecked();
|
||||
}
|
||||
|
||||
QString CustomScriptsEditor::chooseScript(QString dir) {
|
||||
return QFileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)");
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::addNewScript() {
|
||||
QString filepath = this->chooseScript(this->importDir);
|
||||
if (filepath.isEmpty())
|
||||
return;
|
||||
this->importDir = filepath;
|
||||
if (filepath.startsWith(this->baseDir))
|
||||
filepath.remove(0, this->baseDir.length());
|
||||
this->displayScript(filepath, true);
|
||||
this->markEdited();
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::removeScript(QListWidgetItem * item) {
|
||||
ui->list->takeItem(ui->list->row(item));
|
||||
this->markEdited();
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::removeSelectedScripts() {
|
||||
QList<QListWidgetItem *> items = ui->list->selectedItems();
|
||||
if (items.length() == 0)
|
||||
return;
|
||||
for (auto item : items)
|
||||
this->removeScript(item);
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::replaceScript(QListWidgetItem * item) {
|
||||
const QString filepath = this->chooseScript(this->getScriptFilepath(item));
|
||||
if (filepath.isEmpty())
|
||||
return;
|
||||
this->setScriptFilepath(item, filepath);
|
||||
this->markEdited();
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::openScript(QListWidgetItem * item) {
|
||||
const QString path = this->getScriptFilepath(item);
|
||||
QFileInfo fileInfo(path);
|
||||
if (!fileInfo.exists() || !fileInfo.isFile()){
|
||||
QMessageBox::warning(this, "", QString("Failed to open script '%1'").arg(path));
|
||||
return;
|
||||
}
|
||||
Editor::openInTextEditor(path);
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::openSelectedScripts() {
|
||||
for (auto item : ui->list->selectedItems())
|
||||
this->openScript(item);
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::reloadScripts() {
|
||||
if (this->hasUnsavedChanges) {
|
||||
if (this->prompt("Scripts have been modified, save changes and reload the script engine?", QMessageBox::Yes) == QMessageBox::No)
|
||||
return;
|
||||
this->save();
|
||||
}
|
||||
emit reloadScriptEngine();
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::save() {
|
||||
if (!this->hasUnsavedChanges)
|
||||
return;
|
||||
|
||||
QStringList paths;
|
||||
QList<bool> enabledStates;
|
||||
for (int i = 0; i < ui->list->count(); i++) {
|
||||
auto item = ui->list->item(i);
|
||||
const QString path = this->getScriptFilepath(item, false);
|
||||
if (!path.isEmpty()) {
|
||||
paths.append(path);
|
||||
enabledStates.append(this->getScriptEnabled(item));
|
||||
}
|
||||
}
|
||||
|
||||
userConfig.setCustomScripts(paths, enabledStates);
|
||||
this->hasUnsavedChanges = false;
|
||||
this->reloadScripts();
|
||||
}
|
||||
|
||||
int CustomScriptsEditor::prompt(const QString &text, QMessageBox::StandardButton defaultButton) {
|
||||
QMessageBox messageBox(this);
|
||||
messageBox.setText(text);
|
||||
messageBox.setIcon(QMessageBox::Question);
|
||||
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | defaultButton);
|
||||
messageBox.setDefaultButton(defaultButton);
|
||||
return messageBox.exec();
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::dialogButtonClicked(QAbstractButton *button) {
|
||||
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
|
||||
this->save();
|
||||
close(); // All buttons (OK and Cancel) close the window
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::closeEvent(QCloseEvent* event) {
|
||||
if (this->hasUnsavedChanges) {
|
||||
int result = this->prompt("Scripts have been modified, save changes?", QMessageBox::Cancel);
|
||||
if (result == QMessageBox::Cancel) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
if (result == QMessageBox::Yes)
|
||||
this->save();
|
||||
}
|
||||
|
||||
porymapConfig.setCustomScriptsEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState()
|
||||
);
|
||||
}
|
14
src/ui/customscriptslistitem.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "customscriptslistitem.h"
|
||||
#include "ui_customscriptslistitem.h"
|
||||
|
||||
CustomScriptsListItem::CustomScriptsListItem(QWidget *parent) :
|
||||
QFrame(parent),
|
||||
ui(new Ui::CustomScriptsListItem)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
CustomScriptsListItem::~CustomScriptsListItem()
|
||||
{
|
||||
delete ui;
|
||||
}
|
|
@ -29,11 +29,30 @@ void NoScrollComboBox::wheelEvent(QWheelEvent *event)
|
|||
QComboBox::wheelEvent(event);
|
||||
}
|
||||
|
||||
void NoScrollComboBox::setItem(int index, const QString &text)
|
||||
{
|
||||
if (index >= 0) {
|
||||
// Valid item
|
||||
this->setCurrentIndex(index);
|
||||
} else if (this->isEditable()) {
|
||||
// Invalid item in editable box, just display the text
|
||||
this->setCurrentText(text);
|
||||
} else {
|
||||
// Invalid item in uneditable box, display text as placeholder
|
||||
// On Qt < 5.15 this will display an empty box
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
this->setPlaceholderText(text);
|
||||
#endif
|
||||
this->setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
void NoScrollComboBox::setTextItem(const QString &text)
|
||||
{
|
||||
int index = this->findText(text);
|
||||
if (index >= 0)
|
||||
this->setCurrentIndex(index);
|
||||
else
|
||||
this->setCurrentText(text);
|
||||
this->setItem(this->findText(text), text);
|
||||
}
|
||||
|
||||
void NoScrollComboBox::setNumberItem(int value)
|
||||
{
|
||||
this->setItem(this->findData(value), QString::number(value));
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
const QString defaultFilepath = "prefabs.json";
|
||||
|
||||
void Prefab::loadPrefabs() {
|
||||
this->items.clear();
|
||||
QString filepath = projectConfig.getPrefabFilepath(false);
|
||||
QString filepath = projectConfig.getPrefabFilepath();
|
||||
if (filepath.isEmpty()) return;
|
||||
|
||||
ParseUtil parser;
|
||||
|
@ -85,8 +87,11 @@ void Prefab::loadPrefabs() {
|
|||
}
|
||||
|
||||
void Prefab::savePrefabs() {
|
||||
QString filepath = projectConfig.getPrefabFilepath(true);
|
||||
if (filepath.isEmpty()) return;
|
||||
QString filepath = projectConfig.getPrefabFilepath();
|
||||
if (filepath.isEmpty()) {
|
||||
filepath = defaultFilepath;
|
||||
projectConfig.setPrefabFilepath(filepath);
|
||||
}
|
||||
|
||||
QFileInfo info(filepath);
|
||||
if (info.isRelative()) {
|
||||
|
@ -269,48 +274,58 @@ void Prefab::addPrefab(MetatileSelection selection, Map *map, QString name) {
|
|||
this->updatePrefabUi(map);
|
||||
}
|
||||
|
||||
void Prefab::tryImportDefaultPrefabs(Map *map) {
|
||||
BaseGameVersion version = projectConfig.getBaseGameVersion();
|
||||
bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, QString filepath) {
|
||||
// Ensure we have default prefabs for the project's game version.
|
||||
if (version != BaseGameVersion::pokeruby && version != BaseGameVersion::pokeemerald && version != BaseGameVersion::pokefirered)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Exit early if the user has already setup prefabs.
|
||||
if (!projectConfig.getPrefabFilepath(false).isEmpty())
|
||||
return;
|
||||
if (filepath.isEmpty())
|
||||
filepath = defaultFilepath;
|
||||
|
||||
// Exit early if the user has already gone through this import prompt before.
|
||||
if (projectConfig.getPrefabImportPrompted())
|
||||
return;
|
||||
// Get the absolute filepath for writing/warnings
|
||||
QString absFilepath;
|
||||
QFileInfo fileInfo(filepath);
|
||||
if (fileInfo.suffix().isEmpty())
|
||||
filepath += ".json";
|
||||
if (fileInfo.isRelative()) {
|
||||
absFilepath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath);
|
||||
} else {
|
||||
absFilepath = filepath;
|
||||
}
|
||||
|
||||
// The warning message when importing defaults changes if there's a pre-existing file.
|
||||
QString fileWarning;
|
||||
if (!QFileInfo::exists(absFilepath)) {
|
||||
fileWarning = QString("This will create a file called '%1'").arg(absFilepath);
|
||||
} else {
|
||||
fileWarning = QString("This will overwrite any existing prefabs in '%1'").arg(absFilepath);
|
||||
}
|
||||
|
||||
// Display a dialog box to the user, asking if the default prefabs should be imported
|
||||
// into their project.
|
||||
QMessageBox::StandardButton prompt =
|
||||
QMessageBox::question(nullptr,
|
||||
QMessageBox::question(parent,
|
||||
"Import Default Prefabs",
|
||||
QString("Would you like to import the default prefabs for %1? This will create a file called 'prefabs.json' in your project directory.")
|
||||
.arg(projectConfig.getBaseGameVersionString()),
|
||||
QString("Would you like to import the default prefabs for %1? %2.")
|
||||
.arg(projectConfig.getBaseGameVersionString(version))
|
||||
.arg(fileWarning),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (prompt == QMessageBox::Yes) {
|
||||
bool acceptedImport = (prompt == QMessageBox::Yes);
|
||||
if (acceptedImport) {
|
||||
// Sets up the default prefabs.json filepath.
|
||||
QString filepath = projectConfig.getPrefabFilepath(true);
|
||||
|
||||
QFileInfo info(filepath);
|
||||
if (info.isRelative()) {
|
||||
filepath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath);
|
||||
}
|
||||
QFile prefabsFile(filepath);
|
||||
projectConfig.setPrefabFilepath(filepath);
|
||||
QFile prefabsFile(absFilepath);
|
||||
if (!prefabsFile.open(QIODevice::WriteOnly)) {
|
||||
projectConfig.setPrefabFilepath(QString());
|
||||
|
||||
logError(QString("Error: Could not open %1 for writing").arg(filepath));
|
||||
QMessageBox messageBox;
|
||||
logError(QString("Error: Could not open %1 for writing").arg(absFilepath));
|
||||
QMessageBox messageBox(parent);
|
||||
messageBox.setText("Failed to import default prefabs file!");
|
||||
messageBox.setInformativeText(QString("Could not open \"%1\" for writing").arg(filepath));
|
||||
messageBox.setInformativeText(QString("Could not open \"%1\" for writing").arg(absFilepath));
|
||||
messageBox.setIcon(QMessageBox::Warning);
|
||||
messageBox.exec();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseUtil parser;
|
||||
|
@ -330,10 +345,10 @@ void Prefab::tryImportDefaultPrefabs(Map *map) {
|
|||
prefabsFile.write(content.toUtf8());
|
||||
prefabsFile.close();
|
||||
this->loadPrefabs();
|
||||
this->updatePrefabUi(map);
|
||||
}
|
||||
|
||||
projectConfig.setPrefabImportPrompted(true);
|
||||
return acceptedImport;
|
||||
}
|
||||
|
||||
Prefab prefab;
|
||||
|
|
|
@ -17,11 +17,14 @@ PreferenceEditor::PreferenceEditor(QWidget *parent) :
|
|||
ui->setupUi(this);
|
||||
auto *formLayout = new QFormLayout(ui->groupBox_Themes);
|
||||
themeSelector = new NoScrollComboBox(ui->groupBox_Themes);
|
||||
themeSelector->setEditable(false);
|
||||
themeSelector->setMinimumContentsLength(0);
|
||||
formLayout->addRow("Themes", themeSelector);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked,
|
||||
this, &PreferenceEditor::dialogButtonClicked);
|
||||
populateFields();
|
||||
initFields();
|
||||
updateFields();
|
||||
}
|
||||
|
||||
PreferenceEditor::~PreferenceEditor()
|
||||
|
@ -29,7 +32,7 @@ PreferenceEditor::~PreferenceEditor()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void PreferenceEditor::populateFields() {
|
||||
void PreferenceEditor::initFields() {
|
||||
QStringList themes = { "default" };
|
||||
static const QRegularExpression re(":/themes/([A-z0-9_-]+).qss");
|
||||
QDirIterator it(":/themes", QDirIterator::Subdirectories);
|
||||
|
@ -38,11 +41,14 @@ void PreferenceEditor::populateFields() {
|
|||
themes.append(themeName);
|
||||
}
|
||||
themeSelector->addItems(themes);
|
||||
}
|
||||
|
||||
void PreferenceEditor::updateFields() {
|
||||
themeSelector->setCurrentText(porymapConfig.getTheme());
|
||||
|
||||
ui->lineEdit_TextEditorOpenFolder->setText(porymapConfig.getTextEditorOpenFolder());
|
||||
|
||||
ui->lineEdit_TextEditorGotoLine->setText(porymapConfig.getTextEditorGotoLine());
|
||||
ui->checkBox_MonitorProjectFiles->setChecked(porymapConfig.getMonitorFiles());
|
||||
ui->checkBox_OpenRecentProject->setChecked(porymapConfig.getReopenOnLaunch());
|
||||
}
|
||||
|
||||
void PreferenceEditor::saveFields() {
|
||||
|
@ -53,8 +59,9 @@ void PreferenceEditor::saveFields() {
|
|||
}
|
||||
|
||||
porymapConfig.setTextEditorOpenFolder(ui->lineEdit_TextEditorOpenFolder->text());
|
||||
|
||||
porymapConfig.setTextEditorGotoLine(ui->lineEdit_TextEditorGotoLine->text());
|
||||
porymapConfig.setMonitorFiles(ui->checkBox_MonitorProjectFiles->isChecked());
|
||||
porymapConfig.setReopenOnLaunch(ui->checkBox_OpenRecentProject->isChecked());
|
||||
|
||||
emit preferencesSaved();
|
||||
}
|
||||
|
|
401
src/ui/projectsettingseditor.cpp
Normal file
|
@ -0,0 +1,401 @@
|
|||
#include "projectsettingseditor.h"
|
||||
#include "ui_projectsettingseditor.h"
|
||||
#include "config.h"
|
||||
#include "noscrollcombobox.h"
|
||||
#include "prefab.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QFormLayout>
|
||||
|
||||
/*
|
||||
Editor for the settings in a user's porymap.project.cfg file (and 'use_encounter_json' in porymap.user.cfg).
|
||||
*/
|
||||
|
||||
ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::ProjectSettingsEditor),
|
||||
project(project),
|
||||
baseDir(userConfig.getProjectDir() + QDir::separator())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
this->initUi();
|
||||
this->createProjectPathsTable();
|
||||
this->connectSignals();
|
||||
this->refresh();
|
||||
this->restoreWindowState();
|
||||
}
|
||||
|
||||
ProjectSettingsEditor::~ProjectSettingsEditor()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::connectSignals() {
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ProjectSettingsEditor::dialogButtonClicked);
|
||||
connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::choosePrefabsFileClicked);
|
||||
connect(ui->button_ImportDefaultPrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::importDefaultPrefabsClicked);
|
||||
connect(ui->comboBox_BaseGameVersion, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::promptRestoreDefaults);
|
||||
connect(ui->comboBox_AttributesSize, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::updateAttributeLimits);
|
||||
connect(ui->checkBox_EnableCustomBorderSize, &QCheckBox::stateChanged, [this](int state) {
|
||||
bool customSize = (state == Qt::Checked);
|
||||
// When switching between the spin boxes or line edit for border metatiles we set
|
||||
// the newly-shown UI using the values from the hidden UI.
|
||||
this->setBorderMetatileIds(customSize, this->getBorderMetatileIds(!customSize));
|
||||
this->setBorderMetatilesUi(customSize);
|
||||
});
|
||||
|
||||
// Record that there are unsaved changes if any of the settings are modified
|
||||
for (auto combo : ui->centralwidget->findChildren<NoScrollComboBox *>())
|
||||
connect(combo, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
|
||||
connect(checkBox, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto lineEdit : ui->centralwidget->findChildren<QLineEdit *>())
|
||||
connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto spinBox : ui->centralwidget->findChildren<NoScrollSpinBox *>())
|
||||
connect(spinBox, &QSpinBox::textChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto spinBox : ui->centralwidget->findChildren<UIntSpinBox *>())
|
||||
connect(spinBox, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::markEdited() {
|
||||
// Don't treat signals emitted while the UI is refreshing as edits
|
||||
if (!this->refreshing)
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::initUi() {
|
||||
// Populate combo boxes
|
||||
if (project) ui->comboBox_DefaultPrimaryTileset->addItems(project->primaryTilesetLabels);
|
||||
if (project) ui->comboBox_DefaultSecondaryTileset->addItems(project->secondaryTilesetLabels);
|
||||
ui->comboBox_BaseGameVersion->addItems(ProjectConfig::versionStrings);
|
||||
ui->comboBox_AttributesSize->addItems({"1", "2", "4"});
|
||||
|
||||
// Validate that the border metatiles text is a comma-separated list of metatile values
|
||||
const QString regex_Hex = "(0[xX])?[A-Fa-f0-9]+";
|
||||
static const QRegularExpression expression(QString("^(%1,)*%1$").arg(regex_Hex)); // Comma-separated list of hex values
|
||||
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression);
|
||||
ui->lineEdit_BorderMetatiles->setValidator(validator);
|
||||
this->setBorderMetatilesUi(projectConfig.getUseCustomBorderSize());
|
||||
|
||||
int maxMetatileId = Project::getNumMetatilesTotal() - 1;
|
||||
ui->spinBox_FillMetatile->setMaximum(maxMetatileId);
|
||||
ui->spinBox_BorderMetatile1->setMaximum(maxMetatileId);
|
||||
ui->spinBox_BorderMetatile2->setMaximum(maxMetatileId);
|
||||
ui->spinBox_BorderMetatile3->setMaximum(maxMetatileId);
|
||||
ui->spinBox_BorderMetatile4->setMaximum(maxMetatileId);
|
||||
ui->spinBox_Elevation->setMaximum(15);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::setBorderMetatilesUi(bool customSize) {
|
||||
ui->widget_DefaultSizeBorderMetatiles->setVisible(!customSize);
|
||||
ui->widget_CustomSizeBorderMetatiles->setVisible(customSize);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::setBorderMetatileIds(bool customSize, QList<uint16_t> metatileIds) {
|
||||
if (customSize) {
|
||||
ui->lineEdit_BorderMetatiles->setText(Metatile::getMetatileIdStringList(metatileIds));
|
||||
} else {
|
||||
ui->spinBox_BorderMetatile1->setValue(metatileIds.value(0, 0x0));
|
||||
ui->spinBox_BorderMetatile2->setValue(metatileIds.value(1, 0x0));
|
||||
ui->spinBox_BorderMetatile3->setValue(metatileIds.value(2, 0x0));
|
||||
ui->spinBox_BorderMetatile4->setValue(metatileIds.value(3, 0x0));
|
||||
}
|
||||
}
|
||||
|
||||
QList<uint16_t> ProjectSettingsEditor::getBorderMetatileIds(bool customSize) {
|
||||
QList<uint16_t> metatileIds;
|
||||
if (customSize) {
|
||||
// Custom border size, read metatiles from line edit
|
||||
for (auto s : ui->lineEdit_BorderMetatiles->text().split(",")) {
|
||||
uint16_t metatileId = s.toUInt(nullptr, 0);
|
||||
metatileIds.append(qMin(metatileId, static_cast<uint16_t>(Project::getNumMetatilesTotal() - 1)));
|
||||
}
|
||||
} else {
|
||||
// Default border size, read metatiles from spin boxes
|
||||
metatileIds.append(ui->spinBox_BorderMetatile1->value());
|
||||
metatileIds.append(ui->spinBox_BorderMetatile2->value());
|
||||
metatileIds.append(ui->spinBox_BorderMetatile3->value());
|
||||
metatileIds.append(ui->spinBox_BorderMetatile4->value());
|
||||
}
|
||||
return metatileIds;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::updateAttributeLimits(const QString &attrSize) {
|
||||
QMap<QString, uint32_t> limits {
|
||||
{"1", 0xFF},
|
||||
{"2", 0xFFFF},
|
||||
{"4", 0xFFFFFFFF},
|
||||
};
|
||||
uint32_t max = limits.value(attrSize, UINT_MAX);
|
||||
ui->spinBox_BehaviorMask->setMaximum(max);
|
||||
ui->spinBox_EncounterTypeMask->setMaximum(max);
|
||||
ui->spinBox_LayerTypeMask->setMaximum(max);
|
||||
ui->spinBox_TerrainTypeMask->setMaximum(max);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::createProjectPathsTable() {
|
||||
auto pathPairs = ProjectConfig::defaultPaths.values();
|
||||
for (auto pathPair : pathPairs) {
|
||||
// Name of the path
|
||||
auto name = new QLabel();
|
||||
name->setAlignment(Qt::AlignBottom);
|
||||
name->setText(pathPair.first);
|
||||
|
||||
// Filepath line edit
|
||||
auto lineEdit = new QLineEdit();
|
||||
lineEdit->setObjectName(pathPair.first); // Used when saving the paths
|
||||
lineEdit->setPlaceholderText(pathPair.second);
|
||||
lineEdit->setClearButtonEnabled(true);
|
||||
|
||||
// "Choose file" button
|
||||
auto button = new QToolButton();
|
||||
button->setIcon(QIcon(":/icons/folder.ico"));
|
||||
connect(button, &QAbstractButton::clicked, [this, lineEdit](bool) {
|
||||
const QString path = this->chooseProjectFile(lineEdit->placeholderText());
|
||||
if (!path.isEmpty()) {
|
||||
lineEdit->setText(path);
|
||||
this->markEdited();
|
||||
}
|
||||
});
|
||||
|
||||
// Add to list
|
||||
auto editArea = new QWidget();
|
||||
auto layout = new QHBoxLayout(editArea);
|
||||
layout->addWidget(lineEdit);
|
||||
layout->addWidget(button);
|
||||
ui->layout_ProjectPaths->addRow(name, editArea);
|
||||
}
|
||||
}
|
||||
|
||||
QString ProjectSettingsEditor::chooseProjectFile(const QString &defaultFilepath) {
|
||||
const QString startDir = this->baseDir + defaultFilepath;
|
||||
|
||||
QString path;
|
||||
if (defaultFilepath.endsWith("/")){
|
||||
// Default filepath is a folder, choose a new folder
|
||||
path = QFileDialog::getExistingDirectory(this, "Choose Project File Folder", startDir) + QDir::separator();
|
||||
} else{
|
||||
// Default filepath is not a folder, choose a new file
|
||||
path = QFileDialog::getOpenFileName(this, "Choose Project File", startDir);
|
||||
}
|
||||
|
||||
if (!path.startsWith(this->baseDir)){
|
||||
// Most of Porymap's file-parsing code for project files will assume that filepaths
|
||||
// are relative to the root project folder, so we enforce that here.
|
||||
QMessageBox::warning(this, "Failed to set custom filepath",
|
||||
QString("Custom filepaths must be inside the root project folder '%1'").arg(this->baseDir));
|
||||
return QString();
|
||||
}
|
||||
return path.remove(0, this->baseDir.length());
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::restoreWindowState() {
|
||||
logInfo("Restoring project settings editor geometry from previous session.");
|
||||
const QMap<QString, QByteArray> geometry = porymapConfig.getProjectSettingsEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("project_settings_editor_geometry"));
|
||||
this->restoreState(geometry.value("project_settings_editor_state"));
|
||||
}
|
||||
|
||||
// Set UI states using config data
|
||||
void ProjectSettingsEditor::refresh() {
|
||||
this->refreshing = true; // Block signals
|
||||
|
||||
// Set combo box texts
|
||||
ui->comboBox_DefaultPrimaryTileset->setTextItem(projectConfig.getDefaultPrimaryTileset());
|
||||
ui->comboBox_DefaultSecondaryTileset->setTextItem(projectConfig.getDefaultSecondaryTileset());
|
||||
ui->comboBox_BaseGameVersion->setTextItem(projectConfig.getBaseGameVersionString());
|
||||
ui->comboBox_AttributesSize->setTextItem(QString::number(projectConfig.getMetatileAttributesSize()));
|
||||
this->updateAttributeLimits(ui->comboBox_AttributesSize->currentText());
|
||||
|
||||
// Set check box states
|
||||
ui->checkBox_UsePoryscript->setChecked(projectConfig.getUsePoryScript());
|
||||
ui->checkBox_ShowWildEncounterTables->setChecked(userConfig.getEncounterJsonActive());
|
||||
ui->checkBox_CreateTextFile->setChecked(projectConfig.getCreateMapTextFileEnabled());
|
||||
ui->checkBox_EnableTripleLayerMetatiles->setChecked(projectConfig.getTripleLayerMetatilesEnabled());
|
||||
ui->checkBox_EnableRequiresItemfinder->setChecked(projectConfig.getHiddenItemRequiresItemfinderEnabled());
|
||||
ui->checkBox_EnableQuantity->setChecked(projectConfig.getHiddenItemQuantityEnabled());
|
||||
ui->checkBox_EnableCloneObjects->setChecked(projectConfig.getEventCloneObjectEnabled());
|
||||
ui->checkBox_EnableWeatherTriggers->setChecked(projectConfig.getEventWeatherTriggerEnabled());
|
||||
ui->checkBox_EnableSecretBases->setChecked(projectConfig.getEventSecretBaseEnabled());
|
||||
ui->checkBox_EnableRespawn->setChecked(projectConfig.getHealLocationRespawnDataEnabled());
|
||||
ui->checkBox_EnableAllowFlags->setChecked(projectConfig.getMapAllowFlagsEnabled());
|
||||
ui->checkBox_EnableFloorNumber->setChecked(projectConfig.getFloorNumberEnabled());
|
||||
ui->checkBox_EnableCustomBorderSize->setChecked(projectConfig.getUseCustomBorderSize());
|
||||
ui->checkBox_OutputCallback->setChecked(projectConfig.getTilesetsHaveCallback());
|
||||
ui->checkBox_OutputIsCompressed->setChecked(projectConfig.getTilesetsHaveIsCompressed());
|
||||
|
||||
// Set spin box values
|
||||
ui->spinBox_Elevation->setValue(projectConfig.getNewMapElevation());
|
||||
ui->spinBox_FillMetatile->setValue(projectConfig.getNewMapMetatileId());
|
||||
ui->spinBox_BehaviorMask->setValue(projectConfig.getMetatileBehaviorMask());
|
||||
ui->spinBox_EncounterTypeMask->setValue(projectConfig.getMetatileEncounterTypeMask());
|
||||
ui->spinBox_LayerTypeMask->setValue(projectConfig.getMetatileLayerTypeMask());
|
||||
ui->spinBox_TerrainTypeMask->setValue(projectConfig.getMetatileTerrainTypeMask());
|
||||
|
||||
// Set (and sync) border metatile IDs
|
||||
auto metatileIds = projectConfig.getNewMapBorderMetatileIds();
|
||||
this->setBorderMetatileIds(false, metatileIds);
|
||||
this->setBorderMetatileIds(true, metatileIds);
|
||||
|
||||
// Set line edit texts
|
||||
ui->lineEdit_PrefabsPath->setText(projectConfig.getPrefabFilepath());
|
||||
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
|
||||
lineEdit->setText(projectConfig.getFilePath(lineEdit->objectName(), true));
|
||||
|
||||
this->refreshing = false; // Allow signals
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::save() {
|
||||
if (!this->hasUnsavedChanges)
|
||||
return;
|
||||
|
||||
// Prevent a call to save() for each of the config settings
|
||||
projectConfig.setSaveDisabled(true);
|
||||
|
||||
projectConfig.setDefaultPrimaryTileset(ui->comboBox_DefaultPrimaryTileset->currentText());
|
||||
projectConfig.setDefaultSecondaryTileset(ui->comboBox_DefaultSecondaryTileset->currentText());
|
||||
projectConfig.setBaseGameVersion(projectConfig.stringToBaseGameVersion(ui->comboBox_BaseGameVersion->currentText()));
|
||||
projectConfig.setMetatileAttributesSize(ui->comboBox_AttributesSize->currentText().toInt());
|
||||
projectConfig.setUsePoryScript(ui->checkBox_UsePoryscript->isChecked());
|
||||
userConfig.setEncounterJsonActive(ui->checkBox_ShowWildEncounterTables->isChecked());
|
||||
projectConfig.setCreateMapTextFileEnabled(ui->checkBox_CreateTextFile->isChecked());
|
||||
projectConfig.setTripleLayerMetatilesEnabled(ui->checkBox_EnableTripleLayerMetatiles->isChecked());
|
||||
projectConfig.setHiddenItemRequiresItemfinderEnabled(ui->checkBox_EnableRequiresItemfinder->isChecked());
|
||||
projectConfig.setHiddenItemQuantityEnabled(ui->checkBox_EnableQuantity->isChecked());
|
||||
projectConfig.setEventCloneObjectEnabled(ui->checkBox_EnableCloneObjects->isChecked());
|
||||
projectConfig.setEventWeatherTriggerEnabled(ui->checkBox_EnableWeatherTriggers->isChecked());
|
||||
projectConfig.setEventSecretBaseEnabled(ui->checkBox_EnableSecretBases->isChecked());
|
||||
projectConfig.setHealLocationRespawnDataEnabled(ui->checkBox_EnableRespawn->isChecked());
|
||||
projectConfig.setMapAllowFlagsEnabled(ui->checkBox_EnableAllowFlags->isChecked());
|
||||
projectConfig.setFloorNumberEnabled(ui->checkBox_EnableFloorNumber->isChecked());
|
||||
projectConfig.setUseCustomBorderSize(ui->checkBox_EnableCustomBorderSize->isChecked());
|
||||
projectConfig.setTilesetsHaveCallback(ui->checkBox_OutputCallback->isChecked());
|
||||
projectConfig.setTilesetsHaveIsCompressed(ui->checkBox_OutputIsCompressed->isChecked());
|
||||
projectConfig.setNewMapElevation(ui->spinBox_Elevation->value());
|
||||
projectConfig.setNewMapMetatileId(ui->spinBox_FillMetatile->value());
|
||||
projectConfig.setMetatileBehaviorMask(ui->spinBox_BehaviorMask->value());
|
||||
projectConfig.setMetatileTerrainTypeMask(ui->spinBox_TerrainTypeMask->value());
|
||||
projectConfig.setMetatileEncounterTypeMask(ui->spinBox_EncounterTypeMask->value());
|
||||
projectConfig.setMetatileLayerTypeMask(ui->spinBox_LayerTypeMask->value());
|
||||
projectConfig.setPrefabFilepath(ui->lineEdit_PrefabsPath->text());
|
||||
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
|
||||
projectConfig.setFilePath(lineEdit->objectName(), lineEdit->text());
|
||||
projectConfig.setNewMapBorderMetatileIds(this->getBorderMetatileIds(ui->checkBox_EnableCustomBorderSize->isChecked()));
|
||||
|
||||
projectConfig.setSaveDisabled(false);
|
||||
projectConfig.save();
|
||||
this->hasUnsavedChanges = false;
|
||||
|
||||
// Technically, a reload is not required for several of the config settings.
|
||||
// For simplicity we prompt the user to reload when any setting is changed regardless.
|
||||
this->projectNeedsReload = true;
|
||||
}
|
||||
|
||||
// Pick a file to use as the new prefabs file path
|
||||
void ProjectSettingsEditor::choosePrefabsFileClicked(bool) {
|
||||
QString startPath = this->project->importExportPath;
|
||||
QFileInfo fileInfo(ui->lineEdit_PrefabsPath->text());
|
||||
if (fileInfo.exists() && fileInfo.isFile() && fileInfo.suffix() == "json") {
|
||||
// Current setting is a valid JSON file. Start the file dialog there
|
||||
startPath = fileInfo.dir().absolutePath();
|
||||
}
|
||||
QString filepath = QFileDialog::getOpenFileName(this, "Choose Prefabs File", startPath, "JSON Files (*.json)");
|
||||
if (filepath.isEmpty())
|
||||
return;
|
||||
this->project->setImportExportPath(filepath);
|
||||
|
||||
// Display relative path if this file is in the project folder
|
||||
if (filepath.startsWith(this->baseDir))
|
||||
filepath.remove(0, this->baseDir.length());
|
||||
ui->lineEdit_PrefabsPath->setText(filepath);
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::importDefaultPrefabsClicked(bool) {
|
||||
// If the prompt is accepted the prefabs file will be created and its filepath will be saved in the config.
|
||||
// No need to set hasUnsavedChanges here.
|
||||
BaseGameVersion version = projectConfig.stringToBaseGameVersion(ui->comboBox_BaseGameVersion->currentText());
|
||||
if (prefab.tryImportDefaultPrefabs(this, version, ui->lineEdit_PrefabsPath->text())) {
|
||||
ui->lineEdit_PrefabsPath->setText(projectConfig.getPrefabFilepath()); // Refresh with new filepath
|
||||
this->projectNeedsReload = true;
|
||||
}
|
||||
}
|
||||
|
||||
int ProjectSettingsEditor::prompt(const QString &text, QMessageBox::StandardButton defaultButton) {
|
||||
QMessageBox messageBox(this);
|
||||
messageBox.setText(text);
|
||||
messageBox.setIcon(QMessageBox::Question);
|
||||
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | defaultButton);
|
||||
messageBox.setDefaultButton(defaultButton);
|
||||
return messageBox.exec();
|
||||
}
|
||||
|
||||
bool ProjectSettingsEditor::promptSaveChanges() {
|
||||
if (!this->hasUnsavedChanges)
|
||||
return true;
|
||||
|
||||
int result = this->prompt("Settings have been modified, save changes?", QMessageBox::Cancel);
|
||||
if (result == QMessageBox::Cancel)
|
||||
return false;
|
||||
|
||||
if (result == QMessageBox::Yes)
|
||||
this->save();
|
||||
else if (result == QMessageBox::No)
|
||||
this->hasUnsavedChanges = false; // Discarding changes
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectSettingsEditor::promptRestoreDefaults() {
|
||||
if (this->refreshing)
|
||||
return false;
|
||||
|
||||
const QString versionText = ui->comboBox_BaseGameVersion->currentText();
|
||||
if (this->prompt(QString("Restore default config settings for %1?").arg(versionText)) == QMessageBox::No)
|
||||
return false;
|
||||
|
||||
// Restore defaults by resetting config in memory, refreshing the UI, then restoring the config.
|
||||
// Don't want to save changes until user accepts them.
|
||||
ProjectConfig tempProject = projectConfig;
|
||||
projectConfig.reset(projectConfig.stringToBaseGameVersion(versionText));
|
||||
this->refresh();
|
||||
projectConfig = tempProject;
|
||||
|
||||
this->hasUnsavedChanges = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::dialogButtonClicked(QAbstractButton *button) {
|
||||
auto buttonRole = ui->buttonBox->buttonRole(button);
|
||||
if (buttonRole == QDialogButtonBox::AcceptRole) {
|
||||
// "OK" button
|
||||
this->save();
|
||||
close();
|
||||
} else if (buttonRole == QDialogButtonBox::RejectRole) {
|
||||
// "Cancel" button
|
||||
if (!this->promptSaveChanges())
|
||||
return;
|
||||
close();
|
||||
} else if (buttonRole == QDialogButtonBox::ResetRole) {
|
||||
// "Restore Defaults" button
|
||||
this->promptRestoreDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::closeEvent(QCloseEvent* event) {
|
||||
if (!this->promptSaveChanges()) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->projectNeedsReload) {
|
||||
if (this->prompt("Settings changed, reload project to apply changes?") == QMessageBox::Yes)
|
||||
emit this->reloadProject();
|
||||
}
|
||||
|
||||
porymapConfig.setProjectSettingsEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState()
|
||||
);
|
||||
}
|
|
@ -117,6 +117,7 @@ void TilesetEditor::setAttributesUi() {
|
|||
for (int num : project->metatileBehaviorMapInverse.keys()) {
|
||||
this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num);
|
||||
}
|
||||
this->ui->comboBox_metatileBehaviors->setMinimumContentsLength(0);
|
||||
} else {
|
||||
this->ui->comboBox_metatileBehaviors->setVisible(false);
|
||||
this->ui->label_metatileBehavior->setVisible(false);
|
||||
|
@ -128,6 +129,8 @@ void TilesetEditor::setAttributesUi() {
|
|||
this->ui->comboBox_terrainType->addItem("Grass", TERRAIN_GRASS);
|
||||
this->ui->comboBox_terrainType->addItem("Water", TERRAIN_WATER);
|
||||
this->ui->comboBox_terrainType->addItem("Waterfall", TERRAIN_WATERFALL);
|
||||
this->ui->comboBox_terrainType->setEditable(false);
|
||||
this->ui->comboBox_terrainType->setMinimumContentsLength(0);
|
||||
} else {
|
||||
this->ui->comboBox_terrainType->setVisible(false);
|
||||
this->ui->label_terrainType->setVisible(false);
|
||||
|
@ -138,6 +141,8 @@ void TilesetEditor::setAttributesUi() {
|
|||
this->ui->comboBox_encounterType->addItem("None", ENCOUNTER_NONE);
|
||||
this->ui->comboBox_encounterType->addItem("Land", ENCOUNTER_LAND);
|
||||
this->ui->comboBox_encounterType->addItem("Water", ENCOUNTER_WATER);
|
||||
this->ui->comboBox_encounterType->setEditable(false);
|
||||
this->ui->comboBox_encounterType->setMinimumContentsLength(0);
|
||||
} else {
|
||||
this->ui->comboBox_encounterType->setVisible(false);
|
||||
this->ui->label_encounterType->setVisible(false);
|
||||
|
@ -148,6 +153,8 @@ void TilesetEditor::setAttributesUi() {
|
|||
this->ui->comboBox_layerType->addItem("Normal - Middle/Top", METATILE_LAYER_MIDDLE_TOP);
|
||||
this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", METATILE_LAYER_BOTTOM_MIDDLE);
|
||||
this->ui->comboBox_layerType->addItem("Split - Bottom/Top", METATILE_LAYER_BOTTOM_TOP);
|
||||
this->ui->comboBox_layerType->setEditable(false);
|
||||
this->ui->comboBox_layerType->setMinimumContentsLength(0);
|
||||
if (!Metatile::getLayerTypeMask()) {
|
||||
// User doesn't have triple layer metatiles, but has no layer type attribute.
|
||||
// Porymap is still using the layer type value to render metatiles, and with
|
||||
|
@ -345,8 +352,7 @@ void TilesetEditor::drawSelectedTiles() {
|
|||
|
||||
void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) {
|
||||
QString label = Tileset::getMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
QString hexString = QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper();
|
||||
QString message = QString("Metatile: 0x%1").arg(hexString);
|
||||
QString message = QString("Metatile: %1").arg(Metatile::getMetatileIdString(metatileId));
|
||||
if (label.size() != 0) {
|
||||
message += QString(" \"%1\"").arg(label);
|
||||
}
|
||||
|
@ -357,24 +363,6 @@ void TilesetEditor::onHoveredMetatileCleared() {
|
|||
this->ui->statusbar->clearMessage();
|
||||
}
|
||||
|
||||
void TilesetEditor::setComboValue(QComboBox * combo, int value) {
|
||||
int index = combo->findData(value);
|
||||
if (index >= 0) {
|
||||
// Valid item
|
||||
combo->setCurrentIndex(index);
|
||||
} else if (combo->isEditable()) {
|
||||
// Invalid item in editable box, just display the text
|
||||
combo->setCurrentText(QString::number(value));
|
||||
} else {
|
||||
// Invalid item in uneditable box, display text as placeholder
|
||||
// On Qt < 5.15 this will display an empty box
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
combo->setPlaceholderText(QString::number(value));
|
||||
#endif
|
||||
combo->setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
|
||||
this->metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
this->metatileLayersItem->setMetatile(metatile);
|
||||
|
@ -385,10 +373,10 @@ void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
|
|||
this->ui->lineEdit_metatileLabel->setText(labels.owned);
|
||||
this->ui->lineEdit_metatileLabel->setPlaceholderText(labels.shared);
|
||||
|
||||
setComboValue(this->ui->comboBox_metatileBehaviors, this->metatile->behavior);
|
||||
setComboValue(this->ui->comboBox_layerType, this->metatile->layerType);
|
||||
setComboValue(this->ui->comboBox_encounterType, this->metatile->encounterType);
|
||||
setComboValue(this->ui->comboBox_terrainType, this->metatile->terrainType);
|
||||
this->ui->comboBox_metatileBehaviors->setNumberItem(this->metatile->behavior);
|
||||
this->ui->comboBox_layerType->setNumberItem(this->metatile->layerType);
|
||||
this->ui->comboBox_encounterType->setNumberItem(this->metatile->encounterType);
|
||||
this->ui->comboBox_terrainType->setNumberItem(this->metatile->terrainType);
|
||||
}
|
||||
|
||||
void TilesetEditor::onHoveredTileChanged(uint16_t tile) {
|
||||
|
|
178
src/ui/uintspinbox.cpp
Normal file
|
@ -0,0 +1,178 @@
|
|||
#include "uintspinbox.h"
|
||||
|
||||
UIntSpinBox::UIntSpinBox(QWidget *parent)
|
||||
: QAbstractSpinBox(parent)
|
||||
{
|
||||
// Don't let scrolling hijack focus.
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
m_minimum = 0;
|
||||
m_maximum = 99;
|
||||
m_value = m_minimum;
|
||||
m_displayIntegerBase = 10;
|
||||
m_numChars = 2;
|
||||
m_hasPadding = false;
|
||||
|
||||
this->updateEdit();
|
||||
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onEditFinished()));
|
||||
};
|
||||
|
||||
void UIntSpinBox::setValue(uint32_t val) {
|
||||
if (m_value != val) {
|
||||
m_value = val;
|
||||
emit valueChanged(m_value);
|
||||
}
|
||||
this->updateEdit();
|
||||
}
|
||||
|
||||
uint32_t UIntSpinBox::valueFromText(const QString &text) const {
|
||||
return this->stripped(text).toUInt(nullptr, m_displayIntegerBase);
|
||||
}
|
||||
|
||||
QString UIntSpinBox::textFromValue(uint32_t val) const {
|
||||
if (m_hasPadding)
|
||||
return m_prefix + QString("%1").arg(val, m_numChars, m_displayIntegerBase, QChar('0')).toUpper();
|
||||
|
||||
return m_prefix + QString::number(val, m_displayIntegerBase).toUpper();
|
||||
}
|
||||
|
||||
void UIntSpinBox::setMinimum(uint32_t min) {
|
||||
this->setRange(min, qMax(min, m_maximum));
|
||||
}
|
||||
|
||||
void UIntSpinBox::setMaximum(uint32_t max) {
|
||||
this->setRange(qMin(m_minimum, max), max);
|
||||
}
|
||||
|
||||
void UIntSpinBox::setRange(uint32_t min, uint32_t max) {
|
||||
max = qMax(min, max);
|
||||
|
||||
if (m_maximum == max && m_minimum == min)
|
||||
return;
|
||||
|
||||
if (m_maximum != max) {
|
||||
// Update number of characters for padding
|
||||
m_numChars = 0;
|
||||
for (uint32_t i = max; i != 0; i /= m_displayIntegerBase)
|
||||
m_numChars++;
|
||||
}
|
||||
|
||||
m_minimum = min;
|
||||
m_maximum = max;
|
||||
|
||||
if (m_value < min)
|
||||
m_value %= min;
|
||||
else if (m_value > max)
|
||||
m_value %= max;
|
||||
this->updateEdit();
|
||||
}
|
||||
|
||||
void UIntSpinBox::setPrefix(const QString &prefix) {
|
||||
if (m_prefix != prefix) {
|
||||
m_prefix = prefix;
|
||||
this->updateEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::setDisplayIntegerBase(int base) {
|
||||
if (base < 2 || base > 36)
|
||||
base = 10;
|
||||
if (m_displayIntegerBase != base) {
|
||||
m_displayIntegerBase = base;
|
||||
this->updateEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::setHasPadding(bool enabled) {
|
||||
if (m_hasPadding != enabled) {
|
||||
m_hasPadding = enabled;
|
||||
this->updateEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::updateEdit() {
|
||||
const QString text = this->textFromValue(m_value);
|
||||
if (text != this->lineEdit()->text()) {
|
||||
this->lineEdit()->setText(text);
|
||||
emit textChanged(this->lineEdit()->text());
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::onEditFinished() {
|
||||
int pos = this->lineEdit()->cursorPosition();
|
||||
QString input = this->lineEdit()->text();
|
||||
|
||||
auto state = this->validate(input, pos);
|
||||
if (state == QValidator::Acceptable) {
|
||||
// Valid input
|
||||
m_value = this->valueFromText(input);
|
||||
} else if (state == QValidator::Intermediate) {
|
||||
// User has deleted all the number text.
|
||||
// If they did this by selecting all text and then hitting delete
|
||||
// make sure to put the cursor back in front of the prefix.
|
||||
m_value = m_minimum;
|
||||
this->lineEdit()->setCursorPosition(m_prefix.length());
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::stepBy(int steps)
|
||||
{
|
||||
auto new_value = m_value;
|
||||
if (steps < 0 && new_value + steps > new_value) {
|
||||
new_value = 0;
|
||||
} else if (steps > 0 && new_value + steps < new_value) {
|
||||
new_value = UINT_MAX;
|
||||
} else {
|
||||
new_value += steps;
|
||||
}
|
||||
this->setValue(new_value);
|
||||
}
|
||||
|
||||
QString UIntSpinBox::stripped(QString input) const {
|
||||
if (m_prefix.length() && input.startsWith(m_prefix))
|
||||
input.remove(0, m_prefix.length());
|
||||
return input.trimmed();
|
||||
}
|
||||
|
||||
QValidator::State UIntSpinBox::validate(QString &input, int &pos) const {
|
||||
QString copy(input);
|
||||
input = m_prefix;
|
||||
|
||||
// Adjust for prefix
|
||||
copy = this->stripped(copy);
|
||||
if (copy.isEmpty())
|
||||
return QValidator::Intermediate;
|
||||
|
||||
// Editing the prefix (if not deleting all text) is not allowed
|
||||
if (pos < m_prefix.length())
|
||||
return QValidator::Invalid;
|
||||
|
||||
bool ok;
|
||||
uint32_t num = copy.toUInt(&ok, m_displayIntegerBase);
|
||||
if (!ok || num < m_minimum || num > m_maximum)
|
||||
return QValidator::Invalid;
|
||||
|
||||
input += copy.toUpper();
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
|
||||
QAbstractSpinBox::StepEnabled UIntSpinBox::stepEnabled() const {
|
||||
QAbstractSpinBox::StepEnabled flags = QAbstractSpinBox::StepNone;
|
||||
if (m_value < m_maximum)
|
||||
flags |= QAbstractSpinBox::StepUpEnabled;
|
||||
if (m_value > m_minimum)
|
||||
flags |= QAbstractSpinBox::StepDownEnabled;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void UIntSpinBox::wheelEvent(QWheelEvent *event) {
|
||||
// Only allow scrolling to modify contents when it explicitly has focus.
|
||||
if (hasFocus())
|
||||
QAbstractSpinBox::wheelEvent(event);
|
||||
}
|
||||
|
||||
void UIntSpinBox::focusOutEvent(QFocusEvent *event) {
|
||||
this->updateEdit();
|
||||
QAbstractSpinBox::focusOutEvent(event);
|
||||
}
|