Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent catching modes from starting when no Pokeballs available #497

Merged
merged 2 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ def first_slot_index_for(self, item: Item) -> int | None:
def number_of_repels(self) -> int:
return sum(slot.quantity for slot in self.items if slot.item.name in ("Repel", "Super Repel", "Max Repel"))

@property
def number_of_balls_except_master_ball(self) -> int:
return sum(slot.quantity for slot in self.poke_balls if slot.item.index != 1)

def to_dict(self) -> dict:
return {
"items": [s.to_dict() for s in self.items],
Expand Down
17 changes: 16 additions & 1 deletion modules/modes/_asserts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from modules.context import context
from modules.items import get_item_bag, get_item_by_name
from modules.map import ObjectEvent, calculate_targeted_coords
from modules.map_data import MapRSE, MapFRLG
from modules.map_data import MapRSE, MapFRLG, is_safari_map
from modules.safari_strategy import get_safari_balls_left
from modules.battle_state import BattleOutcome
from modules.player import get_player
from modules.pokemon import get_party
from modules.save_data import get_save_data
Expand Down Expand Up @@ -161,3 +163,16 @@ def assert_empty_slot_in_party(error_message: str, check_in_saved_game: bool = F
if check_in_saved_game and len(get_party()) < 6:
error_message += _error_message_addendum_if_assert_only_failed_in_saved_game
raise BotModeError(error_message)


def assert_player_has_poke_balls() -> None:
"""
Raises an exception if the player doesn't have any Pokeballs when starting a catching mode
or if safari ball threshold is reached.
"""
if is_safari_map():
if context.rom.is_frlg and get_safari_balls_left() <= 15:
raise BotModeError("You have less than 15 balls left, switching to manual mode...")
else:
if get_item_bag().number_of_balls_except_master_ball == 0:
raise BotModeError("Out of Pokéballs! Better grab more before the next shiny slips away...")
8 changes: 7 additions & 1 deletion modules/modes/bunny_hop.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from modules.context import context
from modules.items import get_item_by_name
from modules.player import AcroBikeState, TileTransitionState, get_player_avatar
from ._asserts import assert_item_exists_in_bag
from modules.battle_state import BattleOutcome
from ._asserts import assert_item_exists_in_bag, assert_player_has_poke_balls
from ._interface import BotMode
from .util import apply_white_flute_if_available, register_key_item

Expand All @@ -20,7 +21,12 @@ def is_selectable() -> bool:
else:
return False

def on_battle_ended(self, outcome: "BattleOutcome") -> None:
if not outcome == BattleOutcome.Lost:
assert_player_has_poke_balls()

def run(self) -> Generator:
assert_player_has_poke_balls()
assert_item_exists_in_bag(("Acro Bike",), "You need to have the Acro Bike in order to use this mode.")
yield from register_key_item(get_item_by_name("Acro Bike"))

Expand Down
8 changes: 8 additions & 0 deletions modules/modes/feebas.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
from modules.map_data import MapRSE
from modules.player import get_player, get_player_avatar, AvatarFlags
from modules.pokemon import get_party
from modules.battle_state import BattleOutcome
from . import BattleAction
from ._asserts import assert_player_has_poke_balls
from ._interface import BotMode, BotModeError
from .util import (
ensure_facing_direction,
Expand Down Expand Up @@ -146,7 +148,13 @@ def on_battle_started(self, encounter: EncounterInfo | None) -> BattleAction | N

return None

def on_battle_ended(self, outcome: "BattleOutcome") -> None:
if not outcome == BattleOutcome.Lost:
assert_player_has_poke_balls()

def run(self) -> Generator:
assert_player_has_poke_balls()

if not get_player_avatar().flags.Surfing:
raise BotModeError("Player is not surfing, only start this mode while surfing in any water at Route 119.")

Expand Down
14 changes: 6 additions & 8 deletions modules/modes/fishing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from modules.items import get_item_bag, get_item_by_name
from modules.player import get_player, get_player_avatar
from modules.runtime import get_sprites_path
from modules.map_data import is_safari_map
from modules.safari_strategy import get_safari_balls_left
from ._asserts import assert_item_exists_in_bag
from modules.battle_state import BattleOutcome
from ._asserts import assert_item_exists_in_bag, assert_player_has_poke_balls
from ._interface import BotMode
from .util import fish, register_key_item

Expand All @@ -24,13 +23,12 @@ def is_selectable() -> bool:
return targeted_tile is not None and targeted_tile.is_surfable

def on_battle_ended(self, outcome: "BattleOutcome") -> None:
if is_safari_map():
balls_left = get_safari_balls_left()
if balls_left <= 15:
context.message = "You have less than 15 balls left, switching to manual mode..."
return context.set_manual_mode()
if not outcome == BattleOutcome.Lost:
assert_player_has_poke_balls()

def run(self) -> Generator:
assert_player_has_poke_balls()

# Ask player to register a rod if they have one
rod_names = ["Old Rod", "Good Rod", "Super Rod"]
assert_item_exists_in_bag(rod_names, "You do not own any fishing rod, so you cannot fish.")
Expand Down
5 changes: 2 additions & 3 deletions modules/modes/kecleon.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
from modules.pokemon import get_party
from modules.save_data import get_last_heal_location
from . import BattleAction
from ._asserts import (
assert_has_pokemon_with_move,
)
from ._asserts import assert_has_pokemon_with_move, assert_player_has_poke_balls
from ._interface import BotMode, BotModeError
from .util import ensure_facing_direction, navigate_to
from ..battle_strategies import BattleStrategy
Expand Down Expand Up @@ -43,6 +41,7 @@ def on_whiteout(self) -> bool:
return True

def run(self) -> Generator:
assert_player_has_poke_balls()
assert_has_pokemon_with_move("Selfdestruct", "This mode requires a Pokémon with the move Selfdestruct.")
if not (get_event_flag("RECEIVED_DEVON_SCOPE")):
raise BotModeError("This mode requires the Devon Scope.")
Expand Down
4 changes: 3 additions & 1 deletion modules/modes/roamer_reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from modules.runtime import get_sprites_path
from modules.save_data import get_save_data
from modules.tasks import get_global_script_context
from ._asserts import SavedMapLocation, assert_save_game_exists, assert_saved_on_map
from ._asserts import SavedMapLocation, assert_save_game_exists, assert_saved_on_map, assert_player_has_poke_balls
from ._interface import BattleAction, BotMode, BotModeError
from .util import (
RanOutOfRepels,
Expand Down Expand Up @@ -115,6 +115,8 @@ def run(self) -> Generator:
f"{highest_encounter_level + 1} in order for Repel to work."
)

assert_player_has_poke_balls()

if save_data.get_item_bag().number_of_repels == 0:
raise BotModeError("You do not have any repels in your item bag. Go and get some first!")

Expand Down
15 changes: 14 additions & 1 deletion modules/modes/rock_smash.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@
from modules.player import TileTransitionState, get_player, get_player_avatar, AvatarFlags, get_player_location
from modules.pokemon import get_opponent
from modules.runtime import get_sprites_path
from modules.battle_state import BattleOutcome
from modules.save_data import get_save_data
from modules.tasks import task_is_active, get_global_script_context
from . import BattleAction
from ._asserts import SavedMapLocation, assert_has_pokemon_with_move, assert_save_game_exists, assert_saved_on_map
from ._asserts import (
SavedMapLocation,
assert_has_pokemon_with_move,
assert_save_game_exists,
assert_saved_on_map,
assert_player_has_poke_balls,
)
from ._interface import BotMode, BotModeError
from .util import (
navigate_to,
Expand Down Expand Up @@ -81,6 +88,10 @@ def on_battle_started(self, encounter: EncounterInfo | None) -> BattleAction | N
debug.debug_values["Nosepass per Hour"] = encounter_rate_at_1x
return handle_encounter(encounter)

def on_battle_ended(self, outcome: "BattleOutcome") -> None:
if not outcome == BattleOutcome.Lost:
assert_player_has_poke_balls()

def on_repel_effect_ended(self) -> None:
if self._using_repel:
try:
Expand Down Expand Up @@ -113,6 +124,8 @@ def run(self) -> Generator:
"In order to rock smash for Shuckle you should save in the entrance building to the Safari Zone.",
)

assert_player_has_poke_balls()

if get_player_avatar().map_group_and_number == MapRSE.GRANITE_CAVE_B2F and get_item_bag().number_of_repels > 0:
mode = ask_for_choice(
[
Expand Down
12 changes: 5 additions & 7 deletions modules/modes/spin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from modules.context import context
from modules.player import get_player_avatar
from modules.map_data import is_safari_map
from modules.safari_strategy import get_safari_balls_left
from modules.battle_state import BattleOutcome
from ._interface import BotMode
from ._asserts import assert_player_has_poke_balls
from .util import apply_white_flute_if_available, spin


Expand All @@ -18,12 +18,10 @@ def is_selectable() -> bool:
return get_player_avatar().map_location.has_encounters

def on_battle_ended(self, outcome: "BattleOutcome") -> None:
if is_safari_map():
balls_left = get_safari_balls_left()
if balls_left <= 15:
context.message = "You have less than 15 balls left, switching to manual mode..."
return context.set_manual_mode()
if not outcome == BattleOutcome.Lost:
assert_player_has_poke_balls()

def run(self) -> Generator:
assert_player_has_poke_balls()
yield from apply_white_flute_if_available()
yield from spin()
3 changes: 3 additions & 0 deletions modules/modes/static_run_away.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from modules.memory import get_event_flag
from modules.player import get_player_avatar
from modules.pokemon import get_opponent
from ._asserts import assert_player_has_poke_balls
from ._interface import BattleAction, BotMode, BotModeError
from .util import (
follow_path,
Expand Down Expand Up @@ -183,6 +184,8 @@ def path():
if get_event_flag(flag_to_check):
raise BotModeError(f"{pokemon_name} has already been caught.")

assert_player_has_poke_balls()

while True:
yield from path()

Expand Down
4 changes: 3 additions & 1 deletion modules/modes/static_soft_resets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from modules.map_data import MapFRLG, MapRSE
from modules.player import get_player_avatar
from modules.save_data import get_save_data
from ._asserts import SavedMapLocation, assert_save_game_exists, assert_saved_on_map
from ._asserts import SavedMapLocation, assert_save_game_exists, assert_saved_on_map, assert_player_has_poke_balls
from ._interface import BattleAction, BotMode, BotModeError
from .util import (
soft_reset,
Expand Down Expand Up @@ -105,6 +105,8 @@ def run(self) -> Generator:
if encounter.condition is not None and not encounter.condition():
raise BotModeError(f"This {encounter.name} has already been encountered.")

assert_player_has_poke_balls()

while context.bot_mode != "Manual":
yield from soft_reset(mash_random_keys=True)
yield from wait_for_unique_rng_value()
Expand Down
11 changes: 10 additions & 1 deletion modules/modes/sudowoodo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
from modules.encounter import handle_encounter, log_encounter, EncounterInfo
from modules.map_data import MapRSE
from modules.player import get_player_avatar
from ._asserts import SavedMapLocation, assert_registered_item, assert_save_game_exists, assert_saved_on_map
from ._asserts import (
SavedMapLocation,
assert_registered_item,
assert_save_game_exists,
assert_saved_on_map,
assert_player_has_poke_balls,
)
from ._interface import BattleAction, BotMode
from .util import soft_reset, wait_for_task_to_start_and_finish, wait_for_unique_rng_value, wait_until_task_is_active

Expand Down Expand Up @@ -33,12 +39,15 @@ def run(self) -> Generator:
SavedMapLocation(MapRSE.BATTLE_FRONTIER_OUTSIDE_EAST, (54, 62), facing=True),
"The game has not been saved on this tile.",
)

assert_registered_item(
["Wailmer Pail"],
"You need to register the Wailmer Pail for the Select button.",
check_in_saved_game=True,
)

assert_player_has_poke_balls()

while context.bot_mode != "Manual":
yield from soft_reset(mash_random_keys=True)
yield from wait_for_unique_rng_value()
Expand Down
8 changes: 8 additions & 0 deletions modules/modes/sweet_scent.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
from modules.menuing import PokemonPartyMenuNavigator, StartMenuNavigator
from modules.modes._asserts import assert_has_pokemon_with_move
from modules.player import get_player_avatar
from modules.battle_state import BattleOutcome
from modules.pokemon import get_move_by_name, get_party
from ._asserts import assert_player_has_poke_balls
from ._interface import BotMode


Expand All @@ -19,7 +21,13 @@ def name() -> str:
def is_selectable() -> bool:
return get_player_avatar().map_location.has_encounters

def on_battle_ended(self, outcome: "BattleOutcome") -> None:
if not outcome == BattleOutcome.Lost:
assert_player_has_poke_balls()

def run(self) -> Generator:
assert_player_has_poke_balls()

assert_has_pokemon_with_move(
"Sweet Scent", "None of your party Pokémon know the move Sweet Scent. Please teach it to someone."
)
Expand Down
Loading