Merge branch 'master' of https://github.com/huderlem/porymap into slam

This commit is contained in:
garak 2024-01-09 15:34:31 -05:00
commit c0f32c6a17
15 changed files with 209 additions and 126 deletions

View file

@ -12,11 +12,14 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Add `metatile_behaviors`, `num_primary_palettes`, and `num_secondary_palettes` to `constants` in the API.
### Changed
- The API functions `addImage` and `createImage` now support project-relative paths.
- Metatile ID strings are now padded to their current max, not the overall max.
- The name of the Heal Locations table is no longer enforced.
- The API functions `addImage` and `createImage` now support project-relative paths.
### Fixed
- Fix the metatile selector rectangle jumping when selecting up or left of the origin.
- Fix the event group tabs sometimes showing an event from the wrong group
- Fix the clear buttons in the Shortcuts Editor not actually removing shortcuts.
- Fix API error reporting
## [5.2.0] - 2024-01-02

View file

@ -86,10 +86,11 @@ In addition to these files, there are some specific symbol and macro names that
``symbol_obj_event_gfx_pointers``, ``gObjectEventGraphicsInfoPointers``, to map Object Event graphics IDs to graphics data
``symbol_pokemon_icon_table``, ``gMonIconTable``, to map species constants to icon images
``symbol_wild_encounters``, ``gWildMonHeaders``, output as the ``label`` property for the top-level wild ecounters JSON object
``symbol_heal_locations``, ``sHealLocations``, only if ``Respawn Map/NPC`` is disabled
``symbol_spawn_points``, ``sSpawnPoints``, only if ``Respawn Map/NPC`` is enabled
``symbol_spawn_maps``, ``sWhiteoutRespawnHealCenterMapIdxs``, values for Heal Locations ``Respawn Map`` field
``symbol_spawn_npcs``, ``sWhiteoutRespawnHealerNpcIds``, values for Heal Locations ``Respawn NPC`` field
``symbol_heal_locations_type``, ``struct HealLocation``, the type for the Heal Locations table
``symbol_heal_locations``, ``sHealLocations``, the default Heal Locations table name when ``Respawn Map/NPC`` is disabled
``symbol_spawn_points``, ``sSpawnPoints``, the default Heal Locations table name when ``Respawn Map/NPC`` is enabled
``symbol_spawn_maps``, ``u16 sWhiteoutRespawnHealCenterMapIdxs``, the type and table name for Heal Location ``Respawn Map`` values
``symbol_spawn_npcs``, ``u8 sWhiteoutRespawnHealerNpcIds``, the type and table name for Heal Location ``Respawn NPC`` values
``symbol_attribute_table``, ``sMetatileAttrMasks``, optionally read to get settings on ``Tilesets`` tab
``symbol_tilesets_prefix``, ``gTileset_``, for new tileset names and to extract base tileset names
``define_obj_event_count``, ``OBJECT_EVENT_TEMPLATES_COUNT``, to limit total Object Events

View file

@ -189,6 +189,7 @@ enum ProjectIdentifier {
symbol_obj_event_gfx_pointers,
symbol_pokemon_icon_table,
symbol_wild_encounters,
symbol_heal_locations_type,
symbol_heal_locations,
symbol_spawn_points,
symbol_spawn_maps,

View file

@ -373,7 +373,8 @@ private:
void openSubWindow(QWidget * window);
void scrollTreeView(QString itemName);
QString getExistingDirectory(QString);
bool openProject(QString dir);
bool openProject(const QString &dir, bool initial = false);
void showProjectOpenFailure();
QString getDefaultMap();
QString getDefaultLayout();
void setRecentMapConfig(QString map_name);
@ -406,7 +407,6 @@ private:
void applyMapListFilter(QString filterText);
void restoreWindowState();
void setTheme(QString);
bool openRecentProject();
void updateTilesetEditor();
Event::Group getEventGroupFromTabWidget(QWidget *tab);
void closeSupplementaryWindows();

View file

@ -95,6 +95,7 @@ public:
};
DataQualifiers getDataQualifiers(QString, QString);
DataQualifiers healLocationDataQualifiers;
QString healLocationsTableName;
QMap<QString, Map*> mapCache;
Map* loadMap(QString);
@ -243,7 +244,6 @@ private:
void saveHealLocationsData(Map *map);
void saveHealLocationsConstants();
QString getHealLocationsTableName();
void ignoreWatchedFileTemporarily(QString filepath);

