public static void OnAttacking(IAttacker attacker, IDamageableEntity target, bool isRetaliation, out bool shouldAbort) { shouldAbort = false; if (_minionAttackingListeners.Any()) { // Triggered Effects get called first, then Secrets, then the game engine var sortedListeners = _minionAttackingListeners.Where(kvp => kvp.Item1.Type != CardType.ACTIVE_SECRET).OrderBy(kvp => kvp.Item1.TimePlayed).ToList(); var secrets = _minionAttackingListeners.Where(kvp => kvp.Item1.Type == CardType.ACTIVE_SECRET).OrderBy(kvp => kvp.Item1.TimePlayed).ToList(); sortedListeners.AddRange(secrets); foreach (var handler in sortedListeners.Select(kvp => kvp.Item2)) { handler(attacker, target, isRetaliation, out shouldAbort); } } if (!shouldAbort) { GameEngine.ApplyAttackDamage(attacker, target, isRetaliation); // Time to trigger deathrattles // If it was a weapon attacking, wait until after the weapon has taken durability hit if (!(attacker is BaseWeapon)) { GameEngine.TriggerDeathrattles(); } } }
/// <summary> /// Plays a card /// </summary> /// <param name="card">The card to play</param> /// <param name="subTarget">The sub target for this card, usually for targeting batlle cry spells</param> /// <param name="cardEffect">The card effect to use</param> /// <param name="gameboardPos">The position on the gameboard to place the card (if applicable). A value of -1 means play it in the next available slot</param> public void PlayCard(BaseCard card, IDamageableEntity subTarget, int gameboardPos = -1, CardEffect cardEffect = CardEffect.NONE) { // Is it even our turn to play? var gameState = GameEngine.GameState; if (gameState.CurrentPlayer != this) { throw new InvalidOperationException(string.Format("You can't play out of turn! It is currently {0}'s turn", gameState.CurrentPlayer)); } // Check if it exists in the player's hand BaseCard cardInHand = this.Hand.FirstOrDefault(c => c.Equals(card)); if (cardInHand == null) { throw new InvalidOperationException(string.Format("You can't play a card that's not in hand (or deck if force summoned from there)! {0}", card)); } // Check if we have enough mana to make the play if (this.Mana < cardInHand.CurrentManaCost) { throw new InvalidOperationException(string.Format("Not enough mana {0} to play that card {1}!", this.Mana, card.CurrentManaCost)); } var minionCard = cardInHand as BaseMinion; if (minionCard != null) { this.SummonMinion(minionCard, subTarget, gameboardPos, cardEffect, forceSummoned: false, cardSource: this.hand); } var spellCard = cardInHand as BaseSpell; if (spellCard != null) { this.PlaySpell(spellCard, subTarget, cardEffect); } var weaponCard = cardInHand as BaseWeapon; if (weaponCard != null) { this.PlayWeapon(weaponCard, subTarget); } // Register any triggered effects var triggeredEffectCard = cardInHand as ITriggeredEffectOwner; if (triggeredEffectCard != null) { triggeredEffectCard.RegisterEffect(); } // Trigger any deathrattles GameEngine.TriggerDeathrattles(); GameEngine.CheckForGameEnd(); }
/// <summary> /// Triggers death rattles after damage has been calculated. /// </summary> public static void TriggerDeathrattles() { // Before we fire trigger any deathrattles, we need to check if the game is over if (GameEngine.CheckForGameEnd()) { return; } // Remove dead minions from the board so the deathrattle doesn't trigger on itself GameEngine.DeadCardsThisTurn.ForEach(card => GameEngine.GameState.Board.RemoveCard(card)); // Deathrattles trigger by TimePlayed first. var sortedDeadCards = GameEngine.DeadCardsThisTurn.OrderBy(card => card.TimePlayed).ToList(); bool deathrattleTriggered = false; foreach (var card in sortedDeadCards) { if (GameEngine.Deathrattles.ContainsKey(card)) { GameEngine.Deathrattles[card].ForEach(deathrattle => deathrattle.Deathrattle()); // If the triggering card's player has a Baron Rivendare on the board, trigger again List <BaseCard> cardPlayZone; if (card.Owner == GameEngine.GameState.CurrentPlayer) { cardPlayZone = GameEngine.GameState.CurrentPlayerPlayZone; } else { cardPlayZone = GameEngine.GameState.WaitingPlayerPlayZone; } if (cardPlayZone.Any(minion => minion is BaronRivendare && !((BaseMinion)minion).IsSilenced)) { GameEngine.Deathrattles[card].ForEach(deathrattle => deathrattle.Deathrattle()); } deathrattleTriggered = true; GameEngine.Deathrattles.Remove(card); } } // We may have actually triggered yet more deathrattles with this so we need to check for it if (deathrattleTriggered) { GameEngine.TriggerDeathrattles(); } }