Skip to content

Commit

Permalink
Add pickup item stat tracking + Discord webhooks
Browse files Browse the repository at this point in the history
  • Loading branch information
40Cakes committed Dec 16, 2023
1 parent 99d9a98 commit bb5dc53
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 140 deletions.
45 changes: 24 additions & 21 deletions modules/battle.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
from modules.pokemon import get_party, get_opponent, Pokemon, Move, LearnedMove
from modules.tasks import get_task, get_tasks, task_is_active

config = context.config


class BattleState(IntEnum):
# out-of-battle states
Expand Down Expand Up @@ -220,7 +218,7 @@ def get_next_func(self):
case "None":
self.current_step = "init_learn_move"
case "init_learn_move":
match config.battle.new_move:
match context.config.battle.new_move:
case "stop":
context.message = "New move trying to be learned, switching to manual mode..."
context.bot_mode = "Manual"
Expand Down Expand Up @@ -434,7 +432,7 @@ def select_option(self):
def handle_evolution(self):
while self.battle_state == BattleState.EVOLVING:
self.update_battle_state()
if config.battle.stop_evolution:
if context.config.battle.stop_evolution:
context.emulator.press_button("B")
yield
else:
Expand Down Expand Up @@ -470,10 +468,10 @@ def determine_battle_menu_action(self):
:return: an ordered pair containing A) the name of the action to take (fight, switch, flee, etc.) and B) the
index of the desired choice.
"""
if not config.battle.battle or not can_battle_happen():
if not context.config.battle.battle or not can_battle_happen():
self.choice = "flee"
self.idx = -1
elif config.battle.replace_lead_battler and self.should_rotate_lead:
elif context.config.battle.replace_lead_battler and self.should_rotate_lead:
mon_to_switch = self.get_mon_to_switch()
if mon_to_switch is None:
self.choice = "flee"
Expand All @@ -482,11 +480,11 @@ def determine_battle_menu_action(self):
self.choice = "switch"
self.idx = mon_to_switch
else:
match config.battle.battle_method:
match context.config.battle.battle_method:
case "strongest":
move = self.get_strongest_move()
if move == -1:
if config.battle.replace_lead_battler:
if context.config.battle.replace_lead_battler:
mon_to_switch = self.get_mon_to_switch()
if mon_to_switch is None:
self.choice = "flee"
Expand Down Expand Up @@ -537,7 +535,7 @@ def get_mon_to_switch(self, show_messages=True) -> int | None:
Pokémon seem to be fit to fight.
:return: the index of the Pokémon to switch with the active Pokémon
"""
match config.battle.switch_strategy:
match context.config.battle.switch_strategy:
case "first_available":
for i in range(len(self.party)):
if self.party[i] == self.current_battler or self.party[i].is_egg:
Expand All @@ -556,7 +554,7 @@ def get_mon_to_switch(self, show_messages=True) -> int | None:

@staticmethod
def is_valid_move(move: Move) -> bool:
return move is not None and move.name not in config.battle.banned_moves and move.base_power > 0
return move is not None and move.name not in context.config.battle.banned_moves and move.base_power > 0

@staticmethod
def get_move_power(move: LearnedMove, battler: Pokemon, target: Pokemon):
Expand Down Expand Up @@ -644,7 +642,7 @@ def handle_battler_faint(self):
function that handles lead battler fainting
"""
context.message = "Lead Pokémon fainted!"
match config.battle.faint_action:
match context.config.battle.faint_action:
case "stop":
context.message = "Switching to manual mode..."
context.bot_mode = "Manual"
Expand All @@ -671,10 +669,10 @@ def handle_battler_faint(self):
new_lead = self.get_mon_to_switch()
if new_lead is None:
context.message = "No viable pokemon to switch in!"
faint_action_default = str(config.battle.faint_action)
config.battle.faint_action = "flee"
faint_action_default = str(context.config.battle.faint_action)
context.config.battle.faint_action = "flee"
self.handle_battler_faint()
config.battle.faint_action = faint_action_default
context.config.battle.faint_action = faint_action_default
return False
switcher = send_out_pokemon(new_lead)
for i in switcher:
Expand Down Expand Up @@ -858,7 +856,7 @@ def calculate_new_move_viability(mon: Pokemon, new_move: Move) -> int:
"""

# exit learning move if new move is banned or has 0 power
if new_move.base_power == 0 or new_move.name in config.battle.banned_moves:
if new_move.base_power == 0 or new_move.name in context.config.battle.banned_moves:
context.message = f"{new_move.name} has base power of 0, so {mon.name} will skip learning it."
return 4
# get the effective power of each move
Expand All @@ -877,7 +875,7 @@ def calculate_new_move_viability(mon: Pokemon, new_move: Move) -> int:
power = move.base_power * attack_bonus
if move.type in mon.species.types:
power *= 1.5
if move.name in config.battle.banned_moves:
if move.name in context.config.battle.banned_moves:
power = 0
move_power.append(power)
# find the weakest move of the bunch
Expand Down Expand Up @@ -907,7 +905,7 @@ def calculate_new_move_viability(mon: Pokemon, new_move: Move) -> int:
power = move.base_power * attack_bonus
if move.type in mon.species.types:
power *= 1.5
if move.name in config.battle.banned_moves:
if move.name in context.config.battle.banned_moves:
power = 0
redundant_move_power.append(power)
weakest_move_power = min(redundant_move_power)
Expand Down Expand Up @@ -950,7 +948,7 @@ def can_battle_happen() -> bool:
if (
move is not None
and move.move.base_power > 0
and move.move.name not in config.battle.banned_moves
and move.move.name not in context.config.battle.banned_moves
and move.pp > 0
):
return True
Expand Down Expand Up @@ -1072,17 +1070,20 @@ def execute_menu_action(decision: tuple):
return


