From 9a5fa9c8b08d3a1ff5648c6e52a0084b79461598 Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Mon, 5 Jun 2023 19:14:54 -0700 Subject: [PATCH] Added Norn's Decree (#10281) * Added Norn's Decree Also added a couple of helper classes: - A triggered ability for when players attack - A triggered ability for the controller taking combat damage from one or more creatures (will be used in Starscream, Power Hungry) - A condition for when an attacked player is poisoned * Fixed rules comment issue * Fixed issue with incorrect logic in CombatDamageDealtToYouTriggeredAbility --- Mage.Sets/src/mage/cards/n/NornsDecree.java | 43 ++++++++ .../sets/PhyrexiaAllWillBeOneCommander.java | 1 + ...ombatDamageDealtToYouTriggeredAbility.java | 101 ++++++++++++++++++ .../common/PlayerAttacksTriggeredAbility.java | 87 +++++++++++++++ .../AttackedPlayersPoisonedCondition.java | 30 ++++++ 5 files changed, 262 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/n/NornsDecree.java create mode 100644 Mage/src/main/java/mage/abilities/common/CombatDamageDealtToYouTriggeredAbility.java create mode 100644 Mage/src/main/java/mage/abilities/common/PlayerAttacksTriggeredAbility.java create mode 100644 Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java diff --git a/Mage.Sets/src/mage/cards/n/NornsDecree.java b/Mage.Sets/src/mage/cards/n/NornsDecree.java new file mode 100644 index 0000000000..55e93f0882 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NornsDecree.java @@ -0,0 +1,43 @@ +package mage.cards.n; + +import java.util.UUID; + +import mage.abilities.common.CombatDamageDealtToYouTriggeredAbility; +import mage.abilities.common.PlayerAttacksTriggeredAbility; +import mage.abilities.condition.common.AttackedPlayersPoisonedCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.counter.AddPoisonCounterTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author alexander-novo + */ +public final class NornsDecree extends CardImpl { + + public NornsDecree(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[] { CardType.ENCHANTMENT }, "{2}{W}"); + + // Whenever one or more creatures an opponent controls deal combat damage to you, that opponent gets a poison counter. + this.addAbility(new CombatDamageDealtToYouTriggeredAbility(new AddPoisonCounterTargetEffect(1).setText("that opponent gets a poison counter."), true)); + + // Whenever a player attacks, if one or more players being attacked are poisoned, the attacking player draws a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new PlayerAttacksTriggeredAbility(new DrawCardTargetEffect(1), true), + AttackedPlayersPoisonedCondition.instance, + "Whenever a player attacks, if one or more players being attacked are poisoned, the attacking player draws a card.")); + } + + private NornsDecree(final NornsDecree card) { + super(card); + } + + @Override + public NornsDecree copy() { + return new NornsDecree(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java index 4f3a7ea631..f8df857355 100644 --- a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java +++ b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java @@ -124,6 +124,7 @@ public final class PhyrexiaAllWillBeOneCommander extends ExpansionSet { cards.add(new SetCardInfo("Norn's Annex", 83, Rarity.RARE, mage.cards.n.NornsAnnex.class)); cards.add(new SetCardInfo("Norn's Choirmaster", 8, Rarity.RARE, mage.cards.n.NornsChoirmaster.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Norn's Choirmaster", 46, Rarity.RARE, mage.cards.n.NornsChoirmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Norn's Decree", 9, Rarity.RARE, mage.cards.n.NornsDecree.class)); cards.add(new SetCardInfo("Noxious Revival", 110, Rarity.UNCOMMON, mage.cards.n.NoxiousRevival.class)); cards.add(new SetCardInfo("Otharri, Suns' Glory", 3, Rarity.MYTHIC, mage.cards.o.OtharriSunsGlory.class)); cards.add(new SetCardInfo("Painful Truths", 95, Rarity.RARE, mage.cards.p.PainfulTruths.class)); diff --git a/Mage/src/main/java/mage/abilities/common/CombatDamageDealtToYouTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CombatDamageDealtToYouTriggeredAbility.java new file mode 100644 index 0000000000..96cf8c137f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/CombatDamageDealtToYouTriggeredAbility.java @@ -0,0 +1,101 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.DamagedPlayerBatchEvent; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; + +/** + * A triggered ability for whenever one or more creatures deal combat damage to + * you. Has an optional component for setting the target pointer to the opponent + * whose creatures dealt combat damage to you. + * + * @author alexander-novo + */ +public class CombatDamageDealtToYouTriggeredAbility extends TriggeredAbilityImpl { + + // Whether or not the ability should set a target targetting the opponent who + // controls the creatures who dealt damage to you + private final boolean setTarget; + + /** + * @param effect The effect that should happen when the ability resolves + */ + public CombatDamageDealtToYouTriggeredAbility(Effect effect) { + this(effect, false); + } + + /** + * @param effect The effect that should happen when the ability resolves + * @param setTarget Whether or not the ability should set a target targetting + * the opponent who controls the creatures who dealt damage to + * you + */ + public CombatDamageDealtToYouTriggeredAbility(Effect effect, boolean setTarget) { + this(Zone.BATTLEFIELD, effect, setTarget, false); + } + + /** + * @param zone Which zone the ability shoudl take effect in + * @param effect The effect that should happen when the ability resolves + * @param setTarget Whether or not the ability should set a target targetting + * the opponent who controls the creatures who dealt damage to + * you + * @param optional Whether or not the ability is optional + */ + public CombatDamageDealtToYouTriggeredAbility(Zone zone, Effect effect, boolean setTarget, + boolean optional) { + super(zone, effect, optional); + + this.setTarget = setTarget; + + setTriggerPhrase(generateTriggerPhrase()); + } + + private CombatDamageDealtToYouTriggeredAbility(final CombatDamageDealtToYouTriggeredAbility ability) { + super(ability); + + this.setTarget = ability.setTarget; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerBatchEvent damageEvent = (DamagedPlayerBatchEvent) event; + boolean check = damageEvent.getEvents() + .stream() + .anyMatch(c -> c.isCombatDamage() && c.getPlayerId() == this.controllerId); + + if (check) { + if (this.setTarget) { + this.getEffects().setTargetPointer( + new FixedTarget(game.getPermanent(damageEvent.getSourceId()).getControllerId())); + } + + return true; + } + return false; + } + + private String generateTriggerPhrase() { + if (setTarget) { + return "Whenever one or more creatures an opponent controls deal combat damage to you, "; + } else { + return "Whenever one or more creatures deal combat damage to you, "; + } + } + + @Override + public CombatDamageDealtToYouTriggeredAbility copy() { + return new CombatDamageDealtToYouTriggeredAbility(this); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/common/PlayerAttacksTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/PlayerAttacksTriggeredAbility.java new file mode 100644 index 0000000000..e230696dc0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/PlayerAttacksTriggeredAbility.java @@ -0,0 +1,87 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; + +/** + * A triggered ability for whenever a player attacks. Has an optional component + * for setting the target pointer on effects to that attacking player. + * + * @author alexander-novo + */ +public class PlayerAttacksTriggeredAbility extends TriggeredAbilityImpl { + + // Whether or not the ability should set a target targetting the player who + // attacked + private final boolean setTarget; + + /** + * @param effect The effect that should happen when the ability resolves + */ + public PlayerAttacksTriggeredAbility(Effect effect) { + this(effect, false); + } + + /** + * @param effect The effect that should happen when the ability resolves + * @param setTarget Whether or not the ability should set a target targetting + * the player who attacked + */ + public PlayerAttacksTriggeredAbility(Effect effect, boolean setTarget) { + this(Zone.BATTLEFIELD, effect, setTarget, false); + } + + /** + * @param zone Which zone the ability shoudl take effect in + * @param effect The effect that should happen when the ability resolves + * @param setTarget Whether or not the ability should set a target targetting + * the player who attacked + * @param optional Whether or not the ability is optional + */ + public PlayerAttacksTriggeredAbility(Zone zone, Effect effect, boolean setTarget, + boolean optional) { + super(zone, effect, optional); + + this.setTarget = setTarget; + + setTriggerPhrase(generateTriggerPhrase()); + } + + private PlayerAttacksTriggeredAbility(final PlayerAttacksTriggeredAbility ability) { + super(ability); + + this.setTarget = ability.setTarget; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getCombat().getAttackers().isEmpty()) { + if (this.setTarget) { + this.getEffects().setTargetPointer( + new FixedTarget(event.getPlayerId())); + } + return true; + } + + return false; + } + + private String generateTriggerPhrase() { + return "Whenever a player attacks, "; + } + + @Override + public PlayerAttacksTriggeredAbility copy() { + return new PlayerAttacksTriggeredAbility(this); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java new file mode 100644 index 0000000000..f0659ff46f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java @@ -0,0 +1,30 @@ + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.counters.CounterType; +import mage.game.Game; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * A condition which checks whether any players being attacked are poisoned + * (have one or more poison counters on them) + * + * @author alexander-novo + */ +public enum AttackedPlayersPoisonedCondition implements Condition { + + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getCombat().getDefenders().stream().map(defender -> game.getPlayer(defender)) + .anyMatch(player -> player != null && player.getCounters().containsKey(CounterType.POISON)); + } + + @Override + public String toString() { + return "one or more players being attacked are poisoned"; + } +} \ No newline at end of file