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)
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)
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")
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)
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()
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
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)
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)
def trigger_on_friendly_summon(self, other_minion: Minion): if MinionType.Demon in other_minion.types: other_minion.add_stats(self.buffAmount, 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)
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)
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)