def check_lead_can_battle():
def check_lead_can_battle() -> bool:
"""
Determines whether the lead Pokémon is fit to fight
"""
if len(get_party()) < 1:
return False

lead = get_party()[0]
lead_has_moves = False
for move in lead.moves:
if (
move is not None
and move.move.base_power > 0
and move.move.name not in config.battle.banned_moves
and move.move.name not in context.config.battle.banned_moves
and move.pp > 0
):
lead_has_moves = True
Expand Down Expand Up @@ -1115,7 +1116,9 @@ def mon_has_enough_hp(mon: Pokemon) -> bool:


def move_is_usable(m: LearnedMove) -> bool:
return m is not None and m.move.base_power > 0 and m.pp > 0 and m.move.name not in config.battle.banned_moves
return (
m is not None and m.move.base_power > 0 and m.pp > 0 and m.move.name not in context.config.battle.banned_moves
)


class RotatePokemon(BaseMenuNavigator):
Expand Down
9 changes: 6 additions & 3 deletions modules/config/schemas_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,12 @@ class Discord(BaseConfig):
pokemon_encounter_milestones: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook(interval=10000))
shiny_pokemon_encounter_milestones: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook(interval=5))
total_encounter_milestones: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook(interval=25000))
phase_summary: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook())
phase_summary: DiscordWebhook = Field(
default_factory=lambda: DiscordWebhook(first_interval=8192, consequent_interval=5000)
)
anti_shiny_pokemon_encounter: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook())
custom_filter_pokemon_encounter: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook())
pickup: DiscordWebhook = Field(default_factory=lambda: DiscordWebhook(interval=10))


class DiscordWebhook(BaseConfig):
Expand All @@ -110,8 +113,8 @@ class DiscordWebhook(BaseConfig):
model_config = ConfigDict(coerce_numbers_to_str=True)

enable: bool = False
first_interval: PositiveInt | None = 0 # Only used by phase_summary.
consequent_interval: PositiveInt | None = 0 # Only used by phase_summary.
first_interval: PositiveInt | None = 0
consequent_interval: PositiveInt | None = 0
interval: PositiveInt = 0
ping_mode: Literal["user", "role", None] = None
ping_id: str | None = None
Expand Down
6 changes: 2 additions & 4 deletions modules/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,10 @@ def sv_colour(value: int) -> str:
def print_stats(total_stats: dict, pokemon: Pokemon, session_pokemon: set, encounter_rate: int) -> None:
type_colour = pokemon.species.types[0].name.lower()
rich_name = f"[{type_colour}]{pokemon.species.name}[/]"
console.print("\n")
console.rule(f"{rich_name} encountered at {pokemon.location_met}", style=type_colour)

