diff --git a/include/core/events.h b/include/core/events.h index e6427466..e0f1d1b9 100644 --- a/include/core/events.h +++ b/include/core/events.h @@ -345,6 +345,8 @@ public: void setDestinationWarpID(QString newDestinationWarpID) { this->destinationWarpID = newDestinationWarpID; } QString getDestinationWarpID() { return this->destinationWarpID; } + void setWarningEnabled(bool enabled); + private: QString destinationMap; QString destinationWarpID; diff --git a/include/editor.h b/include/editor.h index 8f7cd54d..ac1f8e4c 100644 --- a/include/editor.h +++ b/include/editor.h @@ -103,7 +103,8 @@ public: QList getObjects(); void updateCursorRectPos(int x, int y); void setCursorRectVisible(bool visible); - + void updateWarpEventWarning(Event *event); + void updateWarpEventWarnings(); bool eventLimitReached(Map *, Event::Type); QGraphicsScene *scene = nullptr; @@ -189,6 +190,7 @@ private: static bool startDetachedProcess(const QString &command, const QString &workingDirectory = QString(), qint64 *pid = nullptr); + void constructBehaviorValueList(); // TODO: Remove private slots: void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); diff --git a/include/mainwindow.h b/include/mainwindow.h index 6c534dee..2eaedeab 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -159,6 +159,7 @@ public: public slots: void on_mainTabBar_tabBarClicked(int index); void on_mapViewTab_tabBarClicked(int index); + void openProjectSettingsEditor(int tab); private slots: void on_action_Open_Project_triggered(); diff --git a/include/ui/eventframes.h b/include/ui/eventframes.h index 942b9179..9cabd585 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -6,6 +6,7 @@ #include "noscrollspinbox.h" #include "noscrollcombobox.h" +#include "mainwindow.h" #include "events.h" @@ -22,7 +23,7 @@ public: virtual void setup(); void initCustomAttributesTable(); - virtual void connectSignals(); + virtual void connectSignals(MainWindow *); virtual void initialize(); virtual void populate(Project *project); @@ -69,7 +70,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -101,7 +102,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -124,12 +125,13 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: NoScrollComboBox *combo_dest_map; NoScrollComboBox *combo_dest_warp; + QPushButton *warning; private: WarpEvent *warp; @@ -146,7 +148,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -171,7 +173,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -192,7 +194,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -216,7 +218,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -242,7 +244,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: @@ -263,7 +265,7 @@ public: virtual void setup() override; virtual void initialize() override; - virtual void connectSignals() override; + virtual void connectSignals(MainWindow *) override; virtual void populate(Project *project) override; public: diff --git a/include/ui/projectsettingseditor.h b/include/ui/projectsettingseditor.h index 75843886..35ff95f3 100644 --- a/include/ui/projectsettingseditor.h +++ b/include/ui/projectsettingseditor.h @@ -21,6 +21,9 @@ public: explicit ProjectSettingsEditor(QWidget *parent = nullptr, Project *project = nullptr); ~ProjectSettingsEditor(); + static const int warpBehaviorsTab; + void setTab(int index); + signals: void reloadProject(); diff --git a/src/core/events.cpp b/src/core/events.cpp index 585c68e1..788d438f 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -517,6 +517,13 @@ QSet WarpEvent::getExpectedFields() { return expectedFields; } +void WarpEvent::setWarningEnabled(bool enabled) { + WarpFrame * frame = static_cast(this->getEventFrame()); + if (frame && frame->warning) + frame->warning->setVisible(enabled); +} + + Event *TriggerEvent::duplicate() { TriggerEvent *copy = new TriggerEvent(); diff --git a/src/editor.cpp b/src/editor.cpp index c4d314ff..5c3bd7b3 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -106,6 +106,7 @@ void Editor::setEditingMap() { this->cursorMapTileRect->setActive(true); setMapEditingButtonsEnabled(true); + this->constructBehaviorValueList(); // TODO: Remove } void Editor::setEditingCollision() { @@ -151,6 +152,7 @@ void Editor::setEditingObjects() { setConnectionsEditable(false); this->cursorMapTileRect->setSingleTileMode(); this->cursorMapTileRect->setActive(false); + updateWarpEventWarnings(); setMapEditingButtonsEnabled(false); } @@ -1987,6 +1989,82 @@ void Editor::redrawObject(DraggablePixmapItem *item) { } } +/* TODO: Add tab to PSE for warp behaviors, containing: + - A description of what it means + - A check box to enable/disable the warning + - An editable list of warp behavior names + +*/ +// TODO: Relocate to file handling the "warp behaviors" window, make static list below a config default +const QStringList warpBehaviorNames = { + "MB_NORTH_ARROW_WARP", + "MB_SOUTH_ARROW_WARP", + "MB_WEST_ARROW_WARP", + "MB_EAST_ARROW_WARP", + "MB_STAIRS_OUTSIDE_ABANDONED_SHIP", + "MB_WATER_SOUTH_ARROW_WARP", + "MB_SHOAL_CAVE_ENTRANCE", + "MB_SECRET_BASE_SPOT_RED_CAVE_OPEN", + "MB_SECRET_BASE_SPOT_BROWN_CAVE_OPEN", + "MB_SECRET_BASE_SPOT_YELLOW_CAVE_OPEN", + "MB_SECRET_BASE_SPOT_BLUE_CAVE_OPEN", + "MB_SECRET_BASE_SPOT_TREE_LEFT_OPEN", + "MB_SECRET_BASE_SPOT_TREE_RIGHT_OPEN", + "MB_SECRET_BASE_SPOT_SHRUB_OPEN", + "MB_ANIMATED_DOOR", + "MB_NON_ANIMATED_DOOR", + "MB_PETALBURG_GYM_DOOR", + "MB_WATER_DOOR", + "MB_LADDER", + "MB_UP_ESCALATOR", + "MB_DOWN_ESCALATOR", + "MB_DEEP_SOUTH_WARP", + "MB_LAVARIDGE_GYM_B1F_WARP", + "MB_LAVARIDGE_GYM_1F_WARP", + "MB_AQUA_HIDEOUT_WARP", + "MB_MT_PYRE_HOLE", + "MB_MOSSDEEP_GYM_WARP", + "MB_BRIDGE_OVER_OCEAN", +}; +QSet warpBehaviorValues; +void Editor::constructBehaviorValueList() { + if (!project) return; + warpBehaviorValues.clear(); + //warpBehaviorNames.removeDuplicates(); // TODO: Uncomment when list is actually dynamic + for (auto name : warpBehaviorNames) { + if (project->metatileBehaviorMap.contains(name)) { + warpBehaviorValues.insert(project->metatileBehaviorMap[name]); + } + } +} + +// Warp events display a warning if they're not positioned on a metatile with a warp behavior. +void Editor::updateWarpEventWarning(Event *event) { + if (!map || !event || event->getEventType() != Event::Type::Warp) + return; + Block block; + Metatile * metatile = nullptr; + WarpEvent * warpEvent = static_cast(event); + if (map->getBlock(warpEvent->getX(), warpEvent->getY(), &block)) { + metatile = Tileset::getMetatile(block.metatileId(), map->layout->tileset_primary, map->layout->tileset_secondary); + } + // metatile may be null if the warp is in the map border. Display the warning in this case + bool validWarpBehavior = metatile && warpBehaviorValues.contains(metatile->behavior()); + warpEvent->setWarningEnabled(!validWarpBehavior); +} + +// The warp event behavior warning is updated whenever the event moves or the event selection changes. +// It does not respond to changes in the underlying metatile. To capture the common case of a user painting +// metatiles on the Map tab then returning to the Events tab we update the warings for all selected warp +// events when the Events tab is opened. This does not cover the case where metatiles are painted while +// still on the Events tab, such as by Undo/Redo or the scripting API. +void Editor::updateWarpEventWarnings() { + if (selected_events) { + for (auto selection : *selected_events) + updateWarpEventWarning(selection->event); + } +} + void Editor::shouldReselectEvents() { selectNewEvents = true; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9cfd993e..355fcdb3 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2035,7 +2035,7 @@ void MainWindow::updateSelectedObjects() { EventFrame *eventFrame = event->createEventFrame(); eventFrame->populate(this->editor->project); eventFrame->initialize(); - eventFrame->connectSignals(); + eventFrame->connectSignals(this); frames.append(eventFrame); } @@ -2730,16 +2730,20 @@ void MainWindow::togglePreferenceSpecificUi() { ui->actionOpen_Project_in_Text_Editor->setEnabled(true); } -void MainWindow::on_actionProject_Settings_triggered() { +void MainWindow::openProjectSettingsEditor(int tab) { if (!this->projectSettingsEditor) { this->projectSettingsEditor = new ProjectSettingsEditor(this, this->editor->project); connect(this->projectSettingsEditor, &ProjectSettingsEditor::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered); } - + this->projectSettingsEditor->setTab(tab); openSubWindow(this->projectSettingsEditor); } +void MainWindow::on_actionProject_Settings_triggered() { + this->openProjectSettingsEditor(porymapConfig.getProjectSettingsTab()); +} + void MainWindow::on_actionCustom_Scripts_triggered() { if (!this->customScriptsEditor) initCustomScriptsEditor(); diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index 9e7ab855..13625db4 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -17,6 +17,7 @@ void DraggablePixmapItem::updatePosition() { } else { setZValue(event->getY()); } + editor->updateWarpEventWarning(event); } void DraggablePixmapItem::emitPositionChanged() { diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index b015283b..7da0a977 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -3,9 +3,6 @@ #include "editcommands.h" #include "draggablepixmapitem.h" -#include "project.h" -#include "editor.h" - #include using std::numeric_limits; @@ -109,7 +106,7 @@ void EventFrame::initCustomAttributesTable() { this->layout_contents->addWidget(customAttributes); } -void EventFrame::connectSignals() { +void EventFrame::connectSignals(MainWindow *) { this->connected = true; this->spinner_x->disconnect(); @@ -258,10 +255,10 @@ void ObjectFrame::setup() { EventFrame::initCustomAttributesTable(); } -void ObjectFrame::connectSignals() { +void ObjectFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // sprite update this->combo_sprite->disconnect(); @@ -418,10 +415,10 @@ void CloneObjectFrame::setup() { EventFrame::initCustomAttributesTable(); } -void CloneObjectFrame::connectSignals() { +void CloneObjectFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // update icon displayed in frame with target connect(this->clone->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); @@ -472,8 +469,6 @@ void CloneObjectFrame::populate(Project *project) { this->combo_target_map->addItems(project->mapNames); } - - void WarpFrame::setup() { EventFrame::setup(); @@ -493,14 +488,26 @@ void WarpFrame::setup() { l_form_dest_warp->addRow("Destination Warp", this->combo_dest_warp); this->layout_contents->addLayout(l_form_dest_warp); + // warning + static const QString warningText = "Warning:\n" + "This warp event is not positioned on a metatile with a warp behavior.\n" + "Click this warning for more details."; + QVBoxLayout *l_vbox_warning = new QVBoxLayout(); + this->warning = new QPushButton(warningText, this); + this->warning->setFlat(true); + this->warning->setStyleSheet("color: red; text-align: left"); + this->warning->setVisible(false); + l_vbox_warning->addWidget(this->warning); + this->layout_contents->addLayout(l_vbox_warning); + // custom attributes EventFrame::initCustomAttributesTable(); } -void WarpFrame::connectSignals() { +void WarpFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // dest map this->combo_dest_map->disconnect(); @@ -515,6 +522,12 @@ void WarpFrame::connectSignals() { this->warp->setDestinationWarpID(text); this->warp->modify(); }); + + // warning + this->warning->disconnect(); + connect(this->warning, &QPushButton::clicked, [window]() { + window->openProjectSettingsEditor(ProjectSettingsEditor::warpBehaviorsTab); + }); } void WarpFrame::initialize() { @@ -572,10 +585,10 @@ void TriggerFrame::setup() { EventFrame::initCustomAttributesTable(); } -void TriggerFrame::connectSignals() { +void TriggerFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // label this->combo_script->disconnect(); @@ -657,10 +670,10 @@ void WeatherTriggerFrame::setup() { EventFrame::initCustomAttributesTable(); } -void WeatherTriggerFrame::connectSignals() { +void WeatherTriggerFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // weather this->combo_weather->disconnect(); @@ -716,10 +729,10 @@ void SignFrame::setup() { EventFrame::initCustomAttributesTable(); } -void SignFrame::connectSignals() { +void SignFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // facing dir this->combo_facing_dir->disconnect(); @@ -818,10 +831,10 @@ void HiddenItemFrame::setup() { EventFrame::initCustomAttributesTable(); } -void HiddenItemFrame::connectSignals() { +void HiddenItemFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); // item this->combo_item->disconnect(); @@ -909,10 +922,10 @@ void SecretBaseFrame::setup() { EventFrame::initCustomAttributesTable(); } -void SecretBaseFrame::connectSignals() { +void SecretBaseFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); this->combo_base_id->disconnect(); connect(this->combo_base_id, &QComboBox::currentTextChanged, [this](const QString &text) { @@ -973,10 +986,10 @@ void HealLocationFrame::setup() { EventFrame::initCustomAttributesTable(); } -void HealLocationFrame::connectSignals() { +void HealLocationFrame::connectSignals(MainWindow *window) { if (this->connected) return; - EventFrame::connectSignals(); + EventFrame::connectSignals(window); if (projectConfig.getHealLocationRespawnDataEnabled()) { this->combo_respawn_map->disconnect(); diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index 54452072..44930598 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -10,6 +10,8 @@ Editor for the settings in a user's porymap.project.cfg file (and 'use_encounter_json' in porymap.user.cfg). */ +const int ProjectSettingsEditor::warpBehaviorsTab = 4; + ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) : QMainWindow(parent), ui(new Ui::ProjectSettingsEditor), @@ -84,11 +86,6 @@ void ProjectSettingsEditor::markEdited() { this->hasUnsavedChanges = true; } -// Remember the current settings tab for future sessions -void ProjectSettingsEditor::on_mainTabs_tabBarClicked(int index) { - porymapConfig.setProjectSettingsTab(index); -} - void ProjectSettingsEditor::initUi() { // Populate combo boxes if (project) { @@ -99,9 +96,6 @@ void ProjectSettingsEditor::initUi() { ui->comboBox_BaseGameVersion->addItems(ProjectConfig::versionStrings); ui->comboBox_AttributesSize->addItems({"1", "2", "4"}); - // Select tab from last session - ui->mainTabs->setCurrentIndex(porymapConfig.getProjectSettingsTab()); - // Validate that the border metatiles text is a comma-separated list of metatile values static const QString regex_Hex = "(0[xX])?[A-Fa-f0-9]+"; static const QRegularExpression expression(QString("^(%1,)*%1$").arg(regex_Hex)); // Comma-separated list of hex values @@ -163,6 +157,16 @@ void ProjectSettingsEditor::disableParsedSetting(QWidget * widget, const QString widget->setToolTip(QString("This value has been read from '%1' in %2").arg(name).arg(filepath)); } +// Remember the current settings tab for future sessions +void ProjectSettingsEditor::on_mainTabs_tabBarClicked(int index) { + porymapConfig.setProjectSettingsTab(index); +} + +void ProjectSettingsEditor::setTab(int index) { + ui->mainTabs->setCurrentIndex(index); + porymapConfig.setProjectSettingsTab(index); +} + void ProjectSettingsEditor::setBorderMetatilesUi(bool customSize) { ui->stackedWidget_BorderMetatiles->setCurrentIndex(customSize ? 0 : 1); }