Merge pull request #692 from GriffinRichards/utility

Add utility file, more project load speed improvements
This commit is contained in:
GriffinR
2025-03-02 20:11:51 -05:00
committed by GitHub
26 changed files with 262 additions and 216 deletions

View File

@@ -109,6 +109,7 @@ In addition to these files, there are some specific symbol and macro names that
``define_attribute_encounter``, ``METATILE_ATTRIBUTE_ENCOUNTER_TYPE``, name used to extract setting from ``symbol_attribute_table``
``define_metatile_label_prefix``, ``METATILE_``, expected prefix for metatile label macro names
``define_heal_locations_prefix``, ``HEAL_LOCATION_``, default prefix for heal location macro names
``define_layout_prefix``, ``LAYOUT_``, default prefix for layout macro names
``define_map_prefix``, ``MAP_``, expected prefix for map macro names
``define_map_dynamic``, ``DYNAMIC``, macro name after prefix for Dynamic maps
``define_map_empty``, ``UNDEFINED``, macro name after prefix for empty maps

View File

@@ -220,6 +220,7 @@ enum ProjectIdentifier {
define_attribute_encounter,
define_metatile_label_prefix,
define_heal_locations_prefix,
define_layout_prefix,
define_map_prefix,
define_map_dynamic,
define_map_empty,

View File

@@ -45,7 +45,8 @@ public:
void setConstantName(const QString &constantName) { m_constantName = constantName; }
QString constantName() const { return m_constantName; }
static QString mapConstantFromName(QString mapName, bool includePrefix = true);
static QString mapConstantFromName(const QString &name);
QString expectedConstantName() const { return Map::mapConstantFromName(m_name); }
void setLayout(Layout *layout);
Layout* layout() const { return m_layout; }

View File

@@ -20,9 +20,7 @@ public:
Layout() {}
Layout(const Layout &other);
static QString layoutConstantFromName(QString mapName);
static QString defaultSuffix();
static QString layoutConstantFromName(const QString &name);
bool loaded = false;
bool hasUnsavedDataChanges = false;

View File

@@ -54,9 +54,9 @@ public:
QString readCIncbin(const QString &text, const QString &label);
QMap<QString, QString> readCIncbinMulti(const QString &filepath);
QStringList readCIncbinArray(const QString &filename, const QString &label);
QMap<QString, int> readCDefinesByRegex(const QString &filename, const QStringList &regexList, QString *error = nullptr);
QMap<QString, int> readCDefinesByName(const QString &filename, const QStringList &names, QString *error = nullptr);
QStringList readCDefineNames(const QString &filename, const QStringList &regexList, QString *error = nullptr);
QMap<QString, int> readCDefinesByRegex(const QString &filename, const QSet<QString> &regexList, QString *error = nullptr);
QMap<QString, int> readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error = nullptr);
QStringList readCDefineNames(const QString &filename, const QSet<QString> &regexList, QString *error = nullptr);
tsl::ordered_map<QString, QHash<QString, QString>> readCStructs(const QString &, const QString & = "", const QHash<int, QString>& = {});
QList<QStringList> getLabelMacros(const QList<QStringList>&, const QString&);
QStringList getLabelValues(const QList<QStringList>&, const QString&);
@@ -101,10 +101,10 @@ private:
QMap<QString,QString> expressions; // Map of all define names encountered to their expressions
QStringList filteredNames; // List of define names that matched the search text, in the order that they were encountered
};
ParsedDefines readCDefines(const QString &filename, const QStringList &filterList, bool useRegex, QString *error);
QMap<QString, int> evaluateCDefines(const QString &filename, const QStringList &filterList, bool useRegex, QString *error);
bool defineNameMatchesFilter(const QString &name, const QStringList &filterList) const;
bool defineNameMatchesFilter(const QString &name, const QList<QRegularExpression> &filterList) const;
ParsedDefines readCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
QMap<QString, int> evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
bool defineNameMatchesFilter(const QString &name, const QSet<QString> &filterList) const;
bool defineNameMatchesFilter(const QString &name, const QSet<QRegularExpression> &filterList) const;
static const QRegularExpression re_incScriptLabel;
static const QRegularExpression re_globalIncScriptLabel;

14
include/core/utility.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#ifndef UTILITY_H
#define UTILITY_H
#include <QString>
namespace Util {
void numericalModeSort(QStringList &list);
int roundUp(int numToRound, int multiple);
QString toDefineCase(QString input);
QString toHexString(uint32_t value, int minLength = 0);
}
#endif // UTILITY_H

View File

@@ -51,6 +51,7 @@ public:
QStringList itemNames;
QStringList flagNames;
QStringList varNames;
QStringList speciesNames;
QStringList movementTypes;
QStringList mapTypes;
QStringList mapBattleScenes;
@@ -142,8 +143,8 @@ public:
QVector<poryjson::Json::object> extraEncounterGroups;
bool readSpeciesIconPaths();
QPixmap getSpeciesIcon(const QString &species) const;
QMap<QString, QString> speciesToIconPath;
QString getDefaultSpeciesIconPath(const QString &species);
QPixmap getSpeciesIcon(const QString &species);
void addNewMapsec(const QString &idName);
void removeMapsec(const QString &idName);
@@ -251,12 +252,11 @@ public:
static QString getEmptyMapsecName();
static QString getMapGroupPrefix();
static void numericalModeSort(QStringList &list);
private:
QMap<QString, QString> mapSectionDisplayNames;
QMap<QString, qint64> modifiedFileTimestamps;
QMap<QString, QString> facingDirections;
QMap<QString, QString> speciesToIconPath;
struct EventGraphics
{
@@ -277,6 +277,8 @@ private:
void ignoreWatchedFileTemporarily(QString filepath);
void recordFileChange(const QString &filepath);
QString findSpeciesIconPath(const QStringList &names) const;
int maxEventsPerGroup;
int maxObjectEvents;
static int num_tiles_primary;

View File

@@ -5,6 +5,7 @@
#include "selectablepixmapitem.h"
#include "paletteutil.h"
#include "imageproviders.h"
#include "utility.h"
#include <memory>
using std::shared_ptr;
@@ -66,7 +67,7 @@ public:
}
virtual QString info() const {
return QString("Tile: 0x") + QString("%1 ").arg(this->id(), 4, 16, QChar('0')).toUpper();
return QString("Tile: %1 ").arg(Util::toHexString(this->id(), 4));
}
};