View file

@ -14,25 +14,33 @@ public:
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->clearLastModifiedCoords();
this->clearLastHoveredCoords();
setAcceptHoverEvents(true);
}
void draw();
void setTilesets(Tileset*, Tileset*);
void setMetatile(Metatile*);
void clearLastModifiedCoords();
void clearLastHoveredCoords();
bool showGrid;
private:
Metatile* metatile;
Tileset *primaryTileset;
Tileset *secondaryTileset;
QPoint prevChangedTile;
void getBoundedCoords(QPointF, int*, int*);
QPoint prevChangedPos;
QPoint prevHoveredPos;
QPoint getBoundedPos(const QPointF &);
signals:
void tileChanged(int, int);
void selectedTilesChanged(QPoint, int, int);
void hoveredTileChanged(uint16_t);
void hoveredTileCleared();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
void hoverMoveEvent(QGraphicsSceneHoverEvent*);
void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
};
#endif // METATILELAYERSITEM_H

View file

@ -39,6 +39,7 @@ public:
this->primaryTileset = layout->tileset_primary;
this->secondaryTileset = layout->tileset_secondary;
this->selection = MetatileSelection{};
this->cellPos = QPoint(-1, -1);
setAcceptHoverEvents(true);
}
QPoint getSelectionDimensions();
@ -68,13 +69,15 @@ private:
int externalSelectionHeight;
QList<uint16_t> externalSelectedMetatiles;
MetatileSelection selection;
QPoint cellPos;
void updateSelectedMetatiles();
void updateExternalSelectedMetatiles();
uint16_t getMetatileId(int x, int y);
uint16_t getMetatileId(int x, int y) const;
QPoint getMetatileIdCoords(uint16_t);
bool shouldAcceptEvent(QGraphicsSceneMouseEvent*);
bool positionIsValid(const QPoint &pos) const;
bool selectionIsValid();
void hoverChanged();
signals:
void hoveredMetatileSelectionChanged(uint16_t);

View file

@ -74,10 +74,11 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{ProjectIdentifier::symbol_obj_event_gfx_pointers, {"symbol_obj_event_gfx_pointers", "gObjectEventGraphicsInfoPointers"}},
{ProjectIdentifier::symbol_pokemon_icon_table, {"symbol_pokemon_icon_table", "gMonIconTable"}},
{ProjectIdentifier::symbol_wild_encounters, {"symbol_wild_encounters", "gWildMonHeaders"}},
{ProjectIdentifier::symbol_heal_locations_type, {"symbol_heal_locations_type", "struct HealLocation"}},
{ProjectIdentifier::symbol_heal_locations, {"symbol_heal_locations", "sHealLocations"}},
{ProjectIdentifier::symbol_spawn_points, {"symbol_spawn_points", "sSpawnPoints"}},
{ProjectIdentifier::symbol_spawn_maps, {"symbol_spawn_maps", "sWhiteoutRespawnHealCenterMapIdxs"}},
{ProjectIdentifier::symbol_spawn_npcs, {"symbol_spawn_npcs", "sWhiteoutRespawnHealerNpcIds"}},
{ProjectIdentifier::symbol_spawn_maps, {"symbol_spawn_maps", "u16 sWhiteoutRespawnHealCenterMapIdxs"}},
{ProjectIdentifier::symbol_spawn_npcs, {"symbol_spawn_npcs", "u8 sWhiteoutRespawnHealerNpcIds"}},
{ProjectIdentifier::symbol_attribute_table, {"symbol_attribute_table", "sMetatileAttrMasks"}},
{ProjectIdentifier::symbol_tilesets_prefix, {"symbol_tilesets_prefix", "gTileset_"}},
// Defines

View file

