Reorganize some wild encounters parsing

This commit is contained in:
GriffinR 2026-01-02 16:45:33 -05:00
parent e012b790a8
commit a41c923fc8
10 changed files with 214 additions and 161 deletions

View File

@ -14,17 +14,25 @@ public:
int maxLevel;
QString species;
OrderedJson::object customData;
static WildPokemon fromJson(const OrderedJson::object &json);
OrderedJson::object toJson() const;
};
struct WildMonInfo {
// Represents a single set of encounters, e.g. all the "land" encounters for one map.
// This includes the list of WildPokemon that are possible and the rate at which encounters trigger.
struct WildEncounters {
bool active = false;
int encounterRate = 0;
QVector<WildPokemon> wildPokemon;
OrderedJson::object customData;
static WildEncounters fromJson(const OrderedJson::object &json, int numEncountersExpected = 0);
OrderedJson::object toJson() const;
};
struct WildPokemonHeader {
OrderedMap<QString, WildMonInfo> wildMons;
OrderedMap<QString, WildEncounters> wildMons;
OrderedJson::object customData;
};
@ -33,13 +41,16 @@ struct EncounterField {
QVector<int> encounterRates;
OrderedMap<QString, QVector<int>> groups; // Ex: "good_rod", {2, 3, 4}
OrderedJson::object customData;
static EncounterField fromJson(const OrderedJson::object &json);
OrderedJson::object toJson() const;
};
typedef QVector<EncounterField> EncounterFields;
void setDefaultEncounterRate(QString fieldName, int rate);
WildMonInfo getDefaultMonInfo(const EncounterField &field);
WildEncounters getDefaultEncounters(const EncounterField &field);
QVector<double> getWildEncounterPercentages(const EncounterField &field);
void combineEncounters(WildMonInfo &to, WildMonInfo from);
void combineEncounters(WildEncounters &to, WildEncounters from);
#endif // GUARD_WILDMONINFO_H

View File

@ -209,6 +209,7 @@ public:
bool readFieldmapProperties();
bool readFieldmapMasks();
bool readGlobalConstants();
bool readWildMonConstants();
QMap<QString, QMap<QString, QString>> readObjEventGfxInfo();
QPixmap getEventPixmap(const QString &gfxName, const QString &movementName);

View File

@ -14,7 +14,7 @@ class EncounterTableModel : public QAbstractTableModel {
Q_OBJECT
public:
EncounterTableModel(const WildMonInfo &monInfo, const EncounterField &field, QObject *parent = nullptr);
EncounterTableModel(const WildEncounters &encounters, const EncounterField &field, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
@ -28,14 +28,14 @@ public:
Slot, Group, Species, MinLevel, MaxLevel, EncounterChance, SlotRatio, EncounterRate, Count
};
WildMonInfo encounterData() const { return m_monInfo; }
WildEncounters encounterData() const { return m_encounters; }
EncounterField encounterField() const { return m_encounterField; }
QVector<double> percentages() const { return m_slotPercentages; }
private:
int m_numRows = 0;
int m_numCols = 0;
WildMonInfo m_monInfo;
WildEncounters m_encounters;
EncounterField m_encounterField;
QMap<int,QString> m_groupNames;
QVector<double> m_slotPercentages;

View File

@ -17,7 +17,7 @@ public:
~MonTabWidget();
void populate();
void populateTab(int tabIndex, WildMonInfo monInfo);
void populateTab(int tabIndex, const WildEncounters &encounters);
void clear();
void clearTableAt(int index);

View File

@ -11,21 +11,131 @@ WildPokemon::WildPokemon(int minLevel, int maxLevel, const QString &species)
WildPokemon::WildPokemon() : WildPokemon(5, 5, Project::getEmptySpeciesName())
{}
WildPokemon WildPokemon::fromJson(const OrderedJson::object &json_) {
OrderedJson::object json(json_);
WildPokemon wildMon;
wildMon.minLevel = json.take("min_level").toInt();
wildMon.maxLevel = json.take("max_level").toInt();
wildMon.species = json.take("species").toString();
wildMon.customData = json;
return wildMon;
}
OrderedJson::object WildPokemon::toJson() const {
OrderedJson::object json;
json["min_level"] = this->minLevel;
json["max_level"] = this->maxLevel;
json["species"] = this->species;
OrderedJson::append(&json, this->customData);
return json;
}
WildEncounters WildEncounters::fromJson(const OrderedJson::object &json_, int numEncountersExpected) {
OrderedJson::object json(json_);
WildEncounters encounters;
encounters.active = true;
encounters.encounterRate = json.take("encounter_rate").toInt();
// Read wild pokémon list
OrderedJson::array monsArray = json.take("mons").array_items();
for (const auto &monJson : monsArray) {
auto wildMon = WildPokemon::fromJson(monJson.object_items());
encounters.wildPokemon.append(wildMon);
}
encounters.customData = json;
// If the user supplied too few pokémon for this group then we fill in the rest with default values.
if (numEncountersExpected > 0) {
for (int i = encounters.wildPokemon.length(); i < numEncountersExpected; i++) {
encounters.wildPokemon.append(WildPokemon());
}
}
return encounters;
}
OrderedJson::object WildEncounters::toJson() const {
OrderedJson::object json;
json["encounter_rate"] = this->encounterRate;
OrderedJson::array monArray;
for (const WildPokemon &wildMon : this->wildPokemon) {
monArray.push_back(wildMon.toJson());
}
json["mons"] = monArray;
OrderedJson::append(&json, this->customData);
return json;
}
EncounterField EncounterField::fromJson(const OrderedJson::object &json_) {
OrderedJson::object json(json_);
EncounterField field;
field.name = json.take("type").toString();
OrderedJson::array encounterRatesArray = json.take("encounter_rates").array_items();
for (const auto &val : encounterRatesArray) {
field.encounterRates.append(val.toInt());
}
// Each element of the "groups" array is an object with the group name as the key (e.g. "old_rod")
// and an array of slot numbers indicating which encounter slots in this encounter type belong to that group.
OrderedJson::object groups = json.take("groups").object_items();
for (auto groupPair : groups) {
const QString groupName = groupPair.first;
const OrderedJson::array slotNums = groupPair.second.array_items();
for (auto slotNum : slotNums) {
field.groups[groupName].append(slotNum.toInt());
}
}
field.customData = json;
return field;
}
OrderedJson::object EncounterField::toJson() const {
OrderedJson::object json;
json["type"] = this->name;
OrderedJson::array rateArray;
for (int rate : this->encounterRates) {
rateArray.push_back(rate);
}
json["encounter_rates"] = rateArray;
OrderedJson::object groupsObject;
for (const auto &groupPair : this->groups) {
QVector<int> slots_ = groupPair.second;
OrderedJson::array slotsJson;
std::sort(slots_.begin(), slots_.end());
for (int slotIndex : slots_) {
slotsJson.push_back(slotIndex);
}
const QString groupName = groupPair.first;
groupsObject[groupName] = slotsJson;
}
if (!groupsObject.empty()) json["groups"] = groupsObject;
OrderedJson::append(&json, this->customData);
return json;
}
QMap<QString, int> defaultEncounterRates;
void setDefaultEncounterRate(QString fieldName, int rate) {
defaultEncounterRates[fieldName] = rate;
}
WildMonInfo getDefaultMonInfo(const EncounterField &field) {
WildMonInfo newInfo;
newInfo.active = true;
newInfo.encounterRate = defaultEncounterRates.value(field.name, 1);
WildEncounters getDefaultEncounters(const EncounterField &field) {
WildEncounters encounters;
encounters.active = true;
encounters.encounterRate = defaultEncounterRates.value(field.name, 1);
int size = field.encounterRates.size();
while (size--)
newInfo.wildPokemon.append(WildPokemon());
encounters.wildPokemon.append(WildPokemon());
return newInfo;
return encounters;
}
QVector<double> getWildEncounterPercentages(const EncounterField &field) {
@ -60,7 +170,7 @@ QVector<double> getWildEncounterPercentages(const EncounterField &field) {
return percentages;
}
void combineEncounters(WildMonInfo &to, WildMonInfo from) {
void combineEncounters(WildEncounters &to, WildEncounters from) {
to.encounterRate = from.encounterRate;
if (to.wildPokemon.size() == from.wildPokemon.size()) {

View File

@ -413,10 +413,10 @@ void Editor::addNewWildMonGroup(QWidget *window) {
header.wildMons[fieldName] = model->encounterData();
}
else {
header.wildMons[fieldName] = getDefaultMonInfo(monField);
header.wildMons[fieldName] = getDefaultEncounters(monField);
}
} else {
header.wildMons[fieldName] = getDefaultMonInfo(monField);
header.wildMons[fieldName] = getDefaultEncounters(monField);
}
tabWidget->populateTab(tabIndex, header.wildMons[fieldName]);
} else {

View File

@ -210,6 +210,7 @@ bool Project::load() {
&& readTilesetMetatileLabels()
&& readMiscellaneousConstants()
&& readSpeciesIconPaths()
&& readWildMonConstants()
&& readWildMonData()
&& readEventScriptLabels()
&& readObjEventGfxConstants()
@ -968,65 +969,29 @@ bool Project::saveWildMonData() {
monHeadersObject["label"] = this->wildMonTableName;
monHeadersObject["for_maps"] = true;
OrderedJson::array fieldsInfoArray;
for (EncounterField fieldInfo : this->wildMonFields) {
OrderedJson::object fieldObject;
OrderedJson::array rateArray;
for (int rate : fieldInfo.encounterRates) {
rateArray.push_back(rate);
}
fieldObject["type"] = fieldInfo.name;
fieldObject["encounter_rates"] = rateArray;
OrderedJson::object groupsObject;
for (auto groupNamePair : fieldInfo.groups) {
QString groupName = groupNamePair.first;
OrderedJson::array subGroupIndices;
std::sort(fieldInfo.groups[groupName].begin(), fieldInfo.groups[groupName].end());
for (int slotIndex : fieldInfo.groups[groupName]) {
subGroupIndices.push_back(slotIndex);
}
groupsObject[groupName] = subGroupIndices;
}
if (!groupsObject.empty()) fieldObject["groups"] = groupsObject;
OrderedJson::append(&fieldObject, fieldInfo.customData);
fieldsInfoArray.append(fieldObject);
OrderedJson::array fieldsArray;
for (const EncounterField &field : this->wildMonFields) {
fieldsArray.append(field.toJson());
}
monHeadersObject["fields"] = fieldsInfoArray;
if (!fieldsArray.empty()) monHeadersObject["fields"] = fieldsArray;
OrderedJson::array encountersArray;
for (auto keyPair : this->wildMonData) {
QString key = keyPair.first;
for (auto grouplLabelPair : this->wildMonData[key]) {
QString groupLabel = grouplLabelPair.first;
OrderedJson::object encounterObject;
encounterObject["map"] = key;
encounterObject["base_label"] = groupLabel;
OrderedJson::object encountersMapObject;
encountersMapObject["map"] = key;
encountersMapObject["base_label"] = groupLabel;
WildPokemonHeader encounterHeader = this->wildMonData[key][groupLabel];
for (auto fieldNamePair : encounterHeader.wildMons) {
QString fieldName = fieldNamePair.first;
OrderedJson::object monInfoObject;
WildMonInfo monInfo = encounterHeader.wildMons[fieldName];
monInfoObject["encounter_rate"] = monInfo.encounterRate;
OrderedJson::array monArray;
for (WildPokemon wildMon : monInfo.wildPokemon) {
OrderedJson::object monEntry;
monEntry["min_level"] = wildMon.minLevel;
monEntry["max_level"] = wildMon.maxLevel;
monEntry["species"] = wildMon.species;
OrderedJson::append(&monEntry, wildMon.customData);
monArray.push_back(monEntry);
}
monInfoObject["mons"] = monArray;
OrderedJson::append(&monInfoObject, monInfo.customData);
encounterObject[fieldName] = monInfoObject;
for (auto wildMonsPair : encounterHeader.wildMons) {
const QString fieldName = wildMonsPair.first;
const WildEncounters encounters = wildMonsPair.second;
encountersMapObject[fieldName] = encounters.toJson();
}
OrderedJson::append(&encounterObject, encounterHeader.customData);
encountersArray.push_back(encounterObject);
OrderedJson::append(&encountersMapObject, encounterHeader.customData);
encountersArray.push_back(encountersMapObject);
}
}
monHeadersObject["encounters"] = encountersArray;
@ -1729,18 +1694,11 @@ bool Project::appendTextFile(const QString &path, const QString &text) {
return true;
}
bool Project::readWildMonData() {
this->extraEncounterGroups.clear();
this->wildMonFields.clear();
this->wildMonData.clear();
this->wildMonTableName.clear();
this->encounterGroupLabels.clear();
bool Project::readWildMonConstants() {
this->pokemonMinLevel = 0;
this->pokemonMaxLevel = 100;
this->maxEncounterRate = 2880/16;
this->wildEncountersLoaded = false;
this->customWildMonData = OrderedJson::object();
this->customWildMonGroupData = OrderedJson::object();
if (!userConfig.useEncounterJson) {
return true;
}
@ -1758,16 +1716,31 @@ bool Project::readWildMonData() {
const QString levelRangeFile = projectConfig.getFilePath(ProjectFilePath::constants_pokemon);
const QString minLevelName = projectConfig.getIdentifier(ProjectIdentifier::define_min_level);
const QString maxLevelName = projectConfig.getIdentifier(ProjectIdentifier::define_max_level);
watchFile(levelRangeFile);
defines = parser.readCDefinesByName(levelRangeFile, {minLevelName, maxLevelName});
if (defines.contains(minLevelName))
this->pokemonMinLevel = defines.value(minLevelName);
if (defines.contains(maxLevelName))
this->pokemonMaxLevel = defines.value(maxLevelName);
if (defines.contains(minLevelName)) this->pokemonMinLevel = defines.value(minLevelName);
if (defines.contains(maxLevelName)) this->pokemonMaxLevel = defines.value(maxLevelName);
this->pokemonMinLevel = qMin(this->pokemonMinLevel, this->pokemonMaxLevel);
this->pokemonMaxLevel = qMax(this->pokemonMinLevel, this->pokemonMaxLevel);
return true;
}
bool Project::readWildMonData() {
this->extraEncounterGroups.clear();
this->wildMonFields.clear();
this->wildMonData.clear();
this->wildMonTableName.clear();
this->encounterGroupLabels.clear();
this->wildEncountersLoaded = false;
this->customWildMonData = OrderedJson::object();
this->customWildMonGroupData = OrderedJson::object();
if (!userConfig.useEncounterJson) {
return true;
}
// Read encounter data
const QString wildMonJsonFilepath = projectConfig.getFilePath(ProjectFilePath::json_wild_encounters);
watchFile(wildMonJsonFilepath);
@ -1817,27 +1790,7 @@ bool Project::readWildMonData() {
// and whether the encounters are divided into groups (like fishing rods).
OrderedJson::array fieldsArray = mainArrayObject.take("fields").array_items();
for (const OrderedJson &fieldJson : fieldsArray) {
OrderedJson::object fieldObject = fieldJson.object_items();
EncounterField encounterField;
encounterField.name = fieldObject.take("type").toString();
OrderedJson::array encounterRatesArray = fieldObject.take("encounter_rates").array_items();
for (const auto &val : encounterRatesArray) {
encounterField.encounterRates.append(val.toInt());
}
// Each element of the "groups" array is an object with the group name as the key (e.g. "old_rod")
// and an array of slot numbers indicating which encounter slots in this encounter type belong to that group.
OrderedJson::object groups = fieldObject.take("groups").object_items();
for (auto groupPair : groups) {
const QString groupName = groupPair.first;
for (auto slotNum : groupPair.second.array_items()) {
encounterField.groups[groupName].append(slotNum.toInt());
}
}
encounterField.customData = fieldObject;
auto encounterField = EncounterField::fromJson(fieldJson.object_items());
encounterRateFrequencyMaps.insert(encounterField.name, QMap<int, int>());
this->wildMonFields.append(encounterField);
}
@ -1848,49 +1801,27 @@ bool Project::readWildMonData() {
// pokémon associated with any of the encounter types described by the data we parsed above.
OrderedJson::array encountersArray = mainArrayObject.take("encounters").array_items();
for (const auto &encounterJson : encountersArray) {
OrderedJson::object encounterObj = encounterJson.object_items();
OrderedJson::object encountersMapObject = encounterJson.object_items();
WildPokemonHeader header;
// Check for each possible encounter type.
for (const EncounterField &monField : this->wildMonFields) {
const QString field = monField.name;
if (!encounterObj.contains(field)) {
if (!encountersMapObject.contains(field)) {
// Encounter type isn't present
continue;
}
OrderedJson::object encounterFieldObj = encounterObj.take(field).object_items();
int numExpectedEncounters = monField.encounterRates.length();
header.wildMons[field] = WildEncounters::fromJson(encountersMapObject.take(field).object_items(), numExpectedEncounters);
WildMonInfo monInfo;
monInfo.active = true;
// Read encounter rate
monInfo.encounterRate = encounterFieldObj.take("encounter_rate").toInt();
encounterRateFrequencyMaps[field][monInfo.encounterRate]++;
// Read wild pokémon list
OrderedJson::array monsArray = encounterFieldObj.take("mons").array_items();
for (const auto &monJson : monsArray) {
OrderedJson::object monObj = monJson.object_items();
WildPokemon newMon;
newMon.minLevel = monObj.take("min_level").toInt();
newMon.maxLevel = monObj.take("max_level").toInt();
newMon.species = monObj.take("species").toString();
newMon.customData = monObj;
monInfo.wildPokemon.append(newMon);
}
monInfo.customData = encounterFieldObj;
// If the user supplied too few pokémon for this group then we fill in the rest with default values.
for (int i = monInfo.wildPokemon.length(); i < monField.encounterRates.length(); i++) {
monInfo.wildPokemon.append(WildPokemon());
}
header.wildMons[field] = monInfo;
// Record the value of the encounter rate to help build a default value.
auto encounterRate = header.wildMons[field].encounterRate;
encounterRateFrequencyMaps[field][encounterRate]++;
}
const QString mapConstant = encounterObj.take("map").toString();
const QString baseLabel = encounterObj.take("base_label").toString();
header.customData = encounterObj;
const QString mapConstant = encountersMapObject.take("map").toString();
const QString baseLabel = encountersMapObject.take("base_label").toString();
header.customData = encountersMapObject;
this->wildMonData[mapConstant].insert({baseLabel, header});
this->encounterGroupLabels.append(baseLabel);
}

View File

@ -3,11 +3,11 @@
EncounterTableModel::EncounterTableModel(const WildMonInfo &info, const EncounterField &field, QObject *parent)
EncounterTableModel::EncounterTableModel(const WildEncounters &encounters, const EncounterField &field, QObject *parent)
: QAbstractTableModel(parent),
m_numRows(info.wildPokemon.size()),
m_numRows(encounters.wildPokemon.size()),
m_numCols(ColumnType::Count),
m_monInfo(info),
m_encounters(encounters),
m_encounterField(field),
m_slotPercentages(getWildEncounterPercentages(field))
{
@ -39,13 +39,13 @@ QVariant EncounterTableModel::data(const QModelIndex &index, int role) const {
return m_groupNames.value(row);
case ColumnType::Species:
return m_monInfo.wildPokemon.value(row).species;
return m_encounters.wildPokemon.value(row).species;
case ColumnType::MinLevel:
return m_monInfo.wildPokemon.value(row).minLevel;
return m_encounters.wildPokemon.value(row).minLevel;
case ColumnType::MaxLevel:
return m_monInfo.wildPokemon.value(row).maxLevel;
return m_encounters.wildPokemon.value(row).maxLevel;
case ColumnType::EncounterChance:
return QString::number(m_slotPercentages.value(row, 0) * 100, 'f', 2) + "%";
@ -55,7 +55,7 @@ QVariant EncounterTableModel::data(const QModelIndex &index, int role) const {
case ColumnType::EncounterRate:
if (row == 0) {
return m_monInfo.encounterRate;
return m_encounters.encounterRate;
} else {
return QVariant();
}
@ -67,17 +67,17 @@ QVariant EncounterTableModel::data(const QModelIndex &index, int role) const {
else if (role == Qt::EditRole) {
switch (col) {
case ColumnType::Species:
return m_monInfo.wildPokemon.value(row).species;
return m_encounters.wildPokemon.value(row).species;
case ColumnType::MinLevel:
return m_monInfo.wildPokemon.value(row).minLevel;
return m_encounters.wildPokemon.value(row).minLevel;
case ColumnType::MaxLevel:
return m_monInfo.wildPokemon.value(row).maxLevel;
return m_encounters.wildPokemon.value(row).maxLevel;
case ColumnType::EncounterRate:
if (row == 0) {
return m_monInfo.encounterRate;
return m_encounters.encounterRate;
} else {
return QVariant();
}
@ -124,7 +124,7 @@ bool EncounterTableModel::setData(const QModelIndex &index, const QVariant &valu
int row = index.row();
int col = index.column();
auto wildMon = &m_monInfo.wildPokemon[row];
auto wildMon = &m_encounters.wildPokemon[row];
switch (col) {
case ColumnType::Species: {
@ -158,8 +158,8 @@ bool EncounterTableModel::setData(const QModelIndex &index, const QVariant &valu
case ColumnType::EncounterRate: {
int encounterRate = value.toInt();
if (m_monInfo.encounterRate != encounterRate) {
m_monInfo.encounterRate = encounterRate;
if (m_encounters.encounterRate != encounterRate) {
m_encounters.encounterRate = encounterRate;
emit edited();
}
break;

View File

@ -7,7 +7,7 @@
static WildMonInfo encounterClipboard;
static WildEncounters encounterClipboard;
MonTabWidget::MonTabWidget(Editor *editor, QWidget *parent) : QTabWidget(parent) {
this->editor = editor;
@ -64,9 +64,9 @@ void MonTabWidget::paste(int index) {
if (!encounterClipboard.active) return;
clearTableAt(index);
WildMonInfo newInfo = getDefaultMonInfo(this->editor->project->wildMonFields.at(index));
combineEncounters(newInfo, encounterClipboard);
populateTab(index, newInfo);
WildEncounters encounters = getDefaultEncounters(this->editor->project->wildMonFields.at(index));
combineEncounters(encounters, encounterClipboard);
populateTab(index, encounters);
emit edited();
}
@ -93,7 +93,7 @@ void MonTabWidget::actionAddDeleteTab(int index) {
deactivateTab(index);
} else {
// add tab
populateTab(index, getDefaultMonInfo(editor->project->wildMonFields.at(index)));
populateTab(index, getDefaultEncounters(editor->project->wildMonFields.at(index)));
setCurrentIndex(index);
}
emit edited();
@ -111,18 +111,18 @@ void MonTabWidget::deactivateTab(int tabIndex) {
QTableView *speciesTable = tableAt(tabIndex);
EncounterTableModel *oldModel = static_cast<EncounterTableModel *>(speciesTable->model());
WildMonInfo monInfo = oldModel->encounterData();
monInfo.active = false;
EncounterTableModel *newModel = new EncounterTableModel(monInfo, editor->project->wildMonFields[tabIndex], this);
WildEncounters encounters = oldModel->encounterData();
encounters.active = false;
EncounterTableModel *newModel = new EncounterTableModel(encounters, editor->project->wildMonFields[tabIndex], this);
speciesTable->setModel(newModel);
setTabActive(tabIndex, false);
}
void MonTabWidget::populateTab(int tabIndex, WildMonInfo monInfo) {
void MonTabWidget::populateTab(int tabIndex, const WildEncounters &encounters) {
QTableView *speciesTable = tableAt(tabIndex);
EncounterTableModel *model = new EncounterTableModel(monInfo, editor->project->wildMonFields[tabIndex], this);
EncounterTableModel *model = new EncounterTableModel(encounters, editor->project->wildMonFields[tabIndex], this);
connect(model, &EncounterTableModel::edited, this, &MonTabWidget::edited);
speciesTable->setModel(model);

View File

@ -74,11 +74,11 @@ QList<WildMonSearch::RowData> WildMonSearch::search(const QString &species) cons
for (const auto &grouplLabelPair : this->project->wildMonData[mapConstant]) {
QString groupName = grouplLabelPair.first;
WildPokemonHeader encounterHeader = this->project->wildMonData[mapConstant][groupName];
for (const auto &fieldNamePair : encounterHeader.wildMons) {
QString fieldName = fieldNamePair.first;
WildMonInfo monInfo = encounterHeader.wildMons[fieldName];
for (int slot = 0; slot < monInfo.wildPokemon.length(); slot++) {
const WildPokemon wildMon = monInfo.wildPokemon.at(slot);
for (const auto &wildMonsPair : encounterHeader.wildMons) {
const QString fieldName = wildMonsPair.first;
const WildEncounters encounters = wildMonsPair.second;
for (int slot = 0; slot < encounters.wildPokemon.length(); slot++) {
const WildPokemon wildMon = encounters.wildPokemon.at(slot);
if (wildMon.species == species) {
RowData rowData;
rowData.groupName = groupName;