Skip to content

Commit 9d6be62

Browse files
authored
Autostory: Orthworm (#698)
* adjust checkpoint 54 for Switch 2 * checkpoint_55: Orthworm phase 1 * checkpoint 56: beat Orthworm * refactor AutostoryTools and Worldnavigation to avoid circular dependency. Refactor detect_closest_flypoint_and_move_map_cursor_there() so that it can detect both Pokecenters and Fast Travel points. * add comments to checkpoints 55-57 * implement move_cursor_to_position_offset_from_flypoint * add more test code for autostory * checkpoint 57: At East Province (Area Three) Pokecenter. * update checkpoint 54: Levincia to East Province (Area Three) Watchtower. * more checkpoint 54 updates
1 parent 9c9d41d commit 9d6be62

14 files changed

+832
-396
lines changed

SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_FastTravelDetector.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,13 @@ void FastTravelWatcher::make_overlays(VideoOverlaySet& items) const{
122122
}
123123

124124
bool FastTravelWatcher::process_frame(const ImageViewRGB32& screen, WallClock timestamp){
125-
std::vector<ImageFloatBox> hits = m_detector.detect_all(screen);
125+
m_hits = m_detector.detect_all(screen);
126126

127-
m_hits.reset(hits.size());
128-
for (const ImageFloatBox& hit : hits){
129-
m_hits.emplace_back(m_overlay, hit, COLOR_MAGENTA);
127+
m_hit_boxes.reset(m_hits.size());
128+
for (const ImageFloatBox& hit : m_hits){
129+
m_hit_boxes.emplace_back(m_overlay, hit, COLOR_MAGENTA);
130130
}
131-
132-
return !hits.empty();
131+
return !m_hits.empty();
133132
}
134133

135134

SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_FastTravelDetector.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ class FastTravelWatcher : public VisualInferenceCallback{
5858
virtual void make_overlays(VideoOverlaySet& items) const override;
5959
virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override;
6060

61+
const std::vector<ImageFloatBox>& found_locations() const { return m_hits; }
6162

6263
protected:
6364
VideoOverlay& m_overlay;
6465
FastTravelDetector m_detector;
65-
FixedLimitVector<OverlayBoxScope> m_hits;
66+
std::vector<ImageFloatBox> m_hits;
67+
FixedLimitVector<OverlayBoxScope> m_hit_boxes;
6668
};
6769

6870

SerialPrograms/Source/PokemonSV/Programs/AutoStory/PokemonSV_AutoStory.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ std::vector<std::unique_ptr<AutoStory_Segment>> make_autoStory_segment_list(){
9494
segment_list.emplace_back(std::make_unique<AutoStory_Segment_21>());
9595
segment_list.emplace_back(std::make_unique<AutoStory_Segment_22>());
9696
segment_list.emplace_back(std::make_unique<AutoStory_Segment_23>());
97-
// segment_list.emplace_back(std::make_unique<AutoStory_Segment_24>());
97+
segment_list.emplace_back(std::make_unique<AutoStory_Segment_24>());
9898
// segment_list.emplace_back(std::make_unique<AutoStory_Segment_25>());
9999
// segment_list.emplace_back(std::make_unique<AutoStory_Segment_26>());
100100
// segment_list.emplace_back(std::make_unique<AutoStory_Segment_27>());
@@ -415,13 +415,49 @@ AutoStory::AutoStory()
415415
"direction in radians",
416416
LockMode::UNLOCK_WHILE_RUNNING,
417417
0
418-
)
418+
)
419+
, FLYPOINT_TYPE(
420+
"<b>Flypoint type:</b><br>"
421+
"For print_flypoint_location() and move_cursor_to_position_offset_from_flypoint()",
422+
{
423+
{FlyPoint::POKECENTER, "pokecenter", "Pokecenter"},
424+
{FlyPoint::FAST_TRAVEL, "fast-travel", "Fast Travel"},
425+
},
426+
LockMode::UNLOCK_WHILE_RUNNING,
427+
FlyPoint::POKECENTER
428+
)
429+
, TEST_FLYPOINT_LOCATIONS(
430+
"<b>TEST: print_flypoint_location():</b>",
431+
LockMode::UNLOCK_WHILE_RUNNING,
432+
false
433+
)
434+
, TEST_MOVE_CURSOR_OFFSET_FROM_FLYPOINT(
435+
"<b>TEST: move_cursor_to_position_offset_from_flypoint():</b>",
436+
LockMode::UNLOCK_WHILE_RUNNING,
437+
false
438+
)
439+
, X_OFFSET(
440+
"X offset from flypoint",
441+
LockMode::UNLOCK_WHILE_RUNNING,
442+
0
443+
)
444+
, Y_OFFSET(
445+
"Y offset from flypoint",
446+
LockMode::UNLOCK_WHILE_RUNNING,
447+
0
448+
)
419449
{
420450

421451
if (PreloadSettings::instance().DEVELOPER_MODE){
422452
PA_ADD_OPTION(m_advanced_options);
423453
PA_ADD_OPTION(CHANGE_SETTINGS);
424454

455+
PA_ADD_OPTION(FLYPOINT_TYPE);
456+
PA_ADD_OPTION(TEST_FLYPOINT_LOCATIONS);
457+
PA_ADD_OPTION(TEST_MOVE_CURSOR_OFFSET_FROM_FLYPOINT);
458+
PA_ADD_OPTION(X_OFFSET);
459+
PA_ADD_OPTION(Y_OFFSET);
460+
425461
PA_ADD_OPTION(TEST_CURRENT_DIRECTION);
426462
PA_ADD_OPTION(TEST_CHANGE_DIRECTION);
427463
PA_ADD_OPTION(DIR_RADIANS);
@@ -768,6 +804,19 @@ void AutoStory::run_autostory(SingleSwitchProgramEnvironment& env, ProController
768804
}
769805

770806
void AutoStory::test_code(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
807+
808+
if (TEST_FLYPOINT_LOCATIONS){
809+
print_flypoint_location(env.program_info(), env.console, context, FLYPOINT_TYPE);
810+
// print_flypoint_location(env.program_info(), env.console, context, FlyPoint::FAST_TRAVEL);
811+
return;
812+
}
813+
814+
if (TEST_MOVE_CURSOR_OFFSET_FROM_FLYPOINT){
815+
move_cursor_to_position_offset_from_flypoint(env.program_info(), env.console, context, FLYPOINT_TYPE, {X_OFFSET, Y_OFFSET});
816+
817+
return;
818+
}
819+
771820
if (TEST_CURRENT_DIRECTION){
772821
DirectionDetector direction;
773822
// direction.change_direction(env.program_info(), env.console, context, DIR_RADIANS);
@@ -818,6 +867,7 @@ void AutoStory::test_code(SingleSwitchProgramEnvironment& env, ProControllerCont
818867
DirectionDetector direction;
819868

820869

870+
821871
return;
822872
}
823873

SerialPrograms/Source/PokemonSV/Programs/AutoStory/PokemonSV_AutoStory.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ class AutoStory : public SingleSwitchProgramInstance, public ConfigOption::Liste
126126
BooleanCheckBoxOption TEST_CURRENT_DIRECTION;
127127
BooleanCheckBoxOption TEST_CHANGE_DIRECTION;
128128
FloatingPointOption DIR_RADIANS;
129+
130+
EnumDropdownOption<FlyPoint> FLYPOINT_TYPE;
131+
BooleanCheckBoxOption TEST_FLYPOINT_LOCATIONS;
132+
BooleanCheckBoxOption TEST_MOVE_CURSOR_OFFSET_FROM_FLYPOINT;
133+
FloatingPointOption X_OFFSET;
134+
FloatingPointOption Y_OFFSET;
129135
};
130136

131137
const std::vector<std::unique_ptr<AutoStory_Segment>>& ALL_AUTO_STORY_SEGMENT_LIST();

SerialPrograms/Source/PokemonSV/Programs/AutoStory/PokemonSV_AutoStoryTools.cpp

Lines changed: 4 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "PokemonSV/Inference/Overworld/PokemonSV_StationaryOverworldWatcher.h"
2020
#include "PokemonSV/Inference/PokemonSV_MainMenuDetector.h"
2121
#include "PokemonSV/Programs/PokemonSV_MenuNavigation.h"
22-
#include "PokemonSV/Programs/PokemonSV_WorldNavigation.h"
2322
#include "PokemonSV/Programs/PokemonSV_GameEntry.h"
2423
#include "PokemonSV/Programs/PokemonSV_SaveGame.h"
2524
#include "PokemonSV/Programs/Battles/PokemonSV_Battles.h"
@@ -41,178 +40,7 @@ namespace PokemonSV{
4140

4241

4342

44-
// spam A button to choose the first move
45-
// throw exception if wipeout or if your lead faints.
46-
void run_battle_press_A(
47-
VideoStream& stream,
48-
ProControllerContext& context,
49-
BattleStopCondition stop_condition,
50-
std::unordered_set<CallbackEnum> enum_optional_callbacks,
51-
bool detect_wipeout
52-
){
53-
int16_t num_times_seen_overworld = 0;
54-
size_t consecutive_move_select = 0;
55-
while (true){
56-
NormalBattleMenuWatcher battle(COLOR_BLUE);
57-
SwapMenuWatcher fainted(COLOR_PURPLE);
58-
OverworldWatcher overworld(stream.logger(), COLOR_CYAN);
59-
AdvanceDialogWatcher dialog(COLOR_RED);
60-
DialogArrowWatcher dialog_arrow(COLOR_RED, stream.overlay(), {0.850, 0.820, 0.020, 0.050}, 0.8365, 0.846);
61-
GradientArrowWatcher next_pokemon(COLOR_BLUE, GradientArrowType::RIGHT, {0.50, 0.51, 0.30, 0.10});
62-
MoveSelectWatcher move_select_menu(COLOR_YELLOW);
63-
64-
std::vector<PeriodicInferenceCallback> callbacks;
65-
std::vector<CallbackEnum> enum_all_callbacks;
66-
// mandatory callbacks: Battle, Overworld, Advance Dialog, Swap menu, Move select
67-
// optional callbacks: DIALOG_ARROW, NEXT_POKEMON
68-
69-
// merge the mandatory and optional callbacks as a set, to avoid duplicates. then convert to vector
70-
std::unordered_set<CallbackEnum> enum_all_callbacks_set{CallbackEnum::BATTLE, CallbackEnum::OVERWORLD, CallbackEnum::ADVANCE_DIALOG, CallbackEnum::SWAP_MENU, CallbackEnum::MOVE_SELECT}; // mandatory callbacks
71-
enum_all_callbacks_set.insert(enum_optional_callbacks.begin(), enum_optional_callbacks.end()); // append the mandatory and optional callback sets together
72-
enum_all_callbacks.assign(enum_all_callbacks_set.begin(), enum_all_callbacks_set.end());
73-
74-
for (const CallbackEnum& enum_callback : enum_all_callbacks){
75-
switch(enum_callback){
76-
case CallbackEnum::ADVANCE_DIALOG:
77-
callbacks.emplace_back(dialog);
78-
break;
79-
case CallbackEnum::OVERWORLD:
80-
callbacks.emplace_back(overworld);
81-
break;
82-
case CallbackEnum::DIALOG_ARROW:
83-
callbacks.emplace_back(dialog_arrow);
84-
break;
85-
case CallbackEnum::BATTLE:
86-
callbacks.emplace_back(battle);
87-
break;
88-
case CallbackEnum::NEXT_POKEMON: // to detect the "next pokemon" prompt.
89-
callbacks.emplace_back(next_pokemon);
90-
break;
91-
case CallbackEnum::SWAP_MENU: // detecting Swap Menu implies your lead fainted.
92-
callbacks.emplace_back(fainted);
93-
break;
94-
case CallbackEnum::MOVE_SELECT:
95-
callbacks.emplace_back(move_select_menu);
96-
break;
97-
default:
98-
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "run_battle_press_A: Unknown callback requested.");
99-
}
100-
}
101-
context.wait_for_all_requests();
102-
103-
int ret = wait_until(
104-
stream, context,
105-
std::chrono::seconds(90),
106-
callbacks
107-
);
108-
context.wait_for(std::chrono::milliseconds(100));
109-
if (ret < 0){
110-
OperationFailedException::fire(
111-
ErrorReport::SEND_ERROR_REPORT,
112-
"run_battle_press_A(): Timed out. Did not detect expected stop condition.",
113-
stream
114-
);
115-
}
11643

117-
CallbackEnum enum_callback = enum_all_callbacks[ret];
118-
switch (enum_callback){
119-
case CallbackEnum::BATTLE: // battle
120-
stream.log("Detected battle menu.");
121-
consecutive_move_select = 0;
122-
pbf_press_button(context, BUTTON_A, 20, 105);
123-
break;
124-
case CallbackEnum::MOVE_SELECT:
125-
stream.log("Detected move select. Spam first move");
126-
consecutive_move_select++;
127-
select_top_move(stream, context, consecutive_move_select);
128-
break;
129-
case CallbackEnum::OVERWORLD: // overworld
130-
stream.log("Detected overworld, battle over.");
131-
num_times_seen_overworld++;
132-
if (stop_condition == BattleStopCondition::STOP_OVERWORLD){
133-
return;
134-
}
135-
if(num_times_seen_overworld > 30){
136-
OperationFailedException::fire(
137-
ErrorReport::SEND_ERROR_REPORT,
138-
"run_battle_press_A(): Stuck in overworld. Did not detect expected stop condition.",
139-
stream
140-
);
141-
}
142-
break;
143-
case CallbackEnum::ADVANCE_DIALOG: // advance dialog
144-
stream.log("Detected dialog.");
145-
146-
if (detect_wipeout){
147-
context.wait_for_all_requests();
148-
WipeoutDetector wipeout;
149-
VideoSnapshot screen = stream.video().snapshot();
150-
// dump_snapshot(console);
151-
if (wipeout.detect(screen)){
152-
OperationFailedException::fire(
153-
ErrorReport::SEND_ERROR_REPORT,
154-
"run_battle_press_A(): Detected wipeout. All pokemon fainted.",
155-
stream
156-
);
157-
}
158-
}
159-
160-
if (stop_condition == BattleStopCondition::STOP_DIALOG){
161-
return;
162-
}
163-
pbf_press_button(context, BUTTON_A, 20, 105);
164-
break;
165-
case CallbackEnum::DIALOG_ARROW: // dialog arrow
166-
stream.log("run_battle_press_A: Detected dialog arrow.");
167-
pbf_press_button(context, BUTTON_A, 20, 105);
168-
break;
169-
case CallbackEnum::NEXT_POKEMON:
170-
stream.log("run_battle_press_A: Detected prompt for bringing in next pokemon. Keep current pokemon.");
171-
pbf_mash_button(context, BUTTON_B, 100);
172-
break;
173-
case CallbackEnum::SWAP_MENU:
174-
OperationFailedException::fire(
175-
ErrorReport::SEND_ERROR_REPORT,
176-
"run_battle_press_A(): Lead pokemon fainted.",
177-
stream
178-
);
179-
default:
180-
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "run_battle_press_A: Unknown callback triggered.");
181-
182-
}
183-
}
184-
}
185-
186-
void run_trainer_battle_press_A(
187-
VideoStream& stream,
188-
ProControllerContext& context,
189-
BattleStopCondition stop_condition,
190-
std::unordered_set<CallbackEnum> enum_optional_callbacks,
191-
bool detect_wipeout
192-
){
193-
enum_optional_callbacks.insert(CallbackEnum::NEXT_POKEMON); // always check for the "Next pokemon" prompt when in trainer battles
194-
run_battle_press_A(stream, context, stop_condition, enum_optional_callbacks, detect_wipeout);
195-
}
196-
197-
void run_wild_battle_press_A(
198-
VideoStream& stream,
199-
ProControllerContext& context,
200-
BattleStopCondition stop_condition,
201-
std::unordered_set<CallbackEnum> enum_optional_callbacks,
202-
bool detect_wipeout
203-
){
204-
run_battle_press_A(stream, context, stop_condition, enum_optional_callbacks, detect_wipeout);
205-
}
206-
207-
void select_top_move(VideoStream& stream, ProControllerContext& context, size_t consecutive_move_select){
208-
if (consecutive_move_select > 3){
209-
// to handle case where move is disabled/out of PP/taunted
210-
stream.log("Failed to select a move 3 times. Choosing a different move.", COLOR_RED);
211-
pbf_press_dpad(context, DPAD_DOWN, 20, 40);
212-
}
213-
pbf_mash_button(context, BUTTON_A, 100);
214-
215-
}
21644

21745
void clear_tutorial(VideoStream& stream, ProControllerContext& context, uint16_t seconds_timeout){
21846
bool seen_tutorial = false;
@@ -1060,7 +888,7 @@ void realign_player_from_landmark(
1060888
pbf_move_left_joystick(context, move_x1, move_y1, move_duration1, 1 * TICKS_PER_SECOND);
1061889

1062890
// move cursor to pokecenter
1063-
if (!detect_closest_pokecenter_and_move_map_cursor_there(info, stream, context, 0.29)){
891+
if (!detect_closest_flypoint_and_move_map_cursor_there(info, stream, context, FlyPoint::POKECENTER, 0.29)){
1064892
OperationFailedException::fire(
1065893
ErrorReport::SEND_ERROR_REPORT,
1066894
"realign_player_from_landmark(): No visible pokecenter found on map.",
@@ -1133,7 +961,8 @@ void move_cursor_towards_flypoint_and_go_there(
1133961
const ProgramInfo& info,
1134962
VideoStream& stream,
1135963
ProControllerContext& context,
1136-
MoveCursor move_cursor_near_flypoint
964+
MoveCursor move_cursor_near_flypoint,
965+
FlyPoint fly_point
1137966
){
1138967
WallClock start = current_time();
1139968

@@ -1173,7 +1002,7 @@ void move_cursor_towards_flypoint_and_go_there(
11731002
uint16_t move_duration1 = move_cursor_near_flypoint.move_duration;
11741003
pbf_move_left_joystick(context, move_x1, move_y1, move_duration1, 1 * TICKS_PER_SECOND);
11751004

1176-
if (!fly_to_visible_closest_pokecenter_cur_zoom_level(info, stream, context)){
1005+
if (!fly_to_visible_closest_flypoint_cur_zoom_level(info, stream, context, fly_point)){
11771006
OperationFailedException::fire(
11781007
ErrorReport::SEND_ERROR_REPORT,
11791008
"move_cursor_towards_flypoint_and_go_there(): No visible pokecenter found on map.",

0 commit comments

Comments
 (0)