From f1bd5ac3ae3a5c7c7c185e5471267448b52c67c8 Mon Sep 17 00:00:00 2001 From: dayfine Date: Mon, 2 Nov 2026 05:33:01 -0800 Subject: [PATCH 1/3] BDSP AutoStory: plan 01.05 to 01.10 on the maps Segment 01's walks from Oreburgh to Floaroma were events tuned when a turn also stepped. They are now route, step and scene rows on the JSON maps, with the triggers and warps of the Diamond decomp's zone events: Barry's bump in Oreburgh (6, 12..15), the Gate's row 22, Route 203 into Jubilife, Rowan's trigger (45..47, 7), Route 204 up to the Ravaged Path, and the human's line up Route 204's north half into Floaroma. Routes cross maps on joined worlds and fight the trainers of Routes 203 and 204 who see us. Only the Rock Smash stays an event. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../AutoStory/PokemonBDSP_AutoStory_Events.h | 5 +- .../PokemonBDSP_AutoStory_Events_Jubilife.cpp | 46 +---- .../PokemonBDSP_AutoStory_Events_Oreburgh.cpp | 47 +---- .../PokemonBDSP_AutoStory_Events_Route203.cpp | 49 +---- .../PokemonBDSP_AutoStory_Events_Route204.cpp | 145 ++------------ .../PokemonBDSP_AutoStory_StoryExecutor.cpp | 29 ++- .../PokemonBDSP_AutoStory_StoryExecutor.h | 12 +- .../PokemonBDSP_AutoStory_StoryPlaces.cpp | 187 +++++++++++++++--- .../PokemonBDSP_AutoStory_StoryPlaces.h | 10 +- .../PokemonBDSP_AutoStory_StoryTable.cpp | 141 ++++++++++--- ...PokemonBDSP_AutoStory_StoryTable_Tests.cpp | 2 +- .../AutoStory/PokemonBDSP_AutoStory_Walks.cpp | 74 ------- .../AutoStory/PokemonBDSP_AutoStory_Walks.h | 16 -- 13 files changed, 330 insertions(+), 433 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events.h index f3917b1c0c..388b396eae 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events.h +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events.h @@ -11,8 +11,9 @@ * the executor turns that into a failed step. * * They are in files by area: _Events_Lake (00.00 to 00.03), _Sandgem - * (00.04), _Jubilife (00.05, 01.01, 01.06 to 01.08), _Oreburgh (01.02 to - * 01.05) and _Route204 (01.09 to 01.11). + * (00.04), _Jubilife (00.05, 01.01), _Route203 (01.02), _Oreburgh (01.03 + * and 01.04) and _Route204 (01.09's Rock Smash, 01.11). 01.05 to 01.10 + * are planned walks in the story table. * */ diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Jubilife.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Jubilife.cpp index e3d62f952f..c1dc95e0dd 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Jubilife.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Jubilife.cpp @@ -1,10 +1,10 @@ -/* BDSP AutoStory - Events: Jubilife City (00.05, 01.01, 01.07) +/* BDSP AutoStory - Events: Jubilife City (00.05, 01.01) * * From: https://github.com/PokemonAutomation/ * * The Jubilife beats of 00.05 the story table cannot plan (up the steps, - * Barry's Town Map, the talk to a wandering clown), and segment 01's walks - * out of the Center to Barry and up to Rowan's scene. + * Barry's Town Map, the talk to a wandering clown), and segment 01's walk + * out of the Center to Barry. * */ @@ -118,12 +118,6 @@ Pokemon::Outcome talk_to_wanderer(StoryExecutor& executor, Pokemon::RunState&, c } -// 01.07: Rowan's and Dawn's lines after the tag battle. 01.08 starts here, -// from a save at the top of the street. -void rowan_after_the_battle(VideoStream& stream, ProControllerContext& context){ - finish_dialogue(stream, context, BUTTON_B, "Rowan and Dawn after the battle"); -} - // 01.01 from the save at the Jubilife Center's PC: healed at the nurse, // out of the door and through the plaza to the street east to Route 203. void jubilife_heal_and_out_to_street(VideoStream& stream, ProControllerContext& context){ @@ -215,37 +209,6 @@ void east_to_barry(VideoStream& stream, ProControllerContext& context){ } } -// 01.07: west along Jubilife's long road to the crossroads. -void west_to_the_crossroads(VideoStream& stream, ProControllerContext& context){ - // Route 203's row 18 carries on as Jubilife's long road. From the first - // tile the crossroads with the street north to Route 204 is 14 tiles - // west; the street is about 4 tiles wide, so a stick hold does (by hand, - // 2026-10-31: 2 x 1120 ms landed mid-street; 7 D-pad taps stopped below - // a building, and 18 went on to the road down to Route 202, r230b). - route_turn(stream, context, Direction::WEST); - pbf_move_left_joystick(context, {-1, 0}, route_open_hold(Direction::WEST, 14), 300ms); - context.wait_for_all_requests(); -} - -// 01.07: north up the street until Rowan's scene starts. -void north_to_rowan(VideoStream& stream, ProControllerContext& context){ - // North up the street: Rowan's scene starts a few tiles up (by hand, a - // 6 s walk). It opens with a cutscene, not a dialog box, so the walk - // reads as stopped; wait for his first line. The green-haired man walks - // about the crossroads and may stand in our way: wait and walk again. - bool scene = false; - for (size_t tries = 0; tries < 3 && !scene; tries++){ - const WalkResult up = walk_dir(stream, context, Direction::NORTH, 6000ms, "north up the street to Route 204"); - scene = up.stop == WalkStop::DIALOG || dialogue_opens(stream, context, 8000ms); - if (!scene){ - pbf_wait(context, 2500ms); - } - } - if (!scene){ - fail_step(stream, "Jubilife: Rowan's scene did not start up the street north."); - } -} - } @@ -253,11 +216,8 @@ void add_jubilife_events(StoryEvents& events){ events.emplace("up_the_steps", console_event(up_the_steps)); events.emplace("barry_town_map", console_event(barry_town_map)); events.emplace("talk_to_wanderer", talk_to_wanderer); - events.emplace("rowan_after_the_battle", console_event(rowan_after_the_battle)); events.emplace("jubilife_center_to_street", console_event(jubilife_center_to_street)); events.emplace("east_to_barry", console_event(east_to_barry)); - events.emplace("west_to_the_crossroads", console_event(west_to_the_crossroads)); - events.emplace("north_to_rowan", console_event(north_to_rowan)); } diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Oreburgh.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Oreburgh.cpp index 163993a003..7628d3ce8c 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Oreburgh.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Oreburgh.cpp @@ -1,10 +1,9 @@ -/* BDSP AutoStory - Events: Oreburgh (01.03 to 01.05) +/* BDSP AutoStory - Events: Oreburgh (01.03 to 01.04) * * From: https://github.com/PokemonAutomation/ * - * Segment 01's walks in Oreburgh: the mine and Roark, the Gym, and back - * out of the Oreburgh Gate, as they ran on the console (2026-10-29 to - * 10-31). + * Segment 01's walks in Oreburgh: the mine and Roark, and the Gym, as + * they ran on the console (2026-10-29 to 10-31). * */ @@ -21,7 +20,6 @@ #include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_Maps.h" #include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_StoryMaps.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Indoors.h" -#include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Location.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_PlannedRoute.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_RouteWalk.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Walk.h" @@ -504,42 +502,6 @@ void gym_until_roark_beaten(VideoStream& stream, ProControllerContext& context){ } } -// 01.05: out of the gym onto its doorstep. -void out_of_the_gym(VideoStream& stream, ProControllerContext& context){ - // 01.04 ends in front of Roark, just after his battle: its save at - // 01.05's start is right after the win (2026-10-31, at the user's - // request), so a failure later never has to beat him again. - // No trip back to the Oreburgh Center (2026-10-31, the user): talk_or_fight() - // gives a Potion after Roark when HP is low, and Jubilife's Center is on - // the way. Out of the gym onto its doorstep, which is on the Center door's - // row: west along it reaches the stairs' cliff. - if (!dpad_out_of_building(stream, context, 40, "out of the gym")){ - fail_step(stream, "Oreburgh Gym: did not leave by its door."); - } -} - -// 01.05: up to the platform by the Oreburgh Gate and into it (Barry bumps into us there). -void into_oreburgh_gate(VideoStream& stream, ProControllerContext& context){ - // From the gym's doorstep, as walked by hand on 2026-10-29. - walk_dir(stream, context, Direction::WEST, 20000ms, "west to the stairs' cliff"); - walk_dir(stream, context, Direction::NORTH, 10000ms, "north along the cliff"); - hold_tiles(stream, context, Direction::EAST, 2); - walk_dir(stream, context, Direction::NORTH, 5000ms, "north up the stairs"); - walk_dir(stream, context, Direction::WEST, 6000ms, "west along the platform"); - hold_tiles(stream, context, Direction::SOUTH, 1); - // Barry runs into us on the platform ("*thud*") the first time; his lines - // end with no battle. - west_until_transition(stream, context, "Into Oreburgh Gate", 8); -} - -// 01.05: west through the Gate until Route 203's banner shows. -void out_of_oreburgh_gate(VideoStream& stream, ProControllerContext& context){ - // The cave's fade out of the Gate does not always read as a transition - // (r89), so the walk west ends on Route 203's location banner, which - // shows for a few seconds as we step out. 01.06 starts there, from a save. - west_until_transition(stream, context, "West out of Oreburgh Gate", 8, location_banner_showing); -} - } @@ -551,9 +513,6 @@ void add_oreburgh_events(StoryEvents& events){ events.emplace("oreburgh_center_heal", console_event(oreburgh_center_heal)); events.emplace("out_of_the_center", console_event(out_of_the_center)); events.emplace("gym_until_roark_beaten", console_event(gym_until_roark_beaten)); - events.emplace("out_of_the_gym", console_event(out_of_the_gym)); - events.emplace("into_oreburgh_gate", console_event(into_oreburgh_gate)); - events.emplace("out_of_oreburgh_gate", console_event(out_of_oreburgh_gate)); } diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route203.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route203.cpp index 6c3b272bab..6b15500f78 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route203.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route203.cpp @@ -1,10 +1,9 @@ -/* BDSP AutoStory - Events: Route 203 (01.02, 01.06) +/* BDSP AutoStory - Events: Route 203 (01.02) * * From: https://github.com/PokemonAutomation/ * * Segment 01's walks along Route 203: east to the Oreburgh Gate and through - * it to Oreburgh, and back west to Jubilife, as they ran on the console - * (2026-10-29 to 10-31). + * it to Oreburgh, as they ran on the console (2026-10-29 to 10-31). * */ @@ -18,16 +17,12 @@ #include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_StoryMaps.h" -#include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Location.h" -#include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_PlannedRoute.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_RouteWalk.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Walk.h" #include "PokemonBDSP_AutoStory_Talks.h" #include "PokemonBDSP_AutoStory_Walks.h" #include "PokemonBDSP_AutoStory_WildBattle.h" #include "PokemonBDSP_AutoStory_Events.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_Maps.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -188,51 +183,11 @@ void oreburgh_welcome(VideoStream& stream, ProControllerContext& context){ context.wait_for_all_requests(); } -// 01.06: Route 203 on its grid to row 18, below the girl. -Pokemon::Outcome route_203_to_row_18(StoryExecutor& executor, Pokemon::RunState& state, const Pokemon::StoryStep&){ - VideoStream& stream = executor.stream(); - ProControllerContext& context = executor.context(); - // On the grid (Maps/Route203.json, from the decomp, - // its east stretch checked by hand): south from the Gate down the sand, - // west along the sand's bottom row and on under the girl's row, which - // runs straight to Jubilife as row 18. Trainers the map does not list - // are fought where they stop us. - RouteTrainers trainers{{}, [&](const std::string& who){ - talk_or_fight(stream, context, "Route 203: " + who); - }}; - const std::vector at = walk_route(stream, context, run_from_wild_battle, route203_maps(), map_region("Route203", "gate_exit"), trainers, RouteWalkPlan{on_route203_west_line, "Route 203 to row 18", {}, route_plan_costs()}); - if (at.size() != 1){ - fail_step(stream, "Route 203: the walk to row 18 did not end on a known tile."); - } - return Pokemon::outcome_ok(Pokemon::tile_pose("Route203", at)); -} - -// 01.06: west along row 18 into Jubilife, counting tiles. -Pokemon::Outcome row_18_into_jubilife(StoryExecutor& executor, Pokemon::RunState& state, const Pokemon::StoryStep&){ - VideoStream& stream = executor.stream(); - ProControllerContext& context = executor.context(); - const std::vector at = Pokemon::tiles_of(state.pose); - if (at.size() != 1){ - fail_step(stream, "Route 203: row 18's walk needs the one tile the walk to it ended on."); - } - // Row 18 has no wall before Jubilife, so count the tiles: from column - // `x` it is x + 1 taps to Jubilife's first tile (by hand from column 31: - // 32 taps, Jubilife's banner on the last one). 01.07 starts there. - dpad_tiles(stream, context, DPAD_LEFT, (size_t)at[0].x + 1, "west along row 18 into Jubilife"); - context.wait_for_all_requests(); - if (!location_banner_showing(*stream.video().snapshot().frame)){ - fail_step(stream, "Route 203: no Jubilife banner after counting the tiles west."); - } - return Pokemon::outcome_ok(); -} - } void add_route203_events(StoryEvents& events){ events.emplace("route_203_east_to_the_gate", route_203_east_to_the_gate); - events.emplace("route_203_to_row_18", route_203_to_row_18); - events.emplace("row_18_into_jubilife", row_18_into_jubilife); events.emplace("the_hiker", console_event(the_hiker)); events.emplace("hidden_moves_card", console_event(hidden_moves_card)); events.emplace("gate_east_to_oreburgh", console_event(gate_east_to_oreburgh)); diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route204.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route204.cpp index 9988c24e87..438bf8e3c6 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route204.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Events_Route204.cpp @@ -1,27 +1,18 @@ -/* BDSP AutoStory - Events: Route 204 to Floaroma (01.08 to 01.11) +/* BDSP AutoStory - Events: Route 204 to Floaroma (01.09, 01.11) * * From: https://github.com/PokemonAutomation/ * - * Segment 01's walks up Route 204 South and through the Ravaged Path, up Route 204 North into - * Floaroma, and to the old couple in its flower field, as they ran on the - * console (2026-10-31). + * The Ravaged Path's Rock Smash, and segment 01's walks to the old couple + * in Floaroma's flower field, as they ran on the console (2026-10-31). * */ -#include #include "CommonFramework/ImageTools/ImageBoxes.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h" #include "PokemonBDSP/Inference/PokemonBDSP_SelectionArrow.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_Maps.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_Maps.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_Maps.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_StoryMaps.h" -#include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Location.h" -#include "PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_PlanCosts.h" -#include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_PlannedRoute.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_RouteWalk.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Walk.h" #include "PokemonBDSP_AutoStory_Talks.h" @@ -68,122 +59,16 @@ bool answer_rock_smash_prompt(VideoStream& stream, ProControllerContext& context return false; } -// 01.08: north out of Jubilife onto Route 204. -void north_out_of_jubilife(VideoStream& stream, ProControllerContext& context){ - // The street's column 14 carries on as the route's; the location banner - // names Route 204 as we cross. A D-pad walk keeps the column; it may - // stop a tile or two past the border, which the entry set allows for. - const WalkResult out = walk_until_dpad(stream, context, run_from_wild_battle, {0, 1}, 8000ms, "north out of Jubilife", {.arrived = location_banner_showing}); - if (out.stop != WalkStop::ARRIVED){ - fail_step(stream, "Jubilife: no Route 204 banner walking north up the street."); +// 01.09: Rock Smash on the rock east of us, from the tile the route ends on +// facing it. Only the prompt's Yes/No box tells the rock from someone in +// the way (r237a: the walker took one for a person and said No). +void smash_the_rock(VideoStream& stream, ProControllerContext& context){ + route_turn(stream, context, Direction::EAST); + if (!talk_with_nudges(stream, context, run_from_wild_battle, Direction::EAST, "the rock east of us", 2000ms)){ + fail_step(stream, "Ravaged Path: nothing answered facing the rock east of us."); } - context.wait_for(1500ms); -} - -// 01.08: Route 204 South on its grid to below the Ravaged Path's door. -void route_204_south_to_the_door(VideoStream& stream, ProControllerContext& context){ - // On the grid decoded from the game's data (Maps/Route204South.json): - // a tile west onto the tall grass column that runs up to the cliff, - // north up it, then west along the cliff to the signboard beside the - // door (the offline plan: 3 legs). Other trainers' sight lines are kept - // out of; town_plan_costs() lets the one-tile step land exactly. Lass Sarah's east line crosses - // the street, so she may stop us for a battle on the way; a Youngster - // who is not a trainer wanders in front of the door, and the walker - // waits for him. - { - RouteTrainers trainers{route204_south_trainer_sights(), [&](const std::string& who){ talk_or_fight(stream, context, who); }}; - const std::vector at = walk_route(stream, context, run_from_wild_battle, route204_south_maps(), - map_region("Route204South", "jubilife_entry"), trainers, RouteWalkPlan{[](const Pokemon::TilePos& pos){ return pos == map_point("Route204South", "below_door"); }, "Route 204 South to the Ravaged Path", route204_south_tiles_to_avoid(), town_plan_costs()}); - if (at.size() != 1 || !(at[0] == map_point("Route204South", "below_door"))){ - fail_step(stream, "Route 204: the walk did not end below the Ravaged Path's door."); - } - } -} - -// 01.08: north through the door. -void into_the_ravaged_path(VideoStream& stream, ProControllerContext& context){ - if (!dpad_through_door_north(stream, context, "Ravaged Path door")){ - fail_step(stream, "Route 204: no dark screen walking north into the Ravaged Path."); - } - context.wait_for(2000ms); -} - -// 01.09: through the Ravaged Path, smashing its rocks. -void through_the_ravaged_path(VideoStream& stream, ProControllerContext& context){ - // On the traced grid (Maps/RavagedPath.json): from the - // south door up the stairs to the tile west of one of the two rocks - // across the lower corridor, smash it, then east and south to the - // corridor's east end; the way out is a tile west of it, south. - { - // Blocked short of a wall in here, it is a rock the plan counted on - // being gone (r237a: the walker took one for a person and said No). - auto in_the_way = [&](const std::string& who){ - if (!answer_rock_smash_prompt(stream, context, who)){ - talk_or_fight(stream, context, who); - } - }; - RouteTrainers no_trainers{{}, in_the_way}; - std::vector at{map_point("RavagedPath", "entrance")}; - const StoryWorld rocks_in = ravaged_path_maps(); - at = walk_route(stream, context, run_from_wild_battle, rocks_in, at, no_trainers, RouteWalkPlan{[&](const Pokemon::TilePos& pos){ - const auto& before = map_region("RavagedPath", "before_rocks"); - return std::find(before.begin(), before.end(), pos) != before.end(); - }, "To a rock across the corridor", {}, route_plan_costs()}); - route_turn(stream, context, Direction::EAST); - if (!talk_with_nudges(stream, context, run_from_wild_battle, Direction::EAST, "the rock east of us", 2000ms)){ - fail_step(stream, "Ravaged Path: nothing answered facing the rock east of us."); - } - if (!answer_rock_smash_prompt(stream, context, "the rock east of us")){ - fail_step(stream, "Ravaged Path: no Rock Smash offer facing the rock east of us."); - } - // We may be on either row; the rock east of the other stays, but no - // leg from our row crosses it. - std::vector smashed; - for (const Pokemon::TilePos& pos : map_region("RavagedPath", "before_rocks")){ - smashed.push_back(pos.next(Direction::EAST)); - } - const StoryWorld rocks_out = ravaged_path_maps(smashed); - at = walk_route(stream, context, run_from_wild_battle, rocks_out, at, no_trainers, RouteWalkPlan{[&](const Pokemon::TilePos& pos){ return pos == map_point("RavagedPath", "east_end"); }, "To the corridor's east end", {}, route_plan_costs()}); - dpad_tiles(stream, context, DPAD_LEFT, 1, "west onto the tile above the way out"); - const WalkResult out = walk_dir(stream, context, Direction::SOUTH, 4000ms, "south out of the Ravaged Path"); - if (out.stop != WalkStop::TRANSITION && out.stop != WalkStop::LIMIT){ - fail_step(stream, "Ravaged Path: the walk south from a tile west of the corridor's end did not leave the cave."); - } - context.wait_for(2000ms); - } -} - -// 01.10: Route 204 north on the human's line, into Floaroma. -void route_204_to_floaroma(VideoStream& stream, ProControllerContext& context){ - // The line a human walked (Maps/Route204North.json, - // the region floaroma_waypoints): a tile south out of the cave's mouth, west along - // the row under the cliff past the Youngster who wanders there, up the - // stairs, east along the corridor into Aroma Lady Taylor's battle, north - // through the tall grass, west below the flower beds, and north between - // the beds into Floaroma. Whole tiles on the D-pad, corner to corner. - { - RouteTrainers trainers{route204_north_trainer_sights(), [&](const std::string& who){ - const bool doubles = who.find("Twins") != std::string::npos || who.find("someone") != std::string::npos; - talk_or_fight(stream, context, who, TalkExpect::TALK_OR_BATTLE, doubles ? BattleFormat::DOUBLES : BattleFormat::SINGLES); - }}; - std::vector at{map_point("Route204North", "cave_exit")}; - at = walk_waypoints(stream, context, run_from_wild_battle, route204_north_maps(), at, trainers, WaypointWalkPlan{map_region("Route204North", "floaroma_waypoints"), "To the path into Floaroma", waypoint_plan_costs()}); - if (!std::all_of(at.begin(), at.end(), in_floaroma_path)){ - fail_step(stream, "Route 204: the walk did not end in the path into Floaroma."); - } - // North until the town's banner shows: the checkpoint ends right - // there, where the save goes (r247a walked 20 s on and overshot). - const WalkResult in = walk_until_dpad(stream, context, run_from_wild_battle, {0, 1}, 12000ms, "north into Floaroma", {.arrived = location_banner_showing}); - if (in.stop != WalkStop::ARRIVED){ - fail_step(stream, "Route 204: no location banner walking north into Floaroma."); - } - context.wait_for(500ms); - const VideoSnapshot banner = stream.video().snapshot(); - const std::string read = banner ? read_location_banner(*banner.frame) : ""; - if (!location_banner_matches(read, "Floaroma Town")){ - fail_step(stream, "Route 204: the banner read \"" + read + "\", not Floaroma Town."); - } - stream.log("[01.10] In Floaroma Town; the banner read \"" + read + "\".", COLOR_GREEN); + if (!answer_rock_smash_prompt(stream, context, "the rock east of us")){ + fail_step(stream, "Ravaged Path: no Rock Smash offer facing the rock east of us."); } } @@ -219,11 +104,7 @@ void below_the_old_man(VideoStream& stream, ProControllerContext& context){ void add_route204_events(StoryEvents& events){ - events.emplace("north_out_of_jubilife", console_event(north_out_of_jubilife)); - events.emplace("route_204_south_to_the_door", console_event(route_204_south_to_the_door)); - events.emplace("into_the_ravaged_path", console_event(into_the_ravaged_path)); - events.emplace("through_the_ravaged_path", console_event(through_the_ravaged_path)); - events.emplace("route_204_to_floaroma", console_event(route_204_to_floaroma)); + events.emplace("smash_the_rock", console_event(smash_the_rock)); events.emplace("west_8_north_to_the_old_woman", console_event(west_8_north_to_the_old_woman)); events.emplace("below_the_old_man", console_event(below_the_old_man)); } diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp index d3cdd08b85..d0c1982e26 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp @@ -65,6 +65,9 @@ std::optional story_route_costs(const std::string& variant){ if (variant == "robust"){ return RobustPlanCosts(); } + if (variant == "line"){ + return waypoint_plan_costs(); + } return std::nullopt; } @@ -91,6 +94,16 @@ void save_failure_screenshot(VideoStream& stream, size_t attempt){ stream.log("Failure screenshot: " + path); } +// A trainer who saw us on a route: Route 204's Twins battle as a pair +// (01.10), everyone else alone. +void fight_story_trainer(VideoStream& stream, ProControllerContext& context, const std::string& trainer){ + if (trainer.find("Twins") != std::string::npos){ + talk_or_fight(stream, context, trainer, TalkExpect::BATTLE, BattleFormat::DOUBLES); + return; + } + fight_route_trainer(stream, context, trainer); +} + } @@ -102,8 +115,7 @@ void StoryExecutor::begin_attempt(const Pokemon::StoryCheckpoint& data){ m_checkpoint = data.id; // A row starts from its save: after the shoes' row, the save has them. m_can_run = data.id > std::string(RUNNING_SHOES_ROW); - const StoryWorld* south = story_plan_world("Route202", "Route202"); - m_trainers = south ? story_trainers(*south) : std::vector{}; + m_trainers.clear(); } Outcome StoryExecutor::run(RunState& state, const StoryStep& step){ @@ -152,21 +164,18 @@ Pose StoryExecutor::walk_to(const Pose& from, const Pose& goal, const StoryStep& for (const TilePos& pos : Pokemon::tiles_of(goal)){ targets.push_back(story_to_world(*maps, goal.map, pos)); } - // Only Route 202's trainers are known; the ones fought stay off - // m_trainers until the next attempt. - const bool south = maps == story_plan_world("Route202", "Route202"); + // The ones fought stay off m_trainers until the next attempt. + std::vector& sights = m_trainers.try_emplace(maps, story_trainers(*maps)).first->second; RouteTrainers trainers{ - south ? m_trainers : std::vector{}, - [this](const std::string& trainer){ fight_route_trainer(stream(), context(), trainer); } + sights, + [this](const std::string& trainer){ fight_story_trainer(stream(), context(), trainer); } }; const RouteWalkPlan plan{ [&](const TilePos& pos){ return std::find(targets.begin(), targets.end(), pos) != targets.end(); }, label_of(step), {}, *costs, m_can_run ? MoveSpeed::RUN : MoveSpeed::WALK }; const std::vector at = walk_route(stream(), context(), run_from_wild_battle, *maps, start, trainers, plan); - if (south){ - m_trainers = std::move(trainers.sights); - } + sights = std::move(trainers.sights); std::vector local; for (const TilePos& pos : at){ const std::optional tile = story_to_local(*maps, goal.map, pos); diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h index 88dbb00c09..cae0c9e8a7 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h @@ -71,7 +71,8 @@ struct StoryEnv{ // The plan costs a ROUTE step's variant names: "" the route costs, -// "town" town_plan_costs(), "robust" the planner's defaults. Nothing for +// "town" town_plan_costs(), "robust" the planner's defaults, "line" +// waypoint_plan_costs() (a stretch of a line a human walked). Nothing for // another name. std::optional story_route_costs(const std::string& variant); @@ -113,7 +114,7 @@ class StoryExecutor : public Pokemon::StoryStepExecutor{ virtual Pokemon::Outcome new_game(Pokemon::RunState& state, const Pokemon::StoryStep& step) override; // verify_tutorial_settings(): fast text, Autosave off. virtual Pokemon::Outcome options(Pokemon::RunState& state, const Pokemon::StoryStep& step) override; - // Variant: the plan's costs ("" route, "town", "robust"). + // Variant: the plan's costs ("" route, "town", "robust", "line"). virtual Pokemon::Outcome route(Pokemon::RunState& state, const Pokemon::StoryStep& step) override; // Each leg in D-pad taps, turning first. Variant "one_short_ok": a leg // may end a tile short (a door lined up by sight after). Variant @@ -151,9 +152,10 @@ class StoryExecutor : public Pokemon::StoryStepExecutor{ std::string m_checkpoint; // Whether the player has the Running Shoes: the pace routes are timed at. bool m_can_run = false; - // Route 202's trainers who can still see us, in story_plan_world()'s - // world tiles; all of them again at each attempt. - std::vector m_trainers; + // The trainers who can still see us on each story_plan_world() world, + // in its tiles, from its first walk this attempt; all of them again at + // each attempt. + std::map> m_trainers; std::map m_failures; std::map m_run_counts; std::set m_stop_rows; diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.cpp index 475acd37e4..f8863b4c87 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.cpp @@ -16,12 +16,14 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonBDSP{ +using Pokemon::Direction; using Pokemon::TilePos; namespace{ -// The JSON maps' names and the location banners'. +// The JSON maps' names and the location banners'. Both halves of Route +// 204 show "Route 204"; no world holds both. const std::map& banner_names(){ static const std::map names{ {"TwinleafTown", "Twinleaf Town"}, @@ -30,6 +32,11 @@ const std::map& banner_names(){ {"SandgemTown", MAP_SANDGEM_TOWN}, {"Route202", MAP_ROUTE_202}, {"JubilifeCity", MAP_JUBILIFE_CITY}, + {"Route203", MAP_ROUTE_203}, + {"OreburghCity", MAP_OREBURGH_CITY}, + {"Route204South", "Route 204"}, + {"Route204North", "Route 204"}, + {"FloaromaTown", MAP_FLOAROMA_TOWN}, }; return names; } @@ -42,56 +49,173 @@ const std::string& banner_name(const std::string& map){ return map; } +// `maps` (JSON names) in one world, each with its tile (0,0) at the corner +// of its decomp rectangle (devtools/maps/bdsp_maps.py), so maps that meet +// in the game meet here. +StoryWorld joined(const std::vector>& maps){ + StoryWorld world; + for (const auto& [map, corner] : maps){ + world.add(banner_name(map), story_map(map), corner); + } + return world; +} + // TODO(#114 multi-map view): MapData has no joined view of maps yet. // Twinleaf to Route 202 in one world, at the decomp's world tiles (its // cells are 32 tiles square; Route 201's map starts 7 rows into its cell // and Route 202's a column west of its own). const StoryWorld& south_world(){ + static const StoryWorld world = joined({ + {"TwinleafTown", {96, 864}}, + {"Route201", {96, 839}}, + {"VerityLakefront", {64, 832}}, + {"SandgemTown", {160, 832}}, + {"Route202", {159, 800}}, + }); + return world; +} + +// Jubilife without its east column: Route 203's map starts on that column +// (world x 191, its column 0), and two maps may not both hold open ground. +// Jubilife's tiles keep their coordinates. +Pokemon::MapData jubilife_west_of_route_203(){ + const Pokemon::MapData& city = story_map("JubilifeCity"); + Pokemon::MapData cut; + cut.name = city.name; + cut.source = city.source; + cut.tiles = Pokemon::Grid{city.tiles.width - 1, city.tiles.height, {}}; + for (int y = 0; y < cut.tiles.height; y++){ + for (int x = 0; x < cut.tiles.width; x++){ + cut.tiles.cells.push_back(city.tiles.at({x, y})); + } + } + for (const Pokemon::MapObject& object : city.objects){ + if (cut.tiles.contains({object.x, object.y})){ + cut.objects.push_back(object); + } + } + return cut; +} + +// 01.06: Route 203 west into Jubilife (decomp rectangles (191, 740) and +// (128, 736)): Route 203's rows 17..20 are Jubilife's street, rows 21..24. +const StoryWorld& route_203_world(){ static const StoryWorld world = []{ - StoryWorld world; - world.add(banner_name("TwinleafTown"), story_map("TwinleafTown"), {96, 864}); - world.add(banner_name("Route201"), story_map("Route201"), {96, 839}); - world.add(banner_name("VerityLakefront"), story_map("VerityLakefront"), {64, 832}); - world.add(banner_name("SandgemTown"), story_map("SandgemTown"), {160, 832}); - world.add(banner_name("Route202"), story_map("Route202"), {159, 800}); + StoryWorld world = joined({{"Route203", {191, 740}}}); + world.add(MAP_JUBILIFE_CITY, jubilife_west_of_route_203(), {128, 736}); return world; }(); return world; } -// The interiors of 00.01 to 00.04, each its own world. +// 01.08: Jubilife north onto Route 204's south half (160, 704), whose street +// (12..15, 31) carries on Jubilife's (44..47, 0). +const StoryWorld& route_204_south_world(){ + static const StoryWorld world = joined({{"JubilifeCity", {128, 736}}, {"Route204South", {160, 704}}}); + return world; +} + +// 01.10: Route 204's north half (160, 672) up into Floaroma (160, 640). +const StoryWorld& route_204_north_world(){ + static const StoryWorld world = joined({{"Route204North", {160, 672}}, {"FloaromaTown", {160, 640}}}); + return world; +} + +// The Ravaged Path with the rock 01.09 smashes, (22, 43), open: no route +// before the smash can use it, since it only leads east, where the rocks +// shut everything else off. +Pokemon::MapData ravaged_path_smashed(){ + Pokemon::MapData map = story_map("RavagedPath"); + Pokemon::cell_at(map.tiles, map_point("RavagedPath", "rocks.13")) = Pokemon::TileKind::OPEN; + return map; +} + +// The interiors (houses, the lab, the Gym, the Gate, the Ravaged Path), +// each its own world, named by its JSON map. const std::map& room_worlds(){ static const std::map rooms = []{ std::map ret; - for (const char* room : {"PlayerHouse2F", "PlayerHouse1F", "RivalHouse1F", "RivalHouse2F", "RowansLab"}){ + for (const char* room : {"PlayerHouse2F", "PlayerHouse1F", "RivalHouse1F", "RivalHouse2F", "RowansLab", "OreburghGym", "OreburghGate"}){ ret[room].add(room, story_map(room), {0, 0}); } + ret["RavagedPath"].add("RavagedPath", ravaged_path_smashed(), {0, 0}); return ret; }(); return rooms; } -bool on_world(const StoryWorld& world, const std::string& map){ - if (&world == &south_world()){ - static const std::set south{"TwinleafTown", "Route201", "VerityLakefront", "SandgemTown", "Route202"}; - return south.contains(map); +// A world and the JSON maps it holds. +struct PlanWorld{ + const StoryWorld* world; + std::set maps; +}; + +// In order: the first that holds both ends of a walk plans it. Jubilife +// alone comes before the worlds that join it to a route, so 00.05's walks +// plan on the city alone. +const std::vector& plan_worlds(){ + static const std::vector worlds = []{ + std::vector ret{ + {&south_world(), {"TwinleafTown", "Route201", "VerityLakefront", "SandgemTown", "Route202"}}, + {&jubilife_maps(), {"JubilifeCity"}}, + {&oreburgh_city_maps(), {"OreburghCity"}}, + {&route_203_world(), {"Route203", "JubilifeCity"}}, + {&route_204_south_world(), {"JubilifeCity", "Route204South"}}, + {&route_204_north_world(), {"Route204North", "FloaromaTown"}}, + }; + for (const auto& [name, world] : room_worlds()){ + ret.push_back({&world, {name}}); + } + return ret; + }(); + return worlds; +} + +// Route 203's trainers, from the Diamond decomp's events (zone event +// narc_0329): where each stands on the JSON map, the way they look and +// how far. The one by the Gate wanders in BDSP and is off the map. +std::vector route_203_trainer_sights(){ + const auto at = [](const char* name){ + for (const Pokemon::MapObject& object : story_map("Route203").objects){ + if (object.name == name){ + return TilePos{object.x, object.y}; + } + } + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, std::string("Route 203 has no ") + name + "."); + }; + return { + {.name = "Youngster (west)", .pos = at("youngster_1"), .facing = Direction::WEST, .range = 2}, + {.name = "Lass (above row 18)", .pos = at("lass_2"), .facing = Direction::WEST, .range = 5}, + {.name = "Youngster (above row 18)", .pos = at("youngster_3"), .facing = Direction::SOUTH, .range = 4}, + {.name = "Lass (by the Gate)", .pos = at("lass_4"), .facing = Direction::SOUTH, .range = 5}, + }; +} + +// The trainers of the JSON map `map`, in its own tiles. +std::vector map_trainers(const std::string& map){ + if (map == "Route202"){ + return route202_trainer_sights(); + } + if (map == "Route203"){ + return route_203_trainer_sights(); + } + if (map == "Route204South"){ + return route204_south_trainer_sights(); } - return false; + if (map == "Route204North"){ + return route204_north_trainer_sights(); + } + return {}; } } const StoryWorld* story_plan_world(const std::string& from, const std::string& to){ - if (on_world(south_world(), from) && on_world(south_world(), to)){ - return &south_world(); - } - if (from == "JubilifeCity" && to == from){ - return &jubilife_maps(); - } - const auto& rooms = room_worlds(); - if (auto iter = rooms.find(from); iter != rooms.end() && to == from){ - return &iter->second; + for (const PlanWorld& world : plan_worlds()){ + if (world.maps.contains(from) && world.maps.contains(to)){ + return world.world; + } } return nullptr; } @@ -109,12 +233,17 @@ std::optional story_to_local(const StoryWorld& world, const std::string } std::vector story_trainers(const StoryWorld& world){ - if (!on_world(world, "Route202")){ - return {}; - } - std::vector sights = route202_trainer_sights(); - for (TrainerSight& sight : sights){ - sight.pos = story_to_world(world, "Route202", sight.pos); + std::vector sights; + for (const PlanWorld& plan : plan_worlds()){ + if (plan.world != &world){ + continue; + } + for (const std::string& map : plan.maps){ + for (TrainerSight sight : map_trainers(map)){ + sight.pos = story_to_world(world, map, sight.pos); + sights.push_back(std::move(sight)); + } + } } return sights; } diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.h index 171c3db300..5a4dc7d8ab 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.h +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryPlaces.h @@ -3,9 +3,11 @@ * From: https://github.com/PokemonAutomation/ * * Where the story's steps walk: poses are tiles of the JSON maps - * ("Route202"), named by map_point() and map_region(). A walk from one map to another is planned on a story world that holds both: the - * outdoors from Twinleaf to Route 202, Jubilife, and each interior of - * 00.01 to 00.04 on its own. + * ("Route202"), named by map_point() and map_region(). A walk from one map + * to another is planned on a story world that holds both: the outdoors + * from Twinleaf to Route 202, Jubilife, Oreburgh, Route 203 into Jubilife, + * Jubilife up Route 204's south half, Route 204's north half into + * Floaroma, and each interior on its own. * */ @@ -34,7 +36,7 @@ Pokemon::TilePos story_to_world(const StoryWorld& world, const std::string& map, // The world tile `pos` as a tile of `map`, or nothing if it is not on `map`. std::optional story_to_local(const StoryWorld& world, const std::string& map, const Pokemon::TilePos& pos); -// The trainers a walk on `world` may meet (Route 202's), in its tiles. +// The trainers a walk on `world` may meet (Routes 202 to 204's), in its tiles. std::vector story_trainers(const StoryWorld& world); diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp index 5c80713fba..a5361459cb 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp @@ -11,10 +11,13 @@ * on the console (r72 to r261, 2026-10-30); 00.05 to 00.05c, measured there * first, are now planned on Jubilife's map with the people and triggers of * the Diamond decomp's zone events and scripts. Segment 01 ran on the - * console end to end (2026-10-31), its walks kept as events. + * console end to end (2026-10-31), its walks as events; 01.05 to 01.10 + * are now planned on the maps the same way (2026-11-02), and have not + * run on a console as plans yet. * */ +#include #include #include #include @@ -53,6 +56,14 @@ const char* const ROUTE_202 = "Route202"; const char* const JUBILIFE = "JubilifeCity"; // No map of it: a pose there names the room, not its tiles. const char* const TRAINERS_SCHOOL = "TrainersSchool"; +const char* const OREBURGH = "OreburghCity"; +const char* const GYM = "OreburghGym"; +const char* const GATE = "OreburghGate"; +const char* const ROUTE_203 = "Route203"; +const char* const ROUTE_204_SOUTH = "Route204South"; +const char* const RAVAGED_PATH = "RavagedPath"; +const char* const ROUTE_204_NORTH = "Route204North"; +const char* const FLOAROMA = "FloaromaTown"; Pose at(const char* map, int x, int y, std::optional facing = std::nullopt){ return Pokemon::tile_pose(map, {TilePos{x, y}}, facing); @@ -485,6 +496,92 @@ std::vector jubilife(){ return {the_town_map(), the_coupons(), the_poketch()}; } +// Where the Oreburgh Gate lets us out onto Route 203: its warp tile (55, 9) +// (Diamond's warp (246, 749), zone event narc_0329), walking west. +Pose out_of_oreburgh_gate(){ + return at(ROUTE_203, 55, 9, WEST); +} + +// 01.05, planned on Oreburgh's and the Gate's maps with the Diamond decomp's +// zone events (narc_0044, narc_0250) and Platinum's script for the scene +// (scripts_oreburgh_city.s, the same trigger and variable). Barry's coord +// event, column 6 on rows 12 to 15 while VAR_OREBURGH_CITY_STATE is 2 (after +// Roark: the old walk met his "*thud*" there, 2026-10-29), runs him into us +// from the east: the game pushes us a tile west, facing east, and he +// leaves. The Gate's way west is row 22, across the Hiker's trigger (7, 22), +// spent in 01.02. +StoryCheckpoint oreburgh_to_route_203(){ + return row("01.05", "Oreburgh to Route 203", next_to(GYM, "roark", SOUTH, NORTH), { + // 01.04 talks to Roark from below him, up the clear middle column. + step_into("out_of_the_gym", SOUTH, "down the middle column (5, 4..23) and out of the door (5, 24)", + at(OREBURGH, "gym_doorstep", SOUTH)), + route("east_of_barrys_trigger", at(OREBURGH, 7, 13, WEST), + "west along the street, north up the stairs west of the houses, west along the Gate's row", "town"), + step_into("barrys_trigger", WEST, "the trigger (6, 12..15)"), + scene("barry_bumps_into_us", at(OREBURGH, 5, 13, EAST), "Barry runs into us, talks of Eterna, and leaves (assumed: the push)"), + route("by_the_gate", next_to(OREBURGH, "gate_mouth", EAST, WEST), "west to the Gate's warp tile (2, 13)"), + // The warp's other end is the Gate's (27, 22), beside its east mouth. + step_into("into_oreburgh_gate", WEST, "the Gate's mouth (1, 13)", at(GATE, 27, 22, WEST)), + route("through_the_gate", at(GATE, 4, 22, WEST), "west along row 22 to the warp tile by the west mouth (3, 22)"), + step_into("out_of_oreburgh_gate", WEST, "the west mouth (3, 22)", out_of_oreburgh_gate()), + }); +} + +// Below Rowan's trigger in Jubilife: (47, 8), the east tile under the +// trigger (45..47, 7), which (48, 7) beside it is not. +Pose below_rowans_trigger(){ + return at(JUBILIFE, 47, 8, NORTH); +} +// Where Rowan's scene leaves us: walked up to him at (47, 5), then a tile +// west in front of the grunt (Platinum's movements; assumed for BDSP). +Pose after_the_grunts(){ + return Pokemon::tile_pose(JUBILIFE, {TilePos{46, 5}, TilePos{47, 5}}); +} + +// 01.07, from the Diamond decomp (zone event narc_0002) and Platinum's +// script (scripts_jubilife_city.s, the same trigger and variable): the +// coord event (45..47, 7) while VAR_JUBILIFE_CITY_STATE is 3 walks us up to +// Rowan, his and the grunts' lines, then the tag battle with Dawn; after +// it they leave. +StoryCheckpoint rowan_and_the_grunts(){ + return row("01.07", "Jubilife: Rowan, Dawn and the Galactic grunts", below_rowans_trigger(), { + step_into("rowans_trigger", NORTH, "the trigger (45..47, 7): the game walks us up to Rowan"), + battle("galactic_grunts", false, "the lines, then the tag battle with Dawn", "doubles"), + scene("rowan_and_dawn_leave", after_the_grunts(), "Rowan's and Dawn's lines; they walk off"), + }); +} + +// 01.09 on the Ravaged Path's map (Platinum's, checked on the console in +// r235b): up the stairs to west of the rock (22, 43), smash it, east to +// above the way out (28, 45). Its other end is Route 204's (20, 26). +StoryCheckpoint through_the_ravaged_path(){ + const Pose west_of_the_rock = at(RAVAGED_PATH, "before_rocks.2", EAST); + return row("01.09", "Ravaged Path", at(RAVAGED_PATH, "entrance", NORTH), { + route("west_of_the_rock", west_of_the_rock, "up the stairs and along the corridor"), + event("smash_the_rock", "Rock Smash on the rock east of us", west_of_the_rock), + route("above_the_way_out", at(RAVAGED_PATH, "above_exit", SOUTH), "east through the rock's tile"), + step_into("out_of_the_ravaged_path", SOUTH, "the way out (28, 45)", at(ROUTE_204_NORTH, "cave_exit", SOUTH)), + }); +} + +// 01.10: the line a human walked up Route 204's north half (2026-10-31, +// r246a to r251a), corner to corner (Test_Route204NorthLine checks each +// stretch is one straight leg), then north between the flower beds into +// Floaroma. Its trainers who see us are fought: Taylor on the line, the +// Twins as a pair. +StoryCheckpoint route_204_to_floaroma(){ + std::vector steps; + const std::vector line = map_region(ROUTE_204_NORTH, "floaroma_waypoints"); + for (size_t i = 0; i < line.size(); i++){ + steps.push_back(route(std::format("line_{}", i + 1), Pokemon::tile_pose(ROUTE_204_NORTH, {line[i]}), + "a corner of the human's line", "line")); + } + // 01.11 walks on from here: west 8 along row 29, north to the old woman. + steps.push_back(route("into_floaroma", at(FLOAROMA, "route_204_arrival", NORTH), "north up column 11 into Floaroma", "line")); + steps.push_back(save()); + return row("01.10", "Route 204 to Floaroma", at(ROUTE_204_NORTH, "cave_exit", SOUTH), std::move(steps)); +} + std::vector to_oreburgh(){ return { row("01.01", "Jubilife to Barry", std::nullopt, { @@ -510,38 +607,30 @@ std::vector to_oreburgh(){ event("out_of_the_center", "out from the counter"), event("gym_until_roark_beaten", "west into the gym, up it to Roark; again from the Center after a loss"), }), - row("01.05", "Oreburgh to Route 203", std::nullopt, { - event("out_of_the_gym", "out of the gym onto its doorstep"), - event("into_oreburgh_gate", "up to the platform and west into the Gate"), - event("out_of_oreburgh_gate", "west until Route 203's banner shows"), - }), + oreburgh_to_route_203(), }; } std::vector to_floaroma(){ return { - row("01.06", "Route 203 to Jubilife", std::nullopt, { - event("route_203_to_row_18", "on Route 203's map to row 18, below the girl"), - event("row_18_into_jubilife", "west along row 18, counting tiles"), - }), - row("01.07", "Jubilife through Rowan's scene", std::nullopt, { - event("west_to_the_crossroads", "west along the long road"), - event("north_to_rowan", "north up the street until Rowan's scene"), - battle("galactic_grunts", false, "the tag battle with Dawn", "doubles"), - event("rowan_after_the_battle", "Rowan's and Dawn's lines"), - }), - row("01.08", "Jubilife to the Ravaged Path", std::nullopt, { - event("north_out_of_jubilife", "north until Route 204's banner"), - event("route_204_south_to_the_door", "on Route 204 South's map to below the Ravaged Path's door"), - event("into_the_ravaged_path", "north through the door"), + row("01.06", "Route 203 to Jubilife", out_of_oreburgh_gate(), { + // Trainers who saw us going east in 01.02 are beaten; any other + // sees us here and is fought (route_203_trainer_sights()). + route("below_rowans_trigger", below_rowans_trigger(), + "down Route 203 (its ledges only south), west along it into Jubilife's street, north up the road", "town"), }), - row("01.09", "Ravaged Path", std::nullopt, { - event("through_the_ravaged_path", "through the cave with Rock Smash, out onto Route 204 North"), - }), - row("01.10", "Route 204 to Floaroma", std::nullopt, { - event("route_204_to_floaroma", "the human's line north into Floaroma"), - save(), + rowan_and_the_grunts(), + row("01.08", "Jubilife to the Ravaged Path", after_the_grunts(), { + // Lass Sarah's east sight crosses the street (r233a, r234a): she + // may stop us for a battle. + route("below_the_ravaged_path", at(ROUTE_204_SOUTH, "below_door", NORTH), + "north out of Jubilife, up Route 204's street and grass to below the door (11, 1)", "town"), + // Its warp (Diamond's zone event narc_0330) lands us on the + // cave's (19, 50), as in r237a. + step_into("into_the_ravaged_path", NORTH, "the door (11, 1)", at(RAVAGED_PATH, "entrance", NORTH)), }), + through_the_ravaged_path(), + route_204_to_floaroma(), row("01.11", "Floaroma's old couple", std::nullopt, { event("west_8_north_to_the_old_woman", "west 8, north until she stops us"), talk("old_woman", "she gives Mew when a Let's Go save is on the console"), diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp index 8e8bd57129..45f4341f79 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp @@ -37,7 +37,7 @@ using Pokemon::TilePos; // The variants each kind does itself, besides the story's events. const std::map>& built_in_variants(){ static const std::map> variants{ - {StoryStepKind::ROUTE, {"", "town", "robust"}}, + {StoryStepKind::ROUTE, {"", "town", "robust", "line"}}, {StoryStepKind::LEGS, {"", "one_short_ok", "to_wall", "to_dialogue", "to_map"}}, {StoryStepKind::MASH, {"", "a"}}, {StoryStepKind::BATTLE, {"", "doubles"}}, diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.cpp index 900e7aec84..4300a33e77 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.cpp @@ -12,7 +12,6 @@ #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" -#include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_Location.h" #include "PokemonBDSP/Programs/AutoStory/Utils/PokemonBDSP_AutoStory_RouteWalk.h" #include "PokemonBDSP_AutoStory_Talks.h" #include "PokemonBDSP_AutoStory_WildBattle.h" @@ -194,31 +193,6 @@ bool dpad_through_door_north(VideoStream& stream, ProControllerContext& context, return false; } -// Out of a building whose door is straight south of us, a D-pad tile at a -// time, until the location banner shows: it names the map we step into -// (e.g. "Oreburgh City") for a few seconds, and never shows inside. Ends in -// the doorway, facing south. Out of the Oreburgh Gym from in front of Roark -// (by hand, 2026-10-31): 18 taps inside, 2 taps of black fade, the banner on -// the 21st. The door's fade alone was missed between steps (r219g walked on -// south to the rocks below the gym). -bool dpad_out_of_building(VideoStream& stream, ProControllerContext& context, size_t max_taps, const std::string& label){ - pbf_press_dpad(context, DPAD_DOWN, 160ms, 700ms); // turn - for (size_t tap = 0; tap < max_taps; tap++){ - pbf_press_dpad(context, DPAD_DOWN, 160ms, 900ms); - context.wait_for_all_requests(); - const VideoSnapshot frame = stream.video().snapshot(); - if (frame && location_banner_showing(*frame.frame)){ - stream.log("[Walk] " + label + ": the location banner shows after " + std::to_string(tap + 1) + " taps; outside."); - context.wait_for(1000ms); - return true; - } - if (dialogue_opens(stream, context, 100ms)){ - finish_dialogue(stream, context, BUTTON_B, label + ": someone in the way"); - } - } - return false; -} - // For a player inside a Pokemon Center after a whiteout, at the counter: // out through the door. The door is straight south of the nurse in most // Centers; in Oreburgh's it was two tiles east of where we stood (by hand). @@ -267,54 +241,6 @@ void walk_out_of_center(VideoStream& stream, ProControllerContext& context, cons fail_step(stream, label + ": could not find the Pokemon Center's door."); } -// West until a transition, by the user's rule for a wall in the way: walk -// north to the top wall, which pins the row, then try the rows south of it -// one at a time. Trainers who stop us are fought. Returns at the first -// transition (a building, a cave, a gate), or when `arrived` matches. -void west_until_transition( - VideoStream& stream, ProControllerContext& context, const std::string& label, size_t max_legs, - const std::function& arrived -){ - for (size_t leg = 0; leg < max_legs; leg++){ - const WalkResult west = walk_dir(stream, context, Direction::WEST, 20000ms, label, arrived); - if (west.stop == WalkStop::ARRIVED){ - return; - } - if (west.stop == WalkStop::TRANSITION){ - context.wait_for(2000ms); - return; - } - if (west.stop == WalkStop::DIALOG){ - talk_or_fight(stream, context, label + ": someone in the way"); - continue; - } - if (west.stop != WalkStop::BLOCKED){ - continue; - } - walk_dir(stream, context, Direction::NORTH, 4000ms, label + ": north to the top wall"); - bool moved = false; - for (size_t row = 0; row < 10 && !moved; row++){ - hold_tiles(stream, context, Direction::SOUTH, 1); - const WalkResult probe = walk_dir(stream, context, Direction::WEST, 1500ms, label + ": west on the next row", arrived); - if (probe.stop == WalkStop::ARRIVED){ - return; - } - moved = probe.stop != WalkStop::BLOCKED || probe.walked > 1200ms; - if (probe.stop == WalkStop::TRANSITION){ - context.wait_for(2000ms); - return; - } - if (probe.stop == WalkStop::DIALOG){ - talk_or_fight(stream, context, label + ": someone in the way"); - } - } - if (!moved){ - fail_step(stream, label + ": no row below the top wall leads west."); - } - } - fail_step(stream, label + ": no transition after " + std::to_string(max_legs) + " legs."); -} - } diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.h index 07b9174beb..5672d71d00 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.h +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walks.h @@ -67,26 +67,10 @@ void dpad_tiles(VideoStream& stream, ProControllerContext& context, DpadPosition // Through a door straight north of us: tap up to face it, hold up into it, // and wait for its dark fade (by hand: brightness 7, about 1 s after). bool dpad_through_door_north(VideoStream& stream, ProControllerContext& context, const std::string& label); -// Out of a building whose door is straight south of us, a D-pad tile at a -// time, until the location banner shows: it names the map we step into -// (e.g. "Oreburgh City") for a few seconds, and never shows inside. Ends in -// the doorway, facing south. Out of the Oreburgh Gym from in front of Roark -// (by hand, 2026-10-31): 18 taps inside, 2 taps of black fade, the banner on -// the 21st. The door's fade alone was missed between steps (r219g walked on -// south to the rocks below the gym). -bool dpad_out_of_building(VideoStream& stream, ProControllerContext& context, size_t max_taps, const std::string& label); // For a player inside a Pokemon Center after a whiteout, at the counter: // out through the door. The door is straight south of the nurse in most // Centers; in Oreburgh's it was two tiles east of where we stood (by hand). void walk_out_of_center(VideoStream& stream, ProControllerContext& context, const std::string& label); -// West until a transition, by the user's rule for a wall in the way: walk -// north to the top wall, which pins the row, then try the rows south of it -// one at a time. Trainers who stop us are fought. Returns at the first -// transition (a building, a cave, a gate), or when `arrived` matches. -void west_until_transition( - VideoStream& stream, ProControllerContext& context, const std::string& label, size_t max_legs, - const std::function& arrived = nullptr -); From 22a8bc75d7d2a84fc696d41492964cd1bf8248fb Mon Sep 17 00:00:00 2001 From: dayfine Date: Mon, 2 Nov 2026 06:03:58 -0800 Subject: [PATCH 2/3] BDSP AutoStory: tap through 01.05's doors to their banners r75's walk west out of the Oreburgh Gate missed the fade and went on into a pocket by Route 203's pond. The Gym's door and the Gate's and Ravaged Path's mouths are now D-pad taps, each looked at, that stop at the fade and fail on no banner of the map beyond. The Gate's exit is its one warp tile, which 01.06 plans from. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../PokemonBDSP_AutoStory_StoryExecutor.cpp | 66 ++++++++++++++++++- .../PokemonBDSP_AutoStory_StoryExecutor.h | 3 +- .../PokemonBDSP_AutoStory_StoryTable.cpp | 39 +++++++++-- ...PokemonBDSP_AutoStory_StoryTable_Tests.cpp | 1 + 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp index d0c1982e26..af9efebdc4 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.cpp @@ -352,11 +352,15 @@ namespace{ // stops it: holding on under Mom's "Oh, Kosmos!" wasted 12 s (r45). The walker's black-screen watch // missed the lab's fade into Sandgem (r44, "dialog"), and settle's samples // 1.3 s apart can fall either side of it, so watch at settle's own darkness. -bool run_into_door(VideoStream& stream, ProControllerContext& context, Direction direction, bool run){ - const DpadPosition dpad = +DpadPosition dpad_toward(Direction direction){ + return direction == Direction::NORTH ? DPAD_UP : direction == Direction::SOUTH ? DPAD_DOWN : direction == Direction::WEST ? DPAD_LEFT : DPAD_RIGHT; +} + +bool run_into_door(VideoStream& stream, ProControllerContext& context, Direction direction, bool run){ + const DpadPosition dpad = dpad_toward(direction); BlackScreenWatcher dark(COLOR_RED, {0.1, 0.1, 0.8, 0.8}, 3 * FADE_DARKNESS, 60); ShortDialogWatcher dialog(COLOR_YELLOW); const int ret = run_until(stream, context, [&](ProControllerContext& context){ @@ -368,10 +372,68 @@ bool run_into_door(VideoStream& stream, ProControllerContext& context, Direction return ret == 0; } +// What the screen showed after a tap toward a door or a cave mouth. +enum class TapLook{ + NOTHING, + DARK, + BANNER, +}; + +// Looks for `map`'s location banner (and, if `dark_too`, the fade) for up +// to `wait`. +TapLook look_after_tap(VideoStream& stream, ProControllerContext& context, const std::string& map, Milliseconds wait, bool dark_too){ + const WallClock deadline = current_time() + wait; + while (true){ + context.wait_for_all_requests(); + const VideoSnapshot screen = stream.video().snapshot(); + if (screen && location_banner_matches(read_location_banner(*screen.frame), map)){ + return TapLook::BANNER; + } + if (dark_too && screen && image_average(*screen.frame).sum() / 3 < FADE_DARKNESS){ + return TapLook::DARK; + } + if (current_time() >= deadline){ + return TapLook::NOTHING; + } + context.wait_for(100ms); + } +} + +// A STEP "to_banner": D-pad taps one at a time toward a door or cave mouth, +// each followed by a look, until the fade (then no more taps) and +// pose_after's map's location banner. A held walk can carry on past a +// fade that goes unseen: out of the Gym it walked on south (r219g), and +// through the Oreburgh Gate's west mouth on west into a pocket by Route +// 203's pond (r75). Taps during the fade are eaten. No banner fails the +// step right there. +Outcome tap_to_banner(VideoStream& stream, ProControllerContext& context, const StoryStep& step, const std::string& checkpoint){ + const std::string map = step.pose_after ? step.pose_after->map : ""; + for (size_t tap = 1; tap <= 30; tap++){ + pbf_press_dpad(context, dpad_toward(step.direction), 160ms, 0ms); + TapLook look = look_after_tap(stream, context, map, 1500ms, true); + const bool faded = look == TapLook::DARK; + if (faded){ + look = look_after_tap(stream, context, map, 8000ms, false); + } + if (look == TapLook::BANNER){ + stream.log(std::format("[{}] {}: {}'s banner after {} taps {}.", checkpoint, step.name, map, tap, direction_name(step.direction))); + context.wait_for(1000ms); + return Pokemon::outcome_ok(); + } + if (faded){ + break; + } + } + return Pokemon::outcome_failed("no_banner", map); +} + } Outcome StoryExecutor::step(RunState& state, const StoryStep& step){ route_turn(stream(), context(), step.direction); + if (step.variant == "to_banner"){ + return tap_to_banner(stream(), context(), step, m_checkpoint); + } // A step through a door: walk (or run) on until the screen goes dark, // from next to the door or from anywhere in its line (r43: tapping 8 tiles // down the lab took 7 s), then settle for any scene on arrival (Mom's, diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h index cae0c9e8a7..319fea22cb 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryExecutor.h @@ -123,7 +123,8 @@ class StoryExecutor : public Pokemon::StoryStepExecutor{ // unless the step gives one. virtual Pokemon::Outcome legs(Pokemon::RunState& state, const Pokemon::StoryStep& step) override; // One D-pad step `direction`, turning first: onto a scene's trigger or - // into a door. + // into a door. Variant "to_banner": one tap into a cave mouth, then + // wait for pose_after's map's location banner. virtual Pokemon::Outcome step(Pokemon::RunState& state, const Pokemon::StoryStep& step) override; // A at what we face; a dialogue must open. virtual Pokemon::Outcome talk(Pokemon::RunState& state, const Pokemon::StoryStep& step) override; diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp index a5361459cb..fc0bcb1359 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp @@ -143,6 +143,16 @@ StoryStep step_into(std::string name, Direction direction, std::string why, std: step.pose_after = std::move(pose_after); return step; } +// Through a door or a cave mouth `direction` a D-pad tap at a time, each +// looked at, ending on the far map's location banner (the executor's +// "to_banner"): a hold can carry on past a fade it missed (r75, r219g). +// A tap after an unseen fade may step once more on the far side, so +// `pose_after` allows for it. +StoryStep tap_into(std::string name, Direction direction, std::string why, Pose pose_after){ + StoryStep step = step_into(std::move(name), direction, std::move(why), std::move(pose_after)); + step.variant = "to_banner"; + return step; +} StoryStep talk(std::string name, std::string why){ return make(StoryStepKind::TALK, std::move(name), std::move(why)); } @@ -496,11 +506,24 @@ std::vector jubilife(){ return {the_town_map(), the_coupons(), the_poketch()}; } +// `tile` of `map`, or the tile past it `direction`: where a tap_into() may +// leave us. +Pose arrival(const char* map, TilePos tile, Direction direction){ + return Pokemon::tile_pose(map, {tile, tile.next(direction)}, direction); +} // Where the Oreburgh Gate lets us out onto Route 203: its warp tile (55, 9) -// (Diamond's warp (246, 749), zone event narc_0329), walking west. +// (Diamond's warp (246, 749), zone event narc_0329), walking west. The one +// tile only: from it and (54, 9) together the joined Route 203 and Jubilife +// find no plan. The tap stops at the fade, which puts us on the warp tile; +// r78 passed 01.06 from a save there (by the sign, the cave mouth east). Pose out_of_oreburgh_gate(){ return at(ROUTE_203, 55, 9, WEST); } +// Where Route 204's door (11, 1) lets us into the Ravaged Path: (19, 50), +// as in r237a. +Pose into_the_ravaged_path(){ + return arrival(RAVAGED_PATH, map_point(RAVAGED_PATH, "entrance"), NORTH); +} // 01.05, planned on Oreburgh's and the Gate's maps with the Diamond decomp's // zone events (narc_0044, narc_0250) and Platinum's script for the scene @@ -513,17 +536,19 @@ Pose out_of_oreburgh_gate(){ StoryCheckpoint oreburgh_to_route_203(){ return row("01.05", "Oreburgh to Route 203", next_to(GYM, "roark", SOUTH, NORTH), { // 01.04 talks to Roark from below him, up the clear middle column. - step_into("out_of_the_gym", SOUTH, "down the middle column (5, 4..23) and out of the door (5, 24)", - at(OREBURGH, "gym_doorstep", SOUTH)), + tap_into("out_of_the_gym", SOUTH, "down the middle column (5, 4..23) and out of the door (5, 24)", + arrival(OREBURGH, map_point(OREBURGH, "gym_doorstep"), SOUTH)), route("east_of_barrys_trigger", at(OREBURGH, 7, 13, WEST), "west along the street, north up the stairs west of the houses, west along the Gate's row", "town"), step_into("barrys_trigger", WEST, "the trigger (6, 12..15)"), scene("barry_bumps_into_us", at(OREBURGH, 5, 13, EAST), "Barry runs into us, talks of Eterna, and leaves (assumed: the push)"), route("by_the_gate", next_to(OREBURGH, "gate_mouth", EAST, WEST), "west to the Gate's warp tile (2, 13)"), // The warp's other end is the Gate's (27, 22), beside its east mouth. - step_into("into_oreburgh_gate", WEST, "the Gate's mouth (1, 13)", at(GATE, 27, 22, WEST)), + tap_into("into_oreburgh_gate", WEST, "the Gate's mouth (1, 13)", arrival(GATE, {27, 22}, WEST)), route("through_the_gate", at(GATE, 4, 22, WEST), "west along row 22 to the warp tile by the west mouth (3, 22)"), - step_into("out_of_oreburgh_gate", WEST, "the west mouth (3, 22)", out_of_oreburgh_gate()), + // Route 203's banner or a failure here: the old walk west "until the + // banner" went on into the pond's pocket (r75). 01.06 heads south. + tap_into("out_of_oreburgh_gate", WEST, "the west mouth (3, 22)", out_of_oreburgh_gate()), }); } @@ -556,7 +581,7 @@ StoryCheckpoint rowan_and_the_grunts(){ // above the way out (28, 45). Its other end is Route 204's (20, 26). StoryCheckpoint through_the_ravaged_path(){ const Pose west_of_the_rock = at(RAVAGED_PATH, "before_rocks.2", EAST); - return row("01.09", "Ravaged Path", at(RAVAGED_PATH, "entrance", NORTH), { + return row("01.09", "Ravaged Path", into_the_ravaged_path(), { route("west_of_the_rock", west_of_the_rock, "up the stairs and along the corridor"), event("smash_the_rock", "Rock Smash on the rock east of us", west_of_the_rock), route("above_the_way_out", at(RAVAGED_PATH, "above_exit", SOUTH), "east through the rock's tile"), @@ -627,7 +652,7 @@ std::vector to_floaroma(){ "north out of Jubilife, up Route 204's street and grass to below the door (11, 1)", "town"), // Its warp (Diamond's zone event narc_0330) lands us on the // cave's (19, 50), as in r237a. - step_into("into_the_ravaged_path", NORTH, "the door (11, 1)", at(RAVAGED_PATH, "entrance", NORTH)), + tap_into("into_the_ravaged_path", NORTH, "the door (11, 1)", into_the_ravaged_path()), }), through_the_ravaged_path(), route_204_to_floaroma(), diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp index 45f4341f79..807dc4d536 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.cpp @@ -39,6 +39,7 @@ const std::map>& built_in_variants(){ static const std::map> variants{ {StoryStepKind::ROUTE, {"", "town", "robust", "line"}}, {StoryStepKind::LEGS, {"", "one_short_ok", "to_wall", "to_dialogue", "to_map"}}, + {StoryStepKind::STEP, {"", "to_banner"}}, {StoryStepKind::MASH, {"", "a"}}, {StoryStepKind::BATTLE, {"", "doubles"}}, }; From bd37d0cce0bcf7cd3766f5d6bfa7a3dfd12efba3 Mon Sep 17 00:00:00 2001 From: dayfine Date: Mon, 2 Nov 2026 06:11:14 -0800 Subject: [PATCH 3/3] BDSP AutoStory: 01.08 starts from the wall west of Rowan's scene From both tiles Rowan's scene may leave us, the joined Jubilife and Route 204 found no plan. A walk west along row 5 to its wall puts us on (44, 5). The Ravaged Path's arrival is its one tile, as the Gate's. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../PokemonBDSP_AutoStory_StoryTable.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp index fc0bcb1359..5a64d37ecd 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp @@ -135,6 +135,12 @@ StoryStep legs(std::string name, std::vector legs, std::string why, st StoryStep to_walls(std::string name, std::vector legs, std::string why){ return PokemonBDSP::legs(std::move(name), std::move(legs), std::move(why), "to_wall"); } +// `step`, after which the player stands on `pose`: a walk to a wall from +// a few tiles ends on one. +StoryStep anchored(StoryStep step, Pose pose){ + step.pose_after = std::move(pose); + return step; +} // TODO(#114 coord events): the map schema has no scene triggers yet, so // each row steps onto the decomp's trigger tile, named in its reason. StoryStep step_into(std::string name, Direction direction, std::string why, std::optional pose_after = std::nullopt){ @@ -520,9 +526,9 @@ Pose out_of_oreburgh_gate(){ return at(ROUTE_203, 55, 9, WEST); } // Where Route 204's door (11, 1) lets us into the Ravaged Path: (19, 50), -// as in r237a. +// as in r237a. The tap stops at the fade, on that tile. Pose into_the_ravaged_path(){ - return arrival(RAVAGED_PATH, map_point(RAVAGED_PATH, "entrance"), NORTH); + return at(RAVAGED_PATH, "entrance", NORTH); } // 01.05, planned on Oreburgh's and the Gate's maps with the Diamond decomp's @@ -646,6 +652,11 @@ std::vector to_floaroma(){ }), rowan_and_the_grunts(), row("01.08", "Jubilife to the Ravaged Path", after_the_grunts(), { + // One known tile to plan from: from both of the scene's ends + // the joined Jubilife and Route 204 find no plan. Row 5 is open + // from x 44 to 49, a wall at 43. + anchored(to_walls("west_to_the_wall", {{.direction = WEST, .tiles = 4}}, "west along row 5 to the wall"), + at(JUBILIFE, 44, 5, WEST)), // Lass Sarah's east sight crosses the street (r233a, r234a): she // may stop us for a battle. route("below_the_ravaged_path", at(ROUTE_204_SOUTH, "below_door", NORTH),