diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_DigitReader.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_DigitReader.cpp index f0681efa5b..08280d7ea9 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_DigitReader.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_DigitReader.cpp @@ -251,8 +251,8 @@ int read_digits_waterfill_template( auto iter = session->make_iterator(min_area); WaterfillObject obj; while (blobs.size() < 8 && iter->find_next(obj, false)){ - // Require at least 3px wide AND 3px tall to discard noise fragments. - if (obj.max_x - obj.min_x < 3 || obj.max_y - obj.min_y < 3){ + // Require at least 5px wide AND 15px tall to discard noise fragments. + if (obj.max_x - obj.min_x < 5 || obj.max_y - obj.min_y < 15){ continue; } // Use min_x as key so the map is automatically sorted left-to-right. diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_EggRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_EggRng.cpp index 629e337a6a..2b61b8071d 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_EggRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_EggRng.cpp @@ -534,6 +534,7 @@ bool EggRng::held_frame_check( // read stats AdvObservedPokemon observed_egg = read_summary(env.console, context, LANGUAGE); + observed_egg.level[0] = 5; AdvRngFilters filters = observation_to_filters(observed_egg, EGG_STATS.base_stats, AdvRngMethod::Any); RNG_FILTERS.set(filters); @@ -648,6 +649,7 @@ bool EggRng::pickup_frame_check( // ... read ... AdvObservedPokemon observed_egg = read_summary(env.console, context, LANGUAGE); + observed_egg.level[0] = 5; AdvRngFilters filters = observation_to_filters(observed_egg, EGG_STATS.base_stats, AdvRngMethod::Any); RNG_FILTERS.set(filters); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_GiftRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_GiftRng.cpp index 523a6dc47f..5a7d27e98d 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_GiftRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_GiftRng.cpp @@ -243,38 +243,47 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext& BaseStats BASE_STATS; int16_t GENDER_THRESHOLD = -1; + uint8_t LEVEL = 0; switch (TARGET){ case PokemonFRLG_RngTarget::magikarp: BASE_STATS = { 20, 10, 55, 15, 20, 80 }; GENDER_THRESHOLD = 126; + LEVEL = 5; break; case PokemonFRLG_RngTarget::hitmonchan: BASE_STATS = { 50, 105, 79, 35, 110, 76 }; GENDER_THRESHOLD = -1; + LEVEL = 25; break; case PokemonFRLG_RngTarget::hitmonlee: BASE_STATS = { 50, 120, 53, 35, 110, 87 }; GENDER_THRESHOLD = -1; + LEVEL = 25; break; case PokemonFRLG_RngTarget::eevee: BASE_STATS = { 55, 55, 50, 45, 65, 55 }; GENDER_THRESHOLD = 30; + LEVEL = 25; break; case PokemonFRLG_RngTarget::lapras: BASE_STATS = { 130, 85, 80, 85, 95, 60 }; GENDER_THRESHOLD = 126; + LEVEL = 25; break; case PokemonFRLG_RngTarget::omanyte: BASE_STATS = { 35, 40, 100, 90, 55, 35 }; GENDER_THRESHOLD = 30; + LEVEL = 5; break; case PokemonFRLG_RngTarget::kabuto: BASE_STATS = { 30, 80, 90, 55, 45, 55 }; GENDER_THRESHOLD = 30; + LEVEL = 5; break; case PokemonFRLG_RngTarget::aerodactyl: BASE_STATS = { 80, 105, 65, 60, 75, 130 }; GENDER_THRESHOLD = 30; + LEVEL = 5; break; case PokemonFRLG_RngTarget::gamecornerabra: BASE_STATS = { 25, 20, 15, 105, 55, 90 }; @@ -292,10 +301,12 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext& case PokemonFRLG_RngTarget::gamecornerscyther: BASE_STATS = { 70, 110, 80, 55, 80, 105 }; GENDER_THRESHOLD = 126; + LEVEL = 25; break; case PokemonFRLG_RngTarget::gamecornerpinsir: BASE_STATS = { 65, 125, 100, 55, 70, 85 }; GENDER_THRESHOLD = 126; + LEVEL = 18; break; case PokemonFRLG_RngTarget::gamecornerporygon: BASE_STATS = { 65, 60, 70, 85, 75, 40 }; @@ -304,6 +315,7 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext& case PokemonFRLG_RngTarget::togepi: BASE_STATS = { 35, 20, 65, 40, 65, 20 }; GENDER_THRESHOLD = 30; + LEVEL = 5; break; default: break; @@ -433,6 +445,9 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext& } AdvObservedPokemon pokemon = read_summary(env.console, context, LANGUAGE, SPECIES_LIST); + if (LEVEL > 0){ + pokemon.level[0] = LEVEL; + } AdvRngFilters filters = observation_to_filters(pokemon, BASE_STATS); RNG_FILTERS.set(filters); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp index 8258be4cc0..694f4af839 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp @@ -300,6 +300,48 @@ bool ranges_are_valid(IvRanges ivs){ ); } +// reading raw stats is more reliable than reading the level +bool validate_level( + AdvRngFilters& filters, + AdvObservedPokemon& pokemon, + const std::vector& ENCOUNTER_SLOTS, + const BaseStats& BASE_STATS +){ + std::vector valid_slots; + for (const AdvEncounterSlot& slot : ENCOUNTER_SLOTS){ + if (filters.species == slot.species + + ){ + valid_slots.push_back(slot); + } + } + for (const AdvEncounterSlot& slot : valid_slots){ + if ( filters.level >= slot.minlevel + && filters.level <= slot.maxlevel + && ranges_are_valid(filters.ivs) + ){ + return true; + } + } + // try all levels within the encounter slots for the observed species if there was no valid match + std::vector levels; + for (const AdvEncounterSlot& slot : valid_slots){ + for (uint8_t level = slot.minlevel; level <= slot.maxlevel; level++){ + pokemon.level[0] = level; + filters = observation_to_filters(pokemon, BASE_STATS, filters.method); + if (ranges_are_valid(filters.ivs)){ + levels.push_back(level); + } + } + } + if (levels.size() == 1){ + pokemon.level[0] = levels[0]; + filters = observation_to_filters(pokemon, BASE_STATS, filters.method); + return true; + } + return false; +} + void update_filters( AdvRngFilters& filters, AdvObservedPokemon& pokemon, diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h index 6910c927dd..51d440eefe 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h @@ -133,6 +133,16 @@ std::vector> get_egg_search_results( uint16_t tid_xor_sid ); +// double-check read level against possible encounter slots for the observed species and return true if valid +// if stats from the original read are invalid, this tries all possible levels and checks whether IVs are valid, +// returning true if exactly one level produces valid IV ranges +bool validate_level( + AdvRngFilters& filters, + AdvObservedPokemon& pokemon, + const std::vector& ENCOUNTER_SLOTS, + const BaseStats& BASE_STATS +); + // update IV ranges for RNG search filters with new stats/EVs after leveling up void update_filters( AdvRngFilters& filters, diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RoamingLegendaryRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RoamingLegendaryRng.cpp index 3ee66efd53..4e296c89d2 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RoamingLegendaryRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RoamingLegendaryRng.cpp @@ -404,7 +404,12 @@ void RoamingLegendaryRng::program(SingleSwitchProgramEnvironment& env, ProContro go_to_summary(env.console, context); AdvObservedPokemon pokemon = read_summary(env.console, context, LANGUAGE, ROAMER_ROUTE1_SUBSET); + pokemon.level[0] = 50; AdvRngFilters filters = observation_to_filters(pokemon, BASE_STATS); + filters.ivs.defense = {0,0}; + filters.ivs.spatk = {0,0}; + filters.ivs.spdef = {0,0}; + filters.ivs.speed = {0,0}; RNG_FILTERS.set(filters); std::vector search_hits = refine_calibration_with_rare_candy( @@ -413,6 +418,10 @@ void RoamingLegendaryRng::program(SingleSwitchProgramEnvironment& env, ProContro MAX_HISTORY_LENGTH, MAX_RARE_CANDIES, AdvRngMethod::Method1, false, stats.errors, NOTIFICATION_ERROR_RECOVERABLE, [&](AdvRngFilters& f){ + f.ivs.defense = {0,0}; + f.ivs.spatk = {0,0}; + f.ivs.spdef = {0,0}; + f.ivs.speed = {0,0}; return get_search_results(env.console, searcher, f, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD); }, [&](const std::vector& h){ RNG_CALIBRATION.set_hits(h); }, diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp index 41afbff708..aa69a38208 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp @@ -711,6 +711,7 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte // Stage 1: initial search -- starter received AdvObservedPokemon pokemon = read_summary(env.console, context, LANGUAGE, SPECIES_LIST); + pokemon.level[0] = 5; AdvRngFilters filters = observation_to_filters(pokemon, BASE_STATS); RNG_FILTERS.set(filters); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StaticRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StaticRng.cpp index f5b05be874..2a5d3d01d3 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StaticRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StaticRng.cpp @@ -243,50 +243,62 @@ void StaticRng::program(SingleSwitchProgramEnvironment& env, ProControllerContex BaseStats BASE_STATS; int16_t GENDER_THRESHOLD = -1; + uint8_t LEVEL = 30; switch (TARGET){ case PokemonFRLG_RngTarget::electrode: BASE_STATS = { 60, 50, 70, 80, 80, 140 }; GENDER_THRESHOLD = -1; + LEVEL = 34; break; case PokemonFRLG_RngTarget::snorlax: BASE_STATS = { 160, 110, 65, 65, 110, 30 }; GENDER_THRESHOLD = 30; + LEVEL = 30; break; case PokemonFRLG_RngTarget::articuno: BASE_STATS = { 90, 85, 100, 95, 125, 85 }; GENDER_THRESHOLD = -1; + LEVEL = 50; break; case PokemonFRLG_RngTarget::zapdos: BASE_STATS = { 90, 90, 85, 125, 90, 100 }; GENDER_THRESHOLD = -1; + LEVEL = 50; break; case PokemonFRLG_RngTarget::moltres: BASE_STATS = { 90, 100, 90, 125, 85, 90 }; GENDER_THRESHOLD = -1; + LEVEL = 50; break; case PokemonFRLG_RngTarget::mewtwo: BASE_STATS = { 106, 110, 90, 154, 90, 130 }; GENDER_THRESHOLD = -1; + LEVEL = 70; break; case PokemonFRLG_RngTarget::hypno: BASE_STATS = { 85, 73, 70, 73, 115, 67 }; GENDER_THRESHOLD = 126; + LEVEL = 30; break; case PokemonFRLG_RngTarget::hooh: BASE_STATS = { 106, 130, 90, 110, 154, 90 }; GENDER_THRESHOLD = -1; + LEVEL = 70; break; case PokemonFRLG_RngTarget::lugia: BASE_STATS = { 106, 90, 130, 90, 154, 110 }; GENDER_THRESHOLD = -1; + LEVEL = 70; break; case PokemonFRLG_RngTarget::deoxys_attack: BASE_STATS = { 50, 180, 20, 180, 20, 150 }; GENDER_THRESHOLD = -1; + LEVEL = 30; break; case PokemonFRLG_RngTarget::deoxys_defense: BASE_STATS = { 50, 70, 160, 70, 160, 90 }; GENDER_THRESHOLD = -1; + LEVEL = 30; break; default: break; @@ -427,6 +439,7 @@ void StaticRng::program(SingleSwitchProgramEnvironment& env, ProControllerContex go_to_summary(env.console, context); AdvObservedPokemon pokemon = read_summary(env.console, context, LANGUAGE, SPECIES_LIST); + pokemon.level[0] = LEVEL; AdvRngFilters filters = observation_to_filters(pokemon, BASE_STATS); RNG_FILTERS.set(filters); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_WildRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_WildRng.cpp index b779e0a994..7be913d067 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_WildRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_WildRng.cpp @@ -530,8 +530,15 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext& int16_t gender_threshold = species_stats.gender_threshold; AdvRngFilters filters = observation_to_filters(pokemon, base_stats, AdvRngMethod::Any); + bool valid_stats = validate_level(filters, pokemon, ENCOUNTER_SLOTS, base_stats); RNG_FILTERS.set(filters); + if (!valid_stats){ + env.log("Invalid encounter stats. Resetting...", COLOR_RED); + failed_searches++; + continue; + } + std::vector search_hits = refine_calibration_with_rare_candy( env, context, LANGUAGE, pokemon, filters, base_stats, uncertain_history, calibration_history, calibrations,