Read encounter/terrain types

This commit is contained in:
GriffinR 2025-02-27 13:02:10 -05:00
parent ef6eb69c72
commit 969c9d8e5a
9 changed files with 75 additions and 58 deletions

View File

@ -242,6 +242,8 @@ enum ProjectIdentifier {
regex_sign_facing_directions,
regex_trainer_types,
regex_music,
regex_encounter_types,
regex_terrain_types,
regex_gbapal,
regex_bpp,
pals_output_extension,

View File

@ -11,27 +11,6 @@
class Project;
enum {
METATILE_LAYER_MIDDLE_TOP,
METATILE_LAYER_BOTTOM_MIDDLE,
METATILE_LAYER_BOTTOM_TOP,
NUM_METATILE_LAYER_TYPES
};
enum {
ENCOUNTER_NONE,
ENCOUNTER_LAND,
ENCOUNTER_WATER,
NUM_METATILE_ENCOUNTER_TYPES
};
enum {
TERRAIN_NONE,
TERRAIN_GRASS,
TERRAIN_WATER,
TERRAIN_WATERFALL,
NUM_METATILE_TERRAIN_TYPES
};
class Metatile
{
@ -41,6 +20,13 @@ public:
Metatile &operator=(const Metatile &other) = default;
Metatile(const int numTiles);
enum LayerType {
Normal,
Covered,
Split,
Count
};
enum Attr {
Behavior,
TerrainType,

View File

@ -63,6 +63,8 @@ public:
QStringList globalScriptLabels;
QStringList mapSectionIdNamesSaveOrder;
QStringList mapSectionIdNames;
QMap<uint32_t, QString> encounterTypeToName;
QMap<uint32_t, QString> terrainTypeToName;
QMap<QString, MapSectionEntry> regionMapEntries;
QMap<QString, QMap<QString, uint16_t>> metatileLabelsMap;
QMap<QString, uint16_t> unusedMetatileLabels;

View File

@ -125,6 +125,8 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{ProjectIdentifier::regex_sign_facing_directions, {"regex_sign_facing_directions", "\\bBG_EVENT_PLAYER_FACING_"}},
{ProjectIdentifier::regex_trainer_types, {"regex_trainer_types", "\\bTRAINER_TYPE_"}},
{ProjectIdentifier::regex_music, {"regex_music", "\\b(SE|MUS)_"}},
{ProjectIdentifier::regex_encounter_types, {"regex_encounter_types", "\\bTILE_ENCOUNTER_"}},
{ProjectIdentifier::regex_terrain_types, {"regex_terrain_types", "\\bTILE_TERRAIN_"}},
{ProjectIdentifier::regex_gbapal, {"regex_gbapal", "\\.gbapal(\\.[\\w]+)?$"}},
{ProjectIdentifier::regex_bpp, {"regex_bpp", "\\.[\\d]+bpp(\\.[\\w]+)?$"}},
// Other

View File

@ -128,34 +128,39 @@ void Metatile::setLayout(Project * project) {
if (behaviorMask && !project->metatileBehaviorMapInverse.isEmpty()) {
uint32_t maxBehavior = project->metatileBehaviorMapInverse.lastKey();
if (packer.clamp(maxBehavior) != maxBehavior)
logWarn(QString("Metatile Behavior mask '%1' is insufficient to contain all available options.")
.arg(Util::toHexString(behaviorMask)));
logWarn(QString("Metatile Behavior mask '%1' is insufficient to contain largest value '%2'.")
.arg(Util::toHexString(behaviorMask))
.arg(Util::toHexString(maxBehavior)));
}
attributePackers.insert(Metatile::Attr::Behavior, packer);
// Validate terrain type mask
packer.setMask(terrainTypeMask);
const uint32_t maxTerrainType = NUM_METATILE_TERRAIN_TYPES - 1;
if (terrainTypeMask && packer.clamp(maxTerrainType) != maxTerrainType) {
logWarn(QString("Metatile Terrain Type mask '%1' is insufficient to contain all %2 available options.")
if (terrainTypeMask && !project->terrainTypeToName.isEmpty()) {
uint32_t maxTerrainType = project->terrainTypeToName.lastKey();
if (packer.clamp(maxTerrainType) != maxTerrainType) {
logWarn(QString("Metatile Terrain Type mask '%1' is insufficient to contain largest value '%2'.")
.arg(Util::toHexString(terrainTypeMask))
.arg(maxTerrainType + 1));
.arg(Util::toHexString(maxTerrainType)));
}
}
attributePackers.insert(Metatile::Attr::TerrainType, packer);
// Validate encounter type mask
packer.setMask(encounterTypeMask);
const uint32_t maxEncounterType = NUM_METATILE_ENCOUNTER_TYPES - 1;
if (encounterTypeMask && packer.clamp(maxEncounterType) != maxEncounterType) {
logWarn(QString("Metatile Encounter Type mask '%1' is insufficient to contain all %2 available options.")
if (encounterTypeMask && !project->encounterTypeToName.isEmpty()) {
uint32_t maxEncounterType = project->encounterTypeToName.lastKey();
if (packer.clamp(maxEncounterType) != maxEncounterType) {
logWarn(QString("Metatile Encounter Type mask '%1' is insufficient to contain largest value '%2'.")
.arg(Util::toHexString(encounterTypeMask))
.arg(maxEncounterType + 1));
.arg(Util::toHexString(maxEncounterType)));
}
}
attributePackers.insert(Metatile::Attr::EncounterType, packer);
// Validate terrain type mask
// Validate layer type mask
packer.setMask(layerTypeMask);
const uint32_t maxLayerType = NUM_METATILE_LAYER_TYPES - 1;
const uint32_t maxLayerType = Metatile::LayerType::Count - 1;
if (layerTypeMask && packer.clamp(maxLayerType) != maxLayerType) {
logWarn(QString("Metatile Layer Type mask '%1' is insufficient to contain all %2 available options.")
.arg(Util::toHexString(layerTypeMask))

View File

@ -98,19 +98,8 @@ QString ParseUtil::loadTextFile(const QString &path, QString *error) {
auto it = this->fileCache.constFind(path);
if (it != this->fileCache.constEnd()) {
// Load text file from cache
//logWarn(QString("CACHE HIT ON %1").arg(path));
return it.value();
}
/* TODO: Remove
static QSet<QString> parsedFiles;
if (parsedFiles.contains(path)) {
logWarn(QString("CACHE MISS ON %1").arg(path));
} else {
parsedFiles.insert(path);
}
*/
return readTextFile(pathWithRoot(path), error);
}

View File

@ -134,6 +134,7 @@ void Project::resetFileCache() {
projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles),
// We need separate sets of constants from these files
projectConfig.getFilePath(ProjectFilePath::constants_map_types),
projectConfig.getFilePath(ProjectFilePath::global_fieldmap),
};
for (const auto &path : filepaths) {
this->parser.cacheFile(path);
@ -2185,6 +2186,9 @@ bool Project::readFieldmapProperties() {
// Read data masks for Blocks and metatile attributes.
bool Project::readFieldmapMasks() {
this->encounterTypeToName.clear();
this->terrainTypeToName.clear();
const QString metatileIdMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_metatile);
const QString collisionMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_collision);
const QString elevationMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_elevation);
@ -2197,7 +2201,7 @@ bool Project::readFieldmapMasks() {
behaviorMaskName,
layerTypeMaskName,
};
QString globalFieldmap = projectConfig.getFilePath(ProjectFilePath::global_fieldmap);
const QString globalFieldmap = projectConfig.getFilePath(ProjectFilePath::global_fieldmap);
fileWatcher.addPath(root + "/" + globalFieldmap);
QMap<QString, int> defines = parser.readCDefinesByName(globalFieldmap, searchNames);
@ -2281,6 +2285,32 @@ bool Project::readFieldmapMasks() {
}
}
}
// Read #defines for encounter and terrain types to populate in the Tileset Editor dropdowns (if necessary)
QString error;
if (projectConfig.metatileEncounterTypeMask) {
QMap<QString, int> defines = parser.readCDefinesByRegex(globalFieldmap, {projectConfig.getIdentifier(ProjectIdentifier::regex_encounter_types)}, &error);
if (!error.isEmpty()) {
logWarn(QString("Failed to read encounter type constants from '%1': %2").arg(globalFieldmap).arg(error));
error = QString();
} else {
for (auto i = defines.constBegin(); i != defines.constEnd(); i++) {
this->encounterTypeToName.insert(static_cast<uint32_t>(i.value()), i.key());
}
}
}
if (projectConfig.metatileTerrainTypeMask) {
QMap<QString, int> defines = parser.readCDefinesByRegex(globalFieldmap, {projectConfig.getIdentifier(ProjectIdentifier::regex_terrain_types)}, &error);
if (!error.isEmpty()) {
logWarn(QString("Failed to read terrain type constants from '%1': %2").arg(globalFieldmap).arg(error));
error = QString();
} else {
for (auto i = defines.constBegin(); i != defines.constEnd(); i++) {
this->terrainTypeToName.insert(static_cast<uint32_t>(i.value()), i.key());
}
}
}
return true;
}

View File

@ -72,19 +72,19 @@ QImage getMetatileImage(
switch (layerType)
{
default:
case METATILE_LAYER_MIDDLE_TOP:
case Metatile::LayerType::Normal:
if (l == 0)
tile = Tile(projectConfig.unusedTileNormal);
else // Tiles are on layers 1 and 2
tile = metatile->tiles.value(tileOffset + ((l - 1) * 4));
break;
case METATILE_LAYER_BOTTOM_MIDDLE:
case Metatile::LayerType::Covered:
if (l == 2)
tile = Tile(projectConfig.unusedTileCovered);
else // Tiles are on layers 0 and 1
tile = metatile->tiles.value(tileOffset + (l * 4));
break;
case METATILE_LAYER_BOTTOM_TOP:
case Metatile::LayerType::Split:
if (l == 1)
tile = Tile(projectConfig.unusedTileSplit);
else // Tiles are on layers 0 and 2

View File

@ -134,10 +134,9 @@ void TilesetEditor::setAttributesUi() {
// Terrain Type
if (projectConfig.metatileTerrainTypeMask) {
this->ui->comboBox_terrainType->addItem("Normal", TERRAIN_NONE);
this->ui->comboBox_terrainType->addItem("Grass", TERRAIN_GRASS);
this->ui->comboBox_terrainType->addItem("Water", TERRAIN_WATER);
this->ui->comboBox_terrainType->addItem("Waterfall", TERRAIN_WATERFALL);
for (auto i = project->terrainTypeToName.constBegin(); i != project->terrainTypeToName.constEnd(); i++) {
this->ui->comboBox_terrainType->addItem(i.value(), i.key());
}
this->ui->comboBox_terrainType->setEditable(false);
this->ui->comboBox_terrainType->setMinimumContentsLength(0);
} else {
@ -147,9 +146,9 @@ void TilesetEditor::setAttributesUi() {
// Encounter Type
if (projectConfig.metatileEncounterTypeMask) {
this->ui->comboBox_encounterType->addItem("None", ENCOUNTER_NONE);
this->ui->comboBox_encounterType->addItem("Land", ENCOUNTER_LAND);
this->ui->comboBox_encounterType->addItem("Water", ENCOUNTER_WATER);
for (auto i = project->encounterTypeToName.constBegin(); i != project->encounterTypeToName.constEnd(); i++) {
this->ui->comboBox_encounterType->addItem(i.value(), i.key());
}
this->ui->comboBox_encounterType->setEditable(false);
this->ui->comboBox_encounterType->setMinimumContentsLength(0);
} else {
@ -159,9 +158,9 @@ void TilesetEditor::setAttributesUi() {
// Layer Type
if (!projectConfig.tripleLayerMetatilesEnabled) {
this->ui->comboBox_layerType->addItem("Normal - Middle/Top", METATILE_LAYER_MIDDLE_TOP);
this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", METATILE_LAYER_BOTTOM_MIDDLE);
this->ui->comboBox_layerType->addItem("Split - Bottom/Top", METATILE_LAYER_BOTTOM_TOP);
this->ui->comboBox_layerType->addItem("Normal - Middle/Top", Metatile::LayerType::Normal);
this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", Metatile::LayerType::Covered);
this->ui->comboBox_layerType->addItem("Split - Bottom/Top", Metatile::LayerType::Split);
this->ui->comboBox_layerType->setEditable(false);
this->ui->comboBox_layerType->setMinimumContentsLength(0);
if (!projectConfig.metatileLayerTypeMask) {
@ -607,6 +606,7 @@ void TilesetEditor::on_comboBox_layerType_activated(int layerType)
}
}
// TODO: Needs to read data from item, not index of item.
void TilesetEditor::on_comboBox_encounterType_activated(int encounterType)
{
if (this->metatile) {
@ -616,6 +616,7 @@ void TilesetEditor::on_comboBox_encounterType_activated(int encounterType)
}
}
// TODO: Needs to read data from item, not index of item.
void TilesetEditor::on_comboBox_terrainType_activated(int terrainType)
{
if (this->metatile) {