Add warp behavior list to settings

This commit is contained in:
GriffinR 2023-12-16 20:35:54 -05:00
parent 5d4d88d11e
commit fc1e192087
12 changed files with 259 additions and 74 deletions

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>600</width> <width>631</width>
<height>600</height> <height>600</height>
</rect> </rect>
</property> </property>
@ -38,7 +38,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>528</width> <width>559</width>
<height>607</height> <height>607</height>
</rect> </rect>
</property> </property>
@ -178,7 +178,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label_CollisionImagePath">
<property name="text"> <property name="text">
<string>Image Path</string> <string>Image Path</string>
</property> </property>
@ -369,7 +369,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>531</width> <width>559</width>
<height>587</height> <height>587</height>
</rect> </rect>
</property> </property>
@ -743,7 +743,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>528</width> <width>559</width>
<height>568</height> <height>568</height>
</rect> </rect>
</property> </property>
@ -1007,7 +1007,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>559</width> <width>559</width>
<height>490</height> <height>827</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_8"> <layout class="QVBoxLayout" name="verticalLayout_8">
@ -1160,6 +1160,82 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_WarpBehaviors">
<property name="title">
<string>Warp Behaviors</string>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<item row="1" column="2">
<widget class="QToolButton" name="button_RemoveWarpBehavior">
<property name="toolTip">
<string>Remove the current text from the list</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/delete.ico</normaloff>:/icons/delete.ico</iconset>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="label_WarpBehaviors">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Porymap will display a warning on Warp Events if they are not positioned on a metatile with one of these behaviors. This list is purely for the warning and &lt;b&gt;will have no effect on your game&lt;/b&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QCheckBox" name="checkBox_DisableWarning">
<property name="toolTip">
<string>If checked, Warp Events will not display a warning about incompatible metatile behaviors</string>
</property>
<property name="text">
<string>Disable Warning</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="NoScrollComboBox" name="comboBox_WarpBehaviors"/>
</item>
<item row="4" column="0" colspan="3">
<widget class="QTextEdit" name="textEdit_WarpBehaviors">
<property name="toolTip">
<string>Metatile Behaviors on this list won't trigger warnings for Warp Events</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
<property name="placeholderText">
<string>Use the dropbown and buttons to add behaviors to the list...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="button_AddWarpBehavior">
<property name="toolTip">
<string>Add the current text to the list</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QFrame" name="frame_WarningEventsTab"> <widget class="QFrame" name="frame_WarningEventsTab">
<property name="styleSheet"> <property name="styleSheet">
@ -1290,7 +1366,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>528</width> <width>559</width>
<height>490</height> <height>490</height>
</rect> </rect>
</property> </property>
@ -1342,7 +1418,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>502</width> <width>533</width>
<height>440</height> <height>440</height>
</rect> </rect>
</property> </property>

View file

