import random from .enums import Ability, ElementType class ExpiringEffect(): """ Some effect that has a specific amount of time it is active. turns_to_expire can be None, in which case this effect never expires. """ def __init__(self, turns_to_expire: int): self._remaining_turns = turns_to_expire def active(self): """Returns True if this effect is still active, False otherwise.""" if self._remaining_turns is None: return True return bool(self._remaining_turns) def next_turn(self): """ Progresses this effect for a turn. Returns True if the effect just ended. """ if self._remaining_turns is None: return False if self.active(): self._remaining_turns -= 1 return not self.active() return False def set_turns(self, turns_to_expire): """Set the amount of turns until this effect expires.""" self._remaining_turns = turns_to_expire class Weather(ExpiringEffect): """ The current weather of the battlefield. Options: -hail -sandstorm -h-rain -rain -h-sun -sun -h-wind """ def __init__(self, battle): super().__init__(0) self._weather_type = "" self.battle = battle def _expire_weather(self): """Clear the current weather and update Castform forms.""" self._weather_type = "" for poke in (self.battle.trainer1.current_pokemon, self.battle.trainer2.current_pokemon): if poke is None: continue # Forecast if poke.ability() == Ability.FORECAST and poke._name in ("Castform-snowy", "Castform-rainy", "Castform-sunny"): if poke.form("Castform"): poke.type_ids = [ElementType.NORMAL] def next_turn(self): """Progresses the weather a turn.""" if super().next_turn(): self._expire_weather() return True return False def recheck_ability_weather(self): """Checks if strong weather effects from a pokemon with a weather ability need to be removed.""" maintain_weather = False for poke in (self.battle.trainer1.current_pokemon, self.battle.trainer2.current_pokemon): if poke is None: continue if self._weather_type == "h-wind" and poke.ability() == Ability.DELTA_STREAM: maintain_weather = True if self._weather_type == "h-sun" and poke.ability() == Ability.DESOLATE_LAND: maintain_weather = True if self._weather_type == "h-rain" and poke.ability() == Ability.PRIMORDIAL_SEA: maintain_weather = True if self._weather_type in ("h-wind", "h-sun", "h-rain") and not maintain_weather: self._expire_weather() return True return False def get(self): """Get the current weather type.""" for poke in (self.battle.trainer1.current_pokemon, self.battle.trainer2.current_pokemon): if poke is None: continue if poke.ability() in (Ability.CLOUD_NINE, Ability.AIR_LOCK): return "" return self._weather_type def set(self, weather: str, pokemon): """ Set the weather, lasting a certain number of turns. Returns a formatted message indicating any weather change. """ msg = "" turns = None element = None castform = None if self._weather_type == weather: return "" if weather == "hail": if self._weather_type in ("h-rain", "h-sun", "h-wind"): return "" if pokemon.held_item == "icy-rock": turns = 8 else: turns = 5 msg += "It starts to hail!\n" element = ElementType.ICE castform = "Castform-snowy" elif weather == "sandstorm": if self._weather_type in ("h-rain", "h-sun", "h-wind"): return "" if pokemon.held_item == "smooth-rock": turns = 8 else: turns = 5 msg += "A sandstorm is brewing up!\n" element = ElementType.NORMAL castform = "Castform" elif weather == "rain": if self._weather_type in ("h-rain", "h-sun", "h-wind"): return "" if pokemon.held_item == "damp-rock": turns = 8 else: turns = 5 msg += "It starts to rain!\n" element = ElementType.WATER castform = "Castform-rainy" elif weather == "sun": if self._weather_type in ("h-rain", "h-sun", "h-wind"): return "" if pokemon.held_item == "heat-rock": turns = 8 else: turns = 5 msg += "The sunlight is strong!\n" element = ElementType.FIRE castform = "Castform-sunny" elif weather == "h-rain": msg += "Heavy rain begins to fall!\n" element = ElementType.WATER castform = "Castform-rainy" elif weather == "h-sun": msg += "The sunlight is extremely harsh!\n" element = ElementType.FIRE castform = "Castform-sunny" elif weather == "h-wind": msg += "The winds are extremely strong!\n" element = ElementType.NORMAL castform = "Castform" else: raise ValueError("unexpected weather") # Forecast t = ElementType(element).name.lower() for poke in (self.battle.trainer1.current_pokemon, self.battle.trainer2.current_pokemon): if poke is None: continue if poke.ability() == Ability.FORECAST and poke._name != castform: if poke.form(castform): poke.type_ids = [element] msg += f"{poke.name} transformed into a {t} type using its forecast!\n" self._weather_type = weather self._remaining_turns = turns return msg class LockedMove(ExpiringEffect): """A multi-turn move that a pokemon is locked into.""" def __init__(self, move, turns_to_expire: int): super().__init__(turns_to_expire) self.move = move self.turn = 0 def next_turn(self): """Progresses the move a turn.""" expired = super().next_turn() self.turn += 1 return expired def is_last_turn(self): """Returns True if this is the last turn this move will be used.""" return self._remaining_turns == 1 class ExpiringItem(ExpiringEffect): """An expiration timer with some data.""" def __init__(self): super().__init__(0) self.item = None def next_turn(self): """Progresses the effect a turn.""" expired = super().next_turn() if expired: self.item = None return expired def set(self, item, turns: int): """Set the item and turns until expiration.""" self.item = item self._remaining_turns = turns def end(self): """Ends this expiring item.""" self.item = None self._remaining_turns = 0 class Terrain(ExpiringItem): """The terrain of the battle""" def __init__(self, battle): super().__init__() self.battle = battle def next_turn(self): """Progresses the effect a turn.""" expired = super().next_turn() if expired: self.end() return expired def set(self, item, attacker): """ Set the terrain and turns until expiration. Returns a formatted string. """ if item == self.item: return f"There's already a {item} terrain!\n" turns = 8 if attacker.held_item == "terrain-extender" else 5 super().set(item, turns) msg = f"{attacker.name} creates a{'n' if item == 'electric' else ''} {item} terrain!\n" # Mimicry element = None if item == "electric": element = ElementType.ELECTRIC elif item == "grassy": element = ElementType.GRASS elif item == "misty": element = ElementType.FAIRY elif item == "psychic": element = ElementType.PSYCHIC for poke in (self.battle.trainer1.current_pokemon, self.battle.trainer2.current_pokemon): if poke is None: continue if poke.ability() == Ability.MIMICRY: poke.type_ids = [element] t = ElementType(element).name.lower() msg += f"{poke.name} became a {t} type using its mimicry!\n" if poke.held_item == "electric-seed" and item == "electric": msg += poke.append_defense(1, attacker=poke, source="its electric seed") poke.held_item.use() if poke.held_item == "psychic-seed" and item == "psychic": msg += poke.append_spdef(1, attacker=poke, source="its psychic seed") poke.held_item.use() if poke.held_item == "misty-seed" and item == "misty": msg += poke.append_spdef(1, attacker=poke, source="its misty seed") poke.held_item.use() if poke.held_item == "grassy-seed" and item == "grassy": msg += poke.append_defense(1, attacker=poke, source="its grassy seed") poke.held_item.use() return msg def end(self): """Ends the terrain.""" super().end() # Mimicry for poke in (self.battle.trainer1.current_pokemon, self.battle.trainer2.current_pokemon): if poke is None: continue if poke.ability() == Ability.MIMICRY: poke.type_ids = poke.starting_type_ids.copy() class ExpiringWish(ExpiringEffect): """Stores the HP and when to heal for the move Wish.""" def __init__(self): super().__init__(0) self.hp = None def next_turn(self): """Progresses the effect a turn.""" expired = super().next_turn() hp = 0 if expired: hp = self.hp self.hp = None return hp def set(self, hp): """Set the move and turns until expiration.""" self.hp = hp self._remaining_turns = 2 class NonVolatileEffect(): """The current non volatile effect status.""" def __init__(self, pokemon): self.current = "" self.pokemon = pokemon self.sleep_timer = ExpiringEffect(0) self.badly_poisoned_turn = 0 def next_turn(self, battle): """ Progresses this status by a turn. Returns a formatted string if a status wore off. """ if not self.current: return "" if self.current == "b-poison": self.badly_poisoned_turn += 1 if self.pokemon.ability() == Ability.HYDRATION and battle.weather.get() in ("rain", "h-rain"): removed = self.current self.reset() return f"{self.pokemon.name}'s hydration cured its {removed}!\n" if self.pokemon.ability() == Ability.SHED_SKIN and not random.randint(0, 2): removed = self.current self.reset() return f"{self.pokemon.name}'s shed skin cured its {removed}!\n" # The poke still has a status effect, apply damage if self.current == "burn": damage = max(1, self.pokemon.starting_hp // 16) if self.pokemon.ability() == Ability.HEATPROOF: damage //= 2 return self.pokemon.damage(damage, battle, source="its burn") if self.current == "b-poison": if self.pokemon.ability() == Ability.POISON_HEAL: return self.pokemon.heal(self.pokemon.starting_hp // 8, source="its poison heal") damage = max(1, (self.pokemon.starting_hp // 16) * min(15, self.badly_poisoned_turn)) return self.pokemon.damage(damage, battle, source="its bad poison") if self.current == "poison": if self.pokemon.ability() == Ability.POISON_HEAL: return self.pokemon.heal(self.pokemon.starting_hp // 8, source="its poison heal") damage = max(1, self.pokemon.starting_hp // 8) return self.pokemon.damage(damage, battle, source="its poison") if self.current == "sleep" and self.pokemon.nightmare: return self.pokemon.damage(self.pokemon.starting_hp // 4, battle, source="its nightmare") return "" def burn(self): """Returns True if the pokemon is burned.""" return self.current == "burn" def sleep(self): """Returns True if the pokemon is asleep.""" if self.pokemon.ability() == Ability.COMATOSE: return True return self.current == "sleep" def poison(self): """Returns True if the pokemon is poisoned.""" return self.current in ("poison", "b-poison") def paralysis(self): """Returns True if the pokemon is paralyzed.""" return self.current == "paralysis" def freeze(self): """Returns True if the pokemon is frozen.""" return self.current == "freeze" def apply_status(self, status, battle, *, attacker=None, move=None, turns=None, force=False, source: str=""): """ Apply a non volatile status to a pokemon. Returns a formatted message. """ msg = "" if source: source = f" from {source}" if self.current and not force: return f"{self.pokemon.name} already has a status, it can't get {status} too!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.COMATOSE: return f"{self.pokemon.name} already has a status, it can't get {status} too!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.PURIFYING_SALT: return f"{self.pokemon.name}'s purifying salt protects it from being inflicted with {status}!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.LEAF_GUARD and battle.weather.get() in ("sun", "h-sun"): return f"{self.pokemon.name}'s leaf guard protects it from being inflicted with {status}!\n" if self.pokemon.substitute and attacker is not self.pokemon and (move is None or move.is_affected_by_substitute()): return f"{self.pokemon.name}'s substitute protects it from being inflicted with {status}!\n" if self.pokemon.owner.safeguard.active() and attacker is not self.pokemon and (attacker is None or attacker.ability() != Ability.INFILTRATOR): return f"{self.pokemon.name}'s safeguard protects it from being inflicted with {status}!\n" if self.pokemon.grounded(battle, attacker=attacker, move=move) and battle.terrain.item == "misty": return f"The misty terrain protects {self.pokemon.name} from being inflicted with {status}!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.FLOWER_VEIL and ElementType.GRASS in self.pokemon.type_ids: return f"{self.pokemon.name}'s flower veil protects it from being inflicted with {status}!\n" if self.pokemon._name == "Minior": return "Minior's hard shell protects it from status effects!\n" if status == "burn": if ElementType.FIRE in self.pokemon.type_ids: return f"{self.pokemon.name} is a fire type and can't be burned!\n" if self.pokemon.ability(attacker=attacker, move=move) in (Ability.WATER_VEIL, Ability.WATER_BUBBLE): ability_name = Ability(self.pokemon.ability_id).pretty_name return f"{self.pokemon.name}'s {ability_name} prevents it from getting burned!\n" self.current = status msg += f"{self.pokemon.name} was burned{source}!\n" if status == "sleep": if self.pokemon.ability(attacker=attacker, move=move) in (Ability.INSOMNIA, Ability.VITAL_SPIRIT, Ability.SWEET_VEIL): ability_name = Ability(self.pokemon.ability_id).pretty_name return f"{self.pokemon.name}'s {ability_name} keeps it awake!\n" if self.pokemon.grounded(battle, attacker=attacker, move=move) and battle.terrain.item == "electric": return f"The terrain is too electric for {self.pokemon.name} to fall asleep!\n" if battle.trainer1.current_pokemon and battle.trainer1.current_pokemon.uproar.active(): return f"An uproar keeps {self.pokemon.name} from falling asleep!\n" if battle.trainer2.current_pokemon and battle.trainer2.current_pokemon.uproar.active(): return f"An uproar keeps {self.pokemon.name} from falling asleep!\n" if turns is None: turns = random.randint(2, 4) if self.pokemon.ability(attacker=attacker, move=move) == Ability.EARLY_BIRD: turns //= 2 self.current = status self.sleep_timer.set_turns(turns) msg += f"{self.pokemon.name} fell asleep{source}!\n" if status in ("poison", "b-poison"): if attacker is None or attacker.ability() != Ability.CORROSION: if ElementType.STEEL in self.pokemon.type_ids: return f"{self.pokemon.name} is a steel type and can't be poisoned!\n" if ElementType.POISON in self.pokemon.type_ids: return f"{self.pokemon.name} is a poison type and can't be poisoned!\n" if self.pokemon.ability(attacker=attacker, move=move) in (Ability.IMMUNITY, Ability.PASTEL_VEIL): ability_name = Ability(self.pokemon.ability_id).pretty_name return f"{self.pokemon.name}'s {ability_name} keeps it from being poisoned!\n" self.current = status bad = " badly" if status == "b-poison" else "" msg += f"{self.pokemon.name} was{bad} poisoned{source}!\n" if move is not None and attacker is not None and attacker.ability() == Ability.POISON_PUPPETEER: msg += self.pokemon.confuse(attacker=attacker, source=f"{attacker.name}'s poison puppeteer") if status == "paralysis": if ElementType.ELECTRIC in self.pokemon.type_ids: return f"{self.pokemon.name} is an electric type and can't be paralyzed!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.LIMBER: return f"{self.pokemon.name}'s limber keeps it from being paralyzed!\n" self.current = status msg += f"{self.pokemon.name} was paralyzed{source}!\n" if status == "freeze": if ElementType.ICE in self.pokemon.type_ids: return f"{self.pokemon.name} is an ice type and can't be frozen!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.MAGMA_ARMOR: return f"{self.pokemon.name}'s magma armor keeps it from being frozen!\n" if battle.weather.get() in ("sun", "h-sun"): return f"It's too sunny to freeze {self.pokemon.name}!\n" self.current = status msg += f"{self.pokemon.name} was frozen solid{source}!\n" if self.pokemon.ability(attacker=attacker, move=move) == Ability.SYNCHRONIZE and attacker is not None: msg += attacker.nv.apply_status(status, battle, attacker=self.pokemon, source=f"{self.pokemon.name}'s synchronize") if self.pokemon.held_item.should_eat_berry_status(attacker): msg += self.pokemon.held_item.eat_berry(attacker=attacker, move=move) return msg def reset(self): """Remove a non volatile status from a pokemon.""" self.current = "" self.badly_poisoned_turn = 0 self.sleep_timer.set_turns(0) self.pokemon.nightmare = False class Metronome(): """Holds recent move status for the held item metronome.""" def __init__(self): self.move = "" self.count = 0 def reset(self): """A move failed or a non-move action was done.""" self.move = "" self.count = 0 def use(self, movename): """Updates the metronome based on a used move.""" if self.move == movename: self.count += 1 else: self.move = movename self.count = 1 def get_buff(self, movename): """Get the buff multiplier for this metronome.""" if self.move != movename: return 1 return min(2, 1 + (.2 * self.count)) class Item(): """Stores information about an item.""" def __init__(self, item_data): self.name = item_data["identifier"] self.id = item_data["id"] self.power = item_data["fling_power"] self.effect = item_data["fling_effect_id"] class HeldItem(): """Stores information about the current held item for a particualar poke.""" def __init__(self, item_data, owner): if item_data is None: self.item = None else: self.item = Item(item_data) self.owner = owner self.battle = None self.last_used = None self.ever_had_item = self.item is not None def get(self): """Get the current held item identifier.""" if self.item is None: return None if not self.can_remove(): return self.item.name if self.owner.embargo.active(): return None if self.battle and self.battle.magic_room.active(): return None if self.owner.ability() == Ability.KLUTZ: return None if self.owner.corrosive_gas: return None return self.item.name def has_item(self): """Helper method to prevent attempting to acquire a new item if the poke already has one.""" return self.item is not None def can_remove(self): """Returns a boolean indicating whether this held item can be removed.""" return self.name not in ( # Plates "draco-plate", "dread-plate", "earth-plate", "fist-plate", "flame-plate", "icicle-plate", "insect-plate", "iron-plate", "meadow-plate", "mind-plate", "pixie-plate", "sky-plate", "splash-plate", "spooky-plate", "stone-plate", "toxic-plate", "zap-plate", # Memories "dragon-memory", "dark-memory", "ground-memory", "fighting-memory", "fire-memory", "ice-memory", "bug-memory", "steel-memory", "grass-memory", "psychic-memory", "fairy-memory", "flying-memory", "water-memory", "ghost-memory", "rock-memory", "poison-memory", "electric-memory", # Misc "primal-orb", "griseous-orb", "blue-orb", "red-orb", "rusty-sword", "rusty-shield", # Mega Stones "mega-stone", "mega-stone-x", "mega-stone-y", ) def is_berry(self, *, only_active=True): """ Returns a boolean indicating whether this held item is a berry. The optional param only_active determines if this method should only return True if the berry is active and usable. """ if only_active: return self.get() is not None and self.get().endswith("-berry") return self.name is not None and self.name.endswith("-berry") def remove(self): """Remove this held item, setting it to None.""" if not self.can_remove(): raise ValueError(f"{self.name} cannot be removed.") self.item = None def use(self): """Uses this item, setting it to None but also recording that it was used.""" if not self.can_remove(): raise ValueError(f"{self.name} cannot be removed.") self.last_used = self.item self.owner.choice_move = None self.remove() def transfer(self, other): """Transfer the data of this held item to other, and clear this item.""" if not self.can_remove(): raise ValueError(f"{self.name} cannot be removed.") if not other.can_remove(): raise ValueError(f"{other.name} cannot be removed.") other.item = self.item self.remove() def swap(self, other): """Swap the date between this held item and other.""" if not self.can_remove(): raise ValueError(f"{self.name} cannot be removed.") if not other.can_remove(): raise ValueError(f"{other.name} cannot be removed.") self.item, other.item = other.item, self.item self.owner.choice_move = None other.owner.choice_move = None self.ever_had_item = self.ever_had_item or self.item is not None def recover(self, other): """Recover & claim the last_used item from other.""" self.item = other.last_used other.last_used = None self.ever_had_item = self.ever_had_item or self.item is not None def _should_eat_berry_util(self, otherpoke=None): """Util for all the things that are shared between the different kinds of berry.""" if self.owner.hp == 0: return False if otherpoke is not None and otherpoke.ability() in (Ability.UNNERVE, Ability.AS_ONE_SHADOW, Ability.AS_ONE_ICE): #TODO: idk make this check better... return False if not self.is_berry(): return False return True def should_eat_berry_damage(self, otherpoke=None): """Returns True if the pokemon meets the criteria to eat its held berry after being damaged.""" if not self._should_eat_berry_util(otherpoke): return False if self.owner.hp <= self.owner.starting_hp / 4: if self in ( # HP berries "figy-berry", "wiki-berry", "mago-berry", "aguav-berry", "iapapa-berry", # Stat berries "apicot-berry", "ganlon-berry", "lansat-berry", "liechi-berry", "micle-berry", "petaya-berry", "salac-berry", "starf-berry", ): return True if self.owner.hp <= self.owner.starting_hp / 2: if self.owner.ability() == Ability.GLUTTONY: return True if self == "sitrus-berry": return True return False def should_eat_berry_status(self, otherpoke=None): """Returns True if the pokemon meets the criteria to eat its held berry after getting a status.""" if not self._should_eat_berry_util(otherpoke): return False if self in ("aspear-berry", "lum-berry") and self.owner.nv.freeze(): return True if self in ("cheri-berry", "lum-berry") and self.owner.nv.paralysis(): return True if self in ("chesto-berry", "lum-berry") and self.owner.nv.sleep(): return True if self in ("pecha-berry", "lum-berry") and self.owner.nv.poison(): return True if self in ("rawst-berry", "lum-berry") and self.owner.nv.burn(): return True if self in ("persim-berry", "lum-berry") and self.owner.confusion.active(): return True return False def should_eat_berry(self, otherpoke=None): """Returns True if the pokemon meets the criteria to eat its held berry.""" return self.should_eat_berry_damage(otherpoke) or self.should_eat_berry_status(otherpoke) def eat_berry(self, *, consumer=None, attacker=None, move=None): """ Eat this held item berry. Returns a formatted message. """ msg = "" if not self.is_berry(): return "" if consumer is None: consumer = self.owner else: msg += f"{consumer.name} eats {self.owner.name}'s berry!\n" # 2x or 1x ripe = int(consumer.ability(attacker=attacker, move=move) == Ability.RIPEN) + 1 flavor = None if self == "sitrus-berry": msg += consumer.heal((ripe * consumer.starting_hp) // 4, source="eating its berry") elif self == "figy-berry": msg += consumer.heal((ripe * consumer.starting_hp) // 3, source="eating its berry") flavor = "spicy" elif self == "wiki-berry": msg += consumer.heal((ripe * consumer.starting_hp) // 3, source="eating its berry") flavor = "dry" elif self == "mago-berry": msg += consumer.heal((ripe * consumer.starting_hp) // 3, source="eating its berry") flavor = "sweet" elif self == "aguav-berry": msg += consumer.heal((ripe * consumer.starting_hp) // 3, source="eating its berry") flavor = "bitter" elif self == "iapapa-berry": msg += consumer.heal((ripe * consumer.starting_hp) // 3, source="eating its berry") flavor = "sour" elif self == "apicot-berry": msg += consumer.append_spdef(ripe * 1, attacker=attacker, move=move, source="eating its berry") elif self == "ganlon-berry": msg += consumer.append_defense(ripe * 1, attacker=attacker, move=move, source="eating its berry") elif self == "lansat-berry": consumer.lansat_berry_ate = True msg += f"{consumer.name} is powered up by eating its berry.\n" elif self == "liechi-berry": msg += consumer.append_attack(ripe * 1, attacker=attacker, move=move, source="eating its berry") elif self == "micle-berry": consumer.micle_berry_ate = True msg += f"{consumer.name} is powered up by eating its berry.\n" elif self == "petaya-berry": msg += consumer.append_spatk(ripe * 1, attacker=attacker, move=move, source="eating its berry") elif self == "salac-berry": msg += consumer.append_speed(ripe * 1, attacker=attacker, move=move, source="eating its berry") elif self == "starf-berry": funcs = [ consumer.append_attack, consumer.append_defense, consumer.append_spatk, consumer.append_spdef, consumer.append_speed, ] func = random.choice(funcs) msg += func(ripe * 2, attacker=attacker, move=move, source="eating its berry") elif self == "aspear-berry": if consumer.nv.freeze(): consumer.nv.reset() msg += f"{consumer.name} is no longer frozen after eating its berry!\n" else: msg += f"{consumer.name}'s berry had no effect!\n" elif self == "cheri-berry": if consumer.nv.paralysis(): consumer.nv.reset() msg += f"{consumer.name} is no longer paralyzed after eating its berry!\n" else: msg += f"{consumer.name}'s berry had no effect!\n" elif self == "chesto-berry": if consumer.nv.sleep(): consumer.nv.reset() msg += f"{consumer.name} woke up after eating its berry!\n" else: msg += f"{consumer.name}'s berry had no effect!\n" elif self == "pecha-berry": if consumer.nv.poison(): consumer.nv.reset() msg += f"{consumer.name} is no longer poisoned after eating its berry!\n" else: msg += f"{consumer.name}'s berry had no effect!\n" elif self == "rawst-berry": if consumer.nv.burn(): consumer.nv.reset() msg += f"{consumer.name} is no longer burned after eating its berry!\n" else: msg += f"{consumer.name}'s berry had no effect!\n" elif self == "persim-berry": if consumer.confusion.active(): consumer.confusion.set_turns(0) msg += f"{consumer.name} is no longer confused after eating its berry!\n" else: msg += f"{consumer.name}'s berry had no effect!\n" elif self == "lum-berry": consumer.nv.reset() consumer.confusion.set_turns(0) msg += f"{consumer.name}'s statuses were cleared from eating its berry!\n" if flavor is not None and consumer.disliked_flavor == flavor: msg += consumer.confuse(attacker=attacker, move=move, source="disliking its berry's flavor") if consumer.ability(attacker=attacker, move=move) == Ability.CHEEK_POUCH: msg += consumer.heal(consumer.starting_hp // 3, source="its cheek pouch") consumer.last_berry = self.item consumer.ate_berry = True # TODO: right now HeldItem does not support `recover`ing/setting from anything other than another HeldItem object. # this should probably be modified to be an `ExpiringItem` w/ that item for cases where `last_item` gets reset. if consumer.ability(attacker=attacker, move=move) == Ability.CUD_CHEW: consumer.cud_chew.set_turns(2) if consumer is self.owner: self.use() else: self.remove() return msg def __eq__(self, other): return self.get() == other def __getattr__(self, attr): if attr not in ("name", "power", "id", "effect"): raise AttributeError(f"{attr} is not an attribute of {self.__class__.__name__}.") if self.item is None: return None if attr == "name": return self.item.name if attr == "power": return self.item.power if attr == "id": return self.item.id if attr == "effect": return self.item.effect raise AttributeError(f"{attr} is not an attribute of {self.__class__.__name__}.") class BatonPass(): """Stores the necessary data from a pokemon to baton pass to another pokemon.""" def __init__(self, poke): self.attack_stage = poke.attack_stage self.defense_stage = poke.defense_stage self.spatk_stage = poke.spatk_stage self.spdef_stage = poke.spdef_stage self.speed_stage = poke.speed_stage self.evasion_stage = poke.evasion_stage self.accuracy_stage = poke.accuracy_stage self.confusion = poke.confusion self.focus_energy = poke.focus_energy self.mind_reader = poke.mind_reader self.leech_seed = poke.leech_seed self.curse = poke.curse self.substitute = poke.substitute self.ingrain = poke.ingrain self.power_trick = poke.power_trick self.power_shift = poke.power_shift self.heal_block = poke.heal_block self.embargo = poke.embargo self.perish_song = poke.perish_song self.magnet_rise = poke.magnet_rise self.aqua_ring = poke.aqua_ring self.telekinesis = poke.telekinesis def apply(self, poke): """Push this objects data to a poke.""" if poke.ability() != Ability.CURIOUS_MEDICINE: poke.attack_stage = self.attack_stage poke.defense_stage = self.defense_stage poke.spatk_stage = self.spatk_stage poke.spdef_stage = self.spdef_stage poke.speed_stage = self.speed_stage poke.evasion_stage = self.evasion_stage poke.accuracy_stage = self.accuracy_stage poke.confusion = self.confusion poke.focus_energy = self.focus_energy poke.mind_reader = self.mind_reader poke.leech_seed = self.leech_seed poke.curse = self.curse poke.substitute = self.substitute poke.ingrain = self.ingrain poke.power_trick = self.power_trick poke.power_shift = self.power_shift poke.heal_block = self.heal_block poke.embargo = self.embargo poke.perish_song = self.perish_song poke.magnet_rise = self.magnet_rise poke.aqua_ring = self.aqua_ring poke.telekinesis = self.telekinesis