match context.config.logging.console.encounter_data:
case "verbose":
console.rule(f"\n{rich_name} encountered at {pokemon.location_met}", style=type_colour)
pokemon_table = Table()
pokemon_table.add_column("PID", justify="center", width=10)
pokemon_table.add_column("Level", justify="center")
Expand All @@ -89,6 +88,7 @@ def print_stats(total_stats: dict, pokemon: Pokemon, session_pokemon: set, encou
)
console.print(pokemon_table)
case "basic":
console.rule(f"\n{rich_name} encountered at {pokemon.location_met}", style=type_colour)
console.print(
f"{rich_name}: PID: {str(hex(pokemon.personality_value)[2:]).upper()} | "
f"Lv: {pokemon.level:,} | "
Expand Down Expand Up @@ -222,7 +222,5 @@ def print_stats(total_stats: dict, pokemon: Pokemon, session_pokemon: set, encou
f"Total Shiny Average: {total_stats['totals'].get('shiny_average', 'N/A')})"
)

console.print(f"[yellow]Encounter rate[/]: {encounter_rate:,}/h")


console = Console(theme=theme)
6 changes: 6 additions & 0 deletions modules/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
from pathlib import Path
from pypresence import Presence
from discord_webhook import DiscordWebhook, DiscordEmbed

from modules.context import context
from modules.version import pokebot_version


def discord_message(
Expand Down Expand Up @@ -48,6 +50,10 @@ def discord_message(

if embed_footer:
embed_obj.set_footer(text=embed_footer)
else:
embed_obj.set_footer(
text=f"ID: {context.config.discord.bot_id} | {context.rom.game_name}\nPokéBot {pokebot_version}"
)

embed_obj.set_timestamp()
webhook.add_embed(embed_obj)
Expand Down
15 changes: 7 additions & 8 deletions modules/encounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@ def encounter_pokemon(pokemon: Pokemon) -> None:
:return:
"""
config = context.config
if config.logging.save_pk3.all:
if context.config.logging.save_pk3.all:
save_pk3(pokemon)

if pokemon.is_shiny:
config.reload_file("catch_block")
context.config.reload_file("catch_block")

custom_filter_result = total_stats.custom_catch_filters(pokemon)
custom_found = isinstance(custom_filter_result, str)

total_stats.log_encounter(pokemon, config.catch_block.block_list, custom_filter_result)
total_stats.log_encounter(pokemon, context.config.catch_block.block_list, custom_filter_result)

encounter_summary = (
f"Encountered a {pokemon.species.name} with a shiny value of {pokemon.shiny_value:,}!\n\n"
Expand All @@ -49,7 +48,7 @@ def encounter_pokemon(pokemon: Pokemon) -> None:
# TODO temporary until auto-catch is ready
if pokemon.is_shiny or custom_found or BattleTypeFlag.ROAMER in battle_type_flags:
if pokemon.is_shiny:
if not config.logging.save_pk3.all and config.logging.save_pk3.shiny:
if not context.config.logging.save_pk3.all and context.config.logging.save_pk3.shiny:
save_pk3(pokemon)
state_tag = "shiny"
console.print("[bold yellow]Shiny found!")
Expand All @@ -61,7 +60,7 @@ def encounter_pokemon(pokemon: Pokemon) -> None:
alert_message = f"Found a ✨shiny {pokemon.species.name}✨! 🥳"

elif custom_found:
if not config.logging.save_pk3.all and config.logging.save_pk3.custom:
if not context.config.logging.save_pk3.all and context.config.logging.save_pk3.custom:
save_pk3(pokemon)
state_tag = "customfilter"
console.print("[bold green]Custom filter Pokemon found!")
Expand All @@ -83,15 +82,15 @@ def encounter_pokemon(pokemon: Pokemon) -> None:
alert_title = None
alert_message = None

if not custom_found and pokemon.species.name in config.catch_block.block_list:
if not custom_found and pokemon.species.name in context.config.catch_block.block_list:
console.print(f"[bold yellow]{pokemon.species.name} is on the catch block list, skipping encounter...")
else:
filename_suffix = f"{state_tag}_{pokemon.species.safe_name}"
context.emulator.create_save_state(suffix=filename_suffix)

# TEMPORARY until auto-battle/auto-catch is done
# if the mon is saved and imported, no need to catch it by hand
if config.logging.import_pk3:
if context.config.logging.import_pk3:
pokemon_storage = get_pokemon_storage()

if pokemon_storage.contains_pokemon(pokemon):
Expand Down
2 changes: 2 additions & 0 deletions modules/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Item:

index: int
name: str
sprite_name: str
price: int
type: ItemType
pocket: ItemPocket
Expand All @@ -61,6 +62,7 @@ def from_dict(cls, index: int, data: dict) -> "Item":
return Item(
index=index,
name=data["name"],
sprite_name=data["name"].replace("'", "-").replace(".", "-"),
price=data["price"],
type=item_type,
pocket=ItemPocket(data["pocket"]),
Expand Down
24 changes: 11 additions & 13 deletions modules/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,18 @@ def main_loop() -> None:
"""
from modules.encounter import encounter_pokemon # prevents instantiating TotalStats class before profile selected

encounter_counter = 0
pickup_checked = False
lead_rotated = False

try:
mode = None

config = context.config

if config.discord.rich_presence:
if context.config.discord.rich_presence:
from modules.discord import discord_rich_presence

Thread(target=discord_rich_presence).start()

if config.obs.http_server.enable:
if context.config.obs.http_server.enable:
from modules.web.http import http_server

Thread(target=http_server).start()
Expand All @@ -56,24 +53,25 @@ def main_loop() -> None:
pickup_checked = False
lead_rotated = False
encounter_pokemon(get_opponent())
encounter_counter += 1
if context.bot_mode != "Manual":
mode = BattleHandler()

if context.bot_mode == "Manual":
if mode:
mode = None

elif (
not mode and config.battle.pickup and should_check_for_pickup(encounter_counter) and not pickup_checked
):
mode = MenuWrapper(CheckForPickup(encounter_counter))
elif not mode and context.config.battle.pickup and should_check_for_pickup() and not pickup_checked:
pickup_checked = True
encounter_counter = 0
mode = MenuWrapper(CheckForPickup())

elif not mode and config.battle.replace_lead_battler and not check_lead_can_battle() and not lead_rotated:
mode = MenuWrapper(RotatePokemon())
elif (
not mode
and context.config.battle.replace_lead_battler
and not check_lead_can_battle()
and not lead_rotated
):
lead_rotated = True
mode = MenuWrapper(RotatePokemon())

elif not mode:
match context.bot_mode:
Expand Down
Loading

0 comments on commit bb5dc53

Please sign in to comment.