def on_switch_in(self, pokemon, battle): if pokemon.is_fainted(): if __debug__: log.w('%s fainted before HealingWish could heal it') return battle.heal(pokemon, pokemon.max_hp) pokemon.cure_status() pokemon.side.remove_effect(SideCondition.HEALINGWISH)
def describe_possible_foe_moves(self, my_active, foe): if len(foe.moves) >= 4 or foe.base_species == 'ditto': return '' foe_index = rbstats_key(foe) for move in foe.moves: if (move.name not in rbstats.probability[foe_index]['moves'] and move.type != Type.NOTYPE): log.w('%s not in rbstats for %s: Stale showdown data?', move.name, foe) possible_moves = [movedex[move] for move in rbstats[foe_index]['moves'] if movedex[move] not in foe.moves] data = [] known_attrs = [move_.name for move_ in foe.moves if move_.type != Type.NOTYPE] if not rbstats.possible_sets(foe_index, known_attrs): log.w("%s's move probabilities cannot be calculated: unexpected attribute in %s", foe_index, known_attrs) calculate_prob = False else: calculate_prob = True for move in possible_moves[:]: if calculate_prob: prob = rbstats.attr_probability(foe_index, move.name, known_attrs) if not prob: possible_moves.remove(move) continue else: prob = 0.5 dmg_range = self.calculate_damage_range(foe, move, my_active) data.append((move.name, prob, dmg_range)) return sorted(data, key=lambda x: -x[1])
def describe_my_moves(self, my_active, foe): """ :type my_active BattlePokemon """ if not my_active.is_transformed: active = rbstats_key(my_active) for move in my_active.moves: if move.name not in rbstats.probability[active]['moves']: log.w('%s not in rbstats for %s: Stale showdown data?', move.name, my_active) return [(move.name, self.calculate_damage_range(my_active, move, foe)) for move in my_active.moves]
def damage(self, pokemon, damage, cause, source=None, attacker=None, drain_pct=None): """ Return FAIL or int amount of damage done. If the damage is caused directly by a move then source and attacker must be set. Draining effects (drain moves or leechseed) pass a percent of damage to drain to attacker. """ if pokemon.is_fainted(): if __debug__: log.w('Tried to damage fainted pokemon %s: cause: %s, source: %s, attacker: %s', pokemon, cause, source, attacker) return 0 assert pokemon is not attacker assert pokemon.side.active_pokemon is pokemon assert pokemon.is_active assert damage >= 0 assert ((isinstance(attacker, BattlePokemon) and isinstance(source, Move)) if cause is Cause.MOVE else True) if damage == 0: if __debug__: log.w('Battle.damage called with damage=0') # this shouldn't happen return 0 if cause is Cause.WEATHER and pokemon.is_immune_to(source): if __debug__: log.i('Weather immunity: %s / %s', pokemon, source) return 0 if damage < 1: damage = 1 # always do at least 1 damage else: damage = int(damage) damage = pokemon.accumulate_effect('on_damage', pokemon, cause, source, self, damage, failfast=True) if damage is FAIL: return FAIL pokemon.hp -= damage if __debug__: log.i('%s took %s (%.1f%%) damage from %s: %s; hp=%d/%d' % (pokemon, damage, 100*float(damage)/pokemon.max_hp, cause, source, pokemon.hp, pokemon.max_hp)) if pokemon.hp <= 0: damage += pokemon.hp if drain_pct and not attacker.is_fainted(): self.heal(attacker, int(math.ceil(damage * drain_pct / 100.0)), cause=Cause.DRAIN, foe=pokemon) if cause is Cause.MOVE: pokemon.activate_effect('on_after_move_damage', self, pokemon, damage, source, attacker) if pokemon.hp <= 0: self.faint(pokemon, cause, source, attacker) return damage
def resolve_faint_queue(self): while self.faint_queue: pokemon = self.faint_queue.pop() assert pokemon.is_fainted() and pokemon.status is Status.FNT if pokemon.side.remaining_pokemon_on_bench == 0 and self.battlefield.win is None: self.battlefield.win = int(not pokemon.side.index) if __debug__: log.i('Side %d wins!', self.battlefield.win) if __debug__: if pokemon.effects: log.w('Post-fainted pokemon has effects: %r', pokemon)
def attr_probability(self, pokemon, attr, known_attrs): """ Return the probability [0.0, 1.0] that pokemon has attr, given that it has [known_attrs]. pokemon: str, attr: str, known_attrs: list<str> """ attrs_counter = self[pokemon]['sets'] possible = self.possible_sets(pokemon, known_attrs) if not possible: if __debug__: log.w("%s's known_attrs %s does not correspond to any known attrset in rbstats. " "Cannot calculate move probabilities; returning 0.5", pokemon, known_attrs) return 0.5 target_attrs = [attrset for attrset in possible if attr in attrset] probability = (float(sum(attrs_counter[attrset] for attrset in target_attrs)) / sum(attrs_counter[attrset] for attrset in possible)) return probability
def faint(self, pokemon, cause, source=None, attacker=None): if pokemon.status is Status.FNT: if __debug__: log.w('Tried to faint %s twice!', pokemon) return pokemon.hp = 0 pokemon.status = Status.FNT # This should be the only way to assign Status.FNT pokemon.side.last_fainted_on_turn = self.battlefield.turns pokemon.side.active_pokemon = None pokemon.is_active = False if __debug__: log.i('%s fainted: %s (source=%s)', pokemon, cause, source) self.faint_queue.insert(0, pokemon) pokemon.activate_effect('on_faint', pokemon, cause, source, self) if attacker is not None and not attacker.is_fainted(): attacker.activate_effect('on_foe_faint', attacker, cause, source, pokemon, self) foe = self.get_foe(pokemon) if foe is not None: foe.remove_trap_effects() pokemon.clear_effects(self) pokemon.boosts = Boosts()