diff --git a/modules/main.py b/modules/main.py index 5170a646..d823def1 100644 --- a/modules/main.py +++ b/modules/main.py @@ -99,6 +99,11 @@ def main_loop() -> None: from modules.modes.soft_resets import ModeStaticSoftResets mode = ModeStaticSoftResets() + + case "Sweet Scent": + from modules.modes.sweet_scent import ModeSweetScent + + mode = ModeSweetScent() case "Tower Duo": from modules.modes.tower_duo import ModeTowerDuo diff --git a/modules/memory.py b/modules/memory.py index 6774f426..dc011dfc 100644 --- a/modules/memory.py +++ b/modules/memory.py @@ -153,8 +153,6 @@ def get_game_state() -> GameState: return state_cache.game_state.value match get_game_state_symbol(): - case "CB2_SETUPOVERWORLDFORQLPLAYBACKWITHWARPEXIT" | "CB2_SETUPOVERWORLDFORQLPLAYBACK" | "CB2_LOADMAPFORQLPLAYBACK" | "CB2_ENTERFIELDFROMQUESTLOG": - return GameState.QUEST_LOG case "CB2_OVERWORLD": result = GameState.OVERWORLD case "BATTLEMAINCB2": diff --git a/modules/menu_parsers.py b/modules/menu_parsers.py index e1bb7eef..9ea0434a 100644 --- a/modules/menu_parsers.py +++ b/modules/menu_parsers.py @@ -7,7 +7,7 @@ from modules.tasks import get_task, task_is_active -class CursorOptionEFRLG(IntEnum): +class CursorOptionE(IntEnum): SUMMARY = 0 SWITCH = 1 CANCEL_1 = 2 @@ -43,6 +43,39 @@ class CursorOptionEFRLG(IntEnum): SWEET_SCENT = 32 +class CursorOptionFRLG(IntEnum): + SUMMARY = 0 + SWITCH = 1 + CANCEL_1 = 2 + ITEM = 3 + GIVE_ITEM = 4 + TAKE_ITEM = 5 + MAIL = 6 + TAKE_MAIL = 7 + READ = 8 + CANCEL_2 = 9 + SHIFT = 10 + SEND_OUT = 11 + ENTER = 12 + NO_ENTRY = 13 + STORE = 14 + REGISTER = 15 + TRADE_1 = 16 + TRADE_2 = 17 + FLASH = 18 + CUT = 19 + FLY = 20 + STRENGTH = 21 + SURF = 22 + ROCK_SMASH = 23 + WATERFALL = 24 + TELEPORT = 25 + DIG = 26 + MILK_DRINK = 27 + SOFTBOILED = 28 + SWEET_SCENT = 29 + + class CursorOptionRS(IntEnum): SUMMARY = 0 SWITCH = 1 @@ -312,7 +345,9 @@ def switch_requested() -> bool: def get_cursor_options(idx: int) -> str: match context.rom.game_title: - case "POKEMON FIRE" | "POKEMON LEAF" | "POKEMON EMER": - return CursorOptionEFRLG(idx).name + case "POKEMON EMER": + return CursorOptionE(idx).name + case "POKEMON FIRE" | "POKEMON LEAF": + return CursorOptionFRLG(idx).name case _: return CursorOptionRS(idx).name diff --git a/modules/menuing.py b/modules/menuing.py index 93a171c4..e144c853 100644 --- a/modules/menuing.py +++ b/modules/menuing.py @@ -223,18 +223,35 @@ def __init__(self, idx: int, mode: str): self.get_primary_option() self.subnavigator = None self.party = get_party() + self.wait_counter = 0 def get_primary_option(self): if self.mode in ["take_item", "give_item"]: self.primary_option = "ITEM" - if self.mode == "switch": - self.primary_option = "SWITCH" - if self.mode == "summary": - self.primary_option = "SUMMARY" + else: + self.primary_option = self.mode.upper() + + def wait_for_init(self): + match self.game: + case "POKEMON EMER" | "POKEMON FIRE" | "POKEMON LEAF": + task = "TASK_HANDLECHOOSEMONINPUT" + case "POKEMON RUBY" | "POKEMON SAPPH": + task = "HandleDefaultPartyMenu" + case _: + task = "" + while not task_is_active(task): + if self.wait_counter > 60: + context.message = "Error loading Pokemon menu. Switching to Manual mode." + context.bot_mode = "manual" + else: + self.wait_counter += 1 + yield def get_next_func(self): match self.current_step: case "None": + self.current_step = "wait_for_init" + case "wait_for_init": self.current_step = "navigate_to_mon" case "navigate_to_mon": self.current_step = "select_mon" @@ -259,6 +276,8 @@ def get_next_func(self): def update_navigator(self): match self.current_step: + case "wait_for_init": + self.navigator = self.wait_for_init() case "navigate_to_mon": self.navigator = self.navigate_to_mon() case "select_mon": diff --git a/modules/modes/__init__.py b/modules/modes/__init__.py index 80de5d32..28a29802 100644 --- a/modules/modes/__init__.py +++ b/modules/modes/__init__.py @@ -7,6 +7,7 @@ "Fishing", "Bunny Hop", "Static Soft Resets", + "Sweet Scent", "Tower Duo", "Ancient Legendaries", ] diff --git a/modules/modes/starters.py b/modules/modes/starters.py index 5ba0291c..5770eebb 100644 --- a/modules/modes/starters.py +++ b/modules/modes/starters.py @@ -335,15 +335,16 @@ def step(self): self.update_state(ModeStarterStates.TITLE) case ModeStarterStates.TITLE: - match get_game_state(): - case GameState.TITLE_SCREEN: + match get_game_state(), read_symbol("gQuestLogState"): + case GameState.TITLE_SCREEN, _: context.emulator.press_button(random.choice(["A", "Start", "Left", "Right", "Up"])) - case GameState.MAIN_MENU: + case GameState.MAIN_MENU, _: context.emulator.press_button("A") - case GameState.QUEST_LOG: - context.emulator.press_button("B") - case GameState.OVERWORLD: + case GameState.OVERWORLD, bytearray(b"\x00"): self.update_state(ModeStarterStates.OVERWORLD) + case GameState.OVERWORLD, _: + context.emulator.press_button("B") + case ModeStarterStates.OVERWORLD: context.message = "Pathing to starter..." diff --git a/modules/modes/sweet_scent.py b/modules/modes/sweet_scent.py new file mode 100644 index 00000000..b9b2f72d --- /dev/null +++ b/modules/modes/sweet_scent.py @@ -0,0 +1,98 @@ +from enum import Enum, auto +from modules.context import context +from modules.encounter import encounter_pokemon +from modules.memory import ( + get_game_state, + GameState +) +from modules.pokemon import get_opponent +from modules.tasks import task_is_active +from modules.menuing import StartMenuNavigator, PokemonPartyMenuNavigator +from modules.pokemon import get_party + + +class ModeSweetScentStates(Enum): + RESET = auto() + SELECT_SCENT = auto() + OVERWORLD = auto() + BATTLE = auto() + OPPONENT_CRY_START = auto() + OPPONENT_CRY_END = auto() + LOG_OPPONENT = auto() + + +class ModeSweetScent: + def __init__(self) -> None: + self.navigator = None + self.state: ModeSweetScentStates = ModeSweetScentStates.RESET + + def update_state(self, state: ModeSweetScentStates) -> None: + self.state: ModeSweetScentStates = state + + def step(self): + while True: + match self.state: + case ModeSweetScentStates.RESET: + if task_is_active("Task_HandleChooseMonInput"): + self.update_state(ModeSweetScentStates.SELECT_SCENT) + elif not task_is_active("Task_RunPerStepCallback") or task_is_active("Task_ShowStartMenu"): + context.emulator.press_button("B") + yield + else: + self.update_state(ModeSweetScentStates.OVERWORLD) + continue + + case ModeSweetScentStates.OVERWORLD: + if self.navigator is None: + self.navigator = StartMenuNavigator("POKEMON") + else: + yield from self.navigator.step() + match self.navigator.current_step: + case "exit": + self.navigator = None + self.update_state(ModeSweetScentStates.SELECT_SCENT) + continue + continue + + case ModeSweetScentStates.SELECT_SCENT: + if self.navigator is None: + scent_poke = None + for num, poke in enumerate(get_party()): + if "SWEET SCENT" in str(poke.moves).upper(): + scent_poke = num + break + if scent_poke is None: + raise Exception("No Pokemon with Sweet Scent in party") + context.set_manual_mode() + break + self.navigator = PokemonPartyMenuNavigator(scent_poke, "sweet_scent") + else: + yield from self.navigator.step() + match self.navigator.current_step: + case "exit": + self.navigator = None + self.update_state(ModeSweetScentStates.BATTLE) + continue + continue + + case ModeSweetScentStates.BATTLE: + if get_game_state() != GameState.BATTLE: + context.emulator.press_button("A") + else: + self.update_state(ModeSweetScentStates.OPPONENT_CRY_START) + continue + + case ModeSweetScentStates.OPPONENT_CRY_START: + if not task_is_active("Task_DuckBGMForPokemonCry"): + context.emulator.press_button("B") + else: + self.update_state(ModeSweetScentStates.OPPONENT_CRY_END) + continue + + case ModeSweetScentStates.OPPONENT_CRY_END: + if task_is_active("Task_DuckBGMForPokemonCry"): + pass + else: + return + + yield