View File

@@ -51,6 +51,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/core/parseutil.cpp \
src/core/tile.cpp \
src/core/tileset.cpp \
src/core/utility.cpp \
src/core/validator.cpp \
src/core/regionmap.cpp \
src/core/wildmoninfo.cpp \
@@ -162,6 +163,7 @@ HEADERS += include/core/advancemapparser.h \
include/core/parseutil.h \
include/core/tile.h \
include/core/tileset.h \
include/core/utility.h \
include/core/validator.h \
include/core/regionmap.h \
include/core/wildmoninfo.h \

View File

@@ -3,6 +3,7 @@
#include "shortcut.h"
#include "map.h"
#include "validator.h"
#include "utility.h"
#include <QDir>
#include <QFile>
#include <QFormLayout>
@@ -101,6 +102,7 @@ const QMap<ProjectIdentifier, QPair<QString, QString>> ProjectConfig::defaultIde
{ProjectIdentifier::define_attribute_encounter, {"define_attribute_encounter", "METATILE_ATTRIBUTE_ENCOUNTER_TYPE"}},
{ProjectIdentifier::define_metatile_label_prefix, {"define_metatile_label_prefix", "METATILE_"}},
{ProjectIdentifier::define_heal_locations_prefix, {"define_heal_locations_prefix", "HEAL_LOCATION_"}},
{ProjectIdentifier::define_layout_prefix, {"define_layout_prefix", "LAYOUT_"}},
{ProjectIdentifier::define_map_prefix, {"define_map_prefix", "MAP_"}},
{ProjectIdentifier::define_map_dynamic, {"define_map_dynamic", "DYNAMIC"}},
{ProjectIdentifier::define_map_empty, {"define_map_empty", "UNDEFINED"}},
@@ -876,16 +878,16 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("tilesets_have_is_compressed", QString::number(this->tilesetsHaveIsCompressed));
map.insert("set_transparent_pixels_black", QString::number(this->setTransparentPixelsBlack));
map.insert("metatile_attributes_size", QString::number(this->metatileAttributesSize));
map.insert("metatile_behavior_mask", "0x" + QString::number(this->metatileBehaviorMask, 16).toUpper());
map.insert("metatile_terrain_type_mask", "0x" + QString::number(this->metatileTerrainTypeMask, 16).toUpper());
map.insert("metatile_encounter_type_mask", "0x" + QString::number(this->metatileEncounterTypeMask, 16).toUpper());
map.insert("metatile_layer_type_mask", "0x" + QString::number(this->metatileLayerTypeMask, 16).toUpper());
map.insert("block_metatile_id_mask", "0x" + QString::number(this->blockMetatileIdMask, 16).toUpper());
map.insert("block_collision_mask", "0x" + QString::number(this->blockCollisionMask, 16).toUpper());
map.insert("block_elevation_mask", "0x" + QString::number(this->blockElevationMask, 16).toUpper());
map.insert("unused_tile_normal", "0x" + QString::number(this->unusedTileNormal, 16).toUpper());
map.insert("unused_tile_covered", "0x" + QString::number(this->unusedTileCovered, 16).toUpper());
map.insert("unused_tile_split", "0x" + QString::number(this->unusedTileSplit, 16).toUpper());
map.insert("metatile_behavior_mask", Util::toHexString(this->metatileBehaviorMask));
map.insert("metatile_terrain_type_mask", Util::toHexString(this->metatileTerrainTypeMask));
map.insert("metatile_encounter_type_mask", Util::toHexString(this->metatileEncounterTypeMask));
map.insert("metatile_layer_type_mask", Util::toHexString(this->metatileLayerTypeMask));
map.insert("block_metatile_id_mask", Util::toHexString(this->blockMetatileIdMask));
map.insert("block_collision_mask", Util::toHexString(this->blockCollisionMask));
map.insert("block_elevation_mask", Util::toHexString(this->blockElevationMask));
map.insert("unused_tile_normal", Util::toHexString(this->unusedTileNormal));
map.insert("unused_tile_covered", Util::toHexString(this->unusedTileCovered));
map.insert("unused_tile_split", Util::toHexString(this->unusedTileSplit));
map.insert("enable_map_allow_flags", QString::number(this->mapAllowFlagsEnabled));
map.insert("event_icon_path_object", this->eventIconPaths[Event::Group::Object]);
map.insert("event_icon_path_warp", this->eventIconPaths[Event::Group::Warp]);

View File

