diff --git a/Mage.Sets/src/mage/cards/c/CurseOfChaos.java b/Mage.Sets/src/mage/cards/c/CurseOfChaos.java index e30c0a860e3..53b0c265ac9 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfChaos.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfChaos.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -24,13 +23,12 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author LevelX2 */ public final class CurseOfChaos extends CardImpl { public CurseOfChaos(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); this.subtype.add(SubType.AURA, SubType.CURSE); @@ -74,9 +72,9 @@ class CurseOfChaosTriggeredAbility extends TriggeredAbilityImpl { Permanent enchantment = game.getPermanent(this.getSourceId()); if (enchantment != null && enchantment.getAttachedTo() != null - && game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo())) { - for (Effect effect: this.getEffects()) { - effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackingPlayerId())); + && game.getCombat().getPlayerDefenders(game, false).contains(enchantment.getAttachedTo())) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackingPlayerId())); } return true; } @@ -115,7 +113,7 @@ class CurseOfChaosEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player attacker = game.getPlayer(this.getTargetPointer().getFirst(game, source)); if (attacker != null) { - if (!attacker.getHand().isEmpty() && attacker.chooseUse(outcome, "Discard a card and draw a card?", source, game)){ + if (!attacker.getHand().isEmpty() && attacker.chooseUse(outcome, "Discard a card and draw a card?", source, game)) { attacker.discard(1, false, source, game); attacker.drawCards(1, game); } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfInertia.java b/Mage.Sets/src/mage/cards/c/CurseOfInertia.java index 57923e50315..06ccfc4813e 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfInertia.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfInertia.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -23,13 +22,12 @@ import mage.target.TargetPlayer; import java.util.UUID; /** - * * @author LevelX2 */ public final class CurseOfInertia extends CardImpl { public CurseOfInertia(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); this.subtype.add(SubType.AURA, SubType.CURSE); @@ -59,7 +57,7 @@ class CurseOfInertiaTriggeredAbility extends TriggeredAbilityImpl { public CurseOfInertiaTriggeredAbility() { super(Zone.BATTLEFIELD, new CurseOfInertiaTapOrUntapTargetEffect(), false); } - + public CurseOfInertiaTriggeredAbility(final CurseOfInertiaTriggeredAbility ability) { super(ability); } @@ -74,7 +72,7 @@ class CurseOfInertiaTriggeredAbility extends TriggeredAbilityImpl { Permanent enchantment = game.getPermanent(this.getSourceId()); if (enchantment != null && enchantment.getAttachedTo() != null - && game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo())) { + && game.getCombat().getPlayerDefenders(game, false).contains(enchantment.getAttachedTo())) { TargetPermanent target = new TargetPermanent(); target.setTargetController(game.getCombat().getAttackingPlayerId()); addTarget(target); diff --git a/Mage.Sets/src/mage/cards/c/CurseOfShallowGraves.java b/Mage.Sets/src/mage/cards/c/CurseOfShallowGraves.java index a12717fa74d..d83d76ef44d 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfShallowGraves.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfShallowGraves.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -27,7 +26,6 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author LevelX2 */ public final class CurseOfShallowGraves extends CardImpl { @@ -80,7 +78,7 @@ class CurseOfShallowTriggeredAbility extends TriggeredAbilityImpl { Permanent enchantment = game.getPermanent(this.getSourceId()); if (enchantment != null && enchantment.getAttachedTo() != null - && game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo())) { + && game.getCombat().getPlayerDefenders(game, false).contains(enchantment.getAttachedTo())) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackingPlayerId())); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CurseOfShallowGravesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CurseOfShallowGravesTest.java new file mode 100644 index 00000000000..8edd083aa45 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CurseOfShallowGravesTest.java @@ -0,0 +1,79 @@ +package org.mage.test.cards.abilities.curses; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CurseOfShallowGravesTest extends CardTestPlayerBase { + + /* + Curse of Shallow Graves + {2}{B} + Enchant player + Whenever a player attacks enchanted player with one or more creatures, that attacking player may create a tapped 2/2 black Zombie creature token. + + bug: https://www.slightlymagic.net/forum/viewtopic.php?f=70&t=23164&p=229567#p229567 + */ + + @Test + public void test_AttackPlayer() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, "Curse of Shallow Graves"); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Shallow Graves", playerB); + checkPermanentCount("curse on battle", 1, PhaseStep.BEGIN_COMBAT, playerA, "Curse of Shallow Graves", 1); + + // turn 1 - attack without token + attack(1, playerA, "Balduvian Bears", playerB); + setChoice(playerA, "No"); // don't create token + checkPermanentCount("zombie 0", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Zombie", 0); + + // turn 3 - attack with token + attack(3, playerA, "Balduvian Bears", playerB); + setChoice(playerA, "Yes"); // create token + checkPermanentCount("zombie 1", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Zombie", 1); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 20 - 2 - 2); + } + + @Test + public void test_AttackPlaneswalker() { + // planeswalker only attack must be ignored by token's effect + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, "Curse of Shallow Graves"); + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2 + addCard(Zone.BATTLEFIELD, playerB, "Chandra Ablaze", 1); // 5 loyalty + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Shallow Graves", playerB); + checkPermanentCount("curse on battle", 1, PhaseStep.BEGIN_COMBAT, playerA, "Curse of Shallow Graves", 1); + + // turn 1 - attack player without token + attack(1, playerA, "Balduvian Bears", playerB); + setChoice(playerA, "No"); // don't create token + checkPermanentCount("zombie 0", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Zombie", 0); + + // turn 3 - attack planeswalker (no choices at all) + attack(3, playerA, "Balduvian Bears", "Chandra Ablaze"); + //setChoice(playerA, "Yes"); + checkPermanentCount("zombie 0", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Zombie", 0); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 20 - 2); + assertCounterCount(playerB, "Chandra Ablaze", CounterType.LOYALTY, 5 - 2); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/EnchantedPlayerAttackedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EnchantedPlayerAttackedTriggeredAbility.java index a6bd906692f..6c02743b371 100644 --- a/Mage/src/main/java/mage/abilities/common/EnchantedPlayerAttackedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EnchantedPlayerAttackedTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -10,7 +9,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; /** - * * @author LevelX2 */ public class EnchantedPlayerAttackedTriggeredAbility extends TriggeredAbilityImpl { @@ -35,7 +33,7 @@ public class EnchantedPlayerAttackedTriggeredAbility extends TriggeredAbilityImp Permanent enchantment = game.getPermanentOrLKIBattlefield(getSourceId()); Player controller = game.getPlayer(getControllerId()); if (controller != null && enchantment != null) { - return game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo()); + return game.getCombat().getPlayerDefenders(game, false).contains(enchantment.getAttachedTo()); } return false; } diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 055a0339416..50a10ffeafa 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -275,7 +275,7 @@ public class Combat implements Serializable, Copyable { Permanent attackingPermanent = game.getPermanent(attacker); if (attackingPermanent != null) { attackingPermanent.setTapped(false); - attackingPermanent.tap(true,game); // to tap with event finally here is needed to prevent abusing of Vampire Envoy like cards + attackingPermanent.tap(true, game); // to tap with event finally here is needed to prevent abusing of Vampire Envoy like cards } } handleBanding(attacker, game); @@ -298,11 +298,11 @@ public class Combat implements Serializable, Copyable { private void handleBanding(UUID creatureId, Game game) { Player player = game.getPlayer(attackingPlayerId); Permanent attacker = game.getPermanent(creatureId); - if (attacker != null + if (attacker != null && player != null) { CombatGroup combatGroup = findGroup(attacker.getId()); - if (combatGroup != null - && attacker.getBandedCards().isEmpty() + if (combatGroup != null + && attacker.getBandedCards().isEmpty() && getAttackers().size() > 1) { boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId()); List bandsWithOther = new ArrayList<>(); @@ -318,7 +318,7 @@ public class Combat implements Serializable, Copyable { filter.add(Predicates.not(new PermanentIdPredicate(creatureId))); filter.add(new AttackingSameNotBandedPredicate(combatGroup.getDefenderId())); // creature that isn't already banded, and is attacking the same player or planeswalker List> predicates = new ArrayList<>(); - if (!canBand + if (!canBand && canBandWithOther) { for (Ability ab : bandsWithOther) { BandsWithOtherAbility ability = (BandsWithOtherAbility) ab; @@ -341,15 +341,15 @@ public class Combat implements Serializable, Copyable { canBandWithOther &= target.canChoose(attackingPlayerId, game); if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId)) || (!canBand && !canBandWithOther) - || !player.chooseUse(Outcome.Benefit, - "Do you wish to " + (isBanded ? "band " + attacker.getLogName() - + " with another " : "form a band with " + attacker.getLogName() + " and an ") - + "attacking creature?", null, game)) { + || !player.chooseUse(Outcome.Benefit, + "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + + " with another " : "form a band with " + attacker.getLogName() + " and an ") + + "attacking creature?", null, game)) { break; } if (canBand && canBandWithOther) { - if (player.chooseUse(Outcome.Detriment, "Choose type of banding ability to apply:", + if (player.chooseUse(Outcome.Detriment, "Choose type of banding ability to apply:", attacker.getLogName(), "Banding", "Bands with other", null, game)) { canBandWithOther = false; } else { @@ -564,7 +564,7 @@ public class Combat implements Serializable, Copyable { * Handle the blocker selection process * * @param blockController player that controls how to block, if null the - * defender is the controller + * defender is the controller * @param game */ public void selectBlockers(Player blockController, Game game) { @@ -752,8 +752,8 @@ public class Combat implements Serializable, Copyable { * creature can't block unless a player pays a cost, that player is not * required to pay that cost, even if blocking with that creature would * increase the number of requirements being obeyed. - * - * + *

+ *

* Example: A player controls one creature that "blocks if able" and another * creature with no abilities. An effect states "Creatures can't be blocked * except by two or more creatures." Having only the first creature block @@ -1374,7 +1374,7 @@ public class Combat implements Serializable, Copyable { * @param playerId * @param game * @param solveBanding check whether also add creatures banded with - * attackerId + * attackerId */ public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) { Permanent blocker = game.getPermanent(blockerId); @@ -1578,8 +1578,15 @@ public class Combat implements Serializable, Copyable { } public Set getPlayerDefenders(Game game) { + return getPlayerDefenders(game, true); + } + + public Set getPlayerDefenders(Game game, boolean includePlaneswalkers) { Set playerDefenders = new HashSet<>(); for (CombatGroup group : groups) { + if (group.defenderIsPlaneswalker && !includePlaneswalkers) { + continue; + } if (group.defenderIsPlaneswalker) { Permanent permanent = game.getPermanent(group.getDefenderId()); if (permanent != null) {