Merge branch 'dev' into tileset-types

This commit is contained in:
GriffinR 2025-03-26 21:42:37 -04:00 committed by GitHub
commit 5cd53be057
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 431 additions and 288 deletions

View File

@ -153,11 +153,18 @@ Callbacks
.. js:function:: onMapOpened(mapName)
Called when a map or layout is opened.
Called when a map is opened.
:param mapName: the name of the opened map or layout
:param mapName: the name of the opened map
:type mapName: string
.. js:function:: onLayoutOpened(layoutName)
Called when a layout is opened, either by selecting a new map/layout in the map list or swapping the layout for the current map.
:param layoutName: the name of the opened layout
:type layoutName: string
.. js:function:: onBlockChanged(x, y, prevBlock, newBlock)
Called when a block is changed on the map. For example, this is called when a user paints a new tile or changes the collision property of a block.

View File

@ -52,6 +52,8 @@ public:
this->projectManuallyClosed = false;
this->reopenOnLaunch = true;
this->mapListTab = 0;
this->mapListEditGroupsEnabled = false;
this->mapListHideEmptyEnabled.clear();
this->prettyCursors = true;
this->mirrorConnectingMaps = true;
this->showDiveEmergeMaps = false;
@ -110,6 +112,8 @@ public:
bool reopenOnLaunch;
bool projectManuallyClosed;
int mapListTab;
bool mapListEditGroupsEnabled;
QMap<int, bool> mapListHideEmptyEnabled;
bool prettyCursors;
bool mirrorConnectingMaps;
bool showDiveEmergeMaps;

View File

@ -51,9 +51,6 @@ public:
void setLayout(Layout *layout);
Layout* layout() const { return m_layout; }
void setLayoutId(const QString &layoutId) { m_layoutId = layoutId; }
QString layoutId() const { return m_layoutId; }
int getWidth() const;
int getHeight() const;
int getBorderWidth() const;
@ -109,7 +106,6 @@ public:
private:
QString m_name;
QString m_constantName;
QString m_layoutId;
QString m_sharedEventsMap = "";
QString m_sharedScriptsMap = "";
@ -144,6 +140,7 @@ signals:
void openScriptRequested(QString label);
void connectionAdded(MapConnection*);
void connectionRemoved(MapConnection*);
void layoutChanged();
};
#endif // MAP_H

View File

@ -22,7 +22,6 @@ public:
static QString layoutConstantFromName(const QString &name);
bool loaded = false;
bool hasUnsavedDataChanges = false;
QString id;
@ -141,9 +140,7 @@ private:
void setNewBorderDimensionsBlockdata(int newWidth, int newHeight);
signals:
void layoutChanged(Layout *layout);
//void modified();
void layoutDimensionsChanged(const QSize &size);
void dimensionsChanged(const QSize &size);
void needsRedrawing();
};

View File

@ -34,7 +34,7 @@ public:
QString prefix() const { return m_prefix; }
void setPrefix(const QString &prefix);
bool isValid(QString &input) const;
bool isValid(const QString &input) const;
private:
QString m_prefix;

View File

@ -215,6 +215,7 @@ private:
void clearMapBorder();
void clearMapGrid();
void clearWildMonTables();
int getSortedItemIndex(QComboBox *combo, QString item);
void updateBorderVisibility();
void removeConnectionPixmap(MapConnection *connection);
void displayConnection(MapConnection *connection);

View File

@ -186,7 +186,6 @@ private slots:
void copy();
void paste();
void onLayoutChanged(Layout *layout);
void onOpenConnectedMap(MapConnection*);
void onTilesetsSaved(QString, QString);
void onNewMapCreated(Map *newMap, const QString &groupName);
@ -205,6 +204,7 @@ private slots:
void on_actionNew_Tileset_triggered();
void on_action_Save_triggered();
void on_action_Exit_triggered();
void onLayoutSelectorEditingFinished();
void on_comboBox_LayoutSelector_currentTextChanged(const QString &text);
void on_actionShortcuts_triggered();
@ -383,6 +383,8 @@ private:
void refreshRecentProjectsMenu();
void rebuildMapList_Locations();
void rebuildMapList_Layouts();
void updateMapList();
void openMapListItem(const QModelIndex &index);
void onMapListTabChanged(int index);
@ -421,6 +423,7 @@ private:
double getMetatilesZoomScale();
void redrawMetatileSelection();
void scrollMetatileSelectorToSelection();
MapListToolBar* getMapListToolBar(int tab);
MapListToolBar* getCurrentMapListToolBar();
MapTree* getCurrentMapList();
void setLocationComboBoxes(const QStringList &locations);

View File

