mirror of
https://github.com/huderlem/porymap.git
synced 2026-04-20 00:27:32 -05:00
Merge branch 'dev' of https://github.com/huderlem/porymap into event-selection
This commit is contained in:
commit
e67790a8d3
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
|
|
@ -9,8 +9,6 @@ on:
|
|||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
|
|||
- Reduced diff noise when saving maps.
|
||||
- Map names and ``MAP_NAME`` constants are no longer required to match.
|
||||
- Porymap will no longer overwrite ``include/constants/map_groups.h`` or ``include/constants/layouts.h``.
|
||||
- Primary/secondary metatile images are now kept on separate rows, rather than blending together if the primary size is not divisible by 8.
|
||||
|
||||
### Fixed
|
||||
- Fix `Add Region Map...` not updating the region map settings file.
|
||||
|
|
@ -79,6 +80,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
|
|||
- Fix some problems with tileset detection when importing maps from AdvanceMap.
|
||||
- Fix certain input fields allowing invalid identifiers, like names starting with numbers.
|
||||
- Fix crash in the Shortcuts Editor when applying changes after closing certain windows.
|
||||
- Fix `Display Metatile Usage Counts` sometimes changing the counts after repeated use.
|
||||
|
||||
## [5.4.1] - 2024-03-21
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ The filepath that Porymap expects for each file can be overridden on the ``Files
|
|||
data/tilesets/metatiles.inc, yes, yes, ``tilesets_metatiles_asm``, only if ``tilesets_headers`` can't be found
|
||||
data/tilesets/[primary|secondary]/\*, yes, yes, ``data_tilesets_folders``, default tileset data location
|
||||
src/data/wild_encounters.json, yes, yes, ``json_wild_encounters``, optional (only required to use Wild Pokémon tab)
|
||||
src/data/heal_locations.json, yes, yes, ``json_heal_locations``,
|
||||
src/data/object_events/object_event_graphics_info_pointers.h, yes, no, ``data_obj_event_gfx_pointers``,
|
||||
src/data/object_events/object_event_graphics_info.h, yes, no, ``data_obj_event_gfx_info``,
|
||||
src/data/object_events/object_event_pic_tables.h, yes, no, ``data_obj_event_pic_tables``,
|
||||
src/data/object_events/object_event_graphics.h, yes, no, ``data_obj_event_gfx``,
|
||||
src/data/graphics/pokemon.h, yes, no, ``data_pokemon_gfx``, for pokemon sprite icons
|
||||
src/data/heal_locations.h, yes, yes, ``data_heal_locations``,
|
||||
src/data/region_map/region_map_sections.json, yes, yes, ``json_region_map_entries``,
|
||||
src/data/region_map/porymap_config.json, yes, yes, ``json_region_porymap_cfg``,
|
||||
include/constants/global.h, yes, no, ``constants_global``, reads ``define_obj_event_count``
|
||||
|
|
@ -50,7 +50,6 @@ The filepath that Porymap expects for each file can be overridden on the ``Files
|
|||
include/constants/vars.h, yes, no, ``constants_vars``, for Trigger events
|
||||
include/constants/weather.h, yes, no, ``constants_weather``, for map weather and Weather Triggers
|
||||
include/constants/songs.h, yes, no, ``constants_songs``, for map music
|
||||
include/constants/heal_locations.h, yes, yes, ``constants_heal_locations``,
|
||||
include/constants/pokemon.h, yes, no, ``constants_pokemon``, reads ``define_min_level`` and ``define_max_level``
|
||||
include/constants/map_types.h, yes, no, ``constants_map_types``,
|
||||
include/constants/trainer_types.h, yes, no, ``constants_trainer_types``, for Object events
|
||||
|
|
@ -85,11 +84,6 @@ In addition to these files, there are some specific symbol and macro names that
|
|||
``symbol_obj_event_gfx_pointers``, ``gObjectEventGraphicsInfoPointers``, to map Object Event graphics IDs to graphics data
|
||||
``symbol_pokemon_icon_table``, ``gMonIconTable``, to map species constants to icon images
|
||||
``symbol_wild_encounters``, ``gWildMonHeaders``, output as the ``label`` property for the top-level wild ecounters JSON object
|
||||
``symbol_heal_locations_type``, ``struct HealLocation``, the type for the Heal Locations table
|
||||
``symbol_heal_locations``, ``sHealLocations``, the default Heal Locations table name when ``Respawn Map/NPC`` is disabled
|
||||
``symbol_spawn_points``, ``sSpawnPoints``, the default Heal Locations table name when ``Respawn Map/NPC`` is enabled
|
||||
``symbol_spawn_maps``, ``u16 sWhiteoutRespawnHealCenterMapIdxs``, the type and table name for Heal Location ``Respawn Map`` values
|
||||
``symbol_spawn_npcs``, ``u8 sWhiteoutRespawnHealerNpcIds``, the type and table name for Heal Location ``Respawn NPC`` values
|
||||
``symbol_attribute_table``, ``sMetatileAttrMasks``, optionally read to get settings on ``Tilesets`` tab
|
||||
``symbol_tilesets_prefix``, ``gTileset_``, for new tileset names and to extract base tileset names
|
||||
``symbol_dynamic_map_name``, ``Dynamic``, reserved map name to display for ``define_map_dynamic``
|
||||
|
|
@ -114,8 +108,7 @@ In addition to these files, there are some specific symbol and macro names that
|
|||
``define_attribute_terrain``, ``METATILE_ATTRIBUTE_TERRAIN``, name used to extract setting from ``symbol_attribute_table``
|
||||
``define_attribute_encounter``, ``METATILE_ATTRIBUTE_ENCOUNTER_TYPE``, name used to extract setting from ``symbol_attribute_table``
|
||||
``define_metatile_label_prefix``, ``METATILE_``, expected prefix for metatile label macro names
|
||||
``define_heal_locations_prefix``, ``HEAL_LOCATION_``, output as prefix for Heal Location IDs if ``Respawn Map/NPC`` is disabled
|
||||
``define_spawn_prefix``, ``SPAWN_``, output as prefix for Heal Location IDs if ``Respawn Map/NPC`` is enabled
|
||||
``define_heal_locations_prefix``, ``HEAL_LOCATION_``, default prefix for heal location macro names
|
||||
``define_map_prefix``, ``MAP_``, expected prefix for map macro names
|
||||
``define_map_dynamic``, ``DYNAMIC``, macro name after prefix for Dynamic maps
|
||||
``define_map_empty``, ``UNDEFINED``, macro name after prefix for empty maps
|
||||
|
|
|
|||
|
|
@ -2042,9 +2042,9 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_Healspots">
|
||||
<widget class="QWidget" name="tab_HealLocations">
|
||||
<attribute name="title">
|
||||
<string>Healspots</string>
|
||||
<string>Heal Locations</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<property name="leftMargin">
|
||||
|
|
@ -2091,7 +2091,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_Healspots">
|
||||
<spacer name="horizontalSpacer_HealLocations">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
|
|
@ -2106,7 +2106,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_Healspots">
|
||||
<widget class="QScrollArea" name="scrollArea_HealLocations">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
|
|
@ -2116,7 +2116,7 @@
|
|||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_Healspots">
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_HealLocations">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
|||
|
|
@ -54,6 +54,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_DisableEventWarning">
|
||||
<property name="toolTip">
|
||||
<string>If checked, no warning will be shown when deleting an event that has an associated #define that may also be deleted.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable warning when deleting events with IDs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -1119,9 +1119,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_HealspotsIcon">
|
||||
<widget class="QLineEdit" name="lineEdit_HealLocationsIcon">
|
||||
<property name="toolTip">
|
||||
<string>The icon that will be used to represent Healspot events</string>
|
||||
<string>The icon that will be used to represent Heal Location events</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
|
|
@ -1136,9 +1136,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_HealspotsIcon">
|
||||
<widget class="QLabel" name="label_HealLocationsIcon">
|
||||
<property name="text">
|
||||
<string>Healspots</string>
|
||||
<string>Heal Locations</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -1224,7 +1224,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QToolButton" name="button_HealspotsIcon">
|
||||
<widget class="QToolButton" name="button_HealLocationsIcon">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ public:
|
|||
this->paletteEditorBitDepth = 24;
|
||||
this->projectSettingsTab = 0;
|
||||
this->warpBehaviorWarningDisabled = false;
|
||||
this->eventDeleteWarningDisabled = false;
|
||||
this->checkForUpdates = true;
|
||||
this->lastUpdateCheckTime = QDateTime();
|
||||
this->lastUpdateCheckVersion = porymapVersion;
|
||||
|
|
@ -134,6 +135,7 @@ public:
|
|||
int paletteEditorBitDepth;
|
||||
int projectSettingsTab;
|
||||
bool warpBehaviorWarningDisabled;
|
||||
bool eventDeleteWarningDisabled;
|
||||
bool checkForUpdates;
|
||||
QDateTime lastUpdateCheckTime;
|
||||
QVersionNumber lastUpdateCheckVersion;
|
||||
|
|
@ -187,11 +189,6 @@ enum ProjectIdentifier {
|
|||
symbol_obj_event_gfx_pointers,
|
||||
symbol_pokemon_icon_table,
|
||||
symbol_wild_encounters,
|
||||
symbol_heal_locations_type,
|
||||
symbol_heal_locations,
|
||||
symbol_spawn_points,
|
||||
symbol_spawn_maps,
|
||||
symbol_spawn_npcs,
|
||||
symbol_attribute_table,
|
||||
symbol_tilesets_prefix,
|
||||
symbol_dynamic_map_name,
|
||||
|
|
@ -217,7 +214,6 @@ enum ProjectIdentifier {
|
|||
define_attribute_encounter,
|
||||
define_metatile_label_prefix,
|
||||
define_heal_locations_prefix,
|
||||
define_spawn_prefix,
|
||||
define_map_prefix,
|
||||
define_map_dynamic,
|
||||
define_map_empty,
|
||||
|
|
@ -250,6 +246,7 @@ enum ProjectFilePath {
|
|||
json_map_groups,
|
||||
json_layouts,
|
||||
json_wild_encounters,
|
||||
json_heal_locations,
|
||||
json_region_map_entries,
|
||||
json_region_porymap_cfg,
|
||||
tilesets_headers,
|
||||
|
|
@ -263,14 +260,12 @@ enum ProjectFilePath {
|
|||
data_obj_event_pic_tables,
|
||||
data_obj_event_gfx,
|
||||
data_pokemon_gfx,
|
||||
data_heal_locations,
|
||||
constants_global,
|
||||
constants_items,
|
||||
constants_flags,
|
||||
constants_vars,
|
||||
constants_weather,
|
||||
constants_songs,
|
||||
constants_heal_locations,
|
||||
constants_pokemon,
|
||||
constants_map_types,
|
||||
constants_trainer_types,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <QPointer>
|
||||
|
||||
#include "orderedjson.h"
|
||||
using OrderedJson = poryjson::Json;
|
||||
|
||||
|
||||
class Project;
|
||||
|
|
@ -125,7 +124,7 @@ public:
|
|||
// standard public methods
|
||||
public:
|
||||
|
||||
virtual Event *duplicate() = 0;
|
||||
virtual Event *duplicate() const = 0;
|
||||
|
||||
void setMap(Map *newMap) { this->map = newMap; }
|
||||
Map *getMap() const { return this->map; }
|
||||
|
|
@ -155,7 +154,7 @@ public:
|
|||
Event::Type getEventType() const { return this->eventType; }
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) = 0;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) = 0;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) = 0;
|
||||
|
||||
virtual void setDefaultValues(Project *project);
|
||||
|
||||
|
|
@ -168,10 +167,10 @@ public:
|
|||
virtual void loadPixmap(Project *project);
|
||||
|
||||
void setPixmap(QPixmap newPixmap) { this->pixmap = newPixmap; }
|
||||
QPixmap getPixmap() { return this->pixmap; }
|
||||
QPixmap getPixmap() const { return this->pixmap; }
|
||||
|
||||
void setPixmapItem(DraggablePixmapItem *item);
|
||||
DraggablePixmapItem *getPixmapItem() { return this->pixmapItem; }
|
||||
DraggablePixmapItem *getPixmapItem() const { return this->pixmapItem; }
|
||||
|
||||
void setUsingSprite(bool newUsingSprite) { this->usingSprite = newUsingSprite; }
|
||||
bool getUsingSprite() const { return this->usingSprite; }
|
||||
|
|
@ -184,6 +183,9 @@ public:
|
|||
|
||||
int getEventIndex();
|
||||
|
||||
void setIdName(QString newIdName) { this->idName = newIdName; }
|
||||
QString getIdName() const { return this->idName; }
|
||||
|
||||
static QString eventGroupToString(Event::Group group);
|
||||
static QString eventTypeToString(Event::Type type);
|
||||
static Event::Type eventTypeFromString(QString type);
|
||||
|
|
@ -206,6 +208,11 @@ protected:
|
|||
int spriteHeight = 16;
|
||||
bool usingSprite = false;
|
||||
|
||||
// Some events can have an associated #define name that should be unique to this event.
|
||||
// e.g. object events can have a 'LOCALID', or Heal Locations have a 'HEAL_LOCATION' id.
|
||||
// When deleting events like this we want to warn the user that the #define may also be deleted.
|
||||
QString idName;
|
||||
|
||||
QMap<QString, QJsonValue> customAttributes;
|
||||
|
||||
QPixmap pixmap;
|
||||
|
|
@ -227,14 +234,14 @@ public:
|
|||
}
|
||||
virtual ~ObjectEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual void accept(EventVisitor *visitor) override { visitor->visitObject(this); }
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
|
|
@ -243,28 +250,28 @@ public:
|
|||
virtual void loadPixmap(Project *project) override;
|
||||
|
||||
void setGfx(QString newGfx) { this->gfx = newGfx; }
|
||||
QString getGfx() { return this->gfx; }
|
||||
QString getGfx() const { return this->gfx; }
|
||||
|
||||
void setMovement(QString newMovement) { this->movement = newMovement; }
|
||||
QString getMovement() { return this->movement; }
|
||||
QString getMovement() const { return this->movement; }
|
||||
|
||||
void setRadiusX(int newRadiusX) { this->radiusX = newRadiusX; }
|
||||
int getRadiusX() { return this->radiusX; }
|
||||
int getRadiusX() const { return this->radiusX; }
|
||||
|
||||
void setRadiusY(int newRadiusY) { this->radiusY = newRadiusY; }
|
||||
int getRadiusY() { return this->radiusY; }
|
||||
int getRadiusY() const { return this->radiusY; }
|
||||
|
||||
void setTrainerType(QString newTrainerType) { this->trainerType = newTrainerType; }
|
||||
QString getTrainerType() { return this->trainerType; }
|
||||
QString getTrainerType() const { return this->trainerType; }
|
||||
|
||||
void setSightRadiusBerryTreeID(QString newValue) { this->sightRadiusBerryTreeID = newValue; }
|
||||
QString getSightRadiusBerryTreeID() { return this->sightRadiusBerryTreeID; }
|
||||
QString getSightRadiusBerryTreeID() const { return this->sightRadiusBerryTreeID; }
|
||||
|
||||
void setScript(QString newScript) { this->script = newScript; }
|
||||
QString getScript() { return this->script; }
|
||||
QString getScript() const { return this->script; }
|
||||
|
||||
void setFlag(QString newFlag) { this->flag = newFlag; }
|
||||
QString getFlag() { return this->flag; }
|
||||
QString getFlag() const { return this->flag; }
|
||||
|
||||
public:
|
||||
void setFrameFromMovement(QString movement);
|
||||
|
|
@ -300,12 +307,12 @@ public:
|
|||
}
|
||||
virtual ~CloneObjectEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
|
|
@ -314,10 +321,10 @@ public:
|
|||
virtual void loadPixmap(Project *project) override;
|
||||
|
||||
void setTargetMap(QString newTargetMap) { this->targetMap = newTargetMap; }
|
||||
QString getTargetMap() { return this->targetMap; }
|
||||
QString getTargetMap() const { return this->targetMap; }
|
||||
|
||||
void setTargetID(int newTargetID) { this->targetID = newTargetID; }
|
||||
int getTargetID() { return this->targetID; }
|
||||
int getTargetID() const { return this->targetID; }
|
||||
|
||||
private:
|
||||
QString targetMap;
|
||||
|
|
@ -338,22 +345,22 @@ public:
|
|||
}
|
||||
virtual ~WarpEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setDestinationMap(QString newDestinationMap) { this->destinationMap = newDestinationMap; }
|
||||
QString getDestinationMap() { return this->destinationMap; }
|
||||
QString getDestinationMap() const { return this->destinationMap; }
|
||||
|
||||
void setDestinationWarpID(QString newDestinationWarpID) { this->destinationWarpID = newDestinationWarpID; }
|
||||
QString getDestinationWarpID() { return this->destinationWarpID; }
|
||||
QString getDestinationWarpID() const { return this->destinationWarpID; }
|
||||
|
||||
void setWarningEnabled(bool enabled);
|
||||
|
||||
|
|
@ -373,12 +380,12 @@ public:
|
|||
CoordEvent() : Event() {}
|
||||
virtual ~CoordEvent() {}
|
||||
|
||||
virtual Event *duplicate() override = 0;
|
||||
virtual Event *duplicate() const override = 0;
|
||||
|
||||
virtual EventFrame *createEventFrame() override = 0;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override = 0;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override = 0;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override = 0;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override = 0;
|
||||
|
||||
|
|
@ -399,27 +406,27 @@ public:
|
|||
}
|
||||
virtual ~TriggerEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual void accept(EventVisitor *visitor) override { visitor->visitTrigger(this); }
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setScriptVar(QString newScriptVar) { this->scriptVar = newScriptVar; }
|
||||
QString getScriptVar() { return this->scriptVar; }
|
||||
QString getScriptVar() const { return this->scriptVar; }
|
||||
|
||||
void setScriptVarValue(QString newScriptVarValue) { this->scriptVarValue = newScriptVarValue; }
|
||||
QString getScriptVarValue() { return this->scriptVarValue; }
|
||||
QString getScriptVarValue() const { return this->scriptVarValue; }
|
||||
|
||||
void setScriptLabel(QString newScriptLabel) { this->scriptLabel = newScriptLabel; }
|
||||
QString getScriptLabel() { return this->scriptLabel; }
|
||||
QString getScriptLabel() const { return this->scriptLabel; }
|
||||
|
||||
private:
|
||||
QString scriptVar;
|
||||
|
|
@ -441,19 +448,19 @@ public:
|
|||
}
|
||||
virtual ~WeatherTriggerEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setWeather(QString newWeather) { this->weather = newWeather; }
|
||||
QString getWeather() { return this->weather; }
|
||||
QString getWeather() const { return this->weather; }
|
||||
|
||||
private:
|
||||
QString weather;
|
||||
|
|
@ -472,12 +479,12 @@ public:
|
|||
}
|
||||
virtual ~BGEvent() {}
|
||||
|
||||
virtual Event *duplicate() override = 0;
|
||||
virtual Event *duplicate() const override = 0;
|
||||
|
||||
virtual EventFrame *createEventFrame() override = 0;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override = 0;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override = 0;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override = 0;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override = 0;
|
||||
|
||||
|
|
@ -497,24 +504,24 @@ public:
|
|||
}
|
||||
virtual ~SignEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual void accept(EventVisitor *visitor) override { visitor->visitSign(this); }
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setFacingDirection(QString newFacingDirection) { this->facingDirection = newFacingDirection; }
|
||||
QString getFacingDirection() { return this->facingDirection; }
|
||||
QString getFacingDirection() const { return this->facingDirection; }
|
||||
|
||||
void setScriptLabel(QString newScriptLabel) { this->scriptLabel = newScriptLabel; }
|
||||
QString getScriptLabel() { return this->scriptLabel; }
|
||||
QString getScriptLabel() const { return this->scriptLabel; }
|
||||
|
||||
private:
|
||||
QString facingDirection;
|
||||
|
|
@ -534,28 +541,28 @@ public:
|
|||
}
|
||||
virtual ~HiddenItemEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setItem(QString newItem) { this->item = newItem; }
|
||||
QString getItem() { return this->item; }
|
||||
QString getItem() const { return this->item; }
|
||||
|
||||
void setFlag(QString newFlag) { this->flag = newFlag; }
|
||||
QString getFlag() { return this->flag; }
|
||||
QString getFlag() const { return this->flag; }
|
||||
|
||||
void setQuantity(int newQuantity) { this->quantity = newQuantity; }
|
||||
int getQuantity() { return this->quantity; }
|
||||
int getQuantity() const { return this->quantity; }
|
||||
|
||||
void setUnderfoot(bool newUnderfoot) { this->underfoot = newUnderfoot; }
|
||||
bool getUnderfoot() { return this->underfoot; }
|
||||
bool getUnderfoot() const { return this->underfoot; }
|
||||
|
||||
private:
|
||||
QString item;
|
||||
|
|
@ -579,19 +586,19 @@ public:
|
|||
}
|
||||
virtual ~SecretBaseEvent() {}
|
||||
|
||||
virtual Event *duplicate() override;
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject json, Project *project) override;
|
||||
virtual bool loadFromJson(const QJsonObject &json, Project *project) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setBaseID(QString newBaseID) { this->baseID = newBaseID; }
|
||||
QString getBaseID() { return this->baseID; }
|
||||
QString getBaseID() const { return this->baseID; }
|
||||
|
||||
private:
|
||||
QString baseID;
|
||||
|
|
@ -611,38 +618,26 @@ public:
|
|||
}
|
||||
virtual ~HealLocationEvent() {}
|
||||
|
||||
virtual Event *duplicate() override { return nullptr; }
|
||||
virtual Event *duplicate() const override;
|
||||
|
||||
virtual EventFrame *createEventFrame() override;
|
||||
|
||||
virtual OrderedJson::object buildEventJson(Project *project) override;
|
||||
virtual bool loadFromJson(QJsonObject, Project *) override { return false; }
|
||||
virtual bool loadFromJson(const QJsonObject &, Project *) override;
|
||||
|
||||
virtual void setDefaultValues(Project *project) override;
|
||||
|
||||
virtual QSet<QString> getExpectedFields() override { return QSet<QString>(); }
|
||||
virtual QSet<QString> getExpectedFields() override;
|
||||
|
||||
void setIndex(int newIndex) { this->index = newIndex; }
|
||||
int getIndex() { return this->index; }
|
||||
void setRespawnMapName(QString newRespawnMapName) { this->respawnMapName = newRespawnMapName; }
|
||||
QString getRespawnMapName() const { return this->respawnMapName; }
|
||||
|
||||
void setLocationName(QString newLocationName) { this->locationName = newLocationName; }
|
||||
QString getLocationName() { return this->locationName; }
|
||||
|
||||
void setIdName(QString newIdName) { this->idName = newIdName; }
|
||||
QString getIdName() { return this->idName; }
|
||||
|
||||
void setRespawnMap(QString newRespawnMap) { this->respawnMap = newRespawnMap; }
|
||||
QString getRespawnMap() { return this->respawnMap; }
|
||||
|
||||
void setRespawnNPC(uint8_t newRespawnNPC) { this->respawnNPC = newRespawnNPC; }
|
||||
uint8_t getRespawnNPC() { return this->respawnNPC; }
|
||||
void setRespawnNPC(QString newRespawnNPC) { this->respawnNPC = newRespawnNPC; }
|
||||
QString getRespawnNPC() const { return this->respawnNPC; }
|
||||
|
||||
private:
|
||||
int index = -1;
|
||||
QString locationName;
|
||||
QString idName;
|
||||
QString respawnMap;
|
||||
uint8_t respawnNPC = 0;
|
||||
QString respawnMapName;
|
||||
QString respawnNPC;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -656,7 +651,7 @@ public:
|
|||
virtual void visitTrigger(TriggerEvent *trigger) override { this->scripts << trigger->getScriptLabel(); };
|
||||
virtual void visitSign(SignEvent *sign) override { this->scripts << sign->getScriptLabel(); };
|
||||
|
||||
QStringList getScripts() { return this->scripts; }
|
||||
QStringList getScripts() const { return this->scripts; }
|
||||
|
||||
private:
|
||||
QStringList scripts;
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
#pragma once
|
||||
#ifndef HEALLOCATION_H
|
||||
#define HEALLOCATION_H
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
class Event;
|
||||
|
||||
class HealLocation {
|
||||
|
||||
public:
|
||||
HealLocation()=default;
|
||||
HealLocation(QString, QString, int, int16_t, int16_t, QString = "", uint8_t = 1);
|
||||
friend QDebug operator<<(QDebug debug, const HealLocation &hl);
|
||||
|
||||
public:
|
||||
QString idName;
|
||||
QString mapName;
|
||||
int index;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
QString respawnMap;
|
||||
uint8_t respawnNPC;
|
||||
static HealLocation fromEvent(Event *);
|
||||
};
|
||||
|
||||
#endif // HEALLOCATION_H
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
#ifndef PARSEUTIL_H
|
||||
#define PARSEUTIL_H
|
||||
|
||||
#include "heallocation.h"
|
||||
#include "log.h"
|
||||
#include "orderedjson.h"
|
||||
#include "orderedmap.h"
|
||||
|
|
@ -78,11 +77,11 @@ public:
|
|||
static QString removeLineComments(QString text, const QStringList &commentSymbols);
|
||||
|
||||
static QStringList splitShellCommand(QStringView command);
|
||||
static int gameStringToInt(QString gameString, bool * ok = nullptr);
|
||||
static bool gameStringToBool(QString gameString, bool * ok = nullptr);
|
||||
static QString jsonToQString(QJsonValue value, bool * ok = nullptr);
|
||||
static int jsonToInt(QJsonValue value, bool * ok = nullptr);
|
||||
static bool jsonToBool(QJsonValue value, bool * ok = nullptr);
|
||||
static int gameStringToInt(const QString &gameString, bool * ok = nullptr);
|
||||
static bool gameStringToBool(const QString &gameString, bool * ok = nullptr);
|
||||
static QString jsonToQString(const QJsonValue &value, bool * ok = nullptr);
|
||||
static int jsonToInt(const QJsonValue &value, bool * ok = nullptr);
|
||||
static bool jsonToBool(const QJsonValue &value, bool * ok = nullptr);
|
||||
|
||||
private:
|
||||
QString root;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ public:
|
|||
|
||||
public:
|
||||
Ui::MainWindow* ui;
|
||||
QObject *parent = nullptr;
|
||||
|
||||
QPointer<Project> project = nullptr;
|
||||
QPointer<Map> map = nullptr;
|
||||
|
|
|
|||
|
|
@ -247,4 +247,7 @@ protected:
|
|||
|
||||
} // namespace poryjson
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
#endif // ORDERED_JSON_H
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "map.h"
|
||||
#include "blockdata.h"
|
||||
#include "heallocation.h"
|
||||
#include "wildmoninfo.h"
|
||||
#include "parseutil.h"
|
||||
#include "orderedjson.h"
|
||||
|
|
@ -35,8 +34,8 @@ public:
|
|||
QStringList mapNames;
|
||||
QStringList groupNames;
|
||||
QMap<QString, QStringList> groupNameToMapNames;
|
||||
QList<HealLocation> healLocations;
|
||||
QMap<QString, int> healLocationNameToValue;
|
||||
QStringList healLocationSaveOrder;
|
||||
QMap<QString, QList<HealLocationEvent*>> healLocations;
|
||||
QMap<QString, QString> mapConstantsToMapNames;
|
||||
QMap<QString, QString> mapNamesToMapConstants;
|
||||
QMap<QString, QString> mapNameToLayoutId;
|
||||
|
|
@ -87,15 +86,7 @@ public:
|
|||
void clearTilesetCache();
|
||||
void clearMapLayouts();
|
||||
void clearEventGraphics();
|
||||
|
||||
struct DataQualifiers
|
||||
{
|
||||
bool isStatic;
|
||||
bool isConst;
|
||||
};
|
||||
DataQualifiers getDataQualifiers(QString, QString);
|
||||
DataQualifiers healLocationDataQualifiers;
|
||||
QString healLocationsTableName;
|
||||
void clearHealLocations();
|
||||
|
||||
bool sanityCheck();
|
||||
bool load();
|
||||
|
|
@ -142,7 +133,8 @@ public:
|
|||
bool isIdentifierUnique(const QString &identifier) const;
|
||||
bool isValidNewIdentifier(QString identifier) const;
|
||||
QString toUniqueIdentifier(const QString &identifier) const;
|
||||
QString getProjectTitle();
|
||||
QString getProjectTitle() const;
|
||||
QString getNewHealLocationName(const Map* map) const;
|
||||
|
||||
bool readWildMonData();
|
||||
tsl::ordered_map<QString, tsl::ordered_map<QString, WildPokemonHeader>> wildMonData;
|
||||
|
|
@ -187,7 +179,7 @@ public:
|
|||
void saveMapGroups();
|
||||
void saveRegionMapSections();
|
||||
void saveWildMonData();
|
||||
void saveHealLocations(Map*);
|
||||
void saveHealLocations();
|
||||
void saveTilesets(Tileset*, Tileset*);
|
||||
void saveTilesetMetatileLabels(Tileset*, Tileset*);
|
||||
void appendTilesetLabel(const QString &label, const QString &isSecondaryStr);
|
||||
|
|
@ -207,7 +199,6 @@ public:
|
|||
bool readBgEventFacingDirections();
|
||||
bool readTrainerTypes();
|
||||
bool readMetatileBehaviors();
|
||||
bool readHealLocationConstants();
|
||||
bool readHealLocations();
|
||||
bool readMiscellaneousConstants();
|
||||
bool readEventScriptLabels();
|
||||
|
|
@ -267,9 +258,6 @@ private:
|
|||
void setNewLayoutBlockdata(Layout *layout);
|
||||
void setNewLayoutBorder(Layout *layout);
|
||||
|
||||
void saveHealLocationsData(Map *map);
|
||||
void saveHealLocationsConstants();
|
||||
|
||||
void ignoreWatchedFileTemporarily(QString filepath);
|
||||
|
||||
static int num_tiles_primary;
|
||||
|
|
|
|||
|
|
@ -267,10 +267,11 @@ public:
|
|||
virtual void populate(Project *project) override;
|
||||
|
||||
public:
|
||||
QLineEdit *line_edit_id;
|
||||
QFrame *hideable_respawn_map;
|
||||
QFrame *hideable_respawn_npc;
|
||||
NoScrollComboBox *combo_respawn_map;
|
||||
NoScrollSpinBox *spinner_respawn_npc;
|
||||
NoScrollComboBox *combo_respawn_npc;
|
||||
|
||||
private:
|
||||
HealLocationEvent *healLocation;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ private:
|
|||
bool positionIsValid(const QPoint &pos) const;
|
||||
bool selectionIsValid();
|
||||
void hoverChanged();
|
||||
int numPrimaryMetatilesRounded() const;
|
||||
|
||||
signals:
|
||||
void hoveredMetatileSelectionChanged(uint16_t);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ private:
|
|||
void drawCounts();
|
||||
QImage buildAllMetatilesImage();
|
||||
QImage buildImage(int metatileIdStart, int numMetatiles);
|
||||
int numPrimaryMetatilesRounded() const;
|
||||
|
||||
signals:
|
||||
void hoveredMetatileChanged(uint16_t);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ SOURCES += src/core/advancemapparser.cpp \
|
|||
src/core/blockdata.cpp \
|
||||
src/core/events.cpp \
|
||||
src/core/filedialog.cpp \
|
||||
src/core/heallocation.cpp \
|
||||
src/core/imageexport.cpp \
|
||||
src/core/map.cpp \
|
||||
src/core/mapconnection.cpp \
|
||||
|
|
@ -151,7 +150,6 @@ HEADERS += include/core/advancemapparser.h \
|
|||
include/core/blockdata.h \
|
||||
include/core/events.h \
|
||||
include/core/filedialog.h \
|
||||
include/core/heallocation.h \
|
||||
include/core/history.h \
|
||||
include/core/imageexport.h \
|
||||
include/core/map.h \
|
||||
|
|
|
|||
|
|
@ -75,11 +75,6 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
|
|||
{ProjectIdentifier::symbol_obj_event_gfx_pointers, {"symbol_obj_event_gfx_pointers", "gObjectEventGraphicsInfoPointers"}},
|
||||
{ProjectIdentifier::symbol_pokemon_icon_table, {"symbol_pokemon_icon_table", "gMonIconTable"}},
|
||||
{ProjectIdentifier::symbol_wild_encounters, {"symbol_wild_encounters", "gWildMonHeaders"}},
|
||||
{ProjectIdentifier::symbol_heal_locations_type, {"symbol_heal_locations_type", "struct HealLocation"}},
|
||||
{ProjectIdentifier::symbol_heal_locations, {"symbol_heal_locations", "sHealLocations"}},
|
||||
{ProjectIdentifier::symbol_spawn_points, {"symbol_spawn_points", "sSpawnPoints"}},
|
||||
{ProjectIdentifier::symbol_spawn_maps, {"symbol_spawn_maps", "u16 sWhiteoutRespawnHealCenterMapIdxs"}},
|
||||
{ProjectIdentifier::symbol_spawn_npcs, {"symbol_spawn_npcs", "u8 sWhiteoutRespawnHealerNpcIds"}},
|
||||
{ProjectIdentifier::symbol_attribute_table, {"symbol_attribute_table", "sMetatileAttrMasks"}},
|
||||
{ProjectIdentifier::symbol_tilesets_prefix, {"symbol_tilesets_prefix", "gTileset_"}},
|
||||
{ProjectIdentifier::symbol_dynamic_map_name, {"symbol_dynamic_map_name", "Dynamic"}},
|
||||
|
|
@ -106,7 +101,6 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
|
|||
{ProjectIdentifier::define_attribute_encounter, {"define_attribute_encounter", "METATILE_ATTRIBUTE_ENCOUNTER_TYPE"}},
|
||||
{ProjectIdentifier::define_metatile_label_prefix, {"define_metatile_label_prefix", "METATILE_"}},
|
||||
{ProjectIdentifier::define_heal_locations_prefix, {"define_heal_locations_prefix", "HEAL_LOCATION_"}},
|
||||
{ProjectIdentifier::define_spawn_prefix, {"define_spawn_prefix", "SPAWN_"}},
|
||||
{ProjectIdentifier::define_map_prefix, {"define_map_prefix", "MAP_"}},
|
||||
{ProjectIdentifier::define_map_dynamic, {"define_map_dynamic", "DYNAMIC"}},
|
||||
{ProjectIdentifier::define_map_empty, {"define_map_empty", "UNDEFINED"}},
|
||||
|
|
@ -140,6 +134,7 @@ const QMap<ProjectFilePath, QPair<QString, QString>> ProjectConfig::defaultPaths
|
|||
{ProjectFilePath::json_map_groups, { "json_map_groups", "data/maps/map_groups.json"}},
|
||||
{ProjectFilePath::json_layouts, { "json_layouts", "data/layouts/layouts.json"}},
|
||||
{ProjectFilePath::json_wild_encounters, { "json_wild_encounters", "src/data/wild_encounters.json"}},
|
||||
{ProjectFilePath::json_heal_locations, { "json_heal_locations", "src/data/heal_locations.json"}},
|
||||
{ProjectFilePath::json_region_map_entries, { "json_region_map_entries", "src/data/region_map/region_map_sections.json"}},
|
||||
{ProjectFilePath::json_region_porymap_cfg, { "json_region_porymap_cfg", "src/data/region_map/porymap_config.json"}},
|
||||
{ProjectFilePath::tilesets_headers, { "tilesets_headers", "src/data/tilesets/headers.h"}},
|
||||
|
|
@ -153,14 +148,12 @@ const QMap<ProjectFilePath, QPair<QString, QString>> ProjectConfig::defaultPaths
|
|||
{ProjectFilePath::data_obj_event_pic_tables, { "data_obj_event_pic_tables", "src/data/object_events/object_event_pic_tables.h"}},
|
||||
{ProjectFilePath::data_obj_event_gfx, { "data_obj_event_gfx", "src/data/object_events/object_event_graphics.h"}},
|
||||
{ProjectFilePath::data_pokemon_gfx, { "data_pokemon_gfx", "src/data/graphics/pokemon.h"}},
|
||||
{ProjectFilePath::data_heal_locations, { "data_heal_locations", "src/data/heal_locations.h"}},
|
||||
{ProjectFilePath::constants_global, { "constants_global", "include/constants/global.h"}},
|
||||
{ProjectFilePath::constants_items, { "constants_items", "include/constants/items.h"}},
|
||||
{ProjectFilePath::constants_flags, { "constants_flags", "include/constants/flags.h"}},
|
||||
{ProjectFilePath::constants_vars, { "constants_vars", "include/constants/vars.h"}},
|
||||
{ProjectFilePath::constants_weather, { "constants_weather", "include/constants/weather.h"}},
|
||||
{ProjectFilePath::constants_songs, { "constants_songs", "include/constants/songs.h"}},
|
||||
{ProjectFilePath::constants_heal_locations, { "constants_heal_locations", "include/constants/heal_locations.h"}},
|
||||
{ProjectFilePath::constants_pokemon, { "constants_pokemon", "include/constants/pokemon.h"}},
|
||||
{ProjectFilePath::constants_map_types, { "constants_map_types", "include/constants/map_types.h"}},
|
||||
{ProjectFilePath::constants_trainer_types, { "constants_trainer_types", "include/constants/trainer_types.h"}},
|
||||
|
|
@ -399,6 +392,8 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
this->projectSettingsTab = getConfigInteger(key, value, 0);
|
||||
} else if (key == "warp_behavior_warning_disabled") {
|
||||
this->warpBehaviorWarningDisabled = getConfigBool(key, value);
|
||||
} else if (key == "event_delete_warning_disabled") {
|
||||
this->eventDeleteWarningDisabled = getConfigBool(key, value);
|
||||
} else if (key == "check_for_updates") {
|
||||
this->checkForUpdates = getConfigBool(key, value);
|
||||
} else if (key == "last_update_check_time") {
|
||||
|
|
@ -483,6 +478,7 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
|
|||
map.insert("palette_editor_bit_depth", QString::number(this->paletteEditorBitDepth));
|
||||
map.insert("project_settings_tab", QString::number(this->projectSettingsTab));
|
||||
map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled));
|
||||
map.insert("event_delete_warning_disabled", QString::number(this->eventDeleteWarningDisabled));
|
||||
map.insert("check_for_updates", QString::number(this->checkForUpdates));
|
||||
map.insert("last_update_check_time", this->lastUpdateCheckTime.toUTC().toString());
|
||||
map.insert("last_update_check_version", this->lastUpdateCheckVersion.toString());
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ QString Event::eventGroupToString(Event::Group group) {
|
|||
case Event::Group::Bg:
|
||||
return "BG";
|
||||
case Event::Group::Heal:
|
||||
return "Healspot";
|
||||
return "Heal Location";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ QString Event::eventTypeToString(Event::Type type) {
|
|||
case Event::Type::SecretBase:
|
||||
return "event_secret_base";
|
||||
case Event::Type::HealLocation:
|
||||
return "event_healspot";
|
||||
return "event_heal_location";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ Event::Type Event::eventTypeFromString(QString type) {
|
|||
return Event::Type::HiddenItem;
|
||||
} else if (type == "event_secret_base") {
|
||||
return Event::Type::SecretBase;
|
||||
} else if (type == "event_healspot") {
|
||||
} else if (type == "event_heal_location") {
|
||||
return Event::Type::HealLocation;
|
||||
} else {
|
||||
return Event::Type::None;
|
||||
|
|
@ -183,7 +183,7 @@ void Event::setIcons() {
|
|||
}
|
||||
|
||||
|
||||
Event *ObjectEvent::duplicate() {
|
||||
Event *ObjectEvent::duplicate() const {
|
||||
ObjectEvent *copy = new ObjectEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -232,7 +232,7 @@ OrderedJson::object ObjectEvent::buildEventJson(Project *) {
|
|||
return objectJson;
|
||||
}
|
||||
|
||||
bool ObjectEvent::loadFromJson(QJsonObject json, Project *) {
|
||||
bool ObjectEvent::loadFromJson(const QJsonObject &json, Project *) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -362,7 +362,7 @@ void ObjectEvent::setFrameFromMovement(QString facingDir) {
|
|||
|
||||
|
||||
|
||||
Event *CloneObjectEvent::duplicate() {
|
||||
Event *CloneObjectEvent::duplicate() const {
|
||||
CloneObjectEvent *copy = new CloneObjectEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -399,7 +399,7 @@ OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) {
|
|||
return cloneJson;
|
||||
}
|
||||
|
||||
bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) {
|
||||
bool CloneObjectEvent::loadFromJson(const QJsonObject &json, Project *project) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setGfx(ParseUtil::jsonToQString(json["graphics_id"]));
|
||||
|
|
@ -469,7 +469,7 @@ void CloneObjectEvent::loadPixmap(Project *project) {
|
|||
|
||||
|
||||
|
||||
Event *WarpEvent::duplicate() {
|
||||
Event *WarpEvent::duplicate() const {
|
||||
WarpEvent *copy = new WarpEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -506,7 +506,7 @@ OrderedJson::object WarpEvent::buildEventJson(Project *project) {
|
|||
return warpJson;
|
||||
}
|
||||
|
||||
bool WarpEvent::loadFromJson(QJsonObject json, Project *project) {
|
||||
bool WarpEvent::loadFromJson(const QJsonObject &json, Project *project) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -550,7 +550,7 @@ void WarpEvent::setWarningEnabled(bool enabled) {
|
|||
|
||||
|
||||
|
||||
Event *TriggerEvent::duplicate() {
|
||||
Event *TriggerEvent::duplicate() const {
|
||||
TriggerEvent *copy = new TriggerEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -589,7 +589,7 @@ OrderedJson::object TriggerEvent::buildEventJson(Project *) {
|
|||
return triggerJson;
|
||||
}
|
||||
|
||||
bool TriggerEvent::loadFromJson(QJsonObject json, Project *) {
|
||||
bool TriggerEvent::loadFromJson(const QJsonObject &json, Project *) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -626,7 +626,7 @@ QSet<QString> TriggerEvent::getExpectedFields() {
|
|||
|
||||
|
||||
|
||||
Event *WeatherTriggerEvent::duplicate() {
|
||||
Event *WeatherTriggerEvent::duplicate() const {
|
||||
WeatherTriggerEvent *copy = new WeatherTriggerEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -661,7 +661,7 @@ OrderedJson::object WeatherTriggerEvent::buildEventJson(Project *) {
|
|||
return weatherJson;
|
||||
}
|
||||
|
||||
bool WeatherTriggerEvent::loadFromJson(QJsonObject json, Project *) {
|
||||
bool WeatherTriggerEvent::loadFromJson(const QJsonObject &json, Project *) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -692,7 +692,7 @@ QSet<QString> WeatherTriggerEvent::getExpectedFields() {
|
|||
|
||||
|
||||
|
||||
Event *SignEvent::duplicate() {
|
||||
Event *SignEvent::duplicate() const {
|
||||
SignEvent *copy = new SignEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -729,7 +729,7 @@ OrderedJson::object SignEvent::buildEventJson(Project *) {
|
|||
return signJson;
|
||||
}
|
||||
|
||||
bool SignEvent::loadFromJson(QJsonObject json, Project *) {
|
||||
bool SignEvent::loadFromJson(const QJsonObject &json, Project *) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -763,7 +763,7 @@ QSet<QString> SignEvent::getExpectedFields() {
|
|||
|
||||
|
||||
|
||||
Event *HiddenItemEvent::duplicate() {
|
||||
Event *HiddenItemEvent::duplicate() const {
|
||||
HiddenItemEvent *copy = new HiddenItemEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -808,7 +808,7 @@ OrderedJson::object HiddenItemEvent::buildEventJson(Project *) {
|
|||
return hiddenItemJson;
|
||||
}
|
||||
|
||||
bool HiddenItemEvent::loadFromJson(QJsonObject json, Project *) {
|
||||
bool HiddenItemEvent::loadFromJson(const QJsonObject &json, Project *) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -859,7 +859,7 @@ QSet<QString> HiddenItemEvent::getExpectedFields() {
|
|||
|
||||
|
||||
|
||||
Event *SecretBaseEvent::duplicate() {
|
||||
Event *SecretBaseEvent::duplicate() const {
|
||||
SecretBaseEvent *copy = new SecretBaseEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
|
|
@ -894,7 +894,7 @@ OrderedJson::object SecretBaseEvent::buildEventJson(Project *) {
|
|||
return secretBaseJson;
|
||||
}
|
||||
|
||||
bool SecretBaseEvent::loadFromJson(QJsonObject json, Project *) {
|
||||
bool SecretBaseEvent::loadFromJson(const QJsonObject &json, Project *) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
|
||||
|
|
@ -925,6 +925,20 @@ QSet<QString> SecretBaseEvent::getExpectedFields() {
|
|||
|
||||
|
||||
|
||||
Event *HealLocationEvent::duplicate() const {
|
||||
HealLocationEvent *copy = new HealLocationEvent();
|
||||
|
||||
copy->setX(this->getX());
|
||||
copy->setY(this->getY());
|
||||
copy->setIdName(this->getIdName());
|
||||
copy->setRespawnMapName(this->getRespawnMapName());
|
||||
copy->setRespawnNPC(this->getRespawnNPC());
|
||||
|
||||
copy->setCustomAttributes(this->getCustomAttributes());
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
EventFrame *HealLocationEvent::createEventFrame() {
|
||||
if (!this->eventFrame) {
|
||||
this->eventFrame = new HealLocationFrame(this);
|
||||
|
|
@ -933,22 +947,62 @@ EventFrame *HealLocationEvent::createEventFrame() {
|
|||
return this->eventFrame;
|
||||
}
|
||||
|
||||
OrderedJson::object HealLocationEvent::buildEventJson(Project *) {
|
||||
return OrderedJson::object();
|
||||
}
|
||||
OrderedJson::object HealLocationEvent::buildEventJson(Project *project) {
|
||||
OrderedJson::object healLocationJson;
|
||||
|
||||
void HealLocationEvent::setDefaultValues(Project *) {
|
||||
this->setElevation(projectConfig.defaultElevation);
|
||||
if (!this->map)
|
||||
return;
|
||||
|
||||
bool respawnEnabled = projectConfig.healLocationRespawnDataEnabled;
|
||||
const QString prefix = projectConfig.getIdentifier(respawnEnabled ? ProjectIdentifier::define_spawn_prefix
|
||||
: ProjectIdentifier::define_heal_locations_prefix);
|
||||
this->setLocationName(this->map->constantName());
|
||||
this->setIdName(prefix + this->map->constantName());
|
||||
if (respawnEnabled) {
|
||||
this->setRespawnMap(this->map->name());
|
||||
this->setRespawnNPC(1);
|
||||
healLocationJson["id"] = this->getIdName();
|
||||
// This field doesn't need to be stored in the Event itself, so it's output only.
|
||||
healLocationJson["map"] = this->getMap() ? this->getMap()->constantName() : QString();
|
||||
healLocationJson["x"] = this->getX();
|
||||
healLocationJson["y"] = this->getY();
|
||||
if (projectConfig.healLocationRespawnDataEnabled) {
|
||||
const QString mapName = this->getRespawnMapName();
|
||||
healLocationJson["respawn_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
|
||||
healLocationJson["respawn_npc"] = this->getRespawnNPC();
|
||||
}
|
||||
|
||||
this->addCustomAttributesTo(&healLocationJson);
|
||||
|
||||
return healLocationJson;
|
||||
}
|
||||
|
||||
bool HealLocationEvent::loadFromJson(const QJsonObject &json, Project *project) {
|
||||
this->setX(ParseUtil::jsonToInt(json["x"]));
|
||||
this->setY(ParseUtil::jsonToInt(json["y"]));
|
||||
this->setIdName(ParseUtil::jsonToQString(json["id"]));
|
||||
|
||||
if (projectConfig.healLocationRespawnDataEnabled) {
|
||||
// Log a warning if "respawn_map" isn't a known map ID, but don't overwrite user data.
|
||||
const QString mapConstant = ParseUtil::jsonToQString(json["respawn_map"]);
|
||||
if (!project->mapConstantsToMapNames.contains(mapConstant))
|
||||
logWarn(QString("Unknown Respawn Map constant '%1'.").arg(mapConstant));
|
||||
this->setRespawnMapName(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
|
||||
this->setRespawnNPC(ParseUtil::jsonToQString(json["respawn_npc"]));
|
||||
}
|
||||
|
||||
this->readCustomAttributes(json);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HealLocationEvent::setDefaultValues(Project *project) {
|
||||
if (this->map) {
|
||||
this->setIdName(project->getNewHealLocationName(this->map));
|
||||
this->setRespawnMapName(this->map->name());
|
||||
}
|
||||
this->setRespawnNPC(QString::number(0 + this->getIndexOffset(Event::Group::Object)));
|
||||
}
|
||||
|
||||
const QSet<QString> expectedHealLocationFields = {
|
||||
"id",
|
||||
"map"
|
||||
};
|
||||
|
||||
QSet<QString> HealLocationEvent::getExpectedFields() {
|
||||
QSet<QString> expectedFields = expectedHealLocationFields;
|
||||
if (projectConfig.healLocationRespawnDataEnabled) {
|
||||
expectedFields.insert("respawn_map");
|
||||
expectedFields.insert("respawn_npc");
|
||||
}
|
||||
expectedFields << "x" << "y";
|
||||
return expectedFields;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
#include "heallocation.h"
|
||||
#include "config.h"
|
||||
#include "events.h"
|
||||
#include "map.h"
|
||||
|
||||
// TODO: Remove
|
||||
|
||||
HealLocation::HealLocation(QString id, QString map,
|
||||
int i, int16_t x, int16_t y,
|
||||
QString respawnMap, uint8_t respawnNPC) {
|
||||
this->idName = id;
|
||||
this->mapName = map;
|
||||
this->index = i;
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->respawnMap = respawnMap;
|
||||
this->respawnNPC = respawnNPC;
|
||||
}
|
||||
|
||||
HealLocation HealLocation::fromEvent(Event *fromEvent) {
|
||||
HealLocationEvent *event = dynamic_cast<HealLocationEvent *>(fromEvent);
|
||||
|
||||
HealLocation healLocation;
|
||||
healLocation.idName = event->getIdName();
|
||||
healLocation.mapName = event->getLocationName();
|
||||
healLocation.index = event->getIndex();
|
||||
healLocation.x = event->getX();
|
||||
healLocation.y = event->getY();
|
||||
if (projectConfig.healLocationRespawnDataEnabled) {
|
||||
healLocation.respawnNPC = event->getRespawnNPC();
|
||||
healLocation.respawnMap = Map::mapConstantFromName(event->getRespawnMap(), false);
|
||||
}
|
||||
return healLocation;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const HealLocation &healLocation) {
|
||||
debug << "HealLocation_" + healLocation.mapName << "(" << healLocation.x << ',' << healLocation.y << ")";
|
||||
return debug;
|
||||
}
|
||||
|
|
@ -35,13 +35,8 @@ Map::Map(const Map &other, QObject *parent) : Map(parent) {
|
|||
|
||||
// Copy events
|
||||
for (auto i = other.m_events.constBegin(); i != other.m_events.constEnd(); i++) {
|
||||
QList<Event*> newEvents;
|
||||
for (const auto &event : i.value()) {
|
||||
auto newEvent = event->duplicate();
|
||||
m_ownedEvents.insert(newEvent);
|
||||
newEvents.append(newEvent);
|
||||
}
|
||||
m_events[i.key()] = newEvents;
|
||||
for (const auto &event : i.value())
|
||||
addEvent(event->duplicate());
|
||||
}
|
||||
|
||||
// Duplicating the map connections is probably not desirable, so we skip them.
|
||||
|
|
|
|||
|
|
@ -177,8 +177,6 @@ void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bo
|
|||
}
|
||||
|
||||
void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata) {
|
||||
int oldWidth = this->width;
|
||||
int oldHeight = this->height;
|
||||
int newWidth = this->width + margins.left() + margins.right();
|
||||
int newHeight = this->height + margins.top() + margins.bottom();
|
||||
|
||||
|
|
@ -190,7 +188,7 @@ void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata) {
|
|||
if ((x < margins.left()) || (x >= newWidth - margins.right()) || (y < margins.top()) || (y >= newHeight - margins.bottom())) {
|
||||
newBlockdata.append(0);
|
||||
} else {
|
||||
int index = (y - margins.top()) * oldWidth + (x - margins.left());
|
||||
int index = (y - margins.top()) * this->width + (x - margins.left());
|
||||
newBlockdata.append(this->blockdata.value(index));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ static const QMap<QString, int> globalDefineValues = {
|
|||
{"UINT_MAX", UINT_MAX},
|
||||
};
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
|
||||
ParseUtil::ParseUtil() { }
|
||||
|
||||
void ParseUtil::set_root(const QString &dir) {
|
||||
|
|
@ -580,7 +578,7 @@ QMap<QString, QString> ParseUtil::readNamedIndexCArray(const QString &filename,
|
|||
return map;
|
||||
}
|
||||
|
||||
int ParseUtil::gameStringToInt(QString gameString, bool * ok) {
|
||||
int ParseUtil::gameStringToInt(const QString &gameString, bool * ok) {
|
||||
if (ok) *ok = true;
|
||||
if (QString::compare(gameString, "TRUE", Qt::CaseInsensitive) == 0)
|
||||
return 1;
|
||||
|
|
@ -589,7 +587,7 @@ int ParseUtil::gameStringToInt(QString gameString, bool * ok) {
|
|||
return gameString.toInt(ok, 0);
|
||||
}
|
||||
|
||||
bool ParseUtil::gameStringToBool(QString gameString, bool * ok) {
|
||||
bool ParseUtil::gameStringToBool(const QString &gameString, bool * ok) {
|
||||
return gameStringToInt(gameString, ok) != 0;
|
||||
}
|
||||
|
||||
|
|
@ -705,7 +703,7 @@ bool ParseUtil::ensureFieldsExist(const QJsonObject &obj, const QList<QString> &
|
|||
// QJsonValues are strictly typed, and so will not attempt any implicit conversions.
|
||||
// The below functions are for attempting to convert a JSON value read from the user's
|
||||
// project to a QString, int, or bool (whichever Porymap expects).
|
||||
QString ParseUtil::jsonToQString(QJsonValue value, bool * ok) {
|
||||
QString ParseUtil::jsonToQString(const QJsonValue &value, bool * ok) {
|
||||
if (ok) *ok = true;
|
||||
switch (value.type())
|
||||
{
|
||||
|
|
@ -718,7 +716,7 @@ QString ParseUtil::jsonToQString(QJsonValue value, bool * ok) {
|
|||
return QString();
|
||||
}
|
||||
|
||||
int ParseUtil::jsonToInt(QJsonValue value, bool * ok) {
|
||||
int ParseUtil::jsonToInt(const QJsonValue &value, bool * ok) {
|
||||
if (ok) *ok = true;
|
||||
switch (value.type())
|
||||
{
|
||||
|
|
@ -731,7 +729,7 @@ int ParseUtil::jsonToInt(QJsonValue value, bool * ok) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool ParseUtil::jsonToBool(QJsonValue value, bool * ok) {
|
||||
bool ParseUtil::jsonToBool(const QJsonValue &value, bool * ok) {
|
||||
if (ok) *ok = true;
|
||||
switch (value.type())
|
||||
{
|
||||
|
|
|
|||
121
src/editor.cpp
121
src/editor.cpp
|
|
@ -12,6 +12,7 @@
|
|||
#include "scripting.h"
|
||||
#include "customattributesframe.h"
|
||||
#include "validator.h"
|
||||
#include "message.h"
|
||||
#include <QCheckBox>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
|
|
@ -1366,13 +1367,11 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *i
|
|||
if (this->selected_events->size() > 0)
|
||||
eventType = this->selected_events->first()->event->getEventType();
|
||||
|
||||
if (eventType != Event::Type::HealLocation) {
|
||||
DraggablePixmapItem *newEvent = addNewEvent(eventType);
|
||||
if (newEvent) {
|
||||
newEvent->move(pos.x(), pos.y());
|
||||
emit eventsChanged();
|
||||
selectMapEvent(newEvent);
|
||||
}
|
||||
DraggablePixmapItem *newEvent = addNewEvent(eventType);
|
||||
if (newEvent) {
|
||||
newEvent->move(pos.x(), pos.y());
|
||||
emit eventsChanged();
|
||||
selectMapEvent(newEvent);
|
||||
}
|
||||
}
|
||||
} else if (eventEditAction == EditAction::Select) {
|
||||
|
|
@ -2112,15 +2111,7 @@ void Editor::duplicateSelectedEvents() {
|
|||
logWarn(QString("Skipping duplication, the map limit for events of type '%1' has been reached.").arg(Event::eventTypeToString(eventType)));
|
||||
continue;
|
||||
}
|
||||
if (eventType == Event::Type::HealLocation) {
|
||||
logWarn("Skipping duplication, event is a heal location.");
|
||||
continue;
|
||||
}
|
||||
Event *duplicate = original->duplicate();
|
||||
if (!duplicate) {
|
||||
logError("Encountered a problem duplicating an event.");
|
||||
continue;
|
||||
}
|
||||
duplicate->setX(duplicate->getX() + 1);
|
||||
duplicate->setY(duplicate->getY() + 1);
|
||||
selectedEvents.append(duplicate);
|
||||
|
|
@ -2138,13 +2129,6 @@ DraggablePixmapItem *Editor::addNewEvent(Event::Type type) {
|
|||
|
||||
event->setMap(this->map);
|
||||
event->setDefaultValues(this->project);
|
||||
|
||||
if (type == Event::Type::HealLocation) {
|
||||
HealLocation healLocation = HealLocation::fromEvent(event);
|
||||
project->healLocations.append(healLocation);
|
||||
((HealLocationEvent *)event)->setIndex(project->healLocations.length());
|
||||
}
|
||||
|
||||
map->commit(new EventCreate(this, map, event));
|
||||
return event->getPixmapItem();
|
||||
}
|
||||
|
|
@ -2162,42 +2146,73 @@ void Editor::deleteSelectedEvents() {
|
|||
if (!this->selected_events || this->selected_events->length() == 0 || !this->map || this->editMode != EditMode::Events)
|
||||
return;
|
||||
|
||||
DraggablePixmapItem *nextSelectedEvent = nullptr;
|
||||
QList<Event *> selectedEvents;
|
||||
int numDeleted = 0;
|
||||
QList<Event*> eventsToDelete;
|
||||
bool skipWarning = porymapConfig.eventDeleteWarningDisabled;
|
||||
for (DraggablePixmapItem *item : *this->selected_events) {
|
||||
Event::Group event_group = item->event->getEventGroup();
|
||||
if (event_group != Event::Group::Heal) {
|
||||
numDeleted++;
|
||||
item->event->setPixmapItem(item);
|
||||
selectedEvents.append(item->event);
|
||||
}
|
||||
else { // don't allow deletion of heal locations
|
||||
logWarn(QString("Cannot delete event of type '%1'").arg(Event::eventTypeToString(item->event->getEventType())));
|
||||
}
|
||||
}
|
||||
if (numDeleted) {
|
||||
// Get the index for the event that should be selected after this event has been deleted.
|
||||
// Select event at next smallest index when deleting a single event.
|
||||
// If deleting multiple events, just let editor work out next selected.
|
||||
if (numDeleted == 1) {
|
||||
Event::Group event_group = selectedEvents[0]->getEventGroup();
|
||||
int index = this->map->getIndexOfEvent(selectedEvents[0]);
|
||||
if (index != this->map->getNumEvents(event_group) - 1)
|
||||
index++;
|
||||
else
|
||||
index--;
|
||||
Event *event = this->map->getEvent(event_group, index);
|
||||
for (QGraphicsItem *child : this->events_group->childItems()) {
|
||||
DraggablePixmapItem *event_item = static_cast<DraggablePixmapItem *>(child);
|
||||
if (event_item->event == event) {
|
||||
nextSelectedEvent = event_item;
|
||||
break;
|
||||
Event* event = item->event;
|
||||
const QString idName = event->getIdName();
|
||||
if (skipWarning || idName.isEmpty()) {
|
||||
eventsToDelete.append(event);
|
||||
} else {
|
||||
// If an event with a ID #define is deleted, its ID is also deleted (by the user's project, not Porymap).
|
||||
// Warn the user about this and give them a chance to abort.
|
||||
WarningMessage msgBox(QStringLiteral("Deleting this event may also delete the constant listed below. This can stop your project from compiling.\n\n"
|
||||
"Are you sure you want to delete this event?"),
|
||||
ui->graphicsView_Map);
|
||||
msgBox.setInformativeText(idName);
|
||||
msgBox.setIconPixmap(event->getPixmap());
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setDefaultButton(QMessageBox::Cancel);
|
||||
msgBox.addButton(QStringLiteral("Delete"), QMessageBox::DestructiveRole);
|
||||
msgBox.setCheckBox(new QCheckBox(QStringLiteral("Don't warn me again")));
|
||||
|
||||
QAbstractButton* deleteAllButton = nullptr;
|
||||
if (this->selected_events->length() > 1) {
|
||||
deleteAllButton = msgBox.addButton(QStringLiteral("Delete All"), QMessageBox::DestructiveRole);
|
||||
msgBox.addButton(QStringLiteral("Skip"), QMessageBox::NoRole);
|
||||
}
|
||||
|
||||
msgBox.exec();
|
||||
auto clickedButton = msgBox.clickedButton();
|
||||
auto clickedRole = msgBox.buttonRole(clickedButton);
|
||||
porymapConfig.eventDeleteWarningDisabled = msgBox.checkBox()->isChecked();
|
||||
if (clickedRole == QMessageBox::DestructiveRole) {
|
||||
// Confirmed deleting this event.
|
||||
eventsToDelete.append(event);
|
||||
if (deleteAllButton && clickedButton == deleteAllButton) {
|
||||
// Confirmed deleting all events, no more warning.
|
||||
skipWarning = true;
|
||||
}
|
||||
} else if (clickedRole == QMessageBox::NoRole) {
|
||||
// Declined deleting this event.
|
||||
continue;
|
||||
} else if (clickedRole == QMessageBox::RejectRole) {
|
||||
// Canceled delete.
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->map->commit(new EventDelete(this, this->map, selectedEvents, nextSelectedEvent ? nextSelectedEvent->event : nullptr));
|
||||
// TODO: Are we just calling this to invalidate connections?
|
||||
event->setPixmapItem(item);
|
||||
}
|
||||
if (eventsToDelete.isEmpty())
|
||||
return;
|
||||
|
||||
// Get the index for the event that should be selected after this event has been deleted.
|
||||
// Select event at next smallest index when deleting a single event.
|
||||
// If deleting multiple events, just let editor work out next selected.
|
||||
Event *nextSelectedEvent = nullptr;
|
||||
if (eventsToDelete.length() == 1) {
|
||||
Event *eventToDelete = eventsToDelete.first();
|
||||
Event::Group event_group = eventToDelete->getEventGroup();
|
||||
int index = this->map->getIndexOfEvent(eventToDelete);
|
||||
if (index != this->map->getNumEvents(event_group) - 1)
|
||||
index++;
|
||||
else
|
||||
index--;
|
||||
nextSelectedEvent = this->map->getEvent(event_group, index);
|
||||
}
|
||||
|
||||
this->map->commit(new EventDelete(this, this->map, eventsToDelete, nextSelectedEvent));
|
||||
}
|
||||
|
||||
void Editor::openMapScripts() const {
|
||||
|
|
|
|||
|
|
@ -57,9 +57,6 @@
|
|||
#define RELEASE_PLATFORM
|
||||
#endif
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
|
|
@ -858,6 +855,11 @@ bool MainWindow::userSetMap(QString map_name) {
|
|||
if (editor->map && editor->map->name() == map_name)
|
||||
return true; // Already set
|
||||
|
||||
if (map_name.isEmpty()) {
|
||||
WarningMessage::show(QStringLiteral("Cannot open map with empty name."), this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (map_name == editor->project->getDynamicMapName()) {
|
||||
WarningMessage msgBox(QString("Cannot open map '%1'.").arg(map_name), this);
|
||||
msgBox.setInformativeText(QStringLiteral("This map name is a placeholder to indicate that the warp's map will be set programmatically."));
|
||||
|
|
@ -1281,6 +1283,10 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) {
|
|||
void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) {
|
||||
logInfo(QString("Created a new map named %1.").arg(newMap->name()));
|
||||
|
||||
if (newMap->needsHealLocation()) {
|
||||
addNewEvent(Event::Type::HealLocation);
|
||||
}
|
||||
|
||||
// TODO: Creating a new map shouldn't be automatically saved.
|
||||
// For one, it takes away the option to discard the new map.
|
||||
// For two, if the new map uses an existing layout, any unsaved changes to that layout will also be saved.
|
||||
|
|
@ -1302,12 +1308,6 @@ void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) {
|
|||
ui->comboBox_EmergeMap->insertItem(mapIndex, newMap->name());
|
||||
}
|
||||
|
||||
if (newMap->needsHealLocation()) {
|
||||
addNewEvent(Event::Type::HealLocation);
|
||||
editor->project->saveHealLocations(newMap);
|
||||
editor->save();
|
||||
}
|
||||
|
||||
userSetMap(newMap->name());
|
||||
}
|
||||
|
||||
|
|
@ -1618,13 +1618,6 @@ void MainWindow::copy() {
|
|||
|
||||
for (auto item : events) {
|
||||
Event *event = item->event;
|
||||
|
||||
if (event->getEventType() == Event::Type::HealLocation) {
|
||||
// no copy on heal locations
|
||||
logWarn(QString("Copying heal location events is not allowed."));
|
||||
continue;
|
||||
}
|
||||
|
||||
OrderedJson::object eventContainer;
|
||||
eventContainer["event_type"] = Event::eventTypeToString(event->getEventType());
|
||||
OrderedJson::object eventJson = event->buildEventJson(editor->project);
|
||||
|
|
@ -1744,10 +1737,6 @@ void MainWindow::paste() {
|
|||
logWarn(QString("Cannot paste event, the limit for type '%1' has been reached.").arg(typeString));
|
||||
continue;
|
||||
}
|
||||
if (type == Event::Type::HealLocation) {
|
||||
logWarn(QString("Cannot paste events of type '%1'").arg(typeString));
|
||||
continue;
|
||||
}
|
||||
|
||||
Event *pasteEvent = Event::create(type);
|
||||
if (!pasteEvent)
|
||||
|
|
@ -1993,7 +1982,7 @@ void MainWindow::displayEventTabs() {
|
|||
tryAddEventTab(ui->tab_Warps);
|
||||
tryAddEventTab(ui->tab_Triggers);
|
||||
tryAddEventTab(ui->tab_BGs);
|
||||
tryAddEventTab(ui->tab_Healspots);
|
||||
tryAddEventTab(ui->tab_HealLocations);
|
||||
}
|
||||
|
||||
void MainWindow::updateEvents() {
|
||||
|
|
@ -2087,9 +2076,9 @@ void MainWindow::updateSelectedEvents() {
|
|||
break;
|
||||
}
|
||||
case Event::Group::Heal: {
|
||||
scrollTarget = ui->scrollArea_Healspots;
|
||||
target = ui->scrollAreaWidgetContents_Healspots;
|
||||
ui->tabWidget_EventType->setCurrentWidget(ui->tab_Healspots);
|
||||
scrollTarget = ui->scrollArea_HealLocations;
|
||||
target = ui->scrollAreaWidgetContents_HealLocations;
|
||||
ui->tabWidget_EventType->setCurrentWidget(ui->tab_HealLocations);
|
||||
|
||||
QSignalBlocker b(this->ui->spinner_HealID);
|
||||
this->ui->spinner_HealID->setMinimum(event_offs);
|
||||
|
|
@ -2154,11 +2143,11 @@ void MainWindow::updateSelectedEvents() {
|
|||
|
||||
Event::Group MainWindow::getEventGroupFromTabWidget(QWidget *tab) {
|
||||
static const QMap<QWidget*,Event::Group> tabToGroup = {
|
||||
{ui->tab_Objects, Event::Group::Object},
|
||||
{ui->tab_Warps, Event::Group::Warp},
|
||||
{ui->tab_Triggers, Event::Group::Coord},
|
||||
{ui->tab_BGs, Event::Group::Bg},
|
||||
{ui->tab_Healspots, Event::Group::Heal},
|
||||
{ui->tab_Objects, Event::Group::Object},
|
||||
{ui->tab_Warps, Event::Group::Warp},
|
||||
{ui->tab_Triggers, Event::Group::Coord},
|
||||
{ui->tab_BGs, Event::Group::Bg},
|
||||
{ui->tab_HealLocations, Event::Group::Heal},
|
||||
};
|
||||
return tabToGroup.value(tab, Event::Group::None);
|
||||
}
|
||||
|
|
@ -2181,6 +2170,9 @@ void MainWindow::eventTabChanged(int index) {
|
|||
case Event::Group::Bg:
|
||||
ui->newEventToolButton->setDefaultAction(ui->newEventToolButton->newSignAction);
|
||||
break;
|
||||
case Event::Group::Heal:
|
||||
ui->newEventToolButton->setDefaultAction(ui->newEventToolButton->newHealLocationAction);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
370
src/project.cpp
370
src/project.cpp
|
|
@ -23,9 +23,6 @@
|
|||
#include <QRegularExpression>
|
||||
#include <algorithm>
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
int Project::num_tiles_primary = 512;
|
||||
int Project::num_tiles_total = 1024;
|
||||
int Project::num_metatiles_primary = 512;
|
||||
|
|
@ -47,6 +44,7 @@ Project::~Project()
|
|||
clearTilesetCache();
|
||||
clearMapLayouts();
|
||||
clearEventGraphics();
|
||||
clearHealLocations();
|
||||
}
|
||||
|
||||
void Project::set_root(QString dir) {
|
||||
|
|
@ -97,7 +95,6 @@ bool Project::load() {
|
|||
&& readFieldmapMasks()
|
||||
&& readTilesetLabels()
|
||||
&& readTilesetMetatileLabels()
|
||||
&& readHealLocations()
|
||||
&& readMiscellaneousConstants()
|
||||
&& readSpeciesIconPaths()
|
||||
&& readWildMonData()
|
||||
|
|
@ -105,7 +102,8 @@ bool Project::load() {
|
|||
&& readObjEventGfxConstants()
|
||||
&& readEventGraphics()
|
||||
&& readSongNames()
|
||||
&& readMapGroups();
|
||||
&& readMapGroups()
|
||||
&& readHealLocations();
|
||||
|
||||
if (success) {
|
||||
// No need to do this if something failed to load.
|
||||
|
|
@ -118,7 +116,7 @@ bool Project::load() {
|
|||
return success;
|
||||
}
|
||||
|
||||
QString Project::getProjectTitle() {
|
||||
QString Project::getProjectTitle() const {
|
||||
if (!root.isNull()) {
|
||||
return root.section('/', -1);
|
||||
} else {
|
||||
|
|
@ -182,7 +180,6 @@ void Project::initTopLevelMapFields() {
|
|||
"warp_events",
|
||||
"coord_events",
|
||||
"bg_events",
|
||||
"heal_locations",
|
||||
"shared_events_map",
|
||||
"shared_scripts_map",
|
||||
};
|
||||
|
|
@ -316,30 +313,11 @@ bool Project::loadMapData(Map* map) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* TODO: Re-enable
|
||||
const QString mapPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
|
||||
for (auto it = healLocations.begin(); it != healLocations.end(); it++) {
|
||||
HealLocation loc = *it;
|
||||
//if TRUE map is flyable / has healing location
|
||||
if (loc.mapName == Map::mapConstantFromName(map->name, false)) {
|
||||
HealLocationEvent *heal = new HealLocationEvent();
|
||||
heal->setMap(map);
|
||||
heal->setX(loc.x);
|
||||
heal->setY(loc.y);
|
||||
heal->setElevation(projectConfig.defaultElevation);
|
||||
heal->setLocationName(loc.mapName);
|
||||
heal->setIdName(loc.idName);
|
||||
heal->setIndex(loc.index);
|
||||
if (projectConfig.healLocationRespawnDataEnabled) {
|
||||
heal->setRespawnMap(mapConstantsToMapNames.value(QString(mapPrefix + loc.respawnMap)));
|
||||
heal->setRespawnNPC(loc.respawnNPC);
|
||||
}
|
||||
map->events[Event::Group::Heal].append(heal);
|
||||
map->ownedEvents.append(heal);
|
||||
}
|
||||
// Heal locations are global. Populate the Map's heal location events using our global array.
|
||||
const QList<HealLocationEvent*> hlEvents = this->healLocations.value(map->constantName());
|
||||
for (const auto &event : hlEvents) {
|
||||
map->addEvent(event->duplicate());
|
||||
}
|
||||
*/
|
||||
|
||||
map->deleteConnections();
|
||||
QJsonArray connectionsArr = mapObj["connections"].toArray();
|
||||
|
|
@ -847,134 +825,67 @@ void Project::saveWildMonData() {
|
|||
wildEncountersFile.close();
|
||||
}
|
||||
|
||||
void Project::saveHealLocations(Map *map) {
|
||||
this->saveHealLocationsData(map);
|
||||
this->saveHealLocationsConstants();
|
||||
// For a map with a constant of 'MAP_FOO', returns a unique 'HEAL_LOCATION_FOO'.
|
||||
// Because of how event ID names are checked it doesn't guarantee that the name
|
||||
// won't be in-use by some map that hasn't been loaded yet.
|
||||
QString Project::getNewHealLocationName(const Map* map) const {
|
||||
if (!map) return QString();
|
||||
|
||||
QString idName = map->constantName();
|
||||
const QString mapPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
|
||||
if (idName.startsWith(mapPrefix)) {
|
||||
idName.remove(0, mapPrefix.length());
|
||||
}
|
||||
return toUniqueIdentifier(projectConfig.getIdentifier(ProjectIdentifier::define_heal_locations_prefix) + idName);
|
||||
}
|
||||
|
||||
// Saves heal location maps/coords/respawn data in root + /src/data/heal_locations.h
|
||||
void Project::saveHealLocationsData(Map *) {
|
||||
/* TODO: Will be re-implemented as part of changes to reading heal locations from map.json
|
||||
// Update heal locations from map
|
||||
if (map->events[Event::Group::Heal].length() > 0) {
|
||||
for (Event *healEvent : map->events[Event::Group::Heal]) {
|
||||
HealLocation hl = HealLocation::fromEvent(healEvent);
|
||||
this->healLocations[hl.index - 1] = hl;
|
||||
void Project::saveHealLocations() {
|
||||
const QString filepath = QString("%1/%2").arg(this->root).arg(projectConfig.getFilePath(ProjectFilePath::json_heal_locations));
|
||||
QFile file(filepath);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
logError(QString("Could not open '%1' for writing").arg(filepath));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the JSON data for output.
|
||||
QMap<QString, QList<OrderedJson::object>> idNameToJson;
|
||||
for (auto i = this->healLocations.constBegin(); i != this->healLocations.constEnd(); i++) {
|
||||
const QString mapConstant = i.key();
|
||||
for (const auto &event : i.value()) {
|
||||
// Heal location events don't need to track the "map" field, we're already tracking it either with
|
||||
// the keys in the healLocations map or by virtue of the event being added to a particular Map object.
|
||||
// The global JSON data needs this field, so we add it back here.
|
||||
auto eventJson = event->buildEventJson(this);
|
||||
eventJson["map"] = mapConstant;
|
||||
idNameToJson[event->getIdName()].append(eventJson);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find any duplicate constant names
|
||||
QMap<QString, int> healLocationsDupes;
|
||||
QSet<QString> healLocationsUnique;
|
||||
for (auto hl : this->healLocations) {
|
||||
QString idName = hl.idName;
|
||||
if (healLocationsUnique.contains(idName))
|
||||
healLocationsDupes[idName] = 1;
|
||||
else
|
||||
healLocationsUnique.insert(idName);
|
||||
}
|
||||
|
||||
// Create the definition text for each data table
|
||||
bool respawnEnabled = projectConfig.healLocationRespawnDataEnabled;
|
||||
const QString qualifiers = QString(healLocationDataQualifiers.isStatic ? "static " : "")
|
||||
+ QString(healLocationDataQualifiers.isConst ? "const " : "");
|
||||
|
||||
QString locationTableText = QString("%1%2 %3[] =\n{\n").arg(qualifiers)
|
||||
.arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations_type))
|
||||
.arg(this->healLocationsTableName);
|
||||
QString respawnMapTableText, respawnNPCTableText;
|
||||
if (respawnEnabled) {
|
||||
respawnMapTableText = QString("\n%1%2[][2] =\n{\n").arg(qualifiers).arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_maps));
|
||||
respawnNPCTableText = QString("\n%1%2[] =\n{\n").arg(qualifiers).arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_npcs));
|
||||
}
|
||||
|
||||
// Populate the data tables with the heal location data
|
||||
int i = 0;
|
||||
const QString emptyMapName = projectConfig.getIdentifier(ProjectIdentifier::define_map_empty);
|
||||
for (auto hl : this->healLocations) {
|
||||
// Add numbered suffix for duplicate constants
|
||||
if (healLocationsDupes.keys().contains(hl.idName)) {
|
||||
QString duplicateName = hl.idName;
|
||||
hl.idName += QString("_%1").arg(healLocationsDupes[duplicateName]);
|
||||
healLocationsDupes[duplicateName]++;
|
||||
this->healLocations[i].idName = hl.idName; // Update the name for writing constants later
|
||||
// We store Heal Locations in a QMap with map name keys. This makes retrieval by map name easy,
|
||||
// but it will sort them alphabetically. Project::healLocationSaveOrder lets us better support
|
||||
// the (perhaps unlikely) user who has designed something with assumptions about the order of the data.
|
||||
// This also avoids a bunch of diff noise on the first save from Porymap reordering the data.
|
||||
OrderedJson::array eventJsonArr;
|
||||
for (const auto &idName : this->healLocationSaveOrder) {
|
||||
if (!idNameToJson.value(idName).isEmpty()) {
|
||||
eventJsonArr.push_back(idNameToJson[idName].takeFirst());
|
||||
}
|
||||
}
|
||||
// Save any heal locations that weren't covered above (should be any new data).
|
||||
for (auto i = idNameToJson.constBegin(); i != idNameToJson.constEnd(); i++) {
|
||||
for (const auto &object : i.value()) {
|
||||
eventJsonArr.push_back(object);
|
||||
}
|
||||
|
||||
// Add entry to map/coords table
|
||||
QString mapName = !hl.mapName.isEmpty() ? hl.mapName : emptyMapName;
|
||||
locationTableText += QString(" [%1 - 1] = {MAP_GROUP(%2), MAP_NUM(%2), %3, %4},\n")
|
||||
.arg(hl.idName)
|
||||
.arg(mapName)
|
||||
.arg(hl.x)
|
||||
.arg(hl.y);
|
||||
|
||||
// Add entry to respawn map and npc tables
|
||||
if (respawnEnabled) {
|
||||
mapName = !hl.respawnMap.isEmpty() ? hl.respawnMap : emptyMapName;
|
||||
respawnMapTableText += QString(" [%1 - 1] = {MAP_GROUP(%2), MAP_NUM(%2)},\n")
|
||||
.arg(hl.idName)
|
||||
.arg(mapName);
|
||||
|
||||
respawnNPCTableText += QString(" [%1 - 1] = %2,\n")
|
||||
.arg(hl.idName)
|
||||
.arg(hl.respawnNPC);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
const QString tableEnd = QString("};\n");
|
||||
QString text = locationTableText + tableEnd;
|
||||
if (respawnEnabled)
|
||||
text += respawnMapTableText + tableEnd + respawnNPCTableText + tableEnd;
|
||||
|
||||
QString filepath = root + "/" + projectConfig.getFilePath(ProjectFilePath::data_heal_locations);
|
||||
OrderedJson::object object;
|
||||
object["heal_locations"] = eventJsonArr;
|
||||
|
||||
ignoreWatchedFileTemporarily(filepath);
|
||||
saveTextFile(filepath, text);
|
||||
*/
|
||||
}
|
||||
|
||||
// Saves heal location defines in root + /include/constants/heal_locations.h
|
||||
void Project::saveHealLocationsConstants() {
|
||||
// Get existing defines, and create an inverted map so they'll be in sorted order for printing
|
||||
int nextDefineValue = 1;
|
||||
QMap<int, QString> valuesToNames = QMap<int, QString>();
|
||||
QStringList defineNames = this->healLocationNameToValue.keys();
|
||||
QList<int> defineValues = this->healLocationNameToValue.values();
|
||||
for (auto name : defineNames) {
|
||||
int value = this->healLocationNameToValue.value(name);
|
||||
if (valuesToNames.contains(value)) {
|
||||
do { // Redefine duplicate as first available value
|
||||
value = nextDefineValue++;
|
||||
} while (defineValues.contains(value));
|
||||
}
|
||||
valuesToNames.insert(value, name);
|
||||
}
|
||||
|
||||
// Check for new id names in the heal locations list
|
||||
for (auto hl : this->healLocations) {
|
||||
if (this->healLocationNameToValue.contains(hl.idName))
|
||||
continue;
|
||||
int value;
|
||||
do { // Give new heal location first available value
|
||||
value = nextDefineValue++;
|
||||
} while (valuesToNames.contains(value));
|
||||
valuesToNames.insert(value, hl.idName);
|
||||
}
|
||||
|
||||
// Include guards
|
||||
const QString guardName = "GUARD_CONSTANTS_HEAL_LOCATIONS_H";
|
||||
QString constantsText = QString("#ifndef %1\n#define %1\n\n").arg(guardName);
|
||||
|
||||
// List defines in ascending order
|
||||
QMap<int, QString>::const_iterator i;
|
||||
for (i = valuesToNames.constBegin(); i != valuesToNames.constEnd(); i++)
|
||||
constantsText += QString("#define %1 %2\n").arg(i.value()).arg(i.key());
|
||||
|
||||
constantsText += QString("\n#endif // %1\n").arg(guardName);
|
||||
|
||||
QString filepath = root + "/" + projectConfig.getFilePath(ProjectFilePath::constants_heal_locations);
|
||||
ignoreWatchedFileTemporarily(filepath);
|
||||
saveTextFile(filepath, constantsText);
|
||||
OrderedJson json(object);
|
||||
OrderedJsonDoc jsonDoc(&json);
|
||||
jsonDoc.dump(&file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
|
|
@ -1056,7 +967,7 @@ bool Project::loadLayoutTilesets(Layout *layout) {
|
|||
layout->tileset_primary = getTileset(layout->tileset_primary_label);
|
||||
if (!layout->tileset_primary) {
|
||||
QString defaultTileset = this->getDefaultPrimaryTilesetLabel();
|
||||
logWarn(QString("Map layout %1 has invalid primary tileset '%2'. Using default '%3'").arg(layout->id).arg(layout->tileset_primary_label).arg(defaultTileset));
|
||||
logWarn(QString("%1 has invalid primary tileset '%2'. Using default '%3'").arg(layout->name).arg(layout->tileset_primary_label).arg(defaultTileset));
|
||||
layout->tileset_primary_label = defaultTileset;
|
||||
layout->tileset_primary = getTileset(layout->tileset_primary_label);
|
||||
if (!layout->tileset_primary) {
|
||||
|
|
@ -1068,7 +979,7 @@ bool Project::loadLayoutTilesets(Layout *layout) {
|
|||
layout->tileset_secondary = getTileset(layout->tileset_secondary_label);
|
||||
if (!layout->tileset_secondary) {
|
||||
QString defaultTileset = this->getDefaultSecondaryTilesetLabel();
|
||||
logWarn(QString("Map layout %1 has invalid secondary tileset '%2'. Using default '%3'").arg(layout->id).arg(layout->tileset_secondary_label).arg(defaultTileset));
|
||||
logWarn(QString("%1 has invalid secondary tileset '%2'. Using default '%3'").arg(layout->name).arg(layout->tileset_secondary_label).arg(defaultTileset));
|
||||
layout->tileset_secondary_label = defaultTileset;
|
||||
layout->tileset_secondary = getTileset(layout->tileset_secondary_label);
|
||||
if (!layout->tileset_secondary) {
|
||||
|
|
@ -1137,7 +1048,8 @@ bool Project::loadBlockdata(Layout *layout) {
|
|||
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
|
||||
|
||||
if (layout->blockdata.count() != layout->getWidth() * layout->getHeight()) {
|
||||
logWarn(QString("Layout blockdata length %1 does not match dimensions %2x%3 (should be %4). Resizing blockdata.")
|
||||
logWarn(QString("%1 blockdata length %2 does not match dimensions %3x%4 (should be %5). Resizing blockdata.")
|
||||
.arg(layout->name)
|
||||
.arg(layout->blockdata.count())
|
||||
.arg(layout->getWidth())
|
||||
.arg(layout->getHeight())
|
||||
|
|
@ -1174,7 +1086,8 @@ bool Project::loadLayoutBorder(Layout *layout) {
|
|||
|
||||
int borderLength = layout->getBorderWidth() * layout->getBorderHeight();
|
||||
if (layout->border.count() != borderLength) {
|
||||
logWarn(QString("Layout border blockdata length %1 must be %2. Resizing border blockdata.")
|
||||
logWarn(QString("%1 border blockdata length %2 must be %3. Resizing border blockdata.")
|
||||
.arg(layout->name)
|
||||
.arg(layout->border.count())
|
||||
.arg(borderLength));
|
||||
layout->border.resize(borderLength);
|
||||
|
|
@ -1337,6 +1250,16 @@ void Project::saveMap(Map *map) {
|
|||
mapObj["shared_scripts_map"] = map->sharedScriptsMap();
|
||||
}
|
||||
|
||||
// Update the global heal locations array using the Map's heal location events.
|
||||
// This won't get saved to disc until Project::saveHealLocations is called.
|
||||
QList<HealLocationEvent*> hlEvents;
|
||||
for (const auto &event : map->getEvents(Event::Group::Heal)) {
|
||||
auto hl = static_cast<HealLocationEvent*>(event);
|
||||
hlEvents.append(static_cast<HealLocationEvent*>(hl->duplicate()));
|
||||
}
|
||||
qDeleteAll(this->healLocations[map->constantName()]);
|
||||
this->healLocations[map->constantName()] = hlEvents;
|
||||
|
||||
// Custom header fields.
|
||||
const auto customAttributes = map->customAttributes();
|
||||
for (auto i = customAttributes.constBegin(); i != customAttributes.constEnd(); i++) {
|
||||
|
|
@ -1349,7 +1272,6 @@ void Project::saveMap(Map *map) {
|
|||
mapFile.close();
|
||||
|
||||
saveLayout(map->layout());
|
||||
saveHealLocations(map);
|
||||
|
||||
map->setClean();
|
||||
}
|
||||
|
|
@ -1381,6 +1303,7 @@ void Project::saveAllDataStructures() {
|
|||
saveMapLayouts();
|
||||
saveMapGroups();
|
||||
saveRegionMapSections();
|
||||
saveHealLocations();
|
||||
saveWildMonData();
|
||||
saveConfig();
|
||||
this->hasUnsavedDataChanges = false;
|
||||
|
|
@ -1976,6 +1899,15 @@ bool Project::isIdentifierUnique(const QString &identifier) const {
|
|||
return false;
|
||||
if (this->encounterGroupLabels.contains(identifier))
|
||||
return false;
|
||||
// Check event IDs
|
||||
for (const auto &map : this->mapCache) {
|
||||
auto events = map->getEvents();
|
||||
for (const auto &event : events) {
|
||||
QString idName = event->getIdName();
|
||||
if (!idName.isEmpty() && idName == identifier)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2035,18 +1967,6 @@ void Project::initNewLayoutSettings() {
|
|||
this->newLayoutSettings.secondaryTilesetLabel = getDefaultSecondaryTilesetLabel();
|
||||
}
|
||||
|
||||
Project::DataQualifiers Project::getDataQualifiers(QString text, QString label) {
|
||||
Project::DataQualifiers qualifiers;
|
||||
|
||||
QRegularExpression regex(QString("\\s*(?<static>static\\s*)?(?<const>const\\s*)?[A-Za-z0-9_\\s]*\\b%1\\b").arg(label));
|
||||
QRegularExpressionMatch match = regex.match(text);
|
||||
|
||||
qualifiers.isStatic = match.captured("static").isNull() ? false : true;
|
||||
qualifiers.isConst = match.captured("const").isNull() ? false : true;
|
||||
|
||||
return qualifiers;
|
||||
}
|
||||
|
||||
QString Project::getDefaultPrimaryTilesetLabel() const {
|
||||
QString defaultLabel = projectConfig.defaultPrimaryTileset;
|
||||
if (!this->primaryTilesetLabels.contains(defaultLabel)) {
|
||||
|
|
@ -2434,110 +2354,40 @@ void Project::setMapsecDisplayName(const QString &idName, const QString &display
|
|||
emit mapSectionDisplayNameChanged(idName, displayName);
|
||||
}
|
||||
|
||||
// Read the constants to preserve any "unused" heal locations when writing the file later
|
||||
bool Project::readHealLocationConstants() {
|
||||
this->healLocationNameToValue.clear();
|
||||
const QStringList regexList = {
|
||||
QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_heal_locations_prefix)),
|
||||
QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_spawn_prefix))
|
||||
};
|
||||
QString constantsFilename = projectConfig.getFilePath(ProjectFilePath::constants_heal_locations);
|
||||
fileWatcher.addPath(root + "/" + constantsFilename);
|
||||
this->healLocationNameToValue = parser.readCDefinesByRegex(constantsFilename, regexList);
|
||||
// No need to check if empty, not finding any heal location constants is ok
|
||||
return true;
|
||||
void Project::clearHealLocations() {
|
||||
for (auto &events : this->healLocations) {
|
||||
qDeleteAll(events);
|
||||
}
|
||||
this->healLocations.clear();
|
||||
this->healLocationSaveOrder.clear();
|
||||
}
|
||||
|
||||
// TODO: Simplify using the new C struct parsing functions (and indexed array parsing functions)
|
||||
bool Project::readHealLocations() {
|
||||
this->healLocations.clear();
|
||||
clearHealLocations();
|
||||
|
||||
if (!this->readHealLocationConstants())
|
||||
QJsonDocument doc;
|
||||
const QString baseFilepath = projectConfig.getFilePath(ProjectFilePath::json_heal_locations);
|
||||
const QString filepath = QString("%1/%2").arg(this->root).arg(baseFilepath);
|
||||
if (!parser.tryParseJsonFile(&doc, filepath)) {
|
||||
logError(QString("Failed to read heal locations from '%1'").arg(baseFilepath));
|
||||
return false;
|
||||
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::data_heal_locations);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
QString text = parser.readTextFile(root + "/" + filename);
|
||||
|
||||
// Strip comments
|
||||
static const QRegularExpression re_comments("//.*?(\r\n?|\n)|/\\*.*?\\*/", QRegularExpression::DotMatchesEverythingOption);
|
||||
text.replace(re_comments, "");
|
||||
|
||||
bool respawnEnabled = projectConfig.healLocationRespawnDataEnabled;
|
||||
|
||||
// Search for the name of the main Heal Locations table
|
||||
const QRegularExpression tableNameExpr(QString("%1\\s+(?<name>[A-Za-z0-9_]+)\\[").arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations_type)));
|
||||
const QRegularExpressionMatch tableNameMatch = tableNameExpr.match(text);
|
||||
if (tableNameMatch.hasMatch()) {
|
||||
// Found table name, record it and its qualifiers for output when saving.
|
||||
this->healLocationsTableName = tableNameMatch.captured("name");
|
||||
this->healLocationDataQualifiers = this->getDataQualifiers(text, this->healLocationsTableName);
|
||||
} else {
|
||||
// No table name found, initialize default name for output when saving.
|
||||
this->healLocationsTableName = respawnEnabled ? projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_points)
|
||||
: projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations);
|
||||
this->healLocationDataQualifiers = { .isStatic = true, .isConst = true };
|
||||
}
|
||||
fileWatcher.addPath(filepath);
|
||||
|
||||
// Create regex pattern for the constants (ex: "SPAWN_PALLET_TOWN" or "HEAL_LOCATION_PETALBURG_CITY")
|
||||
const QString spawnPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_spawn_prefix);
|
||||
const QString healLocPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_heal_locations_prefix);
|
||||
const QRegularExpression constantsExpr(QString("\\b(%1|%2)[A-Za-z0-9_]+").arg(spawnPrefix).arg(healLocPrefix));
|
||||
|
||||
// Find all the unique heal location constants used in the data tables.
|
||||
// Porymap doesn't care whether or not a constant appeared in the heal locations constants file.
|
||||
// Any data entry without a designated initializer using one of these constants will be silently discarded.
|
||||
// Any data entry that repeats a designated initializer will also be discarded.
|
||||
QStringList constants = QStringList();
|
||||
QRegularExpressionMatchIterator constantsMatch = constantsExpr.globalMatch(text);
|
||||
while (constantsMatch.hasNext())
|
||||
constants << constantsMatch.next().captured();
|
||||
constants.removeDuplicates();
|
||||
|
||||
// Pattern for a map value pair (ex: "MAP_GROUP(PALLET_TOWN), MAP_NUM(PALLET_TOWN)")
|
||||
const QString mapPattern = "MAP_GROUP[\\(\\s]+(?<map>[A-Za-z0-9_]+)[\\s\\)]+,\\s*MAP_NUM[\\(\\s]+(\\1)[\\s\\)]+";
|
||||
// Pattern for an x, y number pair
|
||||
const QString coordPattern = "\\s*(?<x>[0-9A-Fa-fx]+),\\s*(?<y>[0-9A-Fa-fx]+)";
|
||||
|
||||
for (const auto &idName : constants) {
|
||||
// Create regex pattern for e.g. "SPAWN_PALLET_TOWN - 1] = "
|
||||
const QString initializerPattern = QString("%1\\s*-\\s*1\\s*\\]\\s*=\\s*").arg(idName);
|
||||
|
||||
// Expression for location data, e.g. "SPAWN_PALLET_TOWN - 1] = {MAP_GROUP(PALLET_TOWN), MAP_NUM(PALLET_TOWN), x, y}"
|
||||
QRegularExpression locationRegex(QString("%1\\{%2,%3}").arg(initializerPattern).arg(mapPattern).arg(coordPattern));
|
||||
QRegularExpressionMatch match = locationRegex.match(text);
|
||||
|
||||
// Read location data
|
||||
HealLocation healLocation;
|
||||
if (match.hasMatch()) {
|
||||
QString mapName = match.captured("map");
|
||||
int x = match.captured("x").toInt(nullptr, 0);
|
||||
int y = match.captured("y").toInt(nullptr, 0);
|
||||
healLocation = HealLocation(idName, mapName, this->healLocations.size() + 1, x, y);
|
||||
} else {
|
||||
// This heal location has data, but is missing from the location table and won't be displayed by Porymap.
|
||||
// Add a dummy entry, and preserve the rest of its data for the user anyway
|
||||
healLocation = HealLocation(idName, "", this->healLocations.size() + 1, 0, 0);
|
||||
QJsonArray healLocations = doc.object()["heal_locations"].toArray();
|
||||
for (int i = 0; i < healLocations.size(); i++) {
|
||||
QJsonObject healLocationObj = healLocations.at(i).toObject();
|
||||
static const QString mapField = QStringLiteral("map");
|
||||
if (!healLocationObj.contains(mapField)) {
|
||||
logWarn(QString("Ignoring data for heal location %1 in '%2'. Missing required field \"%3\"").arg(i).arg(baseFilepath).arg(mapField));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read respawn data
|
||||
if (respawnEnabled) {
|
||||
// Expression for respawn map data, e.g. "SPAWN_PALLET_TOWN - 1] = {MAP_GROUP(PALLET_TOWN_PLAYERS_HOUSE_1F), MAP_NUM(PALLET_TOWN_PLAYERS_HOUSE_1F)}"
|
||||
QRegularExpression respawnMapRegex(QString("%1\\{%2}").arg(initializerPattern).arg(mapPattern));
|
||||
match = respawnMapRegex.match(text);
|
||||
if (match.hasMatch())
|
||||
healLocation.respawnMap = match.captured("map");
|
||||
|
||||
// Expression for respawn npc data, e.g. "SPAWN_PALLET_TOWN - 1] = 1"
|
||||
QRegularExpression respawnNPCRegex(QString("%1(?<npc>[0-9]+)").arg(initializerPattern));
|
||||
match = respawnNPCRegex.match(text);
|
||||
if (match.hasMatch())
|
||||
healLocation.respawnNPC = match.captured("npc").toInt(nullptr, 0);
|
||||
}
|
||||
|
||||
this->healLocations.append(healLocation);
|
||||
auto event = new HealLocationEvent();
|
||||
event->loadFromJson(healLocationObj, this);
|
||||
this->healLocations[ParseUtil::jsonToQString(healLocationObj["map"])].append(event);
|
||||
this->healLocationSaveOrder.append(event->getIdName());
|
||||
}
|
||||
// No need to check if empty, not finding any heal locations is ok
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) {
|
|||
}
|
||||
}
|
||||
|
||||
// Events with properties that specify a map will open that map when double-clicked.
|
||||
void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
|
||||
Event::Type eventType = this->event->getEventType();
|
||||
if (eventType == Event::Type::Warp) {
|
||||
|
|
@ -126,4 +127,10 @@ void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
|
|||
QString destMap = editor->project->mapConstantsToMapNames.value(mapPrefix + baseId.left(baseId.lastIndexOf("_")));
|
||||
emit editor->warpEventDoubleClicked(destMap, 0, Event::Group::Warp);
|
||||
}
|
||||
else if (eventType == Event::Type::HealLocation && projectConfig.healLocationRespawnDataEnabled) {
|
||||
HealLocationEvent *heal = dynamic_cast<HealLocationEvent *>(this->event);
|
||||
const QString localIdName = heal->getRespawnNPC();
|
||||
int localId = 0; // TODO: Get value from localIdName
|
||||
emit editor->warpEventDoubleClicked(heal->getRespawnMapName(), localId, Event::Group::Object);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -947,6 +947,14 @@ void HealLocationFrame::setup() {
|
|||
this->hideable_label_z->setVisible(false);
|
||||
this->spinner_z->setVisible(false);
|
||||
|
||||
// ID
|
||||
QFormLayout *l_form_id = new QFormLayout();
|
||||
this->line_edit_id = new QLineEdit(this);
|
||||
this->line_edit_id->setToolTip("The unique identifier for this heal location.");
|
||||
this->line_edit_id->setPlaceholderText(projectConfig.getIdentifier(ProjectIdentifier::define_heal_locations_prefix) + "MY_MAP");
|
||||
l_form_id->addRow("ID", this->line_edit_id);
|
||||
this->layout_contents->addLayout(l_form_id);
|
||||
|
||||
// respawn map combo
|
||||
this->hideable_respawn_map = new QFrame;
|
||||
QFormLayout *l_form_respawn_map = new QFormLayout(hideable_respawn_map);
|
||||
|
|
@ -960,10 +968,10 @@ void HealLocationFrame::setup() {
|
|||
this->hideable_respawn_npc = new QFrame;
|
||||
QFormLayout *l_form_respawn_npc = new QFormLayout(hideable_respawn_npc);
|
||||
l_form_respawn_npc->setContentsMargins(0, 0, 0, 0);
|
||||
this->spinner_respawn_npc = new NoScrollSpinBox(hideable_respawn_npc);
|
||||
this->spinner_respawn_npc->setToolTip("event_object ID of the NPC the player interacts with\n"
|
||||
this->combo_respawn_npc = new NoScrollComboBox(hideable_respawn_npc);
|
||||
this->combo_respawn_npc->setToolTip("event_object ID of the NPC the player interacts with\n"
|
||||
"upon respawning after whiteout.");
|
||||
l_form_respawn_npc->addRow("Respawn NPC", this->spinner_respawn_npc);
|
||||
l_form_respawn_npc->addRow("Respawn NPC", this->combo_respawn_npc);
|
||||
this->layout_contents->addWidget(hideable_respawn_npc);
|
||||
|
||||
// custom attributes
|
||||
|
|
@ -975,19 +983,23 @@ void HealLocationFrame::connectSignals(MainWindow *window) {
|
|||
|
||||
EventFrame::connectSignals(window);
|
||||
|
||||
if (projectConfig.healLocationRespawnDataEnabled) {
|
||||
this->combo_respawn_map->disconnect();
|
||||
connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this](const QString &text) {
|
||||
this->healLocation->setRespawnMap(text);
|
||||
this->healLocation->modify();
|
||||
});
|
||||
this->line_edit_id->disconnect();
|
||||
connect(this->line_edit_id, &QLineEdit::textChanged, [this](const QString &text) {
|
||||
this->healLocation->setIdName(text);
|
||||
this->healLocation->modify();
|
||||
});
|
||||
|
||||
this->spinner_respawn_npc->disconnect();
|
||||
connect(this->spinner_respawn_npc, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
|
||||
this->healLocation->setRespawnNPC(value);
|
||||
this->healLocation->modify();
|
||||
});
|
||||
}
|
||||
this->combo_respawn_map->disconnect();
|
||||
connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this](const QString &text) {
|
||||
this->healLocation->setRespawnMapName(text);
|
||||
this->healLocation->modify();
|
||||
});
|
||||
|
||||
this->combo_respawn_npc->disconnect();
|
||||
connect(this->combo_respawn_npc, &QComboBox::currentTextChanged, [this](const QString &text) {
|
||||
this->healLocation->setRespawnNPC(text);
|
||||
this->healLocation->modify();
|
||||
});
|
||||
}
|
||||
|
||||
void HealLocationFrame::initialize() {
|
||||
|
|
@ -996,12 +1008,11 @@ void HealLocationFrame::initialize() {
|
|||
const QSignalBlocker blocker(this);
|
||||
EventFrame::initialize();
|
||||
|
||||
bool respawnEnabled = projectConfig.healLocationRespawnDataEnabled;
|
||||
if (respawnEnabled) {
|
||||
this->combo_respawn_map->setTextItem(this->healLocation->getRespawnMap());
|
||||
this->spinner_respawn_npc->setValue(this->healLocation->getRespawnNPC());
|
||||
}
|
||||
this->line_edit_id->setText(this->healLocation->getIdName());
|
||||
this->combo_respawn_map->setTextItem(this->healLocation->getRespawnMapName());
|
||||
this->combo_respawn_npc->setTextItem(this->healLocation->getRespawnNPC());
|
||||
|
||||
bool respawnEnabled = projectConfig.healLocationRespawnDataEnabled;
|
||||
this->hideable_respawn_map->setVisible(respawnEnabled);
|
||||
this->hideable_respawn_npc->setVisible(respawnEnabled);
|
||||
}
|
||||
|
|
@ -1012,6 +1023,7 @@ void HealLocationFrame::populate(Project *project) {
|
|||
const QSignalBlocker blocker(this);
|
||||
EventFrame::populate(project);
|
||||
|
||||
if (projectConfig.healLocationRespawnDataEnabled)
|
||||
this->combo_respawn_map->addItems(project->mapNames);
|
||||
this->combo_respawn_map->addItems(project->mapNames);
|
||||
// TODO: We should dynamically populate combo_respawn_npc with the local IDs of the respawn_map
|
||||
// Same for warp IDs.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,17 @@ QPoint MetatileSelector::getSelectionDimensions() {
|
|||
return SelectablePixmapItem::getSelectionDimensions();
|
||||
}
|
||||
|
||||
int MetatileSelector::numPrimaryMetatilesRounded() const {
|
||||
// We round up the number of primary metatiles to keep the tilesets on separate rows.
|
||||
return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide;
|
||||
}
|
||||
|
||||
void MetatileSelector::draw() {
|
||||
if (!this->primaryTileset || !this->secondaryTileset) {
|
||||
this->setPixmap(QPixmap());
|
||||
}
|
||||
|
||||
int primaryLength = this->primaryTileset->numMetatiles();
|
||||
int primaryLength = this->numPrimaryMetatilesRounded();
|
||||
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
|
||||
int height_ = length_ / this->numMetatilesWide;
|
||||
if (length_ % this->numMetatilesWide != 0) {
|
||||
|
|
@ -149,7 +154,7 @@ void MetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
|
|||
|
||||
void MetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
|
||||
QPoint pos = this->getCellPos(event->pos());
|
||||
if (!positionIsValid(pos) || this->cellPos == pos)
|
||||
if (this->cellPos == pos)
|
||||
return;
|
||||
|
||||
this->cellPos = pos;
|
||||
|
|
@ -199,10 +204,11 @@ void MetatileSelector::updateExternalSelectedMetatiles() {
|
|||
|
||||
uint16_t MetatileSelector::getMetatileId(int x, int y) const {
|
||||
int index = y * this->numMetatilesWide + x;
|
||||
if (index < this->primaryTileset->numMetatiles()) {
|
||||
int numPrimary = this->numPrimaryMetatilesRounded();
|
||||
if (index < numPrimary) {
|
||||
return static_cast<uint16_t>(index);
|
||||
} else {
|
||||
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - this->primaryTileset->numMetatiles());
|
||||
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +221,7 @@ QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
|
|||
|
||||
int index = metatileId < Project::getNumMetatilesPrimary()
|
||||
? metatileId
|
||||
: metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->numMetatiles();
|
||||
: metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded();
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,9 @@ void NewEventToolButton::init()
|
|||
this->newWarpAction->setIcon(QIcon(":/icons/add.ico"));
|
||||
connect(this->newWarpAction, &QAction::triggered, this, &NewEventToolButton::newWarp);
|
||||
|
||||
/* // disable this functionality for now
|
||||
this->newHealLocationAction = new QAction("New Heal Location", this);
|
||||
this->newHealLocationAction->setIcon(QIcon(":/icons/add.ico"));
|
||||
connect(this->newHealLocationAction, SIGNAL(triggered(bool)), this, SLOT(newHealLocation()));
|
||||
*/
|
||||
connect(this->newHealLocationAction, &QAction::triggered, this, &NewEventToolButton::newHealLocation);
|
||||
|
||||
this->newTriggerAction = new QAction("New Trigger", this);
|
||||
this->newTriggerAction->setIcon(QIcon(":/icons/add.ico"));
|
||||
|
|
@ -56,7 +54,7 @@ void NewEventToolButton::init()
|
|||
alignMenu->addAction(this->newObjectAction);
|
||||
alignMenu->addAction(this->newCloneObjectAction);
|
||||
alignMenu->addAction(this->newWarpAction);
|
||||
//alignMenu->addAction(this->newHealLocationAction);
|
||||
alignMenu->addAction(this->newHealLocationAction);
|
||||
alignMenu->addAction(this->newTriggerAction);
|
||||
alignMenu->addAction(this->newWeatherTriggerAction);
|
||||
alignMenu->addAction(this->newSignAction);
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
#include <QSpacerItem>
|
||||
#include <QMessageBox>
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
const QString defaultFilepath = "prefabs.json";
|
||||
|
||||
void Prefab::loadPrefabs() {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ void PreferenceEditor::updateFields() {
|
|||
ui->checkBox_MonitorProjectFiles->setChecked(porymapConfig.monitorFiles);
|
||||
ui->checkBox_OpenRecentProject->setChecked(porymapConfig.reopenOnLaunch);
|
||||
ui->checkBox_CheckForUpdates->setChecked(porymapConfig.checkForUpdates);
|
||||
ui->checkBox_DisableEventWarning->setChecked(porymapConfig.eventDeleteWarningDisabled);
|
||||
}
|
||||
|
||||
void PreferenceEditor::saveFields() {
|
||||
|
|
@ -69,6 +70,7 @@ void PreferenceEditor::saveFields() {
|
|||
porymapConfig.monitorFiles = ui->checkBox_MonitorProjectFiles->isChecked();
|
||||
porymapConfig.reopenOnLaunch = ui->checkBox_OpenRecentProject->isChecked();
|
||||
porymapConfig.checkForUpdates = ui->checkBox_CheckForUpdates->isChecked();
|
||||
porymapConfig.eventDeleteWarningDisabled = ui->checkBox_DisableEventWarning->isChecked();
|
||||
porymapConfig.save();
|
||||
|
||||
emit preferencesSaved();
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ void ProjectSettingsEditor::connectSignals() {
|
|||
connect(ui->button_WarpsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_WarpsIcon); });
|
||||
connect(ui->button_TriggersIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_TriggersIcon); });
|
||||
connect(ui->button_BGsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_BGsIcon); });
|
||||
connect(ui->button_HealspotsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_HealspotsIcon); });
|
||||
connect(ui->button_HealLocationsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_HealLocationsIcon); });
|
||||
connect(ui->button_PokemonIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_PokemonIcon); });
|
||||
|
||||
|
||||
|
|
@ -476,7 +476,7 @@ void ProjectSettingsEditor::refresh() {
|
|||
ui->lineEdit_WarpsIcon->setText(projectConfig.getEventIconPath(Event::Group::Warp));
|
||||
ui->lineEdit_TriggersIcon->setText(projectConfig.getEventIconPath(Event::Group::Coord));
|
||||
ui->lineEdit_BGsIcon->setText(projectConfig.getEventIconPath(Event::Group::Bg));
|
||||
ui->lineEdit_HealspotsIcon->setText(projectConfig.getEventIconPath(Event::Group::Heal));
|
||||
ui->lineEdit_HealLocationsIcon->setText(projectConfig.getEventIconPath(Event::Group::Heal));
|
||||
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
|
||||
lineEdit->setText(projectConfig.getCustomFilePath(lineEdit->objectName()));
|
||||
for (auto lineEdit : ui->scrollAreaContents_Identifiers->findChildren<QLineEdit*>())
|
||||
|
|
@ -546,7 +546,7 @@ void ProjectSettingsEditor::save() {
|
|||
projectConfig.setEventIconPath(Event::Group::Warp, ui->lineEdit_WarpsIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Coord, ui->lineEdit_TriggersIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Bg, ui->lineEdit_BGsIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Heal, ui->lineEdit_HealspotsIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Heal, ui->lineEdit_HealLocationsIcon->text());
|
||||
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
|
||||
projectConfig.setFilePath(lineEdit->objectName(), lineEdit->text());
|
||||
for (auto lineEdit : ui->scrollAreaContents_Identifiers->findChildren<QLineEdit*>())
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@
|
|||
#include <QTransform>
|
||||
#include <math.h>
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::RegionMapEditor)
|
||||
|
|
|
|||
|
|
@ -29,13 +29,7 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
|||
this->tileYFlip = ui->checkBox_yFlip->isChecked();
|
||||
this->paletteId = ui->spinBox_paletteSelector->value();
|
||||
|
||||
// TODO: The dividing line at the moment is only accurate if the number of primary metatiles is divisible by 8.
|
||||
// If it's not, the secondary metatiles will wrap above the line. This has other problems (like skewing
|
||||
// metatile groups the user may have designed) so this should be fixed by filling the primary metatiles
|
||||
// image with invalid magenta metatiles until it's divisible by 8. Then the line can be re-enabled as-is.
|
||||
ui->actionShow_Tileset_Divider->setChecked(/*porymapConfig.showTilesetEditorDivider*/false);
|
||||
ui->actionShow_Tileset_Divider->setVisible(false);
|
||||
|
||||
ui->actionShow_Tileset_Divider->setChecked(porymapConfig.showTilesetEditorDivider);
|
||||
ui->spinBox_paletteSelector->setMinimum(0);
|
||||
ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
|
||||
ui->lineEdit_metatileLabel->setValidator(new IdentifierValidator(this));
|
||||
|
|
@ -759,7 +753,7 @@ void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
|
|||
{
|
||||
QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowTitle("Change Number of Metatiles");
|
||||
dialog.setWindowModality(Qt::NonModal);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
QFormLayout form(&dialog);
|
||||
|
||||
|
|
@ -1051,20 +1045,19 @@ void TilesetEditor::countMetatileUsage() {
|
|||
// do not double count
|
||||
metatileSelector->usedMetatiles.fill(0);
|
||||
|
||||
for (auto layout : this->project->mapLayouts.values()) {
|
||||
bool usesPrimary = false;
|
||||
bool usesSecondary = false;
|
||||
for (auto layout : this->project->mapLayouts) {
|
||||
// It's possible for a layout's tileset labels to change if they are invalid,
|
||||
// so we need to load all the tilesets even if they aren't the tileset we're looking for.
|
||||
// Otherwise the metatile usage counts may change because the layouts with invalid tilesets
|
||||
// were updated to use a tileset we were looking for.
|
||||
this->project->loadLayoutTilesets(layout);
|
||||
|
||||
if (layout->tileset_primary_label == this->primaryTileset->name) {
|
||||
usesPrimary = true;
|
||||
}
|
||||
|
||||
if (layout->tileset_secondary_label == this->secondaryTileset->name) {
|
||||
usesSecondary = true;
|
||||
}
|
||||
bool usesPrimary = (layout->tileset_primary_label == this->primaryTileset->name);
|
||||
bool usesSecondary = (layout->tileset_secondary_label == this->secondaryTileset->name);
|
||||
|
||||
if (usesPrimary || usesSecondary) {
|
||||
this->project->loadLayout(layout);
|
||||
if (!this->project->loadLayout(layout))
|
||||
continue;
|
||||
|
||||
// for each block in the layout, mark in the vector that it is used
|
||||
for (int i = 0; i < layout->blockdata.length(); i++) {
|
||||
|
|
@ -1097,9 +1090,9 @@ void TilesetEditor::countTileUsage() {
|
|||
QSet<Tileset*> secondaryTilesets;
|
||||
|
||||
for (auto layout : this->project->mapLayouts.values()) {
|
||||
this->project->loadLayoutTilesets(layout);
|
||||
if (layout->tileset_primary_label == this->primaryTileset->name
|
||||
|| layout->tileset_secondary_label == this->secondaryTileset->name) {
|
||||
this->project->loadLayoutTilesets(layout);
|
||||
// need to check metatiles
|
||||
if (layout->tileset_primary && layout->tileset_secondary) {
|
||||
primaryTilesets.insert(layout->tileset_primary);
|
||||
|
|
|
|||
|
|
@ -22,11 +22,16 @@ int TilesetEditorMetatileSelector::numRows(int numMetatiles) {
|
|||
}
|
||||
|
||||
int TilesetEditorMetatileSelector::numRows() {
|
||||
return this->numRows(this->primaryTileset->numMetatiles() + this->secondaryTileset->numMetatiles());
|
||||
return this->numRows(this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles());
|
||||
}
|
||||
|
||||
int TilesetEditorMetatileSelector::numPrimaryMetatilesRounded() const {
|
||||
// We round up the number of primary metatiles to keep the tilesets on separate rows.
|
||||
return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide;
|
||||
}
|
||||
|
||||
QImage TilesetEditorMetatileSelector::buildAllMetatilesImage() {
|
||||
return this->buildImage(0, this->primaryTileset->numMetatiles() + this->secondaryTileset->numMetatiles());
|
||||
return this->buildImage(0, this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles());
|
||||
}
|
||||
|
||||
QImage TilesetEditorMetatileSelector::buildPrimaryMetatilesImage() {
|
||||
|
|
@ -39,11 +44,11 @@ QImage TilesetEditorMetatileSelector::buildSecondaryMetatilesImage() {
|
|||
|
||||
QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMetatiles) {
|
||||
int numMetatilesHigh = this->numRows(numMetatiles);
|
||||
int numPrimary = this->primaryTileset->numMetatiles();
|
||||
int numPrimary = this->numPrimaryMetatilesRounded();
|
||||
int maxPrimary = Project::getNumMetatilesPrimary();
|
||||
bool includesPrimary = metatileIdStart < maxPrimary;
|
||||
|
||||
QImage image(this->numMetatilesWide * 32, numMetatilesHigh * 32, QImage::Format_RGBA8888);
|
||||
QImage image(this->numMetatilesWide * this->cellWidth, numMetatilesHigh * this->cellHeight, QImage::Format_RGBA8888);
|
||||
image.fill(Qt::magenta);
|
||||
QPainter painter(&image);
|
||||
for (int i = 0; i < numMetatiles; i++) {
|
||||
|
|
@ -57,10 +62,10 @@ QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMet
|
|||
this->layout->metatileLayerOrder,
|
||||
this->layout->metatileLayerOpacity,
|
||||
true)
|
||||
.scaled(32, 32);
|
||||
.scaled(this->cellWidth, this->cellHeight);
|
||||
int map_y = i / this->numMetatilesWide;
|
||||
int map_x = i % this->numMetatilesWide;
|
||||
QPoint metatile_origin = QPoint(map_x * 32, map_y * 32);
|
||||
QPoint metatile_origin = QPoint(map_x * this->cellWidth, map_y * this->cellHeight);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
}
|
||||
painter.end();
|
||||
|
|
@ -107,10 +112,11 @@ uint16_t TilesetEditorMetatileSelector::getSelectedMetatileId() {
|
|||
|
||||
uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) {
|
||||
int index = y * this->numMetatilesWide + x;
|
||||
if (index < this->primaryTileset->numMetatiles()) {
|
||||
int numPrimary = numPrimaryMetatilesRounded();
|
||||
if (index < numPrimary) {
|
||||
return static_cast<uint16_t>(index);
|
||||
} else {
|
||||
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - this->primaryTileset->numMetatiles());
|
||||
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +162,7 @@ QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
|
|||
}
|
||||
int index = metatileId < Project::getNumMetatilesPrimary()
|
||||
? metatileId
|
||||
: metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->numMetatiles();
|
||||
: metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded();
|
||||
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
|
||||
}
|
||||
|
||||
|
|
@ -176,12 +182,12 @@ void TilesetEditorMetatileSelector::drawGrid() {
|
|||
const int numColumns = this->numMetatilesWide;
|
||||
const int numRows = this->numRows();
|
||||
for (int column = 1; column < numColumns; column++) {
|
||||
int x = column * 32;
|
||||
painter.drawLine(x, 0, x, numRows * 32);
|
||||
int x = column * this->cellWidth;
|
||||
painter.drawLine(x, 0, x, numRows * this->cellHeight);
|
||||
}
|
||||
for (int row = 1; row < numRows; row++) {
|
||||
int y = row * 32;
|
||||
painter.drawLine(0, y, numColumns * 32, y);
|
||||
int y = row * this->cellHeight;
|
||||
painter.drawLine(0, y, numColumns * this->cellWidth, y);
|
||||
}
|
||||
painter.end();
|
||||
this->setPixmap(pixmap);
|
||||
|
|
@ -191,12 +197,12 @@ void TilesetEditorMetatileSelector::drawDivider() {
|
|||
if (!this->showDivider)
|
||||
return;
|
||||
|
||||
const int y = this->numRows(this->primaryTileset->numMetatiles()) * 32;
|
||||
const int y = this->numRows(this->numPrimaryMetatilesRounded()) * this->cellHeight;
|
||||
|
||||
QPixmap pixmap = this->pixmap();
|
||||
QPainter painter(&pixmap);
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawLine(0, y, this->numMetatilesWide * 32, y);
|
||||
painter.drawLine(0, y, this->numMetatilesWide * this->cellWidth, y);
|
||||
painter.end();
|
||||
this->setPixmap(pixmap);
|
||||
}
|
||||
|
|
@ -212,7 +218,7 @@ void TilesetEditorMetatileSelector::drawFilters() {
|
|||
|
||||
void TilesetEditorMetatileSelector::drawUnused() {
|
||||
// setup the circle with a line through it image to layer above unused metatiles
|
||||
QPixmap redX(32, 32);
|
||||
QPixmap redX(this->cellWidth, this->cellHeight);
|
||||
redX.fill(Qt::transparent);
|
||||
|
||||
QPen whitePen(Qt::white);
|
||||
|
|
@ -223,21 +229,21 @@ void TilesetEditorMetatileSelector::drawUnused() {
|
|||
QPainter oPainter(&redX);
|
||||
|
||||
oPainter.setPen(whitePen);
|
||||
oPainter.drawEllipse(QRect(1, 1, 30, 30));
|
||||
oPainter.drawEllipse(QRect(1, 1, this->cellWidth - 2, this->cellHeight - 2));
|
||||
oPainter.setPen(pinkPen);
|
||||
oPainter.drawEllipse(QRect(2, 2, 28, 28));
|
||||
oPainter.drawEllipse(QRect(3, 3, 26, 26));
|
||||
oPainter.drawEllipse(QRect(2, 2, this->cellWidth - 4, this->cellHeight - 4));
|
||||
oPainter.drawEllipse(QRect(3, 3, this->cellWidth - 6, this->cellHeight - 6));
|
||||
|
||||
oPainter.setPen(whitePen);
|
||||
oPainter.drawEllipse(QRect(4, 4, 24, 24));
|
||||
oPainter.drawEllipse(QRect(4, 4, this->cellHeight - 8, this->cellHeight - 8));
|
||||
|
||||
whitePen.setWidth(5);
|
||||
oPainter.setPen(whitePen);
|
||||
oPainter.drawLine(0, 0, 31, 31);
|
||||
oPainter.drawLine(0, 0, this->cellWidth - 1, this->cellHeight - 1);
|
||||
|
||||
pinkPen.setWidth(3);
|
||||
oPainter.setPen(pinkPen);
|
||||
oPainter.drawLine(2, 2, 29, 29);
|
||||
oPainter.drawLine(2, 2, this->cellWidth - 3, this->cellHeight - 3);
|
||||
|
||||
oPainter.end();
|
||||
|
||||
|
|
@ -247,19 +253,13 @@ void TilesetEditorMetatileSelector::drawUnused() {
|
|||
QPainter unusedPainter(&metatilesPixmap);
|
||||
unusedPainter.setOpacity(0.5);
|
||||
|
||||
int primaryLength = this->primaryTileset->numMetatiles();
|
||||
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
|
||||
|
||||
for (int i = 0; i < length_; i++) {
|
||||
int tile = i;
|
||||
if (i >= primaryLength) {
|
||||
tile += Project::getNumMetatilesPrimary() - primaryLength;
|
||||
}
|
||||
if (!usedMetatiles[tile]) {
|
||||
unusedPainter.drawPixmap((i % 8) * 32, (i / 8) * 32, redX);
|
||||
}
|
||||
for (int metatileId = 0; metatileId < this->usedMetatiles.size(); metatileId++) {
|
||||
if (this->usedMetatiles.at(metatileId) || !Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
continue;
|
||||
// Adjust position from center to top-left corner
|
||||
QPoint pos = getMetatileIdCoordsOnWidget(metatileId) - QPoint(this->cellWidth / 2, this->cellHeight / 2);
|
||||
unusedPainter.drawPixmap(pos.x(), pos.y(), redX);
|
||||
}
|
||||
|
||||
unusedPainter.end();
|
||||
|
||||
this->setPixmap(metatilesPixmap);
|
||||
|
|
@ -268,38 +268,28 @@ void TilesetEditorMetatileSelector::drawUnused() {
|
|||
void TilesetEditorMetatileSelector::drawCounts() {
|
||||
QPen blackPen(Qt::black);
|
||||
blackPen.setWidth(1);
|
||||
|
||||
QPixmap metatilesPixmap = this->pixmap();
|
||||
|
||||
QPainter countPainter(&metatilesPixmap);
|
||||
countPainter.setPen(blackPen);
|
||||
|
||||
for (int tile = 0; tile < this->usedMetatiles.size(); tile++) {
|
||||
int count = usedMetatiles[tile];
|
||||
QString countText = QString::number(count);
|
||||
if (count > 1000) countText = ">1k";
|
||||
countPainter.drawText((tile % 8) * 32, (tile / 8) * 32 + 32, countText);
|
||||
}
|
||||
|
||||
// write in white and black for contrast
|
||||
QPen whitePen(Qt::white);
|
||||
whitePen.setWidth(1);
|
||||
countPainter.setPen(whitePen);
|
||||
|
||||
int primaryLength = this->primaryTileset->numMetatiles();
|
||||
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
|
||||
QPixmap metatilesPixmap = this->pixmap();
|
||||
QPainter countPainter(&metatilesPixmap);
|
||||
|
||||
for (int i = 0; i < length_; i++) {
|
||||
int tile = i;
|
||||
if (i >= primaryLength) {
|
||||
tile += Project::getNumMetatilesPrimary() - primaryLength;
|
||||
}
|
||||
int count = usedMetatiles[tile];
|
||||
QString countText = QString::number(count);
|
||||
if (count > 1000) countText = ">1k";
|
||||
countPainter.drawText((i % 8) * 32 + 1, (i / 8) * 32 + 32 - 1, countText);
|
||||
for (int metatileId = 0; metatileId < this->usedMetatiles.size(); metatileId++) {
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
continue;
|
||||
|
||||
int count = this->usedMetatiles.at(metatileId);
|
||||
QString countText = (count > 1000) ? QStringLiteral(">1k") : QString::number(count);
|
||||
|
||||
// Adjust position from center to bottom-left corner
|
||||
QPoint pos = getMetatileIdCoordsOnWidget(metatileId) + QPoint(-(this->cellWidth / 2), this->cellHeight / 2);
|
||||
|
||||
// write in black and white for contrast
|
||||
countPainter.setPen(blackPen);
|
||||
countPainter.drawText(pos.x(), pos.y(), countText);
|
||||
countPainter.setPen(whitePen);
|
||||
countPainter.drawText(pos.x() + 1, pos.y() - 1, countText);
|
||||
}
|
||||
|
||||
countPainter.end();
|
||||
|
||||
this->setPixmap(metatilesPixmap);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user