Example #1
0
 def test_remove_the_only_status(self):
     poison = Status(5)
     poison.remove('poison')
     assert poison.id == np.array([0])
     assert poison.name == np.array(['normal'])
     assert poison.duration == np.array([float('inf')])
     assert poison.volatile == np.array([False])
Example #2
0
 def test_reduce_the_duration_by_1_where_the_duration_was_1(self):
     burn = Status('burn', 1)
     confused = Status('confused', 5)
     mixed = burn + confused
     mixed.reduce()
     assert mixed.name == ['confused']
     assert mixed.duration == [4]
     assert mixed.volatile == [True]
Example #3
0
    def test_trick_room_reverses_the_order(self):
        p1 = Pokemon('shuckle')
        p2 = Pokemon('deoxys-speed')

        p1_move, p2_move = Move('snatch'), Move('snatch')

        p1.status += Status('trick-room', 5)
        p2.status += Status('trick-room', 5)

        f1, m1, f2, m2 = attacking_order(p1, p1_move, p2, p2_move)

        assert p1 == f1
        assert p2 == f2
Example #4
0
def ailment_inflictor(f1, m1, f2, m2):
    """Inflicts ailment to the selected target."""

    ailment_id = m1.meta_ailment_id
    ailment_chance = 100. if np.isnan(m1.ailment_chance) else m1.ailment_chance
    lasting_turns = None if np.isnan(m1.min_turns) else randint(
        m1.min_turns, m1.max_turns + 1)
    ailment = Status(ailment_id, lasting_turns)

    if binomial(1, ailment_chance / 100.):
        if m1.target_id == 7:
            # Self-inflicted ailment
            f1.status += ailment
            if m1.effect == 38:
                # User sleeps for two turns, completely healing itself.
                # At the beginning of each round, ``is_mobile()``
                # should check if 'rest' is in ``f1.flags`` and if
                # 'sleep' is **not** in ``f1.status``. If both are
                # ``True``, then that means the user once slept and now
                # 'sleep' status has worn off.
                # Then restore all user's current hp. Heal the user and
                # remove the flag.

                f1.flags['rest'] = True

        elif m1.target_id == 14:
            # ailment inflicted to all pokemons.
            f1.status += ailment
            f2.status += ailment

        else:
            # ailment inflicted to the opponent
            f2.status += ailment
Example #5
0
    def test_ability_override_field_effects(self):
        p1 = Pokemon('shuckle')
        p2 = Pokemon('deoxys-speed')

        p1.ability = which_ability('stall')
        p2.ability = 1

        p1_move, p2_move = Move('snatch'), Move('snatch')

        p1.status += Status('trick-room')
        p2.status += Status('trick-room')

        f1, m1, f2, m2 = attacking_order(p1, p1_move, p2, p2_move)

        assert p1 == f2
        assert p2 == f1
