Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions SerialPrograms/Source/Pokemon/Pokemon_AdvRng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ namespace PokemonAutomation{
namespace Pokemon{

void level_up_observed_pokemon(AdvObservedPokemon& pokemon, const StatReads& newstats, const EVs& evyield){
if (pokemon.level.empty() || pokemon.stats.empty() || pokemon.evs.empty()){
return;
}

uint8_t newlevel = pokemon.level.back() + 1;
pokemon.level.emplace_back(newlevel);

Expand Down Expand Up @@ -259,6 +263,13 @@ uint8_t slot_number_from_roll(uint8_t roll, size_t size, bool super_rod) {
}
}

std::string base_species_slug(const std::string& species){
if (species.find("unown") != std::string::npos){
return "unown";
}
return species;
}

int slot_to_unownform(AdvEncounterSlot slot){
if (slot.species.find("unown") == std::string::npos){
return -1;
Expand Down Expand Up @@ -510,8 +521,7 @@ bool check_for_match(AdvPokemonResult res, AdvRngFilters target, int16_t gender_
}

bool check_for_match(AdvWildPokemonResult res, AdvRngFilters target, int16_t gender_threshold, uint16_t tid_xor_sid){
std::string res_name = res.species.find("unown") != std::string::npos ? "unown" : res.species;
return (target.species == res_name)
return (base_species_slug(target.species) == base_species_slug(res.species))
&& (target.level == res.level)
&& (target.nature == AdvNature::Any || (res.nature == target.nature))
&& (target.ability == AdvAbility::Any || (res.ability == target.ability))
Expand Down
5 changes: 5 additions & 0 deletions SerialPrograms/Source/Pokemon/Pokemon_AdvRng.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ Pokemon::NatureAdjustments nature_to_adjustment(AdvNature nature);
Pokemon::AdvNature string_to_nature(const std::string& nature_string);
std::string nature_to_string(const AdvNature& nature);

// Forms that cannot be told apart from an observation (e.g. Unown letters) share a base slug.
// Encounter slots carry the form-specific slug, while an observed species only ever carries
// the base slug, so both sides must be folded before they are compared.
std::string base_species_slug(const std::string& species);

std::string gender_to_string(const AdvGender& gender);

AdvGender gender_from_gender_value(uint8_t gender_value, int16_t threshold);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <utility>
#include <sstream>
#include <algorithm>
#include <set>
#include "CommonFramework/Exceptions/OperationFailedException.h"
#include "PokemonFRLG_BlindNavigation.h"
#include "PokemonFRLG_RngCalibration.h"
Expand Down Expand Up @@ -309,9 +310,7 @@ bool validate_level(
){
std::vector<AdvEncounterSlot> valid_slots;
for (const AdvEncounterSlot& slot : ENCOUNTER_SLOTS){
if (filters.species == slot.species

){
if (base_species_slug(filters.species) == base_species_slug(slot.species)){
valid_slots.push_back(slot);
}
}
Expand All @@ -323,22 +322,31 @@ bool validate_level(
return true;
}
}
// try all levels within the encounter slots for the observed species if there was no valid match
std::vector<uint8_t> levels;
// try all levels within the encounter slots for the observed species if there was no valid match.
// slots commonly overlap in level, so only count each distinct level once.
const AdvRngFilters original_filters = filters;
const uint8_t original_level = pokemon.level[0];
std::set<uint8_t> levels;
for (const AdvEncounterSlot& slot : valid_slots){
for (uint8_t level = slot.minlevel; level <= slot.maxlevel; level++){
if (levels.find(level) != levels.end()){
continue;
}
pokemon.level[0] = level;
filters = observation_to_filters(pokemon, BASE_STATS, filters.method);
if (ranges_are_valid(filters.ivs)){
levels.push_back(level);
AdvRngFilters candidate = observation_to_filters(pokemon, BASE_STATS, filters.method);
if (ranges_are_valid(candidate.ivs)){
levels.insert(level);
}
}
}
if (levels.size() == 1){
pokemon.level[0] = levels[0];
pokemon.level[0] = *levels.begin();
filters = observation_to_filters(pokemon, BASE_STATS, filters.method);
return true;
}
// no unambiguous level was found: leave the caller's state as it was found
pokemon.level[0] = original_level;
filters = original_filters;
return false;
}

Expand All @@ -352,9 +360,19 @@ void update_filters(
){
level_up_observed_pokemon(pokemon, stats, evyield);

if (pokemon.level.empty() || pokemon.stats.empty() || pokemon.evs.empty()){
filters.ivs = { {0,31}, {0,31}, {0,31}, {0,31}, {0,31}, {0,31} };
return;
}

const uint8_t new_level = pokemon.level.back();
const EVs new_evs = pokemon.evs.back();

while (true){
// in the worst case (the new stats are the problem), start over
if (pokemon.level.size() == 0){
pokemon.level = { new_level };
pokemon.stats = { stats };
pokemon.evs = { new_evs };
filters.ivs = { {0,31}, {0,31}, {0,31}, {0,31}, {0,31}, {0,31} };
return;
}
Expand All @@ -364,15 +382,17 @@ void update_filters(
if (!ranges_are_valid(new_filters.ivs)){
IvRanges new_stat_ivs = calc_iv_ranges(BASE_STATS, pokemon.level.back(), pokemon.evs.back(), pokemon.stats.back(), nature_to_adjustment(pokemon.nature));
if (!ranges_are_valid(new_stat_ivs)){
// remove newest stats first if they aren't valid
pokemon.level.erase(pokemon.level.begin());
pokemon.stats.erase(pokemon.stats.begin());
pokemon.evs.erase(pokemon.evs.begin());
}else{
// remove oldest stats first
// the newest reading is impossible on its own.
// don't keep it
pokemon.level.pop_back();
pokemon.stats.pop_back();
pokemon.evs.pop_back();
}else{
// the newest reading is self-consistent but conflicts with an older one.
// discard the oldest reading until things are consistent again
pokemon.level.erase(pokemon.level.begin());
pokemon.stats.erase(pokemon.stats.begin());
pokemon.evs.erase(pokemon.evs.begin());
}
continue;
}
Expand Down
Loading