diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..40c9e1ba
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,40 @@
+name: Build Porymap
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v2
+
+ - name: Cache Qt
+ id: cache-qt
+ uses: actions/cache@v1
+ with:
+ path: ../Qt
+ key: ${{ runner.os }}-QtCache
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v2
+ with:
+ version: '5.14.2'
+ modules: 'qtwidgets qtqml'
+ cached: ${{ steps.cache-qt.outputs.cache-hit }}
+
+ - name: Configure
+ run: qmake porymap.pro
+
+ - name: Compile
+ run: make
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1cea51d..b651c7c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- All plain text boxes now have a clear button to delete the text.
- The window sizes and positions of the tileset editor, palette editor, and region map editor are now stored in `porymap.cfg`.
- Add ruler tool for measuring metatile distance in events tab (Right-click to turn on/off, left-click to lock in place).
+- Add delete button to wild pokemon encounters tab.
+- Add shortcut customization via `Options -> Edit Shortcuts`.
+- Add custom text editor commands in `Options -> Edit Preferences`, a tool-button next to the `Script` combo-box, and `Tools -> Open Project in Text Editor`. The tool-button will open the containing file to the cooresponding script.
### Changed
- Holding `shift` now toggles "Smart Path" drawing; when the "Smart Paths" checkbox is checked, holding `shift` will temporarily disable it.
@@ -27,6 +30,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix porymap icon not showing on window or panel on Linux.
- The main window can now be resized to fit on lower resolution displays.
- Zooming the map in/out will now focus on the cursor.
+- Fix bug where object event sprites whose name contained a 0 character would display the placeholder "N" picture.
## [4.3.1] - 2020-07-17
### Added
diff --git a/README.md b/README.md
index f6f7a8b8..f1bd58d1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# porymap
+[![Actions Status](https://github.com/huderlem/porymap/workflows/Build%20Porymap/badge.svg)](https://github.com/huderlem/porymap/actions)
+
A map editor for the generation 3 decompilation projects using Qt.
Currently supports [pokeruby][pokeruby], [pokeemerald][pokeemerald], and [pokefirered][pokefirered].
diff --git a/docsrc/manual/editing-map-events.rst b/docsrc/manual/editing-map-events.rst
index 4a7ee536..ca0b4e41 100644
--- a/docsrc/manual/editing-map-events.rst
+++ b/docsrc/manual/editing-map-events.rst
@@ -225,11 +225,17 @@ Respawn NPC
Open Map Scripts
----------------
-Clicking the ``Open Map Scripts`` button |open-map-scripts-button| will open the map's scripts file in your default text editor. If nothing happens when this button is clicked, you may need to associate a text editor with the `.inc` file extension.
+Clicking the ``Open Map Scripts`` button |open-map-scripts-button| will open the map's scripts file in your default text editor. If nothing happens when this button is clicked, you may need to associate a text editor with the `.inc` file extension (or `.pory` if you're using Porycript).
+
+Additionally, if you specify a ``Goto Line Command`` in *Options -> Edit Preferences* then a tool-button will appear next to the `Script` combo-box in the *Events* tab. Clicking this button will open the file that contains the script directly to the line number of that script. If the script cannot be found in the project then the current map's scripts file is opened.
+|go-to-script-button|
.. |open-map-scripts-button|
image:: images/editing-map-events/open-map-scripts-button.png
+.. |go-to-script-button|
+ image:: images/editing-map-events/go-to-script-button.png
+
Tool Buttons
------------
@@ -260,7 +266,7 @@ Shift
Ruler Tool
----------
-The Ruler Tool provides a convenient way to measure distance on the map. This is particularly useful for scripting object movement. With the Pointer Tool selected you can activate the ruler with a Right-click. With the ruler active you can drag the mouse around to extend the ruler. The ruler can be deactivated with another Right-click, or locked in place with a Left-click (Left-click again to unlock the ruler). The dimensions of the ruler are displayed in a tool-tip and in the status bar in the bottom left corner of the widnow.
+The Ruler Tool provides a convenient way to measure distance on the map. This is particularly useful for scripting object movement. With the Pointer Tool selected you can activate the ruler with a Right-click. With the ruler active you can move the mouse around to extend the ruler. The ruler can be deactivated with another Right-click, or locked in place with a Left-click (Left-click again to unlock the ruler).
.. figure:: images/editing-map-events/event-tool-ruler.gif
:alt: Measuring metatile distance with the Ruler Tool
diff --git a/docsrc/manual/images/editing-map-events/event-tool-ruler.gif b/docsrc/manual/images/editing-map-events/event-tool-ruler.gif
index fdbbd12f..707c2037 100644
Binary files a/docsrc/manual/images/editing-map-events/event-tool-ruler.gif and b/docsrc/manual/images/editing-map-events/event-tool-ruler.gif differ
diff --git a/docsrc/manual/images/editing-map-events/go-to-script-button.png b/docsrc/manual/images/editing-map-events/go-to-script-button.png
new file mode 100644
index 00000000..859ed2f3
Binary files /dev/null and b/docsrc/manual/images/editing-map-events/go-to-script-button.png differ
diff --git a/docsrc/manual/images/shortcuts/edit-shortcuts.gif b/docsrc/manual/images/shortcuts/edit-shortcuts.gif
new file mode 100644
index 00000000..274abe8b
Binary files /dev/null and b/docsrc/manual/images/shortcuts/edit-shortcuts.gif differ
diff --git a/docsrc/manual/settings-and-options.rst b/docsrc/manual/settings-and-options.rst
index 9b055226..5a4968e3 100644
--- a/docsrc/manual/settings-and-options.rst
+++ b/docsrc/manual/settings-and-options.rst
@@ -31,6 +31,8 @@ determined by this file.
``monitor_files``, 1, global, yes, Whether porymap will monitor changes to project files
``region_map_dimensions``, 32x20, global, yes, The dimensions of the region map tilemap
``theme``, default, global, yes, The color theme for porymap windows and widgets
+ ``text_editor_goto_line``, , global, yes, The command that will be executed when clicking the button next the ``Script`` combo-box.
+ ``text_editor_open_directory``, , global, yes, The command that will be executed when clicking ``Open Project in Text Editor``.
``base_game_version``, , project, no, The base pret repo for this project
``use_encounter_json``, 1, project, yes, Enables wild encounter table editing
``use_poryscript``, 0, project, yes, Whether to open .pory files for scripts
diff --git a/docsrc/manual/shortcuts.rst b/docsrc/manual/shortcuts.rst
index 0acd894d..05a5cfd1 100644
--- a/docsrc/manual/shortcuts.rst
+++ b/docsrc/manual/shortcuts.rst
@@ -2,8 +2,16 @@
Shortcuts
*********
-Porymap has many shortcuts and it can sometimes be hard to keep track of them all.
-Here is a comprehensive list of the shortcuts provided all in one place for your convenience.
+Porymap has many keyboard shortcuts set by default, and even more that can be customized yourself. You can view and customize your shortcuts by going to *Options -> Edit Shortcuts*. Shortcut actions are grouped together by the window that they are used in (Main Window, Tileset Editor...). You can set up to 2 shortcuts per action, but you cannot have duplicate shortcuts set within the same window. If you enter a shortcut that is already in use, Porymap will prompt you cancel or overwrite the old shortcut. You can also restore your shortcuts to the defaults.
+
+.. figure:: images/shortcuts/edit-shortcuts.gif
+ :alt: Edit Shortcuts
+
+ Edit Shortcuts
+
+Your shortcuts are stored at ``%Appdata%\pret\porymap\porymap.shortcuts.cfg`` on Windows and ``~/Library/Application\ Support/pret/porymap/porymap.shortcuts.cfg`` on macOS).
+
+For reference, here is a comprehensive list of the default shortcuts set in Porymap.
Main Window
-----------
@@ -33,6 +41,7 @@ Main Window
Open New Tileset Dialog, ``Ctrl+Shift+N``
Open Tileset Editor, ``Ctrl+T``
Open Region Map Editor, ``Ctrl+M``
+ Edit Preferences, ``Ctrl+,``
.. csv-table::
:header: Map Editing
diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui
index f1061bdb..941a3179 100644
--- a/forms/mainwindow.ui
+++ b/forms/mainwindow.ui
@@ -2527,6 +2527,23 @@
+ -
+
+
+ <html><head/><body><p>Delete a group of wild pokemon data on this map.</p></body></html>
+
+
+
+
+
+
+ :/icons/delete.ico:/icons/delete.ico
+
+
+ true
+
+
+
-
@@ -2607,8 +2624,6 @@
-
-
@@ -2895,16 +2915,29 @@
Ctrl+Shift+N
-
-
- Themes...
-
-
Export Map Stitch Image...
+
+
+ Edit Preferences...
+
+
+ Ctrl+,
+
+
+
+
+ Open Project in Text Editor
+
+
+
+
+ Edit Shortcuts...
+
+
diff --git a/forms/preferenceeditor.ui b/forms/preferenceeditor.ui
new file mode 100644
index 00000000..c44fa98c
--- /dev/null
+++ b/forms/preferenceeditor.ui
@@ -0,0 +1,190 @@
+
+
+ PreferenceEditor
+
+
+
+ 0
+ 0
+ 600
+ 480
+
+
+
+ Preferences
+
+
+
+
+ 9
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Application Theme
+
+
+
+ -
+
+
+ Preferred Text Editor
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 582
+ 372
+
+
+
+
+ QLayout::SetMinimumSize
+
+
-
+
+
+ <html><head/><body><p>When this command is set a button will appear next to the <span style=" font-weight:600; font-style:italic;">Script</span> combo-box in the <span style=" font-weight:600; font-style:italic;">Events</span> tab which executes this command.<span style=" font-weight:600;"> %F</span> will be substituted with the file path of the script and <span style=" font-weight:600;">%L</span> will be substituted with the line number of the script in that file. <span style=" font-weight:600;">%F </span><span style=" font-style:italic;">must</span> be given if <span style=" font-weight:600;">%L</span> is given. If <span style=" font-weight:600;">%F</span> is <span style=" font-style:italic;">not</span> given then the script's file path will be added to the end of the command. If the script can't be found then the current map's scripts file is opened.</p></body></html>
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+ -
+
+
+ Goto Line Command
+
+
+
+ -
+
+
+ The shell command for your preferred text editor (possibly an absolute path if the program doesn't exist in your PATH).
+
+
+ e.g. code %D
+
+
+ true
+
+
+
+ -
+
+
+ <html><head/><body><p>This is the command that is executed when clicking <span style=" font-weight:600; font-style:italic;">Open Project in Text Editor</span> in the <span style=" font-weight:600; font-style:italic;">Tools</span> menu. <span style=" font-weight:600;">%D</span> will be substituted with the project's root directory. If <span style=" font-weight:600;">%D</span> is <span style=" font-style:italic;">not</span> specified then the project directory will be added to the end of the command.</p></body></html>
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+ -
+
+
+ Open Directory Command
+
+
+
+ -
+
+
+ The shell command for your preferred text editor to open a file to a specific line number (possibly an absolute path if the program doesn't exist in your PATH).
+
+
+ e.g. code --goto %F:%L
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 15
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
diff --git a/forms/shortcutseditor.ui b/forms/shortcutseditor.ui
new file mode 100644
index 00000000..fab28d98
--- /dev/null
+++ b/forms/shortcutseditor.ui
@@ -0,0 +1,89 @@
+
+
+ ShortcutsEditor
+
+
+
+ 0
+ 0
+ 800
+ 700
+
+
+
+ Shortcuts Editor
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::Plain
+
+
+ 0
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 794
+ 642
+
+
+
+
+
+ -
+
+
+ QFrame::Box
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/config.h b/include/config.h
index 28c4dfa8..e9264d15 100644
--- a/include/config.h
+++ b/include/config.h
@@ -6,6 +6,8 @@
#include
#include
#include
+#include
+#include
enum MapSortOrder {
Group = 0,
@@ -46,6 +48,8 @@ public:
this->monitorFiles = true;
this->regionMapDimensions = QSize(32, 20);
this->theme = "default";
+ this->textEditorOpenFolder = "";
+ this->textEditorGotoLine = "";
}
void setRecentProject(QString project);
void setRecentMap(QString map);
@@ -62,6 +66,8 @@ public:
void setMonitorFiles(bool monitor);
void setRegionMapDimensions(int width, int height);
void setTheme(QString theme);
+ void setTextEditorOpenFolder(const QString &command);
+ void setTextEditorGotoLine(const QString &command);
QString getRecentProject();
QString getRecentMap();
MapSortOrder getMapSortOrder();
@@ -77,6 +83,8 @@ public:
bool getMonitorFiles();
QSize getRegionMapDimensions();
QString getTheme();
+ QString getTextEditorOpenFolder();
+ QString getTextEditorGotoLine();
protected:
virtual QString getConfigFilepath() override;
virtual void parseConfigKeyValue(QString key, QString value) override;
@@ -108,6 +116,8 @@ private:
bool monitorFiles;
QSize regionMapDimensions;
QString theme;
+ QString textEditorOpenFolder;
+ QString textEditorGotoLine;
};
extern PorymapConfig porymapConfig;
@@ -193,4 +203,53 @@ private:
extern ProjectConfig projectConfig;
+class QAction;
+class Shortcut;
+
+class ShortcutsConfig : public KeyValueConfigBase
+{
+public:
+ ShortcutsConfig() :
+ user_shortcuts({ }),
+ default_shortcuts({ })
+ { }
+
+ virtual void reset() override { user_shortcuts.clear(); }
+
+ // Call this before applying user shortcuts so that the user can restore defaults.
+ void setDefaultShortcuts(const QObjectList &objects);
+ QList defaultShortcuts(const QObject *object) const;
+
+ void setUserShortcuts(const QObjectList &objects);
+ void setUserShortcuts(const QMultiMap &objects_keySequences);
+ QList userShortcuts(const QObject *object) const;
+
+protected:
+ virtual QString getConfigFilepath() override;
+ virtual void parseConfigKeyValue(QString key, QString value) override;
+ virtual QMap getKeyValueMap() override;
+ virtual void onNewConfigFileCreated() override { };
+ virtual void setUnreadKeys() override { };
+
+private:
+ QMultiMap user_shortcuts;
+ QMultiMap default_shortcuts;
+
+ enum StoreType {
+ User,
+ Default
+ };
+
+ QString cfgKey(const QObject *object) const;
+ QList currentShortcuts(const QObject *object) const;
+
+ void storeShortcutsFromList(StoreType storeType, const QObjectList &objects);
+ void storeShortcuts(
+ StoreType storeType,
+ const QString &cfgKey,
+ const QList &keySequences);
+};
+
+extern ShortcutsConfig shortcutsConfig;
+
#endif // CONFIG_H
diff --git a/include/core/event.h b/include/core/event.h
index c46d95eb..4a6e7c4a 100644
--- a/include/core/event.h
+++ b/include/core/event.h
@@ -32,10 +32,10 @@ public:
Event(const Event&);
Event(QJsonObject, QString);
public:
- int x() {
+ int x() const {
return getInt("x");
}
- int y() {
+ int y() const {
return getInt("y");
}
int elevation() {
@@ -47,16 +47,16 @@ public:
void setY(int y) {
put("y", y);
}
- QString get(QString key) {
+ QString get(const QString &key) const {
return values.value(key);
}
- int getInt(QString key) {
+ int getInt(const QString &key) const {
return values.value(key).toInt(nullptr, 0);
}
- uint16_t getU16(QString key) {
+ uint16_t getU16(const QString &key) const {
return values.value(key).toUShort(nullptr, 0);
}
- int16_t getS16(QString key) {
+ int16_t getS16(const QString &key) const {
return values.value(key).toShort(nullptr, 0);
}
void put(QString key, int value) {
diff --git a/include/core/map.h b/include/core/map.h
index 639d7a02..7052802a 100644
--- a/include/core/map.h
+++ b/include/core/map.h
@@ -84,7 +84,8 @@ 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();
+ QList getAllEvents() const;
+ QStringList eventScriptLabels(const QString &event_group_type = QString()) const;
void removeEvent(Event*);
void addEvent(Event*);
QPixmap renderConnection(MapConnection, MapLayout *);
diff --git a/include/core/parseutil.h b/include/core/parseutil.h
index d3b7ee54..82fad4e2 100644
--- a/include/core/parseutil.h
+++ b/include/core/parseutil.h
@@ -41,7 +41,7 @@ class ParseUtil
public:
ParseUtil();
void set_root(QString);
- QString readTextFile(QString);
+ static QString readTextFile(QString);
void strip_comment(QString*);
QList* parseAsm(QString);
int evaluateDefine(QString, QMap*);
@@ -55,6 +55,17 @@ public:
bool tryParseJsonFile(QJsonDocument *out, QString filepath);
bool ensureFieldsExist(QJsonObject obj, QList fields);
+ // Returns the 1-indexed line number for the definition of scriptLabel in the scripts file at filePath.
+ // Returns 0 if a definition for scriptLabel cannot be found.
+ static int getScriptLineNumber(const QString &filePath, const QString &scriptLabel);
+ static int getRawScriptLineNumber(QString text, const QString &scriptLabel);
+ static int getPoryScriptLineNumber(QString text, const QString &scriptLabel);
+ static QString &removeStringLiterals(QString &text);
+ static QString &removeLineComments(QString &text, const QString &commentSymbol);
+ static QString &removeLineComments(QString &text, const QStringList &commentSymbols);
+
+ static QStringList splitShellCommand(QStringView command);
+
private:
QString root;
QString text;
diff --git a/include/editor.h b/include/editor.h
index a24a1bca..e876a3ca 100644
--- a/include/editor.h
+++ b/include/editor.h
@@ -66,7 +66,6 @@ public:
void displayMapBorder();
void displayMapGrid();
void displayWildMonTables();
- void maskNonVisibleConnectionTiles();
void updateMapBorder();
void updateMapConnections();
@@ -85,6 +84,7 @@ public:
void addNewConnection();
void removeCurrentConnection();
void addNewWildMonGroup(QWidget *window);
+ void deleteWildMonGroup();
void updateDiveMap(QString mapName);
void updateEmergeMap(QString mapName);
void setSelectedConnectionFromMap(QString mapName);
@@ -155,6 +155,12 @@ public:
void shouldReselectEvents();
void scaleMapView(int);
+public slots:
+ void openMapScripts() const;
+ void openScript(const QString &scriptLabel) const;
+ void openProjectInTextEditor() const;
+ void maskNonVisibleConnectionTiles();
+
private:
void setConnectionItemsVisible(bool);
void setBorderItemsVisible(bool, qreal = 1);
@@ -182,6 +188,10 @@ private:
QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
QString getMetatileDisplayMessage(uint16_t metatileId);
bool eventLimitReached(Map *, QString);
+ void openInTextEditor(const QString &path, int lineNum = 0) const;
+ bool startDetachedProcess(const QString &command,
+ const QString &workingDirectory = QString(),
+ qint64 *pid = nullptr) const;
private slots:
void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);
@@ -205,7 +215,6 @@ private slots:
void onHoveredMapMovementPermissionCleared();
void onSelectedMetatilesChanged();
void onWheelZoom(int);
- void onMapRulerLengthChanged();
signals:
void objectsChanged();
@@ -214,6 +223,7 @@ signals:
void wildMonDataChanged();
void warpEventDoubleClicked(QString mapName, QString warpNum);
void currentMetatilesSelectionChanged();
+ void mapRulerStatusChanged(const QString &);
};
#endif // EDITOR_H
diff --git a/include/mainwindow.h b/include/mainwindow.h
index bee4222d..d1631b1b 100644
--- a/include/mainwindow.h
+++ b/include/mainwindow.h
@@ -22,6 +22,8 @@
#include "filterchildrenproxymodel.h"
#include "newmappopup.h"
#include "newtilesetdialog.h"
+#include "shortcutseditor.h"
+#include "preferenceeditor.h"
namespace Ui {
class MainWindow;
@@ -119,8 +121,6 @@ private slots:
void duplicate();
- void openInTextEditor();
-
void onLoadMapRequested(QString, QString);
void onMapChanged(Map *map);
void onMapNeedsRedrawing();
@@ -129,6 +129,8 @@ private slots:
void openNewMapPopupWindow(int, QVariant);
void onNewMapCreated();
void onMapCacheCleared();
+ void onMapRulerStatusChanged(const QString &);
+ void applyUserShortcuts();
void on_action_NewMap_triggered();
void on_actionNew_Tileset_triggered();
@@ -148,6 +150,7 @@ private slots:
void on_actionUse_Encounter_Json_triggered(bool checked);
void on_actionMonitor_Project_Files_triggered(bool checked);
void on_actionUse_Poryscript_triggered(bool checked);
+ void on_actionEdit_Shortcuts_triggered();
void on_mainTabBar_tabBarClicked(int index);
@@ -164,7 +167,6 @@ private slots:
void on_actionMap_Shift_triggered();
void on_toolButton_deleteObject_clicked();
- void on_toolButton_Open_Scripts_clicked();
void addNewEvent(QString);
void updateSelectedObjects();
@@ -208,6 +210,7 @@ private slots:
void on_lineEdit_filterBox_textChanged(const QString &arg1);
+ void moveEvent(QMoveEvent *event);
void closeEvent(QCloseEvent *);
void eventTabChanged(int index);
@@ -218,23 +221,28 @@ private slots:
void on_toolButton_ExpandAll_clicked();
void on_toolButton_CollapseAll_clicked();
void on_actionAbout_Porymap_triggered();
- void on_actionThemes_triggered();
void on_pushButton_AddCustomHeaderField_clicked();
void on_pushButton_DeleteCustomHeaderField_clicked();
void on_tableWidget_CustomHeaderFields_cellChanged(int row, int column);
void on_horizontalSlider_MetatileZoom_valueChanged(int value);
void on_pushButton_NewWildMonGroup_clicked();
+ void on_pushButton_DeleteWildMonGroup_clicked();
void on_pushButton_ConfigureEncountersJSON_clicked();
void on_actionRegion_Map_Editor_triggered();
+ void on_actionEdit_Preferences_triggered();
+ void togglePreferenceSpecificUi();
private:
Ui::MainWindow *ui;
+ QLabel *label_MapRulerStatus;
TilesetEditor *tilesetEditor = nullptr;
RegionMapEditor *regionMapEditor = nullptr;
+ ShortcutsEditor *shortcutsEditor = nullptr;
MapImageExporter *mapImageExporter = nullptr;
FilterChildrenProxyModel *mapListProxyModel;
NewMapPopup *newmapprompt = nullptr;
+ PreferenceEditor *preferenceEditor = nullptr;
QStandardItemModel *mapListModel;
QList *mapGroupItemsList;
QMap mapListIndexes;
@@ -260,6 +268,7 @@ private:
DraggablePixmapItem *selectedHealspot;
QList registeredActions;
+ QVector openScriptButtons;
bool isProgrammaticEventTabChange;
bool projectHasUnsavedChanges;
@@ -291,11 +300,12 @@ private:
void initWindow();
void initCustomUI();
- void initExtraShortcuts();
void initExtraSignals();
void initEditor();
void initMiscHeapObjects();
void initMapSortOrder();
+ void initShortcuts();
+ void initExtraShortcuts();
void setProjectSpecificUIVisibility();
void loadUserSettings();
void applyMapListFilter(QString filterText);
@@ -307,9 +317,16 @@ private:
void closeSupplementaryWindows();
void setWindowDisabled(bool);
+ void initTilesetEditor();
+ bool initRegionMapEditor();
+ void initShortcutsEditor();
+ void connectSubEditorsToShortcutsEditor();
+
bool isProjectOpen();
void showExportMapImageWindow(bool stitchMode);
void redrawMetatileSelection();
+
+ QObjectList shortcutableObjects() const;
};
enum MapListUserRoles {
diff --git a/include/project.h b/include/project.h
index fda3ea8f..085cd6d1 100644
--- a/include/project.h
+++ b/include/project.h
@@ -173,8 +173,10 @@ public:
QString fixPalettePath(QString path);
QString fixGraphicPath(QString path);
- QString getScriptFileExtension(bool usePoryScript);
- QString getScriptDefaultString(bool usePoryScript, QString mapName);
+ QString getScriptFileExtension(bool usePoryScript) const;
+ QString getScriptDefaultString(bool usePoryScript, QString mapName) const;
+ QString getMapScriptsFilePath(const QString &mapName) const;
+ QStringList getEventScriptsFilePaths() const;
bool loadMapBorder(Map *map);
diff --git a/include/ui/graphicsview.h b/include/ui/graphicsview.h
index 85fb7a73..960f7dc4 100644
--- a/include/ui/graphicsview.h
+++ b/include/ui/graphicsview.h
@@ -22,6 +22,7 @@ protected:
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void drawForeground(QPainter *painter, const QRectF &rect);
+ void moveEvent(QMoveEvent *event);
};
//Q_DECLARE_METATYPE(GraphicsView)
diff --git a/include/ui/mapruler.h b/include/ui/mapruler.h
index 0ee6e698..151492e3 100644
--- a/include/ui/mapruler.h
+++ b/include/ui/mapruler.h
@@ -2,8 +2,7 @@
#define MAPRULER_H
#include
-#include
-#include
+#include
class MapRuler : public QGraphicsObject, private QLine
@@ -11,20 +10,14 @@ class MapRuler : public QGraphicsObject, private QLine
Q_OBJECT
public:
- MapRuler(QColor innerColor = Qt::yellow, QColor borderColor = Qt::black) :
- innerColor(innerColor),
- borderColor(borderColor),
- mapSize(QSize())
- {
- init();
- }
- void init();
+ // thickness is given in scene pixels
+ MapRuler(int thickness, QColor innerColor = Qt::yellow, QColor borderColor = Qt::black);
+
QRectF boundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override;
bool eventFilter(QObject *, QEvent *event) override;
- void setEnabled(bool enabled);
bool isAnchored() const { return anchored; }
bool isLocked() const { return locked; }
@@ -45,37 +38,33 @@ public:
// Ruler height in metatiles
int height() const { return qAbs(deltaY()); }
- QString statusMessage;
-
public slots:
void mouseEvent(QGraphicsSceneMouseEvent *event);
void setMapDimensions(const QSize &size);
+signals:
+ void statusChanged(const QString &statusMessage);
+
private:
- QColor innerColor;
- QColor borderColor;
+ const int thickness;
+ const qreal half_thickness;
+ const QColor innerColor;
+ const QColor borderColor;
QSize mapSize;
- QRect xRuler;
- QRect yRuler;
+ QRectF xRuler;
+ QRectF yRuler;
QLineF cornerTick;
bool anchored;
bool locked;
- static int thickness;
-
+ void reset();
+ void setAnchor(const QPointF &scenePos);
+ void setEndPos(const QPointF &scenePos);
QPoint snapToWithinBounds(QPoint pos) const;
- void setAnchor(const QPointF &scenePos, const QPoint &screenPos);
- void endAnchor();
- void setEndPos(const QPointF &scenePos, const QPoint &screenPos);
- void showDimensions(const QPoint &screenPos) const;
- void hideDimensions() const;
void updateGeometry();
+ void updateStatus(Qt::Corner corner);
int pixWidth() const { return width() * 16; }
int pixHeight() const { return height() * 16; }
-
-signals:
- void lengthChanged();
- void deactivated(const QPoint &endPos);
};
#endif // MAPRULER_H
diff --git a/include/ui/multikeyedit.h b/include/ui/multikeyedit.h
new file mode 100644
index 00000000..27231330
--- /dev/null
+++ b/include/ui/multikeyedit.h
@@ -0,0 +1,52 @@
+#ifndef MULTIKEYEDIT_H
+#define MULTIKEYEDIT_H
+
+#include
+#include
+
+class QLineEdit;
+
+
+// A collection of QKeySequenceEdit's laid out horizontally.
+class MultiKeyEdit : public QWidget
+{
+ Q_OBJECT
+
+public:
+ MultiKeyEdit(QWidget *parent = nullptr, int fieldCount = 2);
+
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
+ int fieldCount() const;
+ void setFieldCount(int count);
+ QList keySequences() const;
+ bool removeOne(const QKeySequence &keySequence);
+ bool contains(const QKeySequence &keySequence) const;
+ void setContextMenuPolicy(Qt::ContextMenuPolicy policy);
+ bool isClearButtonEnabled() const;
+ void setClearButtonEnabled(bool enable);
+
+public slots:
+ void clear();
+ void setKeySequences(const QList &keySequences);
+ void addKeySequence(const QKeySequence &keySequence);
+
+signals:
+ void keySequenceChanged(const QKeySequence &keySequence);
+ void editingFinished();
+ void customContextMenuRequested(const QPoint &pos);
+
+private:
+ QVector keySequenceEdit_vec;
+ QList keySequence_list; // Used to track changes
+
+ void addNewKeySequenceEdit();
+ void alignKeySequencesLeft();
+ void setFocusToLastNonEmptyKeySequenceEdit();
+
+private slots:
+ void onEditingFinished();
+ void showDefaultContextMenu(QLineEdit *lineEdit, const QPoint &pos);
+};
+
+#endif // MULTIKEYEDIT_H
diff --git a/include/ui/preferenceeditor.h b/include/ui/preferenceeditor.h
new file mode 100644
index 00000000..c16716d9
--- /dev/null
+++ b/include/ui/preferenceeditor.h
@@ -0,0 +1,37 @@
+#ifndef PREFERENCES_H
+#define PREFERENCES_H
+
+#include
+
+class NoScrollComboBox;
+class QAbstractButton;
+
+
+namespace Ui {
+class PreferenceEditor;
+}
+
+class PreferenceEditor : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit PreferenceEditor(QWidget *parent = nullptr);
+ ~PreferenceEditor();
+
+signals:
+ void preferencesSaved();
+ void themeChanged(const QString &theme);
+
+private:
+ Ui::PreferenceEditor *ui;
+ NoScrollComboBox *themeSelector;
+
+ void populateFields();
+ void saveFields();
+
+private slots:
+ void dialogButtonClicked(QAbstractButton *button);
+};
+
+#endif // PREFERENCES_H
diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h
index 484103f3..cba14251 100644
--- a/include/ui/regionmapeditor.h
+++ b/include/ui/regionmapeditor.h
@@ -47,6 +47,11 @@ public:
void resize(int width, int height);
+ QObjectList shortcutableObjects() const;
+
+public slots:
+ void applyUserShortcuts();
+
private:
Ui::RegionMapEditor *ui;
Project *project;
@@ -81,6 +86,7 @@ private:
RegionMapPixmapItem *region_map_item = nullptr;
CityMapPixmapItem *city_map_item = nullptr;
+ void initShortcuts();
void displayRegionMap();
void displayRegionMapImage();
void displayRegionMapLayout();
diff --git a/include/ui/shortcut.h b/include/ui/shortcut.h
new file mode 100644
index 00000000..58980a3b
--- /dev/null
+++ b/include/ui/shortcut.h
@@ -0,0 +1,71 @@
+#ifndef SHORTCUT_H
+#define SHORTCUT_H
+
+#include
+#include
+#include
+
+
+// Alternative to QShortcut that adds support for multiple key sequences.
+// Use this to allow the shortcut to be editable in ShortcutsEditor.
+class Shortcut : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QShortcut)
+ Q_PROPERTY(QKeySequence key READ key WRITE setKey)
+ Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat)
+ Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext)
+
+public:
+ explicit Shortcut(QWidget *parent);
+ Shortcut(const QKeySequence &key, QWidget *parent,
+ const char *member = nullptr, const char *ambiguousMember = nullptr,
+ Qt::ShortcutContext shortcutContext = Qt::WindowShortcut);
+ Shortcut(const QList &keys, QWidget *parent,
+ const char *member = nullptr, const char *ambiguousMember = nullptr,
+ Qt::ShortcutContext shortcutContext = Qt::WindowShortcut);
+ ~Shortcut();
+
+ void addKey(const QKeySequence &key);
+ void setKey(const QKeySequence &key);
+ QKeySequence key() const;
+
+ void addKeys(const QList &keys);
+ void setKeys(const QList &keys);
+ QList keys() const;
+
+ void setEnabled(bool enable);
+ bool isEnabled() const;
+
+ void setContext(Qt::ShortcutContext context);
+ Qt::ShortcutContext context() const;
+
+ void setWhatsThis(const QString &text);
+ QString whatsThis() const;
+
+ void setAutoRepeat(bool on);
+ bool autoRepeat() const;
+
+ int id() const;
+ QList ids() const;
+
+ inline QWidget *parentWidget() const
+ { return static_cast(QObject::parent()); }
+
+signals:
+ void activated();
+ void activatedAmbiguously();
+
+protected:
+ bool event(QEvent *e) override;
+
+private:
+ const char *sc_member;
+ const char *sc_ambiguousmember;
+ Qt::ShortcutContext sc_context;
+ QVector sc_vec;
+};
+
+#endif // SHORTCUT_H
diff --git a/include/ui/shortcutseditor.h b/include/ui/shortcutseditor.h
new file mode 100644
index 00000000..31b0279e
--- /dev/null
+++ b/include/ui/shortcutseditor.h
@@ -0,0 +1,60 @@
+#ifndef SHORTCUTSEDITOR_H
+#define SHORTCUTSEDITOR_H
+
+#include "shortcut.h"
+
+#include
+#include
+#include
+#include
+#include
+
+class QFormLayout;
+class MultiKeyEdit;
+class QAbstractButton;
+
+
+namespace Ui {
+class ShortcutsEditor;
+}
+
+class ShortcutsEditor : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit ShortcutsEditor(QWidget *parent = nullptr);
+ explicit ShortcutsEditor(const QObjectList &shortcutableObjects, QWidget *parent = nullptr);
+ ~ShortcutsEditor();
+
+ void setShortcutableObjects(const QObjectList &shortcutableObjects);
+
+signals:
+ void shortcutsSaved();
+
+private:
+ Ui::ShortcutsEditor *ui;
+ QWidget *main_container;
+ QMultiMap labels_objects;
+ QHash contexts_layouts;
+ QHash multiKeyEdits_objects;
+
+ void parseObjectList(const QObjectList &objectList);
+ QString getLabel(const QObject *object) const;
+ bool stringPropertyIsNotEmpty(const QObject *object, const char *name) const;
+ void populateMainContainer();
+ QString getShortcutContext(const QObject *object) const;
+ void addNewContextGroup(const QString &shortcutContext);
+ void addNewMultiKeyEdit(const QObject *object, const QString &shortcutContext);
+ QList siblings(MultiKeyEdit *multiKeyEdit) const;
+ void promptUserOnDuplicateFound(MultiKeyEdit *current, MultiKeyEdit *sender);
+ void removeKeySequence(const QKeySequence &keySequence, MultiKeyEdit *multiKeyEdit);
+ void saveShortcuts();
+ void resetShortcuts();
+
+private slots:
+ void checkForDuplicates(const QKeySequence &keySequence);
+ void dialogButtonClicked(QAbstractButton *button);
+};
+
+#endif // SHORTCUTSEDITOR_H
diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h
index 283f9cde..1af92f35 100644
--- a/include/ui/tileseteditor.h
+++ b/include/ui/tileseteditor.h
@@ -42,6 +42,11 @@ public:
void updateTilesets(QString primaryTilsetLabel, QString secondaryTilesetLabel);
bool selectMetatile(uint16_t metatileId);
+ QObjectList shortcutableObjects() const;
+
+public slots:
+ void applyUserShortcuts();
+
private slots:
void onHoveredMetatileChanged(uint16_t);
void onHoveredMetatileCleared();
@@ -102,6 +107,8 @@ private:
void initTileSelector();
void initSelectedTileItem();
void initMetatileLayersItem();
+ void initShortcuts();
+ void initExtraShortcuts();
void restoreWindowState();
void initMetatileHistory();
void setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel);
@@ -112,6 +119,7 @@ private:
void refresh();
void saveMetatileLabel();
void closeEvent(QCloseEvent*);
+
Ui::TilesetEditor *ui;
History metatileHistory;
TilesetEditorMetatileSelector *metatileSelector = nullptr;
diff --git a/porymap.pro b/porymap.pro
index 0da4a14b..a741f69a 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -71,6 +71,10 @@ SOURCES += src/core/block.cpp \
src/ui/newtilesetdialog.cpp \
src/ui/flowlayout.cpp \
src/ui/mapruler.cpp \
+ src/ui/shortcut.cpp \
+ src/ui/shortcutseditor.cpp \
+ src/ui/multikeyedit.cpp \
+ src/ui/preferenceeditor.cpp \
src/config.cpp \
src/editor.cpp \
src/main.cpp \
@@ -140,6 +144,10 @@ HEADERS += include/core/block.h \
include/ui/overlay.h \
include/ui/flowlayout.h \
include/ui/mapruler.h \
+ include/ui/shortcut.h \
+ include/ui/shortcutseditor.h \
+ include/ui/multikeyedit.h \
+ include/ui/preferenceeditor.h \
include/config.h \
include/editor.h \
include/mainwindow.h \
@@ -156,7 +164,9 @@ FORMS += forms/mainwindow.ui \
forms/newmappopup.ui \
forms/aboutporymap.ui \
forms/newtilesetdialog.ui \
- forms/mapimageexporter.ui
+ forms/mapimageexporter.ui \
+ forms/shortcutseditor.ui \
+ forms/preferenceeditor.ui
RESOURCES += \
resources/images.qrc \
diff --git a/src/config.cpp b/src/config.cpp
index 3c9fe33d..5ef3723f 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -1,5 +1,6 @@
#include "config.h"
#include "log.h"
+#include "shortcut.h"
#include
#include
#include
@@ -11,6 +12,8 @@
#include
#include
#include
+#include
+#include
KeyValueConfigBase::~KeyValueConfigBase() {
@@ -36,7 +39,7 @@ void KeyValueConfigBase::load() {
QTextStream in(&file);
in.setCodec("UTF-8");
QList configLines;
- QRegularExpression re("^(?.+)=(?.*)$");
+ QRegularExpression re("^(?[^=]+)=(?.*)$");
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
int commentIndex = line.indexOf("#");
@@ -187,6 +190,10 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
}
} else if (key == "theme") {
this->theme = value;
+ } else if (key == "text_editor_open_directory") {
+ this->textEditorOpenFolder = value;
+ } else if (key == "text_editor_goto_line") {
+ this->textEditorGotoLine = value;
} else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
}
@@ -216,6 +223,8 @@ QMap PorymapConfig::getKeyValueMap() {
map.insert("region_map_dimensions", QString("%1x%2").arg(this->regionMapDimensions.width())
.arg(this->regionMapDimensions.height()));
map.insert("theme", this->theme);
+ map.insert("text_editor_open_directory", this->textEditorOpenFolder);
+ map.insert("text_editor_goto_line", this->textEditorGotoLine);
return map;
}
@@ -316,6 +325,16 @@ void PorymapConfig::setTheme(QString theme) {
this->theme = theme;
}
+void PorymapConfig::setTextEditorOpenFolder(const QString &command) {
+ this->textEditorOpenFolder = command;
+ this->save();
+}
+
+void PorymapConfig::setTextEditorGotoLine(const QString &command) {
+ this->textEditorGotoLine = command;
+ this->save();
+}
+
QString PorymapConfig::getRecentProject() {
return this->recentProject;
}
@@ -398,6 +417,14 @@ QString PorymapConfig::getTheme() {
return this->theme;
}
+QString PorymapConfig::getTextEditorOpenFolder() {
+ return this->textEditorOpenFolder;
+}
+
+QString PorymapConfig::getTextEditorGotoLine() {
+ return this->textEditorGotoLine;
+}
+
const QMap baseGameVersionMap = {
{BaseGameVersion::pokeruby, "pokeruby"},
{BaseGameVersion::pokefirered, "pokefirered"},
@@ -414,7 +441,7 @@ ProjectConfig projectConfig;
QString ProjectConfig::getConfigFilepath() {
// porymap config file is in the same directory as porymap itself.
- return QDir(this->projectDir).filePath("porymap.project.cfg");;
+ return QDir(this->projectDir).filePath("porymap.project.cfg");
}
void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
@@ -703,3 +730,128 @@ void ProjectConfig::setCustomScripts(QList scripts) {
QList ProjectConfig::getCustomScripts() {
return this->customScripts;
}
+
+ShortcutsConfig shortcutsConfig;
+
+QString ShortcutsConfig::getConfigFilepath() {
+ QString settingsPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ QDir dir(settingsPath);
+ if (!dir.exists())
+ dir.mkpath(settingsPath);
+
+ QString configPath = dir.absoluteFilePath("porymap.shortcuts.cfg");
+
+ return configPath;
+}
+
+void ShortcutsConfig::parseConfigKeyValue(QString key, QString value) {
+ QStringList keySequences = value.split(' ');
+ for (auto keySequence : keySequences)
+ user_shortcuts.insert(key, keySequence);
+}
+
+QMap ShortcutsConfig::getKeyValueMap() {
+ QMap map;
+ for (auto cfg_key : user_shortcuts.uniqueKeys()) {
+ auto keySequences = user_shortcuts.values(cfg_key);
+ QStringList keySequenceStrings;
+ for (auto keySequence : keySequences)
+ keySequenceStrings.append(keySequence.toString());
+ map.insert(cfg_key, keySequenceStrings.join(' '));
+ }
+ return map;
+}
+
+void ShortcutsConfig::setDefaultShortcuts(const QObjectList &objects) {
+ storeShortcutsFromList(StoreType::Default, objects);
+ save();
+}
+
+QList ShortcutsConfig::defaultShortcuts(const QObject *object) const {
+ return default_shortcuts.values(cfgKey(object));
+}
+
+void ShortcutsConfig::setUserShortcuts(const QObjectList &objects) {
+ storeShortcutsFromList(StoreType::User, objects);
+ save();
+}
+
+void ShortcutsConfig::setUserShortcuts(const QMultiMap &objects_keySequences) {
+ for (auto *object : objects_keySequences.uniqueKeys())
+ if (!object->objectName().isEmpty() && !object->objectName().startsWith("_q_"))
+ storeShortcuts(StoreType::User, cfgKey(object), objects_keySequences.values(object));
+ save();
+}
+
+QList ShortcutsConfig::userShortcuts(const QObject *object) const {
+ return user_shortcuts.values(cfgKey(object));
+}
+
+void ShortcutsConfig::storeShortcutsFromList(StoreType storeType, const QObjectList &objects) {
+ for (const auto *object : objects)
+ if (!object->objectName().isEmpty() && !object->objectName().startsWith("_q_"))
+ storeShortcuts(storeType, cfgKey(object), currentShortcuts(object));
+}
+
+void ShortcutsConfig::storeShortcuts(
+ StoreType storeType,
+ const QString &cfgKey,
+ const QList &keySequences)
+{
+ bool storeUser = (storeType == User) || !user_shortcuts.contains(cfgKey);
+
+ if (storeType == Default)
+ default_shortcuts.remove(cfgKey);
+ if (storeUser)
+ user_shortcuts.remove(cfgKey);
+
+ if (keySequences.isEmpty()) {
+ if (storeType == Default)
+ default_shortcuts.insert(cfgKey, QKeySequence());
+ if (storeUser)
+ user_shortcuts.insert(cfgKey, QKeySequence());
+ } else {
+ for (auto keySequence : keySequences) {
+ if (storeType == Default)
+ default_shortcuts.insert(cfgKey, keySequence);
+ if (storeUser)
+ user_shortcuts.insert(cfgKey, keySequence);
+ }
+ }
+}
+
+/* Creates a config key from the object's name prepended with the parent
+ * window's object name, and converts camelCase to snake_case. */
+QString ShortcutsConfig::cfgKey(const QObject *object) const {
+ auto cfg_key = QString();
+ auto *parentWidget = static_cast(object->parent());
+ if (parentWidget)
+ cfg_key = parentWidget->window()->objectName() + '_';
+ cfg_key += object->objectName();
+
+ QRegularExpression re("[A-Z]");
+ int i = cfg_key.indexOf(re, 1);
+ while (i != -1) {
+ if (cfg_key.at(i - 1) != '_')
+ cfg_key.insert(i++, '_');
+ i = cfg_key.indexOf(re, i + 1);
+ }
+ return cfg_key.toLower();
+}
+
+QList ShortcutsConfig::currentShortcuts(const QObject *object) const {
+ if (object->inherits("QAction")) {
+ const auto *action = qobject_cast(object);
+ return action->shortcuts();
+ } else if (object->inherits("Shortcut")) {
+ const auto *shortcut = qobject_cast(object);
+ return shortcut->keys();
+ } else if (object->inherits("QShortcut")) {
+ const auto *qshortcut = qobject_cast(object);
+ return { qshortcut->key() };
+ } else if (object->property("shortcut").isValid()) {
+ return { object->property("shortcut").value() };
+ } else {
+ return { };
+ }
+}
diff --git a/src/core/map.cpp b/src/core/map.cpp
index 02cd15f8..14da4650 100644
--- a/src/core/map.cpp
+++ b/src/core/map.cpp
@@ -450,12 +450,32 @@ void Map::magicFillCollisionElevation(int initialX, int initialY, uint16_t colli
}
}
-QList Map::getAllEvents() {
- QList all;
- for (QList list : events.values()) {
- all += list;
+QList Map::getAllEvents() const {
+ QList all_events;
+ for (const auto &event_list : events) {
+ all_events << event_list;
}
- return all;
+ return all_events;
+}
+
+QStringList Map::eventScriptLabels(const QString &event_group_type) const {
+ QStringList scriptLabels;
+ if (event_group_type.isEmpty()) {
+ for (const auto *event : getAllEvents())
+ scriptLabels << event->get("script_label");
+ } else {
+ for (const auto *event : events.value(event_group_type))
+ scriptLabels << event->get("script_label");
+ }
+
+ scriptLabels.removeDuplicates();
+ scriptLabels.removeAll(QString());
+ if (scriptLabels.contains("0x0"))
+ scriptLabels.move(scriptLabels.indexOf("0x0"), scriptLabels.count() - 1);
+ if (scriptLabels.contains("NULL"))
+ scriptLabels.move(scriptLabels.indexOf("NULL"), scriptLabels.count() - 1);
+
+ return scriptLabels;
}
void Map::removeEvent(Event *event) {
diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp
index 80a4cf6c..74330738 100644
--- a/src/core/parseutil.cpp
+++ b/src/core/parseutil.cpp
@@ -337,7 +337,7 @@ QMap ParseUtil::readNamedIndexCArray(QString filename, QString
QRegularExpression re_text(QString(R"(\b%1\b\s*(\[?[^\]]*\])?\s*=\s*\{([^\}]*)\})").arg(label));
QString body = re_text.match(text).captured(2).replace(QRegularExpression("\\s*"), "");
- QRegularExpression re("\\[(?[A-Za-z1-9_]*)\\]=(?&?[A-Za-z1-9_]*)");
+ QRegularExpression re("\\[(?[A-Za-z0-9_]*)\\]=(?&?[A-Za-z0-9_]*)");
QRegularExpressionMatchIterator iter = re.globalMatch(body);
while (iter.hasNext()) {
@@ -420,3 +420,118 @@ bool ParseUtil::ensureFieldsExist(QJsonObject obj, QList fields) {
}
return true;
}
+
+int ParseUtil::getScriptLineNumber(const QString &filePath, const QString &scriptLabel) {
+ if (scriptLabel.isEmpty())
+ return 0;
+
+ if (filePath.endsWith(".inc") || filePath.endsWith(".s"))
+ return getRawScriptLineNumber(readTextFile(filePath), scriptLabel);
+ else if (filePath.endsWith(".pory"))
+ return getPoryScriptLineNumber(readTextFile(filePath), scriptLabel);
+
+ return 0;
+}
+
+int ParseUtil::getRawScriptLineNumber(QString text, const QString &scriptLabel) {
+ removeStringLiterals(text);
+ removeLineComments(text, "@");
+
+ static const QRegularExpression re_incScriptLabel("\\b(?