mirror of
https://github.com/huderlem/porymap.git
synced 2026-09-26 09:56:32 -05:00
Merge pull request #689 from GriffinRichards/event-speed-improvements
Event speed improvements
This commit is contained in:
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>522</width>
|
||||
<height>493</height>
|
||||
<width>530</width>
|
||||
<height>432</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -19,244 +19,302 @@
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Miscellaneous</string>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_MonitorProjectFiles">
|
||||
<property name="toolTip">
|
||||
<string>If checked, a prompt to reload your project will appear if relevant project files are edited</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Monitor project files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_OpenRecentProject">
|
||||
<property name="toolTip">
|
||||
<string>If checked, Porymap will automatically open your most recently opened project on startup</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open recent project on launch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_CheckForUpdates">
|
||||
<property name="toolTip">
|
||||
<string>If checked, Porymap will automatically alert you on startup if a new release is available</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Automatically check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_DisableEventWarning">
|
||||
<property name="toolTip">
|
||||
<string>If checked, no warning will be shown when deleting an event that has an associated #define that may also be deleted.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable warning when deleting events with IDs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_EventSelectionMode">
|
||||
<property name="title">
|
||||
<string>Event Selection Mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_EventSelectionMode">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_OnSprite">
|
||||
<property name="toolTip">
|
||||
<string>If enabled, an event can be selected by clicking directly on the opaque pixels of its sprite. This may be preferable when events are overlapping.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select by clicking on sprite</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_WithinRect">
|
||||
<property name="toolTip">
|
||||
<string>If enabled, an event can be selected by clicking anywhere within its sprite dimensions. This may be preferable for events with small or mostly transparent sprites.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select by clicking within bounding rectangle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Themes">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Application Theme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_TextEditor">
|
||||
<property name="title">
|
||||
<string>Preferred Text Editor</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<widget class="QScrollArea" name="scrollArea_TextEditor">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_TextEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>492</width>
|
||||
<height>327</height>
|
||||
</rect>
|
||||
<widget class="QWidget" name="tab_General">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_MonitorProjectFiles">
|
||||
<property name="toolTip">
|
||||
<string>If checked, a prompt to reload your project will appear if relevant project files are edited</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_TextEditorGotoLineHelp">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_TextEditorGotoLine">
|
||||
<property name="text">
|
||||
<string>Goto Line Command</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_TextEditorOpenFolder">
|
||||
<property name="text">
|
||||
<string>Monitor project files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_OpenRecentProject">
|
||||
<property name="toolTip">
|
||||
<string>If checked, Porymap will automatically open your most recently opened project on startup</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open recent project on launch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_CheckForUpdates">
|
||||
<property name="toolTip">
|
||||
<string>If checked, Porymap will automatically alert you on startup if a new release is available</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Automatically check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_Themes">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Application Theme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_Events">
|
||||
<attribute name="title">
|
||||
<string>Events</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_DisableEventWarning">
|
||||
<property name="toolTip">
|
||||
<string>If checked, no warning will be shown when deleting an event that has an associated #define that may also be deleted.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable warning when deleting events with IDs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_AutocompleteAllScripts">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. Enabling this setting will make Porymap's startup slower.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Autocomplete Script labels using all possible scripts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_EventSelectionMode">
|
||||
<property name="title">
|
||||
<string>Selection Mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_EventSelectionMode">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_OnSprite">
|
||||
<property name="toolTip">
|
||||
<string>The shell command for your preferred text editor (possibly an absolute path if the program doesn't exist in your PATH).</string>
|
||||
<string>If enabled, an event can be selected by clicking directly on the opaque pixels of its sprite. This may be preferable when events are overlapping.</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>e.g. code %D</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_TextEditorOpenFolderHelp">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
<string>Select by clicking on sprite</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_TextEditorOpenFolder">
|
||||
<property name="text">
|
||||
<string>Open Directory Command</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_TextEditorGotoLine">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_WithinRect">
|
||||
<property name="toolTip">
|
||||
<string>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).</string>
|
||||
<string>If enabled, an event can be selected by clicking anywhere within its sprite dimensions. This may be preferable for events with small or mostly transparent sprites.</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>e.g. code --goto %F:%L</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
<property name="text">
|
||||
<string>Select by clicking within bounding rectangle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>15</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Text Editor</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_TextEditor">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_TextEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>476</width>
|
||||
<height>343</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_TextEditorGotoLineHelp">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_TextEditorGotoLine">
|
||||
<property name="text">
|
||||
<string>Goto Line Command</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_TextEditorOpenFolder">
|
||||
<property name="toolTip">
|
||||
<string>The shell command for your preferred text editor (possibly an absolute path if the program doesn't exist in your PATH).</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>e.g. code %D</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_TextEditorOpenFolderHelp">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_TextEditorOpenFolder">
|
||||
<property name="text">
|
||||
<string>Open Directory Command</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_TextEditorGotoLine">
|
||||
<property name="toolTip">
|
||||
<string>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).</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>e.g. code --goto %F:%L</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>15</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
<set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
this->textEditorGotoLine = "";
|
||||
this->paletteEditorBitDepth = 24;
|
||||
this->projectSettingsTab = 0;
|
||||
this->loadAllEventScripts = false;
|
||||
this->warpBehaviorWarningDisabled = false;
|
||||
this->eventDeleteWarningDisabled = false;
|
||||
this->eventOverlayEnabled = false;
|
||||
@@ -136,6 +137,7 @@ public:
|
||||
QString textEditorGotoLine;
|
||||
int paletteEditorBitDepth;
|
||||
int projectSettingsTab;
|
||||
bool loadAllEventScripts;
|
||||
bool warpBehaviorWarningDisabled;
|
||||
bool eventDeleteWarningDisabled;
|
||||
bool eventOverlayEnabled;
|
||||
|
||||
@@ -41,14 +41,6 @@ public:
|
||||
virtual void visitSign(SignEvent *) = 0;
|
||||
};
|
||||
|
||||
struct EventGraphics
|
||||
{
|
||||
QImage spritesheet;
|
||||
int spriteWidth;
|
||||
int spriteHeight;
|
||||
bool inanimate;
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// Event base class -- purely virtual
|
||||
@@ -64,11 +56,7 @@ public:
|
||||
Event& operator=(const Event &other) = delete;
|
||||
|
||||
protected:
|
||||
Event() {
|
||||
this->spriteWidth = 16;
|
||||
this->spriteHeight = 16;
|
||||
this->usingSprite = false;
|
||||
}
|
||||
Event() {}
|
||||
|
||||
// public enums & static methods
|
||||
public:
|
||||
@@ -119,8 +107,6 @@ public:
|
||||
|
||||
static Event* create(Event::Type type);
|
||||
|
||||
static QMap<Event::Group, const QPixmap*> icons;
|
||||
|
||||
// standard public methods
|
||||
public:
|
||||
|
||||
@@ -143,8 +129,8 @@ public:
|
||||
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); }
|
||||
int getPixelX() const { return (this->x * 16) - qMax(0, (pixmap.width() - 16) / 2); }
|
||||
int getPixelY() const { return (this->y * 16) - qMax(0, pixmap.height() - 16); }
|
||||
|
||||
virtual EventFrame *getEventFrame();
|
||||
virtual EventFrame *createEventFrame() = 0;
|
||||
@@ -172,14 +158,8 @@ public:
|
||||
void setPixmapItem(DraggablePixmapItem *item);
|
||||
DraggablePixmapItem *getPixmapItem() const { 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; }
|
||||
void setUsesDefaultPixmap(bool newUsesDefaultPixmap) { this->usesDefaultPixmap = newUsesDefaultPixmap; }
|
||||
bool getUsesDefaultPixmap() const { return this->usesDefaultPixmap; }
|
||||
|
||||
int getEventIndex();
|
||||
|
||||
@@ -189,8 +169,6 @@ public:
|
||||
static QString groupToString(Event::Group group);
|
||||
static QString typeToString(Event::Type type);
|
||||
static Event::Type typeFromString(QString type);
|
||||
static void clearIcons();
|
||||
static void setIcons();
|
||||
|
||||
// protected attributes
|
||||
protected:
|
||||
@@ -204,9 +182,7 @@ protected:
|
||||
int y = 0;
|
||||
int elevation = 0;
|
||||
|
||||
int spriteWidth = 16;
|
||||
int spriteHeight = 16;
|
||||
bool usingSprite = false;
|
||||
bool usesDefaultPixmap = true;
|
||||
|
||||
// Some events can have an associated #define name that should be unique to this event.
|
||||
// e.g. object events can have a 'LOCALID', or Heal Locations have a 'HEAL_LOCATION' id.
|
||||
@@ -273,10 +249,6 @@ public:
|
||||
void setFlag(QString newFlag) { this->flag = newFlag; }
|
||||
QString getFlag() const { return this->flag; }
|
||||
|
||||
public:
|
||||
void setFrameFromMovement(QString movement);
|
||||
void setPixmapFromSpritesheet(EventGraphics * gfx);
|
||||
|
||||
|
||||
protected:
|
||||
QString gfx;
|
||||
|
||||
@@ -45,7 +45,6 @@ public:
|
||||
QStringList layoutIdsMaster;
|
||||
QMap<QString, Layout*> mapLayouts;
|
||||
QMap<QString, Layout*> mapLayoutsMaster;
|
||||
QMap<QString, EventGraphics*> eventGraphicsMap;
|
||||
QMap<QString, int> gfxDefines;
|
||||
QString defaultSong;
|
||||
QStringList songNames;
|
||||
@@ -68,7 +67,6 @@ public:
|
||||
QMap<QString, uint16_t> unusedMetatileLabels;
|
||||
QMap<QString, uint32_t> metatileBehaviorMap;
|
||||
QMap<uint32_t, QString> metatileBehaviorMapInverse;
|
||||
QMap<QString, QString> facingDirections;
|
||||
ParseUtil parser;
|
||||
QFileSystemWatcher fileWatcher;
|
||||
QSet<QString> modifiedFiles;
|
||||
@@ -210,7 +208,10 @@ public:
|
||||
bool readFieldmapMasks();
|
||||
QMap<QString, QMap<QString, QString>> readObjEventGfxInfo();
|
||||
|
||||
void setEventPixmap(Event *event, bool forceLoad = false);
|
||||
QPixmap getEventPixmap(const QString &gfxName, const QString &movementName);
|
||||
QPixmap getEventPixmap(const QString &gfxName, int frame, bool hFlip);
|
||||
QPixmap getEventPixmap(Event::Group group);
|
||||
void loadEventPixmap(Event *event, bool forceLoad = false);
|
||||
|
||||
QString fixPalettePath(QString path);
|
||||
QString fixGraphicPath(QString path);
|
||||
@@ -218,6 +219,7 @@ public:
|
||||
static QString getScriptFileExtension(bool usePoryScript);
|
||||
QString getScriptDefaultString(bool usePoryScript, QString mapName) const;
|
||||
QStringList getEventScriptsFilePaths() const;
|
||||
void insertGlobalScriptLabels(QStringList &scriptLabels) const;
|
||||
|
||||
QString getDefaultPrimaryTilesetLabel() const;
|
||||
QString getDefaultSecondaryTilesetLabel() const;
|
||||
@@ -254,6 +256,18 @@ public:
|
||||
private:
|
||||
QMap<QString, QString> mapSectionDisplayNames;
|
||||
QMap<QString, qint64> modifiedFileTimestamps;
|
||||
QMap<QString, QString> facingDirections;
|
||||
|
||||
struct EventGraphics
|
||||
{
|
||||
QString filepath;
|
||||
bool loaded = false;
|
||||
QImage spritesheet;
|
||||
int spriteWidth = -1;
|
||||
int spriteHeight = -1;
|
||||
bool inanimate = false;
|
||||
};
|
||||
QMap<QString, EventGraphics*> eventGraphicsMap;
|
||||
|
||||
void updateLayout(Layout *);
|
||||
|
||||
@@ -284,6 +298,7 @@ signals:
|
||||
void mapSectionDisplayNameChanged(const QString &idName, const QString &displayName);
|
||||
void mapSectionIdNamesChanged(const QStringList &idNames);
|
||||
void mapsExcluded(const QStringList &excludedMapNames);
|
||||
void eventScriptLabelsRead();
|
||||
};
|
||||
|
||||
#endif // PROJECT_H
|
||||
|
||||
@@ -31,8 +31,6 @@ public:
|
||||
void invalidateUi();
|
||||
void invalidateValues();
|
||||
|
||||
void populateScriptDropdown(NoScrollComboBox * combo, Project * project);
|
||||
|
||||
virtual void setActive(bool active);
|
||||
|
||||
public:
|
||||
@@ -59,6 +57,8 @@ protected:
|
||||
bool initialized = false;
|
||||
bool connected = false;
|
||||
|
||||
void populateScriptDropdown(NoScrollComboBox * combo, Project * project);
|
||||
|
||||
private:
|
||||
Event *event;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
signals:
|
||||
void preferencesSaved();
|
||||
void themeChanged(const QString &theme);
|
||||
void scriptSettingsChanged(bool on);
|
||||
|
||||
private:
|
||||
Ui::PreferenceEditor *ui;
|
||||
|
||||
@@ -390,6 +390,8 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
}
|
||||
} else if (key == "project_settings_tab") {
|
||||
this->projectSettingsTab = getConfigInteger(key, value, 0);
|
||||
} else if (key == "load_all_event_scripts") {
|
||||
this->loadAllEventScripts = getConfigBool(key, value);
|
||||
} else if (key == "warp_behavior_warning_disabled") {
|
||||
this->warpBehaviorWarningDisabled = getConfigBool(key, value);
|
||||
} else if (key == "event_delete_warning_disabled") {
|
||||
@@ -481,6 +483,7 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
|
||||
map.insert("text_editor_goto_line", this->textEditorGotoLine);
|
||||
map.insert("palette_editor_bit_depth", QString::number(this->paletteEditorBitDepth));
|
||||
map.insert("project_settings_tab", QString::number(this->projectSettingsTab));
|
||||
map.insert("load_all_event_scripts", QString::number(this->loadAllEventScripts));
|
||||
map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled));
|
||||
map.insert("event_delete_warning_disabled", QString::number(this->eventDeleteWarningDisabled));
|
||||
map.insert("event_overlay_enabled", QString::number(this->eventOverlayEnabled));
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "project.h"
|
||||
#include "config.h"
|
||||
|
||||
QMap<Event::Group, const QPixmap*> Event::icons;
|
||||
|
||||
Event* Event::create(Event::Type type) {
|
||||
switch (type) {
|
||||
case Event::Type::Object: return new ObjectEvent();
|
||||
@@ -107,46 +105,11 @@ Event::Type Event::typeFromString(QString type) {
|
||||
return typeToStringMap.key(type, Event::Type::None);
|
||||
}
|
||||
|
||||
void Event::loadPixmap(Project *) {
|
||||
const QPixmap * pixmap = Event::icons.value(this->getEventGroup());
|
||||
this->pixmap = pixmap ? *pixmap : QPixmap();
|
||||
void Event::loadPixmap(Project *project) {
|
||||
this->pixmap = project->getEventPixmap(this->getEventGroup());
|
||||
this->usesDefaultPixmap = true;
|
||||
}
|
||||
|
||||
void Event::clearIcons() {
|
||||
qDeleteAll(icons);
|
||||
icons.clear();
|
||||
}
|
||||
|
||||
void Event::setIcons() {
|
||||
clearIcons();
|
||||
const int w = 16;
|
||||
const int h = 16;
|
||||
static const QPixmap defaultIcons = QPixmap(":/images/Entities_16x16.png");
|
||||
|
||||
// Custom event icons may be provided by the user.
|
||||
const int numIcons = qMin(defaultIcons.width() / w, static_cast<int>(Event::Group::None));
|
||||
for (int i = 0; i < numIcons; i++) {
|
||||
Event::Group group = static_cast<Event::Group>(i);
|
||||
QString customIconPath = projectConfig.getEventIconPath(group);
|
||||
if (customIconPath.isEmpty()) {
|
||||
// No custom icon specified, use the default icon.
|
||||
icons[group] = new QPixmap(defaultIcons.copy(i * w, 0, w, h));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to load custom icon
|
||||
QString validPath = Project::getExistingFilepath(customIconPath);
|
||||
if (!validPath.isEmpty()) customIconPath = validPath; // Otherwise allow it to fail with the original path
|
||||
const QPixmap customIcon = QPixmap(customIconPath);
|
||||
if (customIcon.isNull()) {
|
||||
// Custom icon failed to load, use the default icon.
|
||||
icons[group] = new QPixmap(defaultIcons.copy(i * w, 0, w, h));
|
||||
logWarn(QString("Failed to load custom event icon '%1', using default icon.").arg(customIconPath));
|
||||
} else {
|
||||
icons[group] = new QPixmap(customIcon.scaled(w, h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Event *ObjectEvent::duplicate() const {
|
||||
@@ -230,7 +193,6 @@ void ObjectEvent::setDefaultValues(Project *project) {
|
||||
this->setRadiusX(0);
|
||||
this->setRadiusY(0);
|
||||
this->setSightRadiusBerryTreeID("0");
|
||||
this->setFrameFromMovement(project->facingDirections.value(this->getMovement()));
|
||||
}
|
||||
|
||||
const QSet<QString> expectedObjectFields = {
|
||||
@@ -257,78 +219,11 @@ QSet<QString> ObjectEvent::getExpectedFields() {
|
||||
}
|
||||
|
||||
void ObjectEvent::loadPixmap(Project *project) {
|
||||
EventGraphics *eventGfx = project->eventGraphicsMap.value(this->gfx, nullptr);
|
||||
if (!eventGfx) {
|
||||
// Invalid gfx constant.
|
||||
// If this is a number, try to use that instead.
|
||||
bool ok;
|
||||
int altGfx = ParseUtil::gameStringToInt(this->gfx, &ok);
|
||||
if (ok && (altGfx < project->gfxDefines.count())) {
|
||||
eventGfx = project->eventGraphicsMap.value(project->gfxDefines.key(altGfx, "NULL"), nullptr);
|
||||
}
|
||||
}
|
||||
if (!eventGfx || eventGfx->spritesheet.isNull()) {
|
||||
// No sprite associated with this gfx constant.
|
||||
// Use default sprite instead.
|
||||
this->pixmap = project->getEventPixmap(this->gfx, this->movement);
|
||||
if (!this->pixmap.isNull()) {
|
||||
this->usesDefaultPixmap = false;
|
||||
} else {
|
||||
Event::loadPixmap(project);
|
||||
this->spriteWidth = 16;
|
||||
this->spriteHeight = 16;
|
||||
this->usingSprite = false;
|
||||
} else {
|
||||
this->setFrameFromMovement(project->facingDirections.value(this->movement));
|
||||
this->setPixmapFromSpritesheet(eventGfx);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectEvent::setPixmapFromSpritesheet(EventGraphics * gfx)
|
||||
{
|
||||
QImage img;
|
||||
if (gfx->inanimate) {
|
||||
img = gfx->spritesheet.copy(0, 0, gfx->spriteWidth, gfx->spriteHeight);
|
||||
} else {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
// Get frame's position in spritesheet.
|
||||
// Assume horizontal layout. If position would exceed sheet width, try vertical layout.
|
||||
if ((this->frame + 1) * gfx->spriteWidth <= gfx->spritesheet.width()) {
|
||||
x = this->frame * gfx->spriteWidth;
|
||||
} else if ((this->frame + 1) * gfx->spriteHeight <= gfx->spritesheet.height()) {
|
||||
y = this->frame * gfx->spriteHeight;
|
||||
}
|
||||
|
||||
img = gfx->spritesheet.copy(x, y, gfx->spriteWidth, gfx->spriteHeight);
|
||||
|
||||
// Right-facing sprite is just the left-facing sprite mirrored
|
||||
if (this->hFlip) {
|
||||
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 = gfx->spriteWidth;
|
||||
this->spriteHeight = gfx->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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,19 +325,7 @@ void CloneObjectEvent::loadPixmap(Project *project) {
|
||||
this->gfx = project->gfxDefines.key(0, "0");
|
||||
this->movement = project->movementTypes.value(0, "0");
|
||||
}
|
||||
|
||||
EventGraphics *eventGfx = project->eventGraphicsMap.value(gfx, nullptr);
|
||||
if (!eventGfx || eventGfx->spritesheet.isNull()) {
|
||||
// No sprite associated with this gfx constant.
|
||||
// Use default sprite instead.
|
||||
Event::loadPixmap(project);
|
||||
this->spriteWidth = 16;
|
||||
this->spriteHeight = 16;
|
||||
this->usingSprite = false;
|
||||
} else {
|
||||
this->setFrameFromMovement(project->facingDirections.value(this->movement));
|
||||
this->setPixmapFromSpritesheet(eventGfx);
|
||||
}
|
||||
ObjectEvent::loadPixmap(project);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -152,13 +152,12 @@ QStringList Map::getScriptLabels(Event::Group group) {
|
||||
scriptLabels = scriptTracker.getScripts();
|
||||
}
|
||||
|
||||
// Add scripts from map's scripts file, and empty names.
|
||||
// Add labels from the map's scripts file
|
||||
scriptLabels.append(m_scriptsFileLabels);
|
||||
scriptLabels.sort(Qt::CaseInsensitive);
|
||||
scriptLabels.prepend("0x0");
|
||||
scriptLabels.prepend("NULL");
|
||||
|
||||
scriptLabels.removeAll("");
|
||||
scriptLabels.removeAll("0");
|
||||
scriptLabels.removeAll("0x0");
|
||||
scriptLabels.removeDuplicates();
|
||||
|
||||
return scriptLabels;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "customattributesframe.h"
|
||||
#include "validator.h"
|
||||
#include "message.h"
|
||||
#include "eventframes.h"
|
||||
#include <QCheckBox>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
@@ -1675,7 +1676,7 @@ void Editor::displayMapEvents() {
|
||||
}
|
||||
|
||||
DraggablePixmapItem *Editor::addEventPixmapItem(Event *event) {
|
||||
this->project->setEventPixmap(event);
|
||||
this->project->loadEventPixmap(event);
|
||||
auto item = new DraggablePixmapItem(event, this);
|
||||
redrawEventPixmapItem(item);
|
||||
this->events_group->addToGroup(item);
|
||||
@@ -1960,13 +1961,13 @@ qreal Editor::getEventOpacity(const Event *event) const {
|
||||
// - On the Events tab, and the event has a custom sprite (1.0)
|
||||
if (this->editMode != EditMode::Events)
|
||||
return porymapConfig.eventOverlayEnabled ? 0.5 : 0.0;
|
||||
return event->getUsingSprite() ? 1.0 : 0.7;
|
||||
return event->getUsesDefaultPixmap() ? 0.7 : 1.0;
|
||||
}
|
||||
|
||||
void Editor::redrawEventPixmapItem(DraggablePixmapItem *item) {
|
||||
if (item && item->event && !item->event->getPixmap().isNull()) {
|
||||
item->setOpacity(getEventOpacity(item->event));
|
||||
project->setEventPixmap(item->event, true);
|
||||
project->loadEventPixmap(item->event, true);
|
||||
item->setPixmap(item->event->getPixmap());
|
||||
item->setShapeMode(porymapConfig.eventSelectionShapeMode);
|
||||
|
||||
|
||||
@@ -1104,7 +1104,6 @@ bool MainWindow::setProjectUI() {
|
||||
ui->newEventToolButton->newSecretBaseAction->setVisible(projectConfig.eventSecretBaseEnabled);
|
||||
ui->newEventToolButton->newCloneObjectAction->setVisible(projectConfig.eventCloneObjectEnabled);
|
||||
|
||||
Event::setIcons();
|
||||
editor->setCollisionGraphics();
|
||||
ui->spinBox_SelectedElevation->setMaximum(Block::getMaxElevation());
|
||||
ui->spinBox_SelectedCollision->setMaximum(Block::getMaxCollision());
|
||||
@@ -1162,8 +1161,6 @@ void MainWindow::clearProjectUI() {
|
||||
delete this->layoutTreeModel;
|
||||
delete this->layoutListProxyModel;
|
||||
resetMapListFilters();
|
||||
|
||||
Event::clearIcons();
|
||||
}
|
||||
|
||||
void MainWindow::scrollMapList(MapTree *list, const QString &itemName) {
|
||||
@@ -2739,12 +2736,10 @@ void MainWindow::on_actionOpen_Config_Folder_triggered() {
|
||||
void MainWindow::on_actionPreferences_triggered() {
|
||||
if (!preferenceEditor) {
|
||||
preferenceEditor = new PreferenceEditor(this);
|
||||
connect(preferenceEditor, &PreferenceEditor::themeChanged,
|
||||
this, &MainWindow::setTheme);
|
||||
connect(preferenceEditor, &PreferenceEditor::themeChanged,
|
||||
editor, &Editor::maskNonVisibleConnectionTiles);
|
||||
connect(preferenceEditor, &PreferenceEditor::preferencesSaved,
|
||||
this, &MainWindow::togglePreferenceSpecificUi);
|
||||
connect(preferenceEditor, &PreferenceEditor::themeChanged, this, &MainWindow::setTheme);
|
||||
connect(preferenceEditor, &PreferenceEditor::themeChanged, editor, &Editor::maskNonVisibleConnectionTiles);
|
||||
connect(preferenceEditor, &PreferenceEditor::preferencesSaved, this, &MainWindow::togglePreferenceSpecificUi);
|
||||
connect(preferenceEditor, &PreferenceEditor::scriptSettingsChanged, editor->project, &Project::readEventScriptLabels);
|
||||
}
|
||||
|
||||
openSubWindow(preferenceEditor);
|
||||
@@ -2759,8 +2754,9 @@ void MainWindow::togglePreferenceSpecificUi() {
|
||||
if (this->updatePromoter)
|
||||
this->updatePromoter->updatePreferences();
|
||||
|
||||
// Redraw all events to use updated porymapConfig.eventSelectionShapeMode
|
||||
this->editor->redrawAllEvents();
|
||||
// Changes to porymapConfig.loadAllEventScripts or porymapConfig.eventSelectionShapeMode
|
||||
// require us to repopulate the EventFrames and redraw event pixmaps, respectively.
|
||||
this->editor->updateEvents();
|
||||
}
|
||||
|
||||
void MainWindow::openProjectSettingsEditor(int tab) {
|
||||
|
||||
254
src/project.cpp
254
src/project.cpp
@@ -44,6 +44,7 @@ Project::~Project()
|
||||
clearMapLayouts();
|
||||
clearEventGraphics();
|
||||
clearHealLocations();
|
||||
QPixmapCache::clear();
|
||||
}
|
||||
|
||||
void Project::set_root(QString dir) {
|
||||
@@ -2637,16 +2638,27 @@ bool Project::readMiscellaneousConstants() {
|
||||
}
|
||||
|
||||
bool Project::readEventScriptLabels() {
|
||||
globalScriptLabels.clear();
|
||||
for (const auto &filePath : getEventScriptsFilePaths())
|
||||
globalScriptLabels << ParseUtil::getGlobalScriptLabels(filePath);
|
||||
this->globalScriptLabels.clear();
|
||||
|
||||
globalScriptLabels.sort(Qt::CaseInsensitive);
|
||||
globalScriptLabels.removeDuplicates();
|
||||
if (porymapConfig.loadAllEventScripts) {
|
||||
for (const auto &filePath : getEventScriptsFilePaths())
|
||||
this->globalScriptLabels << ParseUtil::getGlobalScriptLabels(filePath);
|
||||
|
||||
this->globalScriptLabels.sort(Qt::CaseInsensitive);
|
||||
this->globalScriptLabels.removeDuplicates();
|
||||
}
|
||||
emit eventScriptLabelsRead();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Project::insertGlobalScriptLabels(QStringList &scriptLabels) const {
|
||||
if (this->globalScriptLabels.isEmpty())
|
||||
return;
|
||||
scriptLabels.append(this->globalScriptLabels);
|
||||
scriptLabels.sort();
|
||||
scriptLabels.removeDuplicates();
|
||||
}
|
||||
|
||||
QString Project::fixPalettePath(QString path) {
|
||||
static const QRegularExpression re_gbapal("\\.gbapal$");
|
||||
path = path.replace(re_gbapal, ".pal");
|
||||
@@ -2702,92 +2714,220 @@ QStringList Project::getEventScriptsFilePaths() const {
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
void Project::setEventPixmap(Event *event, bool forceLoad) {
|
||||
void Project::loadEventPixmap(Event *event, bool forceLoad) {
|
||||
if (event && (event->getPixmap().isNull() || forceLoad))
|
||||
event->loadPixmap(this);
|
||||
}
|
||||
|
||||
void Project::clearEventGraphics() {
|
||||
qDeleteAll(eventGraphicsMap);
|
||||
eventGraphicsMap.clear();
|
||||
qDeleteAll(this->eventGraphicsMap);
|
||||
this->eventGraphicsMap.clear();
|
||||
}
|
||||
|
||||
bool Project::readEventGraphics() {
|
||||
clearEventGraphics();
|
||||
|
||||
fileWatcher.addPaths(QStringList() << root + "/" + projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_pointers)
|
||||
<< root + "/" + projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info)
|
||||
<< root + "/" + projectConfig.getFilePath(ProjectFilePath::data_obj_event_pic_tables)
|
||||
<< root + "/" + projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx));
|
||||
|
||||
const QString pointersFilepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_pointers);
|
||||
const QString pointersName = projectConfig.getIdentifier(ProjectIdentifier::symbol_obj_event_gfx_pointers);
|
||||
QMap<QString, QString> pointerHash = parser.readNamedIndexCArray(pointersFilepath, pointersName);
|
||||
const QString gfxInfoFilepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info);
|
||||
const QString picTablesFilepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_pic_tables);
|
||||
const QString gfxFilepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx);
|
||||
fileWatcher.addPaths({pointersFilepath, gfxInfoFilepath, picTablesFilepath, gfxFilepath});
|
||||
|
||||
QStringList gfxNames = gfxDefines.keys();
|
||||
// Read the table mapping OBJ_EVENT_GFX constants to the names of pointers to data about their graphics.
|
||||
const QString pointersName = projectConfig.getIdentifier(ProjectIdentifier::symbol_obj_event_gfx_pointers);
|
||||
const QMap<QString, QString> pointerMap = parser.readNamedIndexCArray(pointersFilepath, pointersName);
|
||||
|
||||
// The positions of each of the required members for the gfx info struct.
|
||||
// For backwards compatibility if the struct doesn't use initializers.
|
||||
static const auto gfxInfoMemberMap = QHash<int, QString>{
|
||||
static const QHash<int, QString> gfxInfoMemberMap = {
|
||||
{4, "width"},
|
||||
{5, "height"},
|
||||
{8, "inanimate"},
|
||||
{11, "oam"},
|
||||
{12, "subspriteTables"},
|
||||
{14, "images"},
|
||||
};
|
||||
// Read the structs containing data about each of the event sprites.
|
||||
auto gfxInfos = parser.readCStructs(gfxInfoFilepath, "", gfxInfoMemberMap);
|
||||
|
||||
QString filepath = projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx_info);
|
||||
auto gfxInfos = parser.readCStructs(filepath, "", gfxInfoMemberMap);
|
||||
// We need data in both of these files to translate data from the structs above into the path for a .png file.
|
||||
const QMap<QString, QStringList> picTables = parser.readCArrayMulti(picTablesFilepath);
|
||||
const QMap<QString, QString> graphicIncbins = parser.readCIncbinMulti(gfxFilepath);
|
||||
|
||||
QMap<QString, QStringList> picTables = parser.readCArrayMulti(projectConfig.getFilePath(ProjectFilePath::data_obj_event_pic_tables));
|
||||
QMap<QString, QString> graphicIncbins = parser.readCIncbinMulti(projectConfig.getFilePath(ProjectFilePath::data_obj_event_gfx));
|
||||
for (auto i = this->gfxDefines.constBegin(); i != this->gfxDefines.constEnd(); i++) {
|
||||
const QString gfxName = i.key();
|
||||
|
||||
for (QString gfxName : gfxNames) {
|
||||
QString info_label = pointerHash[gfxName].replace("&", "");
|
||||
// Strip the address-of operator to get the pointer's name. We'll use this name to get data about the event's sprite.
|
||||
// If we don't recognize the name, ignore it. The event will use a default sprite.
|
||||
QString info_label = pointerMap.value(gfxName);
|
||||
info_label.replace("&", "");
|
||||
if (!gfxInfos.contains(info_label))
|
||||
continue;
|
||||
const QHash<QString, QString> gfxInfoAttributes = gfxInfos[info_label];
|
||||
|
||||
auto gfxInfoAttributes = gfxInfos[info_label];
|
||||
auto gfx = new EventGraphics;
|
||||
|
||||
auto eventGraphics = new EventGraphics;
|
||||
eventGraphics->inanimate = ParseUtil::gameStringToBool(gfxInfoAttributes.value("inanimate"));
|
||||
QString pic_label = gfxInfoAttributes.value("images");
|
||||
QString dimensions_label = gfxInfoAttributes.value("oam");
|
||||
QString subsprites_label = gfxInfoAttributes.value("subspriteTables");
|
||||
|
||||
QString gfx_label = picTables[pic_label].value(0);
|
||||
// We need the .png filepath for the event's sprite. This is buried behind a few levels of indirection.
|
||||
// The 'images' field gives us the name of the table containing the sprite's image data.
|
||||
// The entries in this table are expected to be in the format (PngSymbolName, ...).
|
||||
// We extract the symbol name of the .png's INCBIN'd data by looking at the first entry in this table.
|
||||
// Once we have the .png's symbol name we can get the actual filepath from its INCBIN.
|
||||
QString gfx_label = picTables[gfxInfoAttributes.value("images")].value(0);
|
||||
static const QRegularExpression re_parens("[\\(\\)]");
|
||||
gfx_label = gfx_label.section(re_parens, 1, 1);
|
||||
QString path = graphicIncbins[gfx_label];
|
||||
gfx->filepath = fixGraphicPath(graphicIncbins[gfx_label]);
|
||||
|
||||
if (!path.isNull()) {
|
||||
path = fixGraphicPath(path);
|
||||
eventGraphics->spritesheet = QImage(root + "/" + path);
|
||||
if (!eventGraphics->spritesheet.isNull()) {
|
||||
// Infer the sprite dimensions from the OAM labels.
|
||||
static const QRegularExpression re("\\S+_(\\d+)x(\\d+)");
|
||||
QRegularExpressionMatch dimensionMatch = re.match(dimensions_label);
|
||||
QRegularExpressionMatch oamTablesMatch = re.match(subsprites_label);
|
||||
if (oamTablesMatch.hasMatch()) {
|
||||
eventGraphics->spriteWidth = oamTablesMatch.captured(1).toInt(nullptr, 0);
|
||||
eventGraphics->spriteHeight = oamTablesMatch.captured(2).toInt(nullptr, 0);
|
||||
} else if (dimensionMatch.hasMatch()) {
|
||||
eventGraphics->spriteWidth = dimensionMatch.captured(1).toInt(nullptr, 0);
|
||||
eventGraphics->spriteHeight = dimensionMatch.captured(2).toInt(nullptr, 0);
|
||||
} else {
|
||||
eventGraphics->spriteWidth = eventGraphics->spritesheet.width();
|
||||
eventGraphics->spriteHeight = eventGraphics->spritesheet.height();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eventGraphics->spritesheet = QImage();
|
||||
eventGraphics->spriteWidth = 16;
|
||||
eventGraphics->spriteHeight = 16;
|
||||
// Note: gfx has a 'spritesheet' field that will contain a QImage for the event's sprite.
|
||||
// We don't create this QImage yet. Reading the image now is unnecessary overhead for startup.
|
||||
// We'll read the image file when the event's sprite is first requested to be drawn.
|
||||
|
||||
// The .png file is expected to be a spritesheet that can have multiple frames.
|
||||
// We only want to show one frame at a time, so we need to know the dimensions of each frame.
|
||||
// The true dimensions are buried in the subsprite data, so we try to infer the dimensions from the name of the 'subspriteTables' symbol.
|
||||
// If we are unable to do this, we can read the dimensions from the width and height fields.
|
||||
// This is much more straightforward, but the numbers are not necessarily accurate (one vanilla event sprite,
|
||||
// the Town Map in FRLG, has width/height values that differ from its true dimensions).
|
||||
static const QRegularExpression re_dimensions("\\S+_(\\d+)x(\\d+)");
|
||||
const QRegularExpressionMatch dimensionsMatch = re_dimensions.match(gfxInfoAttributes.value("subspriteTables"));
|
||||
if (dimensionsMatch.hasMatch()) {
|
||||
gfx->spriteWidth = dimensionsMatch.captured(1).toInt(nullptr, 0);
|
||||
gfx->spriteHeight = dimensionsMatch.captured(2).toInt(nullptr, 0);
|
||||
} else if (gfxInfoAttributes.contains("width") && gfxInfoAttributes.contains("height")) {
|
||||
gfx->spriteWidth = gfxInfoAttributes.value("width").toInt(nullptr, 0);
|
||||
gfx->spriteHeight = gfxInfoAttributes.value("height").toInt(nullptr, 0);
|
||||
}
|
||||
eventGraphicsMap.insert(gfxName, eventGraphics);
|
||||
// If we fail to get sprite dimensions then they should remain -1, and the sprite will use the full spritesheet as its image.
|
||||
if (gfx->spriteWidth <= 0 || gfx->spriteHeight <= 0) {
|
||||
gfx->spriteWidth = -1;
|
||||
gfx->spriteHeight = -1;
|
||||
}
|
||||
|
||||
// Inanimate events will only ever use the first frame of their spritesheet.
|
||||
gfx->inanimate = ParseUtil::gameStringToBool(gfxInfoAttributes.value("inanimate"));
|
||||
|
||||
this->eventGraphicsMap.insert(gfxName, gfx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QPixmap Project::getEventPixmap(const QString &gfxName, const QString &movementName) {
|
||||
struct FrameData {
|
||||
int index = 0;
|
||||
bool hFlip = false;
|
||||
};
|
||||
// TODO: Expose as a setting to users
|
||||
static const QMap<QString, FrameData> directionToFrameData = {
|
||||
{"DIR_SOUTH", { .index = 0, .hFlip = false }},
|
||||
{"DIR_NORTH", { .index = 1, .hFlip = false }},
|
||||
{"DIR_WEST", { .index = 2, .hFlip = false }},
|
||||
{"DIR_EAST", { .index = 2, .hFlip = true }}, // East-facing sprite is just the West-facing sprite mirrored
|
||||
};
|
||||
const QString direction = this->facingDirections.value(movementName, "DIR_SOUTH");
|
||||
auto frameData = directionToFrameData.value(direction);
|
||||
return getEventPixmap(gfxName, frameData.index, frameData.hFlip);
|
||||
}
|
||||
|
||||
QPixmap Project::getEventPixmap(const QString &gfxName, int frame, bool hFlip) {
|
||||
QPixmap pixmap;
|
||||
const QString cacheKey = QString("EVENT#%1#%2#%3").arg(gfxName).arg(frame).arg(hFlip ? "1" : "0");
|
||||
if (QPixmapCache::find(cacheKey, &pixmap)) {
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
EventGraphics* gfx = this->eventGraphicsMap.value(gfxName, nullptr);
|
||||
if (!gfx) {
|
||||
// Invalid gfx constant. If this is a number, try to use that instead.
|
||||
bool ok;
|
||||
int gfxNum = ParseUtil::gameStringToInt(gfxName, &ok);
|
||||
if (ok && gfxNum < this->gfxDefines.count()) {
|
||||
gfx = this->eventGraphicsMap.value(this->gfxDefines.key(gfxNum, "NULL"), nullptr);
|
||||
}
|
||||
}
|
||||
if (gfx && !gfx->loaded) {
|
||||
// This is the first request for this event's sprite. We'll attempt to load it now.
|
||||
if (!gfx->filepath.isEmpty()) {
|
||||
gfx->spritesheet = QImage(QString("%1/%2").arg(this->root).arg(gfx->filepath));
|
||||
if (gfx->spritesheet.isNull()) {
|
||||
logWarn(QString("Failed to open '%1' for event's sprite. Event will use a default sprite instead.").arg(gfx->filepath));
|
||||
} else {
|
||||
// If we were unable to find the dimensions of a frame within the spritesheet we'll use the full image dimensions.
|
||||
if (gfx->spriteWidth <= 0) {
|
||||
gfx->spriteWidth = gfx->spritesheet.width();
|
||||
}
|
||||
if (gfx->spriteHeight <= 0) {
|
||||
gfx->spriteHeight = gfx->spritesheet.height();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set this whether we were successful or not, we only need to try to load it once.
|
||||
gfx->loaded = true;
|
||||
}
|
||||
if (!gfx || gfx->spritesheet.isNull()) {
|
||||
// Either we didn't recognize the gfxName, or we were unable to load the sprite's image.
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
QImage img;
|
||||
if (gfx->inanimate) {
|
||||
img = gfx->spritesheet.copy(0, 0, gfx->spriteWidth, gfx->spriteHeight);
|
||||
} else {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
// Get frame's position in spritesheet.
|
||||
// Assume horizontal layout. If position would exceed sheet width, try vertical layout.
|
||||
if ((frame + 1) * gfx->spriteWidth <= gfx->spritesheet.width()) {
|
||||
x = frame * gfx->spriteWidth;
|
||||
} else if ((frame + 1) * gfx->spriteHeight <= gfx->spritesheet.height()) {
|
||||
y = frame * gfx->spriteHeight;
|
||||
}
|
||||
|
||||
img = gfx->spritesheet.copy(x, y, gfx->spriteWidth, gfx->spriteHeight);
|
||||
if (hFlip) {
|
||||
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);
|
||||
QPixmapCache::insert(cacheKey, pixmap);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
QPixmap Project::getEventPixmap(Event::Group group) {
|
||||
if (group == Event::Group::None)
|
||||
return QPixmap();
|
||||
|
||||
QPixmap pixmap;
|
||||
const QString cacheKey = QString("EVENT#%1").arg(Event::groupToString(group));
|
||||
if (QPixmapCache::find(cacheKey, &pixmap)) {
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
const int defaultWidth = 16;
|
||||
const int defaultHeight = 16;
|
||||
static const QPixmap defaultIcons = QPixmap(":/images/Entities_16x16.png");
|
||||
QPixmap defaultIcon = QPixmap(defaultIcons.copy(static_cast<int>(group) * defaultWidth, 0, defaultWidth, defaultHeight));
|
||||
|
||||
// Custom event icons may be provided by the user.
|
||||
QString customIconPath = projectConfig.getEventIconPath(group);
|
||||
if (customIconPath.isEmpty()) {
|
||||
// No custom icon specified, use the default icon.
|
||||
pixmap = defaultIcon;
|
||||
} else {
|
||||
// Try to load custom icon
|
||||
QString validPath = Project::getExistingFilepath(customIconPath);
|
||||
if (!validPath.isEmpty()) customIconPath = validPath; // Otherwise allow it to fail with the original path
|
||||
pixmap = QPixmap(customIconPath);
|
||||
if (pixmap.isNull()) {
|
||||
pixmap = defaultIcon;
|
||||
logWarn(QString("Failed to load custom event icon '%1', using default icon.").arg(customIconPath));
|
||||
}
|
||||
}
|
||||
QPixmapCache::insert(cacheKey, pixmap);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
bool Project::readSpeciesIconPaths() {
|
||||
this->speciesToIconPath.clear();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ void DraggablePixmapItem::emitPositionChanged() {
|
||||
}
|
||||
|
||||
void DraggablePixmapItem::updatePixmap() {
|
||||
editor->project->setEventPixmap(event, true);
|
||||
editor->project->loadEventPixmap(event, true);
|
||||
this->updatePosition();
|
||||
editor->redrawEventPixmapItem(this);
|
||||
emit spriteChanged(event->getPixmap());
|
||||
|
||||
@@ -173,12 +173,19 @@ void EventFrame::setActive(bool active) {
|
||||
}
|
||||
|
||||
void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * project) {
|
||||
// The script dropdown is populated with scripts used by the map's events and from its scripts file.
|
||||
if (this->event->getMap())
|
||||
combo->addItems(this->event->getMap()->getScriptLabels(this->event->getEventGroup()));
|
||||
// The script dropdown and autocomplete are populated with scripts used by the map's events and from its scripts file.
|
||||
if (!this->event->getMap())
|
||||
return;
|
||||
|
||||
// The dropdown's autocomplete has all script labels across the full project.
|
||||
auto completer = new QCompleter(project->globalScriptLabels, combo);
|
||||
QStringList scripts = this->event->getMap()->getScriptLabels(this->event->getEventGroup());
|
||||
combo->addItems(scripts);
|
||||
|
||||
// Depending on the settings, the autocomplete may also contain all global scripts.
|
||||
if (porymapConfig.loadAllEventScripts) {
|
||||
project->insertGlobalScriptLabels(scripts);
|
||||
}
|
||||
|
||||
auto completer = new QCompleter(scripts, combo);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
||||
completer->setFilterMode(Qt::MatchContains);
|
||||
@@ -188,6 +195,9 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj
|
||||
if (popup) popup->setUniformItemSizes(true);
|
||||
|
||||
combo->setCompleter(completer);
|
||||
|
||||
// If the project changes the script labels, update the EventFrame.
|
||||
connect(project, &Project::eventScriptLabelsRead, this, &EventFrame::invalidateValues, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -517,7 +517,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
|
||||
|| (m_settings.showBGs && group == Event::Group::Bg)
|
||||
|| (m_settings.showTriggers && group == Event::Group::Coord)
|
||||
|| (m_settings.showHealLocations && group == Event::Group::Heal)) {
|
||||
m_editor->project->setEventPixmap(event);
|
||||
m_editor->project->loadEventPixmap(event);
|
||||
eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ void PreferenceEditor::updateFields() {
|
||||
ui->checkBox_OpenRecentProject->setChecked(porymapConfig.reopenOnLaunch);
|
||||
ui->checkBox_CheckForUpdates->setChecked(porymapConfig.checkForUpdates);
|
||||
ui->checkBox_DisableEventWarning->setChecked(porymapConfig.eventDeleteWarningDisabled);
|
||||
ui->checkBox_AutocompleteAllScripts->setChecked(porymapConfig.loadAllEventScripts);
|
||||
}
|
||||
|
||||
void PreferenceEditor::saveFields() {
|
||||
@@ -64,6 +65,11 @@ void PreferenceEditor::saveFields() {
|
||||
porymapConfig.theme = theme;
|
||||
emit themeChanged(theme);
|
||||
}
|
||||
bool loadAllEventScripts = ui->checkBox_AutocompleteAllScripts->isChecked();
|
||||
if (loadAllEventScripts != porymapConfig.loadAllEventScripts) {
|
||||
porymapConfig.loadAllEventScripts = loadAllEventScripts;
|
||||
emit scriptSettingsChanged(loadAllEventScripts);
|
||||
}
|
||||
porymapConfig.eventSelectionShapeMode = ui->radioButton_OnSprite->isChecked() ? QGraphicsPixmapItem::MaskShape : QGraphicsPixmapItem::BoundingRectShape;
|
||||
porymapConfig.textEditorOpenFolder = ui->lineEdit_TextEditorOpenFolder->text();
|
||||
porymapConfig.textEditorGotoLine = ui->lineEdit_TextEditorGotoLine->text();
|
||||
|
||||
Reference in New Issue
Block a user