@ -248,10 +248,13 @@ public:
this->blockMetatileIdMask = 0x03FF; this->blockMetatileIdMask = 0x03FF;
this->blockCollisionMask = 0x0C00; this->blockCollisionMask = 0x0C00;
this->blockElevationMask = 0xF000; this->blockElevationMask = 0xF000;
this->warpBehaviors = defaultWarpBehaviors;
this->warpBehaviorWarningDisabled = false;
this->readKeys.clear(); this->readKeys.clear();
} }
static const QMap<ProjectFilePath, std::pair<QString, QString>> defaultPaths; static const QMap<ProjectFilePath, std::pair<QString, QString>> defaultPaths;
static const QStringList versionStrings; static const QStringList versionStrings;
static const QStringList defaultWarpBehaviors;
void reset(BaseGameVersion baseGameVersion); void reset(BaseGameVersion baseGameVersion);
void setBaseGameVersion(BaseGameVersion baseGameVersion); void setBaseGameVersion(BaseGameVersion baseGameVersion);
BaseGameVersion getBaseGameVersion(); BaseGameVersion getBaseGameVersion();
@ -329,7 +332,7 @@ public:
void setEventIconPath(Event::Group group, const QString &path); void setEventIconPath(Event::Group group, const QString &path);
QString getEventIconPath(Event::Group group); QString getEventIconPath(Event::Group group);
void setPokemonIconPath(const QString &species, const QString &path); void setPokemonIconPath(const QString &species, const QString &path);
QString getPokemonIconPath(const QString & species); QString getPokemonIconPath(const QString &species);
QHash<QString, QString> getPokemonIconPaths(); QHash<QString, QString> getPokemonIconPaths();
void setCollisionSheetPath(const QString &path); void setCollisionSheetPath(const QString &path);
QString getCollisionSheetPath(); QString getCollisionSheetPath();
@ -337,6 +340,10 @@ public:
int getCollisionSheetWidth(); int getCollisionSheetWidth();
void setCollisionSheetHeight(int height); void setCollisionSheetHeight(int height);
int getCollisionSheetHeight(); int getCollisionSheetHeight();
void setWarpBehaviors(const QStringList &behaviors);
QStringList getWarpBehaviors();
void setWarpBehaviorWarningDisabled(bool disabled);
bool getWarpBehaviorWarningDisabled();
// TODO: Replace these once there's generic support for editing project names // TODO: Replace these once there's generic support for editing project names
static const QString metatileIdMaskName; static const QString metatileIdMaskName;
@ -396,6 +403,8 @@ private:
QString collisionSheetPath; QString collisionSheetPath;
int collisionSheetWidth; int collisionSheetWidth;
int collisionSheetHeight; int collisionSheetHeight;
QStringList warpBehaviors;
bool warpBehaviorWarningDisabled;
}; };
extern ProjectConfig projectConfig; extern ProjectConfig projectConfig;

View file

@ -190,7 +190,6 @@ private:
static bool startDetachedProcess(const QString &command, static bool startDetachedProcess(const QString &command,
const QString &workingDirectory = QString(), const QString &workingDirectory = QString(),
qint64 *pid = nullptr); qint64 *pid = nullptr);
void constructBehaviorValueList(); // TODO: Remove
private slots: private slots:
void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);

View file

@ -159,7 +159,7 @@ public:
public slots: public slots:
void on_mainTabBar_tabBarClicked(int index); void on_mainTabBar_tabBarClicked(int index);
void on_mapViewTab_tabBarClicked(int index); void on_mapViewTab_tabBarClicked(int index);
void openProjectSettingsEditor(int tab); void onWarpBehaviorWarningClicked();
private slots: private slots:
void on_action_Open_Project_triggered(); void on_action_Open_Project_triggered();
@ -390,7 +390,7 @@ private:
void initShortcutsEditor(); void initShortcutsEditor();
void initCustomScriptsEditor(); void initCustomScriptsEditor();
void connectSubEditorsToShortcutsEditor(); void connectSubEditorsToShortcutsEditor();
void openProjectSettingsEditor(int tab);
bool isProjectOpen(); bool isProjectOpen();
void showExportMapImageWindow(ImageExporterMode mode); void showExportMapImageWindow(ImageExporterMode mode);
void redrawMetatileSelection(); void redrawMetatileSelection();

View file

@ -79,6 +79,7 @@ public:
QMap<QString, QMap<QString, int>> metatileLabelsMap; QMap<QString, QMap<QString, int>> metatileLabelsMap;
QMap<QString, int> metatileBehaviorMap; QMap<QString, int> metatileBehaviorMap;
QMap<int, QString> metatileBehaviorMapInverse; QMap<int, QString> metatileBehaviorMapInverse;
QSet<uint32_t> warpBehaviorValues;
QMap<QString, QString> facingDirections; QMap<QString, QString> facingDirections;
ParseUtil parser; ParseUtil parser;
QFileSystemWatcher fileWatcher; QFileSystemWatcher fileWatcher;

View file

