From 582405d68bc15f7adc3c5b2efbd938e35c79a784 Mon Sep 17 00:00:00 2001 From: garak Date: Tue, 19 Jul 2022 17:56:12 -0400 Subject: [PATCH 01/33] change Event class design in favor of polymorphism --- forms/mainwindow.ui | 310 +++++++- include/core/event.h | 123 --- include/core/events.h | 669 ++++++++++++++++ include/core/heallocation.h | 5 +- include/core/map.h | 18 +- include/core/parseutil.h | 1 + include/core/regionmapeditcommands.h | 2 +- include/editor.h | 10 +- include/lib/orderedjson.h | 4 +- include/mainwindow.h | 10 +- include/project.h | 6 +- include/ui/customattributestable.h | 2 +- include/ui/draggablepixmapitem.h | 29 +- include/ui/eventframes.h | 272 +++++++ include/ui/eventpropertiesframe.h | 29 - include/ui/neweventtoolbutton.h | 8 +- porymap.pro | 10 +- src/core/editcommands.cpp | 44 +- src/core/event.cpp | 480 ------------ src/core/events.cpp | 875 +++++++++++++++++++++ src/core/heallocation.cpp | 39 +- src/core/map.cpp | 30 +- src/core/parseutil.cpp | 2 + src/core/regionmapeditcommands.cpp | 3 +- src/editor.cpp | 131 +++- src/mainwindow.cpp | 697 +++++------------ src/project.cpp | 325 +++----- src/ui/customattributestable.cpp | 9 +- src/ui/draggablepixmapitem.cpp | 60 +- src/ui/eventframes.cpp | 1054 ++++++++++++++++++++++++++ src/ui/eventpropertiesframe.cpp | 28 - src/ui/mapimageexporter.cpp | 30 +- src/ui/neweventtoolbutton.cpp | 20 +- src/ui/newmappopup.cpp | 1 - 34 files changed, 3675 insertions(+), 1661 deletions(-) delete mode 100644 include/core/event.h create mode 100644 include/core/events.h create mode 100644 include/ui/eventframes.h delete mode 100644 include/ui/eventpropertiesframe.h delete mode 100644 src/core/event.cpp create mode 100644 src/core/events.cpp create mode 100644 src/ui/eventframes.cpp delete mode 100644 src/ui/eventpropertiesframe.cpp diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index f7e9e245..3eaded3f 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 @@ -1404,16 +1404,6 @@ 3 - - - - There are no events on the current map. - - - Qt::AlignCenter - - - @@ -1522,6 +1512,16 @@ + + + + There are no events on the current map. + + + Qt::AlignCenter + + + @@ -1544,8 +1544,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + object id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1557,8 +1606,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1589,8 +1638,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + warp id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1602,8 +1700,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1634,8 +1732,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + coord id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1647,8 +1794,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1679,8 +1826,63 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + + + + bg id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1692,8 +1894,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1724,8 +1926,57 @@ 0 + + + + 12 + + + 12 + + + 12 + + + 0 + + + + + + 60 + 0 + + + + + + + + heal id + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::NoFrame + true @@ -1737,8 +1988,8 @@ 0 0 - 98 - 28 + 434 + 581 @@ -1777,6 +2028,9 @@ 0 + + QFrame::NoFrame + true @@ -1788,8 +2042,8 @@ 0 0 - 98 - 28 + 434 + 625 @@ -2679,7 +2933,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..9998d630 --- /dev/null +++ b/include/core/events.h @@ -0,0 +1,669 @@ +#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 excepts 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; } + + 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) { this->pixmapItem = 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); + + +// pure virtual public methods +public: + + // // update spinbox values, etc, combo indices + // virtual void updateFrame(); // setFrameFromMovement, (aka redisplay?) + // virtual void disableFrame(); // setParrent(nullptr), disconnectSignals() + +// 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 { + // + // in each derived class constructor, need to createEventFrame, since not + // doing that in base class. make sure to upcall though + +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 *getEventFrame() 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 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; // TODO: int? + 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 717fd0f3..39aa8fef 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); @@ -105,9 +108,6 @@ public: bool isWithinBounds(int x, int y); bool isWithinBorderBounds(int x, int y); - // for memory management - QVector ownedEvents; - MapPixmapItem *mapItem = nullptr; void setMapItem(MapPixmapItem *item) { mapItem = item; } diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 5cb07366..97bf2e71 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..811848a2 100644 --- a/include/editor.h +++ b/include/editor.h @@ -96,7 +96,7 @@ public: DraggablePixmapItem *addMapEvent(Event *event); void selectMapEvent(DraggablePixmapItem *object); void selectMapEvent(DraggablePixmapItem *object, bool toggle); - DraggablePixmapItem *addNewEvent(QString event_type); + DraggablePixmapItem *addNewEvent(Event::Type type); void deleteEvent(Event *); void updateSelectedEvents(); void duplicateSelectedEvents(); @@ -131,8 +131,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 +150,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 +158,7 @@ public slots: void openProjectInTextEditor() const; void maskNonVisibleConnectionTiles(); void onBorderMetatilesChanged(); + void selectedEventIndexChanged(int index, Event::Group eventGroup); private: void setConnectionItemsVisible(bool); @@ -210,7 +210,7 @@ signals: 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 20463cee..17dafe10 100644 --- a/include/lib/orderedjson.h +++ b/include/lib/orderedjson.h @@ -104,8 +104,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 89319d0e..9b2de654 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -26,6 +26,8 @@ #include "shortcutseditor.h" #include "preferenceeditor.h" + + namespace Ui { class MainWindow; } @@ -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,7 @@ private slots: void on_toolButton_deleteObject_clicked(); - void addNewEvent(QString); + void addNewEvent(Event::Type type); void updateSelectedObjects(); void updateObjects(); @@ -265,8 +267,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(); @@ -374,7 +374,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 0828284f..29486ba4 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..872f07c8 --- /dev/null +++ b/include/ui/eventframes.h @@ -0,0 +1,272 @@ +#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); + + 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 *label_icon; + + QFrame *frame_contents; + QVBoxLayout *layout_contents; + +protected: + bool populated = false; + bool initialized = 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; + 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 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 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: + 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: + 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/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/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..07617e50 --- /dev/null +++ b/src/core/events.cpp @@ -0,0 +1,875 @@ +#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 (eventFrame) delete eventFrame; + eventFrame = nullptr; +} + +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]; + } + } +} + +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(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); + } +} + +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 9bbe68c4..65facef3 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -474,34 +474,40 @@ 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); } diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 63fbd84d..9c2be360 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(? + + + 0 + 0 + + 0 From 385c17fd23ae19a7d21eb0b58b62eabb11f575bd Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 11 Oct 2022 10:34:09 -0400 Subject: [PATCH 15/33] Add path drawing to the API --- include/ui/mapview.h | 1 + include/ui/overlay.h | 18 ++++++++++++++++++ src/scriptapi/apioverlay.cpp | 5 +++++ src/ui/overlay.cpp | 28 ++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 957a8bbd..b9f06888 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -55,6 +55,7 @@ public: Q_INVOKABLE void addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int paletteId, bool setTransparency = false, int layer = 0); Q_INVOKABLE void addTileImage(int x, int y, QJSValue tileObj, bool setTransparency = false, int layer = 0); Q_INVOKABLE void addMetatileImage(int x, int y, int metatileId, bool setTransparency = false, int layer = 0); + Q_INVOKABLE void addPath(QList x, QList y, QString color = "#000000", int layer = 0); private: QMap overlayMap; diff --git a/include/ui/overlay.h b/include/ui/overlay.h index da379110..ac1f64c2 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -70,6 +70,23 @@ private: QImage image; }; +class OverlayPath : public OverlayItem { +public: + OverlayPath(QPainterPath path, QString color) { + this->prevX = 0; + this->prevY = 0; + this->path = path; + this->color = color; + } + ~OverlayPath() {} + virtual void render(QPainter *painter, int x, int y); +private: + int prevX; + int prevY; + QPainterPath path; + QString color; +}; + class Overlay { public: @@ -102,6 +119,7 @@ public: void addRect(int x, int y, int width, int height, QString color = "#000000", bool filled = false); 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 x, QList y, QString color); private: QList items; int x; diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 58bf58dd..892befbc 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -212,3 +212,8 @@ void MapView::addMetatileImage(int x, int y, int metatileId, bool setTransparenc if (this->getOverlay(layer)->addImage(x, y, image)) this->scene()->update(); } + +void MapView::addPath(QList x, QList y, QString color, int layer) { + if (this->getOverlay(layer)->addPath(x, y, color)) + this->scene()->update(); +} diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 22aba3de..67832403 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -23,6 +23,17 @@ void OverlayImage::render(QPainter *painter, int x, int y) { painter->drawImage(this->x + x, this->y + y, this->image); } +void OverlayPath::render(QPainter *painter, int x, int y) { + if (x != this->prevX || y != this->prevY) { + // Overlay has moved since the path was last drawn + path.translate(x - prevX, y - prevY); + } + this->prevX = x; + this->prevY = y; + painter->setPen(this->color); + painter->drawPath(this->path); +} + void Overlay::renderItems(QPainter *painter) { if (this->hidden) return; @@ -175,3 +186,20 @@ bool Overlay::addImage(int x, int y, QImage image) { this->items.append(new OverlayImage(x, y, image)); return true; } + +bool Overlay::addPath(QList x, QList y, QString color) { + int numPoints = qMin(x.length(), y.length()); + if (numPoints < 2) { + logError("Overlay path must have at least two points."); + return false; + } + + QPainterPath path; + path.moveTo(x.at(0), y.at(0)); + + for (int i = 1; i < numPoints; i++) + path.lineTo(x.at(i), y.at(i)); + + this->items.append(new OverlayPath(path, color)); + return true; +} From 77d04bb6debf36be1382982376c5bd434aaae59b Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 11 Oct 2022 13:30:27 -0400 Subject: [PATCH 16/33] Combine addRect and addRectFilled, add color checking --- include/ui/mapview.h | 3 +- include/ui/overlay.h | 21 +++++----- src/scriptapi/apioverlay.cpp | 21 ++++------ src/ui/overlay.cpp | 78 +++++++++++++++++++++++------------- 4 files changed, 71 insertions(+), 52 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index b9f06888..e09c5b65 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -45,8 +45,7 @@ public: Q_INVOKABLE void setOpacity(int opacity, int layer); Q_INVOKABLE void setOpacity(int opacity); 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 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/overlay.h b/include/ui/overlay.h index ac1f64c2..deb262e5 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -36,13 +36,13 @@ private: class OverlayRect : public OverlayItem { public: - OverlayRect(int x, int y, int width, int height, QColor color, bool filled) { + OverlayRect(int x, int y, int width, int height, QColor borderColor, QColor fillColor) { this->x = x; this->y = y; this->width = width; this->height = height; - this->color = color; - this->filled = filled; + this->borderColor = borderColor; + this->fillColor = fillColor; } ~OverlayRect() {} virtual void render(QPainter *painter, int x, int y); @@ -51,8 +51,8 @@ private: int y; int width; int height; - QColor color; - bool filled; + QColor borderColor; + QColor fillColor; }; class OverlayImage : public OverlayItem { @@ -72,7 +72,7 @@ private: class OverlayPath : public OverlayItem { public: - OverlayPath(QPainterPath path, QString color) { + OverlayPath(QPainterPath path, QColor color) { this->prevX = 0; this->prevY = 0; this->path = path; @@ -84,7 +84,7 @@ private: int prevX; int prevY; QPainterPath path; - QString color; + QColor color; }; class Overlay @@ -115,12 +115,13 @@ 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); + bool 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); 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 x, QList y, QString color); + bool addPath(QList x, QList y, QString colorStr); private: + QColor getColor(QString colorStr, bool * ok); QList items; int x; int y; diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 892befbc..17187ec3 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -148,18 +148,18 @@ void MapView::setOpacity(int opacity) { } void MapView::addText(QString text, int x, int y, QString color, int fontSize, int layer) { - this->getOverlay(layer)->addText(text, x, y, color, fontSize); - this->scene()->update(); + if (this->getOverlay(layer)->addText(text, x, y, color, fontSize)) + this->scene()->update(); } -void MapView::addRect(int x, int y, int width, int height, QString color, int layer) { - this->getOverlay(layer)->addRect(x, y, width, height, color, false); - this->scene()->update(); +void MapView::addRect(int x, int y, int width, int height, QString borderColor, QString fillColor, int layer) { + if (this->getOverlay(layer)->addRect(x, y, width, height, borderColor, fillColor)) + this->scene()->update(); } -void MapView::addFilledRect(int x, int y, int width, int height, QString color, int layer) { - this->getOverlay(layer)->addRect(x, y, width, height, color, true); - this->scene()->update(); +void MapView::addPath(QList x, QList y, QString color, int layer) { + if (this->getOverlay(layer)->addPath(x, y, color)) + this->scene()->update(); } void MapView::addImage(int x, int y, QString filepath, int layer, bool useCache) { @@ -212,8 +212,3 @@ void MapView::addMetatileImage(int x, int y, int metatileId, bool setTransparenc if (this->getOverlay(layer)->addImage(x, y, image)) this->scene()->update(); } - -void MapView::addPath(QList x, QList y, QString color, int layer) { - if (this->getOverlay(layer)->addPath(x, y, color)) - this->scene()->update(); -} diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 67832403..1e4e2731 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -11,12 +11,10 @@ void OverlayText::render(QPainter *painter, int x, int y) { } void OverlayRect::render(QPainter *painter, int x, int y) { - if (this->filled) { - painter->fillRect(this->x + x, this->y + y, this->width, this->height, this->color); - } else { - painter->setPen(this->color); - painter->drawRect(this->x + x, this->y + y, this->width, this->height); - } + QRectF rect(this->x + x, this->y + y, this->width, this->height); + painter->setPen(this->borderColor); + painter->drawRect(rect); + painter->fillRect(rect, this->fillColor); } void OverlayImage::render(QPainter *painter, int x, int y) { @@ -124,12 +122,55 @@ void Overlay::move(int deltaX, int deltaY) { this->y += deltaY; } -void Overlay::addText(const QString text, int x, int y, QString color, int fontSize) { - this->items.append(new OverlayText(text, x, y, QColor(color), fontSize)); +QColor Overlay::getColor(QString colorStr, bool * ok) { + if (colorStr.isEmpty()) + colorStr = "transparent"; + QColor color = QColor(colorStr); + if (!color.isValid()) { + logError(QString("\"%1\" is not a valid color. Colors can be in the format \"#RRGGBB\" or \"#AARRGGBB\"").arg(colorStr)); + if (ok) *ok = false; // Not set to true in here, allow for repeat usage + } + return color; } -void Overlay::addRect(int x, int y, int width, int height, QString color, bool filled) { - this->items.append(new OverlayRect(x, y, width, height, QColor(color), filled)); +bool Overlay::addText(const QString text, int x, int y, QString colorStr, int fontSize) { + bool ok = true; + QColor color = getColor(colorStr, &ok); + if (!ok) return false; + + this->items.append(new OverlayText(text, x, y, color, fontSize)); + return true; +} + +bool Overlay::addRect(int x, int y, int width, int height, QString borderColorStr, QString fillColorStr) { + bool ok = true; + QColor borderColor = getColor(borderColorStr, &ok); + QColor fillColor = getColor(fillColorStr, &ok); + if (!ok) return false; + + this->items.append(new OverlayRect(x, y, width, height, borderColor, fillColor)); + return true; +} + +bool Overlay::addPath(QList x, QList y, QString colorStr) { + bool ok = true; + QColor color = getColor(colorStr, &ok); + if (!ok) return false; + + int numPoints = qMin(x.length(), y.length()); + if (numPoints < 2) { + logError("Overlay path must have at least two points."); + return false; + } + + QPainterPath path; + path.moveTo(x.at(0), y.at(0)); + + for (int i = 1; i < numPoints; i++) + path.lineTo(x.at(i), y.at(i)); + + this->items.append(new OverlayPath(path, color)); + return true; } bool Overlay::addImage(int x, int y, QString filepath, bool useCache, int width, int height, int xOffset, int yOffset, qreal hScale, qreal vScale, QList palette, bool setTransparency) { @@ -186,20 +227,3 @@ bool Overlay::addImage(int x, int y, QImage image) { this->items.append(new OverlayImage(x, y, image)); return true; } - -bool Overlay::addPath(QList x, QList y, QString color) { - int numPoints = qMin(x.length(), y.length()); - if (numPoints < 2) { - logError("Overlay path must have at least two points."); - return false; - } - - QPainterPath path; - path.moveTo(x.at(0), y.at(0)); - - for (int i = 1; i < numPoints; i++) - path.lineTo(x.at(i), y.at(i)); - - this->items.append(new OverlayPath(path, color)); - return true; -} From 67bec313a5480e8ab410334f1fbeac28c1dbcae8 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 11 Oct 2022 14:52:52 -0400 Subject: [PATCH 17/33] Add rounding to addRect, add fill color to addPath --- include/ui/mapview.h | 4 +-- include/ui/overlay.h | 45 ++++++++------------------ src/scriptapi/apioverlay.cpp | 16 +++++----- src/ui/overlay.cpp | 61 ++++++++++++++---------------------- 4 files changed, 47 insertions(+), 79 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index e09c5b65..8b463aad 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -45,7 +45,8 @@ public: Q_INVOKABLE void setOpacity(int opacity, int layer); Q_INVOKABLE void setOpacity(int opacity); 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 borderColor = "#000000", QString fillColor = "transparent", 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 x, QList y, 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, @@ -54,7 +55,6 @@ public: Q_INVOKABLE void addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int paletteId, bool setTransparency = false, int layer = 0); Q_INVOKABLE void addTileImage(int x, int y, QJSValue tileObj, bool setTransparency = false, int layer = 0); Q_INVOKABLE void addMetatileImage(int x, int y, int metatileId, bool setTransparency = false, int layer = 0); - Q_INVOKABLE void addPath(QList x, QList y, QString color = "#000000", int layer = 0); private: QMap overlayMap; diff --git a/include/ui/overlay.h b/include/ui/overlay.h index deb262e5..be2d38ec 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -34,23 +34,21 @@ private: int fontSize; }; -class OverlayRect : public OverlayItem { +class OverlayPath : public OverlayItem { public: - OverlayRect(int x, int y, int width, int height, QColor borderColor, QColor fillColor) { - this->x = x; - this->y = y; - this->width = width; - this->height = height; + OverlayPath(QPainterPath path, QColor borderColor, QColor fillColor) { + this->prevX = 0; + this->prevY = 0; + this->path = path; this->borderColor = borderColor; this->fillColor = fillColor; } - ~OverlayRect() {} + ~OverlayPath() {} virtual void render(QPainter *painter, int x, int y); private: - int x; - int y; - int width; - int height; + int prevX; + int prevY; + QPainterPath path; QColor borderColor; QColor fillColor; }; @@ -70,23 +68,6 @@ private: QImage image; }; -class OverlayPath : public OverlayItem { -public: - OverlayPath(QPainterPath path, QColor color) { - this->prevX = 0; - this->prevY = 0; - this->path = path; - this->color = color; - } - ~OverlayPath() {} - virtual void render(QPainter *painter, int x, int y); -private: - int prevX; - int prevY; - QPainterPath path; - QColor color; -}; - class Overlay { public: @@ -115,13 +96,13 @@ public: void renderItems(QPainter *painter); QList getItems(); void clearItems(); - bool 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); + 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 x, QList y, QString colorStr); + bool addPath(QList x, QList y, QString borderColorStr, QString fillColorStr); private: - QColor getColor(QString colorStr, bool * ok); + QColor getColor(QString colorStr); QList items; int x; int y; diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 17187ec3..4d5b86e7 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -148,17 +148,17 @@ void MapView::setOpacity(int opacity) { } void MapView::addText(QString text, int x, int y, QString color, int fontSize, int layer) { - if (this->getOverlay(layer)->addText(text, x, y, color, fontSize)) + this->getOverlay(layer)->addText(text, x, y, color, fontSize); + this->scene()->update(); +} + +void MapView::addRect(int x, int y, int width, int height, QString borderColor, QString fillColor, int rounding, int layer) { + if (this->getOverlay(layer)->addRect(x, y, width, height, borderColor, fillColor, rounding)) this->scene()->update(); } -void MapView::addRect(int x, int y, int width, int height, QString borderColor, QString fillColor, int layer) { - if (this->getOverlay(layer)->addRect(x, y, width, height, borderColor, fillColor)) - this->scene()->update(); -} - -void MapView::addPath(QList x, QList y, QString color, int layer) { - if (this->getOverlay(layer)->addPath(x, y, color)) +void MapView::addPath(QList x, QList y, QString borderColor, QString fillColor, int layer) { + if (this->getOverlay(layer)->addPath(x, y, borderColor, fillColor)) this->scene()->update(); } diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 1e4e2731..c6ae54f2 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -10,17 +10,6 @@ void OverlayText::render(QPainter *painter, int x, int y) { painter->drawStaticText(this->x + x, this->y + y, this->text); } -void OverlayRect::render(QPainter *painter, int x, int y) { - QRectF rect(this->x + x, this->y + y, this->width, this->height); - painter->setPen(this->borderColor); - painter->drawRect(rect); - painter->fillRect(rect, this->fillColor); -} - -void OverlayImage::render(QPainter *painter, int x, int y) { - painter->drawImage(this->x + x, this->y + y, this->image); -} - void OverlayPath::render(QPainter *painter, int x, int y) { if (x != this->prevX || y != this->prevY) { // Overlay has moved since the path was last drawn @@ -28,8 +17,13 @@ void OverlayPath::render(QPainter *painter, int x, int y) { } this->prevX = x; this->prevY = y; - painter->setPen(this->color); + painter->setPen(this->borderColor); painter->drawPath(this->path); + painter->fillPath(this->path, this->fillColor); +} + +void OverlayImage::render(QPainter *painter, int x, int y) { + painter->drawImage(this->x + x, this->y + y, this->image); } void Overlay::renderItems(QPainter *painter) { @@ -76,7 +70,7 @@ int Overlay::getOpacity() { void Overlay::setOpacity(int opacity) { if (opacity < 0 || opacity > 100) { - logError(QString("Invalid overlay opacity '%1'").arg(opacity)); + logError(QString("Invalid overlay opacity '%1', must be in range 0-100").arg(opacity)); return; } this->opacity = static_cast(opacity) / 100; @@ -122,41 +116,34 @@ void Overlay::move(int deltaX, int deltaY) { this->y += deltaY; } -QColor Overlay::getColor(QString colorStr, bool * ok) { +QColor Overlay::getColor(QString colorStr) { if (colorStr.isEmpty()) colorStr = "transparent"; QColor color = QColor(colorStr); if (!color.isValid()) { - logError(QString("\"%1\" is not a valid color. Colors can be in the format \"#RRGGBB\" or \"#AARRGGBB\"").arg(colorStr)); - if (ok) *ok = false; // Not set to true in here, allow for repeat usage + logWarn(QString("Invalid overlay color \"%1\". Colors can be in the format \"#RRGGBB\" or \"#AARRGGBB\"").arg(colorStr)); + color = QColor("transparent"); } return color; } -bool Overlay::addText(const QString text, int x, int y, QString colorStr, int fontSize) { - bool ok = true; - QColor color = getColor(colorStr, &ok); - if (!ok) return false; +void Overlay::addText(const QString text, int x, int y, QString colorStr, int fontSize) { + this->items.append(new OverlayText(text, x, y, getColor(colorStr), fontSize)); +} - this->items.append(new OverlayText(text, x, y, color, fontSize)); +bool Overlay::addRect(int x, int y, int width, int height, QString borderColorStr, QString fillColorStr, int rounding) { + if (rounding < 0 || rounding > 100) { + logError(QString("Invalid rectangle rounding '%1', must be in range 0-100").arg(rounding)); + return false; + } + + QPainterPath path; + path.addRoundedRect(QRectF(x, y, width, height), rounding, rounding, Qt::RelativeSize); + this->items.append(new OverlayPath(path, getColor(borderColorStr), getColor(fillColorStr))); return true; } -bool Overlay::addRect(int x, int y, int width, int height, QString borderColorStr, QString fillColorStr) { - bool ok = true; - QColor borderColor = getColor(borderColorStr, &ok); - QColor fillColor = getColor(fillColorStr, &ok); - if (!ok) return false; - - this->items.append(new OverlayRect(x, y, width, height, borderColor, fillColor)); - return true; -} - -bool Overlay::addPath(QList x, QList y, QString colorStr) { - bool ok = true; - QColor color = getColor(colorStr, &ok); - if (!ok) return false; - +bool Overlay::addPath(QList x, QList y, QString borderColorStr, QString fillColorStr) { int numPoints = qMin(x.length(), y.length()); if (numPoints < 2) { logError("Overlay path must have at least two points."); @@ -169,7 +156,7 @@ bool Overlay::addPath(QList x, QList y, QString colorStr) { for (int i = 1; i < numPoints; i++) path.lineTo(x.at(i), y.at(i)); - this->items.append(new OverlayPath(path, color)); + this->items.append(new OverlayPath(path, getColor(borderColorStr), getColor(fillColorStr))); return true; } From ad5eea229385e01109520673290906b1dff8d110 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 11 Oct 2022 17:21:44 -0400 Subject: [PATCH 18/33] Add rotation and scale to overlay API --- include/ui/mapview.h | 11 ++++++ include/ui/overlay.h | 27 +++++++++----- src/scriptapi/apioverlay.cpp | 60 ++++++++++++++++++++++++++++++ src/ui/overlay.cpp | 72 +++++++++++++++++++++++++----------- 4 files changed, 140 insertions(+), 30 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 8b463aad..2059e93c 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -44,6 +44,17 @@ public: Q_INVOKABLE int getOpacity(int layer = 0); Q_INVOKABLE void setOpacity(int opacity, int layer); Q_INVOKABLE void setOpacity(int opacity); + Q_INVOKABLE int getHorizontalScale(int layer); + Q_INVOKABLE int getVerticalScale(int layer); + Q_INVOKABLE void setHorizontalScale(int scale, int layer); + Q_INVOKABLE void setHorizontalScale(int scale); + Q_INVOKABLE void setVerticalScale(int scale, int layer); + Q_INVOKABLE void setVerticalScale(int scale); + Q_INVOKABLE int getRotation(int layer); + Q_INVOKABLE void setRotation(int rotation, int layer); + Q_INVOKABLE void setRotation(int rotation); + 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 borderColor = "#000000", QString fillColor = "transparent", int rounding = 0, int layer = 0); Q_INVOKABLE void addPath(QList x, QList y, QString borderColor = "#000000", QString fillColor = "transparent", int layer = 0); diff --git a/include/ui/overlay.h b/include/ui/overlay.h index be2d38ec..4cfc513c 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -11,7 +11,7 @@ class OverlayItem { public: OverlayItem() {} virtual ~OverlayItem() {}; - virtual void render(QPainter *, int, int) {}; + virtual void render(QPainter *) {}; }; class OverlayText : public OverlayItem { @@ -25,7 +25,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; @@ -37,17 +37,13 @@ private: class OverlayPath : public OverlayItem { public: OverlayPath(QPainterPath path, QColor borderColor, QColor fillColor) { - this->prevX = 0; - this->prevY = 0; this->path = path; this->borderColor = borderColor; this->fillColor = fillColor; } ~OverlayPath() {} - virtual void render(QPainter *painter, int x, int y); + virtual void render(QPainter *painter); private: - int prevX; - int prevY; QPainterPath path; QColor borderColor; QColor fillColor; @@ -61,7 +57,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; @@ -74,6 +70,9 @@ public: Overlay() { this->x = 0; this->y = 0; + this->rotation = 0; + this->hScale = 1.0; + this->vScale = 1.0; this->hidden = false; this->opacity = 1.0; this->clippingRect = nullptr; @@ -89,6 +88,13 @@ public: int getY(); void setX(int x); void setY(int y); + int getHScale(); + int getVScale(); + void setHScale(int scale); + void setVScale(int scale); + int getRotation(); + void setRotation(int rotation); + void rotate(int degrees); void setClippingRect(QRectF rect); void clearClippingRect(); void setPosition(int x, int y); @@ -100,12 +106,15 @@ public: 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 x, QList y, QString borderColorStr, QString fillColorStr); + bool addPath(QList xCoords, QList yCoords, QString borderColorStr, QString fillColorStr); private: QColor getColor(QString colorStr); QList items; int x; int y; + int rotation; + qreal hScale; + qreal vScale; bool hidden; qreal opacity; QRectF *clippingRect; diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 4d5b86e7..34b9f9a3 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -147,6 +147,66 @@ void MapView::setOpacity(int opacity) { this->scene()->update(); } +int MapView::getHorizontalScale(int layer) { + return this->getOverlay(layer)->getHScale(); +} + +int MapView::getVerticalScale(int layer) { + return this->getOverlay(layer)->getVScale(); +} + +void MapView::setHorizontalScale(int scale, int layer) { + this->getOverlay(layer)->setHScale(scale); + this->scene()->update(); +} + +// Overload. No layer provided, set horizontal scale of all layers +void MapView::setHorizontalScale(int scale) { + foreach (Overlay * layer, this->overlayMap) + layer->setHScale(scale); + this->scene()->update(); +} + +void MapView::setVerticalScale(int scale, int layer) { + this->getOverlay(layer)->setVScale(scale); + this->scene()->update(); +} + +// Overload. No layer provided, set vertical scale of all layers +void MapView::setVerticalScale(int scale) { + foreach (Overlay * layer, this->overlayMap) + layer->setVScale(scale); + this->scene()->update(); +} + +int MapView::getRotation(int layer) { + return this->getOverlay(layer)->getRotation(); +} + +void MapView::setRotation(int rotation, int layer) { + this->getOverlay(layer)->setRotation(rotation); + this->scene()->update(); +} + +// Overload. No layer provided, set rotation of all layers +void MapView::setRotation(int rotation) { + foreach (Overlay * layer, this->overlayMap) + layer->setRotation(rotation); + this->scene()->update(); +} + +void MapView::rotate(int degrees, int layer) { + this->getOverlay(layer)->rotate(degrees); + this->scene()->update(); +} + +// Overload. No layer provided, rotate all layers +void MapView::rotate(int degrees) { + foreach (Overlay * layer, this->overlayMap) + layer->rotate(degrees); + this->scene()->update(); +} + void MapView::addText(QString text, int x, int y, QString color, int fontSize, int layer) { this->getOverlay(layer)->addText(text, x, y, color, fontSize); this->scene()->update(); diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index c6ae54f2..7e3fdf16 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -2,47 +2,49 @@ #include "scripting.h" #include "log.h" -void OverlayText::render(QPainter *painter, int x, int y) { +void OverlayText::render(QPainter *painter) { QFont font = painter->font(); font.setPixelSize(this->fontSize); painter->setFont(font); painter->setPen(this->color); - painter->drawStaticText(this->x + x, this->y + y, this->text); + painter->drawStaticText(this->x, this->y, this->text); } -void OverlayPath::render(QPainter *painter, int x, int y) { - if (x != this->prevX || y != this->prevY) { - // Overlay has moved since the path was last drawn - path.translate(x - prevX, y - prevY); - } - this->prevX = x; - this->prevY = y; +void OverlayPath::render(QPainter *painter) { painter->setPen(this->borderColor); painter->drawPath(this->path); painter->fillPath(this->path, this->fillColor); } -void OverlayImage::render(QPainter *painter, int x, int y) { - painter->drawImage(this->x + x, this->y + y, this->image); +void OverlayImage::render(QPainter *painter) { + painter->drawImage(this->x, this->y, this->image); } void Overlay::renderItems(QPainter *painter) { if (this->hidden) return; + painter->save(); + + QTransform transform = painter->transform(); + transform.translate(this->x, this->y); + transform.rotate(this->rotation); + transform.scale(this->hScale, this->vScale); + painter->setTransform(transform); + + /*painter->translate(this->x, this->y); + painter->rotate(this->rotation); + painter->scale(this->hScale, this->vScale);*/ + if (this->clippingRect) { painter->setClipping(true); painter->setClipRect(*this->clippingRect); } - qreal oldOpacity = painter->opacity(); painter->setOpacity(this->opacity); for (auto item : this->items) - item->render(painter, this->x, this->y); - painter->setOpacity(oldOpacity); + item->render(painter); - if (this->clippingRect) { - painter->setClipping(false); - } + painter->restore(); } void Overlay::clearItems() { @@ -92,6 +94,34 @@ void Overlay::setY(int y) { this->y = y; } +int Overlay::getHScale() { + return this->hScale; +} + +int Overlay::getVScale() { + return this->vScale; +} + +void Overlay::setHScale(int scale) { + this->hScale = scale; +} + +void Overlay::setVScale(int scale) { + this->vScale = scale; +} + +int Overlay::getRotation() { + return this->rotation; +} + +void Overlay::setRotation(int rotation) { + this->rotation = rotation; +} + +void Overlay::rotate(int degrees) { + this->rotation += degrees; +} + void Overlay::setClippingRect(QRectF rect) { if (this->clippingRect) { delete this->clippingRect; @@ -143,18 +173,18 @@ bool Overlay::addRect(int x, int y, int width, int height, QString borderColorSt return true; } -bool Overlay::addPath(QList x, QList y, QString borderColorStr, QString fillColorStr) { - int numPoints = qMin(x.length(), y.length()); +bool Overlay::addPath(QList xCoords, QList yCoords, QString borderColorStr, QString fillColorStr) { + int numPoints = qMin(xCoords.length(), yCoords.length()); if (numPoints < 2) { logError("Overlay path must have at least two points."); return false; } QPainterPath path; - path.moveTo(x.at(0), y.at(0)); + path.moveTo(xCoords.at(0), yCoords.at(0)); for (int i = 1; i < numPoints; i++) - path.lineTo(x.at(i), y.at(i)); + path.lineTo(xCoords.at(i), yCoords.at(i)); this->items.append(new OverlayPath(path, getColor(borderColorStr), getColor(fillColorStr))); return true; From 47f6723bd26958d53f0808f86fd37dcf11589ebc Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 13 Oct 2022 15:14:53 -0400 Subject: [PATCH 19/33] Clean up rotation functions --- include/ui/mapview.h | 10 +++++----- include/ui/overlay.h | 9 +++++---- src/scriptapi/apioverlay.cpp | 14 +++++++------- src/ui/overlay.cpp | 26 ++++++++++++++++---------- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 2059e93c..62d5e461 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -44,15 +44,15 @@ public: Q_INVOKABLE int getOpacity(int layer = 0); Q_INVOKABLE void setOpacity(int opacity, int layer); Q_INVOKABLE void setOpacity(int opacity); - Q_INVOKABLE int getHorizontalScale(int layer); - Q_INVOKABLE int getVerticalScale(int layer); + Q_INVOKABLE int getHorizontalScale(int layer = 0); + Q_INVOKABLE int getVerticalScale(int layer = 0); Q_INVOKABLE void setHorizontalScale(int scale, int layer); Q_INVOKABLE void setHorizontalScale(int scale); Q_INVOKABLE void setVerticalScale(int scale, int layer); Q_INVOKABLE void setVerticalScale(int scale); - Q_INVOKABLE int getRotation(int layer); - Q_INVOKABLE void setRotation(int rotation, int layer); - Q_INVOKABLE void setRotation(int rotation); + Q_INVOKABLE int getAngle(int layer = 0); + Q_INVOKABLE void setAngle(int angle, int layer); + Q_INVOKABLE void setAngle(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); diff --git a/include/ui/overlay.h b/include/ui/overlay.h index 4cfc513c..dfdfea16 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -70,7 +70,7 @@ public: Overlay() { this->x = 0; this->y = 0; - this->rotation = 0; + this->angle = 0; this->hScale = 1.0; this->vScale = 1.0; this->hidden = false; @@ -92,8 +92,8 @@ public: int getVScale(); void setHScale(int scale); void setVScale(int scale); - int getRotation(); - void setRotation(int rotation); + int getAngle(); + void setAngle(int angle); void rotate(int degrees); void setClippingRect(QRectF rect); void clearClippingRect(); @@ -108,11 +108,12 @@ public: 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 rotation; + int angle; qreal hScale; qreal vScale; bool hidden; diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 34b9f9a3..61178c33 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -179,19 +179,19 @@ void MapView::setVerticalScale(int scale) { this->scene()->update(); } -int MapView::getRotation(int layer) { - return this->getOverlay(layer)->getRotation(); +int MapView::getAngle(int layer) { + return this->getOverlay(layer)->getAngle(); } -void MapView::setRotation(int rotation, int layer) { - this->getOverlay(layer)->setRotation(rotation); +void MapView::setAngle(int angle, int layer) { + this->getOverlay(layer)->setAngle(angle); this->scene()->update(); } -// Overload. No layer provided, set rotation of all layers -void MapView::setRotation(int rotation) { +// Overload. No layer provided, set angle of all layers +void MapView::setAngle(int angle) { foreach (Overlay * layer, this->overlayMap) - layer->setRotation(rotation); + layer->setAngle(angle); this->scene()->update(); } diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 7e3fdf16..374c09be 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -27,14 +27,10 @@ void Overlay::renderItems(QPainter *painter) { QTransform transform = painter->transform(); transform.translate(this->x, this->y); - transform.rotate(this->rotation); + transform.rotate(this->angle); transform.scale(this->hScale, this->vScale); painter->setTransform(transform); - /*painter->translate(this->x, this->y); - painter->rotate(this->rotation); - painter->scale(this->hScale, this->vScale);*/ - if (this->clippingRect) { painter->setClipping(true); painter->setClipRect(*this->clippingRect); @@ -110,16 +106,26 @@ void Overlay::setVScale(int scale) { this->vScale = scale; } -int Overlay::getRotation() { - return this->rotation; +int Overlay::getAngle() { + return this->angle; } -void Overlay::setRotation(int rotation) { - this->rotation = rotation; +void Overlay::setAngle(int angle) { + this->angle = angle; + this->clampAngle(); } void Overlay::rotate(int degrees) { - this->rotation += degrees; + this->angle += degrees; + this->clampAngle(); +} + +void Overlay::clampAngle() { + // transform.rotate would handle this already, but we only + // want to report angles 0-359 for Overlay::getAngle + this->angle %= 360; + if (this->angle < 0) + this->angle += 360; } void Overlay::setClippingRect(QRectF rect) { From 82dcecf8f2ce90b8a5ac6d8007baf51113db16df Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 13 Oct 2022 18:24:32 -0400 Subject: [PATCH 20/33] Add addPath overload --- include/ui/mapview.h | 4 +++- src/scriptapi/apioverlay.cpp | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 62d5e461..d7034e6f 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -57,7 +57,9 @@ public: 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 borderColor = "#000000", QString fillColor = "transparent", int rounding = 0, int layer = 0); - Q_INVOKABLE void addPath(QList x, QList y, 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); + // TODO: Not invokable, haven't successfully tested 2D js array --> QList>. + void addPath(QList> coords, 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/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 61178c33..3cef69f9 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -217,11 +217,25 @@ void MapView::addRect(int x, int y, int width, int height, QString borderColor, this->scene()->update(); } -void MapView::addPath(QList x, QList y, QString borderColor, QString fillColor, int layer) { - if (this->getOverlay(layer)->addPath(x, y, borderColor, fillColor)) +void MapView::addPath(QList xCoords, QList yCoords, QString borderColor, QString fillColor, int layer) { + if (this->getOverlay(layer)->addPath(xCoords, yCoords, borderColor, fillColor)) this->scene()->update(); } +void MapView::addPath(QList> coords, QString borderColor, QString fillColor, int layer) { + QList xCoords; + QList yCoords; + for (int i = 0; i < coords.length(); i++) { + if (coords[i].length() < 2){ + logWarn(QString("Element %1 of overlay path does not have an x and y value.").arg(i)); + continue; + } + xCoords.append(coords[i][0]); + yCoords.append(coords[i][1]); + } + this->addPath(xCoords, yCoords, borderColor, fillColor, layer); +} + void MapView::addImage(int x, int y, QString filepath, int layer, bool useCache) { if (this->getOverlay(layer)->addImage(x, y, filepath, useCache)) this->scene()->update(); From d88d6a828e4dc44271b63d178991b8b00577d2ad Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 13 Oct 2022 21:58:59 -0400 Subject: [PATCH 21/33] Scale to qreal, draw path border over fill --- include/ui/mapview.h | 12 ++++++------ include/ui/overlay.h | 8 ++++---- src/scriptapi/apioverlay.cpp | 12 ++++++------ src/ui/overlay.cpp | 10 +++++----- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index d7034e6f..5e012dbe 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -44,12 +44,12 @@ public: Q_INVOKABLE int getOpacity(int layer = 0); Q_INVOKABLE void setOpacity(int opacity, int layer); Q_INVOKABLE void setOpacity(int opacity); - Q_INVOKABLE int getHorizontalScale(int layer = 0); - Q_INVOKABLE int getVerticalScale(int layer = 0); - Q_INVOKABLE void setHorizontalScale(int scale, int layer); - Q_INVOKABLE void setHorizontalScale(int scale); - Q_INVOKABLE void setVerticalScale(int scale, int layer); - Q_INVOKABLE void setVerticalScale(int scale); + 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 getAngle(int layer = 0); Q_INVOKABLE void setAngle(int angle, int layer); Q_INVOKABLE void setAngle(int angle); diff --git a/include/ui/overlay.h b/include/ui/overlay.h index dfdfea16..8df84e37 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -88,10 +88,10 @@ public: int getY(); void setX(int x); void setY(int y); - int getHScale(); - int getVScale(); - void setHScale(int scale); - void setVScale(int scale); + qreal getHScale(); + qreal getVScale(); + void setHScale(qreal scale); + void setVScale(qreal scale); int getAngle(); void setAngle(int angle); void rotate(int degrees); diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 3cef69f9..2c7b55ee 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -147,33 +147,33 @@ void MapView::setOpacity(int opacity) { this->scene()->update(); } -int MapView::getHorizontalScale(int layer) { +qreal MapView::getHorizontalScale(int layer) { return this->getOverlay(layer)->getHScale(); } -int MapView::getVerticalScale(int layer) { +qreal MapView::getVerticalScale(int layer) { return this->getOverlay(layer)->getVScale(); } -void MapView::setHorizontalScale(int scale, int layer) { +void MapView::setHorizontalScale(qreal scale, int layer) { this->getOverlay(layer)->setHScale(scale); this->scene()->update(); } // Overload. No layer provided, set horizontal scale of all layers -void MapView::setHorizontalScale(int scale) { +void MapView::setHorizontalScale(qreal scale) { foreach (Overlay * layer, this->overlayMap) layer->setHScale(scale); this->scene()->update(); } -void MapView::setVerticalScale(int scale, int layer) { +void MapView::setVerticalScale(qreal scale, int layer) { this->getOverlay(layer)->setVScale(scale); this->scene()->update(); } // Overload. No layer provided, set vertical scale of all layers -void MapView::setVerticalScale(int scale) { +void MapView::setVerticalScale(qreal scale) { foreach (Overlay * layer, this->overlayMap) layer->setVScale(scale); this->scene()->update(); diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 374c09be..2713ce2e 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -11,9 +11,9 @@ void OverlayText::render(QPainter *painter) { } void OverlayPath::render(QPainter *painter) { + painter->fillPath(this->path, this->fillColor); painter->setPen(this->borderColor); painter->drawPath(this->path); - painter->fillPath(this->path, this->fillColor); } void OverlayImage::render(QPainter *painter) { @@ -90,19 +90,19 @@ void Overlay::setY(int y) { this->y = y; } -int Overlay::getHScale() { +qreal Overlay::getHScale() { return this->hScale; } -int Overlay::getVScale() { +qreal Overlay::getVScale() { return this->vScale; } -void Overlay::setHScale(int scale) { +void Overlay::setHScale(qreal scale) { this->hScale = scale; } -void Overlay::setVScale(int scale) { +void Overlay::setVScale(qreal scale) { this->vScale = scale; } From ca04b99576c30af9b0e933ba399acd4de698671b Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 14 Oct 2022 00:12:40 -0400 Subject: [PATCH 22/33] Update manual and changelog --- CHANGELOG.md | 10 ++- docsrc/manual/scripting-capabilities.rst | 96 +++++++++++++++++++++--- 2 files changed, 91 insertions(+), 15 deletions(-) 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..f5acd7f3 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -936,7 +936,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 +1010,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.getAngle(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 + +.. js:function:: overlay.setAngle(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.setAngle(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 +1196,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 +1208,19 @@ 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(xCoords, yCoords, 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 coordinates at (``xCoords``, ``yCoords``). 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 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) From ad01ba0feb2a87808a7f2fd223ee1d04a84ad65d Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sat, 15 Oct 2022 11:33:12 -0500 Subject: [PATCH 23/33] Fix Qt6 build and overloaded addPath ordering --- include/ui/mapview.h | 3 +-- include/ui/overlay.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 5e012dbe..5838c4ae 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -57,9 +57,8 @@ public: 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 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); - // TODO: Not invokable, haven't successfully tested 2D js array --> QList>. - void addPath(QList> coords, 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/overlay.h b/include/ui/overlay.h index 8df84e37..f951360d 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -6,6 +6,7 @@ #include #include #include +#include class OverlayItem { public: From a2795d089b95c46f5f51d63b0d130fa1e9ec073c Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sat, 15 Oct 2022 13:12:40 -0400 Subject: [PATCH 24/33] get/setAngle -> get/setRotation --- docsrc/manual/scripting-capabilities.rst | 8 ++++---- include/ui/mapview.h | 6 +++--- include/ui/overlay.h | 4 ++-- src/scriptapi/apioverlay.cpp | 14 +++++++------- src/ui/overlay.cpp | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index f5acd7f3..e6608248 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -1050,21 +1050,21 @@ All overlay functions are callable via the global ``overlay`` object. :param number scale: the scale to set -.. js:function:: overlay.getAngle(layer = 0) +.. 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 + :returns number: the angle the layer is rotated to -.. js:function:: overlay.setAngle(angle, layer) +.. 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.setAngle(angle) +.. 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. diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 5838c4ae..0d727b2a 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -50,9 +50,9 @@ public: Q_INVOKABLE void setHorizontalScale(qreal scale); Q_INVOKABLE void setVerticalScale(qreal scale, int layer); Q_INVOKABLE void setVerticalScale(qreal scale); - Q_INVOKABLE int getAngle(int layer = 0); - Q_INVOKABLE void setAngle(int angle, int layer); - Q_INVOKABLE void setAngle(int angle); + 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); diff --git a/include/ui/overlay.h b/include/ui/overlay.h index f951360d..60c5f904 100644 --- a/include/ui/overlay.h +++ b/include/ui/overlay.h @@ -93,8 +93,8 @@ public: qreal getVScale(); void setHScale(qreal scale); void setVScale(qreal scale); - int getAngle(); - void setAngle(int angle); + int getRotation(); + void setRotation(int angle); void rotate(int degrees); void setClippingRect(QRectF rect); void clearClippingRect(); diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index 2c7b55ee..79c5e4aa 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -179,19 +179,19 @@ void MapView::setVerticalScale(qreal scale) { this->scene()->update(); } -int MapView::getAngle(int layer) { - return this->getOverlay(layer)->getAngle(); +int MapView::getRotation(int layer) { + return this->getOverlay(layer)->getRotation(); } -void MapView::setAngle(int angle, int layer) { - this->getOverlay(layer)->setAngle(angle); +void MapView::setRotation(int angle, int layer) { + this->getOverlay(layer)->setRotation(angle); this->scene()->update(); } -// Overload. No layer provided, set angle of all layers -void MapView::setAngle(int angle) { +// Overload. No layer provided, set rotation of all layers +void MapView::setRotation(int angle) { foreach (Overlay * layer, this->overlayMap) - layer->setAngle(angle); + layer->setRotation(angle); this->scene()->update(); } diff --git a/src/ui/overlay.cpp b/src/ui/overlay.cpp index 2713ce2e..5de4ce37 100644 --- a/src/ui/overlay.cpp +++ b/src/ui/overlay.cpp @@ -106,11 +106,11 @@ void Overlay::setVScale(qreal scale) { this->vScale = scale; } -int Overlay::getAngle() { +int Overlay::getRotation() { return this->angle; } -void Overlay::setAngle(int angle) { +void Overlay::setRotation(int angle) { this->angle = angle; this->clampAngle(); } @@ -122,7 +122,7 @@ void Overlay::rotate(int degrees) { void Overlay::clampAngle() { // transform.rotate would handle this already, but we only - // want to report angles 0-359 for Overlay::getAngle + // want to report angles 0-359 for Overlay::getRotation this->angle %= 360; if (this->angle < 0) this->angle += 360; From acf9673807d48322d957befe40ded0e91287e85b Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sat, 15 Oct 2022 13:17:18 -0400 Subject: [PATCH 25/33] Add 2D array version of addPath to manual --- docsrc/manual/scripting-capabilities.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index e6608248..215f4f53 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -1213,9 +1213,18 @@ All overlay functions are callable via the global ``overlay`` object. :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.addPath(coords, borderColor = "#000000", fillColor = "", layer = 0) + + 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 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) - 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 `_. + 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 From 3f2fea0980d16e74ace549a29a6637936b3c8354 Mon Sep 17 00:00:00 2001 From: garak Date: Sun, 16 Oct 2022 18:37:51 -0400 Subject: [PATCH 26/33] event updates - select first event in order of tabs and lowest index (prev: select top left event) - maintain current event selection between tab changes --- src/editor.cpp | 11 ++++-- src/mainwindow.cpp | 87 ++++++++++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/editor.cpp b/src/editor.cpp index 6f636975..b40f1d68 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2034,7 +2034,10 @@ void Editor::selectMapEvent(DraggablePixmapItem *object, bool toggle) { void Editor::selectedEventIndexChanged(int index, Event::Group eventGroup) { int event_offs = Event::getIndexOffset(eventGroup); - Event *event = this->map->events.value(eventGroup).at(index - event_offs); + Event *event = nullptr; + if (index < this->map->events.value(eventGroup).length()) { + event = this->map->events.value(eventGroup).at(index - event_offs); + } DraggablePixmapItem *selectedEvent = nullptr; for (QGraphicsItem *child : this->events_group->childItems()) { DraggablePixmapItem *item = static_cast(child); @@ -2044,7 +2047,11 @@ void Editor::selectedEventIndexChanged(int index, Event::Group eventGroup) } } - if (selectedEvent) this->selectMapEvent(selectedEvent); + if (selectedEvent) { + this->selectMapEvent(selectedEvent); + } else { + updateSelectedEvents(); + } } void Editor::duplicateSelectedEvents() { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2f18e2b1..1a79e0a0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1934,11 +1934,22 @@ void MainWindow::displayEventTabs() { } void MainWindow::updateObjects() { - selectedObject = nullptr; - selectedWarp = nullptr; - selectedTrigger = nullptr; - selectedBG = nullptr; - selectedHealspot = nullptr; + QList all_objects = editor->getObjects(); + if (selectedObject && !all_objects.contains(selectedObject)) { + selectedObject = nullptr; + } + if (selectedWarp && !all_objects.contains(selectedWarp)) { + selectedWarp = nullptr; + } + if (selectedTrigger && !all_objects.contains(selectedTrigger)) { + selectedTrigger = nullptr; + } + if (selectedBG && !all_objects.contains(selectedBG)) { + selectedBG = nullptr; + } + if (selectedHealspot && !all_objects.contains(selectedHealspot)) { + selectedHealspot = nullptr; + } displayEventTabs(); @@ -1953,11 +1964,17 @@ void MainWindow::updateSelectedObjects() { events = *editor->selected_events; } else { + QList all_events; + if (editor->map) { + all_events = editor->map->getAllEvents(); + } if (all_events.length()) { - DraggablePixmapItem *selectedEvent = all_events.first(); - editor->selected_events->append(selectedEvent); - editor->redrawObject(selectedEvent); - events.append(selectedEvent); + DraggablePixmapItem *selectedEvent = all_events.first()->getPixmapItem(); + if (selectedEvent) { + editor->selected_events->append(selectedEvent); + editor->redrawObject(selectedEvent); + events.append(selectedEvent); + } } } @@ -1978,6 +1995,8 @@ void MainWindow::updateSelectedObjects() { target = ui->scrollAreaWidgetContents_Objects; ui->tabWidget_EventType->setCurrentWidget(ui->tab_Objects); + selectedObject = current->getPixmapItem(); + this->ui->spinner_ObjectID->setMinimum(event_offs); this->ui->spinner_ObjectID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_ObjectID->setValue(current->getEventIndex() + event_offs); @@ -1988,6 +2007,8 @@ void MainWindow::updateSelectedObjects() { target = ui->scrollAreaWidgetContents_Warps; ui->tabWidget_EventType->setCurrentWidget(ui->tab_Warps); + selectedWarp = current->getPixmapItem(); + this->ui->spinner_WarpID->setMinimum(event_offs); this->ui->spinner_WarpID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_WarpID->setValue(current->getEventIndex() + event_offs); @@ -1998,6 +2019,8 @@ void MainWindow::updateSelectedObjects() { target = ui->scrollAreaWidgetContents_Triggers; ui->tabWidget_EventType->setCurrentWidget(ui->tab_Triggers); + selectedTrigger = current->getPixmapItem(); + this->ui->spinner_TriggerID->setMinimum(event_offs); this->ui->spinner_TriggerID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_TriggerID->setValue(current->getEventIndex() + event_offs); @@ -2008,6 +2031,8 @@ void MainWindow::updateSelectedObjects() { target = ui->scrollAreaWidgetContents_BGs; ui->tabWidget_EventType->setCurrentWidget(ui->tab_BGs); + selectedBG = current->getPixmapItem(); + this->ui->spinner_BgID->setMinimum(event_offs); this->ui->spinner_BgID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_BgID->setValue(current->getEventIndex() + event_offs); @@ -2018,6 +2043,8 @@ void MainWindow::updateSelectedObjects() { target = ui->scrollAreaWidgetContents_Healspots; ui->tabWidget_EventType->setCurrentWidget(ui->tab_Healspots); + selectedHealspot = current->getPixmapItem(); + this->ui->spinner_HealID->setMinimum(event_offs); this->ui->spinner_HealID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_HealID->setValue(current->getEventIndex() + event_offs); @@ -2167,26 +2194,7 @@ void MainWindow::on_toolButton_deleteObject_clicked() { for (DraggablePixmapItem *item : *editor->selected_events) { Event::Group event_group = item->event->getEventGroup(); if (event_group != Event::Group::Heal) { - // Get the index for the event that should be selected after this event has been deleted. - // If it's at the end of the list, select the previous event, otherwise select the next one. - // Don't bother getting the event to select if there are still more events to delete - if (++numDeleted == numEvents) { - int index = editor->map->events.value(event_group).indexOf(item->event); - if (index != editor->map->events.value(event_group).size() - 1) - index++; - else - index--; - Event *event = nullptr; - if (index >= 0) - event = editor->map->events.value(event_group).at(index); - for (QGraphicsItem *child : editor->events_group->childItems()) { - DraggablePixmapItem *event_item = static_cast(child); - if (event_item->event == event) { - nextSelectedEvent = event_item; - break; - } - } - } + numDeleted++; item->event->setPixmapItem(item); selectedEvents.append(item->event); } @@ -2195,6 +2203,27 @@ void MainWindow::on_toolButton_deleteObject_clicked() { } } if (numDeleted) { + // Get the index for the event that should be selected after this event has been deleted. + // Select event at next smallest index when deleting a single event. + // If deleting multiple events, just let editor work out next selected. + if (numDeleted == 1) { + Event::Group event_group = selectedEvents[0]->getEventGroup(); + int index = editor->map->events.value(event_group).indexOf(selectedEvents[0]); + if (index != editor->map->events.value(event_group).size() - 1) + index++; + else + index--; + Event *event = nullptr; + if (index >= 0) + event = editor->map->events.value(event_group).at(index); + for (QGraphicsItem *child : editor->events_group->childItems()) { + DraggablePixmapItem *event_item = static_cast(child); + if (event_item->event == event) { + nextSelectedEvent = event_item; + break; + } + } + } editor->map->editHistory.push(new EventDelete(editor, editor->map, selectedEvents, nextSelectedEvent ? nextSelectedEvent->event : nullptr)); } } From 265340eed0a17d3cc23f8a87436325ca5a45cc03 Mon Sep 17 00:00:00 2001 From: garak Date: Sun, 16 Oct 2022 18:43:05 -0400 Subject: [PATCH 27/33] add local script labels to event combos for drop down --- include/ui/eventframes.h | 8 ++++++++ src/ui/eventframes.cpp | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/ui/eventframes.h b/include/ui/eventframes.h index e55e3dca..181ac141 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -148,6 +148,10 @@ 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; @@ -194,6 +198,10 @@ 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; diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index fe0b2bcd..f8b0eacc 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -385,6 +385,11 @@ void ObjectFrame::populate(Project *project) { QStringList scriptLabels = this->object->getMap()->eventScriptLabels() + project->getGlobalScriptLabels(); scriptLabels.removeDuplicates(); + if (this->object->getMap()) { + const auto localScriptLabels = this->object->getMap()->eventScriptLabels(); + this->combo_script->addItems(localScriptLabels); + } + this->scriptCompleter = new QCompleter(scriptLabels, this); this->scriptCompleter->setCaseSensitivity(Qt::CaseInsensitive); this->scriptCompleter->setFilterMode(Qt::MatchContains); @@ -649,6 +654,11 @@ void TriggerFrame::populate(Project *project) { QStringList scriptLabels = this->trigger->getMap()->eventScriptLabels() + project->getGlobalScriptLabels(); scriptLabels.removeDuplicates(); + if (this->trigger->getMap()) { + const auto localScriptLabels = this->trigger->getMap()->eventScriptLabels(); + this->combo_script->addItems(localScriptLabels); + } + this->scriptCompleter = new QCompleter(scriptLabels, this); this->scriptCompleter->setCaseSensitivity(Qt::CaseInsensitive); this->scriptCompleter->setFilterMode(Qt::MatchContains); @@ -792,6 +802,11 @@ void SignFrame::populate(Project *project) { QStringList scriptLabels = this->sign->getMap()->eventScriptLabels() + project->getGlobalScriptLabels(); scriptLabels.removeDuplicates(); + if (this->sign->getMap()) { + const auto localScriptLabels = this->sign->getMap()->eventScriptLabels(); + this->combo_script->addItems(localScriptLabels); + } + this->scriptCompleter = new QCompleter(scriptLabels, this); this->scriptCompleter->setCaseSensitivity(Qt::CaseInsensitive); this->scriptCompleter->setFilterMode(Qt::MatchContains); From 73ccc3d89d85620a041767a6d94b5fdd4e4bd049 Mon Sep 17 00:00:00 2001 From: garak Date: Sun, 16 Oct 2022 19:16:37 -0400 Subject: [PATCH 28/33] fix bug preventing selection of last event from spinner --- src/editor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/editor.cpp b/src/editor.cpp index b40f1d68..ea4307ea 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2031,12 +2031,12 @@ void Editor::selectMapEvent(DraggablePixmapItem *object, bool toggle) { } } -void Editor::selectedEventIndexChanged(int index, Event::Group eventGroup) -{ +void Editor::selectedEventIndexChanged(int index, Event::Group eventGroup) { int event_offs = Event::getIndexOffset(eventGroup); + index = index - event_offs; Event *event = nullptr; if (index < this->map->events.value(eventGroup).length()) { - event = this->map->events.value(eventGroup).at(index - event_offs); + event = this->map->events.value(eventGroup).at(index); } DraggablePixmapItem *selectedEvent = nullptr; for (QGraphicsItem *child : this->events_group->childItems()) { From 715c37572e288500f9e43f063296087c24a97fb9 Mon Sep 17 00:00:00 2001 From: garak Date: Sun, 16 Oct 2022 19:45:12 -0400 Subject: [PATCH 29/33] update clone object sprite icon with target change --- src/ui/eventframes.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index f8b0eacc..90a88f7f 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -435,10 +435,14 @@ void CloneObjectFrame::connectSignals() { EventFrame::connectSignals(); + // update icon displayed in frame with target + connect(this->clone->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); + // target map this->combo_target_map->disconnect(); connect(this->combo_target_map, &QComboBox::currentTextChanged, [this](const QString &text) { this->clone->setTargetMap(text); + this->clone->getPixmapItem()->updatePixmap(); this->clone->modify(); }); @@ -446,6 +450,7 @@ void CloneObjectFrame::connectSignals() { this->spinner_target_id->disconnect(); connect(this->spinner_target_id, QOverload::of(&QSpinBox::valueChanged), [this](int value) { this->clone->setTargetID(value); + this->clone->getPixmapItem()->updatePixmap(); this->clone->modify(); }); } From c3a5e05f493ec42ceacaa571ce55ddb1801c263e Mon Sep 17 00:00:00 2001 From: garak Date: Sun, 16 Oct 2022 21:04:29 -0400 Subject: [PATCH 30/33] update clone sprite text --- src/ui/eventframes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index 90a88f7f..c3f254c1 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -443,6 +443,7 @@ void CloneObjectFrame::connectSignals() { connect(this->combo_target_map, &QComboBox::currentTextChanged, [this](const QString &text) { this->clone->setTargetMap(text); this->clone->getPixmapItem()->updatePixmap(); + this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); }); @@ -451,6 +452,7 @@ void CloneObjectFrame::connectSignals() { connect(this->spinner_target_id, QOverload::of(&QSpinBox::valueChanged), [this](int value) { this->clone->setTargetID(value); this->clone->getPixmapItem()->updatePixmap(); + this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); }); } From 33014830cda83e27eaf277bacefeeecaad4ad79e Mon Sep 17 00:00:00 2001 From: garak Date: Sun, 16 Oct 2022 22:15:48 -0400 Subject: [PATCH 31/33] set map early when creating new events because some types need map info for defaults --- src/editor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/editor.cpp b/src/editor.cpp index ea4307ea..cdd1619f 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2125,6 +2125,7 @@ DraggablePixmapItem *Editor::addNewEvent(Event::Type type) { } if (!event) return nullptr; + event->setMap(this->map); event->setDefaultValues(this->project); map->editHistory.push(new EventCreate(this, map, event)); From 3de8e88b8ec30b4a589adfa240f81a428fb382ae Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Mon, 17 Oct 2022 19:03:11 -0500 Subject: [PATCH 32/33] Add optional forceRedraw argument to palette API functions --- docsrc/manual/scripting-capabilities.rst | 24 ++++++++---- include/mainwindow.h | 16 ++++---- src/scriptapi/apimap.cpp | 48 ++++++++++++++++-------- 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index 215f4f53..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() diff --git a/include/mainwindow.h b/include/mainwindow.h index 89319d0e..d8fa4201 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -78,10 +78,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 +90,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); diff --git a/src/scriptapi/apimap.cpp b/src/scriptapi/apimap.cpp index 419ef1c6..9451d1ee 100644 --- a/src/scriptapi/apimap.cpp +++ b/src/scriptapi/apimap.cpp @@ -347,36 +347,44 @@ void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors) { +void MainWindow::setPrimaryTilesetPalette(int paletteIndex, QList> colors, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return; this->setTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex, colors); - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); + if (forceRedraw) { + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); + } } -void MainWindow::setPrimaryTilesetPalettes(QList>> palettes) { +void MainWindow::setPrimaryTilesetPalettes(QList>> palettes, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return; for (int i = 0; i < palettes.size(); i++) { this->setTilesetPalette(this->editor->map->layout->tileset_primary, i, palettes[i]); } - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); + if (forceRedraw) { + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); + } } -void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors) { +void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return; this->setTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex, colors); - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); + if (forceRedraw) { + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); + } } -void MainWindow::setSecondaryTilesetPalettes(QList>> palettes) { +void MainWindow::setSecondaryTilesetPalettes(QList>> palettes, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return; for (int i = 0; i < palettes.size(); i++) { this->setTilesetPalette(this->editor->map->layout->tileset_secondary, i, palettes[i]); } - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); + if (forceRedraw) { + this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); + } } QJSValue MainWindow::getTilesetPalette(const QList> &palettes, int paletteIndex) { @@ -449,36 +457,44 @@ void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QL } } -void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors) { +void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return; this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, paletteIndex, colors); - this->refreshAfterPalettePreviewChange(); + if (forceRedraw) { + this->refreshAfterPalettePreviewChange(); + } } -void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palettes) { +void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palettes, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) return; for (int i = 0; i < palettes.size(); i++) { this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, i, palettes[i]); } - this->refreshAfterPalettePreviewChange(); + if (forceRedraw) { + this->refreshAfterPalettePreviewChange(); + } } -void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors) { +void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return; this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); - this->refreshAfterPalettePreviewChange(); + if (forceRedraw) { + this->refreshAfterPalettePreviewChange(); + } } -void MainWindow::setSecondaryTilesetPalettesPreview(QList>> palettes) { +void MainWindow::setSecondaryTilesetPalettesPreview(QList>> palettes, bool forceRedraw) { if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) return; for (int i = 0; i < palettes.size(); i++) { this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, i, palettes[i]); } - this->refreshAfterPalettePreviewChange(); + if (forceRedraw) { + this->refreshAfterPalettePreviewChange(); + } } QJSValue MainWindow::getPrimaryTilesetPalettePreview(int paletteIndex) { From 4b565208f7f71746db57cc9cefce41c647e069d8 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Mon, 17 Oct 2022 19:37:26 -0500 Subject: [PATCH 33/33] Fix one-frame EventFrame jank --- src/mainwindow.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1a79e0a0..dc48dcc3 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2088,11 +2088,14 @@ void MainWindow::updateSelectedObjects() { for (QFrame *frame : frames) { layout->addWidget(frame); + } + layout->addStretch(1); + // Show the frames after the vertical spacer is added to avoid visual jank + // where the frame would stretch to the bottom of the layout. + for (QFrame *frame : frames) { frame->show(); } - layout->addStretch(1); - ui->label_NoEvents->hide(); ui->tabWidget_EventType->show(); }