Clean up new map dialog redesign

This commit is contained in:
GriffinR 2024-11-12 14:50:44 -05:00
parent 4f8224359e
commit 7d7db3857d
8 changed files with 93 additions and 90 deletions

View File

@ -92,6 +92,7 @@ In addition to these files, there are some specific symbol and macro names that
``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
``symbol_dynamic_map_name``, ``Dynamic``, reserved map name to display for ``define_map_dynamic``
``define_obj_event_count``, ``OBJECT_EVENT_TEMPLATES_COUNT``, to limit total Object Events
``define_min_level``, ``MIN_LEVEL``, minimum wild encounters level
``define_max_level``, ``MAX_LEVEL``, maximum wild encounters level

View File

@ -187,6 +187,7 @@ enum ProjectIdentifier {
symbol_spawn_npcs,
symbol_attribute_table,
symbol_tilesets_prefix,
symbol_dynamic_map_name,
define_obj_event_count,
define_min_level,
define_max_level,
@ -323,7 +324,7 @@ public:
QString getCustomFilePath(ProjectFilePath pathId);
QString getCustomFilePath(const QString &pathId);
QString getFilePath(ProjectFilePath pathId);
void setIdentifier(ProjectIdentifier id, const QString &text);
void setIdentifier(ProjectIdentifier id, QString text);
void setIdentifier(const QString &id, const QString &text);
QString getCustomIdentifier(ProjectIdentifier id);
QString getCustomIdentifier(const QString &id);

View File

@ -18,10 +18,6 @@
#include <QVariant>
#include <QFileSystemWatcher>
// TODO: Expose to config
// The displayed name of the special map value used by warps with multiple potential destinations
static QString DYNAMIC_MAP_NAME = "Dynamic";
class Project : public QObject
{
Q_OBJECT
@ -80,6 +76,7 @@ public:
QMap<QString, qint64> modifiedFileTimestamps;
bool usingAsmTilesets;
QSet<QString> disabledSettingsNames;
QSet<QString> topLevelMapFields;
int pokemonMinLevel;
int pokemonMaxLevel;
int maxEncounterRate;
@ -145,7 +142,7 @@ public:
bool hasUnsavedChanges();
bool hasUnsavedDataChanges = false;
QSet<QString> getTopLevelMapFields();
void initTopLevelMapFields();
bool readMapJson(const QString &mapName, QJsonDocument * out);
bool loadMapData(Map*);
bool readMapLayouts();
@ -219,7 +216,6 @@ public:
QString getDefaultPrimaryTilesetLabel();
QString getDefaultSecondaryTilesetLabel();
QString getDynamicMapDefineName();
void updateTilesetMetatileLabels(Tileset *tileset);
QString buildMetatileLabelsText(const QMap<QString, uint16_t> defines);
QString findMetatileLabelsTileset(QString label);
@ -227,6 +223,8 @@ public:
static QString getExistingFilepath(QString filepath);
void applyParsedLimits();
static QString getDynamicMapDefineName();
static QString getDynamicMapName();
static int getNumTilesPrimary();
static int getNumTilesTotal();
static int getNumMetatilesPrimary();

View File

@ -81,6 +81,7 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{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_"}},
{ProjectIdentifier::symbol_dynamic_map_name, {"symbol_dynamic_map_name", "Dynamic"}},
// Defines
{ProjectIdentifier::define_obj_event_count, {"define_obj_event_count", "OBJECT_EVENT_TEMPLATES_COUNT"}},
{ProjectIdentifier::define_min_level, {"define_min_level", "MIN_LEVEL"}},
@ -941,13 +942,25 @@ QString ProjectConfig::getFilePath(ProjectFilePath pathId) {
}
void ProjectConfig::setIdentifier(ProjectIdentifier id, const QString &text) {
if (!defaultIdentifiers.contains(id)) return;
QString copy(text);
if (copy.isEmpty()) {
void ProjectConfig::setIdentifier(ProjectIdentifier id, QString text) {
if (!defaultIdentifiers.contains(id))
return;
if (text.isEmpty()) {
this->identifiers.remove(id);
} else {
this->identifiers[id] = copy;
const QString idName = defaultIdentifiers.value(id).first;
if (idName.startsWith("define_") || idName.startsWith("symbol_")) {
// Validate the input for the identifier, depending on the type.
static const QRegularExpression re("[A-Za-z_]+[\\w]*");
auto validator = QRegularExpressionValidator(re);
int temp = 0;
if (validator.validate(text, temp) != QValidator::Acceptable) {
logError(QString("The name '%1' for project identifier '%2' is invalid. It must only contain word characters, and cannot start with a digit.").arg(text).arg(idName));
return;
}
}
this->identifiers[id] = text;
}
}

View File

@ -404,17 +404,11 @@ bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) {
this->setGfx(ParseUtil::jsonToQString(json["graphics_id"]));
this->setTargetID(ParseUtil::jsonToInt(json["target_local_id"]));
// Ensure the target map constant is valid before adding it to the events.
const QString dynamicMapConstant = project->getDynamicMapDefineName();
QString mapConstant = ParseUtil::jsonToQString(json["target_map"]);
if (project->mapConstantsToMapNames.contains(mapConstant)) {
this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant));
} else if (mapConstant == dynamicMapConstant) {
this->setTargetMap(DYNAMIC_MAP_NAME);
} else {
logWarn(QString("Target Map constant '%1' is invalid. Using default '%2'.").arg(mapConstant).arg(dynamicMapConstant));
this->setTargetMap(DYNAMIC_MAP_NAME);
}
// Log a warning if "target_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["target_map"]);
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Target Map constant '%1' is invalid.").arg(mapConstant));
this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomValues(json);
@ -516,17 +510,11 @@ bool WarpEvent::loadFromJson(QJsonObject json, Project *project) {
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setDestinationWarpID(ParseUtil::jsonToQString(json["dest_warp_id"]));
// Ensure the warp destination map constant is valid before adding it to the warps.
const QString dynamicMapConstant = project->getDynamicMapDefineName();
QString mapConstant = ParseUtil::jsonToQString(json["dest_map"]);
if (project->mapConstantsToMapNames.contains(mapConstant)) {
this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant));
} else if (mapConstant == dynamicMapConstant) {
this->setDestinationMap(DYNAMIC_MAP_NAME);
} else {
logWarn(QString("Destination Map constant '%1' is invalid. Using default '%2'.").arg(mapConstant).arg(dynamicMapConstant));
this->setDestinationMap(DYNAMIC_MAP_NAME);
}
// Log a warning if "dest_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["dest_map"]);
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Destination Map constant '%1' is invalid.").arg(mapConstant));
this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomValues(json);

View File

@ -850,10 +850,10 @@ bool MainWindow::userSetMap(QString map_name) {
if (editor->map && editor->map->name() == map_name)
return true; // Already set
if (map_name == DYNAMIC_MAP_NAME) {
if (map_name == editor->project->getDynamicMapName()) {
QMessageBox msgBox(this);
QString errorMsg = QString("The map '%1' can't be opened, it's a placeholder to indicate the specified map will be set programmatically.").arg(map_name);
msgBox.critical(nullptr, "Error Opening Map", errorMsg);
msgBox.warning(nullptr, "Cannot Open Map", errorMsg);
return false;
}
@ -870,14 +870,13 @@ bool MainWindow::userSetMap(QString map_name) {
}
bool MainWindow::setMap(QString map_name) {
if (map_name.isEmpty() || map_name == DYNAMIC_MAP_NAME) {
logInfo(QString("Cannot set map to '%1'").arg(map_name));
if (!editor || !editor->project || map_name.isEmpty() || map_name == editor->project->getDynamicMapName()) {
logWarn(QString("Ignored setting map to '%1'").arg(map_name));
return false;
}
logInfo(QString("Setting map to '%1'").arg(map_name));
if (!editor || !editor->setMap(map_name)) {
if (!editor->setMap(map_name)) {
logWarn(QString("Failed to set map to '%1'").arg(map_name));
return false;
}
@ -996,12 +995,6 @@ void MainWindow::refreshMapScene() {
}
void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_group) {
// Ensure valid destination map name.
if (!editor->project->mapNames.contains(map_name)) {
logError(QString("Invalid map name '%1'").arg(map_name));
return;
}
// Open the destination map.
if (!userSetMap(map_name))
return;
@ -2780,15 +2773,11 @@ void MainWindow::on_pushButton_ConfigureEncountersJSON_clicked() {
}
void MainWindow::on_button_OpenDiveMap_clicked() {
const QString mapName = ui->comboBox_DiveMap->currentText();
if (editor->project->mapNames.contains(mapName))
userSetMap(mapName);
userSetMap(ui->comboBox_DiveMap->currentText());
}
void MainWindow::on_button_OpenEmergeMap_clicked() {
const QString mapName = ui->comboBox_EmergeMap->currentText();
if (editor->project->mapNames.contains(mapName))
userSetMap(mapName);
userSetMap(ui->comboBox_EmergeMap->currentText());
}
void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) {

View File

@ -135,7 +135,7 @@ void Project::clearTilesetCache() {
}
Map* Project::loadMap(QString mapName) {
if (mapName == DYNAMIC_MAP_NAME)
if (mapName == getDynamicMapName())
return nullptr;
Map *map;
@ -148,7 +148,6 @@ Map* Project::loadMap(QString mapName) {
} else {
map = new Map;
map->setName(mapName);
map->setConstantName(this->mapNamesToMapConstants.value(mapName)); // TODO: How should we handle if !mapNamesToMapConstants.contains(mapName) here
}
if (!(loadMapData(map) && loadMapLayout(map))){
@ -161,38 +160,36 @@ Map* Project::loadMap(QString mapName) {
return map;
}
const QSet<QString> defaultTopLevelMapFields = {
"id",
"name",
"layout",
"music",
"region_map_section",
"requires_flash",
"weather",
"map_type",
"show_map_name",
"battle_scene",
"connections",
"object_events",
"warp_events",
"coord_events",
"bg_events",
"shared_events_map",
"shared_scripts_map",
};
QSet<QString> Project::getTopLevelMapFields() {
QSet<QString> topLevelMapFields = defaultTopLevelMapFields;
void Project::initTopLevelMapFields() {
static const QSet<QString> defaultTopLevelMapFields = {
"id",
"name",
"layout",
"music",
"region_map_section",
"requires_flash",
"weather",
"map_type",
"show_map_name",
"battle_scene",
"connections",
"object_events",
"warp_events",
"coord_events",
"bg_events",
"heal_locations",
"shared_events_map",
"shared_scripts_map",
};
this->topLevelMapFields = defaultTopLevelMapFields;
if (projectConfig.mapAllowFlagsEnabled) {
topLevelMapFields.insert("allow_cycling");
topLevelMapFields.insert("allow_escaping");
topLevelMapFields.insert("allow_running");
this->topLevelMapFields.insert("allow_cycling");
this->topLevelMapFields.insert("allow_escaping");
this->topLevelMapFields.insert("allow_running");
}
if (projectConfig.floorNumberEnabled) {
topLevelMapFields.insert("floor_number");
this->topLevelMapFields.insert("floor_number");
}
return topLevelMapFields;
}
bool Project::readMapJson(const QString &mapName, QJsonDocument * out) {
@ -215,6 +212,11 @@ bool Project::loadMapData(Map* map) {
QJsonObject mapObj = mapDoc.object();
// 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());
map->setSong(ParseUtil::jsonToQString(mapObj["music"]));
map->setLayoutId(ParseUtil::jsonToQString(mapObj["layout"]));
map->setLocation(ParseUtil::jsonToQString(mapObj["region_map_section"]));
@ -353,9 +355,8 @@ bool Project::loadMapData(Map* map) {
// Check for custom fields
/* // TODO: Re-enable
QSet<QString> baseFields = this->getTopLevelMapFields();
for (QString key : mapObj.keys()) {
if (!baseFields.contains(key)) {
if (!this->topLevelMapFields.contains(key)) {
map->customHeaders.insert(key, mapObj[key]);
}
}
@ -1822,6 +1823,8 @@ bool Project::readMapGroups() {
this->groupedMapNames.clear();
this->mapNames.clear();
this->initTopLevelMapFields();
const QString filepath = root + "/" + projectConfig.getFilePath(ProjectFilePath::json_map_groups);
fileWatcher.addPath(filepath);
QJsonDocument mapGroupsDoc;
@ -1832,14 +1835,20 @@ bool Project::readMapGroups() {
QJsonObject mapGroupsObj = mapGroupsDoc.object();
QJsonArray mapGroupOrder = mapGroupsObj["group_order"].toArray();
const QString dynamicMapName = getDynamicMapName();
// Process the map group lists
for (int groupIndex = 0; groupIndex < mapGroupOrder.size(); groupIndex++) {
const QString groupName = ParseUtil::jsonToQString(mapGroupOrder.at(groupIndex));
const QJsonArray mapNamesJson = mapGroupsObj.value(groupName).toArray();
this->groupedMapNames.append(QStringList());
this->groupNames.append(groupName);
// Process the names in this map group
for (int j = 0; j < mapNamesJson.size(); j++) {
const QString mapName = ParseUtil::jsonToQString(mapNamesJson.at(j));
if (mapName == DYNAMIC_MAP_NAME) {
if (mapName == dynamicMapName) {
logWarn(QString("Ignoring map with reserved name '%1'.").arg(mapName));
continue;
}
@ -1877,7 +1886,7 @@ bool Project::readMapGroups() {
this->mapGroups.insert(mapName, groupIndex);
this->mapConstantsToMapNames.insert(mapConstant, mapName);
this->mapNamesToMapConstants.insert(mapName, mapConstant);
// TODO: Keep these updated
// TODO: Either verify that these are known IDs, or make sure nothing breaks when they're unknown.
this->mapNameToLayoutId.insert(mapName, ParseUtil::jsonToQString(mapObj["layout"]));
this->mapNameToMapSectionName.insert(mapName, ParseUtil::jsonToQString(mapObj["region_map_section"]));
}
@ -1892,10 +1901,11 @@ bool Project::readMapGroups() {
return false;
}
// Save special "Dynamic" constant
const QString defineName = this->getDynamicMapDefineName();
this->mapConstantsToMapNames.insert(defineName, DYNAMIC_MAP_NAME);
this->mapNamesToMapConstants.insert(DYNAMIC_MAP_NAME, defineName);
this->mapNames.append(DYNAMIC_MAP_NAME);
this->mapConstantsToMapNames.insert(defineName, dynamicMapName);
this->mapNamesToMapConstants.insert(dynamicMapName, defineName);
this->mapNames.append(dynamicMapName);
return true;
}
@ -2941,6 +2951,10 @@ QString Project::getDynamicMapDefineName() {
return prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_dynamic);
}
QString Project::getDynamicMapName() {
return projectConfig.getIdentifier(ProjectIdentifier::symbol_dynamic_map_name);
}
// If the provided filepath is an absolute path to an existing file, return filepath.
// If not, and the provided filepath is a relative path from the project dir to an existing file, return the relative path.
// Otherwise return empty string.

View File

@ -23,7 +23,6 @@ NewMapDialog::NewMapDialog(QWidget *parent, Project *project) :
this->importedMap = false;
// Map names and IDs can only contain word characters, and cannot start with a digit.
// TODO: Also validate this when we read ProjectIdentifier::define_map_prefix from the config
static const QRegularExpression re("[A-Za-z_]+[\\w]*");
auto validator = new QRegularExpressionValidator(re, this);
ui->lineEdit_Name->setValidator(validator);