@@ -2,7 +2,7 @@
#include "map.h"
#include "imageproviders.h"
#include "scripting.h"
#include "utility.h"
#include "editcommands.h"
#include <QTime>
@@ -56,14 +56,9 @@ void Map::setLayout(Layout *layout) {
}
}
QString Map::mapConstantFromName(QString mapName, bool includePrefix) {
// Transform map names of the form 'GraniteCave_B1F` into map constants like 'MAP_GRANITE_CAVE_B1F'.
static const QRegularExpression caseChange("([a-z])([A-Z])");
QString nameWithUnderscores = mapName.replace(caseChange, "\\1_\\2");
const QString prefix = includePrefix ? projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix) : "";
QString withMapAndUppercase = prefix + nameWithUnderscores.toUpper();
static const QRegularExpression underscores("_+");
return withMapAndUppercase.replace(underscores, "_");
// We don't enforce this for existing maps, but for creating new maps we need to formulaically generate a new MAP_NAME ID.
QString Map::mapConstantFromName(const QString &name) {
return projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix) + Util::toDefineCase(name);
}
int Map::getWidth() const {

View File

@@ -4,6 +4,7 @@
#include "scripting.h"
#include "imageproviders.h"
#include "utility.h"
Layout::Layout(const Layout &other) : Layout() {
copyFrom(&other);
@@ -32,13 +33,8 @@ void Layout::copyFrom(const Layout *other) {
this->border = other->border;
}
QString Layout::layoutConstantFromName(QString mapName) {
// Transform map names of the form 'GraniteCave_B1F` into layout constants like 'LAYOUT_GRANITE_CAVE_B1F'.
static const QRegularExpression caseChange("([a-z])([A-Z])");
QString nameWithUnderscores = mapName.replace(caseChange, "\\1_\\2");
QString withMapAndUppercase = "LAYOUT_" + nameWithUnderscores.toUpper();
static const QRegularExpression underscores("_+");
return withMapAndUppercase.replace(underscores, "_");
QString Layout::layoutConstantFromName(const QString &name) {
return projectConfig.getIdentifier(ProjectIdentifier::define_layout_prefix) + Util::toDefineCase(name);
}
Layout::Settings Layout::settings() const {

View File

@@ -1,6 +1,7 @@
#include "metatile.h"
#include "tileset.h"
#include "project.h"
#include "utility.h"
// Stores how each attribute should be laid out for all metatiles, according to the vanilla games.
// Used to set default config values and import maps with AdvanceMap.
@@ -42,7 +43,7 @@ QPoint Metatile::coordFromPixmapCoord(const QPointF &pixelCoord) {
static int numMetatileIdChars = 4;
QString Metatile::getMetatileIdString(uint16_t metatileId) {
return "0x" + QString("%1").arg(metatileId, numMetatileIdChars, 16, QChar('0')).toUpper();
return Util::toHexString(metatileId, numMetatileIdChars);
};
QString Metatile::getMetatileIdStrings(const QList<uint16_t> metatileIds) {
@@ -127,8 +128,8 @@ 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 '0x%1' is insufficient to contain all available options.")
.arg(QString::number(behaviorMask, 16).toUpper()));
logWarn(QString("Metatile Behavior mask '%1' is insufficient to contain all available options.")
.arg(Util::toHexString(behaviorMask)));
}
attributePackers.insert(Metatile::Attr::Behavior, packer);
@@ -136,8 +137,8 @@ void Metatile::setLayout(Project * project) {
packer.setMask(terrainTypeMask);
const uint32_t maxTerrainType = NUM_METATILE_TERRAIN_TYPES - 1;
if (terrainTypeMask && packer.clamp(maxTerrainType) != maxTerrainType) {
logWarn(QString("Metatile Terrain Type mask '0x%1' is insufficient to contain all %2 available options.")
.arg(QString::number(terrainTypeMask, 16).toUpper())
logWarn(QString("Metatile Terrain Type mask '%1' is insufficient to contain all %2 available options.")
.arg(Util::toHexString(terrainTypeMask))
.arg(maxTerrainType + 1));
}
attributePackers.insert(Metatile::Attr::TerrainType, packer);
@@ -146,8 +147,8 @@ void Metatile::setLayout(Project * project) {
packer.setMask(encounterTypeMask);
const uint32_t maxEncounterType = NUM_METATILE_ENCOUNTER_TYPES - 1;
if (encounterTypeMask && packer.clamp(maxEncounterType) != maxEncounterType) {
logWarn(QString("Metatile Encounter Type mask '0x%1' is insufficient to contain all %2 available options.")
.arg(QString::number(encounterTypeMask, 16).toUpper())
logWarn(QString("Metatile Encounter Type mask '%1' is insufficient to contain all %2 available options.")
.arg(Util::toHexString(encounterTypeMask))
.arg(maxEncounterType + 1));
}
attributePackers.insert(Metatile::Attr::EncounterType, packer);
@@ -156,8 +157,8 @@ void Metatile::setLayout(Project * project) {
packer.setMask(layerTypeMask);
const uint32_t maxLayerType = NUM_METATILE_LAYER_TYPES - 1;
if (layerTypeMask && packer.clamp(maxLayerType) != maxLayerType) {
logWarn(QString("Metatile Layer Type mask '0x%1' is insufficient to contain all %2 available options.")
.arg(QString::number(layerTypeMask, 16).toUpper())
logWarn(QString("Metatile Layer Type mask '%1' is insufficient to contain all %2 available options.")
.arg(Util::toHexString(layerTypeMask))
.arg(maxLayerType + 1));
}
attributePackers.insert(Metatile::Attr::LayerType, packer);

View File

@@ -368,11 +368,11 @@ QStringList ParseUtil::readCIncbinArray(const QString &filename, const QString &
return paths;
}
bool ParseUtil::defineNameMatchesFilter(const QString &name, const QStringList &filterList) const {
bool ParseUtil::defineNameMatchesFilter(const QString &name, const QSet<QString> &filterList) const {
return filterList.contains(name);
}
bool ParseUtil::defineNameMatchesFilter(const QString &name, const QList<QRegularExpression> &filterList) const {
bool ParseUtil::defineNameMatchesFilter(const QString &name, const QSet<QRegularExpression> &filterList) const {
for (auto filter : filterList) {
if (filter.match(name).hasMatch())
return true;
@@ -380,7 +380,7 @@ bool ParseUtil::defineNameMatchesFilter(const QString &name, const QList<QRegula
return false;
}
ParseUtil::ParsedDefines ParseUtil::readCDefines(const QString &filename, const QStringList &filterList, bool useRegex, QString *error) {
ParseUtil::ParsedDefines ParseUtil::readCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error) {
ParsedDefines result;
this->file = filename;
@@ -402,10 +402,10 @@ ParseUtil::ParsedDefines ParseUtil::readCDefines(const QString &filename, const
return result;
// If necessary, construct regular expressions from filter list
QList<QRegularExpression> filterList_Regex;
QSet<QRegularExpression> filterList_Regex;
if (useRegex) {
for (auto filter : filterList) {
filterList_Regex.append(QRegularExpression(filter));
filterList_Regex.insert(QRegularExpression(filter));
}
}
@@ -463,7 +463,7 @@ ParseUtil::ParsedDefines ParseUtil::readCDefines(const QString &filename, const
}
// Read all the define names and their expressions in the specified file, then evaluate the ones matching the search text (and any they depend on).
QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QStringList &filterList, bool useRegex, QString *error) {
QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error) {
ParsedDefines defines = readCDefines(filename, filterList, useRegex, error);
// Evaluate defines
@@ -483,19 +483,19 @@ QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QS
}
// Find and evaluate a specific set of defines with known names.
QMap<QString, int> ParseUtil::readCDefinesByName(const QString &filename, const QStringList &names, QString *error) {
QMap<QString, int> ParseUtil::readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error) {
return evaluateCDefines(filename, names, false, error);
}
// Find and evaluate an unknown list of defines with a known name pattern.
QMap<QString, int> ParseUtil::readCDefinesByRegex(const QString &filename, const QStringList &regexList, QString *error) {
QMap<QString, int> ParseUtil::readCDefinesByRegex(const QString &filename, const QSet<QString> &regexList, QString *error) {
return evaluateCDefines(filename, regexList, true, error);
}
// Find an unknown list of defines with a known name pattern.
// Similar to readCDefinesByRegex, but for cases where we only need to show a list of define names.
// We can skip evaluating any expressions (and by extension skip reporting any errors from this process).
QStringList ParseUtil::readCDefineNames(const QString &filename, const QStringList &regexList, QString *error) {
QStringList ParseUtil::readCDefineNames(const QString &filename, const QSet<QString> &regexList, QString *error) {
return readCDefines(filename, regexList, true, error).filteredNames;
}

44
src/core/utility.cpp Normal file
View File

@@ -0,0 +1,44 @@
#include "utility.h"
#include <QCollator>
#include <QRegularExpression>
// Sometimes we want to sort names alphabetically to make them easier to find in large combo box lists.
// QStringList::sort (as of writing) can only sort numbers in lexical order, which has an undesirable
// effect (e.g. 'ROUTE_1, ROUTE_10, ROUTE_2,...' instead of 'ROUTE_1, ROUTE_2,... ROUTE_10').
// We can use QCollator to sort these lists with better handling for numbers.
void Util::numericalModeSort(QStringList &list) {
static QCollator collator;
collator.setNumericMode(true);
std::sort(list.begin(), list.end(), collator);
}
int Util::roundUp(int numToRound, int multiple) {
if (multiple <= 0)
return numToRound;
int remainder = abs(numToRound) % multiple;
if (remainder == 0)
return numToRound;
if (numToRound < 0)
return -(abs(numToRound) - remainder);
else
return numToRound + multiple - remainder;
}
// Ex: input 'GraniteCave_B1F' returns 'GRANITE_CAVE_B1F'.
QString Util::toDefineCase(QString input) {
static const QRegularExpression re_CaseChange("([a-z])([A-Z])");
input.replace(re_CaseChange, "\\1_\\2");
// Remove sequential underscores
static const QRegularExpression re_Underscores("_+");
input.replace(re_Underscores, "_");
return input.toUpper();
}
QString Util::toHexString(uint32_t value, int minLength) {
return "0x" + QString("%1").arg(value, minLength, 16, QChar('0')).toUpper();
}

View File

@@ -987,7 +987,7 @@ QString Editor::getMetatileDisplayMessage(uint16_t metatileId) {
if (label.size())
message += QString(" \"%1\"").arg(label);
if (metatile && metatile->behavior() != 0) { // Skip MB_NORMAL
const QString behaviorStr = this->project->metatileBehaviorMapInverse.value(metatile->behavior(), "0x" + QString::number(metatile->behavior(), 16));
const QString behaviorStr = this->project->metatileBehaviorMapInverse.value(metatile->behavior(), Util::toHexString(metatile->behavior()));
message += QString(", Behavior: %1").arg(behaviorStr);
}
return message;

View File

@@ -10,6 +10,7 @@
#include "filedialog.h"
#include "validator.h"
#include "orderedjson.h"
#include "utility.h"
#include <QDir>
#include <QJsonArray>
@@ -312,7 +313,7 @@ Map *Project::createNewMap(const Project::NewMapSettings &settings, const Map* t
map->setNeedsHealLocation(settings.canFlyTo);
// Generate a unique MAP constant.
map->setConstantName(toUniqueIdentifier(Map::mapConstantFromName(map->name())));
map->setConstantName(toUniqueIdentifier(map->expectedConstantName()));
// Make sure we keep the order of the map names the same as in the map group order.
int mapNamePos;
@@ -1495,7 +1496,7 @@ bool Project::readTilesetMetatileLabels() {
QString metatileLabelsFilename = projectConfig.getFilePath(ProjectFilePath::constants_metatile_labels);
fileWatcher.addPath(root + "/" + metatileLabelsFilename);
const QStringList regexList = {QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_metatile_label_prefix))};
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()) {
@@ -2064,8 +2065,8 @@ bool Project::readTilesetLabels() {
}
}
numericalModeSort(this->primaryTilesetLabels);
numericalModeSort(this->secondaryTilesetLabels);
Util::numericalModeSort(this->primaryTilesetLabels);
Util::numericalModeSort(this->secondaryTilesetLabels);
bool success = true;
if (this->secondaryTilesetLabels.isEmpty()) {
@@ -2087,7 +2088,7 @@ 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 QStringList names = {
const QSet<QString> names = {
numTilesPrimaryName,
numTilesTotalName,
numMetatilesPrimaryName,
@@ -2171,7 +2172,7 @@ bool Project::readFieldmapMasks() {
const QString elevationMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_elevation);
const QString behaviorMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_behavior);
const QString layerTypeMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_layer);
const QStringList searchNames = {
const QSet<QString> searchNames = {
metatileIdMaskName,
collisionMaskName,
elevationMaskName,
@@ -2196,10 +2197,10 @@ bool Project::readFieldmapMasks() {
return false;
*value = static_cast<uint16_t>(it.value());
if (*value != it.value()){
logWarn(QString("Value for %1 truncated from '0x%2' to '0x%3'")
logWarn(QString("Value for %1 truncated from '%2' to '%3'")
.arg(name)
.arg(QString::number(it.value(), 16).toUpper())
.arg(QString::number(*value, 16).toUpper()));
.arg(Util::toHexString(it.value()))
.arg(Util::toHexString(*value)));
}
return true;
};
@@ -2339,7 +2340,7 @@ bool Project::readRegionMapSections() {
if (!this->mapSectionIdNames.contains(defaultName)) {
this->mapSectionIdNames.append(defaultName);
}
numericalModeSort(this->mapSectionIdNames);
Util::numericalModeSort(this->mapSectionIdNames);
return true;
}
@@ -2363,7 +2364,7 @@ void Project::addNewMapsec(const QString &idName) {
}
this->mapSectionIdNames.append(idName);
numericalModeSort(this->mapSectionIdNames);
Util::numericalModeSort(this->mapSectionIdNames);
this->hasUnsavedDataChanges = true;
@@ -2428,44 +2429,40 @@ bool Project::readHealLocations() {
}
bool Project::readItemNames() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_items)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_items);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->itemNames = parser.readCDefineNames(filename, regexList, &error);
this->itemNames = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_items)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read item constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readFlagNames() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_flags)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_flags);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->flagNames = parser.readCDefineNames(filename, regexList, &error);
this->flagNames = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_flags)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read flag constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readVarNames() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_vars)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_vars);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->varNames = parser.readCDefineNames(filename, regexList, &error);
this->varNames = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_vars)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read var constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readMovementTypes() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_movement_types)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_obj_event_movement);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->movementTypes = parser.readCDefineNames(filename, regexList, &error);
this->movementTypes = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_movement_types)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read movement type constants from '%1': %2").arg(filename).arg(error));
return true;
@@ -2482,33 +2479,30 @@ bool Project::readInitialFacingDirections() {
}
bool Project::readMapTypes() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_map_types)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->mapTypes = parser.readCDefineNames(filename, regexList, &error);
this->mapTypes = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_map_types)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read map type constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readMapBattleScenes() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_battle_scenes)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->mapBattleScenes = parser.readCDefineNames(filename, regexList, &error);
this->mapBattleScenes = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_battle_scenes)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read map battle scene constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readWeatherNames() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_weather)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->weatherNames = parser.readCDefineNames(filename, regexList, &error);
this->weatherNames = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_weather)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read weather constants from '%1': %2").arg(filename).arg(error));
return true;
@@ -2518,11 +2512,10 @@ bool Project::readCoordEventWeatherNames() {
if (!projectConfig.eventWeatherTriggerEnabled)
return true;
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_coord_event_weather)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->coordEventWeatherNames = parser.readCDefineNames(filename, regexList, &error);
this->coordEventWeatherNames = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_coord_event_weather)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read coord event weather constants from '%1': %2").arg(filename).arg(error));
return true;
@@ -2532,33 +2525,30 @@ bool Project::readSecretBaseIds() {
if (!projectConfig.eventSecretBaseEnabled)
return true;
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_secret_bases)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_secret_bases);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->secretBaseIds = parser.readCDefineNames(filename, regexList, &error);
this->secretBaseIds = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_secret_bases)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read secret base id constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readBgEventFacingDirections() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_sign_facing_directions)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_event_bg);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->bgEventFacingDirections = parser.readCDefineNames(filename, regexList, &error);
this->bgEventFacingDirections = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_sign_facing_directions)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read bg event facing direction constants from '%1': %2").arg(filename).arg(error));
return true;
}
bool Project::readTrainerTypes() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_trainer_types)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_trainer_types);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->trainerTypes = parser.readCDefineNames(filename, regexList, &error);
this->trainerTypes = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_trainer_types)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read trainer type constants from '%1': %2").arg(filename).arg(error));
return true;
@@ -2568,11 +2558,10 @@ bool Project::readMetatileBehaviors() {
this->metatileBehaviorMap.clear();
this->metatileBehaviorMapInverse.clear();
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_behaviors)};
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_metatile_behaviors);
fileWatcher.addPath(root + "/" + filename);
QString error;
QMap<QString, int> defines = parser.readCDefinesByRegex(filename, regexList, &error);
QMap<QString, int> defines = parser.readCDefinesByRegex(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_behaviors)}, &error);
if (defines.isEmpty() && projectConfig.metatileBehaviorMask) {
// Not having any metatile behavior names is ok (their values will be displayed instead)
// but if the user's metatiles can have nonzero values then warn them, as they likely want names.
@@ -2591,27 +2580,25 @@ bool Project::readMetatileBehaviors() {
}
bool Project::readSongNames() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_music)};
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_songs);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->songNames = parser.readCDefineNames(filename, regexList, &error);
this->songNames = parser.readCDefineNames(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_music)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read song names from '%1': %2").arg(filename).arg(error));
// Song names don't have a very useful order (esp. if we include SE_* values), so sort them alphabetically.
// The default song should be the first in the list, not the first alphabetically, so save that before sorting.
this->defaultSong = this->songNames.value(0, "0");
numericalModeSort(this->songNames);
Util::numericalModeSort(this->songNames);
return true;
}
bool Project::readObjEventGfxConstants() {
const QStringList regexList = {projectConfig.getIdentifier(ProjectIdentifier::regex_obj_event_gfx)};
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_obj_events);
fileWatcher.addPath(root + "/" + filename);
QString error;
this->gfxDefines = parser.readCDefinesByRegex(filename, regexList, &error);
this->gfxDefines = parser.readCDefinesByRegex(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_obj_event_gfx)}, &error);
if (!error.isEmpty())
logWarn(QString("Failed to read object event graphics constants from '%1': %2").arg(filename).arg(error));
return true;
@@ -2937,101 +2924,122 @@ QPixmap Project::getEventPixmap(Event::Group group) {
bool Project::readSpeciesIconPaths() {
this->speciesToIconPath.clear();
this->speciesNames.clear();
// Read map of species constants to icon names
const QString srcfilename = projectConfig.getFilePath(ProjectFilePath::pokemon_icon_table);
fileWatcher.addPath(root + "/" + srcfilename);
fileWatcher.addPath(this->root + "/" + srcfilename);
const QString tableName = projectConfig.getIdentifier(ProjectIdentifier::symbol_pokemon_icon_table);
const QMap<QString, QString> monIconNames = parser.readNamedIndexCArray(srcfilename, tableName);
// Read map of icon names to filepaths
const QString incfilename = projectConfig.getFilePath(ProjectFilePath::data_pokemon_gfx);
fileWatcher.addPath(root + "/" + incfilename);
const QMap<QString, QString> iconIncbins = parser.readCIncbinMulti(incfilename);
// Read species constants. If this fails we can get them from the icon table (but we shouldn't rely on it).
const QStringList regexList = {QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix))};
const QString speciesPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix);
const QString constantsFilename = projectConfig.getFilePath(ProjectFilePath::constants_species);
fileWatcher.addPath(root + "/" + constantsFilename);
QStringList speciesNames = parser.readCDefineNames(constantsFilename, regexList);
if (speciesNames.isEmpty())
speciesNames = monIconNames.keys();
fileWatcher.addPath(this->root + "/" + constantsFilename);
this->speciesNames = parser.readCDefineNames(constantsFilename, {QString("\\b%1").arg(speciesPrefix)});
if (this->speciesNames.isEmpty()) {
this->speciesNames = monIconNames.keys();
}
this->speciesNames.sort();
// For each species, use the information gathered above to find the icon image.
bool missingIcons = false;
for (auto species : speciesNames) {
QString path = QString();
if (monIconNames.contains(species) && iconIncbins.contains(monIconNames.value(species))) {
// We have the icon filepath from the icon table
path = QString("%1/%2").arg(root).arg(this->fixGraphicPath(iconIncbins[monIconNames.value(species)]));
} else {
// Failed to read icon filepath from the icon table, check filepaths where icons are normally located.
// Try to use the icon name (if we have it) to determine the directory, then try the species name.
// The name permuting is overkill, but it's making up for some of the fragility in the way we find icon paths.
QStringList possibleDirNames;
if (monIconNames.contains(species)) {
// Ex: For 'gMonIcon_QuestionMark' try 'question_mark'
static const QRegularExpression re("([a-z])([A-Z0-9])");
QString iconName = monIconNames.value(species);
iconName = iconName.mid(iconName.indexOf("_") + 1); // jump past prefix ('gMonIcon')
possibleDirNames.append(iconName.replace(re, "\\1_\\2").toLower());
}
// Ex: For 'SPECIES_FOO_BAR_BAZ' try 'foo_bar_baz'
possibleDirNames.append(species.mid(8).toLower());
// Permute paths with underscores.
// Ex: Try 'foo_bar/baz', 'foo/bar_baz', 'foobarbaz', 'foo_bar', and 'foo'
QStringList permutedNames;
for (auto dir : possibleDirNames) {
if (!dir.contains("_")) continue;
for (int i = dir.indexOf("_"); i > -1; i = dir.indexOf("_", i + 1)) {
QString temp = dir;
permutedNames.prepend(temp.replace(i, 1, "/"));
permutedNames.append(dir.left(i)); // Prepend the others so the most generic name ('foo') ends up last
// If we successfully found the species icon table we can use this data to get the filepath for each species icon.
// For any species not in the table, or if we failed to find the table at all, we will have to predict where the icon file is.
// That can require checking a lot of files (especially for projects with many species), so to save time on startup we only
// do this on request in Project::getDefaultSpeciesIconPath.
if (!monIconNames.isEmpty()) {
const QString iconGraphicsFile = projectConfig.getFilePath(ProjectFilePath::data_pokemon_gfx);
fileWatcher.addPath(this->root + "/" + iconGraphicsFile);
QMap<QString, QString> iconNameToFilepath = parser.readCIncbinMulti(iconGraphicsFile);
for (auto i = monIconNames.constBegin(); i != monIconNames.constEnd(); i++) {
QString path;
QString species = i.key();
QString iconName = i.value();
if (iconNameToFilepath.contains(iconName)) {
path = fixGraphicPath(iconNameToFilepath.value(iconName));
} else {
// We have an icon name for this species, but we haven't found its filepath.
// Try to find the icon file using the full icon name, and the icon name if we assume it has a prefix.
// Ex: For 'gMonIcon_QuestionMark' search for files by permuting through directories using 'question_mark' and 'g_mon_icon_question_mark.
static const QRegularExpression re_caseChange("([a-z])([A-Z0-9])");
QStringList dirNames;
if (iconName.contains("_")) {
QString iconNameNoPrefix = iconName.mid(iconName.indexOf("_") + 1);
dirNames.append(iconNameNoPrefix.replace(re_caseChange, "\\1_\\2").toLower());
}
permutedNames.prepend(dir.remove("_"));
QString iconNameWithPrefix = iconName; // Leave iconName unchanged by .replace
dirNames.append(iconNameWithPrefix.replace(re_caseChange, "\\1_\\2").toLower());
path = iconNameToFilepath[iconName] = findSpeciesIconPath(dirNames);
}
possibleDirNames.append(permutedNames);
possibleDirNames.removeDuplicates();
for (auto dir : possibleDirNames) {
if (dir.isEmpty()) continue;
const QString stdPath = QString("%1/%2%3/icon.png")
.arg(root)
.arg(projectConfig.getFilePath(ProjectFilePath::pokemon_gfx))
.arg(dir);
if (QFile::exists(stdPath)) {
// Icon found at a normal filepath
path = stdPath;
break;
}
}
if (path.isEmpty() && projectConfig.getPokemonIconPath(species).isEmpty()) {
// Failed to find icon, this species will use a placeholder icon.
logWarn(QString("Failed to find Pokémon icon for '%1'").arg(species));
missingIcons = true;
if (!path.isEmpty()) {
this->speciesToIconPath.insert(species, QString("%1/%2").arg(this->root).arg(path));
}
}
this->speciesToIconPath.insert(species, path);
}
// Logging this alongside every warning (if there are multiple) is obnoxious, just do it once at the end.
if (missingIcons) logInfo("Pokémon icon filepaths can be specified under 'Options->Project Settings'");
return true;
}
QPixmap Project::getSpeciesIcon(const QString &species) const {
QString Project::getDefaultSpeciesIconPath(const QString &species) {
if (this->speciesToIconPath.contains(species)) {
// We already know the icon path for this species (either because we read it from the project, or we found it already).
return this->speciesToIconPath.value(species);
}
if (!this->speciesNames.contains(species)) {
// Don't bother searching for a path if we don't recognize the species name.
return QString();
}
// Ex: For 'SPECIES_FOO_BAR_BAZ' search for files by permuting through directories using 'foo_bar_baz'.
const QString speciesPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix);
const QString path = findSpeciesIconPath({species.mid(speciesPrefix.length()).toLower()});
this->speciesToIconPath.insert(species, path);
// We failed to find a default icon path, this species will use a placeholder icon.
// If the user has no custom icon path for this species, tell them they can provide one.
if (path.isEmpty() && projectConfig.getPokemonIconPath(species).isEmpty()) {
logWarn(QString("Failed to find Pokémon icon for '%1'. The filepath can be specified under 'Options->Project Settings'").arg(species));
}
return path;
}
// The name permuting in here is overkill, but it's making up for some of the fragility in the way we find pokémon icon paths.
// For pokeemerald-expansion in particular this function is solely responsible for finding pokémon icons, because they have no icon table.
QString Project::findSpeciesIconPath(const QStringList &names) const {
QStringList possibleDirNames = names;
// Permute paths with underscores.
// Ex: For a base name of 'foo_bar_baz', try 'foo_bar/baz', 'foo/bar_baz', 'foobarbaz', 'foo_bar', and 'foo'.
QStringList permutedNames;
for (auto dir : possibleDirNames) {
if (!dir.contains("_")) continue;
for (int i = dir.indexOf("_"); i > -1; i = dir.indexOf("_", i + 1)) {
QString temp = dir;
permutedNames.prepend(temp.replace(i, 1, "/"));
permutedNames.append(dir.left(i)); // Prepend the others so the most generic name ('foo') ends up last
}
permutedNames.prepend(dir.remove("_"));
}
possibleDirNames.append(permutedNames);
possibleDirNames.removeDuplicates();
const QString basePath = QString("%1/%2").arg(this->root).arg(projectConfig.getFilePath(ProjectFilePath::pokemon_gfx));
for (const auto &dir : possibleDirNames) {
if (dir.isEmpty()) continue;
const QString path = QString("%1%2/icon.png").arg(basePath).arg(dir);
if (QFile::exists(path))
return path;
}
return QString();
}
QPixmap Project::getSpeciesIcon(const QString &species) {
QPixmap pixmap;
if (!QPixmapCache::find(species, &pixmap)) {
// Prefer path from config. If not present, use the path parsed from project files
QString path = projectConfig.getPokemonIconPath(species);
QString path = Project::getExistingFilepath(projectConfig.getPokemonIconPath(species));
if (path.isEmpty()) {
path = this->speciesToIconPath.value(species);
} else {
path = Project::getExistingFilepath(path);
path = getDefaultSpeciesIconPath(species);
}
QImage img(path);
@@ -3210,14 +3218,3 @@ bool Project::hasUnsavedChanges() {
}
return false;
}
// TODO: This belongs in a more general utility file, once we have one.
// Sometimes we want to sort names alphabetically to make them easier to find in large combo box lists.
// QStringList::sort (as of writing) can only sort numbers in lexical order, which has an undesirable
// effect (e.g. MAPSEC_ROUTE_10 comes after MAPSEC_ROUTE_1, rather than MAPSEC_ROUTE_9).
// We can use QCollator to sort these lists with better handling for numbers.
void Project::numericalModeSort(QStringList &list) {
QCollator collator;
collator.setNumericMode(true);
std::sort(list.begin(), list.end(), collator);
}

View File

@@ -21,7 +21,7 @@ void SpeciesComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
QWidget *SpeciesComboDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const {
NoScrollComboBox *editor = new NoScrollComboBox(parent);
editor->setFrame(false);
editor->addItems(this->project->speciesToIconPath.keys());
editor->addItems(this->project->speciesNames);
return editor;
}

View File

@@ -3,6 +3,7 @@
#include <QMessageBox>
#include "movablerect.h"
#include "utility.h"
MovableRect::MovableRect(bool *enabled, int width, int height, QRgb color)
: QGraphicsRectItem(0, 0, width, height)
@@ -22,10 +23,6 @@ void MovableRect::updateLocation(int x, int y) {
************************************************************************
******************************************************************************/
int roundUp(int numToRound, int multiple) {
return (numToRound + multiple - 1) & -multiple;
}
ResizableRect::ResizableRect(QObject *parent, bool *enabled, int width, int height, QRgb color)
: QObject(parent),
MovableRect(enabled, width * 16, height * 16, color)
@@ -117,8 +114,8 @@ void ResizableRect::mousePressEvent(QGraphicsSceneMouseEvent *event) {
}
void ResizableRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
int dx = roundUp(event->scenePos().x() - this->clickedPos.x(), 16);
int dy = roundUp(event->scenePos().y() - this->clickedPos.y(), 16);
int dx = Util::roundUp(event->scenePos().x() - this->clickedPos.x(), 16);
int dy = Util::roundUp(event->scenePos().y() - this->clickedPos.y(), 16);
QRect resizedRect = this->clickedRect;

View File

@@ -1,4 +1,5 @@
#include "noscrollcombobox.h"
#include "utility.h"
#include <QCompleter>
#include <QLineEdit>
@@ -82,7 +83,7 @@ void NoScrollComboBox::setNumberItem(int value)
void NoScrollComboBox::setHexItem(uint32_t value)
{
this->setItem(this->findData(value), "0x" + QString::number(value, 16).toUpper());
this->setItem(this->findData(value), Util::toHexString(value));
}
void NoScrollComboBox::setClearButtonEnabled(bool enabled) {

View File

@@ -3,6 +3,7 @@
#include "noscrollcombobox.h"
#include "prefab.h"
#include "filedialog.h"
#include "utility.h"
#include <QAbstractButton>
#include <QFormLayout>
@@ -103,7 +104,7 @@ void ProjectSettingsEditor::initUi() {
if (project) {
ui->comboBox_DefaultPrimaryTileset->addItems(project->primaryTilesetLabels);
ui->comboBox_DefaultSecondaryTileset->addItems(project->secondaryTilesetLabels);
ui->comboBox_IconSpecies->addItems(project->speciesToIconPath.keys());
ui->comboBox_IconSpecies->addItems(project->speciesNames);
ui->comboBox_WarpBehaviors->addItems(project->metatileBehaviorMap.keys());
}
ui->comboBox_BaseGameVersion->addItems(ProjectConfig::versionStrings);
@@ -277,11 +278,11 @@ void ProjectSettingsEditor::updatePokemonIconPath(const QString &newSpecies) {
if (!project) return;
// If user was editing a path for a valid species, record filepath text before we wipe it.
if (!this->prevIconSpecies.isEmpty() && this->project->speciesToIconPath.contains(this->prevIconSpecies))
if (!this->prevIconSpecies.isEmpty() && this->project->speciesNames.contains(this->prevIconSpecies))
this->editedPokemonIconPaths[this->prevIconSpecies] = ui->lineEdit_PokemonIcon->text();
QString editedPath = this->editedPokemonIconPaths.value(newSpecies);
QString defaultPath = this->project->speciesToIconPath.value(newSpecies);
QString defaultPath = this->project->getDefaultSpeciesIconPath(newSpecies);
ui->lineEdit_PokemonIcon->setText(this->stripProjectDir(editedPath));
ui->lineEdit_PokemonIcon->setPlaceholderText(this->stripProjectDir(defaultPath));
@@ -294,7 +295,7 @@ QStringList ProjectSettingsEditor::getWarpBehaviorsList() {
void ProjectSettingsEditor::setWarpBehaviorsList(QStringList list) {
list.removeDuplicates();
Project::numericalModeSort(list);
Util::numericalModeSort(list);
ui->textEdit_WarpBehaviors->setText(list.join("\n"));
}
@@ -566,7 +567,7 @@ void ProjectSettingsEditor::save() {
// Save pokemon icon paths
const QString species = ui->comboBox_IconSpecies->currentText();
if (this->project->speciesToIconPath.contains(species))
if (this->project->speciesNames.contains(species))
this->editedPokemonIconPaths.insert(species, ui->lineEdit_PokemonIcon->text());
for (auto i = this->editedPokemonIconPaths.cbegin(), end = this->editedPokemonIconPaths.cend(); i != end; i++)
projectConfig.setPokemonIconPath(i.key(), i.value());

View File

@@ -6,6 +6,7 @@
#include "shortcut.h"
#include "config.h"
#include "log.h"
#include "utility.h"
#include <QDir>
#include <QDialog>
@@ -793,8 +794,7 @@ void RegionMapEditor::onRegionMapTileSelectorSelectedTileChanged(unsigned id) {
}
void RegionMapEditor::onRegionMapTileSelectorHoveredTileChanged(unsigned tileId) {
QString message = QString("Tile: 0x") + QString("%1").arg(tileId, 4, 16, QChar('0')).toUpper();
this->ui->statusbar->showMessage(message);
this->ui->statusbar->showMessage(QString("Tile: %1").arg(Util::toHexString(tileId, 4)));
}
void RegionMapEditor::onRegionMapTileSelectorHoveredTileCleared() {

View File

@@ -2,12 +2,10 @@
#include "editor.h"
#include "movablerect.h"
#include "config.h"
#include "utility.h"
#include "ui_resizelayoutpopup.h"
// TODO: put this in a util file or something
extern int roundUp(int, int);
CheckeredBgScene::CheckeredBgScene(QObject *parent) : QGraphicsScene(parent) { }
void CheckeredBgScene::drawBackground(QPainter *painter, const QRectF &rect) {
@@ -62,7 +60,7 @@ void BoundedPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem
QVariant BoundedPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) {
if (change == ItemPositionChange && scene()) {
QPointF newPos = value.toPointF();
return QPointF(roundUp(newPos.x(), 16), roundUp(newPos.y(), 16));
return QPointF(Util::roundUp(newPos.x(), 16), Util::roundUp(newPos.y(), 16));
}
else
return QGraphicsItem::itemChange(change, value);

View File

@@ -10,6 +10,7 @@
#include "filedialog.h"
#include "validator.h"
#include "eventfilters.h"
#include "utility.h"
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QCloseEvent>
@@ -420,9 +421,7 @@ void TilesetEditor::queueMetatileReload(uint16_t metatileId) {
}
void TilesetEditor::onHoveredTileChanged(uint16_t tile) {
QString message = QString("Tile: 0x%1")
.arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper());
this->ui->statusbar->showMessage(message);
this->ui->statusbar->showMessage(QString("Tile: %1").arg(Util::toHexString(tile, 3)));
}
void TilesetEditor::onHoveredTileCleared() {

View File

@@ -2,6 +2,7 @@
#include "wildmonchart.h"
#include "ui_wildmonchart.h"
#include "config.h"
#include "utility.h"
static const QString baseWindowTitle = QString("Wild Pokémon Summary Charts");
@@ -367,13 +368,7 @@ QChart* WildMonChart::createLevelDistributionChart() {
series->attachAxis(axisY);
// We round the y-axis max up to a multiple of 5.
auto roundUp = [](int num, int multiple) {
auto remainder = num % multiple;
if (remainder == 0)
return num;
return num + multiple - remainder;
};
axisY->setMax(roundUp(qCeil(axisY->max()), 5));
axisY->setMax(Util::roundUp(qCeil(axisY->max()), 5));
return chart;
}

View File

@@ -22,7 +22,7 @@ WildMonSearch::WildMonSearch(Project *project, QWidget *parent) :
ui->setupUi(this);
// Set up species combo box
ui->comboBox_Search->addItems(project->speciesToIconPath.keys());
ui->comboBox_Search->addItems(project->speciesNames);
ui->comboBox_Search->setCurrentText(QString());
ui->comboBox_Search->lineEdit()->setPlaceholderText(Project::getEmptySpeciesName());
connect(ui->comboBox_Search, &QComboBox::currentTextChanged, this, &WildMonSearch::updateResults);