@ -21,7 +21,7 @@ public:
explicit ProjectSettingsEditor(QWidget *parent = nullptr, Project *project = nullptr); explicit ProjectSettingsEditor(QWidget *parent = nullptr, Project *project = nullptr);
~ProjectSettingsEditor(); ~ProjectSettingsEditor();
static const int warpBehaviorsTab; static const int eventsTab;
void setTab(int index); void setTab(int index);
signals: signals:
@ -60,6 +60,8 @@ private:
QString stripProjectDir(QString s); QString stripProjectDir(QString s);
void disableParsedSetting(QWidget * widget, const QString &name, const QString &filepath); void disableParsedSetting(QWidget * widget, const QString &name, const QString &filepath);
void updateMaskOverlapWarning(QLabel * warning, QList<UIntSpinBox*> masks); void updateMaskOverlapWarning(QLabel * warning, QList<UIntSpinBox*> masks);
QStringList getWarpBehaviorsList();
void setWarpBehaviorsList(QStringList list);
private slots: private slots:
void dialogButtonClicked(QAbstractButton *button); void dialogButtonClicked(QAbstractButton *button);
@ -70,6 +72,7 @@ private slots:
void on_mainTabs_tabBarClicked(int index); void on_mainTabs_tabBarClicked(int index);
void updateBlockMaskOverlapWarning(); void updateBlockMaskOverlapWarning();
void updateAttributeMaskOverlapWarning(); void updateAttributeMaskOverlapWarning();
void updateWarpBehaviorsList(bool adding);
}; };
#endif // PROJECTSETTINGSEDITOR_H #endif // PROJECTSETTINGSEDITOR_H

View file

@ -16,6 +16,37 @@
#include <QAction> #include <QAction>
#include <QAbstractButton> #include <QAbstractButton>
const QStringList ProjectConfig::defaultWarpBehaviors = {
"MB_NORTH_ARROW_WARP",
"MB_SOUTH_ARROW_WARP",
"MB_WEST_ARROW_WARP",
"MB_EAST_ARROW_WARP",
"MB_STAIRS_OUTSIDE_ABANDONED_SHIP",
"MB_WATER_SOUTH_ARROW_WARP",
"MB_SHOAL_CAVE_ENTRANCE",
"MB_SECRET_BASE_SPOT_RED_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_BROWN_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_YELLOW_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_BLUE_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_TREE_LEFT_OPEN",
"MB_SECRET_BASE_SPOT_TREE_RIGHT_OPEN",
"MB_SECRET_BASE_SPOT_SHRUB_OPEN",
"MB_ANIMATED_DOOR",
"MB_NON_ANIMATED_DOOR",
"MB_PETALBURG_GYM_DOOR",
"MB_WATER_DOOR",
"MB_LADDER",
"MB_UP_ESCALATOR",
"MB_DOWN_ESCALATOR",
"MB_DEEP_SOUTH_WARP",
"MB_LAVARIDGE_GYM_B1F_WARP",
"MB_LAVARIDGE_GYM_1F_WARP",
"MB_AQUA_HIDEOUT_WARP",
"MB_MT_PYRE_HOLE",
"MB_MOSSDEEP_GYM_WARP",
"MB_BRIDGE_OVER_OCEAN",
};
const QString ProjectConfig::metatileIdMaskName = "MAPGRID_METATILE_ID_MASK"; const QString ProjectConfig::metatileIdMaskName = "MAPGRID_METATILE_ID_MASK";
const QString ProjectConfig::collisionMaskName = "MAPGRID_COLLISION_MASK"; const QString ProjectConfig::collisionMaskName = "MAPGRID_COLLISION_MASK";
const QString ProjectConfig::elevationMaskName = "MAPGRID_ELEVATION_MASK"; const QString ProjectConfig::elevationMaskName = "MAPGRID_ELEVATION_MASK";
@ -746,6 +777,12 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->collisionSheetWidth = getConfigUint32(key, value, 1, Block::maxValue); this->collisionSheetWidth = getConfigUint32(key, value, 1, Block::maxValue);
} else if (key == "collision_sheet_height") { } else if (key == "collision_sheet_height") {
this->collisionSheetHeight = getConfigUint32(key, value, 1, Block::maxValue); this->collisionSheetHeight = getConfigUint32(key, value, 1, Block::maxValue);
} else if (key == "warp_behaviors") {
value.remove(" ");
this->warpBehaviors = value.split(",", Qt::SkipEmptyParts);
this->warpBehaviors.removeDuplicates();
} else if (key == "warp_behavior_warning_disabled") {
this->warpBehaviorWarningDisabled = getConfigBool(key, value);
} else { } else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
} }
@ -829,6 +866,9 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("collision_sheet_path", this->collisionSheetPath); map.insert("collision_sheet_path", this->collisionSheetPath);
map.insert("collision_sheet_width", QString::number(this->collisionSheetWidth)); map.insert("collision_sheet_width", QString::number(this->collisionSheetWidth));
map.insert("collision_sheet_height", QString::number(this->collisionSheetHeight)); map.insert("collision_sheet_height", QString::number(this->collisionSheetHeight));
map.insert("warp_behaviors", this->warpBehaviors.join(","));
map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled));
return map; return map;
} }
@ -1254,6 +1294,24 @@ int ProjectConfig::getCollisionSheetHeight() {
return this->collisionSheetHeight; return this->collisionSheetHeight;
} }
void ProjectConfig::setWarpBehaviors(const QStringList &behaviors) {
this->warpBehaviors = behaviors;
this->save();
}
QStringList ProjectConfig::getWarpBehaviors() {
return this->warpBehaviors;
}
void ProjectConfig::setWarpBehaviorWarningDisabled(bool disabled) {
this->warpBehaviorWarningDisabled = disabled;
this->save();
}
bool ProjectConfig::getWarpBehaviorWarningDisabled() {
return this->warpBehaviorWarningDisabled;
}
UserConfig userConfig; UserConfig userConfig;

