diff --git a/CHANGELOG.md b/CHANGELOG.md index a210459f..4589e78b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,22 +11,22 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Proper support for pokefirered's clone objects was added, which requires the changes made in [pokefirered/#484](https://github.com/pret/pokefirered/pull/484). - Many API functions which were previously accessible via the `map` object are now accessible via one of the new objects `overlay`, `utility`, or `constants`. Some functions were renamed accordingly. See [porymap/#460](https://github.com/huderlem/porymap/pull/460) for a full list of API function name changes. - Arguments for the API function `createImage` have changed: `xflip` and `yflip` have been replaced with `hScale` and `vScale`, and `offset` has been replaced with `xOffset` and `yOffset`. +- The API function `addFilledRect` has been removed; it's been replaced by new arguments in `addRect`: `color` has been replaced with `borderColor` and `fillColor`, and a new `rounding` argument allows ellipses to be drawn. ### Added - Add prefab support - Add Cut/Copy/Paste for metatiles in the Tileset Editor. -- Add new features to the scripting API, including the ability to display message boxes and user input windows, set overlay opacity, get/set map header properties, read/write the map border, read tile pixel data, and set blocks or metatile attributes using a raw value. - Add button to copy the full metatile label to the clipboard in the Tileset Editor. - Add ability to export an image of the primary or secondary tileset's metatiles. - Add option to not open the most recent project on launch. - Add options for customizing how new maps are filled - Add color picker to palette editor for taking colors from the screen. +- Add new features to the scripting API, including the ability to display messages and user input windows, set the overlay's opacity, rotation, scale, and clipping, interact with map header properties and the map border, read tile pixel data, and more. ### Changed - Overhauled the region map editor, adding support for tilemaps, and significant customization. Also now supports pokefirered. - If an object event is inanimate, it will always render using its first frame. -- Only log "Unknown custom script function" when a registered script function is not present in any script. -- Unused metatile attribute bits that are set are preserved instead of being cleared. +- Unused metatile attribute bits are preserved instead of being cleared. - The wild encounter editor is automatically disabled if the encounter JSON data cannot be read - Metatiles are always rendered accurately with 3 layers, and the unused layer is not assumed to be transparent. - `object_event_graphics_info.h` can now be parsed correctly if it uses structs with attributes. @@ -34,6 +34,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - The selection is no longer reset when pasting events. The newly pasted events are selected instead. - Palette editor ui is updated a bit to allow hex and rgb value input. - The metatile behavior is now displayed in the bottom bar mouseover text. +- Removed some unnecessary error logs from the scripting API and added new useful ones. ### Fixed - Fix cursor tile outline not updating at the end of a dragged selection. @@ -43,8 +44,9 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Fix collision values of 2 or 3 not rendering properly. - Fix the map music dropdown being empty when importing a map from Advance Map. - Fix object events added by pasting ignoring the map event limit. -- Fixed a bug where saving the tileset editor would reselect the main editor's first selected metatile. +- Fix a bug where saving the tileset editor would reselect the main editor's first selected metatile. - Fix crashes / unexpected behavior if certain scripting API functions are given invalid palette or tile numbers. +- Fix drawing large amounts of text with the scripting API causing a significant drop in performance. - Silence unnecessary error logging when parsing C defines Porymap doesn't use. - Fix some windows like the Tileset Editor not raising to the front when reactivated. diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index fda7de02..b8c5742a 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -640,12 +640,13 @@ All tileset functions are callable via the global ``map`` object. :param number paletteIndex: the palette index :returns array: array of colors. Each color is a 3-element RGB array -.. js:function:: map.setPrimaryTilesetPalettePreview(paletteIndex, colors) +.. js:function:: map.setPrimaryTilesetPalettePreview(paletteIndex, colors, forceRedraw = true) Sets a palette in the primary tileset of the currently-opened map. This will NOT affect the true underlying colors--it only displays these colors in the map-editing area of Porymap. :param number paletteIndex: the palette index :param array colors: array of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palette. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getPrimaryTilesetPalettesPreview() @@ -653,11 +654,12 @@ All tileset functions are callable via the global ``map`` object. :returns array: array of arrays of colors. Each color is a 3-element RGB array -.. js:function:: map.setPrimaryTilesetPalettesPreview(palettes) +.. js:function:: map.setPrimaryTilesetPalettesPreview(palettes, forceRedraw = true) Sets all of the palettes in the primary tileset of the currently-opened map. This will NOT affect the true underlying colors--it only displays these colors in the map-editing area of Porymap. :param array palettes: array of arrays of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palettes. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getSecondaryTilesetPalettePreview(paletteIndex) @@ -666,12 +668,13 @@ All tileset functions are callable via the global ``map`` object. :param number paletteIndex: the palette index :returns array: array of colors. Each color is a 3-element RGB array -.. js:function:: map.setSecondaryTilesetPalettePreview(paletteIndex, colors) +.. js:function:: map.setSecondaryTilesetPalettePreview(paletteIndex, colors, forceRedraw = true) Sets a palette in the secondary tileset of the currently-opened map. This will NOT affect the true underlying colors--it only displays these colors in the map-editing area of Porymap. :param number paletteIndex: the palette index :param array colors: array of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palette. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getSecondaryTilesetPalettesPreview() @@ -679,11 +682,12 @@ All tileset functions are callable via the global ``map`` object. :returns array: array of arrays of colors. Each color is a 3-element RGB array -.. js:function:: map.setSecondaryTilesetPalettesPreview(palettes) +.. js:function:: map.setSecondaryTilesetPalettesPreview(palettes, forceRedraw = true) Sets all of the palettes in the secondary tileset of the currently-opened map. This will NOT affect the true underlying colors--it only displays these colors in the map-editing area of Porymap. :param array palettes: array of arrays of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palettes. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getPrimaryTilesetPalette(paletteIndex) @@ -692,12 +696,13 @@ All tileset functions are callable via the global ``map`` object. :param number paletteIndex: the palette index :returns array: array of colors. Each color is a 3-element RGB array -.. js:function:: map.setPrimaryTilesetPalette(paletteIndex, colors) +.. js:function:: map.setPrimaryTilesetPalette(paletteIndex, colors, forceRedraw = true) Sets a palette in the primary tileset of the currently-opened map. This will permanently affect the palette and save the palette to disk. :param number paletteIndex: the palette index :param array colors: array of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palette. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getPrimaryTilesetPalettes() @@ -705,11 +710,12 @@ All tileset functions are callable via the global ``map`` object. :returns array: array of arrays of colors. Each color is a 3-element RGB array -.. js:function:: map.setPrimaryTilesetPalettes(palettes) +.. js:function:: map.setPrimaryTilesetPalettes(palettes, forceRedraw = true) Sets all of the palettes in the primary tileset of the currently-opened map. This will permanently affect the palettes and save the palettes to disk. :param array palettes: array of arrays of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palettes. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getSecondaryTilesetPalette(paletteIndex) @@ -718,12 +724,13 @@ All tileset functions are callable via the global ``map`` object. :param number paletteIndex: the palette index :returns array: array of colors. Each color is a 3-element RGB array -.. js:function:: map.setSecondaryTilesetPalette(paletteIndex, colors) +.. js:function:: map.setSecondaryTilesetPalette(paletteIndex, colors, forceRedraw = true) Sets a palette in the secondary tileset of the currently-opened map. This will permanently affect the palette and save the palette to disk. :param number paletteIndex: the palette index :param array colors: array of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palette. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getSecondaryTilesetPalettes() @@ -731,11 +738,12 @@ All tileset functions are callable via the global ``map`` object. :returns array: array of arrays of colors. Each color is a 3-element RGB array -.. js:function:: map.setSecondaryTilesetPalettes(palettes) +.. js:function:: map.setSecondaryTilesetPalettes(palettes, forceRedraw = true) Sets all of the palettes in the secondary tileset of the currently-opened map. This will permanently affect the palettes and save the palettes to disk. :param array palettes: array of arrays of colors. Each color is a 3-element RGB array + :param boolean forceRedraw: Redraw the elements with the updated palettes. Defaults to ``true``. Redrawing the elements that use palettes is expensive, so it can be useful to batch together many calls to palette functions and only set ``redraw`` to ``true`` on the final call. .. js:function:: map.getMetatileLayerOrder() @@ -936,7 +944,7 @@ All tileset functions are callable via the global ``map`` object. Overlay Functions ^^^^^^^^^^^^^^^^^ -The following functions are related to an overlay that is drawn on top of the map area. Text, images, and shapes can be drawn using these functions. Items can be drawn and manipulated on separate layers by specifiying a layer id. Items on higher layer ids will be drawn above those on lower layers. The visibility, position, and opacity of each layer can be changed; by default all layers are visible, at position ``0,0``, and have an opacity of ``100``. +The following functions are related to an overlay that is drawn on top of the map area. Text, images, and shapes can be drawn using these functions. Items can be drawn and manipulated on separate layers by specifiying a layer id. Items on higher layer ids will be drawn above those on lower layers. The visibility, opacity, position, rotation, and scale of each layer can be changed; by default all layers are visible, have an opacity of ``100``, are at position ``0,0``, an angle of ``0``, and a horizontal and vertical scale of ``1.0``. All overlay functions are callable via the global ``overlay`` object. @@ -1010,6 +1018,79 @@ All overlay functions are callable via the global ``overlay`` object. :param number opacity: the opacity +.. js:function:: overlay.getHorizontalScale(layer = 0) + + Gets the horizontal scale of the specified overlay layer. ``1.0`` is normal size. + + :param number layer: the layer id. Defaults to ``0`` + :returns number: the scale + +.. js:function:: overlay.getVerticalScale(layer = 0) + + Gets the vertical scale of the specified overlay layer. ``1.0`` is normal size. + + :param number layer: the layer id. Defaults to ``0`` + :returns number: the scale + +.. js:function:: overlay.setHorizontalScale(scale, layer) + + Sets the horizontal scale of the specified overlay layer. ``1.0`` is normal size. + + :param number scale: the scale to set + :param number layer: the layer id + +.. js:function:: overlay.setHorizontalScale(scale) + + This is an overloaded function. Sets the horizontal scale of all active overlay layers. Layers that have not been used yet will not have their scale changed. ``1.0`` is normal size. + + :param number scale: the scale to set + +.. js:function:: overlay.setVerticalScale(scale, layer) + + Sets the vertical scale of the specified overlay layer. ``1.0`` is normal size. + + :param number scale: the scale to set + :param number layer: the layer id + +.. js:function:: overlay.setVerticalScale(scale) + + This is an overloaded function. Sets the vertical scale of all active overlay layers. Layers that have not been used yet will not have their scale changed. ``1.0`` is normal size. + + :param number scale: the scale to set + +.. js:function:: overlay.getRotation(layer = 0) + + Gets the angle the specified overlay layer is rotated to. + + :param number layer: the layer id. Defaults to ``0`` + :returns number: the angle the layer is rotated to + +.. js:function:: overlay.setRotation(angle, layer) + + Sets the angle the specified overlay layer is rotated to. + + :param number angle: the angle to set + :param number layer: the layer id + +.. js:function:: overlay.setRotation(angle) + + This is an overloaded function. Sets the angle that all active overlay layers are rotated to. Layers that have not been used yet will not have their angle changed. + + :param number angle: the angle to set + +.. js:function:: overlay.rotate(degrees, layer) + + Rotates the specified overlay layer. A positive number of degrees is clockwise rotation, a negative number of degrees is counterclockwise rotation. + + :param number degrees: the number of degrees to rotate + :param number layer: the layer id + +.. js:function:: overlay.rotate(degrees) + + This is an overloaded function. Rotates the all active overlay layers. Layers that have not been used yet will not be rotated. A positive number of degrees is clockwise rotation, a negative number of degrees is counterclockwise rotation. + + :param number degrees: the number of degrees to rotate + .. js:function:: overlay.getX(layer = 0) Gets the x position of the specified overlay layer. @@ -1123,11 +1204,11 @@ All overlay functions are callable via the global ``overlay`` object. :param string text: the text to display :param number x: the x pixel coordinate of the text (relative to the layer's position) :param number y: the y pixel coordinate of the text (relative to the layer's position) - :param string color: the color of the text. Can be specified as "#RRGGBB" or "#AARRGGBB". Defaults to black. + :param string color: the color of the text. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to black. :param number size: the font size of the text. Defaults to 12. :param number layer: the layer id. Defaults to ``0`` -.. js:function:: overlay.addRect(x, y, width, height, color = "#000000", layer = 0) +.. js:function:: overlay.addRect(x, y, width, height, borderColor = "#000000", fillColor = "", rounding = 0, layer = 0) Adds a rectangle outline item to the specified overlay layer. @@ -1135,18 +1216,28 @@ All overlay functions are callable via the global ``overlay`` object. :param number y: the y pixel coordinate of the rectangle's top-left corner (relative to the layer's position) :param number width: the pixel width of the rectangle :param number height: the pixel height of the rectangle - :param string color: the color of the rectangle. Can be specified as "#RRGGBB" or "#AARRGGBB". Defaults to black. + :param string borderColor: the color of the rectangle's border. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to black. + :param string fillColor: the color of the area enclosed by the rectangle. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to transparent. + :param number rounding: the percent degree the corners will be rounded. ``0`` is rectangular, ``100`` is elliptical. Defaults to ``0`` :param number layer: the layer id. Defaults to ``0`` -.. js:function:: overlay.addFilledRect(x, y, width, height, color = "#000000", layer = 0) +.. js:function:: overlay.addPath(coords, borderColor = "#000000", fillColor = "", layer = 0) - Adds a filled rectangle item to the specified overlay layer. + Draws a straight path on the specified layer by connecting the coordinate pairs in ``coords``. The area enclosed by the path can be colored in, and will follow the `"odd-even" fill rule `_. - :param number x: the x pixel coordinate of the rectangle's top-left corner (relative to the layer's position) - :param number y: the y pixel coordinate of the rectangle's top-left corner (relative to the layer's position) - :param number width: the pixel width of the rectangle - :param number height: the pixel height of the rectangle - :param string color: the color of the rectangle. Can be specified as "#RRGGBB" or "#AARRGGBB". Defaults to black. + :param array coords: array of pixel coordinates to connect to create the path. Each element of the array should be an array containing an x and y pixel coordinate + :param string borderColor: the color of the path. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to black. + :param string fillColor: the color of the area enclosed by the path. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to transparent. + :param number layer: the layer id. Defaults to ``0`` + +.. js:function:: overlay.addPath(xCoords, yCoords, borderColor = "#000000", fillColor = "", layer = 0) + + This is an overloaded function. Draws a straight path on the specified layer by connecting the coordinates at (``xCoords``, ``yCoords``). The area enclosed by the path can be colored in, and will follow the `"odd-even" fill rule `_. + + :param array xCoords: array of x pixel coordinates to connect to create the path + :param array yCoords: array of y pixel coordinates to connect to create the path + :param string borderColor: the color of the path. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to black. + :param string fillColor: the color of the area enclosed by the path. Can be specified as ``"#RRGGBB"`` or ``"#AARRGGBB"``. Defaults to transparent. :param number layer: the layer id. Defaults to ``0`` .. js:function:: overlay.addImage(x, y, filepath, layer = 0, useCache = true) diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index 05ece2f1..90227433 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -813,8 +813,8 @@ 0 0 - 428 - 77 + 423 + 74 @@ -1001,10 +1001,10 @@ - 0 + 8 0 411 - 449 + 413 @@ -1155,7 +1155,7 @@ 0 0 428 - 704 + 696 @@ -1387,7 +1387,7 @@ - 330 + 0 0 @@ -1404,16 +1404,6 @@ 3 - - - - There are no events on the current map. - - - Qt::AlignCenter - - - @@ -1423,11 +1413,14 @@ - QFrame::StyledPanel + QFrame::NoFrame QFrame::Raised + + 0 + 0 @@ -1522,8 +1515,24 @@ + + + + There are no events on the current map. + + + Qt::AlignCenter + + + + + + 0 + 0 + + 0 @@ -1544,8 +1553,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + object id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1557,8 +1615,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1589,8 +1647,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + warp id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1602,8 +1709,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1634,8 +1741,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + coord id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1647,8 +1803,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1679,8 +1835,63 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + + + + bg id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1692,8 +1903,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1724,8 +1935,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + heal id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1737,8 +1997,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1777,6 +2037,9 @@ 0 + + QFrame::NoFrame + true @@ -1788,8 +2051,8 @@ 0 0 - 98 - 28 + 434 + 625 @@ -2684,7 +2947,7 @@ 0 0 1287 - 21 + 22 diff --git a/include/core/event.h b/include/core/event.h deleted file mode 100644 index 7a82d5cd..00000000 --- a/include/core/event.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once -#ifndef EVENT_H -#define EVENT_H - -#include -#include -#include - -#include "orderedjson.h" - -using OrderedJson = poryjson::Json; - -class EventType -{ -public: - static QString Object; - static QString CloneObject; - static QString Warp; - static QString Trigger; - static QString WeatherTrigger; - static QString Sign; - static QString HiddenItem; - static QString SecretBase; - static QString HealLocation; -}; - -class EventGroup -{ -public: - static QString Object; - static QString Warp; - static QString Coord; - static QString Bg; - static QString Heal; -}; - -class DraggablePixmapItem; -class Project; -class Event -{ -public: - Event(); - Event(const Event&); - Event(QJsonObject, QString); -public: - int x() const { - return getInt("x"); - } - int y() const { - return getInt("y"); - } - int elevation() { - return getInt("elevation"); - } - void setX(int x) { - put("x", x); - } - void setY(int y) { - put("y", y); - } - QString get(const QString &key) const { - return values.value(key); - } - int getInt(const QString &key) const { - return values.value(key).toInt(nullptr, 0); - } - uint16_t getU16(const QString &key) const { - return values.value(key).toUShort(nullptr, 0); - } - int16_t getS16(const QString &key) const { - return values.value(key).toShort(nullptr, 0); - } - void put(QString key, int value) { - put(key, QString("%1").arg(value)); - } - void put(QString key, QString value) { - values.insert(key, value); - } - - static Event* createNewEvent(QString, QString, Project*); - static Event* createNewObjectEvent(Project*); - static Event* createNewCloneObjectEvent(Project*, QString); - static Event* createNewWarpEvent(QString); - static Event* createNewHealLocationEvent(QString); - static Event* createNewTriggerEvent(Project*); - static Event* createNewWeatherTriggerEvent(Project*); - static Event* createNewSignEvent(Project*); - static Event* createNewHiddenItemEvent(Project*); - static Event* createNewSecretBaseEvent(Project*); - static bool isValidType(QString event_type); - static QString typeToGroup(QString event_type); - static int getIndexOffset(QString group_type); - - OrderedJson::object buildObjectEventJSON(); - OrderedJson::object buildCloneObjectEventJSON(const QMap &); - OrderedJson::object buildWarpEventJSON(const QMap &); - OrderedJson::object buildTriggerEventJSON(); - OrderedJson::object buildWeatherTriggerEventJSON(); - OrderedJson::object buildSignEventJSON(); - OrderedJson::object buildHiddenItemEventJSON(); - OrderedJson::object buildSecretBaseEventJSON(); - void setPixmapFromSpritesheet(QImage, int, int, bool); - int getPixelX(); - int getPixelY(); - QSet getExpectedFields(); - void readCustomValues(QJsonObject values); - void addCustomValuesTo(OrderedJson::object *obj); - void setFrameFromMovement(QString); - - QMap values; - QMap customValues; - QPixmap pixmap; - int spriteWidth; - int spriteHeight; - int frame = 0; - bool hFlip = false; - bool usingSprite; - - DraggablePixmapItem *pixmapItem = nullptr; - void setPixmapItem(DraggablePixmapItem *item) { pixmapItem = item; } -}; - -#endif // EVENT_H diff --git a/include/core/events.h b/include/core/events.h new file mode 100644 index 00000000..51e82340 --- /dev/null +++ b/include/core/events.h @@ -0,0 +1,658 @@ +#pragma once +#ifndef EVENTS_H +#define EVENTS_H + +#include +#include +#include +#include +#include + +#include "orderedjson.h" +using OrderedJson = poryjson::Json; + + +class Project; +class Map; +class EventFrame; +class ObjectFrame; +class CloneObjectFrame; +class WarpFrame; +class DraggablePixmapItem; + +class Event; +class ObjectEvent; +class CloneObjectEvent; +class WarpEvent; +class CoordEvent; +class TriggerEvent; +class WeatherTriggerEvent; +class BgEvent; +class SignEvent; +class HiddenItemEvent; +class SecretBaseEvent; +class HealLocationEvent; + +class EventVisitor { +public: + virtual void nothing() { } + virtual void visitObject(ObjectEvent *) = 0; + virtual void visitTrigger(TriggerEvent *) = 0; + virtual void visitSign(SignEvent *) = 0; +}; + + + +/// +/// Event base class -- purely virtual +/// +class Event { +public: + virtual ~Event(); + + // disable copy constructor + Event(const Event &other) = delete; + + // disable assignment operator + Event& operator=(const Event &other) = delete; + +protected: + Event() { + this->spriteWidth = 16; + this->spriteHeight = 16; + this->usingSprite = false; + } + +// public enums & static methods +public: + enum class Type { + Object, CloneObject, + Warp, + Trigger, WeatherTrigger, + Sign, HiddenItem, SecretBase, + HealLocation, + Generic, + None, + }; + + enum class Group { + Object, + Warp, + Coord, + Bg, + Heal, + None, + }; + + // all event groups except warps have IDs that start at 1 + static int getIndexOffset(Event::Group group) { + return (group == Event::Group::Warp) ? 0 : 1; + } + + static Event::Group typeToGroup(Event::Type type) { + switch (type) { + case Event::Type::Object: + case Event::Type::CloneObject: + return Event::Group::Object; + case Event::Type::Warp: + return Event::Group::Warp; + case Event::Type::Trigger: + case Event::Type::WeatherTrigger: + return Event::Group::Coord; + case Event::Type::Sign: + case Event::Type::HiddenItem: + case Event::Type::SecretBase: + return Event::Group::Bg; + case Event::Type::HealLocation: + return Event::Group::Heal; + default: + return Event::Group::None; + } + } + + +// standard public methods +public: + + virtual Event *duplicate() = 0; + + void setMap(Map *newMap) { this->map = newMap; } + Map *getMap() const { return this->map; } + + void modify(); + + virtual void accept(EventVisitor *) { } + + void setX(int newX) { this->x = newX; } + void setY(int newY) { this->y = newY; } + void setZ(int newZ) { this->elevation = newZ; } + void setElevation(int newElevation) { this->elevation = newElevation; } + + int getX() const { return this->x; } + int getY() const { return this->y; } + int getZ() const { return this->elevation; } + int getElevation() const { return this->elevation; } + + int getPixelX() const { return (this->x * 16) - qMax(0, (this->spriteWidth - 16) / 2); } + int getPixelY() const { return (this->y * 16) - qMax(0, this->spriteHeight - 16); } + + virtual EventFrame *getEventFrame(); + virtual EventFrame *createEventFrame() = 0; + void destroyEventFrame(); + + Event::Group getEventGroup() const { return this->eventGroup; } + Event::Type getEventType() const { return this->eventType; } + + virtual OrderedJson::object buildEventJson(Project *project) = 0; + virtual bool loadFromJson(QJsonObject json, Project *project) = 0; + + virtual void setDefaultValues(Project *project); + + virtual QSet getExpectedFields() = 0; + void readCustomValues(QJsonObject values); + void addCustomValuesTo(OrderedJson::object *obj); + QMap getCustomValues() { return this->customValues; } + void setCustomValues(QMap newCustomValues) { this->customValues = newCustomValues; } + + virtual void loadPixmap(Project *project) = 0; + + void setPixmap(QPixmap newPixmap) { this->pixmap = newPixmap; } + QPixmap getPixmap() { return this->pixmap; } + + void setPixmapItem(DraggablePixmapItem *item); + DraggablePixmapItem *getPixmapItem() { return this->pixmapItem; } + + void setUsingSprite(bool newUsingSprite) { this->usingSprite = newUsingSprite; } + bool getUsingSprite() const { return this->usingSprite; } + + void setSpriteWidth(int newSpriteWidth) { this->spriteWidth = newSpriteWidth; } + int getspriteWidth() const { return this->spriteWidth; } + + void setSpriteHeight(int newSpriteHeight) { this->spriteHeight = newSpriteHeight; } + int getspriteHeight() const { return this->spriteHeight; } + + int getEventIndex(); + + static QString eventTypeToString(Event::Type type); + static Event::Type eventTypeFromString(QString type); + +// protected attributes +protected: + Map *map = nullptr; + + Type eventType = Event::Type::None; + Group eventGroup = Event::Group::None; + + // could be private? + int x = 0; + int y = 0; + int elevation = 0; + + int spriteWidth = 16; + int spriteHeight = 16; + bool usingSprite = false; + + QMap customValues; + + QPixmap pixmap; + DraggablePixmapItem *pixmapItem = nullptr; + + EventFrame *eventFrame = nullptr; +}; + + + +/// +/// Object Event +/// +class ObjectEvent : public Event { +public: + ObjectEvent() : Event() { + this->eventGroup = Event::Group::Object; + this->eventType = Event::Type::Object; + } + virtual ~ObjectEvent() {} + + virtual Event *duplicate() 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 void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + virtual void loadPixmap(Project *project) override; + + void setGfx(QString newGfx) { this->gfx = newGfx; } + QString getGfx() { return this->gfx; } + + void setMovement(QString newMovement) { this->movement = newMovement; } + QString getMovement() { return this->movement; } + + void setRadiusX(int newRadiusX) { this->radiusX = newRadiusX; } + int getRadiusX() { return this->radiusX; } + + void setRadiusY(int newRadiusY) { this->radiusY = newRadiusY; } + int getRadiusY() { return this->radiusY; } + + void setTrainerType(QString newTrainerType) { this->trainerType = newTrainerType; } + QString getTrainerType() { return this->trainerType; } + + void setSightRadiusBerryTreeID(QString newValue) { this->sightRadiusBerryTreeID = newValue; } + QString getSightRadiusBerryTreeID() { return this->sightRadiusBerryTreeID; } + + void setScript(QString newScript) { this->script = newScript; } + QString getScript() { return this->script; } + + void setFlag(QString newFlag) { this->flag = newFlag; } + QString getFlag() { return this->flag; } + +public: + void setFrameFromMovement(QString movement); + void setPixmapFromSpritesheet(QImage, int, int, bool); + + +protected: + QString gfx; + QString movement; + int radiusX = 0; + int radiusY = 0; + QString trainerType; + QString sightRadiusBerryTreeID; + QString script; + QString flag; + + int frame = 0; + bool hFlip = false; + bool vFlip = false; +}; + + + +/// +/// Clone Object Event +/// +class CloneObjectEvent : public ObjectEvent { + +public: + CloneObjectEvent() : ObjectEvent() { + this->eventGroup = Event::Group::Object; + this->eventType = Event::Type::CloneObject; + } + virtual ~CloneObjectEvent() {} + + virtual Event *duplicate() override; + + virtual EventFrame *createEventFrame() override; + + virtual OrderedJson::object buildEventJson(Project *project) override; + virtual bool loadFromJson(QJsonObject json, Project *project) override; + + virtual void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + virtual void loadPixmap(Project *project) override; + + void setTargetMap(QString newTargetMap) { this->targetMap = newTargetMap; } + QString getTargetMap() { return this->targetMap; } + + void setTargetID(int newTargetID) { this->targetID = newTargetID; } + int getTargetID() { return this->targetID; } + +private: + QString targetMap; + int targetID = 0; +}; + + + +/// +/// Warp Event +/// +class WarpEvent : public Event { + +public: + WarpEvent() : Event() { + this->eventGroup = Event::Group::Warp; + this->eventType = Event::Type::Warp; + } + virtual ~WarpEvent() {} + + virtual Event *duplicate() override; + + virtual EventFrame *createEventFrame() override; + + virtual OrderedJson::object buildEventJson(Project *project) override; + virtual bool loadFromJson(QJsonObject json, Project *project) override; + + virtual void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + virtual void loadPixmap(Project *) override; + + void setDestinationMap(QString newDestinationMap) { this->destinationMap = newDestinationMap; } + QString getDestinationMap() { return this->destinationMap; } + + void setDestinationWarpID(int newDestinationWarpID) { this->destinationWarpID = newDestinationWarpID; } + int getDestinationWarpID() { return this->destinationWarpID; } + +private: + QString destinationMap; + int destinationWarpID = 0; +}; + + + +/// +/// Coord Event +/// +class CoordEvent : public Event { + +public: + CoordEvent() : Event() {} + virtual ~CoordEvent() {} + + virtual Event *duplicate() 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 void setDefaultValues(Project *project) override = 0; + + virtual QSet getExpectedFields() override = 0; + + virtual void loadPixmap(Project *) override; +}; + + + +/// +/// Trigger Event +/// +class TriggerEvent : public CoordEvent { + +public: + TriggerEvent() : CoordEvent() { + this->eventGroup = Event::Group::Coord; + this->eventType = Event::Type::Trigger; + } + virtual ~TriggerEvent() {} + + virtual Event *duplicate() 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 void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + void setScriptVar(QString newScriptVar) { this->scriptVar = newScriptVar; } + QString getScriptVar() { return this->scriptVar; } + + void setScriptVarValue(QString newScriptVarValue) { this->scriptVarValue = newScriptVarValue; } + QString getScriptVarValue() { return this->scriptVarValue; } + + void setScriptLabel(QString newScriptLabel) { this->scriptLabel = newScriptLabel; } + QString getScriptLabel() { return this->scriptLabel; } + +private: + QString scriptVar; + QString scriptVarValue; + QString scriptLabel; +}; + + + +/// +/// Weather Trigger Event +/// +class WeatherTriggerEvent : public CoordEvent { + +public: + WeatherTriggerEvent() : CoordEvent() { + this->eventGroup = Event::Group::Coord; + this->eventType = Event::Type::WeatherTrigger; + } + virtual ~WeatherTriggerEvent() {} + + virtual Event *duplicate() override; + + virtual EventFrame *createEventFrame() override; + + virtual OrderedJson::object buildEventJson(Project *project) override; + virtual bool loadFromJson(QJsonObject json, Project *project) override; + + virtual void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + void setWeather(QString newWeather) { this->weather = newWeather; } + QString getWeather() { return this->weather; } + +private: + QString weather; +}; + + + +/// +/// BG Event +/// +class BGEvent : public Event { + +public: + BGEvent() : Event() { + this->eventGroup = Event::Group::Bg; + } + virtual ~BGEvent() {} + + virtual Event *duplicate() 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 void setDefaultValues(Project *project) override = 0; + + virtual QSet getExpectedFields() override = 0; + + virtual void loadPixmap(Project *project) override; +}; + + + +/// +/// Sign Event +/// +class SignEvent : public BGEvent { + +public: + SignEvent() : BGEvent() { + this->eventType = Event::Type::Sign; + } + virtual ~SignEvent() {} + + virtual Event *duplicate() 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 void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + void setFacingDirection(QString newFacingDirection) { this->facingDirection = newFacingDirection; } + QString getFacingDirection() { return this->facingDirection; } + + void setScriptLabel(QString newScriptLabel) { this->scriptLabel = newScriptLabel; } + QString getScriptLabel() { return this->scriptLabel; } + +private: + QString facingDirection; + QString scriptLabel; +}; + + + +/// +/// Hidden Item Event +/// +class HiddenItemEvent : public BGEvent { + +public: + HiddenItemEvent() : BGEvent() { + this->eventType = Event::Type::HiddenItem; + } + virtual ~HiddenItemEvent() {} + + virtual Event *duplicate() override; + + virtual EventFrame *createEventFrame() override; + + virtual OrderedJson::object buildEventJson(Project *project) override; + virtual bool loadFromJson(QJsonObject json, Project *project) override; + + virtual void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + void setItem(QString newItem) { this->item = newItem; } + QString getItem() { return this->item; } + + void setFlag(QString newFlag) { this->flag = newFlag; } + QString getFlag() { return this->flag; } + + void setQuantity(int newQuantity) { this->quantity = newQuantity; } + int getQuantity() { return this->quantity; } + + void setUnderfoot(bool newUnderfoot) { this->underfoot = newUnderfoot; } + bool getUnderfoot() { return this->underfoot; } + +private: + QString item; + QString flag; + + // optional + int quantity = 0; + bool underfoot = false; +}; + + + +/// +/// Secret Base Event +/// +class SecretBaseEvent : public BGEvent { + +public: + SecretBaseEvent() : BGEvent() { + this->eventType = Event::Type::SecretBase; + } + virtual ~SecretBaseEvent() {} + + virtual Event *duplicate() override; + + virtual EventFrame *createEventFrame() override; + + virtual OrderedJson::object buildEventJson(Project *project) override; + virtual bool loadFromJson(QJsonObject json, Project *project) override; + + virtual void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override; + + void setBaseID(QString newBaseID) { this->baseID = newBaseID; } + QString getBaseID() { return this->baseID; } + +private: + QString baseID; +}; + + + +/// +/// Heal Location Event +/// +class HealLocationEvent : public Event { + +public: + HealLocationEvent() : Event() { + this->eventGroup = Event::Group::Heal; + this->eventType = Event::Type::HealLocation; + } + virtual ~HealLocationEvent() {} + + virtual Event *duplicate() override { return nullptr; } + + virtual EventFrame *createEventFrame() override; + + virtual OrderedJson::object buildEventJson(Project *project) override; + virtual bool loadFromJson(QJsonObject, Project *) override { return false; } + + virtual void setDefaultValues(Project *project) override; + + virtual QSet getExpectedFields() override { return QSet(); } + + virtual void loadPixmap(Project *project) override; + + void setIndex(int newIndex) { this->index = newIndex; } + int getIndex() { return this->index; } + + 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(uint16_t newRespawnNPC) { this->respawnNPC = newRespawnNPC; } + uint16_t getRespawnNPC() { return this->respawnNPC; } + +private: + int index = -1; + QString locationName; + QString idName; + QString respawnMap; + uint16_t respawnNPC = 0; +}; + + + +/// +/// Keeps track of scripts +/// +class ScriptTracker : public EventVisitor { +public: + virtual void visitObject(ObjectEvent *object) override { this->scripts << object->getScript(); }; + 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; } + +private: + QStringList scripts; +}; + + +#endif // EVENTS_H diff --git a/include/core/heallocation.h b/include/core/heallocation.h index a39386c1..fbb93308 100644 --- a/include/core/heallocation.h +++ b/include/core/heallocation.h @@ -2,10 +2,11 @@ #ifndef HEALLOCATION_H #define HEALLOCATION_H -#include "event.h" #include #include +class Event; + class HealLocation { public: @@ -21,7 +22,7 @@ public: uint16_t y; QString respawnMap; uint16_t respawnNPC; - static HealLocation fromEvent(Event*); + static HealLocation fromEvent(Event *); }; #endif // HEALLOCATION_H diff --git a/include/core/map.h b/include/core/map.h index fb898317..24bcfd9e 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -6,7 +6,7 @@ #include "mapconnection.h" #include "maplayout.h" #include "tileset.h" -#include "event.h" +#include "events.h" #include #include @@ -63,7 +63,10 @@ public: QPixmap collision_pixmap; QImage image; QPixmap pixmap; - QMap> events; + + QMap> events; + QList ownedEvents; // for memory management + QList connections; QList metatileLayerOrder; QList metatileLayerOpacity; @@ -92,10 +95,10 @@ public: void floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); void _floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); void magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); - QList getAllEvents() const; - QStringList eventScriptLabels(const QString &event_group_type = QString()) const; - void removeEvent(Event*); - void addEvent(Event*); + QList getAllEvents() const; + QStringList eventScriptLabels(Event::Group group = Event::Group::None) const; + void removeEvent(Event *); + void addEvent(Event *); QPixmap renderConnection(MapConnection, MapLayout *); QPixmap renderBorder(bool ignoreCache = false); void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); @@ -104,9 +107,7 @@ public: bool hasUnsavedChanges(); bool isWithinBounds(int x, int y); bool isWithinBorderBounds(int x, int y); - - // for memory management - QVector ownedEvents; + void openScript(QString label); MapPixmapItem *mapItem = nullptr; void setMapItem(MapPixmapItem *item) { mapItem = item; } @@ -118,6 +119,8 @@ public: void setBorderItem(BorderMetatilesPixmapItem *item) { borderItem = item; } QUndoStack editHistory; + void modify(); + void clean(); private: void setNewDimensionsBlockdata(int newWidth, int newHeight); @@ -125,8 +128,10 @@ private: signals: void mapChanged(Map *map); + void modified(); void mapDimensionsChanged(const QSize &size); void mapNeedsRedrawing(); + void openScriptRequested(QString label); }; #endif // MAP_H diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 0200d7ae..298325ee 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -4,6 +4,7 @@ #include "heallocation.h" #include "log.h" +#include "orderedjson.h" #include #include diff --git a/include/core/regionmapeditcommands.h b/include/core/regionmapeditcommands.h index 16cd6486..69bea251 100644 --- a/include/core/regionmapeditcommands.h +++ b/include/core/regionmapeditcommands.h @@ -158,7 +158,7 @@ public: void undo() override; void redo() override; - bool mergeWith(const QUndoCommand *command) override { return false; } + bool mergeWith(const QUndoCommand *) override { return false; } int id() const override { return RMCommandId::ID_ClearEntries; } private: diff --git a/include/editor.h b/include/editor.h index 03573ae1..61de1e51 100644 --- a/include/editor.h +++ b/include/editor.h @@ -96,8 +96,7 @@ public: DraggablePixmapItem *addMapEvent(Event *event); void selectMapEvent(DraggablePixmapItem *object); void selectMapEvent(DraggablePixmapItem *object, bool toggle); - DraggablePixmapItem *addNewEvent(QString event_type); - void deleteEvent(Event *); + DraggablePixmapItem *addNewEvent(Event::Type type); void updateSelectedEvents(); void duplicateSelectedEvents(); void redrawObject(DraggablePixmapItem *item); @@ -105,6 +104,8 @@ public: void updateCursorRectPos(int x, int y); void setCursorRectVisible(bool visible); + bool eventLimitReached(Map *, Event::Type); + QGraphicsScene *scene = nullptr; QGraphicsPixmapItem *current_view = nullptr; MapPixmapItem *map_item = nullptr; @@ -131,8 +132,7 @@ public: CurrentSelectedMetatilesPixmapItem *current_metatile_selection_item = nullptr; MovementPermissionsSelector *movement_permissions_selector_item = nullptr; - QList *events = nullptr; - QList *selected_events = nullptr; + QList *selected_events = nullptr; QString map_edit_mode = "paint"; QString obj_edit_mode = "select"; @@ -151,7 +151,7 @@ public: void shouldReselectEvents(); void scaleMapView(int); void openInTextEditor(const QString &path, int lineNum = 0) const; - bool eventLimitReached(QString event_type); + bool eventLimitReached(Event::Type type); public slots: void openMapScripts() const; @@ -159,6 +159,7 @@ public slots: void openProjectInTextEditor() const; void maskNonVisibleConnectionTiles(); void onBorderMetatilesChanged(); + void selectedEventIndexChanged(int index, Event::Group eventGroup); private: void setConnectionItemsVisible(bool); @@ -207,10 +208,9 @@ private slots: signals: void objectsChanged(); - void selectedObjectsChanged(); void loadMapRequested(QString, QString); void wildMonDataChanged(); - void warpEventDoubleClicked(QString, QString, QString); + void warpEventDoubleClicked(QString, int, Event::Group); void currentMetatilesSelectionChanged(); void mapRulerStatusChanged(const QString &); void editedMapData(); diff --git a/include/lib/orderedjson.h b/include/lib/orderedjson.h index 0b43080c..c13ec13c 100644 --- a/include/lib/orderedjson.h +++ b/include/lib/orderedjson.h @@ -107,8 +107,8 @@ public: Json(double value); // NUMBER Json(int value); // NUMBER Json(bool value); // BOOL - Json(const QString &value); // STRING - Json(QString &&value); // STRING + Json(const QString &value); // STRING + Json(QString &&value); // STRING Json(const char * value); // STRING Json(const array &values); // ARRAY Json(array &&values); // ARRAY diff --git a/include/mainwindow.h b/include/mainwindow.h index 455e79eb..34915653 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -26,6 +26,8 @@ #include "shortcutseditor.h" #include "preferenceeditor.h" + + namespace Ui { class MainWindow; } @@ -78,10 +80,10 @@ public: Q_INVOKABLE void setBorderHeight(int height); void refreshAfterPaletteChange(Tileset *tileset); void setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors); - Q_INVOKABLE void setPrimaryTilesetPalette(int paletteIndex, QList> colors); - Q_INVOKABLE void setPrimaryTilesetPalettes(QList>> palettes); - Q_INVOKABLE void setSecondaryTilesetPalette(int paletteIndex, QList> colors); - Q_INVOKABLE void setSecondaryTilesetPalettes(QList>> palettes); + Q_INVOKABLE void setPrimaryTilesetPalette(int paletteIndex, QList> colors, bool forceRedraw = true); + Q_INVOKABLE void setPrimaryTilesetPalettes(QList>> palettes, bool forceRedraw = true); + Q_INVOKABLE void setSecondaryTilesetPalette(int paletteIndex, QList> colors, bool forceRedraw = true); + Q_INVOKABLE void setSecondaryTilesetPalettes(QList>> palettes, bool forceRedraw = true); QJSValue getTilesetPalette(const QList> &palettes, int paletteIndex); QJSValue getTilesetPalettes(const QList> &palettes); Q_INVOKABLE QJSValue getPrimaryTilesetPalette(int paletteIndex); @@ -90,10 +92,10 @@ public: Q_INVOKABLE QJSValue getSecondaryTilesetPalettes(); void refreshAfterPalettePreviewChange(); void setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors); - Q_INVOKABLE void setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors); - Q_INVOKABLE void setPrimaryTilesetPalettesPreview(QList>> palettes); - Q_INVOKABLE void setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors); - Q_INVOKABLE void setSecondaryTilesetPalettesPreview(QList>> palettes); + Q_INVOKABLE void setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors, bool forceRedraw = true); + Q_INVOKABLE void setPrimaryTilesetPalettesPreview(QList>> palettes, bool forceRedraw = true); + Q_INVOKABLE void setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors, bool forceRedraw = true); + Q_INVOKABLE void setSecondaryTilesetPalettesPreview(QList>> palettes, bool forceRedraw = true); Q_INVOKABLE QJSValue getPrimaryTilesetPalettePreview(int paletteIndex); Q_INVOKABLE QJSValue getPrimaryTilesetPalettesPreview(); Q_INVOKABLE QJSValue getSecondaryTilesetPalettePreview(int paletteIndex); @@ -161,7 +163,7 @@ private slots: void on_action_Reload_Project_triggered(); void on_mapList_activated(const QModelIndex &index); void on_action_Save_Project_triggered(); - void openWarpMap(QString map_name, QString event_id, QString event_group); + void openWarpMap(QString map_name, int event_id, Event::Group event_group); void duplicate(); void setClipboardData(poryjson::Json::object); @@ -216,7 +218,8 @@ private slots: void on_toolButton_deleteObject_clicked(); - void addNewEvent(QString); + void addNewEvent(Event::Type type); + void displayEventTabs(); void updateSelectedObjects(); void updateObjects(); @@ -265,8 +268,6 @@ private slots: void eventTabChanged(int index); - void selectedEventIndexChanged(int index); - void on_horizontalSlider_CollisionTransparency_valueChanged(int value); void on_toolButton_ExpandAll_clicked(); void on_toolButton_CollapseAll_clicked(); @@ -325,8 +326,6 @@ private: DraggablePixmapItem *selectedBG; DraggablePixmapItem *selectedHealspot; - QVector openScriptButtons; - bool isProgrammaticEventTabChange; bool projectHasUnsavedChanges; bool projectOpenFailure = false; @@ -374,7 +373,7 @@ private: void setTheme(QString); bool openRecentProject(); void updateTilesetEditor(); - QString getEventGroupFromTabWidget(QWidget *tab); + Event::Group getEventGroupFromTabWidget(QWidget *tab); void closeSupplementaryWindows(); void setWindowDisabled(bool); diff --git a/include/project.h b/include/project.h index cdc81615..a844b225 100644 --- a/include/project.h +++ b/include/project.h @@ -5,7 +5,6 @@ #include "map.h" #include "blockdata.h" #include "heallocation.h" -#include "event.h" #include "wildmoninfo.h" #include "parseutil.h" #include "orderedjson.h" @@ -83,6 +82,8 @@ public: QMap modifiedFileTimestamps; bool usingAsmTilesets; + const QPixmap entitiesPixmap = QPixmap(":/images/Entities_16x16.png"); + void set_root(QString); void initSignals(); @@ -194,7 +195,7 @@ public: bool readEventGraphics(); QMap> readObjEventGfxInfo(); - void setEventPixmap(Event * event, bool forceLoad = false); + void setEventPixmap(Event *event, bool forceLoad = false); QString fixPalettePath(QString path); QString fixGraphicPath(QString path); @@ -204,6 +205,7 @@ public: QString getMapScriptsFilePath(const QString &mapName) const; QStringList getEventScriptsFilePaths() const; QCompleter *getEventScriptLabelCompleter(QStringList additionalScriptLabels); + QStringList getGlobalScriptLabels(); void saveMapHealEvents(Map *map); diff --git a/include/ui/customattributestable.h b/include/ui/customattributestable.h index de353b7c..bf9334d0 100644 --- a/include/ui/customattributestable.h +++ b/include/ui/customattributestable.h @@ -1,7 +1,7 @@ #ifndef CUSTOMATTRIBUTESTABLE_H #define CUSTOMATTRIBUTESTABLE_H -#include "event.h" +#include "events.h" #include #include #include diff --git a/include/ui/draggablepixmapitem.h b/include/ui/draggablepixmapitem.h index a836ea23..9c318962 100644 --- a/include/ui/draggablepixmapitem.h +++ b/include/ui/draggablepixmapitem.h @@ -8,7 +8,7 @@ #include -#include "event.h" +#include "events.h" class Editor; @@ -17,10 +17,10 @@ class DraggablePixmapItem : public QObject, public QGraphicsPixmapItem { public: DraggablePixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) {} - DraggablePixmapItem(Event *event_, Editor *editor_) : QGraphicsPixmapItem(event_->pixmap) { - event = event_; + DraggablePixmapItem(Event *event, Editor *editor) : QGraphicsPixmapItem(event->getPixmap()) { + this->event = event; event->setPixmapItem(this); - editor = editor_; + this->editor = editor; updatePosition(); } @@ -37,8 +37,6 @@ public: void moveTo(const QPoint &pos); void emitPositionChanged(); void updatePixmap(); - void bind(QComboBox *combo, QString key); - void bindToUserData(QComboBox *combo, QString key); signals: void positionChanged(Event *event); @@ -49,25 +47,18 @@ signals: void onPropertyChanged(QString key, QString value); public slots: - void set_x(const QString &text) { - event->put("x", text); + void set_x(int x) { + event->setX(x); updatePosition(); } - void set_y(const QString &text) { - event->put("y", text); + void set_y(int y) { + event->setY(y); updatePosition(); } - void set_elevation(const QString &text) { - event->put("elevation", text); + void set_elevation(int z) { + event->setElevation(z); updatePosition(); } - void set_sprite(const QString &text) { - event->put("sprite", text); - updatePixmap(); - } - void set_script(const QString &text) { - event->put("script_label", text); - } protected: void mousePressEvent(QGraphicsSceneMouseEvent*); diff --git a/include/ui/eventframes.h b/include/ui/eventframes.h new file mode 100644 index 00000000..181ac141 --- /dev/null +++ b/include/ui/eventframes.h @@ -0,0 +1,291 @@ +#pragma once +#ifndef EVENTRAMES_H +#define EVENTRAMES_H + +#include + +#include "noscrollspinbox.h" +#include "noscrollcombobox.h" + +#include "events.h" + + + +class Project; + +class EventFrame : public QFrame { + Q_OBJECT + +public: + EventFrame(Event *event, QWidget *parent = nullptr) + : QFrame(parent), event(event) { } + + virtual void setup(); + void initCustomAttributesTable(); + virtual void connectSignals(); + virtual void initialize(); + virtual void populate(Project *project); + + void invalidateConnections(); + void invalidateUi(); + void invalidateValues(); + + virtual void setActive(bool active); + +public: + QLabel *label_id; + + QVBoxLayout *layout_main; + + QSpinBox *spinner_id; + + NoScrollSpinBox *spinner_x; + NoScrollSpinBox *spinner_y; + NoScrollSpinBox *spinner_z; + QLabel *hideable_label_z; + + QLabel *label_icon; + + QFrame *frame_contents; + QVBoxLayout *layout_contents; + +protected: + bool populated = false; + bool initialized = false; + bool connected = false; + +private: + Event *event; +}; + + + +class ObjectFrame : public EventFrame { + Q_OBJECT + +public: + ObjectFrame(ObjectEvent *object, QWidget *parent = nullptr) + : EventFrame(object, parent), object(object) {} + + virtual ~ObjectFrame() { + delete this->scriptCompleter; + } + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_sprite; + NoScrollComboBox *combo_movement; + NoScrollSpinBox *spinner_radius_x; + NoScrollSpinBox *spinner_radius_y; + NoScrollComboBox *combo_script; + QToolButton *button_script; + NoScrollComboBox *combo_flag; + NoScrollComboBox *combo_trainer_type; + NoScrollComboBox *combo_radius_treeid; + QCheckBox *check_in_connection; + +private: + ObjectEvent *object; + + QCompleter *scriptCompleter = nullptr; +}; + + + +class CloneObjectFrame : public EventFrame { + Q_OBJECT + +public: + CloneObjectFrame(CloneObjectEvent *clone, QWidget *parent = nullptr) + : EventFrame(clone, parent), clone(clone) {} + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_sprite; + NoScrollSpinBox *spinner_target_id; + NoScrollComboBox *combo_target_map; + +private: + CloneObjectEvent *clone; +}; + + + +class WarpFrame : public EventFrame { + Q_OBJECT + +public: + WarpFrame(WarpEvent *warp, QWidget *parent = nullptr) + : EventFrame(warp, parent), warp(warp) {} + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_dest_map; + NoScrollSpinBox *spinner_dest_warp; + +private: + WarpEvent *warp; +}; + + + +class TriggerFrame : public EventFrame { + Q_OBJECT + +public: + TriggerFrame(TriggerEvent *trigger, QWidget *parent = nullptr) + : EventFrame(trigger, parent), trigger(trigger) {} + + virtual ~TriggerFrame() { + delete this->scriptCompleter; + } + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_script; + NoScrollComboBox *combo_var; + NoScrollComboBox *combo_var_value; + +private: + TriggerEvent *trigger; + + QCompleter *scriptCompleter = nullptr; +}; + + + +class WeatherTriggerFrame : public EventFrame { + Q_OBJECT + +public: + WeatherTriggerFrame(WeatherTriggerEvent *weatherTrigger, QWidget *parent = nullptr) + : EventFrame(weatherTrigger, parent), weatherTrigger(weatherTrigger) {} + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_weather; + +private: + WeatherTriggerEvent *weatherTrigger; +}; + + + +class SignFrame : public EventFrame { + Q_OBJECT + +public: + SignFrame(SignEvent *sign, QWidget *parent = nullptr) + : EventFrame(sign, parent), sign(sign) {} + + virtual ~SignFrame() { + delete this->scriptCompleter; + } + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_facing_dir; + NoScrollComboBox *combo_script; + +private: + SignEvent *sign; + + QCompleter *scriptCompleter = nullptr; +}; + + + +class HiddenItemFrame : public EventFrame { + Q_OBJECT + +public: + HiddenItemFrame(HiddenItemEvent *hiddenItem, QWidget *parent = nullptr) + : EventFrame(hiddenItem, parent), hiddenItem(hiddenItem) {} + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + QFrame *hideable_quantity; + QFrame *hideable_itemfinder; + NoScrollComboBox *combo_item; + NoScrollComboBox *combo_flag; + NoScrollSpinBox *spinner_quantity; + QCheckBox *check_itemfinder; + +private: + HiddenItemEvent *hiddenItem; +}; + + + +class SecretBaseFrame : public EventFrame { + Q_OBJECT + +public: + SecretBaseFrame(SecretBaseEvent *secretBase, QWidget *parent = nullptr) + : EventFrame(secretBase, parent), secretBase(secretBase) {} + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + NoScrollComboBox *combo_base_id; + +private: + SecretBaseEvent *secretBase; +}; + + + +class HealLocationFrame : public EventFrame { + Q_OBJECT + +public: + HealLocationFrame(HealLocationEvent *healLocation, QWidget *parent = nullptr) + : EventFrame(healLocation, parent), healLocation(healLocation) {} + + virtual void setup() override; + virtual void initialize() override; + virtual void connectSignals() override; + virtual void populate(Project *project) override; + +public: + QFrame *hideable_respawn_map; + QFrame *hideable_respawn_npc; + NoScrollComboBox *combo_respawn_map; + NoScrollSpinBox *spinner_respawn_npc; + +private: + HealLocationEvent *healLocation; +}; + +#endif // EVENTRAMES_H diff --git a/include/ui/eventpropertiesframe.h b/include/ui/eventpropertiesframe.h deleted file mode 100644 index 7e526ab7..00000000 --- a/include/ui/eventpropertiesframe.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef EVENTPROPERTIESFRAME_H -#define EVENTPROPERTIESFRAME_H - -#include "event.h" - -#include - -namespace Ui { -class EventPropertiesFrame; -} - -class EventPropertiesFrame : public QFrame -{ - Q_OBJECT - -public: - explicit EventPropertiesFrame(Event *event, QWidget *parent = nullptr); - ~EventPropertiesFrame(); - void paintEvent(QPaintEvent*); - -public: - Ui::EventPropertiesFrame *ui; - -private: - Event *event; - bool firstShow = true; -}; - -#endif // EVENTPROPERTIESFRAME_H diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 957a8bbd..0d727b2a 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -44,9 +44,21 @@ public: Q_INVOKABLE int getOpacity(int layer = 0); Q_INVOKABLE void setOpacity(int opacity, int layer); Q_INVOKABLE void setOpacity(int opacity); + Q_INVOKABLE qreal getHorizontalScale(int layer = 0); + Q_INVOKABLE qreal getVerticalScale(int layer = 0); + Q_INVOKABLE void setHorizontalScale(qreal scale, int layer); + Q_INVOKABLE void setHorizontalScale(qreal scale); + Q_INVOKABLE void setVerticalScale(qreal scale, int layer); + Q_INVOKABLE void setVerticalScale(qreal scale); + Q_INVOKABLE int getRotation(int layer = 0); + Q_INVOKABLE void setRotation(int angle, int layer); + Q_INVOKABLE void setRotation(int angle); + Q_INVOKABLE void rotate(int degrees, int layer); + Q_INVOKABLE void rotate(int degrees); Q_INVOKABLE void addText(QString text, int x, int y, QString color = "#000000", int fontSize = 12, int layer = 0); - Q_INVOKABLE void addRect(int x, int y, int width, int height, QString color = "#000000", int layer = 0); - Q_INVOKABLE void addFilledRect(int x, int y, int width, int height, QString color = "#000000", int layer = 0); + Q_INVOKABLE void addRect(int x, int y, int width, int height, QString borderColor = "#000000", QString fillColor = "transparent", int rounding = 0, int layer = 0); + Q_INVOKABLE void addPath(QList> coords, QString borderColor = "#000000", QString fillColor = "transparent", int layer = 0); + Q_INVOKABLE void addPath(QList xCoords, QList yCoords, QString borderColor = "#000000", QString fillColor = "transparent", int layer = 0); Q_INVOKABLE void addImage(int x, int y, QString filepath, int layer = 0, bool useCache = true); Q_INVOKABLE void createImage(int x, int y, QString filepath, int width = -1, int height = -1, int xOffset = 0, int yOffset = 0, diff --git a/include/ui/neweventtoolbutton.h b/include/ui/neweventtoolbutton.h index fa2405df..721fe2f2 100644 --- a/include/ui/neweventtoolbutton.h +++ b/include/ui/neweventtoolbutton.h @@ -1,7 +1,7 @@ #ifndef NEWEVENTTOOLBUTTON_H #define NEWEVENTTOOLBUTTON_H -#include "event.h" +#include "events.h" #include class NewEventToolButton : public QToolButton @@ -9,7 +9,7 @@ class NewEventToolButton : public QToolButton Q_OBJECT public: explicit NewEventToolButton(QWidget *parent = nullptr); - QString getSelectedEventType(); + Event::Type getSelectedEventType(); QAction *newObjectAction; QAction *newCloneObjectAction; QAction *newWarpAction; @@ -30,9 +30,9 @@ public slots: void newHiddenItem(); void newSecretBase(); signals: - void newEventAdded(QString); + void newEventAdded(Event::Type); private: - QString selectedEventType; + Event::Type selectedEventType; void init(); }; diff --git a/include/ui/overlay.h b/include/ui/overlay.h index da379110..60c5f904 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -6,12 +6,13 @@ #include #include #include +#include class OverlayItem { public: OverlayItem() {} virtual ~OverlayItem() {}; - virtual void render(QPainter *, int, int) {}; + virtual void render(QPainter *) {}; }; class OverlayText : public OverlayItem { @@ -25,7 +26,7 @@ public: this->fontSize = fontSize; } ~OverlayText() {} - virtual void render(QPainter *painter, int x, int y); + virtual void render(QPainter *painter); private: const QStaticText text; int x; @@ -34,25 +35,19 @@ private: int fontSize; }; -class OverlayRect : public OverlayItem { +class OverlayPath : public OverlayItem { public: - OverlayRect(int x, int y, int width, int height, QColor color, bool filled) { - this->x = x; - this->y = y; - this->width = width; - this->height = height; - this->color = color; - this->filled = filled; + OverlayPath(QPainterPath path, QColor borderColor, QColor fillColor) { + this->path = path; + this->borderColor = borderColor; + this->fillColor = fillColor; } - ~OverlayRect() {} - virtual void render(QPainter *painter, int x, int y); + ~OverlayPath() {} + virtual void render(QPainter *painter); private: - int x; - int y; - int width; - int height; - QColor color; - bool filled; + QPainterPath path; + QColor borderColor; + QColor fillColor; }; class OverlayImage : public OverlayItem { @@ -63,7 +58,7 @@ public: this->image = image; } ~OverlayImage() {} - virtual void render(QPainter *painter, int x, int y); + virtual void render(QPainter *painter); private: int x; int y; @@ -76,6 +71,9 @@ public: Overlay() { this->x = 0; this->y = 0; + this->angle = 0; + this->hScale = 1.0; + this->vScale = 1.0; this->hidden = false; this->opacity = 1.0; this->clippingRect = nullptr; @@ -91,6 +89,13 @@ public: int getY(); void setX(int x); void setY(int y); + qreal getHScale(); + qreal getVScale(); + void setHScale(qreal scale); + void setVScale(qreal scale); + int getRotation(); + void setRotation(int angle); + void rotate(int degrees); void setClippingRect(QRectF rect); void clearClippingRect(); void setPosition(int x, int y); @@ -98,14 +103,20 @@ public: void renderItems(QPainter *painter); QList getItems(); void clearItems(); - void addText(const QString text, int x, int y, QString color = "#000000", int fontSize = 12); - void addRect(int x, int y, int width, int height, QString color = "#000000", bool filled = false); + void addText(const QString text, int x, int y, QString colorStr, int fontSize); + bool addRect(int x, int y, int width, int height, QString borderColorStr, QString fillColorStr, int rounding); bool addImage(int x, int y, QString filepath, bool useCache = true, int width = -1, int height = -1, int xOffset = 0, int yOffset = 0, qreal hScale = 1, qreal vScale = 1, QList palette = QList(), bool setTransparency = false); bool addImage(int x, int y, QImage image); + bool addPath(QList xCoords, QList yCoords, QString borderColorStr, QString fillColorStr); private: + void clampAngle(); + QColor getColor(QString colorStr); QList items; int x; int y; + int angle; + qreal hScale; + qreal vScale; bool hidden; qreal opacity; QRectF *clippingRect; diff --git a/porymap.pro b/porymap.pro index 97b017ce..b0e321a2 100644 --- a/porymap.pro +++ b/porymap.pro @@ -17,7 +17,7 @@ QMAKE_TARGET_BUNDLE_PREFIX = com.pret SOURCES += src/core/block.cpp \ src/core/blockdata.cpp \ - src/core/event.cpp \ + src/core/events.cpp \ src/core/heallocation.cpp \ src/core/imageexport.cpp \ src/core/map.cpp \ @@ -53,7 +53,7 @@ SOURCES += src/core/block.cpp \ src/ui/regionmapentriespixmapitem.cpp \ src/ui/cursortilerect.cpp \ src/ui/customattributestable.cpp \ - src/ui/eventpropertiesframe.cpp \ + src/ui/eventframes.cpp \ src/ui/filterchildrenproxymodel.cpp \ src/ui/graphicsview.cpp \ src/ui/imageproviders.cpp \ @@ -99,7 +99,7 @@ SOURCES += src/core/block.cpp \ HEADERS += include/core/block.h \ include/core/blockdata.h \ - include/core/event.h \ + include/core/events.h \ include/core/heallocation.h \ include/core/history.h \ include/core/imageexport.h \ @@ -136,7 +136,7 @@ HEADERS += include/core/block.h \ include/ui/regionmapentriespixmapitem.h \ include/ui/cursortilerect.h \ include/ui/customattributestable.h \ - include/ui/eventpropertiesframe.h \ + include/ui/eventframes.h \ include/ui/filterchildrenproxymodel.h \ include/ui/graphicsview.h \ include/ui/imageproviders.h \ @@ -185,7 +185,6 @@ HEADERS += include/core/block.h \ include/log.h FORMS += forms/mainwindow.ui \ - forms/eventpropertiesframe.ui \ forms/prefabcreationdialog.ui \ forms/prefabframe.ui \ forms/tileseteditor.ui \ @@ -209,5 +208,6 @@ INCLUDEPATH += include INCLUDEPATH += include/core INCLUDEPATH += include/ui INCLUDEPATH += include/lib +INCLUDEPATH += forms include(src/vendor/QtGifImage/gifimage/qtgifimage.pri) diff --git a/src/core/editcommands.cpp b/src/core/editcommands.cpp index 5b21d878..394c51cb 100644 --- a/src/core/editcommands.cpp +++ b/src/core/editcommands.cpp @@ -9,16 +9,16 @@ int getEventTypeMask(QList events) { int eventTypeMask = 0; for (auto event : events) { - QString groupType = event->get("event_group_type"); - if (groupType == EventGroup::Object) { + Event::Group groupType = event->getEventGroup(); + if (groupType == Event::Group::Object) { eventTypeMask |= IDMask_EventType_Object; - } else if (groupType == EventGroup::Warp) { + } else if (groupType == Event::Group::Warp) { eventTypeMask |= IDMask_EventType_Warp; - } else if (groupType == EventGroup::Coord) { + } else if (groupType == Event::Group::Coord) { eventTypeMask |= IDMask_EventType_Trigger; - } else if (groupType == EventGroup::Bg) { + } else if (groupType == Event::Group::Bg) { eventTypeMask |= IDMask_EventType_BG; - } else if (groupType == EventGroup::Heal) { + } else if (groupType == Event::Group::Heal) { eventTypeMask |= IDMask_EventType_Heal; } } @@ -261,13 +261,13 @@ void EventMove::redo() { QUndoCommand::redo(); for (Event *event : events) { - event->pixmapItem->move(deltaX, deltaY); + event->getPixmapItem()->move(deltaX, deltaY); } } void EventMove::undo() { for (Event *event : events) { - event->pixmapItem->move(-deltaX, -deltaY); + event->getPixmapItem()->move(-deltaX, -deltaY); } QUndoCommand::undo(); @@ -331,16 +331,16 @@ void EventCreate::redo() { // select this event editor->selected_events->clear(); - editor->selectMapEvent(event->pixmapItem, false); + editor->selectMapEvent(event->getPixmapItem(), false); } void EventCreate::undo() { map->removeEvent(event); - if (editor->scene->items().contains(event->pixmapItem)) { - editor->scene->removeItem(event->pixmapItem); + if (editor->scene->items().contains(event->getPixmapItem())) { + editor->scene->removeItem(event->getPixmapItem()); } - editor->selected_events->removeOne(event->pixmapItem); + editor->selected_events->removeOne(event->getPixmapItem()); editor->shouldReselectEvents(); @@ -377,15 +377,15 @@ void EventDelete::redo() { for (Event *event : selectedEvents) { map->removeEvent(event); - if (editor->scene->items().contains(event->pixmapItem)) { - editor->scene->removeItem(event->pixmapItem); + if (editor->scene->items().contains(event->getPixmapItem())) { + editor->scene->removeItem(event->getPixmapItem()); } - editor->selected_events->removeOne(event->pixmapItem); + editor->selected_events->removeOne(event->getPixmapItem()); } editor->selected_events->clear(); if (nextSelectedEvent) - editor->selected_events->append(nextSelectedEvent->pixmapItem); + editor->selected_events->append(nextSelectedEvent->getPixmapItem()); editor->shouldReselectEvents(); } @@ -399,7 +399,7 @@ void EventDelete::undo() { // select these events editor->selected_events->clear(); for (Event *event : selectedEvents) { - editor->selected_events->append(event->pixmapItem); + editor->selected_events->append(event->getPixmapItem()); } editor->shouldReselectEvents(); @@ -441,7 +441,7 @@ void EventDuplicate::redo() { // select these events editor->selected_events->clear(); for (Event *event : selectedEvents) { - editor->selected_events->append(event->pixmapItem); + editor->selected_events->append(event->getPixmapItem()); } editor->shouldReselectEvents(); } @@ -450,10 +450,10 @@ void EventDuplicate::undo() { for (Event *event : selectedEvents) { map->removeEvent(event); - if (editor->scene->items().contains(event->pixmapItem)) { - editor->scene->removeItem(event->pixmapItem); + if (editor->scene->items().contains(event->getPixmapItem())) { + editor->scene->removeItem(event->getPixmapItem()); } - editor->selected_events->removeOne(event->pixmapItem); + editor->selected_events->removeOne(event->getPixmapItem()); } editor->shouldReselectEvents(); @@ -471,7 +471,7 @@ int EventDuplicate::id() const { EventPaste::EventPaste(Editor *editor, Map *map, QList pastedEvents, - QUndoCommand *parent) : EventDuplicate(editor, map, pastedEvents) { + QUndoCommand *parent) : EventDuplicate(editor, map, pastedEvents, parent) { if (pastedEvents.size() > 1) { setText("Paste Events"); } else { diff --git a/src/core/event.cpp b/src/core/event.cpp deleted file mode 100644 index df247559..00000000 --- a/src/core/event.cpp +++ /dev/null @@ -1,480 +0,0 @@ -#include "event.h" -#include "map.h" -#include "project.h" -#include "config.h" - -QString EventGroup::Object = "object_event_group"; -QString EventGroup::Warp = "warp_event_group"; -QString EventGroup::Heal = "heal_event_group"; -QString EventGroup::Coord = "coord_event_group"; -QString EventGroup::Bg = "bg_event_group"; - -QString EventType::Object = "event_object"; -QString EventType::CloneObject = "event_clone_object"; -QString EventType::Warp = "event_warp"; -QString EventType::Trigger = "event_trigger"; -QString EventType::WeatherTrigger = "event_weather_trigger"; -QString EventType::Sign = "event_sign"; -QString EventType::HiddenItem = "event_hidden_item"; -QString EventType::SecretBase = "event_secret_base"; -QString EventType::HealLocation = "event_healspot"; - -const QMap EventTypeTable = { - {EventType::Object, EventGroup::Object}, - {EventType::CloneObject, EventGroup::Object}, - {EventType::Warp, EventGroup::Warp}, - {EventType::Trigger, EventGroup::Coord}, - {EventType::WeatherTrigger, EventGroup::Coord}, - {EventType::Sign, EventGroup::Bg}, - {EventType::HiddenItem, EventGroup::Bg}, - {EventType::SecretBase, EventGroup::Bg}, - {EventType::HealLocation, EventGroup::Heal}, -}; - -Event::Event() : - spriteWidth(16), - spriteHeight(16), - usingSprite(false) -{ } - -Event::Event(const Event& toCopy) : - values(toCopy.values), - customValues(toCopy.customValues), - pixmap(toCopy.pixmap), - spriteWidth(toCopy.spriteWidth), - spriteHeight(toCopy.spriteHeight), - frame(toCopy.frame), - hFlip(toCopy.hFlip), - usingSprite(toCopy.usingSprite) -{ } - -Event::Event(QJsonObject obj, QString type) : Event() -{ - this->put("event_type", type); - this->readCustomValues(obj); -} - -Event* Event::createNewEvent(QString event_type, QString map_name, Project *project) -{ - Event *event = nullptr; - if (event_type == EventType::Object) { - event = createNewObjectEvent(project); - event->setFrameFromMovement(event->get("movement_type")); - } else if (event_type == EventType::CloneObject) { - event = createNewCloneObjectEvent(project, map_name); - } else if (event_type == EventType::Warp) { - event = createNewWarpEvent(map_name); - } else if (event_type == EventType::HealLocation) { - event = createNewHealLocationEvent(map_name); - } else if (event_type == EventType::Trigger) { - event = createNewTriggerEvent(project); - } else if (event_type == EventType::WeatherTrigger) { - event = createNewWeatherTriggerEvent(project); - } else if (event_type == EventType::Sign) { - event = createNewSignEvent(project); - } else if (event_type == EventType::HiddenItem) { - event = createNewHiddenItemEvent(project); - } else if (event_type == EventType::SecretBase) { - event = createNewSecretBaseEvent(project); - } else { - // should never be reached but just in case - event = new Event; - } - - event->put("event_type", event_type); - event->put("event_group_type", typeToGroup(event_type)); - event->setX(0); - event->setY(0); - return event; -} - -Event* Event::createNewObjectEvent(Project *project) -{ - Event *event = new Event; - event->put("sprite", project->gfxDefines.keys().first()); - event->put("movement_type", project->movementTypes.first()); - event->put("radius_x", 0); - event->put("radius_y", 0); - event->put("script_label", "NULL"); - event->put("event_flag", "0"); - event->put("replacement", "0"); - event->put("trainer_type", project->trainerTypes.value(0, "0")); - event->put("sight_radius_tree_id", 0); - event->put("elevation", 3); - return event; -} - -Event* Event::createNewCloneObjectEvent(Project *project, QString map_name) -{ - Event *event = new Event; - event->put("sprite", project->gfxDefines.keys().first()); - event->put("target_local_id", 1); - event->put("target_map", map_name); - return event; -} - -Event* Event::createNewWarpEvent(QString map_name) -{ - Event *event = new Event; - event->put("destination_warp", 0); - event->put("destination_map_name", map_name); - event->put("elevation", 0); - return event; -} - -Event* Event::createNewHealLocationEvent(QString map_name) -{ - Event *event = new Event; - event->put("loc_name", QString(Map::mapConstantFromName(map_name)).remove(0,4)); - event->put("id_name", map_name.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2").toUpper()); - event->put("elevation", 3); - if (projectConfig.getHealLocationRespawnDataEnabled()) { - event->put("respawn_map", map_name); - event->put("respawn_npc", 1); - } - return event; -} - -Event* Event::createNewTriggerEvent(Project *project) -{ - Event *event = new Event; - event->put("script_label", "NULL"); - event->put("script_var", project->varNames.first()); - event->put("script_var_value", "0"); - event->put("elevation", 0); - return event; -} - -Event* Event::createNewWeatherTriggerEvent(Project *project) -{ - Event *event = new Event; - event->put("weather", project->coordEventWeatherNames.first()); - event->put("elevation", 0); - return event; -} - -Event* Event::createNewSignEvent(Project *project) -{ - Event *event = new Event; - event->put("player_facing_direction", project->bgEventFacingDirections.first()); - event->put("script_label", "NULL"); - event->put("elevation", 0); - return event; -} - -Event* Event::createNewHiddenItemEvent(Project *project) -{ - Event *event = new Event; - event->put("item", project->itemNames.first()); - event->put("flag", project->flagNames.first()); - event->put("elevation", 3); - if (projectConfig.getHiddenItemQuantityEnabled()) { - event->put("quantity", 1); - } - if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { - event->put("underfoot", false); - } - return event; -} - -Event* Event::createNewSecretBaseEvent(Project *project) -{ - Event *event = new Event; - event->put("secret_base_id", project->secretBaseIds.first()); - event->put("elevation", 0); - return event; -} - -int Event::getPixelX() -{ - return (this->x() * 16) - qMax(0, (this->spriteWidth - 16) / 2); -} - -int Event::getPixelY() -{ - return (this->y() * 16) - qMax(0, this->spriteHeight - 16); -} - -const QSet expectedObjectFields = { - "graphics_id", - "elevation", - "movement_type", - "movement_range_x", - "movement_range_y", - "trainer_type", - "trainer_sight_or_berry_tree_id", - "script", - "flag", -}; - -const QSet expectedCloneObjectFields = { - "type", - "graphics_id", - "target_local_id", - "target_map", -}; - -const QSet expectedWarpFields = { - "elevation", - "dest_map", - "dest_warp_id", -}; - -const QSet expectedTriggerFields = { - "type", - "elevation", - "var", - "var_value", - "script", -}; - -const QSet expectedWeatherTriggerFields = { - "type", - "elevation", - "weather", -}; - -const QSet expectedSignFields = { - "type", - "elevation", - "player_facing_dir", - "script", -}; - -const QSet expectedHiddenItemFields = { - "type", - "elevation", - "item", - "flag", -}; - -const QSet expectedSecretBaseFields = { - "type", - "elevation", - "secret_base_id", -}; - -QSet Event::getExpectedFields() -{ - QString type = this->get("event_type"); - QSet expectedFields = QSet(); - if (type == EventType::Object) { - expectedFields = expectedObjectFields; - if (projectConfig.getEventCloneObjectEnabled()) { - expectedFields.insert("type"); - } - } else if (type == EventType::CloneObject) { - expectedFields = expectedCloneObjectFields; - } else if (type == EventType::Warp) { - expectedFields = expectedWarpFields; - } else if (type == EventType::Trigger) { - expectedFields = expectedTriggerFields; - } else if (type == EventType::WeatherTrigger) { - expectedFields = expectedWeatherTriggerFields; - } else if (type == EventType::Sign) { - expectedFields = expectedSignFields; - } else if (type == EventType::HiddenItem) { - expectedFields = expectedHiddenItemFields; - if (projectConfig.getHiddenItemQuantityEnabled()) { - expectedFields.insert("quantity"); - } - if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { - expectedFields.insert("underfoot"); - } - } else if (type == EventType::SecretBase) { - expectedFields = expectedSecretBaseFields; - } - expectedFields << "x" << "y"; - return expectedFields; -}; - -void Event::readCustomValues(QJsonObject values) -{ - this->customValues.clear(); - QSet expectedFields = this->getExpectedFields(); - for (QString key : values.keys()) { - if (!expectedFields.contains(key)) { - this->customValues[key] = values[key].toString(); - } - } -} - -void Event::addCustomValuesTo(OrderedJson::object *obj) -{ - for (QString key : this->customValues.keys()) { - if (!obj->contains(key)) { - (*obj)[key] = this->customValues[key]; - } - } -} - -OrderedJson::object Event::buildObjectEventJSON() -{ - OrderedJson::object objectObj; - if (projectConfig.getEventCloneObjectEnabled()) { - objectObj["type"] = "object"; - } - objectObj["graphics_id"] = this->get("sprite"); - objectObj["x"] = this->getS16("x"); - objectObj["y"] = this->getS16("y"); - objectObj["elevation"] = this->getInt("elevation"); - objectObj["movement_type"] = this->get("movement_type"); - objectObj["movement_range_x"] = this->getInt("radius_x"); - objectObj["movement_range_y"] = this->getInt("radius_y"); - objectObj["trainer_type"] = this->get("trainer_type"); - objectObj["trainer_sight_or_berry_tree_id"] = this->get("sight_radius_tree_id"); - objectObj["script"] = this->get("script_label"); - objectObj["flag"] = this->get("event_flag"); - this->addCustomValuesTo(&objectObj); - - return objectObj; -} - -OrderedJson::object Event::buildCloneObjectEventJSON(const QMap &mapNamesToMapConstants) -{ - OrderedJson::object cloneObj; - cloneObj["type"] = "clone"; - cloneObj["graphics_id"] = this->get("sprite"); - cloneObj["x"] = this->getS16("x"); - cloneObj["y"] = this->getS16("y"); - cloneObj["target_local_id"] = this->getInt("target_local_id"); - cloneObj["target_map"] = mapNamesToMapConstants.value(this->get("target_map")); - this->addCustomValuesTo(&cloneObj); - - return cloneObj; -} - -OrderedJson::object Event::buildWarpEventJSON(const QMap &mapNamesToMapConstants) -{ - OrderedJson::object warpObj; - warpObj["x"] = this->getU16("x"); - warpObj["y"] = this->getU16("y"); - warpObj["elevation"] = this->getInt("elevation"); - warpObj["dest_map"] = mapNamesToMapConstants.value(this->get("destination_map_name")); - warpObj["dest_warp_id"] = this->getInt("destination_warp"); - this->addCustomValuesTo(&warpObj); - - return warpObj; -} - -OrderedJson::object Event::buildTriggerEventJSON() -{ - OrderedJson::object triggerObj; - triggerObj["type"] = "trigger"; - triggerObj["x"] = this->getU16("x"); - triggerObj["y"] = this->getU16("y"); - triggerObj["elevation"] = this->getInt("elevation"); - triggerObj["var"] = this->get("script_var"); - triggerObj["var_value"] = this->get("script_var_value"); - triggerObj["script"] = this->get("script_label"); - this->addCustomValuesTo(&triggerObj); - - return triggerObj; -} - -OrderedJson::object Event::buildWeatherTriggerEventJSON() -{ - OrderedJson::object weatherObj; - weatherObj["type"] = "weather"; - weatherObj["x"] = this->getU16("x"); - weatherObj["y"] = this->getU16("y"); - weatherObj["elevation"] = this->getInt("elevation"); - weatherObj["weather"] = this->get("weather"); - this->addCustomValuesTo(&weatherObj); - - return weatherObj; -} - -OrderedJson::object Event::buildSignEventJSON() -{ - OrderedJson::object signObj; - signObj["type"] = "sign"; - signObj["x"] = this->getU16("x"); - signObj["y"] = this->getU16("y"); - signObj["elevation"] = this->getInt("elevation"); - signObj["player_facing_dir"] = this->get("player_facing_direction"); - signObj["script"] = this->get("script_label"); - this->addCustomValuesTo(&signObj); - - return signObj; -} - -OrderedJson::object Event::buildHiddenItemEventJSON() -{ - OrderedJson::object hiddenItemObj; - hiddenItemObj["type"] = "hidden_item"; - hiddenItemObj["x"] = this->getU16("x"); - hiddenItemObj["y"] = this->getU16("y"); - hiddenItemObj["elevation"] = this->getInt("elevation"); - hiddenItemObj["item"] = this->get("item"); - hiddenItemObj["flag"] = this->get("flag"); - if (projectConfig.getHiddenItemQuantityEnabled()) { - hiddenItemObj["quantity"] = this->getInt("quantity"); - } - if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { - hiddenItemObj["underfoot"] = this->getInt("underfoot") > 0 || this->get("underfoot") == "TRUE"; - } - this->addCustomValuesTo(&hiddenItemObj); - - return hiddenItemObj; -} - -OrderedJson::object Event::buildSecretBaseEventJSON() -{ - OrderedJson::object secretBaseObj; - secretBaseObj["type"] = "secret_base"; - secretBaseObj["x"] = this->getU16("x"); - secretBaseObj["y"] = this->getU16("y"); - secretBaseObj["elevation"] = this->getInt("elevation"); - secretBaseObj["secret_base_id"] = this->get("secret_base_id"); - this->addCustomValuesTo(&secretBaseObj); - - return secretBaseObj; -} - -void Event::setPixmapFromSpritesheet(QImage spritesheet, int spriteWidth, int spriteHeight, bool inanimate) -{ - int frame = inanimate ? 0 : this->frame; - QImage img = spritesheet.copy(frame * spriteWidth % spritesheet.width(), 0, spriteWidth, spriteHeight); - if (this->hFlip && !inanimate) { - img = img.transformed(QTransform().scale(-1, 1)); - } - // Set first palette color fully transparent. - img.setColor(0, qRgba(0, 0, 0, 0)); - pixmap = QPixmap::fromImage(img); - this->spriteWidth = spriteWidth; - this->spriteHeight = spriteHeight; - this->usingSprite = true; -} - -void Event::setFrameFromMovement(QString facingDir) { - // defaults - this->frame = 0; - this->hFlip = false; - if (facingDir == "DIR_NORTH") { - this->frame = 1; - this->hFlip = false; - } else if (facingDir == "DIR_SOUTH") { - this->frame = 0; - this->hFlip = false; - } else if (facingDir == "DIR_WEST") { - this->frame = 2; - this->hFlip = false; - } else if (facingDir == "DIR_EAST") { - this->frame = 2; - this->hFlip = true; - } -} - -// All event groups excepts warps have IDs that start at 1 -int Event::getIndexOffset(QString group_type) { - return (group_type == EventGroup::Warp) ? 0 : 1; -} - -bool Event::isValidType(QString event_type) { - return EventTypeTable.contains(event_type); -} - -QString Event::typeToGroup(QString event_type) { - return EventTypeTable.value(event_type, QString()); -} diff --git a/src/core/events.cpp b/src/core/events.cpp new file mode 100644 index 00000000..a7b03b53 --- /dev/null +++ b/src/core/events.cpp @@ -0,0 +1,895 @@ +#include "events.h" + +#include "eventframes.h" +#include "project.h" +#include "config.h" + + + +Event::~Event() { + if (this->eventFrame) + this->eventFrame->deleteLater(); +} + +EventFrame *Event::getEventFrame() { + if (!this->eventFrame) createEventFrame(); + return this->eventFrame; +} + +void Event::destroyEventFrame() { + if (this->eventFrame) delete this->eventFrame; + this->eventFrame = nullptr; +} + +void Event::setPixmapItem(DraggablePixmapItem *item) { + this->pixmapItem = item; + if (this->eventFrame) { + this->eventFrame->invalidateConnections(); + } +} + +int Event::getEventIndex() { + return this->map->events.value(this->getEventGroup()).indexOf(this); +} + +void Event::setDefaultValues(Project *) { + this->setX(0); + this->setY(0); + this->setElevation(3); +} + +void Event::readCustomValues(QJsonObject values) { + this->customValues.clear(); + QSet expectedFields = this->getExpectedFields(); + for (QString key : values.keys()) { + if (!expectedFields.contains(key)) { + this->customValues[key] = values[key].toString(); + } + } +} + +void Event::addCustomValuesTo(OrderedJson::object *obj) { + for (QString key : this->customValues.keys()) { + if (!obj->contains(key)) { + (*obj)[key] = this->customValues[key]; + } + } +} + +void Event::modify() { + this->map->modify(); +} + +QString Event::eventTypeToString(Event::Type type) { + switch (type) { + case Event::Type::Object: + return "event_object"; + case Event::Type::CloneObject: + return "event_clone_object"; + case Event::Type::Warp: + return "event_warp"; + case Event::Type::Trigger: + return "event_trigger"; + case Event::Type::WeatherTrigger: + return "event_weather_trigger"; + case Event::Type::Sign: + return "event_sign"; + case Event::Type::HiddenItem: + return "event_hidden_item"; + case Event::Type::SecretBase: + return "event_secret_base"; + case Event::Type::HealLocation: + return "event_healspot"; + default: + return ""; + } +} + +Event::Type Event::eventTypeFromString(QString type) { + if (type == "event_object") { + return Event::Type::Object; + } else if (type == "event_clone_object") { + return Event::Type::CloneObject; + } else if (type == "event_warp") { + return Event::Type::Warp; + } else if (type == "event_trigger") { + return Event::Type::Trigger; + } else if (type == "event_weather_trigger") { + return Event::Type::WeatherTrigger; + } else if (type == "event_sign") { + return Event::Type::Sign; + } else if (type == "event_hidden_item") { + return Event::Type::HiddenItem; + } else if (type == "event_secret_base") { + return Event::Type::SecretBase; + } else if (type == "event_healspot") { + return Event::Type::HealLocation; + } else { + return Event::Type::None; + } +} + + + +Event *ObjectEvent::duplicate() { + ObjectEvent *copy = new ObjectEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setGfx(this->getGfx()); + copy->setMovement(this->getMovement()); + copy->setRadiusX(this->getRadiusX()); + copy->setRadiusY(this->getRadiusY()); + copy->setTrainerType(this->getTrainerType()); + copy->setSightRadiusBerryTreeID(this->getSightRadiusBerryTreeID()); + copy->setScript(this->getScript()); + copy->setFlag(this->getFlag()); + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *ObjectEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new ObjectFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object ObjectEvent::buildEventJson(Project *) { + OrderedJson::object objectJson; + + if (projectConfig.getEventCloneObjectEnabled()) { + objectJson["type"] = "object"; + } + objectJson["graphics_id"] = this->getGfx(); + objectJson["x"] = this->getX(); + objectJson["y"] = this->getY(); + objectJson["elevation"] = this->getElevation(); + objectJson["movement_type"] = this->getMovement(); + objectJson["movement_range_x"] = this->getRadiusX(); + objectJson["movement_range_y"] = this->getRadiusY(); + objectJson["trainer_type"] = this->getTrainerType(); + objectJson["trainer_sight_or_berry_tree_id"] = this->getSightRadiusBerryTreeID(); + objectJson["script"] = this->getScript(); + objectJson["flag"] = this->getFlag(); + this->addCustomValuesTo(&objectJson); + + return objectJson; +} + +bool ObjectEvent::loadFromJson(QJsonObject json, Project *) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setGfx(json["graphics_id"].toString()); + this->setMovement(json["movement_type"].toString()); + this->setRadiusX(json["movement_range_x"].toInt()); + this->setRadiusY(json["movement_range_y"].toInt()); + this->setTrainerType(json["trainer_type"].toString()); + this->setSightRadiusBerryTreeID(json["trainer_sight_or_berry_tree_id"].toString()); + this->setScript(json["script"].toString()); + this->setFlag(json["flag"].toString()); + + this->readCustomValues(json); + + return true; +} + +void ObjectEvent::setDefaultValues(Project *project) { + this->setGfx(project->gfxDefines.keys().first()); + this->setMovement(project->movementTypes.first()); + this->setScript("NULL"); + this->setTrainerType(project->trainerTypes.value(0, "0")); + + this->setRadiusX(0); + this->setRadiusY(0); + + this->setFrameFromMovement(project->facingDirections.value(this->getMovement())); +} + +const QSet expectedObjectFields = { + "graphics_id", + "elevation", + "movement_type", + "movement_range_x", + "movement_range_y", + "trainer_type", + "trainer_sight_or_berry_tree_id", + "script", + "flag", +}; + +QSet ObjectEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedObjectFields; + if (projectConfig.getEventCloneObjectEnabled()) { + expectedFields.insert("type"); + } + expectedFields << "x" << "y"; + return expectedFields; +} + +void ObjectEvent::loadPixmap(Project *project) { + EventGraphics *eventGfx = project->eventGraphicsMap.value(this->gfx, nullptr); + if (!eventGfx) { + // Invalid gfx constant. + // If this is a number, try to use that instead. + bool ok; + int altGfx = this->gfx.toInt(&ok); + if (ok && (altGfx < project->gfxDefines.count())) { + eventGfx = project->eventGraphicsMap.value(project->gfxDefines.key(altGfx, "NULL"), nullptr); + } + } + if (!eventGfx || eventGfx->spritesheet.isNull()) { + // No sprite associated with this gfx constant. + // Use default sprite instead. + this->pixmap = project->entitiesPixmap.copy(0, 0, 16, 16); + } else { + this->setFrameFromMovement(project->facingDirections.value(this->movement)); + this->setPixmapFromSpritesheet(eventGfx->spritesheet, eventGfx->spriteWidth, eventGfx->spriteHeight, eventGfx->inanimate); + } +} + +void ObjectEvent::setPixmapFromSpritesheet(QImage spritesheet, int spriteWidth, int spriteHeight, bool inanimate) +{ + int frame = inanimate ? 0 : this->frame; + QImage img = spritesheet.copy(frame * spriteWidth % spritesheet.width(), 0, spriteWidth, spriteHeight); + if (this->hFlip && !inanimate) { + img = img.transformed(QTransform().scale(-1, 1)); + } + // Set first palette color fully transparent. + img.setColor(0, qRgba(0, 0, 0, 0)); + pixmap = QPixmap::fromImage(img); + this->spriteWidth = spriteWidth; + this->spriteHeight = spriteHeight; + this->usingSprite = true; +} + +void ObjectEvent::setFrameFromMovement(QString facingDir) { + // defaults + // TODO: read this from a file somewhere? + this->frame = 0; + this->hFlip = false; + if (facingDir == "DIR_NORTH") { + this->frame = 1; + this->hFlip = false; + } else if (facingDir == "DIR_SOUTH") { + this->frame = 0; + this->hFlip = false; + } else if (facingDir == "DIR_WEST") { + this->frame = 2; + this->hFlip = false; + } else if (facingDir == "DIR_EAST") { + this->frame = 2; + this->hFlip = true; + } +} + + + +Event *CloneObjectEvent::duplicate() { + CloneObjectEvent *copy = new CloneObjectEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setGfx(this->getGfx()); + copy->setTargetID(this->getTargetID()); + copy->setTargetMap(this->getTargetMap()); + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *CloneObjectEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new CloneObjectFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) { + OrderedJson::object cloneJson; + + cloneJson["type"] = "clone"; + cloneJson["graphics_id"] = this->getGfx(); + cloneJson["x"] = this->getX(); + cloneJson["y"] = this->getY(); + cloneJson["target_local_id"] = this->getTargetID(); + cloneJson["target_map"] = project->mapNamesToMapConstants.value(this->getTargetMap()); + this->addCustomValuesTo(&cloneJson); + + return cloneJson; +} + +bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setGfx(json["graphics_id"].toString()); + this->setTargetID(json["target_local_id"].toInt()); + + // Ensure the target map constant is valid before adding it to the events. + QString mapConstant = json["target_map"].toString(); + if (project->mapConstantsToMapNames.contains(mapConstant)) { + this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant)); + } else if (mapConstant == NONE_MAP_CONSTANT) { + this->setTargetMap(NONE_MAP_NAME); + } else { + logError(QString("Destination map constant '%1' is invalid").arg(mapConstant)); + return false; + } + + this->readCustomValues(json); + + return true; +} + +void CloneObjectEvent::setDefaultValues(Project *project) { + this->setGfx(project->gfxDefines.keys().first()); + this->setTargetID(1); + if (this->getMap()) this->setTargetMap(this->getMap()->name); +} + +const QSet expectedCloneObjectFields = { + "type", + "graphics_id", + "target_local_id", + "target_map", +}; + +QSet CloneObjectEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedCloneObjectFields; + expectedFields << "x" << "y"; + return expectedFields; +} + +void CloneObjectEvent::loadPixmap(Project *project) { + // Try to get the targeted object to clone + int eventIndex = this->targetID - 1; + Map *clonedMap = project->getMap(this->targetMap); + Event *clonedEvent = clonedMap ? clonedMap->events[Event::Group::Object].value(eventIndex, nullptr) : nullptr; + + if (clonedEvent && clonedEvent->getEventType() == Event::Type::Object) { + // Get graphics data from cloned object + ObjectEvent *clonedObject = dynamic_cast(clonedEvent); + this->gfx = clonedObject->getGfx(); + this->movement = clonedObject->getMovement(); + } else { + // Invalid object specified, use default graphics data (as would be shown in-game) + this->gfx = project->gfxDefines.key(0); + this->movement = project->movementTypes.first(); + } + + EventGraphics *eventGfx = project->eventGraphicsMap.value(gfx, nullptr); + if (!eventGfx || eventGfx->spritesheet.isNull()) { + // No sprite associated with this gfx constant. + // Use default sprite instead. + this->pixmap = project->entitiesPixmap.copy(0, 0, 16, 16); + } else { + this->setFrameFromMovement(project->facingDirections.value(this->movement)); + this->setPixmapFromSpritesheet(eventGfx->spritesheet, eventGfx->spriteWidth, eventGfx->spriteHeight, eventGfx->inanimate); + } +} + + + +Event *WarpEvent::duplicate() { + WarpEvent *copy = new WarpEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setDestinationMap(this->getDestinationMap()); + copy->setDestinationWarpID(this->getDestinationWarpID()); + + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *WarpEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new WarpFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object WarpEvent::buildEventJson(Project *project) { + OrderedJson::object warpJson; + + warpJson["x"] = this->getX(); + warpJson["y"] = this->getY(); + warpJson["elevation"] = this->getElevation(); + warpJson["dest_map"] = project->mapNamesToMapConstants.value(this->getDestinationMap()); + warpJson["dest_warp_id"] = this->getDestinationWarpID(); + + this->addCustomValuesTo(&warpJson); + + return warpJson; +} + +bool WarpEvent::loadFromJson(QJsonObject json, Project *project) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setDestinationWarpID(json["dest_warp_id"].toInt()); + + // Ensure the warp destination map constant is valid before adding it to the warps. + QString mapConstant = json["dest_map"].toString(); + if (project->mapConstantsToMapNames.contains(mapConstant)) { + this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant)); + } else if (mapConstant == NONE_MAP_CONSTANT) { + this->setDestinationMap(NONE_MAP_NAME); + } else { + logError(QString("Destination map constant '%1' is invalid for warp").arg(mapConstant)); + return false; + } + + this->readCustomValues(json); + + return true; +} + +void WarpEvent::setDefaultValues(Project *) { + if (this->getMap()) this->setDestinationMap(this->getMap()->name); + this->setDestinationWarpID(0); + this->setElevation(0); +} + +const QSet expectedWarpFields = { + "elevation", + "dest_map", + "dest_warp_id", +}; + +QSet WarpEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedWarpFields; + expectedFields << "x" << "y"; + return expectedFields; +} + +void WarpEvent::loadPixmap(Project *project) { + this->pixmap = project->entitiesPixmap.copy(16, 0, 16, 16); +} + + + +void CoordEvent::loadPixmap(Project *project) { + this->pixmap = project->entitiesPixmap.copy(32, 0, 16, 16); +} + + + +Event *TriggerEvent::duplicate() { + TriggerEvent *copy = new TriggerEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setScriptVar(this->getScriptVar()); + copy->setScriptVarValue(this->getScriptVarValue()); + copy->setScriptLabel(this->getScriptLabel()); + + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *TriggerEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new TriggerFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object TriggerEvent::buildEventJson(Project *) { + OrderedJson::object triggerJson; + + triggerJson["type"] = "trigger"; + triggerJson["x"] = this->getX(); + triggerJson["y"] = this->getY(); + triggerJson["elevation"] = this->getElevation(); + triggerJson["var"] = this->getScriptVar(); + triggerJson["var_value"] = this->getScriptVarValue(); + triggerJson["script"] = this->getScriptLabel(); + + this->addCustomValuesTo(&triggerJson); + + return triggerJson; +} + +bool TriggerEvent::loadFromJson(QJsonObject json, Project *) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setScriptVar(json["var"].toString()); + this->setScriptVarValue(json["var_value"].toString()); + this->setScriptLabel(json["script"].toString()); + + this->readCustomValues(json); + + return true; +} + +void TriggerEvent::setDefaultValues(Project *project) { + this->setScriptLabel("NULL"); + this->setScriptVar(project->varNames.first()); + this->setScriptVarValue("0"); + this->setElevation(0); +} + +const QSet expectedTriggerFields = { + "type", + "elevation", + "var", + "var_value", + "script", +}; + +QSet TriggerEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedTriggerFields; + expectedFields << "x" << "y"; + return expectedFields; +} + + + +Event *WeatherTriggerEvent::duplicate() { + WeatherTriggerEvent *copy = new WeatherTriggerEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setWeather(this->getWeather()); + + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *WeatherTriggerEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new WeatherTriggerFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object WeatherTriggerEvent::buildEventJson(Project *) { + OrderedJson::object weatherJson; + + weatherJson["type"] = "weather"; + weatherJson["x"] = this->getX(); + weatherJson["y"] = this->getY(); + weatherJson["elevation"] = this->getElevation(); + weatherJson["weather"] = this->getWeather(); + + this->addCustomValuesTo(&weatherJson); + + return weatherJson; +} + +bool WeatherTriggerEvent::loadFromJson(QJsonObject json, Project *) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setWeather(json["weather"].toString()); + + this->readCustomValues(json); + + return true; +} + +void WeatherTriggerEvent::setDefaultValues(Project *project) { + this->setWeather(project->coordEventWeatherNames.first()); + this->setElevation(0); +} + +const QSet expectedWeatherTriggerFields = { + "type", + "elevation", + "weather", +}; + +QSet WeatherTriggerEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedWeatherTriggerFields; + expectedFields << "x" << "y"; + return expectedFields; +} + + + +void BGEvent::loadPixmap(Project *project) { + this->pixmap = project->entitiesPixmap.copy(48, 0, 16, 16); +} + + + +Event *SignEvent::duplicate() { + SignEvent *copy = new SignEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setFacingDirection(this->getFacingDirection()); + copy->setScriptLabel(this->getScriptLabel()); + + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *SignEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new SignFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object SignEvent::buildEventJson(Project *) { + OrderedJson::object signJson; + + signJson["type"] = "sign"; + signJson["x"] = this->getX(); + signJson["y"] = this->getY(); + signJson["elevation"] = this->getElevation(); + signJson["player_facing_dir"] = this->getFacingDirection(); + signJson["script"] = this->getScriptLabel(); + + this->addCustomValuesTo(&signJson); + + return signJson; +} + +bool SignEvent::loadFromJson(QJsonObject json, Project *) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setFacingDirection(json["player_facing_dir"].toString()); + this->setScriptLabel(json["script"].toString()); + + this->readCustomValues(json); + + return true; +} + +void SignEvent::setDefaultValues(Project *project) { + this->setFacingDirection(project->bgEventFacingDirections.first()); + this->setScriptLabel("NULL"); + this->setElevation(0); +} + +const QSet expectedSignFields = { + "type", + "elevation", + "player_facing_dir", + "script", +}; + +QSet SignEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedSignFields; + expectedFields << "x" << "y"; + return expectedFields; +} + + + +Event *HiddenItemEvent::duplicate() { + HiddenItemEvent *copy = new HiddenItemEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setItem(this->getItem()); + copy->setFlag(this->getFlag()); + copy->setQuantity(this->getQuantity()); + copy->setQuantity(this->getQuantity()); + + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *HiddenItemEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new HiddenItemFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object HiddenItemEvent::buildEventJson(Project *) { + OrderedJson::object hiddenItemJson; + + hiddenItemJson["type"] = "hidden_item"; + hiddenItemJson["x"] = this->getX(); + hiddenItemJson["y"] = this->getY(); + hiddenItemJson["elevation"] = this->getElevation(); + hiddenItemJson["item"] = this->getItem(); + hiddenItemJson["flag"] = this->getFlag(); + if (projectConfig.getHiddenItemQuantityEnabled()) { + hiddenItemJson["quantity"] = this->getQuantity(); + } + if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { + hiddenItemJson["underfoot"] = this->getUnderfoot(); + } + + this->addCustomValuesTo(&hiddenItemJson); + + return hiddenItemJson; +} + +bool HiddenItemEvent::loadFromJson(QJsonObject json, Project *) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setItem(json["item"].toString()); + this->setFlag(json["flag"].toString()); + if (projectConfig.getHiddenItemQuantityEnabled()) { + this->setQuantity(json["quantity"].toInt()); + } + if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { + this->setUnderfoot(json["underfoot"].toBool()); + } + + this->readCustomValues(json); + + return true; +} + +void HiddenItemEvent::setDefaultValues(Project *project) { + this->setItem(project->itemNames.first()); + this->setFlag(project->flagNames.first()); + if (projectConfig.getHiddenItemQuantityEnabled()) { + this->setQuantity(1); + } + if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { + this->setUnderfoot(false); + } +} + +const QSet expectedHiddenItemFields = { + "type", + "elevation", + "item", + "flag", +}; + +QSet HiddenItemEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedHiddenItemFields; + if (projectConfig.getHiddenItemQuantityEnabled()) { + expectedFields << "quantity"; + } + if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { + expectedFields << "underfoot"; + } + expectedFields << "x" << "y"; + return expectedFields; +} + + + +Event *SecretBaseEvent::duplicate() { + SecretBaseEvent *copy = new SecretBaseEvent(); + + copy->setX(this->getX()); + copy->setY(this->getY()); + copy->setElevation(this->getElevation()); + copy->setBaseID(this->getBaseID()); + + copy->setCustomValues(this->getCustomValues()); + + return copy; +} + +EventFrame *SecretBaseEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new SecretBaseFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object SecretBaseEvent::buildEventJson(Project *) { + OrderedJson::object secretBaseJson; + + secretBaseJson["type"] = "secret_base"; + secretBaseJson["x"] = this->getX(); + secretBaseJson["y"] = this->getY(); + secretBaseJson["elevation"] = this->getElevation(); + secretBaseJson["secret_base_id"] = this->getBaseID(); + + this->addCustomValuesTo(&secretBaseJson); + + return secretBaseJson; +} + +bool SecretBaseEvent::loadFromJson(QJsonObject json, Project *) { + this->setX(json["x"].toInt()); + this->setY(json["y"].toInt()); + this->setElevation(json["elevation"].toInt()); + this->setBaseID(json["secret_base_id"].toString()); + + this->readCustomValues(json); + + return true; +} + +void SecretBaseEvent::setDefaultValues(Project *project) { + this->setBaseID(project->secretBaseIds.first()); + this->setElevation(0); +} + +const QSet expectedSecretBaseFields = { + "type", + "elevation", + "secret_base_id", +}; + +QSet SecretBaseEvent::getExpectedFields() { + QSet expectedFields = QSet(); + expectedFields = expectedSecretBaseFields; + expectedFields << "x" << "y"; + return expectedFields; +} + + + +EventFrame *HealLocationEvent::createEventFrame() { + if (!this->eventFrame) { + this->eventFrame = new HealLocationFrame(this); + this->eventFrame->setup(); + + QObject::connect(this->eventFrame, &QObject::destroyed, [this](){ this->eventFrame = nullptr; }); + } + return this->eventFrame; +} + +OrderedJson::object HealLocationEvent::buildEventJson(Project *) { + return OrderedJson::object(); +} + +void HealLocationEvent::setDefaultValues(Project *) { + if (this->getMap()) { + this->setLocationName(Map::mapConstantFromName(this->getMap()->name).remove(0,4)); + this->setIdName(this->getMap()->name.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2").toUpper()); + } + this->setElevation(3); + if (projectConfig.getHealLocationRespawnDataEnabled()) { + if (this->getMap()) this->setRespawnMap(this->getMap()->name); + this->setRespawnNPC(1); + } +} + +void HealLocationEvent::loadPixmap(Project *project) { + this->pixmap = project->entitiesPixmap.copy(64, 0, 16, 16); +} diff --git a/src/core/heallocation.cpp b/src/core/heallocation.cpp index 8ecb70a8..7dc59a19 100644 --- a/src/core/heallocation.cpp +++ b/src/core/heallocation.cpp @@ -1,9 +1,11 @@ #include "heallocation.h" #include "config.h" +#include "events.h" #include "map.h" -HealLocation::HealLocation(QString id, QString map, int i, uint16_t x, uint16_t y, QString respawnMap, uint16_t respawnNPC) -{ +HealLocation::HealLocation(QString id, QString map, + int i, uint16_t x, uint16_t y, + QString respawnMap, uint16_t respawnNPC) { this->idName = id; this->mapName = map; this->index = i; @@ -13,28 +15,23 @@ HealLocation::HealLocation(QString id, QString map, int i, uint16_t x, uint16_t this->respawnNPC = respawnNPC; } -HealLocation HealLocation::fromEvent(Event *event) -{ - HealLocation hl; - hl.idName = event->get("id_name"); - hl.mapName = event->get("loc_name"); - try { - hl.index = event->get("index").toInt(); - } - catch(...) { - hl.index = 0; - } - hl.x = event->getU16("x"); - hl.y = event->getU16("y"); +HealLocation HealLocation::fromEvent(Event *fromEvent) { + HealLocationEvent *event = dynamic_cast(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.getHealLocationRespawnDataEnabled()) { - hl.respawnNPC = event->getU16("respawn_npc"); - hl.respawnMap = Map::mapConstantFromName(event->get("respawn_map")).remove(0,4); + healLocation.respawnNPC = event->getRespawnNPC(); + healLocation.respawnMap = Map::mapConstantFromName(event->getRespawnMap()).remove(0,4); } - return hl; + return healLocation; } -QDebug operator<<(QDebug debug, const HealLocation &hl) -{ - debug << "HealLocation_" + hl.mapName << "(" << hl.x << ',' << hl.y << ")"; +QDebug operator<<(QDebug debug, const HealLocation &healLocation) { + debug << "HealLocation_" + healLocation.mapName << "(" << healLocation.x << ',' << healLocation.y << ")"; return debug; } diff --git a/src/core/map.cpp b/src/core/map.cpp index 107033b3..8080682e 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -340,6 +340,10 @@ void Map::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, emit mapChanged(this); } +void Map::openScript(QString label) { + emit openScriptRequested(label); +} + bool Map::getBlock(int x, int y, Block *out) { if (isWithinBounds(x, y)) { int i = y * getWidth() + x; @@ -474,37 +478,51 @@ QList Map::getAllEvents() const { return all_events; } -QStringList Map::eventScriptLabels(const QString &event_group_type) const { +QStringList Map::eventScriptLabels(Event::Group group) const { QStringList scriptLabels; - if (event_group_type.isEmpty()) { - for (const auto *event : getAllEvents()) - scriptLabels << event->get("script_label"); + + if (group == Event::Group::None) { + ScriptTracker scriptTracker; + for (Event *event : this->getAllEvents()) { + event->accept(&scriptTracker); + } + scriptLabels = scriptTracker.getScripts(); } else { - for (const auto *event : events.value(event_group_type)) - scriptLabels << event->get("script_label"); + ScriptTracker scriptTracker; + for (Event *event : events.value(group)) { + event->accept(&scriptTracker); + } + scriptLabels = scriptTracker.getScripts(); } scriptLabels.removeAll(""); scriptLabels.removeDuplicates(); - if (scriptLabels.contains("0x0")) - scriptLabels.move(scriptLabels.indexOf("0x0"), scriptLabels.count() - 1); - if (scriptLabels.contains("NULL")) - scriptLabels.move(scriptLabels.indexOf("NULL"), scriptLabels.count() - 1); + scriptLabels.prepend("0x0"); + scriptLabels.prepend("NULL"); return scriptLabels; } void Map::removeEvent(Event *event) { - for (QString key : events.keys()) { + for (Event::Group key : events.keys()) { events[key].removeAll(event); } } void Map::addEvent(Event *event) { - events[event->get("event_group_type")].append(event); + event->setMap(this); + events[event->getEventGroup()].append(event); if (!ownedEvents.contains(event)) ownedEvents.append(event); } +void Map::modify() { + emit modified(); +} + +void Map::clean() { + this->hasUnsavedDataChanges = false; +} + bool Map::hasUnsavedChanges() { return !editHistory.isClean() || hasUnsavedDataChanges || !isPersistedToFile; } diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index f765ff79..1f933e7c 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -15,6 +15,8 @@ const QRegularExpression ParseUtil::re_poryScriptLabel("\\b(script)(\\((global|l const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?