@ -62,7 +62,7 @@ MainWindow::MainWindow(QWidget *parent) :
cleanupLargeLog();
this->initWindow();
if (this->openRecentProject())
if (porymapConfig.getReopenOnLaunch() && this->openProject(porymapConfig.getRecentProject(), true))
on_toolButton_Paint_clicked();
// there is a bug affecting macOS users, where the trackpad deilveres a bad touch-release gesture
@ -492,35 +492,29 @@ void MainWindow::setTheme(QString theme) {
}
}
bool MainWindow::openRecentProject() {
if (!porymapConfig.getReopenOnLaunch())
return false;
QString default_dir = porymapConfig.getRecentProject();
if (default_dir.isNull() || default_dir.length() <= 0)
return false;
if (!QDir(default_dir).exists()) {
QString message = QString("Recent project directory '%1' doesn't exist.").arg(QDir::toNativeSeparators(default_dir));
logWarn(message);
this->statusBar()->showMessage(message);
return false;
}
logInfo(QString("Opening recent project: '%1'").arg(default_dir));
return openProject(default_dir);
}
bool MainWindow::openProject(QString dir) {
if (dir.isNull()) {
bool MainWindow::openProject(const QString &dir, bool initial) {
if (dir.isNull() || dir.length() <= 0) {
projectOpenFailure = true;
setWindowDisabled(true);
if (!initial) setWindowDisabled(true);
return false;
}
QString nativeDir = QDir::toNativeSeparators(dir);
const QString projectString = QString("%1project '%2'").arg(initial ? "recent " : "").arg(QDir::toNativeSeparators(dir));
this->statusBar()->showMessage(QString("Opening project %1").arg(nativeDir));
if (!QDir(dir).exists()) {
projectOpenFailure = true;
const QString errorMsg = QString("Failed to open %1: No such directory").arg(projectString);
this->statusBar()->showMessage(errorMsg);
if (initial) {
// Graceful startup if recent project directory is missing
logWarn(errorMsg);
} else {
logError(errorMsg);
showProjectOpenFailure();
}
return false;
}
this->statusBar()->showMessage(QString("Opening %1").arg(projectString));
userConfig.setProjectDir(dir);
userConfig.load();
@ -530,7 +524,10 @@ bool MainWindow::openProject(QString dir) {
this->closeSupplementaryWindows();
this->newMapDefaultsSet = false;
if (isProjectOpen())
Scripting::cb_ProjectClosed(editor->project->root);
Scripting::init(this);
bool already_open = isProjectOpen() && (editor->project->root == dir);
if (!already_open) {
editor->closeProject();
@ -555,19 +552,16 @@ bool MainWindow::openProject(QString dir) {
&& setInitialMap());
if (this->projectOpenFailure) {
this->statusBar()->showMessage(QString("Failed to open project %1").arg(nativeDir));
QMessageBox msgBox(this);
QString errorMsg = QString("There was an error opening the project %1. Please see %2 for full error details.\n\n%3")
.arg(dir)
.arg(getLogPath())
.arg(getMostRecentError());
msgBox.critical(nullptr, "Error Opening Project", errorMsg);
setWindowDisabled(true);
this->statusBar()->showMessage(QString("Failed to open %1").arg(projectString));
showProjectOpenFailure();
return false;
}
showWindowTitle();
this->statusBar()->showMessage(QString("Opened project %1").arg(nativeDir));
const QString successMessage = QString("Opened %1").arg(projectString);
this->statusBar()->showMessage(successMessage);
logInfo(successMessage);
porymapConfig.addRecentProject(dir);
refreshRecentProjectsMenu();
@ -582,6 +576,14 @@ bool MainWindow::openProject(QString dir) {
return true;
}
void MainWindow::showProjectOpenFailure() {
QString errorMsg = QString("There was an error opening the project. Please see %1 for full error details.").arg(getLogPath());
QMessageBox error(QMessageBox::Critical, "porymap", errorMsg, QMessageBox::Ok, this);
error.setDetailedText(getMostRecentError());
error.exec();
setWindowDisabled(true);
}
bool MainWindow::isProjectOpen() {
return !projectOpenFailure && editor && editor->project;
}
@ -662,17 +664,22 @@ void MainWindow::refreshRecentProjectsMenu() {
recentProjects.removeOne(this->editor->project->root);
}
// Add project paths to menu. Arbitrary limit of 10 items.
const int numItems = qMin(10, recentProjects.length());
for (int i = 0; i < numItems; i++) {
// Add project paths to menu. Skip any paths to folders that don't exist
for (int i = 0; i < recentProjects.length(); i++) {
const QString path = recentProjects.at(i);
if (QDir(path).exists()) {
ui->menuOpen_Recent_Project->addAction(path, [this, path](){
this->openProject(path);
});
}
// Arbitrary limit of 10 items.
if (ui->menuOpen_Recent_Project->actions().length() >= 10)
break;
}
// Add action to clear list of paths
if (!recentProjects.isEmpty()) ui->menuOpen_Recent_Project->addSeparator();
if (!ui->menuOpen_Recent_Project->actions().isEmpty())
ui->menuOpen_Recent_Project->addSeparator();
QAction *clearAction = ui->menuOpen_Recent_Project->addAction("Clear Items", [this](){
QStringList paths = QStringList();
if (isProjectOpen())
@ -719,14 +726,9 @@ void MainWindow::on_action_Open_Project_triggered()
recent = userConfig.getRecentMap();
}
QString dir = getExistingDirectory(recent);
if (!dir.isEmpty()) {
if (this->editor && this->editor->project) {
Scripting::cb_ProjectClosed(this->editor->project->root);
this->ui->graphicsView_Map->clearOverlayMap();
}
if (!dir.isEmpty())
openProject(dir);
}
}
void MainWindow::on_action_Reload_Project_triggered() {
// TODO: when undo history is complete show only if has unsaved changes
@ -2349,6 +2351,7 @@ void MainWindow::on_toolButton_Paint_clicked()
ui->graphicsView_Map->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QScroller::ungrabGesture(ui->graphicsView_Map);
ui->graphicsView_Map->setViewportUpdateMode(QGraphicsView::ViewportUpdateMode::MinimalViewportUpdate);
ui->graphicsView_Map->setFocus();
checkToolButtons();
}
@ -2367,6 +2370,7 @@ void MainWindow::on_toolButton_Select_clicked()
ui->graphicsView_Map->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QScroller::ungrabGesture(ui->graphicsView_Map);
ui->graphicsView_Map->setViewportUpdateMode(QGraphicsView::ViewportUpdateMode::MinimalViewportUpdate);
ui->graphicsView_Map->setFocus();
checkToolButtons();
}
@ -2385,6 +2389,7 @@ void MainWindow::on_toolButton_Fill_clicked()
ui->graphicsView_Map->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QScroller::ungrabGesture(ui->graphicsView_Map);
ui->graphicsView_Map->setViewportUpdateMode(QGraphicsView::ViewportUpdateMode::MinimalViewportUpdate);
ui->graphicsView_Map->setFocus();
checkToolButtons();
}
@ -2403,6 +2408,7 @@ void MainWindow::on_toolButton_Dropper_clicked()
ui->graphicsView_Map->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QScroller::ungrabGesture(ui->graphicsView_Map);
ui->graphicsView_Map->setViewportUpdateMode(QGraphicsView::ViewportUpdateMode::MinimalViewportUpdate);
ui->graphicsView_Map->setFocus();
checkToolButtons();
}
@ -2421,6 +2427,7 @@ void MainWindow::on_toolButton_Move_clicked()
ui->graphicsView_Map->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QScroller::grabGesture(ui->graphicsView_Map, QScroller::LeftMouseButtonGesture);
ui->graphicsView_Map->setViewportUpdateMode(QGraphicsView::ViewportUpdateMode::FullViewportUpdate);
ui->graphicsView_Map->setFocus();
checkToolButtons();
}
@ -2439,6 +2446,7 @@ void MainWindow::on_toolButton_Shift_clicked()
ui->graphicsView_Map->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QScroller::ungrabGesture(ui->graphicsView_Map);
ui->graphicsView_Map->setViewportUpdateMode(QGraphicsView::ViewportUpdateMode::MinimalViewportUpdate);
ui->graphicsView_Map->setFocus();
checkToolButtons();
}
@ -2942,7 +2950,6 @@ void MainWindow::initCustomScriptsEditor() {
void MainWindow::reloadScriptEngine() {
Scripting::init(this);
this->ui->graphicsView_Map->clearOverlayMap();
Scripting::populateGlobalObject(this);
// Lying to the scripts here, simulating a project reload
Scripting::cb_ProjectOpened(projectConfig.getProjectDir());

View file

@ -781,12 +781,6 @@ void Project::saveHealLocations(Map *map) {
this->saveHealLocationsConstants();
}
QString Project::getHealLocationsTableName() {
if (projectConfig.getHealLocationRespawnDataEnabled())
return projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_points);
return projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations);
}
// Saves heal location maps/coords/respawn data in root + /src/data/heal_locations.h
void Project::saveHealLocationsData(Map *map) {
// Update heal locations from map
@ -813,11 +807,13 @@ void Project::saveHealLocationsData(Map *map) {
const QString qualifiers = QString(healLocationDataQualifiers.isStatic ? "static " : "")
+ QString(healLocationDataQualifiers.isConst ? "const " : "");
QString locationTableText = QString("%1struct HealLocation %2[] =\n{\n").arg(qualifiers).arg(this->getHealLocationsTableName());
QString locationTableText = QString("%1%2 %3[] =\n{\n").arg(qualifiers)
.arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations_type))
.arg(this->healLocationsTableName);
QString respawnMapTableText, respawnNPCTableText;
if (respawnEnabled) {
respawnMapTableText = QString("\n%1u16 %2[][2] =\n{\n").arg(qualifiers).arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_maps));
respawnNPCTableText = QString("\n%1u8 %2[] =\n{\n").arg(qualifiers).arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_npcs));
respawnMapTableText = QString("\n%1%2[][2] =\n{\n").arg(qualifiers).arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_maps));
respawnNPCTableText = QString("\n%1%2[] =\n{\n").arg(qualifiers).arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_npcs));
}
// Populate the data tables with the heal location data
@ -2129,7 +2125,6 @@ bool Project::readHealLocationConstants() {
// TODO: Simplify using the new C struct parsing functions (and indexed array parsing functions)
bool Project::readHealLocations() {
this->healLocationDataQualifiers = {};
this->healLocations.clear();
if (!this->readHealLocationConstants())
@ -2145,8 +2140,19 @@ bool Project::readHealLocations() {
bool respawnEnabled = projectConfig.getHealLocationRespawnDataEnabled();
// Get data qualifiers for the location data table
this->healLocationDataQualifiers = this->getDataQualifiers(text, this->getHealLocationsTableName());
// Search for the name of the main Heal Locations table
const QRegularExpression tableNameExpr(QString("%1\\s+(?<name>[A-Za-z0-9_]+)\\[").arg(projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations_type)));
const QRegularExpressionMatch tableNameMatch = tableNameExpr.match(text);
if (tableNameMatch.hasMatch()) {
// Found table name, record it and its qualifiers for output when saving.
this->healLocationsTableName = tableNameMatch.captured("name");
this->healLocationDataQualifiers = this->getDataQualifiers(text, this->healLocationsTableName);
} else {
// No table name found, initialize default name for output when saving.
this->healLocationsTableName = respawnEnabled ? projectConfig.getIdentifier(ProjectIdentifier::symbol_spawn_points)
: projectConfig.getIdentifier(ProjectIdentifier::symbol_heal_locations);
this->healLocationDataQualifiers = { .isStatic = true, .isConst = true };
}
// Create regex pattern for the constants (ex: "SPAWN_PALLET_TOWN" or "HEAL_LOCATION_PETALBURG_CITY")
const QString spawnPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_spawn_prefix);

View file

@ -23,6 +23,7 @@ QMap<CallbackType, QString> callbackFunctions = {
Scripting *instance = nullptr;
void Scripting::init(MainWindow *mainWindow) {
mainWindow->ui->graphicsView_Map->clearOverlayMap();
if (instance) {
instance->engine->setInterrupted(true);
instance->scriptUtility->clearActions();

View file

@ -3,22 +3,22 @@
#include "imageproviders.h"
#include <QPainter>
void MetatileLayersItem::draw() {
static const QList<QPoint> tileCoords = QList<QPoint>{
static const QList<QPoint> tilePositions = {
QPoint(0, 0),
QPoint(16, 0),
QPoint(0, 16),
QPoint(16, 16),
QPoint(32, 0),
QPoint(48, 0),
QPoint(32, 16),
QPoint(48, 16),
QPoint(64, 0),
QPoint(80, 0),
QPoint(64, 16),
QPoint(80, 16),
QPoint(1, 0),
QPoint(0, 1),
QPoint(1, 1),
QPoint(2, 0),
QPoint(3, 0),
QPoint(2, 1),
QPoint(3, 1),
QPoint(4, 0),
QPoint(5, 0),
QPoint(4, 1),
QPoint(5, 1),
};
void MetatileLayersItem::draw() {
const int numLayers = projectConfig.getNumLayersInMetatile();
QPixmap pixmap(numLayers * 32, 32);
QPainter painter(&pixmap);
@ -30,7 +30,7 @@ void MetatileLayersItem::draw() {
QImage tileImage = getPalettedTileImage(tile.tileId, this->primaryTileset, this->secondaryTileset, tile.palette, true)
.mirrored(tile.xflip, tile.yflip)
.scaled(16, 16);
painter.drawImage(tileCoords.at(i), tileImage);
painter.drawImage(tilePositions.at(i) * 16, tileImage);
}
if (this->showGrid) {
// Draw grid
@ -47,6 +47,7 @@ void MetatileLayersItem::draw() {
void MetatileLayersItem::setMetatile(Metatile *metatile) {
this->metatile = metatile;
this->clearLastModifiedCoords();
this->clearLastHoveredCoords();
}
void MetatileLayersItem::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
@ -54,6 +55,7 @@ void MetatileLayersItem::setTilesets(Tileset *primaryTileset, Tileset *secondary
this->secondaryTileset = secondaryTileset;
this->draw();
this->clearLastModifiedCoords();
this->clearLastHoveredCoords();
}
void MetatileLayersItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
@ -64,11 +66,10 @@ void MetatileLayersItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
emit this->selectedTilesChanged(selectionOrigin, dimensions.x(), dimensions.y());
this->drawSelection();
} else {
int x, y;
this->getBoundedCoords(event->pos(), &x, &y);
this->prevChangedTile.setX(x);
this->prevChangedTile.setY(y);
emit this->tileChanged(x, y);
const QPoint pos = this->getBoundedPos(event->pos());
this->prevChangedPos = pos;
this->clearLastHoveredCoords();
emit this->tileChanged(pos.x(), pos.y());
}
}
@ -80,12 +81,11 @@ void MetatileLayersItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
emit this->selectedTilesChanged(selectionOrigin, dimensions.x(), dimensions.y());
this->drawSelection();
} else {
int x, y;
this->getBoundedCoords(event->pos(), &x, &y);
if (prevChangedTile.x() != x || prevChangedTile.y() != y) {
this->prevChangedTile.setX(x);
this->prevChangedTile.setY(y);
emit this->tileChanged(x, y);
const QPoint pos = this->getBoundedPos(event->pos());
if (prevChangedPos != pos) {
this->prevChangedPos = pos;
this->clearLastHoveredCoords();
emit this->tileChanged(pos.x(), pos.y());
}
}
}
@ -101,17 +101,40 @@ void MetatileLayersItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
this->draw();
}
void MetatileLayersItem::clearLastModifiedCoords() {
this->prevChangedTile.setX(-1);
this->prevChangedTile.setY(-1);
void MetatileLayersItem::hoverMoveEvent(QGraphicsSceneHoverEvent * event) {
const QPoint pos = this->getBoundedPos(event->pos());
if (pos == this->prevHoveredPos)
return;
this->prevHoveredPos = pos;
int tileIndex = tilePositions.indexOf(pos);
if (tileIndex < 0 || tileIndex >= this->metatile->tiles.length())
return;
emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex).tileId);
}
void MetatileLayersItem::getBoundedCoords(QPointF pos, int *x, int *y) {
int maxX = (projectConfig.getNumLayersInMetatile() * 2) - 1;
*x = static_cast<int>(pos.x()) / 16;
*y = static_cast<int>(pos.y()) / 16;
if (*x < 0) *x = 0;
if (*y < 0) *y = 0;
if (*x > maxX) *x = maxX;
if (*y > 1) *y = 1;
void MetatileLayersItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
this->clearLastHoveredCoords();
emit this->hoveredTileCleared();
}
void MetatileLayersItem::clearLastModifiedCoords() {
this->prevChangedPos = QPoint(-1, -1);
}
void MetatileLayersItem::clearLastHoveredCoords() {
this->prevHoveredPos = QPoint(-1, -1);
}
QPoint MetatileLayersItem::getBoundedPos(const QPointF &pos) {
int x, y;
int maxX = (projectConfig.getNumLayersInMetatile() * 2) - 1;
x = static_cast<int>(pos.x()) / 16;
y = static_cast<int>(pos.y()) / 16;
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x > maxX) x = maxX;
if (y > 1) y = 1;
return QPoint(x, y);
}

View file

@ -4,7 +4,9 @@
#include <QPainter>
QPoint MetatileSelector::getSelectionDimensions() {
if (this->prefabSelection || this->externalSelection)
return selection.dimensions;
return SelectablePixmapItem::getSelectionDimensions();
}
void MetatileSelector::draw() {
@ -112,41 +114,55 @@ void MetatileSelector::setPrefabSelection(MetatileSelection selection) {
emit selectedMetatilesChanged();
}
bool MetatileSelector::shouldAcceptEvent(QGraphicsSceneMouseEvent *event) {
QPoint pos = this->getCellPos(event->pos());
bool MetatileSelector::positionIsValid(const QPoint &pos) const {
return Tileset::metatileIsValid(getMetatileId(pos.x(), pos.y()), this->primaryTileset, this->secondaryTileset);
}
void MetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
if (!shouldAcceptEvent(event)) return;
QPoint pos = this->getCellPos(event->pos());
if (!positionIsValid(pos))
return;
this->cellPos = pos;
SelectablePixmapItem::mousePressEvent(event);
this->updateSelectedMetatiles();
}
void MetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if (!shouldAcceptEvent(event)) return;
QPoint pos = this->getCellPos(event->pos());
if (!positionIsValid(pos) || this->cellPos == pos)
return;
this->cellPos = pos;
SelectablePixmapItem::mouseMoveEvent(event);
this->updateSelectedMetatiles();
QPoint pos = this->getCellPos(event->pos());
uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
emit this->hoveredMetatileSelectionChanged(metatileId);
this->hoverChanged();
}
void MetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
if (!shouldAcceptEvent(event)) return;
QPoint pos = this->getCellPos(event->pos());
if (!positionIsValid(pos))
return;
SelectablePixmapItem::mouseReleaseEvent(event);
this->updateSelectedMetatiles();
}
void MetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
QPoint pos = this->getCellPos(event->pos());
uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
if (!positionIsValid(pos) || this->cellPos == pos)
return;
this->cellPos = pos;
this->hoverChanged();
}
void MetatileSelector::hoverChanged() {
uint16_t metatileId = this->getMetatileId(this->cellPos.x(), this->cellPos.y());
emit this->hoveredMetatileSelectionChanged(metatileId);
}
void MetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
emit this->hoveredMetatileSelectionCleared();
this->cellPos = QPoint(-1, -1);
}
void MetatileSelector::updateSelectedMetatiles() {
@ -155,7 +171,7 @@ void MetatileSelector::updateSelectedMetatiles() {
this->selection.metatileItems.clear();
this->selection.collisionItems.clear();
this->selection.hasCollision = false;
this->selection.dimensions = SelectablePixmapItem::getSelectionDimensions();
this->selection.dimensions = this->getSelectionDimensions();
QPoint origin = this->getSelectionStart();
for (int j = 0; j < this->selection.dimensions.y(); j++) {
for (int i = 0; i < this->selection.dimensions.x(); i++) {
@ -180,7 +196,7 @@ void MetatileSelector::updateExternalSelectedMetatiles() {
emit selectedMetatilesChanged();
}
uint16_t MetatileSelector::getMetatileId(int x, int y) {
uint16_t MetatileSelector::getMetatileId(int x, int y) const {
int index = y * this->numMetatilesWide + x;
if (index < this->primaryTileset->metatiles.length()) {
return static_cast<uint16_t>(index);

View file

@ -140,6 +140,14 @@ void MultiKeyEdit::addNewKeySequenceEdit() {
connect(lineEdit, &QLineEdit::customContextMenuRequested,
this, &MultiKeyEdit::customContextMenuRequested);
// Gross way to connect the line edit's clear button.
auto actions = lineEdit->findChildren<QAction*>();
if (!actions.isEmpty()) {
connect(actions.first(), &QAction::triggered, this, [this, keySequenceEdit]() {
removeOne(keySequenceEdit->keySequence());
});
}
layout()->addWidget(keySequenceEdit);
keySequenceEdit_vec.append(keySequenceEdit);
}

View file

@ -206,6 +206,10 @@ void TilesetEditor::initMetatileLayersItem() {
this, &TilesetEditor::onMetatileLayerTileChanged);
connect(this->metatileLayersItem, &MetatileLayersItem::selectedTilesChanged,
this, &TilesetEditor::onMetatileLayerSelectionChanged);
connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileChanged,
this, &TilesetEditor::onHoveredTileChanged);
connect(this->metatileLayersItem, &MetatileLayersItem::hoveredTileCleared,
this, &TilesetEditor::onHoveredTileCleared);
bool showGrid = porymapConfig.getShowTilesetEditorLayerGrid();
this->ui->actionLayer_Grid->setChecked(showGrid);
@ -861,6 +865,7 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
this->metatileSelector->draw();
this->metatileLayersItem->draw();
this->metatileLayersItem->clearLastModifiedCoords();
this->metatileLayersItem->clearLastHoveredCoords();
return true;
}