def remove_minion(self, minion: Minion, enemy_board):
        """Remove minion from board

        Arguments:
            minion {Minion} -- Minion to be removed from board
        """
        self.killed_minions += 1

        position = minion.position
        neighbors = self.get_minions_neighbors(minion)
        if neighbors[0]:
            neighbors[0].right_neighbor = neighbors[1]
        if neighbors[1]:
            neighbors[1].left_neighbor = neighbors[0]

        self.minions.pop(position)
        for other_minion in self.minions[position:]:
            other_minion.shift_left()

        if len(self.dead_friendly_mechs
               ) < 4 and MinionType.Mech in minion.types:
            self.dead_friendly_mechs.append(minion)

        minion.on_self_removal(self)
        self.remove_minion_triggers(minion)
        self.call_triggers(TriggerType.ON_MINION_REMOVAL, minion, self,
                           enemy_board)
Beispiel #2
0
 def trigger_on_friendly_before_attack(self, attacking_minion: Minion,
                                       own_board: PlayerBoard,
                                       defending_minion: Minion,
                                       defending_board: PlayerBoard):
     if MinionType.Pirate in attacking_minion.types and attacking_minion is not self:
         stats = 4 if self.golden else 2
         attacking_minion.add_stats(stats, stats)
Beispiel #3
0
    def add_minion(self, new_minion: Minion, position: Optional[int] = None) -> Optional[Minion]:
        """Add minion to the board if there is space

        Arguments:
            new_minion {Minion} -- Instance of minion to be added

        Keyword Arguments:
            position {Optional[int]} -- Optional position to insert the minion at, if None add at the end (default: {None})

        Returns:
            Optional[Minion] -- Return the updated Minion if inserted succesfully, otherwise return None
        """
        if len(self.minions) < 7:
            if position is None:
                position = len(self.minions)
            for minion in self.minions:
                minion.on_other_enter(other_minion=new_minion)
            new_minion.position = position
            new_minion.player_id = self.player_id
            self.minions.insert(position, new_minion)
            for minion in self.minions[position + 1:]:
                minion.shift_right()
            logging.debug(f"Adding {new_minion.minion_string()}")
            return new_minion
        else:
            logging.debug(f"Did not add {new_minion.minion_string()} because of a lack of space")
Beispiel #4
0
 def trigger_on_friendly_before_attack(self, attacking_minion: Minion,
                                       own_board: PlayerBoard,
                                       defending_minion: Minion,
                                       defending_board: PlayerBoard):
     if MinionType.Dragon in attacking_minion.types and attacking_minion is not self:
         defending_minion.receive_damage(amount=self.bonus_damage,
                                         own_board=defending_board)
Beispiel #5
0
    def remove_minion(self, minion: Minion):
        """Remove minion from board

        Arguments:
            minion {Minion} -- Minion to be removed from board
        """
        logging.debug(f"Removing {minion.minion_string()}")
        position = minion.position
        self.minions.pop(position)
        for minion in self.minions[position:]:
            minion.shift_left()
