Read MAP_OFFSET_W, MAP_OFFSET_H from project

This commit is contained in:
GriffinR
2025-04-13 21:32:42 -04:00
parent 918fafa638
commit 35d5851a8f
7 changed files with 78 additions and 118 deletions

View File

@@ -214,6 +214,8 @@ enum ProjectIdentifier {
define_pals_total,
define_tiles_per_metatile,
define_map_size,
define_map_offset_width,
define_map_offset_height,
define_mask_metatile,
define_mask_collision,
define_mask_elevation,

View File

@@ -240,24 +240,27 @@ public:
static QString getExistingFilepath(QString filepath);
void applyParsedLimits();
int getMapDataSize(int width, int height) const;
int getMaxMapDataSize() const { return this->maxMapDataSize; }
int getMaxMapWidth() const;
int getMaxMapHeight() const;
bool mapDimensionsValid(int width, int height) const;
bool calculateDefaultMapSize();
int getDefaultMapDimension() const { return this->defaultMapDimension; }
QSize getMapSizeAddition() const { return this->mapSizeAddition; }
int getMaxEvents(Event::Group group) const;
static QString getEmptyMapDefineName();
static QString getDynamicMapDefineName();
static QString getDynamicMapName();
static QString getEmptySpeciesName();
static int getNumTilesPrimary();
static int getNumTilesTotal();
static int getNumMetatilesPrimary();
static int getNumMetatilesTotal();
static int getNumPalettesPrimary();
static int getNumPalettesTotal();
static int getMaxMapDataSize();
static int getDefaultMapDimension();
static int getMaxMapWidth();
static int getMaxMapHeight();
static int getMapDataSize(int width, int height);
static bool mapDimensionsValid(int width, int height);
bool calculateDefaultMapSize();
int getMaxEvents(Event::Group group);
static int getNumTilesPrimary() { return num_tiles_primary; }
static int getNumTilesTotal() { return num_tiles_total; }
static int getNumMetatilesPrimary() { return num_metatiles_primary; }
static int getNumMetatilesTotal() { return Block::getMaxMetatileId() + 1; }
static int getNumPalettesPrimary(){ return num_pals_primary; }
static int getNumPalettesTotal() { return num_pals_total; }
static QString getEmptyMapsecName();
static QString getMapGroupPrefix();
@@ -302,15 +305,19 @@ private:
QString findSpeciesIconPath(const QStringList &names) const;
int maxEventsPerGroup;
int maxObjectEvents;
QSize mapSizeAddition;
int maxMapDataSize;
int defaultMapDimension;
// TODO: These really shouldn't be static, they're specific to a single project.
// We're making an assumption here that we only have one project open at a single time
// (which is true, but then if that's the case we should have some global Project instance instead)
static int num_tiles_primary;
static int num_tiles_total;
static int num_metatiles_primary;
static int num_pals_primary;
static int num_pals_total;
static int max_map_data_size;
static int default_map_dimension;
signals:
void fileChanged(const QString &filepath);

View File

@@ -89,6 +89,8 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{ProjectIdentifier::define_pals_total, {"define_pals_total", "NUM_PALS_TOTAL"}},
{ProjectIdentifier::define_tiles_per_metatile, {"define_tiles_per_metatile", "NUM_TILES_PER_METATILE"}},
{ProjectIdentifier::define_map_size, {"define_map_size", "MAX_MAP_DATA_SIZE"}},
{ProjectIdentifier::define_map_offset_width, {"define_map_offset_width", "MAP_OFFSET_W"}},
{ProjectIdentifier::define_map_offset_height, {"define_map_offset_height", "MAP_OFFSET_H"}},
{ProjectIdentifier::define_mask_metatile, {"define_mask_metatile", "MAPGRID_METATILE_ID_MASK"}},
{ProjectIdentifier::define_mask_collision, {"define_mask_collision", "MAPGRID_COLLISION_MASK"}},
{ProjectIdentifier::define_mask_elevation, {"define_mask_elevation", "MAPGRID_ELEVATION_MASK"}},

View File

@@ -29,8 +29,6 @@ int Project::num_tiles_total = 1024;
int Project::num_metatiles_primary = 512;
int Project::num_pals_primary = 6;
int Project::num_pals_total = 13;
int Project::max_map_data_size = 10240; // 0x2800
int Project::default_map_dimension = 20;
Project::Project(QObject *parent) :
QObject(parent),
@@ -2109,7 +2107,12 @@ bool Project::readFieldmapProperties() {
const QString numPalsTotalName = projectConfig.getIdentifier(ProjectIdentifier::define_pals_total);
const QString maxMapSizeName = projectConfig.getIdentifier(ProjectIdentifier::define_map_size);
const QString numTilesPerMetatileName = projectConfig.getIdentifier(ProjectIdentifier::define_tiles_per_metatile);
const QSet<QString> names = {
const QString mapOffsetWidthName = projectConfig.getIdentifier(ProjectIdentifier::define_map_offset_width);
const QString mapOffsetHeightName = projectConfig.getIdentifier(ProjectIdentifier::define_map_offset_height);
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_fieldmap);
fileWatcher.addPath(root + "/" + filename);
const QMap<QString, int> defines = parser.readCDefinesByName(filename, {
numTilesPrimaryName,
numTilesTotalName,
numMetatilesPrimaryName,
@@ -2117,10 +2120,9 @@ bool Project::readFieldmapProperties() {
numPalsTotalName,
maxMapSizeName,
numTilesPerMetatileName,
};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_fieldmap);
fileWatcher.addPath(root + "/" + filename);
const QMap<QString, int> defines = parser.readCDefinesByName(filename, names);
mapOffsetWidthName,
mapOffsetHeightName,
});
auto loadDefine = [defines](const QString name, int * dest, int min, int max) {
auto it = defines.find(name);
@@ -2146,25 +2148,35 @@ bool Project::readFieldmapProperties() {
// we don't actually know what the maximum number of metatiles is.
loadDefine(numMetatilesPrimaryName, &Project::num_metatiles_primary, 1, 0xFFFF - 1);
int w = 15, h = 14; // Default values of MAP_OFFSET_W, MAP_OFFSET_H
loadDefine(mapOffsetWidthName, &w, 0, INT_MAX);
loadDefine(mapOffsetHeightName, &h, 0, INT_MAX);
this->mapSizeAddition = QSize(w, h);
this->maxMapDataSize = 10240; // Default value of MAX_MAP_DATA_SIZE
this->defaultMapDimension = 20; // Arbitrary default of 20x20.
auto it = defines.find(maxMapSizeName);
if (it != defines.end()) {
int min = getMapDataSize(1, 1);
if (it.value() >= min) {
Project::max_map_data_size = it.value();
calculateDefaultMapSize();
this->maxMapDataSize = it.value();
if (getMapDataSize(this->defaultMapDimension, this->defaultMapDimension) > this->maxMapDataSize) {
// The specified map size is too small to use the default map dimensions.
// Calculate the largest square map size that we can use instead.
this->defaultMapDimension = qFloor((qSqrt(4 * this->maxMapDataSize + 1) - (w + h)) / 2);
}
} else {
// must be large enough to support a 1x1 map
logWarn(QString("Value for map property '%1' is %2, must be at least %3. Using default (%4) instead.")
logWarn(QString("Value for map property '%1' of %2 is too small to support a 1x1 map. Must be at least %3. Using default (%4) instead.")
.arg(maxMapSizeName)
.arg(it.value())
.arg(min)
.arg(Project::max_map_data_size));
.arg(this->maxMapDataSize));
}
}
else {
logWarn(QString("Value for map property '%1' not found. Using default (%2) instead.")
.arg(maxMapSizeName)
.arg(Project::max_map_data_size));
.arg(this->maxMapDataSize));
}
it = defines.find(numTilesPerMetatileName);
@@ -3112,91 +3124,28 @@ QPixmap Project::getSpeciesIcon(const QString &species) {
return pixmap;
}
int Project::getNumTilesPrimary()
{
return Project::num_tiles_primary;
int Project::getMapDataSize(int width, int height) const {
return (width + this->mapSizeAddition.width())
* (height + this->mapSizeAddition.height());
}
int Project::getNumTilesTotal()
{
return Project::num_tiles_total;
int Project::getMaxMapWidth() const {
return (getMaxMapDataSize() / (1 + this->mapSizeAddition.height())) - this->mapSizeAddition.width();
}
int Project::getNumMetatilesPrimary()
{
return Project::num_metatiles_primary;
int Project::getMaxMapHeight() const {
return (getMaxMapDataSize() / (1 + this->mapSizeAddition.width())) - this->mapSizeAddition.height();
}
int Project::getNumMetatilesTotal()
{
return Block::getMaxMetatileId() + 1;
}
int Project::getNumPalettesPrimary()
{
return Project::num_pals_primary;
}
int Project::getNumPalettesTotal()
{
return Project::num_pals_total;
}
int Project::getMaxMapDataSize()
{
return Project::max_map_data_size;
}
int Project::getMapDataSize(int width, int height)
{
// + 15 and + 14 come from fieldmap.c in pokeruby/pokeemerald/pokefirered.
return (width + 15) * (height + 14);
}
int Project::getDefaultMapDimension()
{
return Project::default_map_dimension;
}
int Project::getMaxMapWidth()
{
return (getMaxMapDataSize() / (1 + 14)) - 15;
}
int Project::getMaxMapHeight()
{
return (getMaxMapDataSize() / (1 + 15)) - 14;
}
bool Project::mapDimensionsValid(int width, int height) {
bool Project::mapDimensionsValid(int width, int height) const {
return getMapDataSize(width, height) <= getMaxMapDataSize();
}
// Get largest possible square dimensions for a map up to maximum of 20x20 (arbitrary)
bool Project::calculateDefaultMapSize(){
int max = getMaxMapDataSize();
if (max >= getMapDataSize(20, 20)) {
default_map_dimension = 20;
} else if (max >= getMapDataSize(1, 1)) {
// Below equation derived from max >= (x + 15) * (x + 14)
// x^2 + 29x + (210 - max), then complete the square and simplify
default_map_dimension = qFloor((qSqrt(4 * getMaxMapDataSize() + 1) - 29) / 2);
} else {
logError(QString("'%1' of %2 is too small to support a 1x1 map. Must be at least %3.")
.arg(projectConfig.getIdentifier(ProjectIdentifier::define_map_size))
.arg(max)
.arg(getMapDataSize(1, 1)));
return false;
}
return true;
}
// Object events have their own limit specified by ProjectIdentifier::define_obj_event_count.
// The default value for this is 64. All events (object events included) are also limited by
// the data types of the event counters in the project. This would normally be u8, so the limit is 255.
// We let the users tell us this limit in case they change these data types.
int Project::getMaxEvents(Event::Group group) {
int Project::getMaxEvents(Event::Group group) const {
if (group == Event::Group::Object)
return qMin(this->maxObjectEvents, projectConfig.maxEventsPerGroup);
return projectConfig.maxEventsPerGroup;

View File

@@ -227,7 +227,7 @@ int MainWindow::getHeight() {
void MainWindow::setDimensions(int width, int height) {
if (!this->editor || !this->editor->layout)
return;
if (!Project::mapDimensionsValid(width, height))
if (this->editor->project && !this->editor->project->mapDimensionsValid(width, height))
return;
this->editor->layout->setDimensions(width, height);
this->tryCommitMapChanges(true);
@@ -237,7 +237,7 @@ void MainWindow::setDimensions(int width, int height) {
void MainWindow::setWidth(int width) {
if (!this->editor || !this->editor->layout)
return;
if (!Project::mapDimensionsValid(width, this->editor->layout->getHeight()))
if (this->editor->project && !this->editor->project->mapDimensionsValid(width, this->editor->layout->getHeight()))
return;
this->editor->layout->setDimensions(width, this->editor->layout->getHeight());
this->tryCommitMapChanges(true);
@@ -247,7 +247,7 @@ void MainWindow::setWidth(int width) {
void MainWindow::setHeight(int height) {
if (!this->editor || !this->editor->layout)
return;
if (!Project::mapDimensionsValid(this->editor->layout->getWidth(), height))
if (this->editor->project && !this->editor->project->mapDimensionsValid(this->editor->layout->getWidth(), height))
return;
this->editor->layout->setDimensions(this->editor->layout->getWidth(), height);
this->tryCommitMapChanges(true);

View File

@@ -86,17 +86,14 @@ bool NewLayoutForm::validateMapDimensions() {
int size = m_project->getMapDataSize(ui->spinBox_MapWidth->value(), ui->spinBox_MapHeight->value());
int maxSize = m_project->getMaxMapDataSize();
// TODO: Get from project
const int additionalWidth = 15;
const int additionalHeight = 14;
QString errorText;
if (size > maxSize) {
QSize addition = m_project->getMapSizeAddition();
errorText = QString("The specified width and height are too large.\n"
"The maximum map width and height is the following: (width + %1) * (height + %2) <= %3\n"
"The specified map width and height was: (%4 + %1) * (%5 + %2) = %6")
.arg(additionalWidth)
.arg(additionalHeight)
.arg(addition.width())
.arg(addition.height())
.arg(maxSize)
.arg(ui->spinBox_MapWidth->value())
.arg(ui->spinBox_MapHeight->value())

View File

@@ -145,15 +145,18 @@ void ResizeLayoutPopup::setupLayoutView() {
// Upper limits: maximum metatiles in a map formula:
// max = (width + 15) * (height + 14)
// This limit can be found in fieldmap.c in pokeruby/pokeemerald/pokefirered.
int numMetatiles = editor->project->getMapDataSize(rect.width() / 16, rect.height() / 16);
int maxMetatiles = editor->project->getMaxMapDataSize();
if (numMetatiles > maxMetatiles) {
QString errorText = QString("The maximum layout width and height is the following: (width + 15) * (height + 14) <= %1\n"
"The specified layout width and height was: (%2 + 15) * (%3 + 14) = %4")
.arg(maxMetatiles)
int size = editor->project->getMapDataSize(rect.width() / 16, rect.height() / 16);
int maxSize = editor->project->getMaxMapDataSize();
if (size > maxSize) {
QSize addition = editor->project->getMapSizeAddition();
QString errorText = QString("The maximum layout width and height is the following: (width + %1) * (height + %2) <= %3\n"
"The specified layout width and height was: (%4 + %1) * (%5 + %2) = %6")
.arg(addition.width())
.arg(addition.height())
.arg(maxSize)
.arg(rect.width() / 16)
.arg(rect.height() / 16)
.arg(numMetatiles);
.arg(size);
QMessageBox warning;
warning.setIcon(QMessageBox::Warning);
warning.setText("The specified width and height are too large.");