@ -37,9 +37,6 @@ public:
QStringList healLocationSaveOrder;
QMap<QString, QList<HealLocationEvent*>> healLocations;
QMap<QString, QString> mapConstantsToMapNames;
QMap<QString, QString> mapNamesToMapConstants;
QMap<QString, QString> mapNameToLayoutId;
QMap<QString, QString> mapNameToMapSectionName;
QString layoutsLabel;
QStringList layoutIds;
QStringList layoutIdsMaster;
@ -83,7 +80,7 @@ public:
void set_root(QString);
void clearMapCache();
void clearMaps();
void clearTilesetCache();
void clearMapLayouts();
void clearEventGraphics();
@ -92,9 +89,15 @@ public:
bool sanityCheck();
bool load();
QMap<QString, Map*> mapCache;
Map* loadMap(QString);
Map* getMap(QString);
Map* loadMap(const QString &mapName);
// Note: This does not guarantee the map is loaded.
Map* getMap(const QString &mapName) { return this->maps.value(mapName); }
bool isMapLoaded(const Map *map) const { return map && isMapLoaded(map->name()); }
bool isMapLoaded(const QString &mapName) const { return this->loadedMapNames.contains(mapName); }
bool isLayoutLoaded(const Layout *layout) const { return layout && isLayoutLoaded(layout->id); }
bool isLayoutLoaded(const QString &layoutId) const { return this->loadedLayoutIds.contains(layoutId); }
QMap<QString, Tileset*> tilesetCache;
Tileset* loadTileset(QString, Tileset *tileset = nullptr);
@ -113,7 +116,10 @@ public:
bool readMapGroups();
void addNewMapGroup(const QString &groupName);
QString mapNameToMapGroup(const QString &mapName);
QString mapNameToMapGroup(const QString &mapName) const;
QString getMapConstant(const QString &mapName, const QString &defaultValue = QString()) const;
QString getMapLayoutId(const QString &mapName, const QString &defaultValue = QString()) const;
QString getMapLocation(const QString &mapName, const QString &defaultValue = QString()) const;
struct NewMapSettings {
QString name;
@ -132,7 +138,7 @@ public:
Layout *createNewLayout(const Layout::Settings &layoutSettings, const Layout* toDuplicate = nullptr);
Tileset *createNewTileset(QString name, bool secondary, bool checkerboardFill);
bool isIdentifierUnique(const QString &identifier) const;
bool isValidNewIdentifier(QString identifier) const;
bool isValidNewIdentifier(const QString &identifier) const;
QString toUniqueIdentifier(const QString &identifier) const;
QString getProjectTitle() const;
QString getNewHealLocationName(const Map* map) const;
@ -148,7 +154,7 @@ public:
QString getDefaultSpeciesIconPath(const QString &species);
QPixmap getSpeciesIcon(const QString &species);
void addNewMapsec(const QString &idName);
bool addNewMapsec(const QString &idName, const QString &displayName = QString());
void removeMapsec(const QString &idName);
QString getMapsecDisplayName(const QString &idName) const { return this->mapSectionDisplayNames.value(idName); }
void setMapsecDisplayName(const QString &idName, const QString &displayName);
@ -255,10 +261,20 @@ public:
static QString getMapGroupPrefix();
private:
QMap<QString, QString> mapSectionDisplayNames;
QHash<QString, QString> mapSectionDisplayNames;
QMap<QString, qint64> modifiedFileTimestamps;
QMap<QString, QString> facingDirections;
QMap<QString, QString> speciesToIconPath;
QHash<QString, QString> speciesToIconPath;
QHash<QString, Map*> maps;
// Maps/layouts represented in these sets have been fully loaded from the project.
// If a valid map name / layout id is not in these sets, a Map / Layout object exists
// for it in Project::maps / Project::mapLayouts, but it has been minimally populated
// (i.e. for a map layout it only has the data read from layouts.json, none of its assets
// have been loaded, and for a map it only has the data needed to identify it in the map
// list, none of the rest of its data in map.json).
QSet<QString> loadedMapNames;
QSet<QString> loadedLayoutIds;
const QRegularExpression re_gbapalExtension;
const QRegularExpression re_bppExtension;

View File

@ -9,6 +9,8 @@
#include <QStringList>
#include <QJSEngine>
// !! New callback functions or changes to existing callback function names/arguments
// should be synced to resources/text/script_template.txt and docsrc/manual/scripting-capabilities.rst
enum CallbackType {
OnProjectOpened,
OnProjectClosed,
@ -17,6 +19,7 @@ enum CallbackType {
OnBlockHoverChanged,
OnBlockHoverCleared,
OnMapOpened,
OnLayoutOpened,
OnMapResized,
OnBorderResized,
OnMapShifted,
@ -43,6 +46,7 @@ public:
static void cb_BlockHoverChanged(int x, int y);
static void cb_BlockHoverCleared();
static void cb_MapOpened(QString mapName);
static void cb_LayoutOpened(QString layoutName);
static void cb_MapResized(int oldWidth, int oldHeight, int newWidth, int newHeight);
static void cb_BorderResized(int oldWidth, int oldHeight, int newWidth, int newHeight);
static void cb_MapShifted(int xDelta, int yDelta);

View File

@ -42,6 +42,8 @@ public:
signals:
void filterCleared(MapTree*);
void addFolderClicked();
void editsAllowedChanged(bool allowed);
void emptyFoldersVisibleChanged(bool visible);
private:
Ui::MapListToolBar *ui;

View File

@ -70,11 +70,13 @@ public:
Q_INVOKABLE void addTileImage(int x, int y, QJSValue tileObj, bool setTransparency = false, int layer = 0);
Q_INVOKABLE void addMetatileImage(int x, int y, int metatileId, bool setTransparency = false, int layer = 0);
private:
QMap<int, Overlay*> overlayMap;
protected:
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
virtual void keyPressEvent(QKeyEvent*) override;
private:
QMap<int, Overlay*> overlayMap;
void updateScene();
};
#endif // GRAPHICSVIEW_H

View File

@ -30,7 +30,7 @@ win32 {
DEFINES += PORYMAP_LATEST_COMMIT=\\\"$$LATEST_COMMIT\\\"
VERSION = 5.4.1
VERSION = 6.0.0
DEFINES += PORYMAP_VERSION=\\\"$$VERSION\\\"
SOURCES += src/core/advancemapparser.cpp \

View File

@ -13,6 +13,11 @@ export function onMapOpened(mapName) {
}
// Called when a layout is opened, either by selecting a new map/layout in the map list or swapping the layout for the current map.
export function onLayoutOpened(layoutName) {
}
// Called when a block is changed on the map. For example, this is called when a user paints a new tile or changes the collision property of a block.
export function onBlockChanged(x, y, prevBlock, newBlock) {

View File

@ -306,6 +306,16 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
this->prettyCursors = getConfigBool(key, value);
} else if (key == "map_list_tab") {
this->mapListTab = getConfigInteger(key, value, 0, 2, 0);
} else if (key == "map_list_edit_groups_enabled") {
this->mapListEditGroupsEnabled = getConfigBool(key, value);
} else if (key.startsWith("map_list_hide_empty_enabled/")) {
bool ok;
int tab = key.mid(QStringLiteral("map_list_hide_empty_enabled/").length()).toInt(&ok, 0);
if (!ok) {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
return;
}
this->mapListHideEmptyEnabled.insert(tab, getConfigBool(key, value));
} else if (key == "main_window_geometry") {
this->mainWindowGeometry = bytesFromString(value);
} else if (key == "main_window_state") {
@ -447,6 +457,10 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
map.insert("reopen_on_launch", this->reopenOnLaunch ? "1" : "0");
map.insert("pretty_cursors", this->prettyCursors ? "1" : "0");
map.insert("map_list_tab", QString::number(this->mapListTab));
map.insert("map_list_edit_groups_enabled", this->mapListEditGroupsEnabled ? "1" : "0");
for (auto i = this->mapListHideEmptyEnabled.constBegin(); i != this->mapListHideEmptyEnabled.constEnd(); i++) {
map.insert(QStringLiteral("map_list_hide_empty_enabled/") + QString::number(i.key()), i.value() ? "1" : "0");
}
map.insert("main_window_geometry", stringFromByteArray(this->mainWindowGeometry));
map.insert("main_window_state", stringFromByteArray(this->mainWindowState));
map.insert("map_splitter_state", stringFromByteArray(this->mapSplitterState));
@ -771,14 +785,14 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
userConfig.parseCustomScripts(value);
#endif
} else if (key.startsWith("path/")) {
auto k = reverseDefaultPaths(key.mid(5));
auto k = reverseDefaultPaths(key.mid(QStringLiteral("path/").length()));
if (k != static_cast<ProjectFilePath>(-1)) {
this->setFilePath(k, value);
} else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
}
} else if (key.startsWith("ident/")) {
auto identifierId = reverseDefaultIdentifier(key.mid(6));
auto identifierId = reverseDefaultIdentifier(key.mid(QStringLiteral("ident/").length()));
if (identifierId != static_cast<ProjectIdentifier>(-1)) {
this->setIdentifier(identifierId, value);
} else {
@ -805,7 +819,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
} else if (key == "event_icon_path_heal") {
this->eventIconPaths[Event::Group::Heal] = value;
} else if (key.startsWith("pokemon_icon_path/")) {
this->pokemonIconPaths.insert(key.mid(18).toUpper(), value);
this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()).toUpper(), value);
} else if (key == "collision_sheet_path") {
this->collisionSheetPath = value;
} else if (key == "collision_sheet_width") {

View File

@ -220,7 +220,7 @@ void ResizeLayout::redo() {
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight());
layout->needsRedrawing();
emit layout->needsRedrawing();
}
void ResizeLayout::undo() {
@ -237,7 +237,7 @@ void ResizeLayout::undo() {
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight());
layout->needsRedrawing();
emit layout->needsRedrawing();
QUndoCommand::undo();
}

View File

@ -206,7 +206,7 @@ bool ObjectEvent::loadFromJson(const QJsonObject &json, Project *) {
}
void ObjectEvent::setDefaultValues(Project *project) {
this->setGfx(project->gfxDefines.keys().value(0, "0"));
this->setGfx(project->gfxDefines.key(0, "0"));
this->setMovement(project->movementTypes.value(0, "0"));
this->setScript("NULL");
this->setTrainerType(project->trainerTypes.value(0, "0"));
@ -285,7 +285,7 @@ OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) {
cloneJson["y"] = this->getY();
cloneJson["target_local_id"] = this->getTargetID();
const QString mapName = this->getTargetMap();
cloneJson["target_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
cloneJson["target_map"] = project->getMapConstant(mapName, mapName);
this->addCustomAttributesTo(&cloneJson);
return cloneJson;
@ -310,7 +310,7 @@ bool CloneObjectEvent::loadFromJson(const QJsonObject &json, Project *project) {
}
void CloneObjectEvent::setDefaultValues(Project *project) {
this->setGfx(project->gfxDefines.keys().value(0, "0"));
this->setGfx(project->gfxDefines.key(0, "0"));
this->setTargetID(1);
if (this->getMap()) this->setTargetMap(this->getMap()->name());
}
@ -333,7 +333,7 @@ QSet<QString> CloneObjectEvent::getExpectedFields() {
void CloneObjectEvent::loadPixmap(Project *project) {
// Try to get the targeted object to clone
int eventIndex = this->targetID - 1;
Map *clonedMap = project->getMap(this->targetMap);
Map *clonedMap = project->loadMap(this->targetMap);
Event *clonedEvent = clonedMap ? clonedMap->getEvent(Event::Group::Object, eventIndex) : nullptr;
if (clonedEvent && clonedEvent->getEventType() == Event::Type::Object) {
@ -380,7 +380,7 @@ OrderedJson::object WarpEvent::buildEventJson(Project *project) {
warpJson["y"] = this->getY();
warpJson["elevation"] = this->getElevation();
const QString mapName = this->getDestinationMap();
warpJson["dest_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
warpJson["dest_map"] = project->getMapConstant(mapName, mapName);
warpJson["dest_warp_id"] = this->getDestinationWarpID();
this->addCustomAttributesTo(&warpJson);
@ -839,7 +839,7 @@ OrderedJson::object HealLocationEvent::buildEventJson(Project *project) {
healLocationJson["y"] = this->getY();
if (projectConfig.healLocationRespawnDataEnabled) {
const QString mapName = this->getRespawnMapName();
healLocationJson["respawn_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
healLocationJson["respawn_map"] = project->getMapConstant(mapName, mapName);
healLocationJson["respawn_npc"] = this->getRespawnNPC();
}

View File

@ -23,7 +23,6 @@ Map::Map(QObject *parent) : QObject(parent)
Map::Map(const Map &other, QObject *parent) : Map(parent) {
m_name = other.m_name;
m_constantName = other.m_constantName;
m_layoutId = other.m_layoutId;
m_sharedEventsMap = other.m_sharedEventsMap;
m_sharedScriptsMap = other.m_sharedScriptsMap;
m_customAttributes = other.m_customAttributes;
@ -50,10 +49,10 @@ Map::~Map() {
// Note: Map does not take ownership of layout
void Map::setLayout(Layout *layout) {
if (layout == m_layout)
return;
m_layout = layout;
if (layout) {
m_layoutId = layout->id;
}
emit layoutChanged();
}
// We don't enforce this for existing maps, but for creating new maps we need to formulaically generate a new MAP_NAME ID.

View File

@ -53,7 +53,7 @@ void MapConnection::markMapEdited() {
}
Map* MapConnection::getMap(const QString& mapName) const {
return project ? project->getMap(mapName) : nullptr;
return project ? project->loadMap(mapName) : nullptr;
}
Map* MapConnection::targetMap() const {

View File

@ -168,8 +168,7 @@ void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bo
Scripting::cb_MapResized(oldWidth, oldHeight, newWidth, newHeight);
}
emit layoutChanged(this);
emit layoutDimensionsChanged(QSize(getWidth(), getHeight()));
emit dimensionsChanged(QSize(getWidth(), getHeight()));
}
void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata) {
@ -194,8 +193,7 @@ void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata) {
this->width = newWidth;
this->height = newHeight;
emit layoutChanged(this);
emit layoutDimensionsChanged(QSize(getWidth(), getHeight()));
emit dimensionsChanged(QSize(getWidth(), getHeight()));
}
void Layout::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) {
@ -211,8 +209,6 @@ void Layout::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockda
if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) {
Scripting::cb_BorderResized(oldWidth, oldHeight, newWidth, newHeight);
}
emit layoutChanged(this);
}
void Layout::setNewDimensionsBlockdata(int newWidth, int newHeight) {

View File

@ -699,7 +699,6 @@ bool ParseUtil::tryParseJsonFile(QJsonDocument *out, const QString &filepath, QS
}
bool ParseUtil::tryParseOrderedJsonFile(poryjson::Json::object *out, const QString &filepath, QString *error) {
QString err;
QString jsonTxt = loadTextFile(filepath, error);
if (error && !error->isEmpty()) {
return false;

View File

@ -35,7 +35,8 @@ void PrefixValidator::fixup(QString &input) const {
input.prepend(m_prefix);
}
bool PrefixValidator::isValid(QString &input) const {
bool PrefixValidator::isValid(const QString &input) const {
int pos = 0;
return validate(input, pos) == QValidator::Acceptable;
QString s(input);
return validate(s, pos) == QValidator::Acceptable;
}

View File

@ -198,6 +198,15 @@ void Editor::clearWildMonTables() {
emit wildMonTableClosed();
}
int Editor::getSortedItemIndex(QComboBox *combo, QString item) {
int i = 0;
for (; i < combo->count(); i++) {
if (item < combo->itemText(i))
break;
}
return i;
}
void Editor::displayWildMonTables() {
clearWildMonTables();
@ -207,17 +216,17 @@ void Editor::displayWildMonTables() {
}
QComboBox *labelCombo = ui->comboBox_EncounterGroupLabel;
QStringList labelComboStrings;
for (auto groupPair : project->wildMonData[map->constantName()])
labelCombo->addItem(groupPair.first);
labelComboStrings.append(groupPair.first);
labelComboStrings.sort();
labelCombo->addItems(labelComboStrings);
labelCombo->setCurrentText(labelCombo->itemText(0));
QStackedWidget *stack = ui->stackedWidget_WildMons;
int labelIndex = 0;
for (auto labelPair : project->wildMonData[map->constantName()]) {
QString label = labelPair.first;
for (QString label : labelComboStrings) {
WildPokemonHeader header = project->wildMonData[map->constantName()][label];
MonTabWidget *tabWidget = new MonTabWidget(this);
@ -272,7 +281,7 @@ void Editor::addNewWildMonGroup(QWidget *window) {
}
});
// Give a default value to the label.
lineEdit->setText(QString("g%1%2").arg(map->name()).arg(stack->count()));
lineEdit->setText(this->project->toUniqueIdentifier("g" + map->name()));
// Fields [x] copy from existing
QLabel *fieldsLabel = new QLabel("Fields:");
@ -323,11 +332,12 @@ void Editor::addNewWildMonGroup(QWidget *window) {
header.wildMons[fieldName].encounterRate = 0;
}
MonTabWidget *tabWidget = new MonTabWidget(this);
stack->insertWidget(stack->count(), tabWidget);
QString tempItemLabel = lineEdit->text();
int newItemIndex = getSortedItemIndex(labelCombo, tempItemLabel);
labelCombo->insertItem(newItemIndex, tempItemLabel);
labelCombo->addItem(lineEdit->text());
labelCombo->setCurrentIndex(labelCombo->count() - 1);
MonTabWidget *tabWidget = new MonTabWidget(this);
int tabIndex = 0;
for (EncounterField &monField : project->wildMonFields) {
@ -353,6 +363,10 @@ void Editor::addNewWildMonGroup(QWidget *window) {
}
tabIndex++;
}
stack->insertWidget(newItemIndex, tabWidget);
labelCombo->setCurrentIndex(newItemIndex);
saveEncounterTabData();
emit wildMonTableEdited();
}
@ -865,7 +879,7 @@ void Editor::displayDivingConnection(MapConnection *connection) {
}
void Editor::renderDivingConnections() {
for (auto item : diving_map_items.values())
for (auto &item : diving_map_items)
item->updatePixmap();
}
@ -1191,6 +1205,9 @@ bool Editor::setLayout(QString layoutId) {
return false;
}
QString prevLayoutName;
if (this->layout) prevLayoutName = this->layout->name;
Layout *loadedLayout = this->project->loadLayout(layoutId);
if (!loadedLayout) {
return false;
@ -1204,7 +1221,7 @@ bool Editor::setLayout(QString layoutId) {
editGroup.addStack(&this->layout->editHistory);
map_ruler->setMapDimensions(QSize(this->layout->getWidth(), this->layout->getHeight()));
connect(this->layout, &Layout::layoutDimensionsChanged, map_ruler, &MapRuler::setMapDimensions);
connect(this->layout, &Layout::dimensionsChanged, map_ruler, &MapRuler::setMapDimensions);
ui->comboBox_PrimaryTileset->blockSignals(true);
ui->comboBox_SecondaryTileset->blockSignals(true);
@ -1218,6 +1235,9 @@ bool Editor::setLayout(QString layoutId) {
if (index < 0) index = 0;
this->ui->comboBox_LayoutSelector->setCurrentIndex(index);
if (this->layout->name != prevLayoutName)
Scripting::cb_LayoutOpened(this->layout->name);
return true;
}
@ -1696,7 +1716,7 @@ void Editor::removeEventPixmapItem(Event *event) {
}
void Editor::clearMapConnections() {
for (auto item : connection_items) {
for (auto &item : connection_items) {
if (item->scene())
item->scene()->removeItem(item);
delete item;
@ -1708,7 +1728,7 @@ void Editor::clearMapConnections() {
ui->comboBox_DiveMap->setCurrentText("");
ui->comboBox_EmergeMap->setCurrentText("");
for (auto item : diving_map_items.values()) {
for (auto &item : diving_map_items) {
if (item->scene())
item->scene()->removeItem(item);
delete item;

View File

@ -298,6 +298,7 @@ void MainWindow::initExtraSignals() {
connect(ui->action_NewMap, &QAction::triggered, this, &MainWindow::openNewMapDialog);
connect(ui->action_NewLayout, &QAction::triggered, this, &MainWindow::openNewLayoutDialog);
connect(ui->actionDuplicate_Current_Map_Layout, &QAction::triggered, this, &MainWindow::openDuplicateMapOrLayoutDialog);
connect(ui->comboBox_LayoutSelector->lineEdit(), &QLineEdit::editingFinished, this, &MainWindow::onLayoutSelectorEditingFinished);
}
void MainWindow::on_actionCheck_for_Updates_triggered() {
@ -448,6 +449,27 @@ void MainWindow::initMapList() {
ui->mapListToolBar_Locations->setEditsAllowedButtonVisible(false);
ui->mapListToolBar_Layouts->setEditsAllowedButtonVisible(false);
// Initialize settings from config
ui->mapListToolBar_Groups->setEditsAllowed(porymapConfig.mapListEditGroupsEnabled);
for (auto i = porymapConfig.mapListHideEmptyEnabled.constBegin(); i != porymapConfig.mapListHideEmptyEnabled.constEnd(); i++) {
auto toolbar = getMapListToolBar(i.key());
if (toolbar) toolbar->setEmptyFoldersVisible(!i.value());
}
// Update config if map list settings change
connect(ui->mapListToolBar_Groups, &MapListToolBar::editsAllowedChanged, [](bool allowed) {
porymapConfig.mapListEditGroupsEnabled = allowed;
});
connect(ui->mapListToolBar_Groups, &MapListToolBar::emptyFoldersVisibleChanged, [](bool visible) {
porymapConfig.mapListHideEmptyEnabled[MapListTab::Groups] = !visible;
});
connect(ui->mapListToolBar_Locations, &MapListToolBar::emptyFoldersVisibleChanged, [](bool visible) {
porymapConfig.mapListHideEmptyEnabled[MapListTab::Locations] = !visible;
});
connect(ui->mapListToolBar_Layouts, &MapListToolBar::emptyFoldersVisibleChanged, [](bool visible) {
porymapConfig.mapListHideEmptyEnabled[MapListTab::Layouts] = !visible;
});
// When map list search filter is cleared we want the current map/layout in the editor to be visible in the list.
connect(ui->mapListToolBar_Groups, &MapListToolBar::filterCleared, this, &MainWindow::scrollMapListToCurrentMap);
connect(ui->mapListToolBar_Locations, &MapListToolBar::filterCleared, this, &MainWindow::scrollMapListToCurrentMap);
@ -926,7 +948,11 @@ bool MainWindow::setMap(QString map_name) {
connect(editor->map, &Map::modified, this, &MainWindow::markMapEdited, Qt::UniqueConnection);
connect(editor->layout, &Layout::layoutChanged, this, &MainWindow::onLayoutChanged, Qt::UniqueConnection);
// If the map's MAPSEC / layout changes, update the map's position in the map list.
// These are doing more work than necessary, rather than rebuilding the entire list they should find and relocate the appropriate row.
connect(editor->map, &Map::layoutChanged, this, &MainWindow::rebuildMapList_Layouts, Qt::UniqueConnection);
connect(editor->map->header(), &MapHeader::locationChanged, this, &MainWindow::rebuildMapList_Locations, Qt::UniqueConnection);
connect(editor->layout, &Layout::needsRedrawing, this, &MainWindow::redrawMapScene, Qt::UniqueConnection);
userConfig.recentMapOrLayout = map_name;
@ -983,7 +1009,6 @@ bool MainWindow::setLayout(QString layoutId) {
connect(editor->layout, &Layout::needsRedrawing, this, &MainWindow::redrawMapScene, Qt::UniqueConnection);
Scripting::cb_MapOpened(layout->name);
updateTilesetEditor();
userConfig.recentMapOrLayout = layoutId;
@ -1064,12 +1089,37 @@ void MainWindow::displayMapProperties() {
}
void MainWindow::on_comboBox_LayoutSelector_currentTextChanged(const QString &text) {
if (editor && editor->project && editor->map) {
if (editor->project->mapLayouts.contains(text)) {
editor->map->setLayout(editor->project->loadLayout(text));
setMap(editor->map->name());
markMapEdited();
}
if (!this->editor || !this->editor->project || !this->editor->map)
return;
if (!this->editor->project->mapLayouts.contains(text)) {
// User may be in the middle of typing the name of a layout, don't bother trying to load it.
return;
}
Layout* layout = this->editor->project->loadLayout(text);
if (!layout) {
RecentErrorMessage::show(QString("Unable to set layout '%1'.").arg(text), this);
// New layout failed to load, restore previous layout
const QSignalBlocker b(ui->comboBox_LayoutSelector);
ui->comboBox_LayoutSelector->setCurrentText(this->editor->map->layout()->id);
return;
}
this->editor->map->setLayout(layout);
setMap(this->editor->map->name());
markMapEdited();
}
void MainWindow::onLayoutSelectorEditingFinished() {
if (!this->editor || !this->editor->project || !this->editor->layout)
return;
// If the user left the layout selector in an invalid state, restore it so that it displays the current layout.
const QString text = ui->comboBox_LayoutSelector->currentText();
if (!this->editor->project->mapLayouts.contains(text)) {
const QSignalBlocker b(ui->comboBox_LayoutSelector);
ui->comboBox_LayoutSelector->setCurrentText(this->editor->layout->id);
}
}
@ -1120,7 +1170,8 @@ bool MainWindow::setProjectUI() {
// map models
this->mapGroupModel = new MapGroupModel(editor->project);
this->groupListProxyModel = new FilterChildrenProxyModel();
groupListProxyModel->setSourceModel(this->mapGroupModel);
this->groupListProxyModel->setSourceModel(this->mapGroupModel);
this->groupListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Groups]);
ui->mapList->setModel(groupListProxyModel);
this->ui->mapList->setItemDelegateForColumn(0, new GroupNameDelegate(this->editor->project, this));
@ -1128,13 +1179,16 @@ bool MainWindow::setProjectUI() {
this->mapLocationModel = new MapLocationModel(editor->project);
this->locationListProxyModel = new FilterChildrenProxyModel();
locationListProxyModel->setSourceModel(this->mapLocationModel);
this->locationListProxyModel->setSourceModel(this->mapLocationModel);
this->locationListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Locations]);
ui->locationList->setModel(locationListProxyModel);
ui->locationList->sortByColumn(0, Qt::SortOrder::AscendingOrder);
this->layoutTreeModel = new LayoutTreeModel(editor->project);
this->layoutListProxyModel = new FilterChildrenProxyModel();
this->layoutListProxyModel->setSourceModel(this->layoutTreeModel);
this->layoutListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Layouts]);
ui->layoutList->setModel(layoutListProxyModel);
ui->layoutList->sortByColumn(0, Qt::SortOrder::AscendingOrder);
@ -1384,7 +1438,7 @@ void MainWindow::openNewMapDialog() {
}
void MainWindow::openDuplicateMapDialog(const QString &mapName) {
const Map *map = this->editor->project->getMap(mapName);
const Map *map = this->editor->project->loadMap(mapName);
if (map) {
auto dialog = new NewMapDialog(this->editor->project, map, this);
dialog->open();
@ -1526,6 +1580,19 @@ void MainWindow::openMapListItem(const QModelIndex &index) {
if (toolbar) toolbar->setFilterLocked(false);
}
void MainWindow::rebuildMapList_Locations() {
this->mapLocationModel->deleteLater();
this->mapLocationModel = new MapLocationModel(this->editor->project);
this->locationListProxyModel->setSourceModel(this->mapLocationModel);
resetMapListFilters();
}
void MainWindow::rebuildMapList_Layouts() {
this->layoutTreeModel->deleteLater();
this->layoutTreeModel = new LayoutTreeModel(this->editor->project);
this->layoutListProxyModel->setSourceModel(this->layoutTreeModel);
resetMapListFilters();
}
void MainWindow::updateMapList() {
// Get the name of the open map/layout (or clear the relevant selection if there is none).
QString activeItemName;
@ -1546,9 +1613,9 @@ void MainWindow::updateMapList() {
this->mapLocationModel->setActiveItem(activeItemName);
this->layoutTreeModel->setActiveItem(activeItemName);
this->groupListProxyModel->layoutChanged();
this->locationListProxyModel->layoutChanged();
this->layoutListProxyModel->layoutChanged();
emit this->groupListProxyModel->layoutChanged();
emit this->locationListProxyModel->layoutChanged();
emit this->layoutListProxyModel->layoutChanged();
}
void MainWindow::on_action_Save_Project_triggered() {
@ -1570,9 +1637,9 @@ void MainWindow::save(bool currentOnly) {
if (!porymapConfig.shownInGameReloadMessage) {
// Show a one-time warning that the user may need to reload their map to see their new changes.
static const QString message = QStringLiteral("Reload your map in-game!\n\nIf your game is currently saved on a map you have edited, "
"the changes may not appear until you leave the map and return.");
InfoMessage::show(message, this);
InfoMessage::show(QStringLiteral("Reload your map in-game!\n\nIf your game is currently saved on a map you have edited, "
"the changes may not appear until you leave the map and return."),
this);
porymapConfig.shownInGameReloadMessage = true;
}
@ -2407,10 +2474,6 @@ void MainWindow::onOpenConnectedMap(MapConnection *connection) {
editor->setSelectedConnection(connection->findMirror());
}
void MainWindow::onLayoutChanged(Layout *) {
updateMapList();
}
void MainWindow::onMapLoaded(Map *map) {
connect(map, &Map::modified, [this, map] { this->markSpecificMapEdited(map); });
}
@ -2670,8 +2733,8 @@ void MainWindow::initTilesetEditor() {
connect(this->tilesetEditor, &TilesetEditor::tilesetsSaved, this, &MainWindow::onTilesetsSaved);
}
MapListToolBar* MainWindow::getCurrentMapListToolBar() {
switch (ui->mapListContainer->currentIndex()) {
MapListToolBar* MainWindow::getMapListToolBar(int tab) {
switch (tab) {
case MapListTab::Groups: return ui->mapListToolBar_Groups;
case MapListTab::Locations: return ui->mapListToolBar_Locations;
case MapListTab::Layouts: return ui->mapListToolBar_Layouts;
@ -2679,6 +2742,10 @@ MapListToolBar* MainWindow::getCurrentMapListToolBar() {
}
}
MapListToolBar* MainWindow::getCurrentMapListToolBar() {
return getMapListToolBar(ui->mapListContainer->currentIndex());
}
MapTree* MainWindow::getCurrentMapList() {
auto toolbar = getCurrentMapListToolBar();
if (toolbar)
@ -2809,13 +2876,10 @@ void MainWindow::reloadScriptEngine() {
// Lying to the scripts here, simulating a project reload
Scripting::cb_ProjectOpened(projectConfig.projectDir);
if (this->editor) {
QString curName;
if (this->editor->layout)
Scripting::cb_LayoutOpened(this->editor->layout->name);
if (this->editor->map)
curName = this->editor->map->name();
else if (editor->layout)
curName = this->editor->layout->name;
Scripting::cb_MapOpened(curName);
Scripting::cb_MapOpened(this->editor->map->name());
}
}

View File

@ -42,7 +42,7 @@ Project::Project(QObject *parent) :
Project::~Project()
{
clearMapCache();
clearMaps();
clearTilesetCache();
clearMapLayouts();
clearEventGraphics();
@ -149,9 +149,10 @@ QString Project::getProjectTitle() const {
}
}
void Project::clearMapCache() {
qDeleteAll(this->mapCache);
this->mapCache.clear();
void Project::clearMaps() {
qDeleteAll(this->maps);
this->maps.clear();
this->loadedMapNames.clear();
}
void Project::clearTilesetCache() {
@ -159,32 +160,20 @@ void Project::clearTilesetCache() {
this->tilesetCache.clear();
}
Map* Project::loadMap(QString mapName) {
if (mapName == getDynamicMapName())
Map* Project::loadMap(const QString &mapName) {
Map* map = this->maps.value(mapName);
if (!map)
return nullptr;
Map *map;
if (mapCache.contains(mapName)) {
map = mapCache.value(mapName);
// TODO: uncomment when undo/redo history is fully implemented for all actions.
if (true/*map->hasUnsavedChanges()*/) {
return map;
}
} else {
map = new Map;
map->setName(mapName);
}
if (isMapLoaded(map))
return map;
if (!(loadMapData(map) && loadMapLayout(map))){
delete map;
if (!(loadMapData(map) && loadMapLayout(map)))
return nullptr;
}
// If the map's MAPSEC value in the header changes, update our global array to keep it in sync.
connect(map->header(), &MapHeader::locationChanged, [this, map] { this->mapNameToMapSectionName.insert(map->name(), map->header()->location()); });
mapCache.insert(mapName, map);
this->loadedMapNames.insert(mapName);
emit mapLoaded(map);
return map;
}
@ -257,11 +246,18 @@ bool Project::loadMapData(Map* map) {
// We should already know the map constant ID from the initial project launch, but we'll ensure it's correct here anyway.
map->setConstantName(ParseUtil::jsonToQString(mapObj["id"]));
this->mapNamesToMapConstants.insert(map->name(), map->constantName());
this->mapConstantsToMapNames.insert(map->constantName(), map->name());
const QString layoutId = ParseUtil::jsonToQString(mapObj["layout"]);
Layout* layout = this->mapLayouts.value(layoutId);
if (!layout) {
// We've already verified layout IDs on project launch and ignored maps with invalid IDs, so this shouldn't happen.
logError(QString("Cannot load map with unknown layout ID '%1'").arg(layoutId));
return false;
}
map->setLayout(layout);
map->header()->setSong(ParseUtil::jsonToQString(mapObj["music"]));
map->setLayoutId(ParseUtil::jsonToQString(mapObj["layout"]));
map->header()->setLocation(ParseUtil::jsonToQString(mapObj["region_map_section"]));
map->header()->setRequiresFlash(ParseUtil::jsonToBool(mapObj["requires_flash"]));
map->header()->setWeather(ParseUtil::jsonToQString(mapObj["weather"]));
@ -374,21 +370,15 @@ Map *Project::createNewMap(const Project::NewMapSettings &settings, const Map* t
}
map->setLayout(layout);
const QString location = map->header()->location();
if (!this->mapSectionIdNames.contains(location) && isValidNewIdentifier(location)) {
// Unrecognized MAPSEC name, we can automatically add a new MAPSEC for it.
addNewMapsec(location);
}
// Try to record the MAPSEC name in case this is a new name.
addNewMapsec(map->header()->location());
this->mapNames.insert(mapNamePos, map->name());
this->groupNameToMapNames[settings.group].append(map->name());
this->mapConstantsToMapNames.insert(map->constantName(), map->name());
this->mapNamesToMapConstants.insert(map->name(), map->constantName());
this->mapNameToLayoutId.insert(map->name(), map->layoutId());
this->mapNameToMapSectionName.insert(map->name(), map->header()->location());
map->setIsPersistedToFile(false);
this->mapCache.insert(map->name(), map);
this->maps.insert(map->name(), map);
emit mapCreated(map, settings.group);
@ -427,14 +417,14 @@ Layout *Project::createNewLayout(const Layout::Settings &settings, const Layout
}
// No need for a full load, we already have all the blockdata.
layout->loaded = loadLayoutTilesets(layout);
if (!layout->loaded) {
if (!loadLayoutTilesets(layout)) {
delete layout;
return nullptr;
}
this->mapLayouts.insert(layout->id, layout);
this->layoutIds.append(layout->id);
this->loadedLayoutIds.insert(layout->id);
emit layoutCreated(layout);
@ -442,14 +432,14 @@ Layout *Project::createNewLayout(const Layout::Settings &settings, const Layout
}
bool Project::loadLayout(Layout *layout) {
if (!layout->loaded) {
if (!isLayoutLoaded(layout)) {
// Force these to run even if one fails
bool loadedTilesets = loadLayoutTilesets(layout);
bool loadedBlockdata = loadBlockdata(layout);
bool loadedBorder = loadLayoutBorder(layout);
if (loadedTilesets && loadedBlockdata && loadedBorder) {
layout->loaded = true;
this->loadedLayoutIds.insert(layout->id);
return true;
} else {
return false;
@ -468,18 +458,7 @@ Layout *Project::loadLayout(QString layoutId) {
}
bool Project::loadMapLayout(Map* map) {
if (!map->isPersistedToFile()) {
return true;
}
Layout *layout = this->mapLayouts.value(map->layoutId());
if (!layout) {
logError(QString("Map '%1' has an unknown layout '%2'").arg(map->name()).arg(map->layoutId()));
return false;
}
map->setLayout(layout);
if (map->hasUnsavedChanges()) {
if (!map->isPersistedToFile() || map->hasUnsavedChanges()) {
return true;
} else {
return loadLayout(map->layout());
@ -493,6 +472,7 @@ void Project::clearMapLayouts() {
this->mapLayoutsMaster.clear();
this->layoutIds.clear();
this->layoutIdsMaster.clear();
this->loadedLayoutIds.clear();
}
bool Project::readMapLayouts() {
@ -508,11 +488,6 @@ bool Project::readMapLayouts() {
}
QJsonObject layoutsObj = layoutsDoc.object();
QJsonArray layouts = layoutsObj["layouts"].toArray();
if (layouts.size() == 0) {
logError(QString("'layouts' array is missing from %1.").arg(layoutsFilepath));
return false;
}
this->layoutsLabel = ParseUtil::jsonToQString(layoutsObj["layouts_table_label"]);
if (this->layoutsLabel.isEmpty()) {
@ -522,6 +497,7 @@ bool Project::readMapLayouts() {
.arg(layoutsLabel));
}
QJsonArray layouts = layoutsObj["layouts"].toArray();
for (int i = 0; i < layouts.size(); i++) {
QJsonObject layoutObj = layouts[i].toObject();
if (layoutObj.isEmpty())
@ -604,6 +580,11 @@ bool Project::readMapLayouts() {
this->layoutIdsMaster.append(layout->id);
}
if (this->mapLayouts.isEmpty()) {
logError(QString("Failed to read any map layouts from '%1'. At least one map layout is required.").arg(layoutsFilepath));
return false;
}
return true;
}
@ -688,7 +669,7 @@ void Project::saveMapGroups() {
for (const auto &groupName : this->groupNames) {
OrderedJson::array groupArr;
for (const auto &mapName : this->groupNameToMapNames.value(groupName)) {
if (this->mapCache.value(mapName) && !this->mapCache.value(mapName)->isPersistedToFile()) {
if (this->maps.value(mapName) && !this->maps.value(mapName)->isPersistedToFile()) {
// This is a new map that hasn't been saved yet, don't add it to the global map groups list yet.
continue;
}
@ -713,7 +694,6 @@ void Project::saveRegionMapSections() {
return;
}
const QString emptyMapsecName = getEmptyMapsecName();
OrderedJson::array mapSectionArray;
for (const auto &idName : this->mapSectionIdNamesSaveOrder) {
OrderedJson::object mapSectionObj;
@ -910,11 +890,12 @@ void Project::updateTilesetMetatileLabels(Tileset *tileset) {
// Erase old labels, then repopulate with new labels
const QString prefix = tileset->getMetatileLabelPrefix();
this->metatileLabelsMap[tileset->name].clear();
for (int metatileId : tileset->metatileLabels.keys()) {
if (tileset->metatileLabels[metatileId].isEmpty())
continue;
QString label = prefix + tileset->metatileLabels[metatileId];
this->metatileLabelsMap[tileset->name][label] = metatileId;
for (auto i = tileset->metatileLabels.constBegin(); i != tileset->metatileLabels.constEnd(); i++) {
uint16_t metatileId = i.key();
QString label = i.value();
if (!label.isEmpty()) {
this->metatileLabelsMap[tileset->name][prefix + label] = metatileId;
}
}
}
@ -953,11 +934,12 @@ void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *second
const QString guardName = "GUARD_METATILE_LABELS_H";
QString outputText = QString("#ifndef %1\n#define %1\n").arg(guardName);
for (QString tilesetName : metatileLabelsMap.keys()) {
if (metatileLabelsMap[tilesetName].size() == 0)
for (auto i = this->metatileLabelsMap.constBegin(); i != this->metatileLabelsMap.constEnd(); i++) {
const QString tilesetName = i.key();
const QMap<QString, uint16_t> tilesetMetatileLabels = i.value();
if (tilesetMetatileLabels.isEmpty())
continue;
outputText += QString("\n// %1\n").arg(tilesetName);
outputText += buildMetatileLabelsText(metatileLabelsMap[tilesetName]);
outputText += QString("\n// %1\n%2").arg(tilesetName).arg(buildMetatileLabelsText(tilesetMetatileLabels));
}
if (unusedMetatileLabels.size() != 0) {
@ -1145,7 +1127,7 @@ void Project::writeBlockdata(QString path, const Blockdata &blockdata) {
}
void Project::saveAll() {
for (auto map : this->mapCache) {
for (auto map : this->maps) {
saveMap(map, true); // Avoid double-saving the layouts
}
for (auto layout : this->mapLayouts) {
@ -1155,6 +1137,8 @@ void Project::saveAll() {
}
void Project::saveMap(Map *map, bool skipLayout) {
if (!map || !isMapLoaded(map)) return;
// Create/Modify a few collateral files for brand new maps.
const QString folderPath = projectConfig.getFilePath(ProjectFilePath::data_map_folders) + map->name();
const QString fullPath = QString("%1/%2").arg(this->root).arg(folderPath);
@ -1216,7 +1200,7 @@ void Project::saveMap(Map *map, bool skipLayout) {
OrderedJson::array connectionsArr;
for (const auto &connection : connections) {
OrderedJson::object connectionObj;
connectionObj["map"] = this->mapNamesToMapConstants.value(connection->targetMapName(), connection->targetMapName());
connectionObj["map"] = getMapConstant(connection->targetMapName(), connection->targetMapName());
connectionObj["offset"] = connection->offset();
connectionObj["direction"] = connection->direction();
connectionsArr.append(connectionObj);
@ -1286,11 +1270,14 @@ void Project::saveMap(Map *map, bool skipLayout) {
if (!skipLayout) saveLayout(map->layout());
// Try to record the MAPSEC name in case this is a new name.
addNewMapsec(map->header()->location());
map->setClean();
}
void Project::saveLayout(Layout *layout) {
if (!layout || !layout->loaded)
if (!layout || !isLayoutLoaded(layout))
return;
if (!layout->newFolderPath.isEmpty()) {
@ -1517,10 +1504,10 @@ bool Project::readTilesetMetatileLabels() {
fileWatcher.addPath(root + "/" + metatileLabelsFilename);
const QSet<QString> regexList = {QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_metatile_label_prefix))};
QMap<QString, int> defines = parser.readCDefinesByRegex(metatileLabelsFilename, regexList);
for (QString label : defines.keys()) {
uint32_t metatileId = static_cast<uint32_t>(defines[label]);
const QMap<QString, int> defines = parser.readCDefinesByRegex(metatileLabelsFilename, regexList);
for (auto i = defines.constBegin(); i != defines.constEnd(); i++) {
QString label = i.key();
uint32_t metatileId = i.value();
if (metatileId > Block::maxValue) {
metatileId &= Block::maxValue;
logWarn(QString("Value of metatile label '%1' truncated to %2").arg(label).arg(Metatile::getMetatileIdString(metatileId)));
@ -1566,15 +1553,6 @@ Blockdata Project::readBlockdata(QString path, bool *ok) {
return blockdata;
}
Map* Project::getMap(QString map_name) {
if (mapCache.contains(map_name)) {
return mapCache.value(map_name);
} else {
Map *map = loadMap(map_name);
return map;
}
}
Tileset* Project::getTileset(QString label, bool forceLoad) {
Tileset *existingTileset = nullptr;
if (tilesetCache.contains(label)) {
@ -1777,8 +1755,8 @@ bool Project::readWildMonData() {
}
bool Project::readMapGroups() {
clearMaps();
this->mapConstantsToMapNames.clear();
this->mapNamesToMapConstants.clear();
this->mapNames.clear();
this->groupNames.clear();
this->groupNameToMapNames.clear();
@ -1872,24 +1850,21 @@ bool Project::readMapGroups() {
logWarn(QString("Map '%1' has unknown \"region_map_section\" value '%2'.").arg(mapName).arg(mapSectionName));
}
// Success, save the constants to the project
// Success, create the Map object
auto map = new Map;
map->setName(mapName);
map->setConstantName(mapConstant);
map->setLayout(this->mapLayouts.value(layoutId));
map->header()->setLocation(mapSectionName);
this->maps.insert(mapName, map);
this->mapNames.append(mapName);
this->groupNameToMapNames[groupName].append(mapName);
this->mapConstantsToMapNames.insert(mapConstant, mapName);
this->mapNamesToMapConstants.insert(mapName, mapConstant);
this->mapNameToLayoutId.insert(mapName, layoutId);
this->mapNameToMapSectionName.insert(mapName, mapSectionName);
}
}
if (this->groupNames.isEmpty()) {
logError(QString("Failed to find any map groups in %1").arg(filepath));
return false;
}
if (this->mapNames.isEmpty()) {
logError(QString("Failed to find any map names in %1").arg(filepath));
return false;
}
// Note: Not successfully reading any maps or map groups is ok. We only require at least 1 map layout.
if (!failedMapNames.isEmpty()) {
// At least 1 map was excluded due to an error.
@ -1899,7 +1874,6 @@ bool Project::readMapGroups() {
// Save special "Dynamic" constant
this->mapConstantsToMapNames.insert(dynamicMapConstant, dynamicMapName);
this->mapNamesToMapConstants.insert(dynamicMapName, dynamicMapConstant);
this->mapNames.append(dynamicMapName);
return true;
@ -1916,7 +1890,7 @@ void Project::addNewMapGroup(const QString &groupName) {
emit mapGroupAdded(groupName);
}
QString Project::mapNameToMapGroup(const QString &mapName) {
QString Project::mapNameToMapGroup(const QString &mapName) const {
for (auto it = this->groupNameToMapNames.constBegin(); it != this->groupNameToMapNames.constEnd(); it++) {
const QStringList mapNames = it.value();
if (mapNames.contains(mapName)) {
@ -1926,6 +1900,23 @@ QString Project::mapNameToMapGroup(const QString &mapName) {
return QString();
}
QString Project::getMapConstant(const QString &mapName, const QString &defaultValue) const {
if (mapName == getDynamicMapName()) return getDynamicMapDefineName();
Map* map = this->maps.value(mapName);
return map ? map->constantName() : defaultValue;
}
QString Project::getMapLayoutId(const QString &mapName, const QString &defaultValue) const {
Map* map = this->maps.value(mapName);
return (map && map->layout()) ? map->layout()->id : defaultValue;
}
QString Project::getMapLocation(const QString &mapName, const QString &defaultValue) const {
Map* map = this->maps.value(mapName);
return map ? map->header()->location() : defaultValue;
}
// When we ask the user to provide a new identifier for something (like a map name or MAPSEC id)
// we use this to make sure that it doesn't collide with any known identifiers first.
// Porymap knows of many more identifiers than this, but for simplicity we only check the lists that users can add to via Porymap.
@ -1954,9 +1945,8 @@ bool Project::isIdentifierUnique(const QString &identifier) const {
if (this->encounterGroupLabels.contains(identifier))
return false;
// Check event IDs
for (const auto &map : this->mapCache) {
auto events = map->getEvents();
for (const auto &event : events) {
for (const auto &mapName : this->loadedMapNames) {
for (const auto &event : this->maps.value(mapName)->getEvents()) {
QString idName = event->getIdName();
if (!idName.isEmpty() && idName == identifier)
return false;
@ -1966,8 +1956,8 @@ bool Project::isIdentifierUnique(const QString &identifier) const {
}
// For some arbitrary string, return true if it's both a valid identifier name and not one that's already in-use.
bool Project::isValidNewIdentifier(QString identifier) const {
IdentifierValidator validator;
bool Project::isValidNewIdentifier(const QString &identifier) const {
static const IdentifierValidator validator;
return validator.isValid(identifier) && isIdentifierUnique(identifier);
}
@ -1984,7 +1974,7 @@ QString Project::toUniqueIdentifier(const QString &identifier) const {
void Project::initNewMapSettings() {
this->newMapSettings.name = QString();
this->newMapSettings.group = this->groupNames.at(0);
this->newMapSettings.group = this->groupNames.value(0);
this->newMapSettings.canFlyTo = false;
this->newMapSettings.layout.folderName = this->newMapSettings.name;
@ -2401,9 +2391,19 @@ QString Project::getMapGroupPrefix() {
return QStringLiteral("gMapGroup_");
}
// This function assumes a valid and unique name
void Project::addNewMapsec(const QString &idName) {
if (this->mapSectionIdNamesSaveOrder.last() == getEmptyMapsecName()) {
bool Project::addNewMapsec(const QString &idName, const QString &displayName) {
if (this->mapSectionIdNames.contains(idName)) {
// Already added
return false;
}
IdentifierValidator validator(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix));
if (!validator.isValid(idName)) {
logWarn(QString("Cannot add new MAPSEC with invalid name '%1'").arg(idName));
return false;
}
if (!this->mapSectionIdNamesSaveOrder.isEmpty() && this->mapSectionIdNamesSaveOrder.last() == getEmptyMapsecName()) {
// If the default map section name (MAPSEC_NONE) is last in the list we'll keep it last in the list.
this->mapSectionIdNamesSaveOrder.insert(this->mapSectionIdNames.length() - 1, idName);
} else {
@ -2417,6 +2417,8 @@ void Project::addNewMapsec(const QString &idName) {
emit mapSectionAdded(idName);
emit mapSectionIdNamesChanged(this->mapSectionIdNames);
if (!displayName.isEmpty()) setMapsecDisplayName(idName, displayName);
return true;
}
void Project::removeMapsec(const QString &idName) {
@ -3246,16 +3248,14 @@ bool Project::hasUnsavedChanges() {
return true;
// Check layouts for unsaved changes
for (auto i = this->mapLayouts.constBegin(); i != this->mapLayouts.constEnd(); i++) {
auto layout = i.value();
if (layout && layout->hasUnsavedChanges())
for (const auto &layout : this->mapLayouts) {
if (layout->hasUnsavedChanges())
return true;
}
// Check loaded maps for unsaved changes
for (auto i = this->mapCache.constBegin(); i != this->mapCache.constEnd(); i++) {
auto map = i.value();
if (map && map->hasUnsavedChanges())
// Check maps for unsaved changes
for (const auto &map : this->maps) {
if (map->hasUnsavedChanges())
return true;
}
return false;

View File

@ -2,15 +2,21 @@
#include "scripting.h"
#include "imageproviders.h"
void MapView::updateScene() {
if (this->scene()) {
this->scene()->update();
}
}
void MapView::clear(int layer) {
this->getOverlay(layer)->clearItems();
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, clear all layers
void MapView::clear() {
this->clearOverlayMap();
this->scene()->update();
this->updateScene();
}
void MapView::hide(int layer) {
@ -37,14 +43,14 @@ bool MapView::getVisibility(int layer) {
void MapView::setVisibility(bool visible, int layer) {
this->getOverlay(layer)->setHidden(!visible);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set visibility of all layers
void MapView::setVisibility(bool visible) {
foreach (Overlay * layer, this->overlayMap)
layer->setHidden(!visible);
this->scene()->update();
this->updateScene();
}
int MapView::getX(int layer) {
@ -57,49 +63,49 @@ int MapView::getY(int layer) {
void MapView::setX(int x, int layer) {
this->getOverlay(layer)->setX(x);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set x of all layers
void MapView::setX(int x) {
foreach (Overlay * layer, this->overlayMap)
layer->setX(x);
this->scene()->update();
this->updateScene();
}
void MapView::setY(int y, int layer) {
this->getOverlay(layer)->setY(y);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set y of all layers
void MapView::setY(int y) {
foreach (Overlay * layer, this->overlayMap)
layer->setY(y);
this->scene()->update();
this->updateScene();
}
void MapView::setClippingRect(int x, int y, int width, int height, int layer) {
this->getOverlay(layer)->setClippingRect(QRectF(x, y, width, height));
this->scene()->update();
this->updateScene();
}
void MapView::setClippingRect(int x, int y, int width, int height) {
QRectF rect = QRectF(x, y, width, height);
foreach (Overlay * layer, this->overlayMap)
layer->setClippingRect(rect);
this->scene()->update();
this->updateScene();
}
void MapView::clearClippingRect(int layer) {
this->getOverlay(layer)->clearClippingRect();
this->scene()->update();
this->updateScene();
}
void MapView::clearClippingRect() {
foreach (Overlay * layer, this->overlayMap)
layer->clearClippingRect();
this->scene()->update();
this->updateScene();
}
QJSValue MapView::getPosition(int layer) {
@ -109,26 +115,26 @@ QJSValue MapView::getPosition(int layer) {
void MapView::setPosition(int x, int y, int layer) {
this->getOverlay(layer)->setPosition(x, y);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set position of all layers
void MapView::setPosition(int x, int y) {
foreach (Overlay * layer, this->overlayMap)
layer->setPosition(x, y);
this->scene()->update();
this->updateScene();
}
void MapView::move(int deltaX, int deltaY, int layer) {
this->getOverlay(layer)->move(deltaX, deltaY);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, move all layers
void MapView::move(int deltaX, int deltaY) {
foreach (Overlay * layer, this->overlayMap)
layer->move(deltaX, deltaY);
this->scene()->update();
this->updateScene();
}
int MapView::getOpacity(int layer) {
@ -137,14 +143,14 @@ int MapView::getOpacity(int layer) {
void MapView::setOpacity(int opacity, int layer) {
this->getOverlay(layer)->setOpacity(opacity);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set opacity of all layers
void MapView::setOpacity(int opacity) {
foreach (Overlay * layer, this->overlayMap)
layer->setOpacity(opacity);
this->scene()->update();
this->updateScene();
}
qreal MapView::getHorizontalScale(int layer) {
@ -157,38 +163,38 @@ qreal MapView::getVerticalScale(int layer) {
void MapView::setHorizontalScale(qreal scale, int layer) {
this->getOverlay(layer)->setHScale(scale);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set horizontal scale of all layers
void MapView::setHorizontalScale(qreal scale) {
foreach (Overlay * layer, this->overlayMap)
layer->setHScale(scale);
this->scene()->update();
this->updateScene();
}
void MapView::setVerticalScale(qreal scale, int layer) {
this->getOverlay(layer)->setVScale(scale);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set vertical scale of all layers
void MapView::setVerticalScale(qreal scale) {
foreach (Overlay * layer, this->overlayMap)
layer->setVScale(scale);
this->scene()->update();
this->updateScene();
}
void MapView::setScale(qreal hScale, qreal vScale, int layer) {
this->getOverlay(layer)->setScale(hScale, vScale);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set scale of all layers
void MapView::setScale(qreal hScale, qreal vScale) {
foreach (Overlay * layer, this->overlayMap)
layer->setScale(hScale, vScale);
this->scene()->update();
this->updateScene();
}
int MapView::getRotation(int layer) {
@ -197,41 +203,41 @@ int MapView::getRotation(int layer) {
void MapView::setRotation(int angle, int layer) {
this->getOverlay(layer)->setRotation(angle);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, set rotation of all layers
void MapView::setRotation(int angle) {
foreach (Overlay * layer, this->overlayMap)
layer->setRotation(angle);
this->scene()->update();
this->updateScene();
}
void MapView::rotate(int degrees, int layer) {
this->getOverlay(layer)->rotate(degrees);
this->scene()->update();
this->updateScene();
}
// Overload. No layer provided, rotate all layers
void MapView::rotate(int degrees) {
foreach (Overlay * layer, this->overlayMap)
layer->rotate(degrees);
this->scene()->update();
this->updateScene();
}
void MapView::addText(QString text, int x, int y, QString color, int fontSize, int layer) {
this->getOverlay(layer)->addText(text, x, y, color, fontSize);
this->scene()->update();
this->updateScene();
}
void MapView::addRect(int x, int y, int width, int height, QString borderColor, QString fillColor, int rounding, int layer) {
if (this->getOverlay(layer)->addRect(x, y, width, height, borderColor, fillColor, rounding))
this->scene()->update();
this->updateScene();
}
void MapView::addPath(QList<int> xCoords, QList<int> yCoords, QString borderColor, QString fillColor, int layer) {
if (this->getOverlay(layer)->addPath(xCoords, yCoords, borderColor, fillColor))
this->scene()->update();
this->updateScene();
}
void MapView::addPath(QList<QList<int>> coords, QString borderColor, QString fillColor, int layer) {
@ -250,7 +256,7 @@ void MapView::addPath(QList<QList<int>> coords, QString borderColor, QString fil
void MapView::addImage(int x, int y, QString filepath, int layer, bool useCache) {
if (this->getOverlay(layer)->addImage(x, y, filepath, useCache))
this->scene()->update();
this->updateScene();
}
void MapView::createImage(int x, int y, QString filepath, int width, int height, int xOffset, int yOffset, qreal hScale, qreal vScale, int paletteId, bool setTransparency, int layer, bool useCache) {
@ -260,7 +266,7 @@ void MapView::createImage(int x, int y, QString filepath, int width, int height,
if (paletteId != -1)
palette = Tileset::getPalette(paletteId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
if (this->getOverlay(layer)->addImage(x, y, filepath, useCache, width, height, xOffset, yOffset, hScale, vScale, palette, setTransparency))
this->scene()->update();
this->updateScene();
}
void MapView::addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int paletteId, bool setTransparency, int layer) {
@ -274,7 +280,7 @@ void MapView::addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int
if (setTransparency)
image.setColor(0, qRgba(0, 0, 0, 0));
if (this->getOverlay(layer)->addImage(x, y, image))
this->scene()->update();
this->updateScene();
}
void MapView::addTileImage(int x, int y, QJSValue tileObj, bool setTransparency, int layer) {
@ -293,5 +299,5 @@ void MapView::addMetatileImage(int x, int y, int metatileId, bool setTransparenc
if (setTransparency)
image.setColor(0, qRgba(0, 0, 0, 0));
if (this->getOverlay(layer)->addImage(x, y, image))
this->scene()->update();
this->updateScene();
}

View File

@ -12,6 +12,7 @@ const QMap<CallbackType, QString> callbackFunctions = {
{OnBlockHoverChanged, "onBlockHoverChanged"},
{OnBlockHoverCleared, "onBlockHoverCleared"},
{OnMapOpened, "onMapOpened"},
{OnLayoutOpened, "onLayoutOpened"},
{OnMapResized, "onMapResized"},
{OnBorderResized, "onBorderResized"},
{OnMapShifted, "onMapShifted"},
@ -258,6 +259,15 @@ void Scripting::cb_MapOpened(QString mapName) {
instance->invokeCallback(OnMapOpened, args);
}
void Scripting::cb_LayoutOpened(QString layoutName) {
if (!instance) return;
QJSValueList args {
layoutName,
};
instance->invokeCallback(OnLayoutOpened, args);
}
void Scripting::cb_MapResized(int oldWidth, int oldHeight, int newWidth, int newHeight) {
if (!instance) return;

View File

@ -104,7 +104,7 @@ QStandardItem *MapListModel::createMapItem(const QString &mapName, QStandardItem
map->setData(mapName, MapListUserRoles::NameRole);
map->setData("map_name", MapListUserRoles::TypeRole);
map->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemNeverHasChildren);
map->setToolTip(this->project->mapNamesToMapConstants.value(mapName));
map->setToolTip(this->project->getMapConstant(mapName));
this->mapItems.insert(mapName, map);
return map;
}
@ -164,10 +164,10 @@ QVariant MapListModel::data(const QModelIndex &index, int role) const {
if (name == this->activeItemName)
return this->mapOpenedIcon;
const Map* map = this->project->mapCache.value(name);
if (!map)
const Map* map = this->project->getMap(name);
if (!this->project->isMapLoaded(map))
return this->mapGrayIcon;
return map->hasUnsavedChanges() ? this->mapEditedIcon : this->mapIcon;
return map->hasUnsavedChanges() ? this->mapEditedIcon : this->mapIcon;
} else if (type == this->folderTypeName) {
// Decorating map folder in the map list
return item->hasChildren() ? this->mapFolderIcon : this->emptyMapFolderIcon;
@ -446,7 +446,7 @@ MapLocationModel::MapLocationModel(Project *project, QObject *parent) : MapListM
insertMapFolderItem(idName);
}
for (const auto &mapName : this->project->mapNames) {
insertMapItem(mapName, this->project->mapNameToMapSectionName.value(mapName));
insertMapItem(mapName, this->project->getMapLocation(mapName));
}
}
@ -470,7 +470,7 @@ LayoutTreeModel::LayoutTreeModel(Project *project, QObject *parent) : MapListMod
insertMapFolderItem(layoutId);
}
for (const auto &mapName : this->project->mapNames) {
insertMapItem(mapName, this->project->mapNameToLayoutId.value(mapName));
insertMapItem(mapName, this->project->getMapLayoutId(mapName));
}
}
@ -510,7 +510,7 @@ QVariant LayoutTreeModel::data(const QModelIndex &index, int role) const {
return this->mapOpenedIcon;
const Layout* layout = this->project->mapLayouts.value(name);
if (!layout || !layout->loaded)
if (!this->project->isLayoutLoaded(layout))
return this->mapGrayIcon;
return layout->hasUnsavedChanges() ? this->mapEditedIcon : this->mapIcon;
}

View File

@ -4,11 +4,6 @@
#include <QToolTip>
/*
TODO: The button states for each tool bar (just the two toggleable buttons, hide empty folders and allow editing)
should be saved in the config. This will be cleaner/easier once the config is JSON, so holding off on that for now.
*/
MapListToolBar::MapListToolBar(QWidget *parent)
: QFrame(parent)
, ui(new Ui::MapListToolBar)
@ -18,7 +13,7 @@ MapListToolBar::MapListToolBar(QWidget *parent)
ui->button_ToggleEmptyFolders->setChecked(!m_emptyFoldersVisible);
ui->button_ToggleEdit->setChecked(m_editsAllowed);
connect(ui->button_AddFolder, &QAbstractButton::clicked, this, &MapListToolBar::addFolderClicked); // TODO: Tool tip
connect(ui->button_AddFolder, &QAbstractButton::clicked, this, &MapListToolBar::addFolderClicked);
connect(ui->button_ExpandAll, &QAbstractButton::clicked, this, &MapListToolBar::expandList);
connect(ui->button_CollapseAll, &QAbstractButton::clicked, this, &MapListToolBar::collapseList);
connect(ui->button_ToggleEdit, &QAbstractButton::clicked, this, &MapListToolBar::toggleEditsAllowed);
@ -57,28 +52,30 @@ void MapListToolBar::toggleEditsAllowed() {
}
void MapListToolBar::setEditsAllowed(bool allowed) {
m_editsAllowed = allowed;
if (m_list) {
if (allowed) {
m_list->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_list->setDragEnabled(true);
m_list->setAcceptDrops(true);
m_list->setDropIndicatorShown(true);
m_list->setDragDropMode(QAbstractItemView::InternalMove);
m_list->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
} else {
m_list->setSelectionMode(QAbstractItemView::NoSelection);
m_list->setDragEnabled(false);
m_list->setAcceptDrops(false);
m_list->setDropIndicatorShown(false);
m_list->setDragDropMode(QAbstractItemView::NoDragDrop);
m_list->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
}
const QSignalBlocker b(ui->button_ToggleEdit);
ui->button_ToggleEdit->setChecked(allowed);
const QSignalBlocker b(ui->button_ToggleEdit);
ui->button_ToggleEdit->setChecked(allowed);
if (!m_list)
return;
if (allowed) {
m_list->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_list->setDragEnabled(true);
m_list->setAcceptDrops(true);
m_list->setDropIndicatorShown(true);
m_list->setDragDropMode(QAbstractItemView::InternalMove);
m_list->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
} else {
m_list->setSelectionMode(QAbstractItemView::NoSelection);
m_list->setDragEnabled(false);
m_list->setAcceptDrops(false);
m_list->setDropIndicatorShown(false);
m_list->setDragDropMode(QAbstractItemView::NoDragDrop);
m_list->setEditTriggers(QAbstractItemView::NoEditTriggers);
if (m_editsAllowed != allowed) {
m_editsAllowed = allowed;
emit editsAllowedChanged(allowed);
}
}
@ -87,8 +84,6 @@ void MapListToolBar::toggleEmptyFolders() {
}
void MapListToolBar::setEmptyFoldersVisible(bool visible) {
m_emptyFoldersVisible = visible;
if (m_list) {
auto model = static_cast<FilterChildrenProxyModel*>(m_list->model());
if (model) {
@ -103,6 +98,11 @@ void MapListToolBar::setEmptyFoldersVisible(bool visible) {
const QSignalBlocker b(ui->button_ToggleEmptyFolders);
ui->button_ToggleEmptyFolders->setChecked(!visible);
if (m_emptyFoldersVisible != visible) {
m_emptyFoldersVisible = visible;
emit emptyFoldersVisibleChanged(visible);
}
}
void MapListToolBar::expandList() {

View File

@ -69,11 +69,7 @@ void NewLocationDialog::accept() {
if (!validateIdName())
return;
const QString idName = ui->lineEdit_IdName->text();
const QString displayName = ui->lineEdit_DisplayName->text();
this->project->addNewMapsec(idName);
this->project->setMapsecDisplayName(idName, displayName);
this->project->addNewMapsec(ui->lineEdit_IdName->text(), ui->lineEdit_DisplayName->text());
QDialog::accept();
}

View File

@ -172,7 +172,7 @@ void ResizeLayoutPopup::setupLayoutView() {
scene->addItem(outline);
layoutPixmap->setBoundary(outline);
this->outline->rectUpdated(outline->rect().toAlignedRect());
emit this->outline->rectUpdated(outline->rect().toAlignedRect());
// TODO: is this an ideal size for all maps, or should this adjust based on starting dimensions?
this->ui->graphicsView->setTransform(QTransform::fromScale(0.5, 0.5));

View File

@ -134,8 +134,8 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi
void TilesetEditor::setAttributesUi() {
// Behavior
if (projectConfig.metatileBehaviorMask) {
for (int num : project->metatileBehaviorMapInverse.keys()) {
this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num);
for (auto i = project->metatileBehaviorMapInverse.constBegin(); i != project->metatileBehaviorMapInverse.constEnd(); i++) {
this->ui->comboBox_metatileBehaviors->addItem(i.value(), i.key());
}
this->ui->comboBox_metatileBehaviors->setMinimumContentsLength(0);
} else {
@ -1125,7 +1125,7 @@ void TilesetEditor::countTileUsage() {
QSet<Tileset*> primaryTilesets;
QSet<Tileset*> secondaryTilesets;
for (auto layout : this->project->mapLayouts.values()) {
for (auto &layout : this->project->mapLayouts) {
this->project->loadLayoutTilesets(layout);
if (layout->tileset_primary_label == this->primaryTileset->name
|| layout->tileset_secondary_label == this->secondaryTileset->name) {