Beispiel #6
0
    def attack(self, attacking_minion: Minion, defending_minion: Minion):
        """Let one minion attack the other

        Arguments:
            attacking_minion {Minion} -- Minion that attacks
            defending_minion {Minion} -- Minion that is attacked
        """
        # TODO: Cleave
        current, other = self.attacking_player_board(), self.defending_player_board()
        logging.debug(f"{attacking_minion.minion_string()} attacks {defending_minion.minion_string()}")
        attacking_minion.on_attack()
        attacking_minion_attack, _ = attacking_minion.total_attack_and_defense(current, other)
        defending_minion_attack, _ = defending_minion.total_attack_and_defense(other, current)
        self.deal_damage(attacking_minion, current, defending_minion_attack, defending_minion.poisonous)
        self.deal_damage(defending_minion, other, attacking_minion_attack, attacking_minion.poisonous)
    def attack(self, attacking_minion: Minion, defending_minion: Minion,
               attacking_board: PlayerBoard, defending_board: PlayerBoard):
        """Let one minion attack the other

        Arguments:
            attacking_minion {Minion} -- Minion that attacks
            defending_minion {Minion} -- Minion that is attacked
        """
        # Pre-attack triggers
        attacking_board.call_triggers(TriggerType.ON_FRIENDLY_BEFORE_ATTACK,
                                      attacking_minion, attacking_board,
                                      defending_minion, defending_board)
        defending_board.call_triggers(TriggerType.ON_FRIENDLY_ATTACKED,
                                      defending_minion, defending_board,
                                      attacking_board)

        # If minion is killed by pyro spawn trigger, attack is cancelled
        if defending_minion.dead:
            attacking_minion.on_attack_after(attacking_board, defending_board)
            return

        # Deal attack damage
        if attacking_minion.cleave:
            # Minions hit with cleave should take damage all at once, but triggers resolve after
            neighbors = defending_board.get_minions_neighbors(defending_minion)
            defenders = [neighbors[0], defending_minion, neighbors[1]]
            for minion in [x for x in defenders if x]:
                self.deal_attack_damage(minion, defending_board,
                                        attacking_minion, attacking_board,
                                        True)
            self.deal_attack_damage(attacking_minion, attacking_board,
                                    defending_minion, defending_board)
        else:
            self.deal_attack_damage(defending_minion, defending_board,
                                    attacking_minion, attacking_board)
            self.deal_attack_damage(attacking_minion, attacking_board,
                                    defending_minion, defending_board)

        # Damage triggers from cleave and herald of flame should be resolved after attack and overkill trigger resolution
        for trigger in defending_board.deferred_damage_triggers:
            trigger(defending_board)
        defending_board.deferred_damage_triggers.clear()

        # Post attack triggers (macaw)
        attacking_minion.on_attack_after(attacking_board, defending_board)
    def add_minion(
            self,
            new_minion: Minion,
            position: Optional[int] = None,
            allow_copy: Optional[bool] = True,
            summoning_minion: Optional[Minion] = None) -> Optional[Minion]:
        """Add minion to the board if there is space

        Arguments:
            new_minion {Minion} -- Instance of minion to be added
            token {Bool} -- is this minion being added a token (including reborns)? For khadgar multipliers

        Keyword Arguments:
            position {Optional[int]} -- Optional position to insert the minion at, if None add at the end (default: {None})
        """
        if len(self.minions) == 7:
            return None

        if position is None:
            position = len(self.minions)

        new_minion.own_board = self  # TODO: Avoid this? or lean into it?

        # Apply all Auras, Hero power effects, and any other triggers resulting from a minion being summoned
        self.call_triggers(TriggerType.ON_MINION_SUMMON, new_minion)

        new_minion.on_self_summon(self)
        self.add_minion_triggers(new_minion)

        # Set neighbors for new minion and existing minions
        left_neighbor, right_neighbor = None, None
        if len(self.minions) == 0:
            # left and right are None
            left_neighbor, right_neighbor = None, None
        elif position > len(self.minions) - 1:
            # left exists, right is None
            left_neighbor = self.minions[position - 1]
            left_neighbor.right_neighbor = new_minion
        elif position == 0 and len(self.minions) > 0:
            # right exists, left is None
            right_neighbor = self.minions[position]
            right_neighbor.left_neighbor = new_minion
        else:
            # left and right exist
            left_neighbor = self.minions[position - 1]
            right_neighbor = self.minions[position]
            left_neighbor.right_neighbor = new_minion
            right_neighbor.left_neighbor = new_minion

        new_minion.position = position
        new_minion.player_id = self.player_id
        new_minion.left_neighbor = left_neighbor
        new_minion.right_neighbor = right_neighbor

        self.minions.insert(position, new_minion)
        for minion in self.minions[position + 1:]:
            minion.shift_right()

        copied_minion = None
        if allow_copy:
            for _ in range(self.token_creation_multiplier):
                copied_minion = copy.deepcopy(new_minion)
                copied_minion = self.add_minion(copied_minion,
                                                copied_minion.position + 1,
                                                allow_copy=False)

        inserted_minion = copied_minion if copied_minion else new_minion
        # If there is a minion that is set aside (ie dead) that summoned this minion, (ie via deathrattle)
        # Then its important for the parent minion to track in case that minion will be reborn.
        # If it is reborn, then it should spawn to the right of its child minions (pending space restrictions)
        if summoning_minion:
            assert summoning_minion.dead
            summoning_minion.left_neighbor = inserted_minion

        return inserted_minion
Beispiel #9
0
 def trigger_on_friendly_summon(self, other_minion: Minion):
     if MinionType.Beast in other_minion.types:
         stats = 10 if self.golden else 5
         other_minion.add_stats(stats, stats)
Beispiel #10
0
 def trigger_on_friendly_attacked(self, defending_minion: Minion, defending_board: PlayerBoard, attacking_board: PlayerBoard):
     if defending_minion.taunt:
         defending_minion.add_stats(self.bonus_attack, 0)
Beispiel #11
0
 def trigger_on_friendly_summon(self, other_minion: Minion):
     if MinionType.Demon in other_minion.types:
         other_minion.add_stats(self.buffAmount, 0)
Beispiel #12
0
 def trigger_on_friendly_gained_stats(self, minion: Minion, attack, health):
     if MinionType.Dragon in minion.types and attack > 0:
         minion.add_stats(0, 2 if self.golden else 1)
Beispiel #13
0
 def buff_if_pirate(self, other_minion: Minion):
     if MinionType.Pirate in other_minion.types:
         other_minion.add_stats(self.buff_amount, self.buff_amount)
Beispiel #14
0
 def trigger_on_friendly_divine_shield_lost(self,
                                            minion_losing_shield: Minion,
                                            own_board: PlayerBoard):
     attack = 4 if self.golden else 2
     health = 2 if self.golden else 1
     minion_losing_shield.add_stats(attack, health)