def start_volatile_status(battle, split_msg): if is_opponent(battle, split_msg): pkmn = battle.opponent.active else: pkmn = battle.user.active volatile_status = normalize_name(split_msg[3].split(":")[-1]) if volatile_status not in pkmn.volatile_statuses: logger.debug("Starting the volatile status {} on {}".format( volatile_status, pkmn.name)) pkmn.volatile_statuses.append(volatile_status) if volatile_status == constants.DYNAMAX: pkmn.hp *= 2 pkmn.max_hp *= 2 logger.debug("{} started dynamax - doubling their HP to {}/{}".format( pkmn.name, pkmn.hp, pkmn.max_hp)) if constants.ABILITY in split_msg[3]: pkmn.ability = volatile_status if len(split_msg) == 6 and constants.ABILITY in normalize_name( split_msg[5]): pkmn.ability = normalize_name(split_msg[5].split('ability:')[-1]) if volatile_status == constants.TYPECHANGE: new_type = normalize_name(split_msg[4]) logger.debug("Setting {}'s type to {}".format(pkmn.name, new_type)) pkmn.types = [new_type]
def heal_or_damage(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent other_side = battle.user pkmn = battle.opponent.active # opponent hp is given as a percentage if constants.FNT in split_msg[3]: pkmn.hp = 0 else: new_hp_percentage = float(split_msg[3].split('/')[0]) / 100 pkmn.hp = pkmn.max_hp * new_hp_percentage else: side = battle.user other_side = battle.opponent pkmn = battle.user.active if constants.FNT in split_msg[3]: pkmn.hp = 0 else: pkmn.hp = float(split_msg[3].split('/')[0]) pkmn.max_hp = float(split_msg[3].split('/')[1].split()[0]) # increase the amount of turns toxic has been active if len(split_msg) == 5 and constants.TOXIC in split_msg[ 3] and '[from] psn' in split_msg[4]: side.side_conditions[constants.TOXIC_COUNT] += 1 if len(split_msg) == 6 and split_msg[4].startswith( '[from] item:') and other_side.name in split_msg[5]: item = normalize_name(split_msg[4].split('item:')[-1]) logger.debug("Setting {}'s item to: {}".format(other_side.active.name, item)) other_side.active.item = item # set the ability for the other side (the side not taking damage, '-damage' only) if len(split_msg) == 6 and split_msg[4].startswith( '[from] ability:' ) and other_side.name in split_msg[5] and split_msg[1] == '-damage': ability = normalize_name(split_msg[4].split('ability:')[-1]) logger.debug("Setting {}'s ability to: {}".format( other_side.active.name, ability)) other_side.active.ability = ability # set the ability of the side (the side being healed, '-heal' only) if len(split_msg) == 6 and constants.ABILITY in split_msg[ 4] and other_side.name in split_msg[5] and split_msg[1] == '-heal': ability = normalize_name(split_msg[4].split( constants.ABILITY)[-1].strip(": ")) logger.debug("Setting {}'s ability to: {}".format(pkmn.name, ability)) pkmn.ability = ability # give that pokemon an item if this string specifies one if len(split_msg ) == 5 and constants.ITEM in split_msg[4] and pkmn.item is not None: item = normalize_name(split_msg[4].split( constants.ITEM)[-1].strip(": ")) logger.debug("Setting {}'s item to: {}".format(pkmn.name, item)) pkmn.item = item
def get_move_information(m): try: return m.split('|')[2], all_move_json[normalize_name( m.split('|')[3])] except KeyError: logger.debug( "Unknown move {} - using standard 0 priority move".format( normalize_name(m.split('|')[3]))) return m.split('|')[2], {constants.PRIORITY: 0}
def set_ability(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent else: side = battle.user for msg in split_msg: if constants.ABILITY in normalize_name(msg): ability = normalize_name(msg.split(':')[-1]) logger.debug("Setting {}'s ability to {}".format(side.active.name, ability)) side.active.ability = ability
def get_pokemon_information(smogon_stats_url, pkmn_names=None): r = requests.get(smogon_stats_url) if r.status_code == 404: r = requests.get(get_smogon_stats_file_name(ntpath.basename(smogon_stats_url.replace('-0.txt', '')), month_delta=2)) infos = r.json()['data'] final_infos = {} for pkmn_name, pkmn_information in infos.items(): normalized_name = normalize_name(pkmn_name) # if `pkmn_names` is provided, only find data on pkmn in that list if pkmn_names and normalized_name not in pkmn_names: continue spreads = [] items = [] moves = [] abilities = [] total_count = pkmn_information['Raw count'] final_infos[normalized_name] = {} for spread, count in sorted(pkmn_information['Spreads'].items(), key=lambda x: x[1], reverse=True): percentage = round(100 * count / total_count, 2) if percentage > 0: nature, evs = [normalize_name(i) for i in spread.split(":")] evs = evs.replace("/", ",") for sp in spreads: if spreads_are_alike(sp, (nature, evs)): sp[2] += percentage break else: spreads.append([nature, evs, percentage]) for item, count in pkmn_information['Items'].items(): if count > 0: items.append((item, round(100*count / total_count, 2))) for move, count in pkmn_information['Moves'].items(): if count > 0 and move and move.lower() != "nothing": moves.append((move, round(100*count / total_count, 2))) for ability, count in pkmn_information['Abilities'].items(): if count > 0: abilities.append( (ability, round(100 * count / total_count, 2)) ) final_infos[normalized_name][SPREADS_STRING] = sorted(spreads, key=lambda x: x[2], reverse=True) final_infos[normalized_name][ITEM_STRING] = sorted(items, key=lambda x: x[1], reverse=True) final_infos[normalized_name][MOVES_STRING] = sorted(moves, key=lambda x: x[1], reverse=True) final_infos[normalized_name][ABILITY_STRING] = sorted(abilities, key=lambda x: x[1], reverse=True) return final_infos
def single_pokemon_export_to_dict(pkmn_export_string): def get_species(s): if '(' in s and ')' in s: species = s[s.find("(") + 1:s.find(")")] return species return None pkmn_dict = { "name": "", "species": "", "level": "", "gender": "", "item": "", "ability": "", "moves": [], "nature": "", "evs": { "hp": "", "atk": "", "def": "", "spa": "", "spd": "", "spe": "", }, } pkmn_info = pkmn_export_string.split('\n') name = pkmn_info[0].split('@')[0] if "(M)" in name: pkmn_dict["gender"] = "M" name = name.replace('(M)', '') if "(F)" in name: pkmn_dict["gender"] = "F" name = name.replace('(F)', '') species = get_species(name) if species: pkmn_dict["species"] = normalize_name(species) pkmn_dict["name"] = name.split('(')[0].strip() else: pkmn_dict["name"] = normalize_name(name.strip()) if '@' in pkmn_info[0]: pkmn_dict["item"] = normalize_name(pkmn_info[0].split('@')[1]) for line in map(str.strip, pkmn_info[1:]): if line.startswith('Ability: '): pkmn_dict["ability"] = normalize_name(line.split('Ability: ')[-1]) elif line.startswith('Level: '): pkmn_dict["level"] = normalize_name(line.split('Level: ')[-1]) elif line.startswith('EVs: '): evs = line.split('EVs: ')[-1] for ev in evs.split('/'): ev = ev.strip() amount = normalize_name(ev.split(' ')[0]) stat = normalize_name(ev.split(' ')[1]) pkmn_dict['evs'][stat] = amount elif line.endswith('Nature'): pkmn_dict["nature"] = normalize_name(line.split('Nature')[0]) elif line.startswith('-'): pkmn_dict["moves"].append(normalize_name(line[1:])) return pkmn_dict
def weather(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent else: side = battle.user weather_name = normalize_name(split_msg[2].split(':')[-1].strip()) logger.debug("Weather {} started".format(weather_name)) battle.weather = weather_name if len(split_msg) >= 5 and side.name in split_msg[4]: ability = normalize_name(split_msg[3].split(':')[-1].strip()) logger.debug("Setting {} ability to {}".format(side.active.name, ability)) side.active.ability = ability
def get_damage_dealt(battle, split_msg, next_messages): move_name = normalize_name(split_msg[3]) critical_hit = False if is_opponent(battle, split_msg): attacking_side = battle.opponent defending_side = battle.user else: attacking_side = battle.user defending_side = battle.opponent for line in next_messages: next_line_split = line.split('|') # if one of these strings appears in index 1 then # exit out since we are done with this pokemon's move if len(next_line_split) < 2 or next_line_split[1] in MOVE_END_STRINGS: break elif next_line_split[1] == '-crit': critical_hit = True # if '-damage' appears, we want to parse the percentage damage dealt elif next_line_split[1] == '-damage' and defending_side.name in next_line_split[2]: final_health, maxhp, _ = get_pokemon_info_from_condition(next_line_split[3]) # maxhp can be 0 if the targetted pokemon fainted # the message would be: "0 fnt" if maxhp == 0: maxhp = defending_side.active.max_hp damage_dealt = (defending_side.active.hp / defending_side.active.max_hp)*maxhp - final_health damage_percentage = round(damage_dealt / maxhp, 4) logger.debug("{} did {}% damage to {} with {}".format(attacking_side.active.name, damage_percentage * 100, defending_side.active.name, move_name)) return DamageDealt(attacker=attacking_side.active.name, defender=defending_side.active.name, move=move_name, percent_damage=damage_percentage, crit=critical_hit)
def can_have_speed_modified(battle, pokemon): return ((pokemon.item is None and "unburden" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]) or (battle.weather == constants.RAIN and pokemon.ability is None and "swiftswim" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]) or (battle.weather == constants.SUN and pokemon.ability is None and "chlorophyll" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]) or (battle.weather == constants.SAND and pokemon.ability is None and "sandrush" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]) or (battle.weather == constants.HAIL and pokemon.ability is None and "slushrush" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]) or (battle.field == constants.ELECTRIC_TERRAIN and pokemon.ability is None and "surgesurfer" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]) or (pokemon.status == constants.PARALYZED and pokemon.ability is None and "quickfeet" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ]))
def set_opponent_ability_from_ability_tag(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent else: side = battle.user ability = normalize_name(split_msg[3]) logger.debug("Setting {}'s ability to {}".format(side.active.name, ability)) side.active.ability = ability
def activate(battle, split_msg): if is_opponent(battle, split_msg): pkmn = battle.opponent.active else: pkmn = battle.user.active if split_msg[3].lower() == 'move: poltergeist': item = normalize_name(split_msg[4]) logger.debug("{} has the item {}".format(pkmn.name, item)) pkmn.item = item
def start_volatile_status(battle, split_msg): if is_opponent(battle, split_msg): pkmn = battle.opponent.active side = battle.opponent else: pkmn = battle.user.active side = battle.user volatile_status = normalize_name(split_msg[3].split(":")[-1]) # for some reason futuresight is sent with the `-start` message # `-start` is typically reserved for volatile statuses if volatile_status == "futuresight": side.future_sight = (3, pkmn.name) return if volatile_status not in pkmn.volatile_statuses: logger.debug("Starting the volatile status {} on {}".format( volatile_status, pkmn.name)) pkmn.volatile_statuses.append(volatile_status) if volatile_status == constants.DYNAMAX: pkmn.hp *= 2 pkmn.max_hp *= 2 logger.debug("{} started dynamax - doubling their HP to {}/{}".format( pkmn.name, pkmn.hp, pkmn.max_hp)) if constants.ABILITY in split_msg[3]: pkmn.ability = volatile_status if len(split_msg) == 6 and constants.ABILITY in normalize_name( split_msg[5]): pkmn.ability = normalize_name(split_msg[5].split('ability:')[-1]) if volatile_status == constants.TYPECHANGE: if split_msg[4] == "[from] move: Reflect Type": pkmn_name = normalize_name(split_msg[5].split(":")[-1]) new_types = deepcopy(pokedex[pkmn_name][constants.TYPES]) else: new_types = [normalize_name(t) for t in split_msg[4].split("/")] logger.debug("Setting {}'s types to {}".format(pkmn.name, new_types)) pkmn.types = new_types
def set_item(battle, split_msg): """Set the opponent's item""" if is_opponent(battle, split_msg): side = battle.opponent else: side = battle.user item = normalize_name(split_msg[3].strip()) logger.debug("Setting {}'s item to {}".format(side.active.name, item)) side.active.item = item
def check_choicescarf(battle, msg_lines): def get_move_information(m): try: return m.split('|')[2], all_move_json[normalize_name( m.split('|')[3])] except KeyError: logger.debug( "Unknown move {} - using standard 0 priority move".format( normalize_name(m.split('|')[3]))) return m.split('|')[2], {constants.PRIORITY: 0} if (battle.opponent.active is None or battle.opponent.active.item != constants.UNKNOWN_ITEM or 'prankster' in [ normalize_name(a) for a in pokedex[battle.opponent.active.name] [constants.ABILITIES].values() ]): return moves = [ get_move_information(m) for m in msg_lines if m.startswith('|move|') ] if len(moves) != 2 or moves[0][0].startswith( battle.user.name ) or moves[0][1][constants.PRIORITY] != moves[1][1][constants.PRIORITY]: return battle_copy = deepcopy(battle) battle_copy.user.from_json(battle_copy.request_json) if battle.battle_type == constants.RANDOM_BATTLE: battle_copy.opponent.active.set_spread( 'serious', '85,85,85,85,85,85') # random battles have known spreads else: if battle.trick_room: battle_copy.opponent.active.set_spread( 'quiet', '0,0,0,0,0,0') # assume as slow as possible in trickroom else: battle_copy.opponent.active.set_spread( 'jolly', '0,0,0,0,0,252') # assume as fast as possible state = battle_copy.create_state() opponent_effective_speed = get_effective_speed(state, state.opponent) bot_effective_speed = get_effective_speed(state, state.self) if battle.trick_room: has_scarf = opponent_effective_speed > bot_effective_speed else: has_scarf = bot_effective_speed > opponent_effective_speed if has_scarf: logger.debug( "Opponent {} could not have gone first - setting it's item to choicescarf" .format(battle.opponent.active.name)) battle.opponent.active.item = 'choicescarf'
def curestatus(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent else: side = battle.user pkmn_name = split_msg[2].split(':')[-1].strip() if normalize_name(pkmn_name) == side.active.name: pkmn = side.active else: try: pkmn = next(filter(lambda x: x.name == normalize_name(pkmn_name), side.reserve)) except StopIteration: logger.warning( "The pokemon {} does not exist in the party, defaulting to the active pokemon".format(normalize_name(pkmn_name)) ) pkmn = side.active pkmn.status = None
def __init__(self, name): name = normalize_name(name) if constants.HIDDEN_POWER in name and not name.endswith(constants.HIDDEN_POWER_ACTIVE_MOVE_BASE_DAMAGE_STRING): name = "{}{}".format(name, constants.HIDDEN_POWER_ACTIVE_MOVE_BASE_DAMAGE_STRING) move_json = all_move_json[name] self.name = name self.max_pp = int(move_json.get(constants.PP) * 1.6) self.disabled = False self.can_z = False self.current_pp = self.max_pp
def singleturn(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent else: side = battle.user move_name = normalize_name(split_msg[3].split(':')[-1]) if move_name in constants.PROTECT_VOLATILE_STATUSES: # set to 2 because the `upkeep` function will decrement by 1 on every end-of-turn side.side_conditions[constants.PROTECT] = 2 logger.debug("{} used protect".format(side.active.name))
def sideend(battle, split_msg): """Remove a side effect such as stealth rock or sticky web""" condition = split_msg[3].split(':')[-1].strip() condition = normalize_name(condition) if is_opponent(battle, split_msg): logger.debug("Side condition {} ending for opponent".format(condition)) battle.opponent.side_conditions[condition] = 0 else: logger.debug("Side condition {} ending for bot".format(condition)) battle.user.side_conditions[condition] = 0
def fieldend(battle, split_msg): """Remove the battle's field condition""" field_name = normalize_name(split_msg[2].split(':')[-1].strip()) # trick room shows up as a `-fieldend` item but is separate from the other fields if field_name == constants.TRICK_ROOM: logger.debug("Removing trick room") battle.trick_room = False else: logger.debug("Setting the field to None") battle.field = None
def fieldstart(battle, split_msg): """Set the battle's field condition""" field_name = normalize_name(split_msg[2].split(':')[-1].strip()) # trick room shows up as a `-fieldstart` item but is separate from the other fields if field_name == constants.TRICK_ROOM: logger.debug("Setting trickroom") battle.trick_room = True else: logger.debug("Setting the field to {}".format(field_name)) battle.field = field_name
def prepare(battle, split_msg): if is_opponent(battle, split_msg): pkmn = battle.opponent.active else: pkmn = battle.user.active being_prepared = normalize_name(split_msg[3]) if being_prepared in pkmn.volatile_statuses: logger.warning("{} already has the volatile status {}".format( pkmn.name, being_prepared)) else: pkmn.volatile_statuses.append(being_prepared)
def status(battle, split_msg): if is_opponent(battle, split_msg): pkmn = battle.opponent.active else: pkmn = battle.user.active if len(split_msg) > 4 and 'item: ' in split_msg[4]: pkmn.item = normalize_name(split_msg[4].split('item:')[-1]) status_name = split_msg[3].strip() logger.debug("{} got status: {}".format(pkmn.name, status_name)) pkmn.status = status_name
def __init__(self, name: str, level: int, nature="serious", evs=(85, ) * 6): self.name = normalize_name(name) self.base_name = self.name self.level = level self.nature = nature self.evs = evs self.speed_range = StatRange(min=0, max=float("inf")) try: self.base_stats = pokedex[self.name][constants.BASESTATS] except KeyError: logger.info("Could not pokedex entry for {}".format(self.name)) self.name = [k for k in pokedex if self.name.startswith(k)][0] logger.info("Using {} instead".format(self.name)) self.base_stats = pokedex[self.name][constants.BASESTATS] self.stats = calculate_stats(self.base_stats, self.level, nature=nature, evs=evs) self.max_hp = self.stats.pop(constants.HITPOINTS) self.hp = self.max_hp if self.name == 'shedinja': self.max_hp = 1 self.hp = 1 self.ability = None self.types = pokedex[self.name][constants.TYPES] self.item = constants.UNKNOWN_ITEM self.fainted = False self.moves = [] self.status = None self.volatile_statuses = [] self.boosts = defaultdict(lambda: 0) self.can_mega_evo = False self.can_ultra_burst = False self.can_dynamax = False self.is_mega = False self.can_have_assaultvest = True self.can_have_choice_item = True self.can_not_have_band = False self.can_not_have_specs = False self.can_have_life_orb = True self.can_have_heavydutyboots = True
def end_volatile_status(battle, split_msg): if is_opponent(battle, split_msg): pkmn = battle.opponent.active else: pkmn = battle.user.active volatile_status = normalize_name(split_msg[3].split(":")[-1]) if volatile_status not in pkmn.volatile_statuses: logger.warning("Pokemon '{}' does not have the volatile status '{}'".format(pkmn.to_dict(), volatile_status)) else: logger.debug("Removing the volatile status {} from {}".format(volatile_status, pkmn.name)) pkmn.volatile_statuses.remove(volatile_status) if volatile_status == constants.DYNAMAX: pkmn.hp /= 2 pkmn.max_hp /= 2 logger.debug("{} ended dynamax - halving their HP to {}/{}".format(pkmn.name, pkmn.hp, pkmn.max_hp))
def move(battle, split_msg): if '[from]' in split_msg[-1]: return move_name = normalize_name(split_msg[3].strip().lower()) if is_opponent(battle, split_msg): side = battle.opponent pkmn = battle.opponent.active else: side = battle.user pkmn = battle.user.active # add the move to it's moves if it hasn't been seen # decrement the PP by one # if the move is unknown, do nothing move_object = pkmn.get_move(move_name) if move_object is None: new_move = pkmn.add_move(move_name) if new_move is not None: new_move.current_pp -= 1 else: move_object.current_pp -= 1 logger.debug( "{} already has the move {}. Decrementing the PP by 1".format( pkmn.name, move_name)) # if this pokemon used two different moves without switching, # set a flag to signify that it cannot have a choice item if (is_opponent(battle, split_msg) and side.last_used_move.pokemon_name == side.active.name and side.last_used_move.move != move_name): logger.debug( "{} used two different moves - it cannot have a choice item". format(pkmn.name)) pkmn.can_have_choice_item = False if pkmn.item in constants.CHOICE_ITEMS: logger.warning( "{} has a choice item, but used two different moves - setting it's item to UNKNOWN" .format(pkmn.name)) pkmn.item = constants.UNKNOWN_ITEM # if the opponent uses a boosting status move, they cannot have a choice item # this COULD be set for any status move, but some pkmn uncommonly run things like specs + wisp try: if constants.BOOSTS in all_move_json[move_name] and all_move_json[ move_name][constants.CATEGORY] == constants.STATUS: logger.debug( "{} used a boosting status-move. Setting can_have_choice_item to False" .format(pkmn.name)) pkmn.can_have_choice_item = False except KeyError: pass try: if all_move_json[move_name][constants.CATEGORY] == constants.STATUS: logger.debug( "{} used a status-move. Setting can_have_assultvest to False". format(pkmn.name)) pkmn.can_have_assaultvest = False except KeyError: pass try: category = all_move_json[move_name][constants.CATEGORY] logger.debug("Setting {}'s last used move: {}".format( pkmn.name, move_name)) side.last_used_move = LastUsedMove(pokemon_name=pkmn.name, move=move_name, turn=battle.turn) except KeyError: category = None side.last_used_move = LastUsedMove(pokemon_name=pkmn.name, move=constants.DO_NOTHING_MOVE, turn=battle.turn) # if this pokemon used a damaging move, eliminate the possibility of it having a lifeorb # the lifeorb will reveal itself if it has it if category in constants.DAMAGING_CATEGORIES and not any([ normalize_name(a) in ['sheerforce', 'magicguard'] for a in pokedex[pkmn.name][constants.ABILITIES].values() ]): logger.debug( "{} used a damaging move - not guessing lifeorb anymore".format( pkmn.name)) pkmn.can_have_life_orb = False # there is nothing special in the protocol for "wish" - it must be extracted here if move_name == constants.WISH and 'still' not in split_msg[4]: logger.debug( "{} used wish - expecting {} health of recovery next turn".format( side.active.name, side.active.max_hp / 2)) side.wish = (2, side.active.max_hp / 2)
def get_move(self, move_name: str): for m in self.moves: if m.name == normalize_name(move_name): return m return None
def get_pokemon_information(smogon_stats_url): """Parses a Smogon stats document, such as: 'https://www.smogon.com/stats/2019-02/moveset/gen7ou-1825.txt' Returns a dictionary containing the most likely spreads, items, and moves for each pokemon in order of likelihood """ r = requests.get(smogon_stats_url) if r.status_code == 404: r = requests.get( get_smogon_stats_file_name(ntpath.basename( smogon_stats_url.replace('-0.txt', '')), month_delta=2)) split_string = str(r.content).split(NEW_PKMN_INDICATOR) pokemon_information = dict() for pokemon_data in split_string: segments = pokemon_data.split('|') it = iter(segments) pokemon_name = normalize_name(segments[1]) pokemon_information[pokemon_name] = dict() pokemon_information[pokemon_name][SPREADS_STRING] = list() pokemon_information[pokemon_name][ITEM_STRING] = list() pokemon_information[pokemon_name][MOVES_STRING] = list() pokemon_information[pokemon_name][ABILITY_STRING] = list() for segment in it: if normalize_name(segment) == SPREADS_STRING: while SECTION_END_STRING not in segment: segment = next(it) if ':' in segment: split_segment = segment.split() spread = split_segment[0] nature = normalize_name(spread.split(':')[0]) evs = spread.split(':')[1].replace('/', ',') percentage = float( re.search(PERCENTAGES_REGEX, segment).group()[:-1]) pokemon_information[pokemon_name][ SPREADS_STRING].append((nature, evs, percentage)) elif normalize_name(segment) == ITEM_STRING: while SECTION_END_STRING not in segment: segment = next(it) if '%' in segment: item = normalize_name( re.sub(PERCENTAGES_REGEX, '', segment).strip()) percentage = float( re.search(PERCENTAGES_REGEX, segment).group()[:-1]) if item != OTHER_STRING: pokemon_information[pokemon_name][ ITEM_STRING].append((item, percentage)) elif normalize_name(segment) == MOVES_STRING: while SECTION_END_STRING not in segment: segment = next(it) if '%' in segment: move = normalize_name( re.sub(PERCENTAGES_REGEX, '', segment).strip()) percentage = float( re.search(PERCENTAGES_REGEX, segment).group()[:-1]) if move != OTHER_STRING: if constants.HIDDEN_POWER in move: move = "{}{}".format( move, constants. HIDDEN_POWER_ACTIVE_MOVE_BASE_DAMAGE_STRING ) pokemon_information[pokemon_name][ MOVES_STRING].append((move, percentage)) elif normalize_name(segment) == ABILITY_STRING: while SECTION_END_STRING not in segment: segment = next(it) if '%' in segment: ability = normalize_name( re.sub(PERCENTAGES_REGEX, '', segment).strip()) percentage = float( re.search(PERCENTAGES_REGEX, segment).group()[:-1]) if ability != OTHER_STRING: pokemon_information[pokemon_name][ ABILITY_STRING].append((ability, percentage)) return pokemon_information
def check_heavydutyboots(battle, msg_lines): side_to_check = battle.opponent if (side_to_check.active.item != constants.UNKNOWN_ITEM or 'magicguard' in [ normalize_name(a) for a in pokedex[side_to_check.active.name][ constants.ABILITIES].values() ]): return if side_to_check.side_conditions[constants.STEALTH_ROCK] > 0: pkmn_took_stealthrock_damage = False for line in msg_lines: split_line = line.split('|') # |-damage|p2a: Weedle|88/100|[from] Stealth Rock if (len(split_line) > 4 and split_line[1] == '-damage' and split_line[2].startswith(side_to_check.name) and split_line[4] == '[from] Stealth Rock'): pkmn_took_stealthrock_damage = True if not pkmn_took_stealthrock_damage: logger.debug("{} has heavydutyboots".format( side_to_check.active.name)) side_to_check.active.item = 'heavydutyboots' else: logger.debug( "{} was affected by stealthrock, it cannot have heavydutyboots" .format(side_to_check.active.name)) side_to_check.active.can_have_heavydutyboots = False elif (side_to_check.side_conditions[constants.SPIKES] > 0 and 'flying' not in side_to_check.active.types and side_to_check.active.ability != 'levitate'): pkmn_took_spikes_damage = False for line in msg_lines: split_line = line.split('|') # |-damage|p2a: Weedle|88/100|[from] Spikes if (len(split_line) > 4 and split_line[1] == '-damage' and split_line[2].startswith(side_to_check.name) and split_line[4] == '[from] Spikes'): pkmn_took_spikes_damage = True if not pkmn_took_spikes_damage: logger.debug("{} has heavydutyboots".format( side_to_check.active.name)) side_to_check.active.item = 'heavydutyboots' else: logger.debug( "{} was affected by spikes, it cannot have heavydutyboots". format(side_to_check.active.name)) side_to_check.active.can_have_heavydutyboots = False elif (side_to_check.side_conditions[constants.TOXIC_SPIKES] > 0 and 'flying' not in side_to_check.active.types and 'poison' not in side_to_check.active.types and 'steel' not in side_to_check.active.types and side_to_check.active.ability != 'levitate' and side_to_check.active.ability not in constants.IMMUNE_TO_POISON_ABILITIES): pkmn_took_toxicspikes_poison = False for line in msg_lines: split_line = line.split('|') # a pokemon can be toxic-ed from sources other than toxicspikes # stopping at one of these strings ensures those other sources aren't considered if len(split_line) < 2 or split_line[1] in MOVE_END_STRINGS: break # |-status|p2a: Pikachu|psn if (split_line[1] == '-status' and (split_line[3] == constants.POISON or split_line[3] == constants.TOXIC) and split_line[2].startswith(side_to_check.name)): pkmn_took_toxicspikes_poison = True if not pkmn_took_toxicspikes_poison: logger.debug("{} has heavydutyboots".format( side_to_check.active.name)) side_to_check.active.item = 'heavydutyboots' else: logger.debug( "{} was affected by toxicspikes, it cannot have heavydutyboots" .format(side_to_check.active.name)) side_to_check.active.can_have_heavydutyboots = False elif (side_to_check.side_conditions[constants.STICKY_WEB] > 0 and 'flying' not in side_to_check.active.types and side_to_check.active.ability != 'levitate'): pkmn_was_affected_by_stickyweb = False for line in msg_lines: split_line = line.split('|') # |-activate|p2a: Gengar|move: Sticky Web if (len(split_line) == 4 and split_line[1] == '-activate' and split_line[2].startswith(side_to_check.name) and split_line[3] == 'move: Sticky Web'): pkmn_was_affected_by_stickyweb = True if not pkmn_was_affected_by_stickyweb: logger.debug("{} has heavydutyboots".format( side_to_check.active.name)) side_to_check.active.item = 'heavydutyboots' else: logger.debug( "{} was affected by sticky web, it cannot have heavydutyboots". format(side_to_check.active.name)) side_to_check.active.can_have_heavydutyboots = False
def can_have_priority_modified(battle, pokemon, move_name): return ("prankster" in [ normalize_name(a) for a in pokedex[pokemon.name][constants.ABILITIES].values() ] or move_name == "grassyglide" and battle.field == constants.GRASSY_TERRAIN)
if len(split_lines) != 8: continue pkmn = split_lines[0] level = int(split_lines[1]) if pkmn in ['unown', 'ditto']: moves = [split_lines[2]] ability = split_lines[3] item = split_lines[4] else: moves = split_lines[2:6] ability = split_lines[6] item = split_lines[7] if item.endswith("ite") and item != "eviolite": pkmn = pkmn + "mega" ability = normalize_name(pokedex[pkmn]["most_likely_ability"]) elif item.endswith("itex"): pkmn = pkmn + "megax" ability = normalize_name(pokedex[pkmn]["most_likely_ability"]) elif item.endswith("itey"): pkmn = pkmn + "megay" ability = normalize_name(pokedex[pkmn]["most_likely_ability"]) identifier = "{}".format(pkmn) if identifier in all_pokemon_dict: all_pokemon_dict[identifier][constants.COUNT] += 1 else: all_pokemon_dict[identifier] = {constants.SETS: dict(), constants.MOVES: dict(), constants.ABILITIES: dict(), constants.ITEMS: dict(), constants.COUNT: 1} this_pkmn_dict = all_pokemon_dict[identifier] for i, m in enumerate(moves[:]):