View file

@ -106,7 +106,6 @@ void Editor::setEditingMap() {
this->cursorMapTileRect->setActive(true); this->cursorMapTileRect->setActive(true);
setMapEditingButtonsEnabled(true); setMapEditingButtonsEnabled(true);
this->constructBehaviorValueList(); // TODO: Remove
} }
void Editor::setEditingCollision() { void Editor::setEditingCollision() {
@ -1989,58 +1988,11 @@ void Editor::redrawObject(DraggablePixmapItem *item) {
} }
} }
/* TODO: Add tab to PSE for warp behaviors, containing:
- A description of what it means
- A check box to enable/disable the warning
- An editable list of warp behavior names
*/
// TODO: Relocate to file handling the "warp behaviors" window, make static list below a config default
const QStringList warpBehaviorNames = {
"MB_NORTH_ARROW_WARP",
"MB_SOUTH_ARROW_WARP",
"MB_WEST_ARROW_WARP",
"MB_EAST_ARROW_WARP",
"MB_STAIRS_OUTSIDE_ABANDONED_SHIP",
"MB_WATER_SOUTH_ARROW_WARP",
"MB_SHOAL_CAVE_ENTRANCE",
"MB_SECRET_BASE_SPOT_RED_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_BROWN_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_YELLOW_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_BLUE_CAVE_OPEN",
"MB_SECRET_BASE_SPOT_TREE_LEFT_OPEN",
"MB_SECRET_BASE_SPOT_TREE_RIGHT_OPEN",
"MB_SECRET_BASE_SPOT_SHRUB_OPEN",
"MB_ANIMATED_DOOR",
"MB_NON_ANIMATED_DOOR",
"MB_PETALBURG_GYM_DOOR",
"MB_WATER_DOOR",
"MB_LADDER",
"MB_UP_ESCALATOR",
"MB_DOWN_ESCALATOR",
"MB_DEEP_SOUTH_WARP",
"MB_LAVARIDGE_GYM_B1F_WARP",
"MB_LAVARIDGE_GYM_1F_WARP",
"MB_AQUA_HIDEOUT_WARP",
"MB_MT_PYRE_HOLE",
"MB_MOSSDEEP_GYM_WARP",
"MB_BRIDGE_OVER_OCEAN",
};
QSet<uint32_t> warpBehaviorValues;
void Editor::constructBehaviorValueList() {
if (!project) return;
warpBehaviorValues.clear();
//warpBehaviorNames.removeDuplicates(); // TODO: Uncomment when list is actually dynamic
for (auto name : warpBehaviorNames) {
if (project->metatileBehaviorMap.contains(name)) {
warpBehaviorValues.insert(project->metatileBehaviorMap[name]);
}
}
}
// Warp events display a warning if they're not positioned on a metatile with a warp behavior. // Warp events display a warning if they're not positioned on a metatile with a warp behavior.
void Editor::updateWarpEventWarning(Event *event) { void Editor::updateWarpEventWarning(Event *event) {
if (!map || !event || event->getEventType() != Event::Type::Warp) if (projectConfig.getWarpBehaviorWarningDisabled())
return;
if (!project || !map || !event || event->getEventType() != Event::Type::Warp)
return; return;
Block block; Block block;
Metatile * metatile = nullptr; Metatile * metatile = nullptr;
@ -2049,16 +2001,18 @@ void Editor::updateWarpEventWarning(Event *event) {
metatile = Tileset::getMetatile(block.metatileId(), map->layout->tileset_primary, map->layout->tileset_secondary); metatile = Tileset::getMetatile(block.metatileId(), map->layout->tileset_primary, map->layout->tileset_secondary);
} }
// metatile may be null if the warp is in the map border. Display the warning in this case // metatile may be null if the warp is in the map border. Display the warning in this case
bool validWarpBehavior = metatile && warpBehaviorValues.contains(metatile->behavior()); bool validWarpBehavior = metatile && project->warpBehaviorValues.contains(metatile->behavior());
warpEvent->setWarningEnabled(!validWarpBehavior); warpEvent->setWarningEnabled(!validWarpBehavior);
} }
// The warp event behavior warning is updated whenever the event moves or the event selection changes. // The warp event behavior warning is updated whenever the event moves or the event selection changes.
// It does not respond to changes in the underlying metatile. To capture the common case of a user painting // It does not respond to changes in the underlying metatile. To capture the common case of a user painting
// metatiles on the Map tab then returning to the Events tab we update the warings for all selected warp // metatiles on the Map tab then returning to the Events tab we update the warnings for all selected warp
// events when the Events tab is opened. This does not cover the case where metatiles are painted while // events when the Events tab is opened. This does not cover the case where metatiles are painted while
// still on the Events tab, such as by Undo/Redo or the scripting API. // still on the Events tab, such as by Undo/Redo or the scripting API.
void Editor::updateWarpEventWarnings() { void Editor::updateWarpEventWarnings() {
if (projectConfig.getWarpBehaviorWarningDisabled())
return;
if (selected_events) { if (selected_events) {
for (auto selection : *selected_events) for (auto selection : *selected_events)
updateWarpEventWarning(selection->event); updateWarpEventWarning(selection->event);

View file

@ -2744,6 +2744,33 @@ void MainWindow::on_actionProject_Settings_triggered() {
this->openProjectSettingsEditor(porymapConfig.getProjectSettingsTab()); this->openProjectSettingsEditor(porymapConfig.getProjectSettingsTab());
} }
void MainWindow::onWarpBehaviorWarningClicked() {
static const QString text = QString(
"By default, Warp Events only function as exits if they're positioned on a metatile "
"whose Metatile Behavior is treated specially in your project's code."
);
static const QString informative = QString(
"<html><head/><body><p>"
"For instance, most floor metatiles in a cave have the behavior <b>MB_CAVE</b>, but the floor space in front of an "
"exit will have <b>MB_SOUTH_ARROW_WARP</b>, which is treated specially and will allow a Warp Event to warp the player. "
"You can see in the status bar what behavior a metatile has when you mouse over it, or by selecting it in the Tileset Editor."
"<br><br>"
"<b>Note</b>: Not all Warp Events that show this warning are incorrect! For example some warps may function "
"as a 1-way entrance, and others may have the metatile underneath them changed programmatically."
"<br><br>"
"You can disable this warning or edit the list of behaviors that silence this warning under <b>Options -> Project Settings...</b>"
"<br></html></body></p>"
);
QMessageBox msgBox(QMessageBox::Information, "porymap", text, QMessageBox::Close, this);
QPushButton *settings = msgBox.addButton("Open Settings...", QMessageBox::ActionRole);
msgBox.setDefaultButton(QMessageBox::Close);
msgBox.setTextFormat(Qt::RichText);
msgBox.setInformativeText(informative);
msgBox.exec();
if (msgBox.clickedButton() == settings)
this->openProjectSettingsEditor(ProjectSettingsEditor::eventsTab);
}
void MainWindow::on_actionCustom_Scripts_triggered() { void MainWindow::on_actionCustom_Scripts_triggered() {
if (!this->customScriptsEditor) if (!this->customScriptsEditor)
initCustomScriptsEditor(); initCustomScriptsEditor();

View file

@ -2291,6 +2291,17 @@ bool Project::readMetatileBehaviors() {
for (QString defineName : this->metatileBehaviorMap.keys()) { for (QString defineName : this->metatileBehaviorMap.keys()) {
this->metatileBehaviorMapInverse.insert(this->metatileBehaviorMap[defineName], defineName); this->metatileBehaviorMapInverse.insert(this->metatileBehaviorMap[defineName], defineName);
} }
// Construct warp behavior value list for the warp metatile behavior warning
const QStringList warpBehaviorNames = projectConfig.getWarpBehaviors();
this->warpBehaviorValues.clear();
for (auto name : warpBehaviorNames) {
if (this->metatileBehaviorMap.contains(name)) {
int value = this->metatileBehaviorMap.value(name);
this->warpBehaviorValues.insert(static_cast<uint32_t>(value));
}
}
return true; return true;
} }

View file

@ -525,9 +525,7 @@ void WarpFrame::connectSignals(MainWindow *window) {
// warning // warning
this->warning->disconnect(); this->warning->disconnect();
connect(this->warning, &QPushButton::clicked, [window]() { connect(this->warning, &QPushButton::clicked, window, &MainWindow::onWarpBehaviorWarningClicked);
window->openProjectSettingsEditor(ProjectSettingsEditor::warpBehaviorsTab);
});
} }
void WarpFrame::initialize() { void WarpFrame::initialize() {

View file

@ -10,7 +10,7 @@
Editor for the settings in a user's porymap.project.cfg file (and 'use_encounter_json' in porymap.user.cfg). Editor for the settings in a user's porymap.project.cfg file (and 'use_encounter_json' in porymap.user.cfg).
*/ */
const int ProjectSettingsEditor::warpBehaviorsTab = 4; const int ProjectSettingsEditor::eventsTab = 3;
ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) : ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) :
QMainWindow(parent), QMainWindow(parent),
@ -45,6 +45,8 @@ void ProjectSettingsEditor::connectSignals() {
this->setBorderMetatileIds(customSize, this->getBorderMetatileIds(!customSize)); this->setBorderMetatileIds(customSize, this->getBorderMetatileIds(!customSize));
this->setBorderMetatilesUi(customSize); this->setBorderMetatilesUi(customSize);
}); });
connect(ui->button_AddWarpBehavior, &QAbstractButton::clicked, [this](bool) { this->updateWarpBehaviorsList(true); });
connect(ui->button_RemoveWarpBehavior, &QAbstractButton::clicked, [this](bool) { this->updateWarpBehaviorsList(false); });
// Connect file selection buttons // Connect file selection buttons
connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, [this](bool) { this->choosePrefabsFile(); }); connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, [this](bool) { this->choosePrefabsFile(); });
@ -56,6 +58,7 @@ void ProjectSettingsEditor::connectSignals() {
connect(ui->button_HealspotsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_HealspotsIcon); }); connect(ui->button_HealspotsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_HealspotsIcon); });
connect(ui->button_PokemonIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_PokemonIcon); }); connect(ui->button_PokemonIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_PokemonIcon); });
// Display a warning if a mask value overlaps with another mask in its group. // Display a warning if a mask value overlaps with another mask in its group.
connect(ui->spinBox_MetatileIdMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning); connect(ui->spinBox_MetatileIdMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning);
connect(ui->spinBox_CollisionMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning); connect(ui->spinBox_CollisionMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning);
@ -67,7 +70,8 @@ void ProjectSettingsEditor::connectSignals() {
// Record that there are unsaved changes if any of the settings are modified // Record that there are unsaved changes if any of the settings are modified
for (auto combo : ui->centralwidget->findChildren<NoScrollComboBox *>()){ for (auto combo : ui->centralwidget->findChildren<NoScrollComboBox *>()){
if (combo != ui->comboBox_IconSpecies) // Changes to the icon species combo box are just for info display, don't mark as unsaved // Changes to these two combo boxes are just for info display, don't mark as unsaved
if (combo != ui->comboBox_IconSpecies && combo != ui->comboBox_WarpBehaviors)
connect(combo, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited); connect(combo, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited);
} }
for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>()) for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
@ -92,17 +96,25 @@ void ProjectSettingsEditor::initUi() {
ui->comboBox_DefaultPrimaryTileset->addItems(project->primaryTilesetLabels); ui->comboBox_DefaultPrimaryTileset->addItems(project->primaryTilesetLabels);
ui->comboBox_DefaultSecondaryTileset->addItems(project->secondaryTilesetLabels); ui->comboBox_DefaultSecondaryTileset->addItems(project->secondaryTilesetLabels);
ui->comboBox_IconSpecies->addItems(project->speciesToIconPath.keys()); ui->comboBox_IconSpecies->addItems(project->speciesToIconPath.keys());
ui->comboBox_WarpBehaviors->addItems(project->metatileBehaviorMap.keys());
} }
ui->comboBox_BaseGameVersion->addItems(ProjectConfig::versionStrings); ui->comboBox_BaseGameVersion->addItems(ProjectConfig::versionStrings);
ui->comboBox_AttributesSize->addItems({"1", "2", "4"}); ui->comboBox_AttributesSize->addItems({"1", "2", "4"});
// Validate that the border metatiles text is a comma-separated list of metatile values // Validate that the border metatiles text is a comma-separated list of metatile values
static const QString regex_Hex = "(0[xX])?[A-Fa-f0-9]+"; static const QString regex_Hex = "(0[xX])?[A-Fa-f0-9]+";
static const QRegularExpression expression(QString("^(%1,)*%1$").arg(regex_Hex)); // Comma-separated list of hex values static const QRegularExpression expression_HexList(QString("^(%1,)*%1$").arg(regex_Hex)); // Comma-separated list of hex values
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression); QRegularExpressionValidator *validator_HexList = new QRegularExpressionValidator(expression_HexList);
ui->lineEdit_BorderMetatiles->setValidator(validator); ui->lineEdit_BorderMetatiles->setValidator(validator_HexList);
this->setBorderMetatilesUi(projectConfig.getUseCustomBorderSize()); this->setBorderMetatilesUi(projectConfig.getUseCustomBorderSize());
// Validate that the text added to the warp behavior list could be a valid define
// (we don't care whether it actually is a metatile behavior define)
static const QRegularExpression expression_Word("^[A-Za-z0-9_]*$");
QRegularExpressionValidator *validator_Word = new QRegularExpressionValidator(expression_Word);
ui->comboBox_WarpBehaviors->setValidator(validator_Word);
ui->textEdit_WarpBehaviors->setTextColor(Qt::gray);
// Set spin box limits // Set spin box limits
uint16_t maxMetatileId = Block::getMaxMetatileId(); uint16_t maxMetatileId = Block::getMaxMetatileId();
ui->spinBox_FillMetatile->setMaximum(maxMetatileId); ui->spinBox_FillMetatile->setMaximum(maxMetatileId);
@ -272,6 +284,38 @@ void ProjectSettingsEditor::updatePokemonIconPath(const QString &newSpecies) {
this->prevIconSpecies = newSpecies; this->prevIconSpecies = newSpecies;
} }
QStringList ProjectSettingsEditor::getWarpBehaviorsList() {
return ui->textEdit_WarpBehaviors->toPlainText().split("\n");
}
void ProjectSettingsEditor::setWarpBehaviorsList(QStringList list) {
ui->textEdit_WarpBehaviors->setText(list.join("\n"));
}
void ProjectSettingsEditor::updateWarpBehaviorsList(bool adding) {
const QString input = ui->comboBox_WarpBehaviors->currentText();
if (input.isEmpty())
return;
QStringList list = this->getWarpBehaviorsList();
int pos = list.indexOf(input);
if (adding && pos < 0) {
// Add text to list
list.prepend(input);
} else if (!adding && pos >= 0) {
// Remove text from list
list.removeAt(pos);
} else {
// Trying to add text already in list,
// or trying to remove text not in list
return;
}
this->setWarpBehaviorsList(list);
this->hasUnsavedChanges = true;
}
void ProjectSettingsEditor::createProjectPathsTable() { void ProjectSettingsEditor::createProjectPathsTable() {
auto pathPairs = ProjectConfig::defaultPaths.values(); auto pathPairs = ProjectConfig::defaultPaths.values();
for (auto pathPair : pathPairs) { for (auto pathPair : pathPairs) {
@ -366,6 +410,7 @@ void ProjectSettingsEditor::refresh() {
ui->checkBox_EnableCustomBorderSize->setChecked(projectConfig.getUseCustomBorderSize()); ui->checkBox_EnableCustomBorderSize->setChecked(projectConfig.getUseCustomBorderSize());
ui->checkBox_OutputCallback->setChecked(projectConfig.getTilesetsHaveCallback()); ui->checkBox_OutputCallback->setChecked(projectConfig.getTilesetsHaveCallback());
ui->checkBox_OutputIsCompressed->setChecked(projectConfig.getTilesetsHaveIsCompressed()); ui->checkBox_OutputIsCompressed->setChecked(projectConfig.getTilesetsHaveIsCompressed());
ui->checkBox_DisableWarning->setChecked(projectConfig.getWarpBehaviorWarningDisabled());
// Set spin box values // Set spin box values
ui->spinBox_Elevation->setValue(projectConfig.getDefaultElevation()); ui->spinBox_Elevation->setValue(projectConfig.getDefaultElevation());
@ -396,6 +441,7 @@ void ProjectSettingsEditor::refresh() {
ui->lineEdit_HealspotsIcon->setText(projectConfig.getEventIconPath(Event::Group::Heal)); ui->lineEdit_HealspotsIcon->setText(projectConfig.getEventIconPath(Event::Group::Heal));
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>()) for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
lineEdit->setText(projectConfig.getFilePath(lineEdit->objectName(), true)); lineEdit->setText(projectConfig.getFilePath(lineEdit->objectName(), true));
this->setWarpBehaviorsList(projectConfig.getWarpBehaviors());
this->refreshing = false; // Allow signals this->refreshing = false; // Allow signals
} }
@ -429,6 +475,7 @@ void ProjectSettingsEditor::save() {
projectConfig.setUseCustomBorderSize(ui->checkBox_EnableCustomBorderSize->isChecked()); projectConfig.setUseCustomBorderSize(ui->checkBox_EnableCustomBorderSize->isChecked());
projectConfig.setTilesetsHaveCallback(ui->checkBox_OutputCallback->isChecked()); projectConfig.setTilesetsHaveCallback(ui->checkBox_OutputCallback->isChecked());
projectConfig.setTilesetsHaveIsCompressed(ui->checkBox_OutputIsCompressed->isChecked()); projectConfig.setTilesetsHaveIsCompressed(ui->checkBox_OutputIsCompressed->isChecked());
projectConfig.setWarpBehaviorWarningDisabled(ui->checkBox_DisableWarning->isChecked());
// Save spin box settings // Save spin box settings
projectConfig.setDefaultElevation(ui->spinBox_Elevation->value()); projectConfig.setDefaultElevation(ui->spinBox_Elevation->value());
@ -455,6 +502,8 @@ void ProjectSettingsEditor::save() {
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>()) for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
projectConfig.setFilePath(lineEdit->objectName(), lineEdit->text()); projectConfig.setFilePath(lineEdit->objectName(), lineEdit->text());
projectConfig.setWarpBehaviors(this->getWarpBehaviorsList());
// Save border metatile IDs // Save border metatile IDs
projectConfig.setNewMapBorderMetatileIds(this->getBorderMetatileIds(ui->checkBox_EnableCustomBorderSize->isChecked())); projectConfig.setNewMapBorderMetatileIds(this->getBorderMetatileIds(ui->checkBox_EnableCustomBorderSize->isChecked()));