Example #6
0
def calculate_damage(f1, m1, f2, m2):
    """Calculate the damage including the moves dealing direct damages
    and regular damages.
    """
    effect = m1.effect_id

    def immuned(damage):
        """A simple filter for damage that takes type-immunity into
        account.
        """
        return 0 if efficacy(m1.type, f2.types) == 0 else damage

    if effect == 27:
        # User waits for two turns.
        # On the second turn, the user inflicts twice the damage it
        # accumulated on the last Pokémon to hit it.  Damage inflicted
        # is [typeless]{mechanic:typeless}.
        #
        # This move cannot be selected by []{move:sleep-talk}.
        # XXX: group moves with `charge` flag into a new function.
        f1.flags += Status('bide', 2)
        return 0

    elif effect == 41:
        # Inflicts [typeless]{mechanic:typeless} damage equal to half
        # the target's remaining [HP]{mechanic:hp}.
        return f2.current.hp / 2.

    elif effect == 42:
        # Inflicts 40 points of damage.
        return immuned(40.)

    elif effect == 88:
        # Inflicts damage equal to the user's level.  Type immunity
        # applies, but other type effects are ignored.
        return immuned(f1.level)

    elif effect == 89:
        # Inflicts [typeless]{mechanic:typeless} damage between 50% and
        # 150% of the user's level, selected at random in increments of
        # 10%.
        return f1.level * randint(5, 15) / 10.

    elif effect == 90:
        # Targets the last opposing Pokémon to hit the user with a
        # physical move this turn.
        # Inflicts twice the damage that move did to the user.
        # If there is no eligible target, this move will fail.
        # Type immunity applies, but other type effects are ignored.

        if f1.order == 2:
            received_damage = f1.history.damage[0]
            if m2.damage_class_id == 2:
                return immuned(received_damage * 2)

        return 0

    elif effect == 131:
        # Inflicts exactly 20 damage.
        return immuned(20.)

    elif effect == 145:
        # Targets the last opposing Pokémon to hit the user with a
        # [special]{mechanic:special} move this turn.
        # Inflicts twice the damage that move did to the user.
        # If there is no eligible target, this move will
        # [fail]{mechanic:fail}.
        # Type immunity applies, but other type effects are ignored.

        if f1.order == 2:
            received_damage = f1.history.damage[0]
            if received_damage and m2.damage_class_id == 3:
                return immuned(received_damage * 2)

        return 0

    elif effect == 155:
        # Inflicts {mechanic:typeless} {mechanic:regular-damage}.
        # Every Pokémon in the user's party, excepting those that have
        # fainted or have a {mechanic:major-status-effect}, attacks the
        # target.
        # Calculated stats are ignored; the base stats for the target
        # and assorted attackers are used instead.
        # The random factor in the damage formula is not used.
        # []{type:dark} Pokémon still get [STAB]{mechanic:stab}.

        damage = 0
        for pokemon in f1.trainer.party():
            if pokemon.status.volatile.all():
                damage += regular_damage(pokemon, m1, f2, m2)
        return damage

    elif effect == 190:
        # Inflicts exactly enough damage to lower the target's
        # {mechanic:hp} to equal the user's.  If the target's HP is not
        # higher than the user's, this move has no effect.
        # Type immunity applies, but other type effects are ignored.
        # This effect counts as damage for moves that respond to damage.
        return immuned(
            np.clip(a=f2.current.hp - f1.current.hp,
                    a_min=0,
                    a_max=f2.current.hp))

    elif effect == 228:
        # Targets the last opposing Pokémon to hit the user with a
        # damaging move this turn.
        # Inflicts 1.5× the damage that move did to the user.
        # If there is no eligible target, this move will fail.
        # Type immunity applies, but other type effects are ignored.

        if f1.order == 2:
            received_damage = f1.history.damage[0]
            if m2.damage_class_id != 1:
                return immuned(received_damage * 1.5)

        return 0.

    elif effect == 321:
        # Inflicts damage equal to the user's remaining
        # [HP]{mechanic:hp}.  User faints.

        damage = f1.current.hp
        f1.current.hp = 0

        return damage

    else:
        # All cases up to Gen.5 should be covered.
        return regular_damage(f1, m1, f2, m2)
