diff --git a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/target/maven-archiver/pom.properties b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/target/maven-archiver/pom.properties index 8a2cedc90dc..d7097083df5 100644 --- a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/target/maven-archiver/pom.properties +++ b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/target/maven-archiver/pom.properties @@ -1,5 +1,5 @@ -#Generated by Maven -#Tue Aug 15 00:36:02 CEST 2017 -version=1.4.26 -groupId=org.mage -artifactId=mage-game-pennydreadfulcommanderfreeforall +#Generated by Maven +#Mon Aug 28 09:53:46 CEST 2017 +version=1.4.26 +groupId=org.mage +artifactId=mage-game-pennydreadfulcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 20201a7e306..27129fed99c 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -680,7 +680,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target.getOriginalTarget() instanceof TargetDefender) { // TODO: Improve, now planeswalker is always chosen if it exits List targets; - targets = game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), randomOpponentId, game); + targets = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, randomOpponentId, game); if (targets != null && !targets.isEmpty()) { for (Permanent planeswalker : targets) { if (target.canTarget(getId(), planeswalker.getId(), source, game)) { diff --git a/Mage.Sets/src/mage/cards/o/OathOfJace.java b/Mage.Sets/src/mage/cards/o/OathOfJace.java index ae036ea5195..5e17e2e9b1e 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfJace.java +++ b/Mage.Sets/src/mage/cards/o/OathOfJace.java @@ -39,7 +39,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.TargetController; -import mage.filter.common.FilterPlaneswalkerPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -50,7 +50,7 @@ import mage.players.Player; public class OathOfJace extends CardImpl { public OathOfJace(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); addSuperType(SuperType.LEGENDARY); // When Oath of Jace enters the battlefield, draw three cards, then discard two cards. @@ -91,7 +91,7 @@ class OathOfJaceEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int planeswalker = game.getBattlefield().countAll(new FilterPlaneswalkerPermanent(), source.getControllerId(), game); + int planeswalker = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_PLANESWALKER, source.getControllerId(), game); if (planeswalker > 0) { controller.scry(planeswalker, source, game); } diff --git a/Mage.Sets/src/mage/cards/p/PortalMage.java b/Mage.Sets/src/mage/cards/p/PortalMage.java new file mode 100644 index 00000000000..0d311349b81 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PortalMage.java @@ -0,0 +1,160 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.p; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.IsStepCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.filter.StaticFilters; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetDefender; + +/** + * + * @author LevelX2 + */ +public class PortalMage extends CardImpl { + + public PortalMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add("Human"); + this.subtype.add("Wizard"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // If Portal Mage enters the battlefield during the declare attackers step, you may reselect the player or planeswalker that the target attacking creature attacks. + Ability ability = new ConditionalTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new PortalMageEffect(), true), + new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), + "If {this} enters the battlefield during the declare attackers step, you may reselect the player or planeswalker that the target attacking creature attacks. " + + "(It can't attack its controller or its controller's planeswalkers.)"); + ability.addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); + this.addAbility(ability); + } + + public PortalMage(final PortalMage card) { + super(card); + } + + @Override + public PortalMage copy() { + return new PortalMage(this); + } +} + +class PortalMageEffect extends OneShotEffect { + + public PortalMageEffect() { + super(Outcome.Benefit); + this.staticText = "you may reselect the player or planeswalker that the target attacking creature attacks"; + } + + public PortalMageEffect(final PortalMageEffect effect) { + super(effect); + } + + @Override + public PortalMageEffect copy() { + return new PortalMageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent attackingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (attackingCreature != null) { + CombatGroup combatGroupTarget = null; + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + if (combatGroup.getAttackers().contains(attackingCreature.getId())) { + combatGroupTarget = combatGroup; + break; + } + } + if (combatGroupTarget == null) { + return false; + } + // Reselecting which player or planeswalker a creature is attacking ignores all requirements, + // restrictions, and costs associated with attacking. + + // Update possible defender + Set defenders = new LinkedHashSet<>(); + for (UUID playerId : game.getCombat().getAttackablePlayers(game)) { + defenders.add(playerId); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, playerId, game)) { + defenders.add(permanent.getId()); + } + } + // Select the new defender + TargetDefender target = new TargetDefender(defenders, null); + target.setNotTarget(true); // player or planswalker hexproof does not prevent attacking a player + if (controller.chooseTarget(Outcome.Damage, target, source, game)) { + if (!combatGroupTarget.getDefenderId().equals(target.getFirstTarget())) { + if (combatGroupTarget.changeDefenderPostDeclaration(target.getFirstTarget(), game)) { + String attacked = ""; + Player player = game.getPlayer(target.getFirstTarget()); + if (player != null) { + attacked = player.getLogName(); + } else { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + attacked = permanent.getLogName(); + } + } + game.informPlayers(attackingCreature.getLogName() + " attacks now " + attacked); + return true; + } + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/ToTheSlaughter.java b/Mage.Sets/src/mage/cards/t/ToTheSlaughter.java index 5936bfe4664..b0a42032451 100644 --- a/Mage.Sets/src/mage/cards/t/ToTheSlaughter.java +++ b/Mage.Sets/src/mage/cards/t/ToTheSlaughter.java @@ -37,7 +37,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; -import mage.filter.common.FilterPlaneswalkerPermanent; import mage.target.TargetPlayer; /** @@ -61,7 +60,7 @@ public class ToTheSlaughter extends CardImpl { DeliriumCondition.instance, "
Delirium — If there are four or more card types among cards in your graveyard, instead that player sacrifices a creature")); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new SacrificeEffect(new FilterPlaneswalkerPermanent(), 1, "Target player"), + new SacrificeEffect(StaticFilters.FILTER_PERMANENT_PLANESWALKER, 1, "Target player"), DeliriumCondition.instance, "and a planeswalker.")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/sets/Commander2017.java b/Mage.Sets/src/mage/sets/Commander2017.java index ed00435de7c..f74fc1779ba 100644 --- a/Mage.Sets/src/mage/sets/Commander2017.java +++ b/Mage.Sets/src/mage/sets/Commander2017.java @@ -88,6 +88,7 @@ public class Commander2017 extends ExpansionSet { cards.add(new SetCardInfo("O-Kagachi, Vengeful Kami", 45, Rarity.MYTHIC, mage.cards.o.OKagachiVengefulKami.class)); cards.add(new SetCardInfo("Path of Ancestry", 56, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); cards.add(new SetCardInfo("Patron of the Vein", 20, Rarity.RARE, mage.cards.p.PatronOfTheVein.class)); + cards.add(new SetCardInfo("Portal Mage", 13, Rarity.RARE, mage.cards.p.PortalMage.class)); cards.add(new SetCardInfo("Qasali Slingers", 33, Rarity.RARE, mage.cards.q.QasaliSlingers.class)); cards.add(new SetCardInfo("Ramos, Dragon Engine", 55, Rarity.MYTHIC, mage.cards.r.RamosDragonEngine.class)); cards.add(new SetCardInfo("Scalelord Reckoner", 6, Rarity.RARE, mage.cards.s.ScalelordReckoner.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index ce65580ab56..74a9d873994 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -51,6 +51,7 @@ import mage.counters.Counter; import mage.counters.Counters; import mage.filter.Filter; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.*; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; @@ -594,7 +595,7 @@ public class TestPlayer implements Player { String group = groups[i]; if (group.startsWith("planeswalker=")) { String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, game)) { if (permanent.getName().equals(planeswalkerName)) { defenderId = permanent.getId(); } diff --git a/Mage/src/main/java/mage/abilities/effects/PlaneswalkerRedirectionEffect.java b/Mage/src/main/java/mage/abilities/effects/PlaneswalkerRedirectionEffect.java index be713663adb..00857194670 100644 --- a/Mage/src/main/java/mage/abilities/effects/PlaneswalkerRedirectionEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/PlaneswalkerRedirectionEffect.java @@ -32,7 +32,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.common.FilterPlaneswalkerPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; @@ -48,8 +48,6 @@ import mage.target.TargetPermanent; */ public class PlaneswalkerRedirectionEffect extends RedirectionEffect { - private static FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent(); - public PlaneswalkerRedirectionEffect() { super(Duration.EndOfGame); } @@ -76,11 +74,11 @@ public class PlaneswalkerRedirectionEffect extends RedirectionEffect { Player target = game.getPlayer(event.getTargetId()); Player player = game.getPlayer(playerId); if (target != null && player != null) { - int numPlaneswalkers = game.getBattlefield().countAll(filter, target.getId(), game); + int numPlaneswalkers = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_PLANESWALKER, target.getId(), game); if (numPlaneswalkers > 0 && player.chooseUse(outcome, "Redirect damage to planeswalker?", source, game)) { - redirectTarget = new TargetPermanent(filter); + redirectTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_PLANESWALKER); if (numPlaneswalkers == 1) { - List planeswalker = game.getBattlefield().getAllActivePermanents(filter, target.getId(), game); + List planeswalker = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, target.getId(), game); if (!planeswalker.isEmpty()) { redirectTarget.add(planeswalker.get(0).getId(), game); } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 8638ff9ddd1..8b0b40b5a65 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -53,6 +53,7 @@ public final class StaticFilters { public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES = new FilterCreaturePermanent("creatures"); public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_GOBLINS = new FilterCreaturePermanent(SubType.GOBLIN, "Goblin creatures"); public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_SLIVERS = new FilterCreaturePermanent(SubType.SLIVER, "Sliver creatures"); + public static final FilterPlaneswalkerPermanent FILTER_PERMANENT_PLANESWALKER = new FilterPlaneswalkerPermanent(); public static final FilterPermanent FILTER_PERMANENT_NON_LAND = new FilterNonlandPermanent(); diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 3dc593d64ca..496520ed0c4 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -40,7 +40,6 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureForCombatBlock; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterPlaneswalkerPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -60,7 +59,6 @@ public class Combat implements Serializable, Copyable { private static final Logger logger = Logger.getLogger(Combat.class); - private static FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent(); private static FilterCreatureForCombatBlock filterBlockers = new FilterCreatureForCombatBlock(); // There are effects that let creatures assigns combat damage equal to its toughness rather than its power private boolean useToughnessForDamage; @@ -1026,6 +1024,13 @@ public class Combat implements Serializable, Copyable { } public void setDefenders(Game game) { + for (UUID playerId : getAttackablePlayers(game)) { + addDefender(playerId, game); + } + } + + public List getAttackablePlayers(Game game) { + List attackablePlayers = new ArrayList<>(); Player attackingPlayer = game.getPlayer(attackingPlayerId); if (attackingPlayer != null) { PlayerList players; @@ -1035,7 +1040,7 @@ public class Combat implements Serializable, Copyable { while (attackingPlayer.isInGame()) { Player opponent = players.getNext(game); if (attackingPlayer.hasOpponent(opponent.getId(), game)) { - addDefender(opponent.getId(), game); + attackablePlayers.add(opponent.getId()); break; } } @@ -1045,18 +1050,19 @@ public class Combat implements Serializable, Copyable { while (attackingPlayer.isInGame()) { Player opponent = players.getPrevious(game); if (attackingPlayer.hasOpponent(opponent.getId(), game)) { - addDefender(opponent.getId(), game); + attackablePlayers.add(opponent.getId()); break; } } break; case MULTIPLE: for (UUID opponentId : game.getOpponents(attackingPlayerId)) { - addDefender(opponentId, game); + attackablePlayers.add(opponentId); } break; } } + return attackablePlayers; } private void addDefender(UUID defenderId, Game game) { @@ -1074,7 +1080,7 @@ public class Combat implements Serializable, Copyable { } } defenders.add(defenderId); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPlaneswalker, defenderId, game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, defenderId, game)) { defenders.add(permanent.getId()); } } diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index f0ecf761ea0..3b900692b7b 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -655,4 +655,23 @@ public class CombatGroup implements Serializable, Copyable { public CombatGroup copy() { return new CombatGroup(this); } + + public boolean changeDefenderPostDeclaration(UUID newDefenderId, Game game) { + Permanent permanent = game.getPermanent(newDefenderId); + if (permanent != null) { + defenderId = newDefenderId; + defendingPlayerId = permanent.getControllerId(); + defenderIsPlaneswalker = true; + return true; + } else { + Player defender = game.getPlayer(newDefenderId); + if (defender != null) { + defenderId = newDefenderId; + defendingPlayerId = newDefenderId; + defenderIsPlaneswalker = false; + return true; + } + } + return false; + } } diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java index 1fbbaaa8939..d63d1ab3535 100644 --- a/Mage/src/main/java/mage/target/common/TargetDefender.java +++ b/Mage/src/main/java/mage/target/common/TargetDefender.java @@ -34,8 +34,8 @@ import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; import mage.filter.Filter; +import mage.filter.StaticFilters; import mage.filter.common.FilterPlaneswalkerOrPlayer; -import mage.filter.common.FilterPlaneswalkerPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -93,7 +93,7 @@ public class TargetDefender extends TargetImpl { } } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, game)) { count++; @@ -117,7 +117,7 @@ public class TargetDefender extends TargetImpl { } } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { if (filter.match(permanent, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -140,7 +140,7 @@ public class TargetDefender extends TargetImpl { possibleTargets.add(playerId); } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, game)) { possibleTargets.add(permanent.getId()); @@ -158,7 +158,7 @@ public class TargetDefender extends TargetImpl { possibleTargets.add(playerId); } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { if (filter.match(permanent, game)) { possibleTargets.add(permanent.getId()); }