Example #7
0
def effect(f1, m1, f2, m2):
    """Activates m1's effect if it is a unique effect.

    Exceptions
    ----------
        Move id     | Effect id
       -------------|-----------
         18, 46     |   29
         54         |   47
         100        |   154
         113        |   36


    """

    effect = m1.effect_id
    if not np.isnan(m1.healing):
        # A positive heal cures the user; a negative heal damages
        # the user, based on the user's max hp.
        f1.current.hp += m1.healing * f1.stats.hp // 100.

    if not np.isnan(m1.flinch_chance) and f2.order == 2:  # oxymoron?
        # If the move makes the opponent flinch, then add `flinch`
        # to the opponent's status.
        if binomial(1, m1.flinch_chance / 100.):
            f2.status += Status('flinch', 1)

    if not str(m1.stat_change).isnumeric():
        # stat-changers
        stat_changer(f1, m1, f2, m2)

    if m1.meta_category_id in [1, 5]:
        # moves that inflicts status conditions.
        ailment_inflictor(f1, m1, f2, m2)

    if effect == 26:
        # Removes [stat]{mechanic:stat}, [accuracy]{mechanic:accuracy},
        # and [evasion]{mechanic:evasion} modifiers from every Pokémon
        # on the [field]{mechanic:field}.
        #
        # This does not count as a stat reduction for the purposes of
        # []{ability:clear-body} or []{ability:white-smoke}.
        for f in [f1, f2]:
            for stat in f.STAT_NAMES:
                f.current[stat] = f.stats[stat]
            for stat in ['accuracy', 'evasion']:
                f.current[stat] = 100.

    elif effect == 58:
        # User copies the target's species, weight, type,
        # [ability]{mechanic:ability}, [calculated stats]{mechanic:
        # calculated-stats} (except [HP]{mechanic:hp}), and moves.
        # Copied moves will all have 5 [PP]{mechanic:pp} remaining.
        # [IV]{mechanic:iv}s are copied for the purposes of []{move:
        # hidden-power}, but stats are not recalculated.
        #
        # []{item:choice-band}, []{item:choice-scarf}, and []{item:
        # choice-specs} stay in effect, and the user must select a new
        # move.
        #
        # This move cannot be copied by []{move:mirror-move}, nor forced
        # by []{move:encore}.
        pass  # XXX: passed.

    elif effect == 83:
        # This move is replaced by the target's last successfully used
        # move, and its PP changes to 5.  If the target hasn't used a
        # move since entering the field, if it tried to use a move this
        # turn and [failed]{mechanic:fail}, or if the user already knows
        # the targeted move, this move will fail.  This effect vanishes
        # when the user leaves the field.
        #
        # If []{move:chatter}, []{move:metronome}, []{move:mimic},
        # []{move:sketch}, or []{move:struggle} is selected, this move
        # will [fail]{mechanic:fail}.
        #
        # This move cannot be copied by []{move:mirror-move}, nor
        # selected by []{move:assist} or []{move:metronome}, nor forced
        # by []{move:encore}.
        if 'last-successfully-used-move' in f2.flags:
            m1 = Move(f2.flags['last-successfully-used-move'])
            m1.pp = 5

    elif effect == 84:
        # Selects any move at random and uses it.
        # Moves the user already knows are not eligible.
        # Assist, meta, protection, and reflection moves are also not
        # eligible; specifically, []{move:assist}, []{move:chatter},
        # []{move:copycat}, []{move:counter}, []{move:covet},
        # []{move:destiny-bond}, []{move:detect}, []{move:endure},
        # []{move:feint}, []{move:focus-punch}, []{move:follow-me},
        # []{move:helping-hand}, []{move:me-first}, []{move:metronome},
        # []]{move:mimic}, []{move:mirror-coat}, []{move:mirror-move},
        # []{move:protect}, []{move:quick-guard}, []{move:sketch},
        # []{move:sleep-talk}, []{move:snatch}, []{move:struggle},
        # []{move:switcheroo}, []{move:thief}, []{move:trick}, and
        # []{move:wide-guard} will not be selected by this move.
        #
        # This move cannot be copied by []{move:mimic} or
        # []{move:mirror-move}, nor selected by []{move:assist},
        # []{move:metronome}, or []{move:sleep-talk}.

        ineligible_moves = deque([x.name for x in f1.moves])
        ineligible_moves.extend([
            'assist', 'chatter', 'copycat', 'counter', 'covet', 'destiny-bond',
            'detect', 'endure', 'feint', 'focus-punch', 'follow-me',
            'helping-hand', 'me-first', 'metronome', 'mimic', 'mirror-coat',
            'mirror-move', 'protect', 'quick-guard', 'sketch', 'sleep-talk',
            'snatch', 'struggle', 'switcheroo', 'thief', 'trick', 'wide-guard'
        ])

        all_moves = deque([x for x in tb.moves.identifier.values])
        eligible_moves = list(set(all_moves) - set(ineligible_moves))
        m1 = np.random.choice(eligible_moves)

    elif effect == 95:
        # If the user targets the same target again before the end of
        # the next turn, the move it uses is guaranteed to hit.
        # This move itself also ignores [accuracy]{mechanic:accuracy}
        # and [evasion]{mechanic:evasion} modifiers.
        #
        # One-hit KO moves are also guaranteed to hit, as long as the
        # user is equal or higher level than the target.  This effect
        # also allows the user to hit Pokémon that are off the field
        # due to moves such as []{move:dig} or []{move:fly}.
        #
        # If the target uses []{move:detect} or []{move:protect} while
        # under the effect of this move, the user is not guaranteed to
        # hit, but has a (100 - accuracy)% chance to break through the
        # protection.
        #
        # This effect is passed on by []{move:baton-pass}.

        # XXX: finish its counterpart in ``makes_hit``
        f2.status += Status('taking-aim', 2)

    elif effect == 101:
        # Lowers the PP of the target's last used move by 4.
        # If the target hasn't used a move since entering the [field]
        # {mechanic:field}, if it tried to use a move this turn and
        # [failed]{mechanic:failed}, or if its last used move has 0 PP
        # remaining, this move will fail.

        move_names = [x.name for x in f2.moves]
        try:
            last_move = f2.flags['last-successfully-used-move']
            index = np.where(move_names == last_move)[0][0]
            f2.moves[index].pp -= 4
        except KeyError:
            pass

    elif effect == 112:
        pass
Example #8
0
def setUpStatus():
    poison = Status(5)
    burn = Status('burn')
    confused = Status('confused', 5)
    disabled = Status('disabled', 4)
    yield poison, burn, confused, disabled
Example #9
0
 def test_status_volatility(self):
     assert Status(20).volatile[0] == True
     assert Status(0).volatile[0] == False
Example #10
0
 def test_declare_a_custom_status(self):
     trick_room = Status('trick-room', 5)
     assert trick_room.id[0] >= 100000
     assert trick_room.duration[0] == 5
     assert trick_room.volatile[0] == True
Example #11
0
 def test_declare_a_status_by_name_from_the_table(self):
     assert Status('poison').id[0] == 5
Example #12
0
 def test_declare_a_status_with_a_timer(self):
     assert Status(5, 5).duration[0] == 5
Example #13
0
 def test_declare_a_status_by_id_from_the_table(self):
     assert Status(5).name[0] == 'poison'
Example #14
0
 def test_remove_a_non_existing_status(self):
     poison = Status(5)
     with pytest.raises(KeyError):
         poison.remove('burn')