From 74a47bc63adba65cf43a71e442b2035956e573c6 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 16 Feb 2018 16:28:13 -0600 Subject: [PATCH 001/133] - Fixed Myr Battlesphere #4522 --- .../src/mage/cards/m/MyrBattlesphere.java | 70 +++++++++------- .../CreatureAttackedWhichPlayerWatcher.java | 84 +++++++++++++++++++ 2 files changed, 122 insertions(+), 32 deletions(-) create mode 100644 Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java diff --git a/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java b/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java index 923d1f6a541..03a8a691cce 100644 --- a/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java +++ b/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.cards.m; import java.io.Serializable; @@ -50,9 +49,11 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.token.MyrToken; import mage.players.Player; import mage.target.TargetPermanent; +import mage.watchers.common.CreatureAttackedWhichPlayerWatcher; /** * @@ -61,7 +62,7 @@ import mage.target.TargetPermanent; public class MyrBattlesphere extends CardImpl { public MyrBattlesphere(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}"); this.subtype.add(SubType.MYR); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(4); @@ -71,7 +72,8 @@ public class MyrBattlesphere extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new MyrToken(), 4), false)); // Whenever Myr Battlesphere attacks, you may tap X untapped Myr you control. If you do, Myr Battlesphere gets +X/+0 until end of turn and deals X damage to defending player. - this.addAbility(new AttacksTriggeredAbility(new MyrBattlesphereEffect(), true)); + this.addAbility(new AttacksTriggeredAbility(new MyrBattlesphereEffect(), true), new CreatureAttackedWhichPlayerWatcher()); + } public MyrBattlesphere(final MyrBattlesphere card) { @@ -107,42 +109,46 @@ class MyrBattlesphereEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - int tappedAmount = 0; - TargetPermanent target = new TargetPermanent(0,1,filter, false); - while (true && controller.canRespond()) { - target.clearChosen(); - if (target.canChoose(source.getControllerId(), game)) { - Map options = new HashMap<>(); - options.put("UI.right.btn.text", "Myr tapping complete"); - controller.choose(outcome, target, source.getControllerId(), game, options); - if (!target.getTargets().isEmpty()) { - UUID creature = target.getFirstTarget(); - if (creature != null) { - game.getPermanent(creature).tap(game); - tappedAmount++; + CreatureAttackedWhichPlayerWatcher watcher = (CreatureAttackedWhichPlayerWatcher) game.getState().getWatchers().get(CreatureAttackedWhichPlayerWatcher.class.getSimpleName()); + if (watcher != null) { + // even if the Myr Battlesphere is off the battlefield, it still does damage to the defender + Permanent myr = game.getPermanentOrLKIBattlefield(source.getSourceId()); + UUID defenderId = watcher.getPlayerAttackedThisTurnByCreature(myr.getId()); + Player defender = game.getPlayer(defenderId); + int tappedAmount = 0; + TargetPermanent target = new TargetPermanent(0, 1, filter, false); + while (true && controller.canRespond()) { + target.clearChosen(); + if (target.canChoose(source.getControllerId(), game)) { + Map options = new HashMap<>(); + options.put("UI.right.btn.text", "Myr tapping complete"); + controller.choose(outcome, target, source.getControllerId(), game, options); + if (!target.getTargets().isEmpty()) { + UUID creature = target.getFirstTarget(); + if (creature != null) { + game.getPermanent(creature).tap(game); + tappedAmount++; + } + } else { + break; } } else { break; } } - else { - break; - } - } - if (tappedAmount > 0) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(" taps ").append(tappedAmount).append(" Myrs").toString()); - // boost effect - game.addEffect(new BoostSourceEffect(tappedAmount, 0, Duration.EndOfTurn), source); - // damage to defender - UUID defenderId = game.getCombat().getDefendingPlayerId(source.getSourceId(), game); - Player defender = game.getPlayer(defenderId); - if (defender != null) { - defender.damage(tappedAmount, source.getSourceId(), game, false, true); - return true; - } + if (tappedAmount > 0) { + game.informPlayers(new StringBuilder(controller.getLogName()).append(" taps ").append(tappedAmount).append(" Myrs").toString()); + // boost effect + game.addEffect(new BoostSourceEffect(tappedAmount, 0, Duration.EndOfTurn), source); + // damage to defender + if (defender != null) { + defender.damage(tappedAmount, myr.getId(), game, false, true); + return true; + } + } + return true; } - return true; } return false; } diff --git a/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java b/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java new file mode 100644 index 00000000000..7d519e6e043 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java @@ -0,0 +1,84 @@ +/* + * 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.watchers.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +/** + * @author jeffwadsworth + * + * Return the last player that was attacked by specified creature this turn + */ +public class CreatureAttackedWhichPlayerWatcher extends Watcher { + + private final Map getPlayerAttackedThisTurnByCreature = new HashMap<>(); + + public CreatureAttackedWhichPlayerWatcher() { + super(CreatureAttackedWhichPlayerWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public CreatureAttackedWhichPlayerWatcher(final CreatureAttackedWhichPlayerWatcher watcher) { + super(watcher); + for (Entry entry : watcher.getPlayerAttackedThisTurnByCreature.entrySet()) { + getPlayerAttackedThisTurnByCreature.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { + UUID creatureId = event.getSourceId(); + UUID playerId = event.getTargetId(); + if (playerId != null + && creatureId != null) { + getPlayerAttackedThisTurnByCreature.putIfAbsent(creatureId, playerId); + } + } + } + + public UUID getPlayerAttackedThisTurnByCreature(UUID creatureId) { + return getPlayerAttackedThisTurnByCreature.getOrDefault(creatureId, null); + } + + @Override + public void reset() { + getPlayerAttackedThisTurnByCreature.clear(); + } + + @Override + public CreatureAttackedWhichPlayerWatcher copy() { + return new CreatureAttackedWhichPlayerWatcher(this); + } +} From 77762cbf59e80995c03bc9fd07fb82ff17838f45 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 16 Feb 2018 23:59:17 +0000 Subject: [PATCH 002/133] Some multi-blocker damage changes Fixes situations like "two-man band with Viscera Seer gets blocked, Viscera Seer sacs itself afterwards" - before, the blocker would deal its damage twice to the remaining attacker. --- .../java/mage/game/combat/CombatGroup.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 2a42f65b5e9..57b9877d491 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -173,21 +173,16 @@ public class CombatGroup implements Serializable, Copyable { public void assignDamageToAttackers(boolean first, Game game) { if (!blockers.isEmpty() && (!first || hasFirstOrDoubleStrike(game))) { // this should only come up if Butcher Orgg is granted the ability to block multiple blockers - boolean altDamageMethod = false; for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { - altDamageMethod = true; + return; } } - if (altDamageMethod) { - // this could be necessary to remake in the future (banding with Butcher Orgg?) - return; - } - if (attackers.size() == 1) { - singleAttackerDamage(first, game); - } else { + if (attackers.size() != 1) { multiAttackerDamage(first, game); + // } else { + // singleAttackerDamage(first, game); } } } @@ -462,10 +457,15 @@ public class CombatGroup implements Serializable, Copyable { * {@link #singleBlockerDamage}. * * Handles abilities like "{this} an block any number of creatures.". + * + * Blocker damage for blockers blocking single creatures is handled in + * the single/multi blocker methods, so this shouldn't be used anymore. * * @param first * @param game + * @deprecated */ + @Deprecated private void singleAttackerDamage(boolean first, Game game) { Permanent blocker = game.getPermanent(blockers.get(0)); Permanent attacker = game.getPermanent(attackers.get(0)); From fe2eec6553a7862094efcda9d7968129cc0d94cf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 17 Feb 2018 11:54:57 +0100 Subject: [PATCH 003/133] * Fixed a problem with Animate Dead and bringing creatures with triggered abilities into play that had no targets. It was related to manipulating spellAbility and not copying spellAbility correctly during states copies. (related to #4428). --- Mage.Sets/src/mage/cards/a/AnimateDead.java | 2 +- .../org/mage/test/cards/copy/IsochronScepterTest.java | 10 +++++----- Mage/src/main/java/mage/cards/CardImpl.java | 10 +++++++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AnimateDead.java b/Mage.Sets/src/mage/cards/a/AnimateDead.java index 4c57bb51d26..a6e9ad0b75d 100644 --- a/Mage.Sets/src/mage/cards/a/AnimateDead.java +++ b/Mage.Sets/src/mage/cards/a/AnimateDead.java @@ -100,7 +100,7 @@ class AnimateDeadReAttachEffect extends OneShotEffect { public AnimateDeadReAttachEffect() { super(Outcome.Benefit); - this.staticText = "Return enchanted creature card to the battlefield under your control and attach {this} to it"; + this.staticText = "return enchanted creature card to the battlefield under your control and attach {this} to it"; } public AnimateDeadReAttachEffect(final AnimateDeadReAttachEffect effect) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java index 83697190404..b695a681ac4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java @@ -118,11 +118,11 @@ public class IsochronScepterTest extends CardTestPlayerBase { public void testAngelsGrace() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.HAND, playerA, "Isochron Scepter"); + // Split second (As long as this spell is on the stack, players can't cast spells or activate abilities that aren't mana abilities.) // You can't lose the game this turn and your opponents can't win the game this turn. // Until end of turn, damage that would reduce your life total to less than 1 reduces it to 1 instead. - - addCard(Zone.HAND, playerA, "Angel's Grace"); + addCard(Zone.HAND, playerA, "Angel's Grace"); // Instant {W} addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 4);// 5/1 addCard(Zone.HAND, playerB, "Lightning Bolt", 2); @@ -149,9 +149,6 @@ public class IsochronScepterTest extends CardTestPlayerBase { setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); - assertExileCount("Angel's Grace", 1); - assertGraveyardCount(playerA, "Angel's Grace", 0); - assertLife(playerA, 1); assertLife(playerB, 20); @@ -160,6 +157,9 @@ public class IsochronScepterTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Dross Crocodile", 3); assertPermanentCount(playerA, "Isochron Scepter", 1); + assertExileCount("Angel's Grace", 1); + assertGraveyardCount(playerA, "Angel's Grace", 0); + } /** diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index d73dd8e6bba..f82868e628b 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -29,7 +29,6 @@ package mage.cards; import java.lang.reflect.Constructor; import java.util.*; - import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; @@ -165,6 +164,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card { secondSideCardClazz = card.secondSideCardClazz; nightCard = card.nightCard; } + if (card.spellAbility != null) { + spellAbility = card.getSpellAbility().copy(); + } else { + spellAbility = null; + } + flipCard = card.flipCard; flipCardName = card.flipCardName; splitCard = card.splitCard; @@ -177,6 +182,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { this.objectId = UUID.randomUUID(); this.abilities.newOriginalId(); this.abilities.setSourceId(objectId); + if (this.spellAbility != null) { + this.spellAbility.setSourceId(objectId); + } } public static Card createCard(String name, CardSetInfo setInfo) { From af42c2dd4cbc475bfb3cac30d85231df7b833061 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 13:45:08 +0000 Subject: [PATCH 004/133] Updated False Orders to better handle banding --- Mage.Sets/src/mage/cards/f/FalseOrders.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/FalseOrders.java b/Mage.Sets/src/mage/cards/f/FalseOrders.java index 8d35c683fa5..f1b4a1cd2ff 100644 --- a/Mage.Sets/src/mage/cards/f/FalseOrders.java +++ b/Mage.Sets/src/mage/cards/f/FalseOrders.java @@ -175,20 +175,35 @@ class FalseOrdersUnblockEffect extends OneShotEffect { // Relevant ruling for Balduvian Warlord: // 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,” // it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point. - boolean notYetBlocked = chosenGroup.getBlockers().isEmpty(); - chosenGroup.addBlocker(permanent.getId(), controller.getId(), game); + chosenGroup.addBlockerToGroup(permanent.getId(), controller.getId(), game); + game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } + CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again + if (blockGroup != null) { + blockGroup.pickAttackerOrder(permanent.getControllerId(), game); + } } } - } + } return true; } } return false; } + + private CombatGroup findBlockingGroup(Permanent blocker, Game game) { + if (game.getCombat().blockingGroupsContains(blocker.getId())) { // if (blocker.getBlocking() > 1) { + for (CombatGroup group : game.getCombat().getBlockingGroups()) { + if (group.getBlockers().contains(blocker.getId())) { + return group; + } + } + } + return null; + } } From e3502bfc81ea7ff57875b6cf7a018393efaa5574 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 13:45:56 +0000 Subject: [PATCH 005/133] Updated Balduvian Warlord to better handle banding --- .../src/mage/cards/b/BalduvianWarlord.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index 196e0544663..adfd89885b1 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -161,14 +161,18 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { // Relevant ruling for Balduvian Warlord: // 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,” // it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point. - boolean notYetBlocked = chosenGroup.getBlockers().isEmpty(); chosenGroup.addBlocker(permanent.getId(), controller.getId(), game); + game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } + CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again + if (blockGroup != null) { + blockGroup.pickAttackerOrder(permanent.getControllerId(), game); + } } } return true; @@ -176,4 +180,15 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { } return false; } + + private CombatGroup findBlockingGroup(Permanent blocker, Game game) { + if (game.getCombat().blockingGroupsContains(blocker.getId())) { // if (blocker.getBlocking() > 1) { + for (CombatGroup group : game.getCombat().getBlockingGroups()) { + if (group.getBlockers().contains(blocker.getId())) { + return group; + } + } + } + return null; + } } From c0866c9ad7a04588a1e21cd6d78b81892ade8945 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 13:46:34 +0000 Subject: [PATCH 006/133] Small fix --- Mage.Sets/src/mage/cards/b/BalduvianWarlord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index adfd89885b1..1bca32e4a9c 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -162,7 +162,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { // 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,” // it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point. boolean notYetBlocked = chosenGroup.getBlockers().isEmpty(); - chosenGroup.addBlocker(permanent.getId(), controller.getId(), game); + chosenGroup.addBlockerToGroup(permanent.getId(), controller.getId(), game); game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); From 1cb703fbdf80fc7f27f6c39971b58877dbf9df52 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 13:59:15 +0000 Subject: [PATCH 007/133] Some fireEvent changes --- Mage.Sets/src/mage/cards/f/FalseOrders.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Sets/src/mage/cards/f/FalseOrders.java b/Mage.Sets/src/mage/cards/f/FalseOrders.java index f1b4a1cd2ff..c2dccd0b35e 100644 --- a/Mage.Sets/src/mage/cards/f/FalseOrders.java +++ b/Mage.Sets/src/mage/cards/f/FalseOrders.java @@ -180,6 +180,12 @@ class FalseOrdersUnblockEffect extends OneShotEffect { game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); + for (UUID bandedId : chosenPermanent.getBandedCards()) { + CombatGroup bandedGroup = game.getCombat().findGroup(bandedId); + if (bandedGroup != null && chosenGroup.getBlockers().size() == 1) { + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, bandedId, null)); + } + } } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } From 851b867ffd22bfd3e3cf808a5563ba466da7d506 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 13:59:22 +0000 Subject: [PATCH 008/133] Some fireEvent changes --- Mage.Sets/src/mage/cards/b/BalduvianWarlord.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index 1bca32e4a9c..e8378805b5f 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -166,6 +166,12 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h if (notYetBlocked) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); + for (UUID bandedId : chosenPermanent.getBandedCards()) { + CombatGroup bandedGroup = game.getCombat().findGroup(bandedId); + if (bandedGroup != null && chosenGroup.getBlockers().size() == 1) { + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, bandedId, null)); + } + } } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } From 5cc3b4c4f20a22e24e7ff02f6abb7d0c2f238c71 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 16:55:46 +0100 Subject: [PATCH 009/133] Implemented Power Leak --- Mage.Sets/src/mage/cards/p/PowerLeak.java | 133 ++++++++++++++++++ Mage.Sets/src/mage/sets/FourthEdition.java | 1 + .../src/mage/sets/LimitedEditionAlpha.java | 1 + .../src/mage/sets/LimitedEditionBeta.java | 1 + Mage.Sets/src/mage/sets/RevisedEdition.java | 1 + Mage.Sets/src/mage/sets/UnlimitedEdition.java | 1 + 6 files changed, 138 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/PowerLeak.java diff --git a/Mage.Sets/src/mage/cards/p/PowerLeak.java b/Mage.Sets/src/mage/cards/p/PowerLeak.java new file mode 100644 index 00000000000..4e4d9120885 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PowerLeak.java @@ -0,0 +1,133 @@ +/* + * 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.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetEnchantmentPermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public class PowerLeak extends CardImpl { + + public PowerLeak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); + this.subtype.add(SubType.AURA); + + // Enchant enchantment + TargetPermanent auraTarget = new TargetEnchantmentPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // At the beginning of the upkeep of enchanted enchantment's controller, that player may pay any amount of mana. Power Leak deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new PowerLeakEffect(), TargetController.CONTROLLER_ATTACHED_TO, false, true, "At the beginning of the upkeep of enchanted enchantment's controller, ")); + } + + public PowerLeak(final PowerLeak card) { + super(card); + } + + @Override + public PowerLeak copy() { + return new PowerLeak(this); + } +} + +class PowerLeakEffect extends OneShotEffect { + + public PowerLeakEffect() { + super(Outcome.Detriment); + this.staticText = "that player may pay any amount of mana. {this} deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way"; + } + + public PowerLeakEffect(final PowerLeakEffect effect) { + super(effect); + } + + @Override + public PowerLeakEffect copy() { + return new PowerLeakEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getActivePlayerId()); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (player != null && permanent != null) { + ManaCosts cost = new ManaCostsImpl<>("{X}"); + String message = "Pay {X} to prevent X damage from " + permanent.getLogName() + "?"; + int xValue = 0; + if (player != null && player.chooseUse(Outcome.Neutral, message, source, game)) { + xValue = player.announceXMana(0, Integer.MAX_VALUE, "Choose the amount of mana to pay", game, source); + cost.add(new GenericManaCost(xValue)); + if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) { + game.informPlayers(player.getLogName() + " paid {" + xValue + "} for " + permanent.getLogName()); + } else { + game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName()); + } + } else { + game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName()); + } + + PreventDamageByTargetEffect effect = new PreventDamageByTargetEffect(Duration.OneUse, xValue, false); + if (xValue != 0 && cost.isPaid()) { + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + } + player.damage(2, source.getSourceId(), game, false, true); + effect.discard(); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/FourthEdition.java b/Mage.Sets/src/mage/sets/FourthEdition.java index 3b5f2616571..17ca81da470 100644 --- a/Mage.Sets/src/mage/sets/FourthEdition.java +++ b/Mage.Sets/src/mage/sets/FourthEdition.java @@ -142,6 +142,7 @@ public class FourthEdition extends ExpansionSet { cards.add(new SetCardInfo("Phantasmal Terrain", 89, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); cards.add(new SetCardInfo("Phantom Monster", 90, Rarity.UNCOMMON, mage.cards.p.PhantomMonster.class)); cards.add(new SetCardInfo("Pirate Ship", 91, Rarity.RARE, mage.cards.p.PirateShip.class)); + cards.add(new SetCardInfo("Power Leak", 92, Rarity.COMMON, mage.cards.p.PowerLeak.class)); cards.add(new SetCardInfo("Power Sink", 93, Rarity.COMMON, mage.cards.p.PowerSink.class)); cards.add(new SetCardInfo("Prodigal Sorcerer", 94, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); cards.add(new SetCardInfo("Psionic Entity", 95, Rarity.RARE, mage.cards.p.PsionicEntity.class)); diff --git a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java index f5c1f066061..4a87e3baacc 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java @@ -209,6 +209,7 @@ public class LimitedEditionAlpha extends ExpansionSet { cards.add(new SetCardInfo("Plains", 285, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 286, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plateau", 287, Rarity.RARE, mage.cards.p.Plateau.class)); + cards.add(new SetCardInfo("Power Leak", 72, Rarity.COMMON, mage.cards.p.PowerLeak.class)); cards.add(new SetCardInfo("Power Sink", 73, Rarity.COMMON, mage.cards.p.PowerSink.class)); cards.add(new SetCardInfo("Power Surge", 168, Rarity.RARE, mage.cards.p.PowerSurge.class)); cards.add(new SetCardInfo("Prodigal Sorcerer", 74, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); diff --git a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java index ff30f36357b..aed946ed8a6 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java @@ -214,6 +214,7 @@ public class LimitedEditionBeta extends ExpansionSet { cards.add(new SetCardInfo("Plains", 289, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 290, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plateau", 280, Rarity.RARE, mage.cards.p.Plateau.class)); + cards.add(new SetCardInfo("Power Leak", 72, Rarity.COMMON, mage.cards.p.PowerLeak.class)); cards.add(new SetCardInfo("Power Sink", 73, Rarity.COMMON, mage.cards.p.PowerSink.class)); cards.add(new SetCardInfo("Power Surge", 168, Rarity.RARE, mage.cards.p.PowerSurge.class)); cards.add(new SetCardInfo("Prodigal Sorcerer", 74, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); diff --git a/Mage.Sets/src/mage/sets/RevisedEdition.java b/Mage.Sets/src/mage/sets/RevisedEdition.java index a798e9d1fcd..5a07e32a60c 100644 --- a/Mage.Sets/src/mage/sets/RevisedEdition.java +++ b/Mage.Sets/src/mage/sets/RevisedEdition.java @@ -220,6 +220,7 @@ public class RevisedEdition extends ExpansionSet { cards.add(new SetCardInfo("Plains", 294, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 295, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plateau", 296, Rarity.RARE, mage.cards.p.Plateau.class)); + cards.add(new SetCardInfo("Power Leak", 73, Rarity.COMMON, mage.cards.p.PowerLeak.class)); cards.add(new SetCardInfo("Power Sink", 74, Rarity.COMMON, mage.cards.p.PowerSink.class)); cards.add(new SetCardInfo("Power Surge", 169, Rarity.RARE, mage.cards.p.PowerSurge.class)); cards.add(new SetCardInfo("Primal Clay", 271, Rarity.RARE, mage.cards.p.PrimalClay.class)); diff --git a/Mage.Sets/src/mage/sets/UnlimitedEdition.java b/Mage.Sets/src/mage/sets/UnlimitedEdition.java index e12d2c0d435..c1213ef3314 100644 --- a/Mage.Sets/src/mage/sets/UnlimitedEdition.java +++ b/Mage.Sets/src/mage/sets/UnlimitedEdition.java @@ -214,6 +214,7 @@ public class UnlimitedEdition extends ExpansionSet { cards.add(new SetCardInfo("Plains", 290, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 291, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plateau", 292, Rarity.RARE, mage.cards.p.Plateau.class)); + cards.add(new SetCardInfo("Power Leak", 72, Rarity.COMMON, mage.cards.p.PowerLeak.class)); cards.add(new SetCardInfo("Power Sink", 73, Rarity.COMMON, mage.cards.p.PowerSink.class)); cards.add(new SetCardInfo("Power Surge", 168, Rarity.RARE, mage.cards.p.PowerSurge.class)); cards.add(new SetCardInfo("Prodigal Sorcerer", 74, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); From 005fcd1040ba2b0cc7d7bad4f354397a5e90eb0a Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 19:00:35 +0100 Subject: [PATCH 010/133] Implemented Reverse Polarity --- .../src/mage/cards/r/ReversePolarity.java | 135 ++++++++++++++++++ Mage.Sets/src/mage/sets/Antiquities.java | 1 + Mage.Sets/src/mage/sets/RevisedEdition.java | 1 + 3 files changed, 137 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/ReversePolarity.java diff --git a/Mage.Sets/src/mage/cards/r/ReversePolarity.java b/Mage.Sets/src/mage/cards/r/ReversePolarity.java new file mode 100644 index 00000000000..8e6766f700c --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReversePolarity.java @@ -0,0 +1,135 @@ +/* + * 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.r; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +/** + * + * @author jeffwadsworth, MTGFan & L_J + */ +public class ReversePolarity extends CardImpl { + + public ReversePolarity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{W}"); + + // You gain X life, where X is twice the damage dealt to you so far this turn by artifacts. + this.getSpellAbility().addEffect(new GainLifeEffect(new ReversePolarityAmount(), "You gain X life, where X is twice the damage dealt to you so far this turn by artifacts")); + this.getSpellAbility().addWatcher(new ReversePolarityWatcher()); + } + + public ReversePolarity(final ReversePolarity card) { + super(card); + } + + @Override + public ReversePolarity copy() { + return new ReversePolarity(this); + } +} + +class ReversePolarityAmount implements DynamicValue { + + @Override + public int calculate(Game game, Ability source, Effect effect) { + ReversePolarityWatcher watcher = (ReversePolarityWatcher) game.getState().getWatchers().get(ReversePolarityWatcher.class.getSimpleName()); + if(watcher != null) { + return watcher.getArtifactDamageReceivedThisTurn(source.getControllerId()) * 2; + } + return 0; + } + + @Override + public ReversePolarityAmount copy() { + return new ReversePolarityAmount(); + } + + @Override + public String getMessage() { + return ""; + } +} + +class ReversePolarityWatcher extends Watcher { + + private final Map artifactDamageReceivedThisTurn = new HashMap<>(); + + public ReversePolarityWatcher() { + super(ReversePolarityWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public ReversePolarityWatcher(final ReversePolarityWatcher watcher) { + super(watcher); + for (Entry entry : watcher.artifactDamageReceivedThisTurn.entrySet()) { + artifactDamageReceivedThisTurn.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { + UUID playerId = event.getTargetId(); + if (playerId != null) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null && permanent.isArtifact()) { + artifactDamageReceivedThisTurn.putIfAbsent(playerId, 0); + artifactDamageReceivedThisTurn.compute(playerId, (k, v) -> v + event.getAmount()); + } + } + } + } + + public int getArtifactDamageReceivedThisTurn(UUID playerId) { + return artifactDamageReceivedThisTurn.getOrDefault(playerId, 0); + } + + @Override + public void reset() { + artifactDamageReceivedThisTurn.clear(); + } + + @Override + public ReversePolarityWatcher copy() { + return new ReversePolarityWatcher(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Antiquities.java b/Mage.Sets/src/mage/sets/Antiquities.java index e64f3108eaa..80b2acc03fe 100644 --- a/Mage.Sets/src/mage/sets/Antiquities.java +++ b/Mage.Sets/src/mage/sets/Antiquities.java @@ -111,6 +111,7 @@ public class Antiquities extends ExpansionSet { cards.add(new SetCardInfo("Primal Clay", 26, Rarity.UNCOMMON, mage.cards.p.PrimalClay.class)); cards.add(new SetCardInfo("Rakalite", 27, Rarity.UNCOMMON, mage.cards.r.Rakalite.class)); cards.add(new SetCardInfo("Reconstruction", 56, Rarity.COMMON, mage.cards.r.Reconstruction.class)); + cards.add(new SetCardInfo("Reverse Polarity", 100, Rarity.COMMON, mage.cards.r.ReversePolarity.class)); cards.add(new SetCardInfo("Rocket Launcher", 28, Rarity.UNCOMMON, mage.cards.r.RocketLauncher.class)); cards.add(new SetCardInfo("Sage of Lat-Nam", 57, Rarity.COMMON, mage.cards.s.SageOfLatNam.class)); cards.add(new SetCardInfo("Shapeshifter", 29, Rarity.RARE, mage.cards.s.Shapeshifter.class)); diff --git a/Mage.Sets/src/mage/sets/RevisedEdition.java b/Mage.Sets/src/mage/sets/RevisedEdition.java index 5a07e32a60c..74924028acb 100644 --- a/Mage.Sets/src/mage/sets/RevisedEdition.java +++ b/Mage.Sets/src/mage/sets/RevisedEdition.java @@ -235,6 +235,7 @@ public class RevisedEdition extends ExpansionSet { cards.add(new SetCardInfo("Regrowth", 121, Rarity.UNCOMMON, mage.cards.r.Regrowth.class)); cards.add(new SetCardInfo("Resurrection", 218, Rarity.UNCOMMON, mage.cards.r.Resurrection.class)); cards.add(new SetCardInfo("Reverse Damage", 219, Rarity.RARE, mage.cards.r.ReverseDamage.class)); + cards.add(new SetCardInfo("Reverse Polarity", 220, Rarity.UNCOMMON, mage.cards.r.ReversePolarity.class)); cards.add(new SetCardInfo("Righteousness", 221, Rarity.RARE, mage.cards.r.Righteousness.class)); cards.add(new SetCardInfo("Roc of Kher Ridges", 171, Rarity.RARE, mage.cards.r.RocOfKherRidges.class)); cards.add(new SetCardInfo("Rock Hydra", 172, Rarity.RARE, mage.cards.r.RockHydra.class)); From 3a800dea8e77c37c9665906d5f24fb760559c785 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 18:41:23 +0000 Subject: [PATCH 011/133] Added missing Goblin Rock Sled --- Mage.Sets/src/mage/sets/FourthEdition.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/FourthEdition.java b/Mage.Sets/src/mage/sets/FourthEdition.java index 17ca81da470..0b5d5d5f421 100644 --- a/Mage.Sets/src/mage/sets/FourthEdition.java +++ b/Mage.Sets/src/mage/sets/FourthEdition.java @@ -264,6 +264,7 @@ public class FourthEdition extends ExpansionSet { cards.add(new SetCardInfo("Giant Strength", 196, Rarity.COMMON, mage.cards.g.GiantStrength.class)); cards.add(new SetCardInfo("Goblin Balloon Brigade", 197, Rarity.UNCOMMON, mage.cards.g.GoblinBalloonBrigade.class)); cards.add(new SetCardInfo("Goblin King", 198, Rarity.RARE, mage.cards.g.GoblinKing.class)); + cards.add(new SetCardInfo("Goblin Rock Sled", 199, Rarity.COMMON, mage.cards.g.GoblinRockSled.class)); cards.add(new SetCardInfo("Gray Ogre", 200, Rarity.COMMON, mage.cards.g.GrayOgre.class)); cards.add(new SetCardInfo("Hill Giant", 201, Rarity.COMMON, mage.cards.h.HillGiant.class)); cards.add(new SetCardInfo("Hurloon Minotaur", 202, Rarity.COMMON, mage.cards.h.HurloonMinotaur.class)); From 17800b6df158a9e0b0b8e585ad47954770987b60 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 22:25:27 +0000 Subject: [PATCH 012/133] Menace fix --- .../java/mage/game/combat/CombatGroup.java | 57 ++++++++----------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 57b9877d491..350dfd4e6f6 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -302,7 +302,7 @@ public class CombatGroup implements Serializable, Copyable { Map assigned = new HashMap<>(); if (blocked) { boolean excessDamageToDefender = true; - for (UUID blockerId : new ArrayList<>(blockerOrder)) { // prevent ConcurrentModificationException + for (UUID blockerId : blockerOrder) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { int lethalDamage; @@ -732,42 +732,35 @@ public class CombatGroup implements Serializable, Copyable { for (UUID uuid : attackers) { Permanent attacker = game.getPermanent(uuid); - // Check if there are enough blockers to have a legal block - if (attacker != null && this.blocked && attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) { - for (UUID blockerId : blockers) { - Permanent blocker = game.getPermanent(blockerId); - if (blocker != null) { - blocker.setBlocking(blocker.getBlocking() - 1); + if (attacker != null && this.blocked) { + // Check if there are enough blockers to have a legal block + if (attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) { + for (UUID blockerId : new ArrayList<>(blockers)) { + game.getCombat().removeBlocker(blockerId, game); } - } - blockers.clear(); - blockerOrder.clear(); - this.blocked = false; - if (!game.isSimulation()) { - game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded."); - } - blockWasLegal = false; - } - // Check if there are too many blockers (maxBlockedBy = 0 means no restrictions) - if (attacker != null && this.blocked && attacker.getMaxBlockedBy() > 0 && attacker.getMaxBlockedBy() < blockers.size()) { - for (UUID blockerId : blockers) { - Permanent blocker = game.getPermanent(blockerId); - if (blocker != null) { - blocker.setBlocking(blocker.getBlocking() - 1); + blockers.clear(); + blockerOrder.clear(); + if (!game.isSimulation()) { + game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded."); } + blockWasLegal = false; } - blockers.clear(); - blockerOrder.clear(); - this.blocked = false; - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(attacker.getLogName()) - .append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy()) - .append(attacker.getMaxBlockedBy() == 1 ? " creature." : " creatures.") - .append(" Blockers discarded.").toString()); + // Check if there are too many blockers (maxBlockedBy = 0 means no restrictions) + if (attacker.getMaxBlockedBy() > 0 && attacker.getMaxBlockedBy() < blockers.size()) { + for (UUID blockerId : new ArrayList<>(blockers)) { + game.getCombat().removeBlocker(blockerId, game); + } + blockers.clear(); + blockerOrder.clear(); + if (!game.isSimulation()) { + game.informPlayers(new StringBuilder(attacker.getLogName()) + .append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy()) + .append(attacker.getMaxBlockedBy() == 1 ? " creature." : " creatures.") + .append(" Blockers discarded.").toString()); + } + blockWasLegal = false; } - blockWasLegal = false; } - } return blockWasLegal; } From 1f5e2f1bede7996e3abfdf4c7d940ba0899aae02 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 22:27:07 +0000 Subject: [PATCH 013/133] Minor revert --- Mage/src/main/java/mage/game/combat/CombatGroup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 350dfd4e6f6..c66dcd0c06d 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -302,7 +302,7 @@ public class CombatGroup implements Serializable, Copyable { Map assigned = new HashMap<>(); if (blocked) { boolean excessDamageToDefender = true; - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : new ArrayList<>(blockerOrder)) { // prevent ConcurrentModificationException Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { int lethalDamage; From 2e827a50ecfabac0524f719feaab13e9483a4a18 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 17 Feb 2018 23:13:19 +0000 Subject: [PATCH 014/133] Hellcarver Demon fix Was able to cast previously exiled cards --- .../src/mage/cards/h/HellcarverDemon.java | 118 +++++++----------- 1 file changed, 43 insertions(+), 75 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HellcarverDemon.java b/Mage.Sets/src/mage/cards/h/HellcarverDemon.java index 1032ae1f699..af8462e4b7e 100644 --- a/Mage.Sets/src/mage/cards/h/HellcarverDemon.java +++ b/Mage.Sets/src/mage/cards/h/HellcarverDemon.java @@ -27,35 +27,34 @@ */ package mage.cards.h; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterNonlandCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetCardInExile; -import mage.target.targetpointer.FixedTarget; +import mage.target.TargetCard; /** * - * @author jeffwadsworth + * @author jeffwadsworth & L_J */ public class HellcarverDemon extends CardImpl { @@ -84,9 +83,6 @@ public class HellcarverDemon extends CardImpl { class HellcarverDemonEffect extends OneShotEffect { - private static final FilterControlledPermanent filterPermanents = new FilterControlledPermanent("Permanent"); - private static FilterNonlandCard filter = new FilterNonlandCard("nonland card exiled with Hellcarver Demon"); - public HellcarverDemonEffect() { super(Outcome.PlayForFree); staticText = "sacrifice all other permanents you control and discard your hand. Exile the top six cards of your library. You may cast any number of nonland cards exiled this way without paying their mana costs."; @@ -99,41 +95,43 @@ class HellcarverDemonEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent hellcarverDemon = game.getPermanent(source.getSourceId()); - - for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanents, source.getControllerId(), game)) { - if (!Objects.equals(permanent, hellcarverDemon)) { - permanent.sacrifice(source.getSourceId(), game); - } - } - - if (controller != null && !controller.getHand().isEmpty()) { - int cardsInHand = controller.getHand().size(); - controller.discard(cardsInHand, false, source, game); - } - - for (int i = 0; i < 6; i++) { - if (controller != null - && controller.getLibrary().hasCards()) { - Card topCard = controller.getLibrary().getFromTop(game); - topCard.moveToExile(source.getSourceId(), "Cards exiled by Hellcarver Demon", source.getSourceId(), game); - } - } - - while (controller != null - && controller.canRespond() - && controller.chooseUse(Outcome.PlayForFree, controller.getLogName() + " can cast another nonland card exiled with Hellcarver Demon without paying that card's mana cost.", source, game)) { - TargetCardInExile target = new TargetCardInExile(filter, source.getSourceId()); - while (controller.chooseUse(Outcome.PlayForFree, "Cast another spell exiled by Hellcarver Demon?", source, game)) { - controller.choose(Outcome.PlayForFree, game.getExile().getExileZone(source.getSourceId()), target, game); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - ContinuousEffect effect = new HellcarverDemonCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - controller.cast(card.getSpellAbility(), game, true); + Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null && sourceObject != null) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + if (!Objects.equals(permanent, sourceObject)) { + permanent.sacrifice(source.getSourceId(), game); + } + } + if (!controller.getHand().isEmpty()) { + int cardsInHand = controller.getHand().size(); + controller.discard(cardsInHand, false, source, game); + } + // move cards from library to exile + Set currentExiledCards = new HashSet<>(); + currentExiledCards.addAll(controller.getLibrary().getTopCards(game, 6)); + controller.moveCardsToExile(currentExiledCards, source, game, true, source.getSourceId(), sourceObject.getIdName()); + + // cast the possible cards without paying the mana + Cards cardsToCast = new CardsImpl(); + cardsToCast.addAll(currentExiledCards); + boolean alreadyCast = false; + while (!cardsToCast.isEmpty() + && controller.canRespond()) { + if (!controller.chooseUse(outcome, "Cast a" + (alreadyCast ? "nother" : "" ) + " card exiled with " + sourceObject.getLogName() + " without paying its mana cost?", source, game)) { + break; + } + TargetCard targetCard = new TargetCard(1, Zone.EXILED, new FilterCard("nonland card to cast for free")); + if (controller.choose(Outcome.PlayForFree, cardsToCast, targetCard, game)) { + alreadyCast = true; + Card card = game.getCard(targetCard.getFirstTarget()); + if (card != null) { + if (controller.cast(card.getSpellAbility(), game, true)) { + cardsToCast.remove(card); + } else { + game.informPlayer(controller, "You're not able to cast " + card.getIdName() + " or you canceled the casting."); + } + } } - target.clearChosen(); } return true; } @@ -145,33 +143,3 @@ class HellcarverDemonEffect extends OneShotEffect { return new HellcarverDemonEffect(this); } } - -class HellcarverDemonCastFromExileEffect extends AsThoughEffectImpl { - - public HellcarverDemonCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "You may play the card from exile without paying its mana cost"; - } - - public HellcarverDemonCastFromExileEffect(final HellcarverDemonCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public HellcarverDemonCastFromExileEffect copy() { - return new HellcarverDemonCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (targetPointer.getTargets(game, source).contains(sourceId)) { - return game.getState().getZone(sourceId) == Zone.EXILED; - } - return false; - } -} From d80d58896379dc6eea8cd4c7f83a3aa5854cec1f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 18 Feb 2018 02:52:16 +0100 Subject: [PATCH 015/133] * Reworked flashback ability (fixes #4482 #3324 #215 #3435 #3883 #3337 #2183 #2447). --- Mage.Sets/src/mage/cards/c/Conflagrate.java | 5 +- .../abilities/keywords/FlashbackTest.java | 36 ++++- .../main/java/mage/abilities/AbilityImpl.java | 14 +- .../java/mage/abilities/SpellAbility.java | 9 +- .../abilities/costs/VariableCostImpl.java | 2 - .../abilities/keyword/FlashbackAbility.java | 150 ++++++------------ Mage/src/main/java/mage/cards/CardImpl.java | 2 +- .../mage/constants/SpellAbilityCastMode.java | 3 +- .../main/java/mage/players/PlayerImpl.java | 8 +- 9 files changed, 107 insertions(+), 122 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/Conflagrate.java b/Mage.Sets/src/mage/cards/c/Conflagrate.java index 11e5cf6b274..70289727353 100644 --- a/Mage.Sets/src/mage/cards/c/Conflagrate.java +++ b/Mage.Sets/src/mage/cards/c/Conflagrate.java @@ -30,7 +30,6 @@ package mage.cards.c; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; -import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.common.DiscardXTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; @@ -82,8 +81,8 @@ class ConflagrateVariableValue implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { int xValue = sourceAbility.getManaCostsToPay().getX(); for (Cost cost : sourceAbility.getCosts()) { - if (cost instanceof DiscardTargetCost) { - xValue = ((DiscardTargetCost) cost).getCards().size(); + if (cost instanceof DiscardXTargetCost) { + xValue = ((DiscardXTargetCost) cost).getAmount(); } } return xValue; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java index 30f0d24fb93..02bf64326b3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java @@ -27,9 +27,9 @@ */ package org.mage.test.cards.abilities.keywords; +import mage.abilities.keyword.TrampleAbility; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -39,6 +39,27 @@ import org.mage.test.serverside.base.CardTestPlayerBase; */ public class FlashbackTest extends CardTestPlayerBase { + @Test + public void testNormalWildHunger() { + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + // Target creature gets +3/+1 and gains trample until end of turn. + // Flashback {3}{R} + addCard(Zone.GRAVEYARD, playerA, "Wild Hunger"); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Silvercoat Lion", 5, 3); + assertAbility(playerA, "Silvercoat Lion", TrampleAbility.getInstance(), true); + assertExileCount("Wild Hunger", 1); + } + /** * Fracturing Gust is bugged. In a match against Affinity, it worked * properly when cast from hand. When I cast it from graveyard c/o @@ -219,7 +240,7 @@ public class FlashbackTest extends CardTestPlayerBase { // Conflagrate deals X damage divided as you choose among any number of target creatures and/or players. // Flashback-{R}{R}, Discard X cards. - addCard(Zone.HAND, playerA, "Conflagrate", 1); + addCard(Zone.HAND, playerA, "Conflagrate", 1); // Sorcery {X}{X}{R} addCard(Zone.HAND, playerA, "Forest", 4); @@ -307,20 +328,26 @@ public class FlashbackTest extends CardTestPlayerBase { public void testAltarsReap() { addCard(Zone.LIBRARY, playerA, "Island", 2); - addCard(Zone.GRAVEYARD, playerA, "Altar's Reap", 1); + // As an additional cost to cast Altar's Reap, sacrifice a creature. + // Draw two cards. + addCard(Zone.GRAVEYARD, playerA, "Altar's Reap", 1); // Instant {1}{B} addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4); addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + // Flash + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. + // The flashback cost is equal to its mana cost. castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); setChoice(playerA, "Altar's Reap"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {1}{B}"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback"); setChoice(playerA, "Snapcaster Mage"); setStopAt(1, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerA, "Snapcaster Mage", 1); + assertExileCount(playerA, "Altar's Reap", 1); } /** @@ -520,7 +547,6 @@ public class FlashbackTest extends CardTestPlayerBase { * to a spell, and the flashback cost is already an alternative cost. */ @Test - @Ignore public void testSnapcasterMageSpellWithAlternateCost() { addCard(Zone.BATTLEFIELD, playerA, "Island", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 8caf2db48c3..23f81762129 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -44,7 +44,6 @@ import mage.abilities.effects.Effects; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DynamicManaEffect; import mage.abilities.effects.common.ManaEffect; -import mage.abilities.keyword.FlashbackAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.Card; import mage.cards.SplitCard; @@ -337,9 +336,7 @@ public abstract class AbilityImpl implements Ability { if (sourceObject != null && this.getAbilityType() != AbilityType.TRIGGERED) { // triggered abilities check this already in playerImpl.triggerAbility sourceObject.adjustTargets(this, game); } - // Flashback abilities haven't made the choices the underlying spell might need for targeting. - if (!(this instanceof FlashbackAbility) - && !getTargets().isEmpty()) { + if (!getTargets().isEmpty()) { Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome(); if (getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game) == false) { if ((variableManaCost != null || announceString != null)) { @@ -445,8 +442,15 @@ public abstract class AbilityImpl implements Ability { @Override public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) { + if (this instanceof SpellAbility) { + if (((SpellAbility) this).getSpellAbilityCastMode() != SpellAbilityCastMode.NORMAL) { + // A player can't apply two alternative methods of casting or two alternative costs to a single spell. + // So can only use alternate costs if the spell is cast in normal mode + return false; + } + } boolean alternativeCostisUsed = false; - if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) { + if (sourceObject != null && !(sourceObject instanceof Permanent)) { Abilities abilities = null; if (sourceObject instanceof Card) { abilities = ((Card) sourceObject).getAbilities(game); diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index 2cd291a957a..289fa05592e 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -217,7 +217,7 @@ public class SpellAbility extends ActivatedAbilityImpl { this.name = "Cast fused " + cardName; break; default: - this.name = "Cast " + cardName + (this.spellAbilityCastMode != SpellAbilityCastMode.NORMAL ? " by " + spellAbilityCastMode.toString() : ""); + this.name = "Cast " + cardName + (this.spellAbilityCastMode != SpellAbilityCastMode.NORMAL ? " using " + spellAbilityCastMode.toString() : ""); } } @@ -230,4 +230,11 @@ public class SpellAbility extends ActivatedAbilityImpl { setSpellName(); } + public SpellAbility getSpellAbilityToResolve(Game game) { + return this; + } + + public void setId(UUID idToUse) { + this.id = idToUse; + } } diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java index aefaf6f5269..6b96444bd20 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java @@ -29,7 +29,6 @@ package mage.abilities.costs; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.keyword.FlashbackAbility; import mage.abilities.mana.ManaAbility; import mage.game.Game; import mage.game.stack.StackObject; @@ -173,7 +172,6 @@ public abstract class VariableCostImpl implements Cost, VariableCost { StackObject stackObject = game.getStack().getStackObject(source.getId()); if (controller != null && (source instanceof ManaAbility - || source instanceof FlashbackAbility || stackObject != null)) { xValue = controller.announceXCost(getMinValue(source, game), getMaxValue(source, game), "Announce the number of " + actionText, game, source, this); diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index c05f20118f5..aa457aea70a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -32,14 +32,13 @@ import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; -import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SpellAbilityCastMode; import mage.constants.SpellAbilityType; import mage.constants.TimingRule; import mage.constants.Zone; @@ -66,23 +65,21 @@ import mage.target.targetpointer.FixedTarget; public class FlashbackAbility extends SpellAbility { private String abilityName; + private SpellAbility spellAbilityToResolve; public FlashbackAbility(Cost cost, TimingRule timingRule) { - super(null, "", Zone.GRAVEYARD); + super(null, "", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE, SpellAbilityCastMode.FLASHBACK); this.setAdditionalCostsRuleVisible(false); this.name = "Flashback " + cost.getText(); - this.addEffect(new FlashbackEffect()); this.addCost(cost); this.timing = timingRule; - this.usesStack = false; - this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; - setCostModificationActive(false); } public FlashbackAbility(final FlashbackAbility ability) { super(ability); this.spellAbilityType = ability.spellAbilityType; this.abilityName = ability.abilityName; + this.spellAbilityToResolve = ability.spellAbilityToResolve; } @Override @@ -108,6 +105,47 @@ public class FlashbackAbility extends SpellAbility { return false; } + @Override + public SpellAbility getSpellAbilityToResolve(Game game) { + Card card = game.getCard(getSourceId()); + if (card != null) { + if (spellAbilityToResolve == null) { + SpellAbility spellAbilityCopy = null; + if (card.isSplitCard()) { + if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) { + spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy(); + } else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) { + spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy(); + } + } else { + spellAbilityCopy = card.getSpellAbility().copy(); + } + if (spellAbilityCopy == null) { + return null; + } + spellAbilityCopy.setId(this.getId()); + spellAbilityCopy.getManaCosts().clear(); + spellAbilityCopy.getManaCostsToPay().clear(); + spellAbilityCopy.getCosts().addAll(this.getCosts()); + spellAbilityCopy.addCost(this.getManaCosts()); + spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode()); + spellAbilityToResolve = spellAbilityCopy; + ContinuousEffect effect = new FlashbackReplacementEffect(); + effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId()))); + game.addEffect(effect, this); + } + } + return spellAbilityToResolve; + } + + @Override + public Costs getCosts() { + if (spellAbilityToResolve == null) { + return super.getCosts(); + } + return spellAbilityToResolve.getCosts(); + } + @Override public FlashbackAbility copy() { return new FlashbackAbility(this); @@ -144,102 +182,18 @@ public class FlashbackAbility extends SpellAbility { return sbRule.toString(); } - @Override - public void setSpellAbilityType(SpellAbilityType spellAbilityType) { - this.spellAbilityType = spellAbilityType; - } - - @Override - public SpellAbilityType getSpellAbilityType() { - return this.spellAbilityType; - } - + /** + * Used for split card sin PlayerImpl method: + * getOtherUseableActivatedAbilities + * + * @param abilityName + */ public void setAbilityName(String abilityName) { this.abilityName = abilityName; } } -class FlashbackEffect extends OneShotEffect { - - public FlashbackEffect() { - super(Outcome.Benefit); - staticText = ""; - } - - public FlashbackEffect(final FlashbackEffect effect) { - super(effect); - } - - @Override - public FlashbackEffect copy() { - return new FlashbackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = (Card) game.getObject(source.getSourceId()); - if (card != null) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - SpellAbility spellAbility; - switch (((FlashbackAbility) source).getSpellAbilityType()) { - case SPLIT_LEFT: - spellAbility = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy(); - break; - case SPLIT_RIGHT: - spellAbility = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy(); - break; - default: - spellAbility = card.getSpellAbility().copy(); - } - - spellAbility.clear(); - // set the payed flashback costs to the spell ability so abilities like Converge or calculation of {X} values work - spellAbility.getManaCostsToPay().clear(); - spellAbility.getManaCostsToPay().addAll(source.getManaCosts()); - spellAbility.getManaCosts().clear(); - spellAbility.getManaCosts().addAll(source.getManaCosts()); - // needed to get e.g. paid costs from Conflagrate - - for (Cost cost : source.getCosts()) { - if (cost instanceof Costs) { - Costs listOfCosts = (Costs) cost; - for (Cost singleCost : listOfCosts) { - if (singleCost instanceof ManaCost) { - singleCost.clearPaid(); - spellAbility.getManaCosts().add((ManaCost) singleCost); - spellAbility.getManaCostsToPay().add((ManaCost) singleCost); - } else { - spellAbility.getCosts().add(singleCost); - } - } - - } else { - if (cost instanceof ManaCost) { - spellAbility.getManaCosts().add((ManaCost) cost); - spellAbility.getManaCostsToPay().add((ManaCost) cost); - } else { - spellAbility.getCosts().add(cost); - } - } - } - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " flashbacks " + card.getLogName()); - } - if (controller.cast(spellAbility, game, false)) { - ContinuousEffect effect = new FlashbackReplacementEffect(); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()))); - game.addEffect(effect, source); - } - return true; - } - } - return false; - } - -} - class FlashbackReplacementEffect extends ReplacementEffectImpl { public FlashbackReplacementEffect() { @@ -287,7 +241,7 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl { && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { int zcc = game.getState().getZoneChangeCounter(source.getSourceId()); - if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() == zcc) { + if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc) { return true; } diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index f82868e628b..4fc6a220e94 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -516,7 +516,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { Card mainCard = getMainCard(); ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK); ZoneChangeInfo.Stack info - = new ZoneChangeInfo.Stack(event, new Spell(this, ability.copy(), controllerId, event.getFromZone())); + = new ZoneChangeInfo.Stack(event, new Spell(this, ability.getSpellAbilityToResolve(game), controllerId, event.getFromZone())); return ZonesHandler.cast(info, game); } diff --git a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java index 663c8c8f1bf..71aecf37ed5 100644 --- a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java +++ b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java @@ -33,7 +33,8 @@ package mage.constants; */ public enum SpellAbilityCastMode { NORMAL("Normal"), - MADNESS("Madness"); + MADNESS("Madness"), + FLASHBACK("Flashback"); private final String text; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index ceb43d787f0..a61e8023f3b 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1158,7 +1158,7 @@ public abstract class PlayerImpl implements Player, Serializable { } } else { int bookmark = game.bookmarkState(); - if (ability.activate(game, ability instanceof FlashbackAbility)) { + if (ability.activate(game, false)) { ability.resolve(game); game.removeBookmark(bookmark); resetStoredBookmark(game); @@ -1219,11 +1219,7 @@ public abstract class PlayerImpl implements Player, Serializable { result = playManaAbility((ActivatedManaAbilityImpl) ability.copy(), game); break; case SPELL: - if (ability instanceof FlashbackAbility) { - result = playAbility(ability.copy(), game); - } else { - result = cast((SpellAbility) ability, game, false); - } + result = cast((SpellAbility) ability.copy(), game, false); break; default: result = playAbility(ability.copy(), game); From 71ed488c1e06d9739f37b52142bfd9ff719c3ef0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 18 Feb 2018 18:31:10 +0100 Subject: [PATCH 016/133] * Some deck format tests changed/added. --- .../mage/sets/MasterpieceSeriesAmonkhet.java | 3 - .../serverside/deck/DeckValidatorTest.java | 179 ++++++++++++++++-- 2 files changed, 165 insertions(+), 17 deletions(-) diff --git a/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java b/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java index 291c6782fef..d526453921f 100644 --- a/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java +++ b/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java @@ -27,9 +27,7 @@ */ package mage.sets; -import mage.cards.CardGraphicInfo; import mage.cards.ExpansionSet; -import mage.cards.FrameStyle; import mage.constants.Rarity; import mage.constants.SetType; @@ -50,7 +48,6 @@ public class MasterpieceSeriesAmonkhet extends ExpansionSet { this.blockName = "Masterpiece Series"; this.hasBoosters = false; this.hasBasicLands = false; - CardGraphicInfo cardGraphicInfo = new CardGraphicInfo(FrameStyle.KLD_INVENTION, false); cards.add(new SetCardInfo("Aggravated Assault", 25, Rarity.SPECIAL, mage.cards.a.AggravatedAssault.class)); cards.add(new SetCardInfo("Armageddon", 31, Rarity.SPECIAL, mage.cards.a.Armageddon.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java index f7bf2fe462e..38d002d5240 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java @@ -27,24 +27,24 @@ */ package org.mage.test.serverside.deck; +import java.util.ArrayList; +import java.util.List; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.deck.Limited; import mage.deck.Modern; +import mage.deck.Standard; import org.junit.Assert; import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -import java.util.ArrayList; -import java.util.List; +import org.mage.test.serverside.base.MageTestBase; /** * * @author LevelX2 */ -public class DeckValidatorTest extends CardTestPlayerBase { +public class DeckValidatorTest extends MageTestBase { static class CardNameAmount { @@ -84,6 +84,38 @@ public class DeckValidatorTest extends CardTestPlayerBase { } + @Test + public void testStandardValid() { + ArrayList deck = new ArrayList<>(); + + deck.add(new CardNameAmount("MPS-AKH", 28, 4)); // Rhonas the Indomitable + deck.add(new CardNameAmount("Built to Smash", 4)); + deck.add(new CardNameAmount("Heroic Intervention", 4)); + deck.add(new CardNameAmount("Mountain", 48)); + + DeckValidator validator = new Standard(); + boolean validationSuccessful = testDeckValid(validator, deck); + Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); + } + + @Test + public void testStandardNotValid() { + ArrayList deck = new ArrayList<>(); + + deck.add(new CardNameAmount("MPS-AKH", 28, 4)); // Rhonas the Indomitable + deck.add(new CardNameAmount("Built to Smash", 4)); + deck.add(new CardNameAmount("Heroic Intervention", 4)); + deck.add(new CardNameAmount("Mountain", 47)); + + ArrayList sideboard = new ArrayList<>(); + sideboard.add(new CardNameAmount("Mountain", 16)); + + DeckValidator validator = new Standard(); + testDeckValid(validator, deck, sideboard); + Assert.assertEquals("invalid message not correct", + "{Sideboard=Must contain no more than 15 cards : has 16 cards, Deck=Must contain at least 60 cards: has only 59 cards}", validator.getInvalid().toString()); + } + @Test public void testLimitedValid() { ArrayList deck = new ArrayList<>(); @@ -226,37 +258,156 @@ public class DeckValidatorTest extends CardTestPlayerBase { @Test public void testModernBanned() { ArrayList deckList = new ArrayList<>(); + DeckValidator validator = new Modern(); + deckList.add(new CardNameAmount("Ancestral Vision", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + boolean validationSuccessful = testDeckValid(validator, deckList); + Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Ancient Den", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + deckList.add(new CardNameAmount("Birthing Pod", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Blazing Shoal", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Bloodbraid Elf", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Chrome Mox", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Cloudpost", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Dark Depths", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Deathrite Shaman", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Dig Through Time", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Dread Return", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Glimpse of Nature", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Great Furnace", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Green Sun's Zenith", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Hypergenesis", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Jace, the Mind Sculptor", 4)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); + + deckList.clear(); deckList.add(new CardNameAmount("Mental Misstep", 4)); - Assert.assertFalse("banned cards are not allowed", testDeckValid(new Modern(), deckList)); + deckList.add(new CardNameAmount("Mountain", 56)); + validationSuccessful = testDeckValid(validator, deckList); + Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful); + validator.getInvalid().clear(); } private boolean testDeckValid(DeckValidator validator, List cards) { + return testDeckValid(validator, cards, null); + } + + private boolean testDeckValid(DeckValidator validator, List cards, List cardsSideboard) { Deck deckToTest = new Deck(); - for (CardNameAmount cardNameAmount : cards) { - CardInfo cardinfo; - if (cardNameAmount.getName().isEmpty()) { - cardinfo = CardRepository.instance.findCard(cardNameAmount.getSetCode(), cardNameAmount.getCardNumber()); - } else { - cardinfo = CardRepository.instance.findCard(cardNameAmount.getName()); + if (cards != null) { + for (CardNameAmount cardNameAmount : cards) { + CardInfo cardinfo; + if (cardNameAmount.getName().isEmpty()) { + cardinfo = CardRepository.instance.findCard(cardNameAmount.getSetCode(), cardNameAmount.getCardNumber()); + } else { + cardinfo = CardRepository.instance.findCard(cardNameAmount.getName()); + } + for (int i = 0; i < cardNameAmount.getNumber(); i++) { + deckToTest.getCards().add(cardinfo.getCard()); + } } - for (int i = 0; i < cardNameAmount.getNumber(); i++) { - deckToTest.getCards().add(cardinfo.getCard()); + } + if (cardsSideboard != null) { + for (CardNameAmount cardNameAmount : cardsSideboard) { + CardInfo cardinfo; + if (cardNameAmount.getName().isEmpty()) { + cardinfo = CardRepository.instance.findCard(cardNameAmount.getSetCode(), cardNameAmount.getCardNumber()); + } else { + cardinfo = CardRepository.instance.findCard(cardNameAmount.getName()); + } + for (int i = 0; i < cardNameAmount.getNumber(); i++) { + deckToTest.getSideboard().add(cardinfo.getCard()); + } } } return validator.validate(deckToTest); From f0ee60eedb3bd027492e3112cbccca93323e04cb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 18 Feb 2018 18:31:48 +0100 Subject: [PATCH 017/133] * Fixed some possible null pointer exceptions seen in xmage.de server log. --- Mage.Sets/src/mage/cards/d/DreamThief.java | 12 ++++++--- Mage.Sets/src/mage/cards/p/Panharmonicon.java | 8 +++--- .../mage/cards/t/TreacherousPitDweller.java | 25 +++++++++++-------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DreamThief.java b/Mage.Sets/src/mage/cards/d/DreamThief.java index 79338e861ce..d8d5364b3f8 100644 --- a/Mage.Sets/src/mage/cards/d/DreamThief.java +++ b/Mage.Sets/src/mage/cards/d/DreamThief.java @@ -27,6 +27,7 @@ */ package mage.cards.d; +import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -52,7 +53,7 @@ public class DreamThief extends CardImpl { private static final String rule = "draw a card if you've cast another blue spell this turn"; public DreamThief(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.ROGUE); @@ -84,9 +85,12 @@ class CastBlueSpellThisTurnCondition implements Condition { public boolean apply(Game game, Ability source) { SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getSimpleName()); if (watcher != null) { - for (Spell spell : watcher.getSpellsCastThisTurn(source.getControllerId())) { - if (!spell.getSourceId().equals(source.getSourceId()) && spell.getColor(game).isBlue()) { - return true; + List spells = watcher.getSpellsCastThisTurn(source.getControllerId()); + if (spells != null) { + for (Spell spell : spells) { + if (!spell.getSourceId().equals(source.getSourceId()) && spell.getColor(game).isBlue()) { + return true; + } } } } diff --git a/Mage.Sets/src/mage/cards/p/Panharmonicon.java b/Mage.Sets/src/mage/cards/p/Panharmonicon.java index 4bade4472d6..26dfcafdf5e 100644 --- a/Mage.Sets/src/mage/cards/p/Panharmonicon.java +++ b/Mage.Sets/src/mage/cards/p/Panharmonicon.java @@ -50,7 +50,7 @@ import mage.game.events.NumberOfTriggersEvent; public class Panharmonicon extends CardImpl { public Panharmonicon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PanharmoniconEffect())); @@ -95,7 +95,9 @@ class PanharmoniconEffect extends ReplacementEffectImpl { if (source.getControllerId().equals(event.getPlayerId())) { GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); // Only EtB triggers - if (sourceEvent.getType() == EventType.ENTERS_THE_BATTLEFIELD && sourceEvent instanceof EntersTheBattlefieldEvent) { + if (sourceEvent != null + && sourceEvent.getType() == EventType.ENTERS_THE_BATTLEFIELD + && sourceEvent instanceof EntersTheBattlefieldEvent) { EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) sourceEvent; // Only for entering artifacts or creatures if (entersTheBattlefieldEvent.getTarget().isArtifact() @@ -116,4 +118,4 @@ class PanharmoniconEffect extends ReplacementEffectImpl { event.setAmount(event.getAmount() + 1); return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/t/TreacherousPitDweller.java b/Mage.Sets/src/mage/cards/t/TreacherousPitDweller.java index aeb1ccac8f9..16429b4734f 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousPitDweller.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousPitDweller.java @@ -29,6 +29,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.ContinuousEffectImpl; @@ -49,7 +50,7 @@ import mage.target.common.TargetOpponent; public class TreacherousPitDweller extends CardImpl { public TreacherousPitDweller(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}"); this.subtype.add(SubType.DEMON); this.power = new MageInt(4); @@ -77,24 +78,24 @@ class TreacherousPitDwellerTriggeredAbility extends TriggeredAbilityImpl { private static final String ruleText = "When {this} enters the battlefield from a graveyard, "; public TreacherousPitDwellerTriggeredAbility() { - super(Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(),false); + super(Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(), false); addTarget(new TargetOpponent()); } public TreacherousPitDwellerTriggeredAbility(final TreacherousPitDwellerTriggeredAbility ability) { super(ability); } - + @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; } - + @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(getSourceId()) && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.GRAVEYARD; - } - + return event.getTargetId().equals(getSourceId()) && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.GRAVEYARD; + } + @Override public TreacherousPitDwellerTriggeredAbility copy() { return new TreacherousPitDwellerTriggeredAbility(this); @@ -104,7 +105,7 @@ class TreacherousPitDwellerTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return ruleText + super.getRule(); } - + } class TreacherousPitDwellerEffect extends ContinuousEffectImpl { @@ -125,10 +126,12 @@ class TreacherousPitDwellerEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); + MageObject permanent = source.getSourceObjectIfItStillExists(game); // it can also return Card object Player targetOpponent = game.getPlayer(source.getFirstTarget()); - if (permanent != null && targetOpponent != null) { - return permanent.changeControllerId(targetOpponent.getId(), game); + if (permanent != null + && (permanent instanceof Permanent) + && targetOpponent != null) { + return ((Permanent) permanent).changeControllerId(targetOpponent.getId(), game); } else { discard(); } From a6644b0eb214c6beea694c74866580457d9b6556 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 18 Feb 2018 22:37:08 +0100 Subject: [PATCH 018/133] * Added Steamflogger Boss, Thunderblade Charge and Putrid Cyclops. --- Mage.Sets/src/mage/cards/p/PutridCyclops.java | 112 ++++++++++++++++++ .../src/mage/cards/s/SteamfloggerBoss.java | 83 +++++++++++++ .../src/mage/cards/t/ThunderbladeCharge.java | 104 ++++++++++++++++ Mage.Sets/src/mage/sets/FutureSight.java | 3 + Mage.Sets/src/mage/sets/Unstable.java | 1 + 5 files changed, 303 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/PutridCyclops.java create mode 100644 Mage.Sets/src/mage/cards/s/SteamfloggerBoss.java create mode 100644 Mage.Sets/src/mage/cards/t/ThunderbladeCharge.java diff --git a/Mage.Sets/src/mage/cards/p/PutridCyclops.java b/Mage.Sets/src/mage/cards/p/PutridCyclops.java new file mode 100644 index 00000000000..1dfe474b520 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PutridCyclops.java @@ -0,0 +1,112 @@ +/* + * 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.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class PutridCyclops extends CardImpl { + + public PutridCyclops(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.CYCLOPS); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Putrid Cyclops enters the battlefield, scry 1, then reveal the top card of your library. Putrid Cyclops gets -X/-X until end of turn, where X is that card's converted mana cost. + this.addAbility(new EntersBattlefieldTriggeredAbility(new PutridCyclopEffect())); + } + + public PutridCyclops(final PutridCyclops card) { + super(card); + } + + @Override + public PutridCyclops copy() { + return new PutridCyclops(this); + } +} + +class PutridCyclopEffect extends OneShotEffect { + + public PutridCyclopEffect() { + super(Outcome.Detriment); + this.staticText = "scry 1, then reveal the top card of your library. {this} gets -X/-X until end of turn, where X is that card's converted mana cost" + + " (To scry 1, look at the top card of your library, then you may put that card on the bottom of your library.)"; + } + + public PutridCyclopEffect(final PutridCyclopEffect effect) { + super(effect); + } + + @Override + public PutridCyclopEffect copy() { + return new PutridCyclopEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + new ScryEffect(1).apply(game, source); + Card card = controller.getLibrary().getFromTop(game); + if (card != null) { + controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); + int unboost = card.getConvertedManaCost() * -1; + ContinuousEffect effect = new BoostSourceEffect(unboost, unboost, Duration.EndOfTurn); + game.addEffect(effect, source); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteamfloggerBoss.java b/Mage.Sets/src/mage/cards/s/SteamfloggerBoss.java new file mode 100644 index 00000000000..d613079e9a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SteamfloggerBoss.java @@ -0,0 +1,83 @@ +/* + * 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.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import static mage.filter.predicate.permanent.ControllerControlsIslandPredicate.filter; + +/** + * + * @author LevelX2 + */ +public class SteamfloggerBoss extends CardImpl { + + public SteamfloggerBoss(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.RIGGER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Other Rigger creatures you control get +1/+0 and have haste. + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, + new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, new FilterCreaturePermanent(SubType.RIGGER, "Rigger creatures"), true)); + Effect effect = new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter); + effect.setText("and have haste"); + ability.addEffect(effect); + this.addAbility(ability); + + // If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead. (NOT IMPLEMENTED)"))); + + } + + public SteamfloggerBoss(final SteamfloggerBoss card) { + super(card); + } + + @Override + public SteamfloggerBoss copy() { + return new SteamfloggerBoss(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThunderbladeCharge.java b/Mage.Sets/src/mage/cards/t/ThunderbladeCharge.java new file mode 100644 index 00000000000..658c9920ecd --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThunderbladeCharge.java @@ -0,0 +1,104 @@ +/* + * 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.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author LevelX2 + */ +public class ThunderbladeCharge extends CardImpl { + + public ThunderbladeCharge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); + + // Thunderblade Charge deals 3 damage to target creature or player. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + + // Whenever one or more creatures you control deal combat damage to a player, if Thunderblade Charge is in your graveyard, you may pay {2}{R}{R}{R}. If you do, you may cast it without paying its mana cost. + this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone.GRAVEYARD, + new DoIfCostPaid(new ThunderbladeChargeCastEffect(), new ManaCostsImpl("{2}{R}{R}{R}")) + .setText("if {this} is in your graveyard, you may pay {2}{R}{R}{R}. If you do, you may cast it without paying its mana cost"))); + } + + public ThunderbladeCharge(final ThunderbladeCharge card) { + super(card); + } + + @Override + public ThunderbladeCharge copy() { + return new ThunderbladeCharge(this); + } +} + +class ThunderbladeChargeCastEffect extends OneShotEffect { + + public ThunderbladeChargeCastEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "you may cast {this} without paying its mana cost"; + } + + public ThunderbladeChargeCastEffect(final ThunderbladeChargeCastEffect effect) { + super(effect); + } + + @Override + public ThunderbladeChargeCastEffect copy() { + return new ThunderbladeChargeCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card sourceCard = game.getCard(source.getSourceId()); + if (controller != null + && sourceCard != null + && Zone.GRAVEYARD == game.getState().getZone(sourceCard.getId())) { + controller.cast(sourceCard.getSpellAbility(), game, true); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/FutureSight.java b/Mage.Sets/src/mage/sets/FutureSight.java index 9f578ac5ff6..a8669801cc5 100644 --- a/Mage.Sets/src/mage/sets/FutureSight.java +++ b/Mage.Sets/src/mage/sets/FutureSight.java @@ -171,6 +171,7 @@ public class FutureSight extends ExpansionSet { cards.add(new SetCardInfo("Petrified Plating", 133, Rarity.COMMON, mage.cards.p.PetrifiedPlating.class)); cards.add(new SetCardInfo("Phosphorescent Feast", 149, Rarity.UNCOMMON, mage.cards.p.PhosphorescentFeast.class)); cards.add(new SetCardInfo("Pooling Venom", 74, Rarity.UNCOMMON, mage.cards.p.PoolingVenom.class)); + cards.add(new SetCardInfo("Putrid Cyclops", 75, Rarity.COMMON, mage.cards.p.PutridCyclops.class)); cards.add(new SetCardInfo("Pyromancer's Swath", 104, Rarity.RARE, mage.cards.p.PyromancersSwath.class)); cards.add(new SetCardInfo("Quagnoth", 150, Rarity.RARE, mage.cards.q.Quagnoth.class)); cards.add(new SetCardInfo("Quiet Disrepair", 134, Rarity.COMMON, mage.cards.q.QuietDisrepair.class)); @@ -206,6 +207,7 @@ public class FutureSight extends ExpansionSet { cards.add(new SetCardInfo("Spirit en-Dal", 17, Rarity.UNCOMMON, mage.cards.s.SpiritEnDal.class)); cards.add(new SetCardInfo("Sporoloth Ancient", 152, Rarity.COMMON, mage.cards.s.SporolothAncient.class)); cards.add(new SetCardInfo("Sprout Swarm", 138, Rarity.COMMON, mage.cards.s.SproutSwarm.class)); + cards.add(new SetCardInfo("Steamflogger Boss", 121, Rarity.RARE, mage.cards.s.SteamfloggerBoss.class)); cards.add(new SetCardInfo("Storm Entity", 122, Rarity.UNCOMMON, mage.cards.s.StormEntity.class)); cards.add(new SetCardInfo("Street Wraith", 90, Rarity.UNCOMMON, mage.cards.s.StreetWraith.class)); cards.add(new SetCardInfo("Stronghold Rats", 79, Rarity.UNCOMMON, mage.cards.s.StrongholdRats.class)); @@ -215,6 +217,7 @@ public class FutureSight extends ExpansionSet { cards.add(new SetCardInfo("Tarmogoyf", 153, Rarity.RARE, mage.cards.t.Tarmogoyf.class)); cards.add(new SetCardInfo("Tarox Bladewing", 123, Rarity.RARE, mage.cards.t.TaroxBladewing.class)); cards.add(new SetCardInfo("Thornweald Archer", 154, Rarity.COMMON, mage.cards.t.ThornwealdArcher.class)); + cards.add(new SetCardInfo("Thunderblade Charge", 124, Rarity.RARE, mage.cards.t.ThunderbladeCharge.class)); cards.add(new SetCardInfo("Tolaria West", 173, Rarity.UNCOMMON, mage.cards.t.TolariaWest.class)); cards.add(new SetCardInfo("Tombstalker", 91, Rarity.RARE, mage.cards.t.Tombstalker.class)); cards.add(new SetCardInfo("Unblinking Bleb", 45, Rarity.COMMON, mage.cards.u.UnblinkingBleb.class)); diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index f675d4cbebb..7f1e655814f 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -75,6 +75,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class)); cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class)); + cards.add(new SetCardInfo("Steamflogger Boss", 93, Rarity.RARE, mage.cards.s.SteamfloggerBoss.class)); cards.add(new SetCardInfo("Steel Squirrel", 162, Rarity.UNCOMMON, mage.cards.s.SteelSquirrel.class)); cards.add(new SetCardInfo("Summon the Pack", 74, Rarity.MYTHIC, mage.cards.s.SummonThePack.class)); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); From 4d4b0d145e3dcd32e0b43f56b00c0f29834bfbe0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 18 Feb 2018 23:44:14 +0100 Subject: [PATCH 019/133] * Sacrificed a bug of SacrificeAllCost (fixing a problem with Soulblast looping forever). --- Mage.Sets/src/mage/cards/k/KaerveksSpite.java | 13 +++++----- Mage.Sets/src/mage/cards/s/Soulblast.java | 12 ++++------ .../costs/common/SacrificeAllCost.java | 24 +++++++++---------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java index bc0c37007de..349fa1ccf82 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java @@ -12,25 +12,24 @@ import mage.filter.common.FilterControlledPermanent; public class KaerveksSpite extends CardImpl { - private FilterControlledPermanent permanentsYouControl = new FilterControlledPermanent("all permanents you control"); - public KaerveksSpite(UUID ownerId, CardSetInfo cardSetInfo) { super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}"); - //As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand. - this.getSpellAbility().addCost(new SacrificeAllCost(permanentsYouControl)); + // As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand. + this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("all permanents you control"))); this.getSpellAbility().addCost(new DiscardHandCost()); - //Target player loses 5 life. + // Target player loses 5 life. Effect effect = new LoseLifeTargetEffect(5); this.getSpellAbility().addEffect(effect); } - public KaerveksSpite(final KaerveksSpite other){ + public KaerveksSpite(final KaerveksSpite other) { super(other); } - public KaerveksSpite copy(){ + @Override + public KaerveksSpite copy() { return new KaerveksSpite(this); } } diff --git a/Mage.Sets/src/mage/cards/s/Soulblast.java b/Mage.Sets/src/mage/cards/s/Soulblast.java index 1c9d495fcab..c6391096b60 100644 --- a/Mage.Sets/src/mage/cards/s/Soulblast.java +++ b/Mage.Sets/src/mage/cards/s/Soulblast.java @@ -36,7 +36,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -48,14 +48,12 @@ import mage.target.common.TargetCreatureOrPlayer; */ public class Soulblast extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control"); - public Soulblast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}{R}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}{R}{R}"); // As an additional cost to cast Soulblast, sacrifice all creatures you control. - this.getSpellAbility().addCost(new SacrificeAllCost(filter)); + this.getSpellAbility().addCost(new SacrificeAllCost(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED)); + // Soulblast deals damage to target creature or player equal to the total power of the sacrificed creatures. this.getSpellAbility().addEffect(new SoulblastEffect()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); @@ -90,7 +88,7 @@ class SoulblastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int power = 0; - for (Cost cost :source.getCosts()) { + for (Cost cost : source.getCosts()) { if (cost instanceof SacrificeAllCost) { for (Permanent permanent : ((SacrificeAllCost) cost).getPermanents()) { power += permanent.getPower().getValue(); diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeAllCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeAllCost.java index 88f5d8af975..a784838b8c9 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeAllCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeAllCost.java @@ -55,19 +55,19 @@ public class SacrificeAllCost extends CostImpl { public SacrificeAllCost(final SacrificeAllCost cost) { super(cost); - for (Permanent permanent: cost.permanents) { - this.permanents.add(permanent.copy()); - } + this.permanents.addAll(cost.permanents); // because this are already copied permanents, they can't change, so no copy again is needed this.filter = cost.filter.copy(); } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) { - permanents.add(permanent.copy()); - permanent.sacrifice(sourceId, game); + if (permanent.sacrifice(sourceId, game)) { + permanents.add(permanent.copy()); + } } - return true; + paid = true; + return paid; } @Override @@ -81,13 +81,13 @@ public class SacrificeAllCost extends CostImpl { activator = controllerId; } } - - for (Permanent permanent :game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) { - if(!game.getPlayer(activator).canPaySacrificeCost(permanent, sourceId, controllerId, game)) { - return false; - } + + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) { + if (!game.getPlayer(activator).canPaySacrificeCost(permanent, sourceId, controllerId, game)) { + return false; + } } - + return true; } From 210770669c5e78fe23aae9b7e5c8e0f3a0d8d159 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 18 Feb 2018 23:44:52 +0100 Subject: [PATCH 020/133] * Added Shah of Naar Isle (Future Sight complete now). --- .../src/mage/cards/s/ShahOfNaarIsle.java | 142 ++++++++++++++++++ Mage.Sets/src/mage/sets/FutureSight.java | 1 + .../mage/abilities/keyword/EchoAbility.java | 1 + .../main/java/mage/game/events/GameEvent.java | 1 + 4 files changed, 145 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java diff --git a/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java b/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java new file mode 100644 index 00000000000..0dc5a2fcc2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java @@ -0,0 +1,142 @@ +/* + * 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.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.EchoAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class ShahOfNaarIsle extends CardImpl { + + public ShahOfNaarIsle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.EFREET); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Echo {0} + this.addAbility(new EchoAbility("{0}")); + + // When Shah of Naar Isle's echo cost is paid, each opponent may draw up to three cards. + this.addAbility(new ShahOfNaarIsleTriggeredAbility()); + } + + public ShahOfNaarIsle(final ShahOfNaarIsle card) { + super(card); + } + + @Override + public ShahOfNaarIsle copy() { + return new ShahOfNaarIsle(this); + } +} + +class ShahOfNaarIsleTriggeredAbility extends TriggeredAbilityImpl { + + public ShahOfNaarIsleTriggeredAbility() { + super(Zone.BATTLEFIELD, new ShahOfNaarIsleEffect(), false); + } + + public ShahOfNaarIsleTriggeredAbility(final ShahOfNaarIsleTriggeredAbility effect) { + super(effect); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ECHO_PAID; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return getSourceId().equals(event.getSourceId()); + } + + @Override + public ShahOfNaarIsleTriggeredAbility copy() { + return new ShahOfNaarIsleTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When {this}'s echo cost is paid, " + super.getRule(); + } +} + +class ShahOfNaarIsleEffect extends OneShotEffect { + + public ShahOfNaarIsleEffect() { + super(Outcome.DrawCard); + this.staticText = "each opponent may draw up to three cards"; + } + + public ShahOfNaarIsleEffect(final ShahOfNaarIsleEffect effect) { + super(effect); + } + + @Override + public ShahOfNaarIsleEffect copy() { + return new ShahOfNaarIsleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : game.getOpponents(controller.getId())) { + Player opponent = game.getPlayer(playerId); + if (opponent != null) { + int number = opponent.getAmount(0, 3, "Draw how many cards?", game); + opponent.drawCards(number, game); + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/FutureSight.java b/Mage.Sets/src/mage/sets/FutureSight.java index a8669801cc5..f1ff59de3c8 100644 --- a/Mage.Sets/src/mage/sets/FutureSight.java +++ b/Mage.Sets/src/mage/sets/FutureSight.java @@ -190,6 +190,7 @@ public class FutureSight extends ExpansionSet { cards.add(new SetCardInfo("Scout's Warning", 16, Rarity.RARE, mage.cards.s.ScoutsWarning.class)); cards.add(new SetCardInfo("Second Wind", 57, Rarity.UNCOMMON, mage.cards.s.SecondWind.class)); cards.add(new SetCardInfo("Seht's Tiger", 31, Rarity.RARE, mage.cards.s.SehtsTiger.class)); + cards.add(new SetCardInfo("Shah of Naar Isle", 119, Rarity.RARE, mage.cards.s.ShahOfNaarIsle.class)); cards.add(new SetCardInfo("Shapeshifter's Marrow", 58, Rarity.RARE, mage.cards.s.ShapeshiftersMarrow.class)); cards.add(new SetCardInfo("Shimian Specter", 76, Rarity.RARE, mage.cards.s.ShimianSpecter.class)); cards.add(new SetCardInfo("Shivan Sand-Mage", 108, Rarity.UNCOMMON, mage.cards.s.ShivanSandMage.class)); diff --git a/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java b/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java index afe35d923f2..e978cb9dc0e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EchoAbility.java @@ -187,6 +187,7 @@ class EchoEffect extends OneShotEffect { if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) { cost.clearPaid(); if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ECHO_PAID, source.getSourceId(), source.getSourceId(), source.getControllerId())); return true; } } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 06816e0f4af..ef7ec2b5b38 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -92,6 +92,7 @@ public class GameEvent implements Serializable { DRAW_CARDS, // applies to an instruction to draw more than one card before any replacement effects apply to individual cards drawn DRAW_CARD, DREW_CARD, EXPLORED, + ECHO_PAID, MIRACLE_CARD_REVEALED, MADNESS_CARD_EXILED, INVESTIGATED, From bb512faa9878eb77a98d9d3b38e84b8698c7be49 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 19 Feb 2018 12:12:18 +1100 Subject: [PATCH 021/133] Add option to get debug information from game state in chat --- .../main/java/mage/server/ChatManager.java | 36 +++++-- .../java/mage/server/game/GameController.java | 94 +++++++++++++++++++ 2 files changed, 124 insertions(+), 6 deletions(-) diff --git a/Mage.Server/src/main/java/mage/server/ChatManager.java b/Mage.Server/src/main/java/mage/server/ChatManager.java index 371ead70e5f..d479f1b569f 100644 --- a/Mage.Server/src/main/java/mage/server/ChatManager.java +++ b/Mage.Server/src/main/java/mage/server/ChatManager.java @@ -28,6 +28,7 @@ package mage.server; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; @@ -37,6 +38,8 @@ import java.util.regex.Pattern; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.server.exceptions.UserNotFoundException; +import mage.server.game.GameController; +import mage.server.game.GameManager; import mage.server.util.SystemUtil; import mage.view.ChatMessage.MessageColor; import mage.view.ChatMessage.MessageType; @@ -220,6 +223,27 @@ public enum ChatManager { chatSessions.get(chatId).broadcastInfoToUser(user, message); return true; } + if (command.startsWith("GAME")) { + message += "
" + GameManager.instance.getChatId(chatId); + ChatSession session = chatSessions.get(chatId); + if (session != null && session.getInfo() != null) { + String gameId = session.getInfo(); + if (gameId.startsWith("Game ")) { + UUID id = java.util.UUID.fromString(gameId.substring(5, gameId.length())); + for (Entry entry : GameManager.instance.getGameController().entrySet()) { + if (entry.getKey().equals(id)) { + GameController controller = entry.getValue(); + if (controller != null) { + message += controller.getGameStateDebugMessage(); + chatSessions.get(chatId).broadcastInfoToUser(user, message); + } + } + } + + } + } + return true; + } if (command.startsWith("CARD ")) { Matcher matchPattern = getCardTextPattern.matcher(message.toLowerCase()); if (matchPattern.find()) { @@ -289,18 +313,18 @@ public enum ChatManager { public void sendReconnectMessage(UUID userId) { UserManager.instance.getUser(userId).ifPresent(user -> getChatSessions() - .stream() - .filter(chat -> chat.hasUser(userId)) - .forEach(chatSession -> chatSession.broadcast(null, user.getName() + " has reconnected", MessageColor.BLUE, true, MessageType.STATUS, null))); + .stream() + .filter(chat -> chat.hasUser(userId)) + .forEach(chatSession -> chatSession.broadcast(null, user.getName() + " has reconnected", MessageColor.BLUE, true, MessageType.STATUS, null))); } public void sendLostConnectionMessage(UUID userId, DisconnectReason reason) { UserManager.instance.getUser(userId).ifPresent(user -> getChatSessions() - .stream() - .filter(chat -> chat.hasUser(userId)) - .forEach(chatSession -> chatSession.broadcast(null, user.getName() + reason.getMessage(), MessageColor.BLUE, true, MessageType.STATUS, null))); + .stream() + .filter(chat -> chat.hasUser(userId)) + .forEach(chatSession -> chatSession.broadcast(null, user.getName() + reason.getMessage(), MessageColor.BLUE, true, MessageType.STATUS, null))); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index d039beff8da..1af7921751c 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -50,6 +50,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.GameException; import mage.game.GameOptions; +import mage.game.GameState; import mage.game.Table; import mage.game.events.Listener; import mage.game.events.PlayerQueryEvent; @@ -1088,4 +1089,97 @@ public class GameController implements GameCallback { return false; } + public String getGameStateDebugMessage() { + if (game == null) { + return ""; + } + GameState state = game.getState(); + if (state == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append("
Game State:
"); + sb.append(state); + + sb.append("
Active player is: "); + sb.append(game.getPlayer(state.getActivePlayerId()).getName()); + sb.append("
isGameOver: "); + sb.append(state.isGameOver()); + sb.append("
Current phase is: "); + sb.append(state.getTurn().getPhase()); + sb.append("
getBattlefield: "); + sb.append(state.getBattlefield()); + sb.append("
getChoosingPlayerId: "); + if (state.getChoosingPlayerId() != null) { + sb.append(game.getPlayer(state.getChoosingPlayerId()).getName()); + } else { + sb.append("noone!"); + } + sb.append("
getCombat: "); + sb.append(state.getCombat()); + sb.append("
getCommand: "); + sb.append(state.getCommand()); + sb.append("
getContinuousEffects: "); + sb.append(state.getContinuousEffects()); + sb.append("
getCopiedCards: "); + sb.append(state.getCopiedCards()); + sb.append("
getDelayed: "); + sb.append(state.getDelayed()); + sb.append("
getDesignations: "); + sb.append(state.getDesignations()); + sb.append("
getExile: "); + sb.append(state.getExile()); + sb.append("
getMonarchId: "); + sb.append(state.getMonarchId()); + sb.append("
getNextPermanentOrderNumber: "); + sb.append(state.getNextPermanentOrderNumber()); + sb.append("
getPlayerByOrderId: "); + if (state.getPlayerByOrderId() != null) { + sb.append(game.getPlayer(state.getPlayerByOrderId()).getName()); + } else { + sb.append("noone!"); + } + sb.append("
getPlayerList: "); + sb.append(state.getPlayerList()); + sb.append("
getPlayers: "); + sb.append(state.getPlayers()); + sb.append("
Player with Priority is: "); + if (state.getPriorityPlayerId() != null) { + sb.append(game.getPlayer(state.getPriorityPlayerId()).getName()); + } else { + sb.append("noone!"); + } + sb.append("
getRevealed: "); + sb.append(state.getRevealed()); + sb.append("
getSpecialActions: "); + sb.append(state.getSpecialActions()); + sb.append("
getStack: "); + sb.append(state.getStack()); + sb.append("
getStepNum: "); + sb.append(state.getStepNum()); + sb.append("
getTriggers: "); + sb.append(state.getTriggers()); + sb.append("
getTurn: "); + sb.append(state.getTurn()); + sb.append("
getTurnId: "); + sb.append(state.getTurnId()); + sb.append("
getTurnMods: "); + sb.append(state.getTurnMods()); + sb.append("
getTurnNum: "); + sb.append(state.getTurnNum()); + sb.append("
Future Timeout:"); + if (futureTimeout != null) { + sb.append("Cancelled?="); + sb.append(futureTimeout.isCancelled()); + sb.append(",,,Done?="); + sb.append(futureTimeout.isDone()); + sb.append(",,,GetDelay?="); + sb.append((int) futureTimeout.getDelay(TimeUnit.SECONDS)); + } else { + sb.append("Not using future Timeout!"); + } + sb.append("
"); + return sb.toString(); + } + } From 51f52dcc8baa91a3ce0786083537b77e9ce59c4b Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Mon, 19 Feb 2018 23:13:41 +0100 Subject: [PATCH 022/133] Fix MaximumHandSizeControllerEffect setText function --- .../MaximumHandSizeControllerEffect.java | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/MaximumHandSizeControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/MaximumHandSizeControllerEffect.java index b3193d96699..cedfb9fcec1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/MaximumHandSizeControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/MaximumHandSizeControllerEffect.java @@ -142,42 +142,50 @@ public class MaximumHandSizeControllerEffect extends ContinuousEffectImpl { private void setText() { StringBuilder sb = new StringBuilder(); - switch (targetController) { - case ANY: - if (handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) { - sb.append("All players have no "); - } else { - sb.append("All players "); - } - break; - case OPPONENT: - if (handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) { - sb.append("Each opponent has no "); - } else { - sb.append("Each opponent's "); - } - break; - case YOU: - if (handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) { - sb.append("You have no "); - } else { - sb.append("Your "); - } - break; - } - sb.append("maximum hand size"); - if (handSizeModification == HandSizeModification.INCREASE) { - sb.append(" is increased by "); - } else if (handSizeModification == HandSizeModification.REDUCE) { - sb.append(" is reduced by "); - } else if (!((handSize instanceof StaticValue) && ((StaticValue) handSize).getValue() != Integer.MAX_VALUE) || !(handSize instanceof StaticValue)) { - sb.append(" is "); - } - if ((handSize instanceof StaticValue && ((StaticValue) handSize).getValue() != Integer.MAX_VALUE)) { - sb.append(CardUtil.numberToText(((StaticValue) handSize).getValue())); - } else if (!(handSize instanceof StaticValue)) { - sb.append(handSize.getMessage()); + if(handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) { + switch (targetController) { + case ANY: + sb.append("Players have no maximum hand size"); + break; + case OPPONENT: + sb.append("Each opponent has no maximum hand size"); + break; + case YOU: + sb.append("You have no maximum hand size"); + break; + } + } else { + switch (targetController) { + case ANY: + sb.append("All players maximum hand size"); + break; + case OPPONENT: + sb.append("Each opponent's maximum hand size"); + break; + case YOU: + sb.append("Your maximum hand size"); + break; + } + + switch (handSizeModification) { + case SET: + sb.append(" is "); + break; + case INCREASE: + sb.append(" is increased by "); + break; + case REDUCE: + sb.append(" is reduced by "); + break; + } + + if (handSize instanceof StaticValue) { + sb.append(CardUtil.numberToText(((StaticValue) handSize).getValue())); + } else if (!(handSize instanceof StaticValue)) { + sb.append(handSize.getMessage()); + } } + if (duration == Duration.EndOfGame) { sb.append(" for the rest of the game"); } From d09aacdbfb36564611a6ab85d57876324cfc71fc Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Mon, 19 Feb 2018 23:27:33 +0100 Subject: [PATCH 023/133] Fix Diamond Kaleidoscope second ability --- Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java b/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java index 1676e4475b2..53d804be964 100644 --- a/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java +++ b/Mage.Sets/src/mage/cards/d/DiamondKaleidoscope.java @@ -68,8 +68,7 @@ public class DiamondKaleidoscope extends CardImpl { this.addAbility(ability); // Sacrifice a Prism token: Add one mana of any color to your mana pool. - ability = new AnyColorManaAbility(); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability = new AnyColorManaAbility(new SacrificeTargetCost(new TargetControlledPermanent(filter))); this.addAbility(ability); } From 89b6aeacd6ef5c5827ad9033746c00e26197f943 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 20 Feb 2018 16:16:26 +0100 Subject: [PATCH 024/133] * Added a test related to #4539. --- .../java/mage/server/TableController.java | 2 +- .../src/mage/cards/s/SultaiAscendancy.java | 3 +- .../cards/enchantments/SpreadingSeasTest.java | 43 +++++++++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 35df50ba08f..c45f07adc47 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -990,7 +990,7 @@ public class TableController { || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { Optional user = UserManager.instance.getUser(userPlayerEntry.getKey()); - if (!user.isPresent()) { + if (!user.isPresent() || user.get().isActive()) { logger.warn("- Active user of match is missing: " + matchPlayer.getName()); logger.warn("-- matchId:" + match.getId()); logger.warn("-- userId:" + userPlayerEntry.getKey()); diff --git a/Mage.Sets/src/mage/cards/s/SultaiAscendancy.java b/Mage.Sets/src/mage/cards/s/SultaiAscendancy.java index d516d565ad5..053e802bbd3 100644 --- a/Mage.Sets/src/mage/cards/s/SultaiAscendancy.java +++ b/Mage.Sets/src/mage/cards/s/SultaiAscendancy.java @@ -46,8 +46,7 @@ import mage.filter.FilterCard; public class SultaiAscendancy extends CardImpl { public SultaiAscendancy(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}{G}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}{G}{U}"); // At the beginning of your upkeep, look at the top two cards of your library. Put any number of them into your graveyard and the rest on top of your library in any order. Effect effect = new LookLibraryAndPickControllerEffect( diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SpreadingSeasTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SpreadingSeasTest.java index c44ce2e37d3..f5528bbe33e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SpreadingSeasTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SpreadingSeasTest.java @@ -86,12 +86,12 @@ public class SpreadingSeasTest extends CardTestPlayerBase { } @Test - public void testUtopiaSprawlWithSpreadingSeas(){ + public void testUtopiaSprawlWithSpreadingSeas() { addCard(Zone.HAND, playerA, "Spreading Seas", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Island", 10); addCard(Zone.HAND, playerA, "Utopia Sprawl"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Utopia Sprawl","Forest"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Utopia Sprawl", "Forest"); setChoice(playerA, "Green"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Forest"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); @@ -99,9 +99,8 @@ public class SpreadingSeasTest extends CardTestPlayerBase { assertNotSubtype("Forest", SubType.FOREST); } - @Test - public void testSpreadingSeasWithUrzaLand(){ + public void testSpreadingSeasWithUrzaLand() { addCard(Zone.HAND, playerA, "Spreading Seas", 1); addCard(Zone.BATTLEFIELD, playerA, "Urza's Tower", 1); addCard(Zone.BATTLEFIELD, playerA, "Island", 10); @@ -111,4 +110,40 @@ public class SpreadingSeasTest extends CardTestPlayerBase { assertNotSubtype("Urza's Tower", SubType.URZAS); assertNotSubtype("Urza's Tower", SubType.TOWER); } + + /** + * https://github.com/magefree/mage/issues/4529 Some spell effects that + * effect the use of mana abilities on lands are inoperative. Example + * Spreading Seas transforms enchanted land into an island and it loses all + * other abilities. The AI does not recognize this and is able to use all + * abilities of the enchanted land including all previous mana abilities and + * activated abilities, in addition to now also being an island due to + * Spreading Sea's effect. + */ + @Test + public void testSpreadingRemovesOtherAbilities() { + + // Enchant land + // When Spreading Seas enters the battlefield, draw a card. + // Enchanted land is an Island. + addCard(Zone.HAND, playerA, "Spreading Seas", 1); // ENCHANTMENT {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + // {T}: Add {C} to your mana pool. + // {1}{R}, {T}: Create a 0/1 red Kobold creature token named Kobolds of Kher Keep. + addCard(Zone.BATTLEFIELD, playerB, "Kher Keep", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Kher Keep"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{1}{R}"); // Ability should not be available + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Spreading Seas", 1); + + assertPermanentCount(playerB, "Kobolds of Kher Keep", 0); + assertTapped("Kher Keep", false); + } + } From c5cbdcf68fcc52c6b9068d52b9ee33d71b57cc0d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 20 Feb 2018 17:57:23 +0100 Subject: [PATCH 025/133] * Fixed some target pointer handling (fixes #4540). --- .../src/mage/cards/a/ActOfAuthority.java | 6 +-- .../src/mage/cards/b/BackFromTheBrink.java | 2 +- .../src/mage/cards/b/BarrinsUnmaking.java | 6 +-- Mage.Sets/src/mage/cards/m/MimicVat.java | 34 +++++++-------- .../mage/test/cards/copy/MimicVatTest.java | 43 +++++++++++++++++++ 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java index 3a0f108245a..913dc43fbba 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java +++ b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java @@ -27,6 +27,7 @@ */ package mage.cards.a; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -44,8 +45,6 @@ import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; -import java.util.UUID; - /** * * @author LevelX2 @@ -53,8 +52,7 @@ import java.util.UUID; public class ActOfAuthority extends CardImpl { public ActOfAuthority(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); // When Act of Authority enters the battlefield, you may exile target artifact or enchantment. Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect(), true); diff --git a/Mage.Sets/src/mage/cards/b/BackFromTheBrink.java b/Mage.Sets/src/mage/cards/b/BackFromTheBrink.java index 6e92e76df4d..b887c7b5a27 100644 --- a/Mage.Sets/src/mage/cards/b/BackFromTheBrink.java +++ b/Mage.Sets/src/mage/cards/b/BackFromTheBrink.java @@ -103,7 +103,7 @@ class BackFromTheBrinkCost extends CostImpl { if (controller != null) { Card card = controller.getGraveyard().get(targets.getFirstTarget(), game); if (card != null && controller.moveCards(card, Zone.EXILED, ability, game)) { - ability.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId())); + ability.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); paid = card.getManaCost().pay(ability, game, sourceId, controllerId, noMana); } } diff --git a/Mage.Sets/src/mage/cards/b/BarrinsUnmaking.java b/Mage.Sets/src/mage/cards/b/BarrinsUnmaking.java index 2ef729a53cc..ec6af647eea 100644 --- a/Mage.Sets/src/mage/cards/b/BarrinsUnmaking.java +++ b/Mage.Sets/src/mage/cards/b/BarrinsUnmaking.java @@ -52,7 +52,7 @@ public class BarrinsUnmaking extends CardImpl { public BarrinsUnmaking(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); - // Return target permanent to its owner's hand if that permanent shares a color with the most common color among all permanents or a color tied for most common. + // Return target permanent to its owner's hand if that permanent shares a color with the most common color among all permanents or a color tied for most common. this.getSpellAbility().addEffect(new BarrinsUnmakingEffect()); this.getSpellAbility().addTarget(new TargetPermanent()); } @@ -85,12 +85,12 @@ class BarrinsUnmakingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { Condition condition = new MostCommonColorCondition(permanent.getColor(game)); if (condition.apply(game, source)) { Effect effect = new ReturnToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/m/MimicVat.java b/Mage.Sets/src/mage/cards/m/MimicVat.java index 8fd4c37ba9d..9f5b5636112 100644 --- a/Mage.Sets/src/mage/cards/m/MimicVat.java +++ b/Mage.Sets/src/mage/cards/m/MimicVat.java @@ -38,8 +38,8 @@ import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbil import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -122,7 +122,7 @@ class MimicVatTriggeredAbility extends TriggeredAbilityImpl { && !(permanent instanceof PermanentToken) && permanent.isCreature()) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId(), game)); return true; } return false; @@ -152,22 +152,22 @@ class MimicVatEffect extends OneShotEffect { if (controller == null || permanent == null) { return false; } - // return older cards to graveyard - Set toGraveyard = new HashSet<>(); - for (UUID imprintedId : permanent.getImprinted()) { - Card card = game.getCard(imprintedId); - if (card != null) { - toGraveyard.add(card); - } - } - controller.moveCards(toGraveyard, Zone.GRAVEYARD, source, game); - permanent.clearImprinted(game); - // Imprint a new one - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), permanent.getName() + " (Imprint)"); - permanent.imprint(card.getId(), game); + Card newCard = game.getCard(getTargetPointer().getFirst(game, source)); + if (newCard != null) { + // return older cards to graveyard + Set toGraveyard = new HashSet<>(); + for (UUID imprintedId : permanent.getImprinted()) { + Card card = game.getCard(imprintedId); + if (card != null) { + toGraveyard.add(card); + } + } + controller.moveCards(toGraveyard, Zone.GRAVEYARD, source, game); + permanent.clearImprinted(game); + + controller.moveCardsToExile(newCard, source, game, true, source.getSourceId(), permanent.getName() + " (Imprint)"); + permanent.imprint(newCard.getId(), game); } return true; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java index b708a3d6c9a..73f7b9eacb9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java @@ -111,4 +111,47 @@ public class MimicVatTest extends CardTestPlayerBase { } + /** + * Player A has Mimic Vat and plays Sidisi, Undead Vizier and exploits. + * Player N responds to Mimic Vat Trigger with Shred Memory, exiling Sidisi. + * Sidisi gets exiled but then xmage allows player A to imprint the + * creature, which shouldn't be possible. + */ + @Test + public void TestExileFails() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // Imprint - Whenever a nontoken creature dies, you may exile that card. If you do, return each other card exiled with Mimic Vat to its owner's graveyard. + // {3}, {T}: Create a token that's a copy of a card exiled with Mimic Vat. It gains haste. Exile it at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Mimic Vat", 1); // Artifact {3} + + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + // Exile up to four target cards from a single graveyard. + // Transmute {1}{B}{B} + addCard(Zone.HAND, playerB, "Shred Memory", 1); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Shred Memory", "Silvercoat Lion", "Whenever a nontoken creature dies"); + setChoice(playerA, "Yes"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}, {T}: Create a token that's a copy of a card exiled with "); + setChoice(playerA, "Yes"); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerB, "Shred Memory", 1); + + assertExileCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 0); + + } } From 866c1c3973cc6b1f448a08dec485724bf502b4f0 Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Tue, 20 Feb 2018 18:29:10 +0100 Subject: [PATCH 026/133] Fix Helm of Awakening and Sphere of Resistance text --- .../effects/common/cost/SpellsCostReductionAllEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java index 54bc9bbd81f..0b7af33808d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java @@ -55,7 +55,7 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl { private final boolean upTo; public SpellsCostReductionAllEffect(int amount) { - this(new FilterCard("All Spells "), amount); + this(new FilterCard("Spells"), amount); } public SpellsCostReductionAllEffect(FilterCard filter, int amount) { From c8d142492f07f68ae4fddba1b8f7121e3e3ca1ed Mon Sep 17 00:00:00 2001 From: L_J Date: Tue, 20 Feb 2018 17:38:14 +0000 Subject: [PATCH 027/133] Updated watcher for Gaze of the Gorgon --- .../java/mage/watchers/common/BlockedAttackerWatcher.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java b/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java index 23a15d10cd2..e43ed433fee 100644 --- a/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java @@ -89,4 +89,9 @@ public class BlockedAttackerWatcher extends Watcher { Set blockedAttackers = blockData.get(new MageObjectReference(blocker, game)); return blockedAttackers != null && blockedAttackers.contains(new MageObjectReference(attacker, game)); } + + public boolean creatureHasBlockedAttacker(MageObjectReference attacker, MageObjectReference blocker, Game game) { + Set blockedAttackers = blockData.get(blocker); + return blockedAttackers != null && blockedAttackers.contains(attacker); + } } From 6d01e8e16f9738dfce0d0f6d547389178de9191b Mon Sep 17 00:00:00 2001 From: L_J Date: Tue, 20 Feb 2018 17:39:42 +0000 Subject: [PATCH 028/133] Gaze of the Gorgon rewrite (fixes #4199) --- .../src/mage/cards/g/GazeOfTheGorgon.java | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GazeOfTheGorgon.java b/Mage.Sets/src/mage/cards/g/GazeOfTheGorgon.java index ca7d2159fdc..33c2702a8bd 100644 --- a/Mage.Sets/src/mage/cards/g/GazeOfTheGorgon.java +++ b/Mage.Sets/src/mage/cards/g/GazeOfTheGorgon.java @@ -27,10 +27,13 @@ */ package mage.cards.g; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.RegenerateTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -43,10 +46,6 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.BlockedAttackerWatcher; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * * @author LevelX2 @@ -60,8 +59,7 @@ public class GazeOfTheGorgon extends CardImpl { // Regenerate target creature. At end of combat, destroy all creatures that blocked or were blocked by that creature this turn. this.getSpellAbility().addEffect(new RegenerateTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( - new AtTheEndOfCombatDelayedTriggeredAbility(new GazeOfTheGorgonEffect()))); + this.getSpellAbility().addEffect(new GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect()); this.getSpellAbility().addWatcher(new BlockedAttackerWatcher()); } @@ -75,15 +73,46 @@ public class GazeOfTheGorgon extends CardImpl { } } -class GazeOfTheGorgonEffect extends OneShotEffect { +class GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect extends OneShotEffect { - public GazeOfTheGorgonEffect() { + public GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect() { + super(Outcome.Benefit); + this.staticText = "At this turn's next end of combat, destroy all creatures that blocked or were blocked by it this turn"; + } + + public GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect(final GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect copy() { + return new GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!source.getTargets().isEmpty() && source.getFirstTarget() != null) { + MageObjectReference mor = new MageObjectReference(source.getFirstTarget(), game); + AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new GazeOfTheGorgonEffect(mor)); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } + return false; + } +} + +class GazeOfTheGorgonEffect extends OneShotEffect { + + MageObjectReference targetCreature; + + public GazeOfTheGorgonEffect(MageObjectReference targetCreature) { super(Outcome.DestroyPermanent); - this.staticText = "destroy all creatures that blocked or were blocked by that creature this turn"; + this.targetCreature = targetCreature; } public GazeOfTheGorgonEffect(final GazeOfTheGorgonEffect effect) { super(effect); + targetCreature = effect.targetCreature; } @Override @@ -94,14 +123,13 @@ class GazeOfTheGorgonEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent targetCreature = game.getPermanentOrLKIBattlefield(source.getTargets().getFirstTarget()); if (controller != null && targetCreature != null) { BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName()); if (watcher != null) { List toDestroy = new ArrayList<>(); for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) { - if (!creature.getId().equals(targetCreature.getId())) { - if (watcher.creatureHasBlockedAttacker(creature, targetCreature, game) || watcher.creatureHasBlockedAttacker(targetCreature, creature, game)) { + if (!creature.getId().equals(targetCreature.getSourceId())) { + if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), targetCreature, game) || watcher.creatureHasBlockedAttacker(targetCreature, new MageObjectReference(creature, game), game)) { toDestroy.add(creature); } } From 42a9959a5dcd139a96caae31d90610fbf751e92a Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Wed, 21 Feb 2018 00:06:38 +0100 Subject: [PATCH 029/133] WickedReward add missing target --- Mage.Sets/src/mage/cards/w/WickedReward.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/cards/w/WickedReward.java b/Mage.Sets/src/mage/cards/w/WickedReward.java index 90499a3f4a8..9261954c8de 100644 --- a/Mage.Sets/src/mage/cards/w/WickedReward.java +++ b/Mage.Sets/src/mage/cards/w/WickedReward.java @@ -8,6 +8,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; public class WickedReward extends CardImpl { @@ -19,6 +20,7 @@ public class WickedReward extends CardImpl { //Target creature gets +4/+2 until end of turn. this.getSpellAbility().addEffect(new BoostTargetEffect(4, 2, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } public WickedReward(WickedReward other){ From 9528a89416588236dba9bf3edc7945eb014b8992 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 01:22:46 +0100 Subject: [PATCH 030/133] Implemented cards --- .../src/mage/cards/m/MinionOfTheWastes.java | 113 ++++++++++++ Mage.Sets/src/mage/cards/n/NamelessRace.java | 136 ++++++++++++++ Mage.Sets/src/mage/cards/r/Runesword.java | 173 ++++++++++++++++++ Mage.Sets/src/mage/cards/t/TheFallen.java | 146 +++++++++++++++ Mage.Sets/src/mage/sets/Chronicles.java | 2 + Mage.Sets/src/mage/sets/MastersEdition.java | 1 + Mage.Sets/src/mage/sets/Tempest.java | 1 + Mage.Sets/src/mage/sets/TheDark.java | 3 + 8 files changed, 575 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java create mode 100644 Mage.Sets/src/mage/cards/n/NamelessRace.java create mode 100644 Mage.Sets/src/mage/cards/r/Runesword.java create mode 100644 Mage.Sets/src/mage/cards/t/TheFallen.java diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java new file mode 100644 index 00000000000..35064a7591b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java @@ -0,0 +1,113 @@ +/* + * 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.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public class MinionOfTheWastes extends CardImpl { + + public MinionOfTheWastes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}{B}"); + this.subtype.add(SubType.MINION); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As Minion of the Wastes enters the battlefield, pay any amount of life. The amount you pay can't be more than the total number of white nontoken permanents your opponents control plus the total number of white cards in their graveyards. + this.addAbility(new AsEntersBattlefieldAbility(new MinionOfTheWastesEffect())); + + // Minion of the Wastes's power and toughness are each equal to the life paid as it entered the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the life paid as it entered the battlefield"))); + } + + public MinionOfTheWastes(final MinionOfTheWastes card) { + super(card); + } + + @Override + public MinionOfTheWastes copy() { + return new MinionOfTheWastes(this); + } +} + +class MinionOfTheWastesEffect extends OneShotEffect { + + public MinionOfTheWastesEffect() { + super(Outcome.LoseLife); + staticText = "pay any amount of life"; + } + + public MinionOfTheWastesEffect(final MinionOfTheWastesEffect effect) { + super(effect); + } + + @Override + public MinionOfTheWastesEffect copy() { + return new MinionOfTheWastesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card sourceCard = game.getCard(source.getSourceId()); + int payAmount = controller.getAmount(0, controller.getLife(), "Pay any amount of life", game); + controller.loseLife(payAmount, game, false); + game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) + .append(" pays ").append(payAmount).append(" life").toString()); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NamelessRace.java b/Mage.Sets/src/mage/cards/n/NamelessRace.java new file mode 100644 index 00000000000..0f413268ebf --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NamelessRace.java @@ -0,0 +1,136 @@ +/* + * 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.n; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.other.OwnerPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public class NamelessRace extends CardImpl { + + public NamelessRace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As Nameless Race enters the battlefield, pay any amount of life. The amount you pay can't be more than the total number of white nontoken permanents your opponents control plus the total number of white cards in their graveyards. + this.addAbility(new AsEntersBattlefieldAbility(new NamelessRaceEffect())); + + // Nameless Race's power and toughness are each equal to the life paid as it entered the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the life paid as it entered the battlefield"))); + } + + public NamelessRace(final NamelessRace card) { + super(card); + } + + @Override + public NamelessRace copy() { + return new NamelessRace(this); + } +} + +class NamelessRaceEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("white nontoken permanents your opponents control"); + private static final FilterCard filter2 = new FilterCard("white cards in their graveyards"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + filter.add(Predicates.not(new TokenPredicate())); + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + filter2.add(new ColorPredicate(ObjectColor.WHITE)); + filter2.add(new OwnerPredicate(TargetController.OPPONENT)); + } + + public NamelessRaceEffect() { + super(Outcome.LoseLife); + staticText = "pay any amount of life. The amount you pay can't be more than the total number of white nontoken permanents your opponents control plus the total number of white cards in their graveyards"; + } + + public NamelessRaceEffect(final NamelessRaceEffect effect) { + super(effect); + } + + @Override + public NamelessRaceEffect copy() { + return new NamelessRaceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card sourceCard = game.getCard(source.getSourceId()); + int permanentsInPlay = new PermanentsOnBattlefieldCount(filter).calculate(game, source, null); + int cardsInGraveyards = new CardsInAllGraveyardsCount(filter2).calculate(game, source, null); + int maxAmount = Math.min(permanentsInPlay + cardsInGraveyards, controller.getLife()); + int payAmount = controller.getAmount(0, maxAmount, "Pay up to " + maxAmount + " life", game); + controller.loseLife(payAmount, game, false); + game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) + .append(" pays ").append(payAmount).append(" life").toString()); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/r/Runesword.java b/Mage.Sets/src/mage/cards/r/Runesword.java new file mode 100644 index 00000000000..a5ece841531 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Runesword.java @@ -0,0 +1,173 @@ +/* + * 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.r; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.replacement.DealtDamageToCreatureBySourceDies; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.common.TargetAttackingCreature; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.DamagedByWatcher; + +/** + * + * @author L_J + */ +public class Runesword extends CardImpl { + + public Runesword(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}"); + + // {3}, {T}: Target attacking creature gets +2/+0 until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0, Duration.EndOfTurn), new GenericManaCost(3)); + // When that creature leaves the battlefield this turn, sacrifice Runesword. + ability.addEffect(new RuneswordCreateTriggeredAbilityEffect()); + // If the creature deals damage to a creature this turn, the creature dealt damage can't be regenerated this turn. + ability.addEffect(new RuneswordCantBeRegeneratedEffect()); + // If a creature dealt damage by the targeted creature would die this turn, exile that creature instead. + SimpleStaticAbility ability2 = new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.Custom)); + ability2.addWatcher(new DamagedByWatcher()); + ability2.setRuleVisible(false); + ability.addEffect(new GainAbilityTargetEffect(ability2, Duration.EndOfTurn, null, false).setText( + "If a creature dealt damage by the targeted creature would die this turn, exile that creature instead")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetAttackingCreature()); + this.addAbility(ability); + } + + public Runesword(final Runesword card) { + super(card); + } + + @Override + public Runesword copy() { + return new Runesword(this); + } +} + +class RuneswordCreateTriggeredAbilityEffect extends OneShotEffect { + + public RuneswordCreateTriggeredAbilityEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "When that creature leaves the battlefield this turn, sacrifice {this}"; + } + + public RuneswordCreateTriggeredAbilityEffect(final RuneswordCreateTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); + Permanent targetObject = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + if (sourceObject != null && targetObject != null) { + Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice " + sourceObject.getName()); + sacrificeEffect.setTargetPointer(new FixedTarget(sourceObject, game)); + LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false); + triggerAbility.setRuleVisible(false); + ContinuousEffect continuousEffect = new GainAbilityTargetEffect(triggerAbility, Duration.EndOfTurn); + continuousEffect.setTargetPointer(new FixedTarget(targetObject, game)); + game.addEffect(continuousEffect, source); + return true; + } + return false; + } + + @Override + public RuneswordCreateTriggeredAbilityEffect copy() { + return new RuneswordCreateTriggeredAbilityEffect(this); + } +} + +class RuneswordCantBeRegeneratedEffect extends ContinuousRuleModifyingEffectImpl { + + private UUID targetCreatureId; + + public RuneswordCantBeRegeneratedEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, false, false); + this.staticText = "If the creature deals damage to a creature this turn, the creature dealt damage can't be regenerated this turn"; + } + + public RuneswordCantBeRegeneratedEffect(final RuneswordCantBeRegeneratedEffect effect) { + super(effect); + targetCreatureId = effect.targetCreatureId; + } + + @Override + public RuneswordCantBeRegeneratedEffect copy() { + return new RuneswordCantBeRegeneratedEffect(this); + } + + public void init(Ability source, Game game) { + targetCreatureId = getTargetPointer().getFirst(game, source); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.REGENERATE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (targetCreatureId != null) { + DamagedByWatcher watcher = (DamagedByWatcher) game.getState().getWatchers().get(DamagedByWatcher.class.getSimpleName(), targetCreatureId); + if (watcher != null) { + return watcher.wasDamaged(event.getTargetId(), game); + } + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/cards/t/TheFallen.java b/Mage.Sets/src/mage/cards/t/TheFallen.java new file mode 100644 index 00000000000..ab009381c6d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheFallen.java @@ -0,0 +1,146 @@ +/* + * 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.t; + +import java.util.*; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.watchers.Watcher; + +/** + * + * @author L_J + */ +public class TheFallen extends CardImpl { + + public TheFallen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}{B}"); + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // At the beginning of your upkeep, The Fallen deals 1 damage to each opponent it has dealt damage to this game. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TheFallenEffect(), TargetController.YOU, false), new TheFallenWatcher()); + } + + public TheFallen(final TheFallen card) { + super(card); + } + + @Override + public TheFallen copy() { + return new TheFallen(this); + } +} + +class TheFallenEffect extends OneShotEffect { + + public TheFallenEffect() { + super(Outcome.Damage); + this.staticText = "{this} deals 1 damage to each opponent it has dealt damage to this game"; + } + + public TheFallenEffect(final TheFallenEffect effect) { + super(effect); + } + + @Override + public TheFallenEffect copy() { + return new TheFallenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + TheFallenWatcher watcher = (TheFallenWatcher) game.getState().getWatchers().get(TheFallenWatcher.class.getSimpleName()); + if (watcher != null && watcher.getPlayerDealtDamageThisGame(source.getSourceId()) != null) { + for (UUID playerId : watcher.getPlayerDealtDamageThisGame(source.getSourceId())) { + if (!source.getControllerId().equals(playerId)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.damage(1, source.getSourceId(), game, false, true); + } + } + } + return true; + } + return false; + } +} + +class TheFallenWatcher extends Watcher { + + private Map> playersDealtDamageThisGame = new HashMap<>(); // Map> + + public TheFallenWatcher() { + super(TheFallenWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public TheFallenWatcher(final TheFallenWatcher watcher) { + super(watcher); + playersDealtDamageThisGame = new HashMap<>(watcher.playersDealtDamageThisGame); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (permanent != null) { + Set toAdd; + if (playersDealtDamageThisGame.get(event.getSourceId()) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = playersDealtDamageThisGame.get(event.getSourceId()); + } + toAdd.add(event.getPlayerId()); + playersDealtDamageThisGame.put(event.getSourceId(), toAdd); + } + } + } + + public Set getPlayerDealtDamageThisGame(UUID creatureId) { + return playersDealtDamageThisGame.get(creatureId); + } + + @Override + public TheFallenWatcher copy() { + return new TheFallenWatcher(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Chronicles.java b/Mage.Sets/src/mage/sets/Chronicles.java index 44a3c47cfea..75eb7d6636c 100644 --- a/Mage.Sets/src/mage/sets/Chronicles.java +++ b/Mage.Sets/src/mage/sets/Chronicles.java @@ -128,6 +128,7 @@ public class Chronicles extends ExpansionSet { cards.add(new SetCardInfo("Repentant Blacksmith", 67, Rarity.COMMON, mage.cards.r.RepentantBlacksmith.class)); cards.add(new SetCardInfo("Revelation", 40, Rarity.RARE, mage.cards.r.Revelation.class)); cards.add(new SetCardInfo("Rubinia Soulsinger", 118, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); + cards.add(new SetCardInfo("Runesword", 86, Rarity.COMMON, mage.cards.r.Runesword.class)); cards.add(new SetCardInfo("Safe Haven", 93, Rarity.RARE, mage.cards.s.SafeHaven.class)); cards.add(new SetCardInfo("Scavenger Folk", 41, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); cards.add(new SetCardInfo("Sentinel", 87, Rarity.RARE, mage.cards.s.Sentinel.class)); @@ -139,6 +140,7 @@ public class Chronicles extends ExpansionSet { cards.add(new SetCardInfo("Stangg", 121, Rarity.RARE, mage.cards.s.Stangg.class)); cards.add(new SetCardInfo("Storm Seeker", 42, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); cards.add(new SetCardInfo("Teleport", 26, Rarity.RARE, mage.cards.t.Teleport.class)); + cards.add(new SetCardInfo("The Fallen", 10, Rarity.UNCOMMON, mage.cards.t.TheFallen.class)); cards.add(new SetCardInfo("The Wretched", 11, Rarity.RARE, mage.cards.t.TheWretched.class)); cards.add(new SetCardInfo("Tobias Andrion", 122, Rarity.UNCOMMON, mage.cards.t.TobiasAndrion.class)); cards.add(new SetCardInfo("Tormod's Crypt", 89, Rarity.COMMON, mage.cards.t.TormodsCrypt.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEdition.java b/Mage.Sets/src/mage/sets/MastersEdition.java index d38ce5df2e2..cdf94aba0e5 100644 --- a/Mage.Sets/src/mage/sets/MastersEdition.java +++ b/Mage.Sets/src/mage/sets/MastersEdition.java @@ -114,6 +114,7 @@ public class MastersEdition extends ExpansionSet { cards.add(new SetCardInfo("Erg Raiders", 68, Rarity.COMMON, mage.cards.e.ErgRaiders.class)); cards.add(new SetCardInfo("Eureka", 117, Rarity.RARE, mage.cards.e.Eureka.class)); cards.add(new SetCardInfo("Exile", 12, Rarity.COMMON, mage.cards.e.Exile.class)); + cards.add(new SetCardInfo("The Fallen", 69, Rarity.UNCOMMON, mage.cards.t.TheFallen.class)); cards.add(new SetCardInfo("Feast or Famine", 70, Rarity.COMMON, mage.cards.f.FeastOrFamine.class)); cards.add(new SetCardInfo("Fire Covenant", 145, Rarity.UNCOMMON, mage.cards.f.FireCovenant.class)); cards.add(new SetCardInfo("Fissure", 93, Rarity.COMMON, mage.cards.f.Fissure.class)); diff --git a/Mage.Sets/src/mage/sets/Tempest.java b/Mage.Sets/src/mage/sets/Tempest.java index 4766f25acd5..e764804b94d 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -196,6 +196,7 @@ public class Tempest extends ExpansionSet { cards.add(new SetCardInfo("Meditate", 76, Rarity.RARE, mage.cards.m.Meditate.class)); cards.add(new SetCardInfo("Metallic Sliver", 287, Rarity.COMMON, mage.cards.m.MetallicSliver.class)); cards.add(new SetCardInfo("Mindwhip Sliver", 39, Rarity.UNCOMMON, mage.cards.m.MindwhipSliver.class)); + cards.add(new SetCardInfo("Minion of the Wastes", 40, Rarity.RARE, mage.cards.m.MinionOfTheWastes.class)); cards.add(new SetCardInfo("Mirri's Guile", 130, Rarity.RARE, mage.cards.m.MirrisGuile.class)); cards.add(new SetCardInfo("Mnemonic Sliver", 77, Rarity.UNCOMMON, mage.cards.m.MnemonicSliver.class)); cards.add(new SetCardInfo("Mogg Cannon", 288, Rarity.UNCOMMON, mage.cards.m.MoggCannon.class)); diff --git a/Mage.Sets/src/mage/sets/TheDark.java b/Mage.Sets/src/mage/sets/TheDark.java index 4fe90148e64..f67cff2be9c 100644 --- a/Mage.Sets/src/mage/sets/TheDark.java +++ b/Mage.Sets/src/mage/sets/TheDark.java @@ -126,6 +126,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Miracle Worker", 86, Rarity.COMMON, mage.cards.m.MiracleWorker.class)); cards.add(new SetCardInfo("Morale", 87, Rarity.COMMON, mage.cards.m.Morale.class)); cards.add(new SetCardInfo("Murk Dwellers", 11, Rarity.COMMON, mage.cards.m.MurkDwellers.class)); + cards.add(new SetCardInfo("Nameless Race", 12, Rarity.RARE, mage.cards.n.NamelessRace.class)); cards.add(new SetCardInfo("Necropolis", 102, Rarity.RARE, mage.cards.n.Necropolis.class)); cards.add(new SetCardInfo("Niall Silvain", 45, Rarity.RARE, mage.cards.n.NiallSilvain.class)); cards.add(new SetCardInfo("Orc General", 72, Rarity.UNCOMMON, mage.cards.o.OrcGeneral.class)); @@ -135,6 +136,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Psychic Allergy", 33, Rarity.RARE, mage.cards.p.PsychicAllergy.class)); cards.add(new SetCardInfo("Rag Man", 13, Rarity.RARE, mage.cards.r.RagMan.class)); cards.add(new SetCardInfo("Riptide", 34, Rarity.COMMON, mage.cards.r.Riptide.class)); + cards.add(new SetCardInfo("Runesword", 104, Rarity.RARE, mage.cards.r.Runesword.class)); cards.add(new SetCardInfo("Safe Haven", 115, Rarity.RARE, mage.cards.s.SafeHaven.class)); cards.add(new SetCardInfo("Savaen Elves", 47, Rarity.COMMON, mage.cards.s.SavaenElves.class)); cards.add(new SetCardInfo("Scarecrow", 105, Rarity.RARE, mage.cards.s.Scarecrow.class)); @@ -152,6 +154,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Stone Calendar", 108, Rarity.RARE, mage.cards.s.StoneCalendar.class)); cards.add(new SetCardInfo("Sunken City", 35, Rarity.COMMON, mage.cards.s.SunkenCity.class)); cards.add(new SetCardInfo("Tangle Kelp", 36, Rarity.RARE, mage.cards.t.TangleKelp.class)); + cards.add(new SetCardInfo("The Fallen", 15, Rarity.RARE, mage.cards.t.TheFallen.class)); cards.add(new SetCardInfo("Tivadar's Crusade", 91, Rarity.UNCOMMON, mage.cards.t.TivadarsCrusade.class)); cards.add(new SetCardInfo("Tormod's Crypt", 109, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class)); cards.add(new SetCardInfo("Tower of Coireall", 110, Rarity.RARE, mage.cards.t.TowerOfCoireall.class)); From 9e941914088393783a25fc6e8251c8cb7bb2c53e Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:11:45 +0000 Subject: [PATCH 031/133] Implemented Vodalian War Machine --- .../src/mage/cards/v/VodalianWarMachine.java | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/v/VodalianWarMachine.java diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java new file mode 100644 index 00000000000..09e17dcfd92 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -0,0 +1,208 @@ +/* + * 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.v; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.watchers.Watcher; + +/** + * + * @author L_J + */ +public class VodalianWarMachine extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Merfolk you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.MERFOLK)); + } + + public VodalianWarMachine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Tap an untapped Merfolk you control: Vodalian War Machine can attack this turn as though it didn't have defender. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); + this.addAbility(ability); + + // Tap an untapped Merfolk you control: Vodalian War Machine gets +2/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); + + // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. + this.addAbility(new DiesTriggeredAbility(new VodalianWarMachineEffect(), false), new VodalianWarMachineWatcher()); + } + + public VodalianWarMachine(final VodalianWarMachine card) { + super(card); + } + + @Override + public VodalianWarMachine copy() { + return new VodalianWarMachine(this); + } +} + +class VodalianWarMachineEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); + + static { + filter.add(new SubtypePredicate(SubType.MERFOLK)); + } + + public VodalianWarMachineEffect() { + super(Outcome.Detriment); + staticText = "destroy all " + filter.getMessage(); + } + + public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { + super(effect); + } + + @Override + public VodalianWarMachineEffect copy() { + return new VodalianWarMachineEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (sourcePermanent != null) { + VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId()) != null) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId()).contains(permanent.getId())) { + permanent.destroy(source.getSourceId(), game, false); + } + } + return true; + } + } + return false; + } + +} + +class VodalianWarMachineWatcher extends Watcher { + + public Map> tappedMerfolkIds = new HashMap<>(); + + public VodalianWarMachineWatcher() { + super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public VodalianWarMachineWatcher(final VodalianWarMachineWatcher watcher) { + super(watcher); + this.tappedMerfolkIds = watcher.tappedMerfolkIds; + } + + @Override + public VodalianWarMachineWatcher copy() { + return new VodalianWarMachineWatcher(this); + } + + public Set getTappedMerfolkIds(UUID sourceId) { + return tappedMerfolkIds.get(sourceId); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { + if (event.getSourceId() != null) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null) { + Ability ability = stackAbility.getStackAbility(); + if (ability != null) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapTargetCost && cost.isPaid()) { + TapTargetCost tapCost = (TapTargetCost) cost; + if (tapCost.getTarget().isChosen()) { + Set toAdd; + if (tappedMerfolkIds.get(event.getSourceId()) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = tappedMerfolkIds.get(event.getSourceId()); + } + for (UUID targetId : tapCost.getTarget().getTargets()) { + toAdd.add(targetId); + } + tappedMerfolkIds.put(event.getSourceId(), toAdd); + break; + } + } + } + } + } + } + } + } + + @Override + public void reset() { + super.reset(); + tappedMerfolkIds.clear(); + } +} From 93280d1835bb0d287bba1bbfb4eb44f312e53463 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:13:38 +0000 Subject: [PATCH 032/133] Implemented Vodalian War Machine --- .../main/java/mage/abilities/costs/common/TapTargetCost.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java index ef095d353cc..82ed1c371fa 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java @@ -78,6 +78,10 @@ public class TapTargetCost extends CostImpl { public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { return target.canChoose(sourceId, controllerId, game); } + + public TargetControlledPermanent getTarget() { + return target; + } @Override public TapTargetCost copy() { From 7b42c36644a310aa1605a944fed8c30315648957 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:14:50 +0000 Subject: [PATCH 033/133] Implemented Vodalian War Machine (Fallen Empires compete) --- Mage.Sets/src/mage/sets/FallenEmpires.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/FallenEmpires.java b/Mage.Sets/src/mage/sets/FallenEmpires.java index 224a0628318..a6a5870493a 100644 --- a/Mage.Sets/src/mage/sets/FallenEmpires.java +++ b/Mage.Sets/src/mage/sets/FallenEmpires.java @@ -268,6 +268,7 @@ public class FallenEmpires extends ExpansionSet { cards.add(new SetCardInfo("Vodalian Soldiers", 63, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vodalian Soldiers", 64, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vodalian Soldiers", 65, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vodalian War Machine", 66, Rarity.RARE, mage.cards.v.VodalianWarMachine.class)); cards.add(new SetCardInfo("Zelyon Sword", 176, Rarity.RARE, mage.cards.z.ZelyonSword.class)); } } From 155fbb86636e25a5170e60daf9b90515cbb18453 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 21 Feb 2018 12:02:58 -0600 Subject: [PATCH 034/133] - Added requested card Parallel Thoughts --- .../src/mage/cards/p/ParallelThoughts.java | 181 ++++++++++++++++++ Mage.Sets/src/mage/sets/Scourge.java | 1 + 2 files changed, 182 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/ParallelThoughts.java diff --git a/Mage.Sets/src/mage/cards/p/ParallelThoughts.java b/Mage.Sets/src/mage/cards/p/ParallelThoughts.java new file mode 100644 index 00000000000..8a803ed86ff --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParallelThoughts.java @@ -0,0 +1,181 @@ +/* + * 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.Arrays; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; +import mage.util.RandomUtil; + +/** + * + * @author jeffwadsworth + */ +public class ParallelThoughts extends CardImpl { + + public ParallelThoughts(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); + + // When Parallel Thoughts enters the battlefield, search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ParallelThoughtsSearchEffect())); + + // If you would draw a card, you may instead put the top card of the pile you exiled into your hand. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ParallelThoughtsReplacementEffect())); + + } + + public ParallelThoughts(final ParallelThoughts card) { + super(card); + } + + @Override + public ParallelThoughts copy() { + return new ParallelThoughts(this); + } +} + +class ParallelThoughtsSearchEffect extends OneShotEffect { + + ParallelThoughtsSearchEffect() { + super(Outcome.Neutral); + this.staticText = "search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library"; + } + + ParallelThoughtsSearchEffect(final ParallelThoughtsSearchEffect effect) { + super(effect); + } + + @Override + public ParallelThoughtsSearchEffect copy() { + return new ParallelThoughtsSearchEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + Cards cardsInExilePile = new CardsImpl(); + if (controller != null + && permanent != null) { + TargetCardInLibrary target = new TargetCardInLibrary(7, new FilterCard()); + if (controller.searchLibrary(target, game)) { + for (UUID targetId : target.getTargets()) { + Card card = controller.getLibrary().getCard(targetId, game); + if (card != null) { + cardsInExilePile.add(card); + } + } + // shuffle that exiled pile + + UUID[] shuffled = cardsInExilePile.toArray(new UUID[0]); + for (int n = shuffled.length - 1; n > 0; n--) { + int r = RandomUtil.nextInt(n + 1); + UUID temp = shuffled[n]; + shuffled[n] = shuffled[r]; + shuffled[r] = temp; + } + cardsInExilePile.clear(); + cardsInExilePile.addAll(Arrays.asList(shuffled)); + + // move to exile zone and turn face down + + for (Card card : cardsInExilePile.getCards(game)) { + controller.moveCardsToExile(card, source, game, false, CardUtil.getCardExileZoneId(game, source), permanent.getLogName()); + card.setFaceDown(true, game); + } + + // shuffle controller library + + controller.shuffleLibrary(source, game); + } + return true; + } + return false; + } +} + +class ParallelThoughtsReplacementEffect extends ReplacementEffectImpl { + + ParallelThoughtsReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.DrawCard); + staticText = "If you would draw a card, you may instead put the top card of the pile you exiled with Parallel Thoughts into your hand"; + } + + ParallelThoughtsReplacementEffect(final ParallelThoughtsReplacementEffect effect) { + super(effect); + } + + @Override + public ParallelThoughtsReplacementEffect copy() { + return new ParallelThoughtsReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null + && !game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)).getCards(game).isEmpty()) { + Card card = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)).getCards(game).iterator().next(); + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); + } + } + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getControllerId().equals(event.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index ad8585d5827..8dff799906e 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -142,6 +142,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Nefashu", 70, Rarity.RARE, mage.cards.n.Nefashu.class)); cards.add(new SetCardInfo("Noble Templar", 19, Rarity.COMMON, mage.cards.n.NobleTemplar.class)); cards.add(new SetCardInfo("One with Nature", 125, Rarity.UNCOMMON, mage.cards.o.OneWithNature.class)); + cards.add(new SetCardInfo("Parallel Thoughts", 44, Rarity.RARE, mage.cards.p.ParallelThoughts.class)); cards.add(new SetCardInfo("Pemmin's Aura", 45, Rarity.UNCOMMON, mage.cards.p.PemminsAura.class)); cards.add(new SetCardInfo("Primitive Etchings", 126, Rarity.RARE, mage.cards.p.PrimitiveEtchings.class)); cards.add(new SetCardInfo("Putrid Raptor", 71, Rarity.UNCOMMON, mage.cards.p.PutridRaptor.class)); From 3a2fe879dcdb1fdcaf6a4adc2542c0b7f5addb4c Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:42:00 +0000 Subject: [PATCH 035/133] Implemented Glyph of Doom --- Mage.Sets/src/mage/cards/g/GlyphOfDoom.java | 153 ++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GlyphOfDoom.java diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java b/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java new file mode 100644 index 00000000000..748661c0a08 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java @@ -0,0 +1,153 @@ +/* + * 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.g; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.BlockedAttackerWatcher; + +/** + * + * @author LevelX2 & L_J + */ +public class GlyphOfDoom extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Wall creature"); + + static { + filter.add(new SubtypePredicate(SubType.WALL)); + } + + public GlyphOfDoom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Choose target Wall creature. At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn. + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addEffect(new InfoEffect("Choose target Wall creature")); + this.getSpellAbility().addEffect(new GlyphOfDoomCreateDelayedTriggeredAbilityEffect()); + this.getSpellAbility().addWatcher(new BlockedAttackerWatcher()); + } + + public GlyphOfDoom(final GlyphOfDoom card) { + super(card); + } + + @Override + public GlyphOfDoom copy() { + return new GlyphOfDoom(this); + } +} + +class GlyphOfDoomCreateDelayedTriggeredAbilityEffect extends OneShotEffect { + + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect() { + super(Outcome.Benefit); + this.staticText = "At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn"; + } + + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect(final GlyphOfDoomCreateDelayedTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect copy() { + return new GlyphOfDoomCreateDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!source.getTargets().isEmpty() && source.getFirstTarget() != null) { + MageObjectReference mor = new MageObjectReference(source.getFirstTarget(), game); + AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new GlyphOfDoomEffect(mor)); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } + return false; + } +} + +class GlyphOfDoomEffect extends OneShotEffect { + + MageObjectReference targetCreature; + + public GlyphOfDoomEffect(MageObjectReference targetCreature) { + super(Outcome.DestroyPermanent); + this.targetCreature = targetCreature; + } + + public GlyphOfDoomEffect(final GlyphOfDoomEffect effect) { + super(effect); + targetCreature = effect.targetCreature; + } + + @Override + public GlyphOfDoomEffect copy() { + return new GlyphOfDoomEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && targetCreature != null) { + BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName()); + if (watcher != null) { + List toDestroy = new ArrayList<>(); + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) { + if (!creature.getId().equals(targetCreature.getSourceId())) { + if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), targetCreature, game)) { + toDestroy.add(creature); + } + } + } + for (Permanent creature : toDestroy) { + creature.destroy(source.getSourceId(), game, false); + } + return true; + } + } + return false; + } +} From a61fd3ecd5711ec6ba437bb1161cc14b6ccab0d8 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:42:50 +0000 Subject: [PATCH 036/133] Implemented Wood Elemental --- Mage.Sets/src/mage/cards/w/WoodElemental.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WoodElemental.java diff --git a/Mage.Sets/src/mage/cards/w/WoodElemental.java b/Mage.Sets/src/mage/cards/w/WoodElemental.java new file mode 100644 index 00000000000..492ce5485d7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WoodElemental.java @@ -0,0 +1,133 @@ +/* + * 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.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author L_J + */ +public class WoodElemental extends CardImpl { + + public WoodElemental(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // As Wood Elemental enters the battlefield, sacrifice any number of untapped Forests. + this.addAbility(new AsEntersBattlefieldAbility(new WoodElementalEffect())); + + // Wood Elemental's power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); + } + + public WoodElemental(final WoodElemental card) { + super(card); + } + + @Override + public WoodElemental copy() { + return new WoodElemental(this); + } +} + +class WoodElementalEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped Forests you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.FOREST)); + } + + public WoodElementalEffect() { + super(Outcome.Sacrifice); + staticText = "sacrifice any number of untapped Forests"; + } + + public WoodElementalEffect(final WoodElementalEffect effect) { + super(effect); + } + + @Override + public WoodElementalEffect copy() { + return new WoodElementalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card sourceCard = game.getCard(source.getSourceId()); + if (controller != null && sourceCard != null) { + Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, filter, true); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.chooseTarget(Outcome.Detriment, target, source, game)) { + if (!target.getTargets().isEmpty()) { + int sacrificedForests = target.getTargets().size(); + game.informPlayers(controller.getLogName() + " sacrifices " + sacrificedForests + " untapped Forests for " + sourceCard.getLogName()); + for (UUID targetId : target.getTargets()) { + Permanent targetPermanent = game.getPermanent(targetId); + if (targetPermanent != null) { + targetPermanent.sacrifice(source.getSourceId(), game); + } + } + game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + return true; + } + } + } + return false; + } +} From 4c6e13d2839a1982b42c9f01c75ade036f947673 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:44:13 +0000 Subject: [PATCH 037/133] Implemented Glyph of Doom and Wood Elemental --- Mage.Sets/src/mage/sets/Legends.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index ffd05358570..779015f3cdf 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -133,6 +133,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Giant Strength", 147, Rarity.COMMON, mage.cards.g.GiantStrength.class)); cards.add(new SetCardInfo("Giant Turtle", 102, Rarity.COMMON, mage.cards.g.GiantTurtle.class)); cards.add(new SetCardInfo("Glyph of Destruction", 148, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class)); + cards.add(new SetCardInfo("Glyph of Doom", 14, Rarity.COMMON, mage.cards.g.GlyphOfDoom.class)); cards.add(new SetCardInfo("Glyph of Life", 184, Rarity.COMMON, mage.cards.g.GlyphOfLife.class)); cards.add(new SetCardInfo("Gravity Sphere", 149, Rarity.RARE, mage.cards.g.GravitySphere.class)); cards.add(new SetCardInfo("Great Defender", 185, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); @@ -299,6 +300,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Winds of Change", 169, Rarity.UNCOMMON, mage.cards.w.WindsOfChange.class)); cards.add(new SetCardInfo("Winter Blast", 127, Rarity.RARE, mage.cards.w.WinterBlast.class)); cards.add(new SetCardInfo("Wolverine Pack", 128, Rarity.COMMON, mage.cards.w.WolverinePack.class)); + cards.add(new SetCardInfo("Wood Elemental", 129, Rarity.RARE, mage.cards.w.WoodElemental.class)); cards.add(new SetCardInfo("Xira Arien", 310, Rarity.RARE, mage.cards.x.XiraArien.class)); cards.add(new SetCardInfo("Zephyr Falcon", 86, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); } From 8b6d443fd385825698eb68bbeafd75f603d1afd0 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:44:51 +0000 Subject: [PATCH 038/133] Implemented Wood Elemental --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 03ff7d240ac..c60ecead216 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -316,6 +316,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Wild Aesthir", 34, Rarity.COMMON, mage.cards.w.WildAesthir.class)); cards.add(new SetCardInfo("Wild Griffin", 35, Rarity.COMMON, mage.cards.w.WildGriffin.class)); cards.add(new SetCardInfo("Wild Ox", 174, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); + cards.add(new SetCardInfo("Wood Elemental", 175, Rarity.RARE, mage.cards.w.WoodElemental.class)); cards.add(new SetCardInfo("Xenic Poltergeist", 104, Rarity.UNCOMMON, mage.cards.x.XenicPoltergeist.class)); cards.add(new SetCardInfo("Yotian Soldier", 240, Rarity.COMMON, mage.cards.y.YotianSoldier.class)); cards.add(new SetCardInfo("Zombie Master", 105, Rarity.UNCOMMON, mage.cards.z.ZombieMaster.class)); From f86cc8a04ad2fb1ec8ae16656533753f7c26ea8e Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 22:07:34 +0000 Subject: [PATCH 039/133] Implemented Orim's Prayer --- Mage.Sets/src/mage/cards/o/OrimsPrayer.java | 94 +++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/o/OrimsPrayer.java diff --git a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java new file mode 100644 index 00000000000..4ad6e297a70 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java @@ -0,0 +1,94 @@ +/* + * 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.o; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.dynamicvalue.common.AttackingCreatureCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +/** + * + * @author L_J + */ +public class OrimsPrayer extends CardImpl { + + public OrimsPrayer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); + + // Whenever one or more creatures attack you, you gain 1 life for each attacking creature. + this.addAbility(new OrimsPrayerTriggeredAbility()); + } + + public OrimsPrayer(final OrimsPrayer card) { + super(card); + } + + @Override + public OrimsPrayer copy() { + return new OrimsPrayer(this); + } +} + +class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { + + public OrimsPrayerTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainLifeEffect(new AttackingCreatureCount())); + } + + public OrimsPrayerTriggeredAbility(final OrimsPrayerTriggeredAbility ability) { + super(ability); + } + + @Override + public OrimsPrayerTriggeredAbility copy() { + return new OrimsPrayerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getCombat().getDefenders().contains(getControllerId()); + } + + @Override + public String getRule() { + return "Whenever one or more creatures attack you, " + super.getRule(); + } +} From 35a065cb326a64a7b2ee3b56a265ab94b2f06643 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 22:08:25 +0000 Subject: [PATCH 040/133] Implemented Orim's Prayer --- Mage.Sets/src/mage/sets/Tempest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Tempest.java b/Mage.Sets/src/mage/sets/Tempest.java index e764804b94d..26a956063fb 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -219,6 +219,7 @@ public class Tempest extends ExpansionSet { cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); cards.add(new SetCardInfo("Oracle en-Vec", 243, Rarity.RARE, mage.cards.o.OracleEnVec.class)); cards.add(new SetCardInfo("Orim, Samite Healer", 244, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); + cards.add(new SetCardInfo("Orim's Prayer", 245, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); cards.add(new SetCardInfo("Overrun", 137, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); cards.add(new SetCardInfo("Pacifism", 246, Rarity.COMMON, mage.cards.p.Pacifism.class)); cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); From f31b1ab7e35c4ea3e2adc6cca3bfd1ff550aa6af Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Thu, 22 Feb 2018 00:02:23 +0100 Subject: [PATCH 041/133] Improve some ability text --- .../src/mage/cards/b/BrassTalonChimera.java | 2 +- Mage.Sets/src/mage/cards/c/CallToGlory.java | 43 ++----------------- Mage.Sets/src/mage/cards/e/ExaltedDragon.java | 2 +- Mage.Sets/src/mage/cards/k/KaerveksSpite.java | 2 +- Mage.Sets/src/mage/cards/l/Leviathan.java | 2 +- Mage.Sets/src/mage/cards/l/LyevDecree.java | 4 +- .../src/mage/cards/m/MerfolkRaiders.java | 4 +- .../src/mage/cards/m/MilitantInquisitor.java | 2 +- Mage.Sets/src/mage/cards/s/SpireTracer.java | 2 +- Mage.Sets/src/mage/cards/s/SpiritCairn.java | 2 +- Mage.Sets/src/mage/cards/s/SpiritMirror.java | 2 +- .../common/CantBeRegeneratedTargetEffect.java | 1 - .../common/CreateTokenCopyTargetEffect.java | 2 +- .../common/PreventDamageByTargetEffect.java | 2 +- ...NextDamageFromChosenSourceToYouEffect.java | 2 +- ...ardOfLibraryIntoGraveEachPlayerEffect.java | 9 ++-- .../TapAllTargetPlayerControlsEffect.java | 2 +- .../cost/SpellsCostIncreasementAllEffect.java | 2 +- .../abilities/keyword/DefenderAbility.java | 2 +- .../abilities/keyword/LifelinkAbility.java | 2 +- .../abilities/keyword/ProtectionAbility.java | 2 +- .../abilities/keyword/TotemArmorAbility.java | 2 +- .../main/java/mage/filter/StaticFilters.java | 6 +++ 23 files changed, 37 insertions(+), 64 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java b/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java index fc3b22aba5f..7620727df5a 100644 --- a/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java +++ b/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java @@ -53,7 +53,7 @@ import java.util.UUID; */ public class BrassTalonChimera extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Chimera creature you control"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Chimera creature"); static { filter.add(new SubtypePredicate(SubType.CHIMERA)); diff --git a/Mage.Sets/src/mage/cards/c/CallToGlory.java b/Mage.Sets/src/mage/cards/c/CallToGlory.java index 9d678be5157..b2c31e6112d 100644 --- a/Mage.Sets/src/mage/cards/c/CallToGlory.java +++ b/Mage.Sets/src/mage/cards/c/CallToGlory.java @@ -28,21 +28,16 @@ package mage.cards.c; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.UntapAllEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @author Loki @@ -57,8 +52,9 @@ public class CallToGlory extends CardImpl { public CallToGlory(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); - - this.getSpellAbility().addEffect(new CalltoGloryFirstEffect()); + + //Untap all creatures you control. Samurai creatures you control get +1/+1 until end of turn. + this.getSpellAbility().addEffect(new UntapAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE)); this.getSpellAbility().addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn, filter, false)); } @@ -70,35 +66,4 @@ public class CallToGlory extends CardImpl { public CallToGlory copy() { return new CallToGlory(this); } - -} - -class CalltoGloryFirstEffect extends OneShotEffect { - - public CalltoGloryFirstEffect() { - super(Outcome.Untap); - staticText = "Untap all creatures you control"; - } - - public CalltoGloryFirstEffect(final CalltoGloryFirstEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Permanent creature : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game)) { - creature.untap(game); - } - return true; - } - return false; - } - - @Override - public CalltoGloryFirstEffect copy() { - return new CalltoGloryFirstEffect(this); - } - } diff --git a/Mage.Sets/src/mage/cards/e/ExaltedDragon.java b/Mage.Sets/src/mage/cards/e/ExaltedDragon.java index 55ee01bf792..941f15b0e4c 100644 --- a/Mage.Sets/src/mage/cards/e/ExaltedDragon.java +++ b/Mage.Sets/src/mage/cards/e/ExaltedDragon.java @@ -80,7 +80,7 @@ class ExaltedDragonCostToAttackBlockEffect extends PayCostToAttackBlockEffectImp ExaltedDragonCostToAttackBlockEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledLandPermanent("a land")))); - staticText = "{this} can't attack unless you sacrifice a land (This cost is paid as attackers are declared.)"; + staticText = "{this} can't attack unless you sacrifice a land. (This cost is paid as attackers are declared.)"; } ExaltedDragonCostToAttackBlockEffect(ExaltedDragonCostToAttackBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java index 349fa1ccf82..8602a8c41be 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java @@ -16,7 +16,7 @@ public class KaerveksSpite extends CardImpl { super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}"); // As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand. - this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("all permanents you control"))); + this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("permanents you control"))); this.getSpellAbility().addCost(new DiscardHandCost()); // Target player loses 5 life. diff --git a/Mage.Sets/src/mage/cards/l/Leviathan.java b/Mage.Sets/src/mage/cards/l/Leviathan.java index cf79670e671..7f5d4b1cb2f 100644 --- a/Mage.Sets/src/mage/cards/l/Leviathan.java +++ b/Mage.Sets/src/mage/cards/l/Leviathan.java @@ -118,7 +118,7 @@ class LeviathanCostToAttackBlockEffect extends PayCostToAttackBlockEffectImpl { LeviathanCostToAttackBlockEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, new SacrificeTargetCost(new TargetControlledPermanent(2, 2, filter, false))); - staticText = "{this} can't attack unless you sacrifice two Islands (This cost is paid as attackers are declared.)"; + staticText = "{this} can't attack unless you sacrifice two Islands. (This cost is paid as attackers are declared.)"; } LeviathanCostToAttackBlockEffect(LeviathanCostToAttackBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LyevDecree.java b/Mage.Sets/src/mage/cards/l/LyevDecree.java index c332e4bf099..70326ded261 100644 --- a/Mage.Sets/src/mage/cards/l/LyevDecree.java +++ b/Mage.Sets/src/mage/cards/l/LyevDecree.java @@ -47,7 +47,7 @@ import mage.target.common.TargetCreaturePermanent; public class LyevDecree extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponent controls"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponents control"); static { filter.add(new ControllerPredicate(TargetController.OPPONENT)); } @@ -56,7 +56,7 @@ public class LyevDecree extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); - // Detain up to two target creatures your opponent controls. + // Detain up to two target creatures your opponents control. this.getSpellAbility().addEffect(new DetainTargetEffect()); Target target = new TargetCreaturePermanent(0,2,filter,false); this.getSpellAbility().addTarget(target); diff --git a/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java b/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java index 21611866db1..09e408b51b5 100644 --- a/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java +++ b/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java @@ -50,10 +50,10 @@ public class MerfolkRaiders extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Phasing - this.addAbility(PhasingAbility.getInstance()); // Islandwalk this.addAbility(new IslandwalkAbility()); + // Phasing + this.addAbility(PhasingAbility.getInstance()); } public MerfolkRaiders(final MerfolkRaiders card) { diff --git a/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java b/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java index 05df6afc78a..9b3e8127988 100644 --- a/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java +++ b/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java @@ -49,7 +49,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class MilitantInquisitor extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("equipment you control"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); static { filter.add(new CardTypePredicate(CardType.ARTIFACT)); diff --git a/Mage.Sets/src/mage/cards/s/SpireTracer.java b/Mage.Sets/src/mage/cards/s/SpireTracer.java index 8c36e4d18c3..7846832cbf4 100644 --- a/Mage.Sets/src/mage/cards/s/SpireTracer.java +++ b/Mage.Sets/src/mage/cards/s/SpireTracer.java @@ -76,7 +76,7 @@ class CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect extends RestrictionE public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect() { super(Duration.WhileOnBattlefield); - staticText = "Can't be blocked except by creatures with flying or reach"; + staticText = "{this} can't be blocked except by creatures with flying or reach"; } public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect(final CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SpiritCairn.java b/Mage.Sets/src/mage/cards/s/SpiritCairn.java index 0153958df44..1d1f92e0b46 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritCairn.java +++ b/Mage.Sets/src/mage/cards/s/SpiritCairn.java @@ -51,7 +51,7 @@ public class SpiritCairn extends CardImpl { // Whenever a player discards a card, you may pay {W}. If you do, create a 1/1 white Spirit creature token with flying. this.addAbility(new SimpleTriggeredAbility(Zone.BATTLEFIELD, GameEvent.EventType.DISCARDED_CARD, new DoIfCostPaid(new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl("{W}")), - "Whenever a player discards a card, you ", + "Whenever a player discards a card, ", false, false)); } diff --git a/Mage.Sets/src/mage/cards/s/SpiritMirror.java b/Mage.Sets/src/mage/cards/s/SpiritMirror.java index 77edaee3128..e9c03b254c6 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritMirror.java +++ b/Mage.Sets/src/mage/cards/s/SpiritMirror.java @@ -66,7 +66,7 @@ public class SpiritMirror extends CardImpl { this.addAbility(new ConditionalTriggeredAbility( new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new ReflectionToken()), TargetController.YOU, false), new PermanentsOnTheBattlefieldCondition(filterToken, ComparisonType.EQUAL_TO, 0, false), - "At the beginning of your upkeep, if there are no Reflection tokens on the battlefield, create a 2/2 white Reflection creature token")); + "At the beginning of your upkeep, if there are no Reflection tokens on the battlefield, create a 2/2 white Reflection creature token.")); // {0}: Destroy target Reflection. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new GenericManaCost(0)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java index a0219501fb8..d844803f730 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java @@ -81,7 +81,6 @@ public class CantBeRegeneratedTargetEffect extends ContinuousRuleModifyingEffect } sb.append(" can't be regenerated"); if (!duration.toString().isEmpty()) { - sb.append(' '); if (duration == Duration.EndOfTurn) { sb.append(" this turn"); } else { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 993422a0d4b..6bb2ce18c58 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -286,7 +286,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { sb.append("tokens that are copies of target "); } if (!mode.getTargets().isEmpty()) { - sb.append(mode.getTargets().get(0).getMessage()); + sb.append(mode.getTargets().get(0).getTargetName()); } else { throw new UnsupportedOperationException("Using default rule generation of target effect without having a target object"); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java index e26affa441f..081cd81b8c8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java @@ -96,7 +96,7 @@ public class PreventDamageByTargetEffect extends PreventionEffectImpl { StringBuilder sb = new StringBuilder(); sb.append("Prevent all"); if (onlyCombat) { - sb.append(" combat "); + sb.append(" combat"); } sb.append(" damage target "); sb.append(mode.getTargets().get(0).getTargetName()).append(" would deal ").append(duration.toString()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java index 6164d9c2de3..86f10acf760 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java @@ -71,7 +71,7 @@ public class PreventNextDamageFromChosenSourceToYouEffect extends PreventionEffe StringBuilder sb = new StringBuilder("The next time a ").append(targetSource.getFilter().getMessage()); sb.append(" of your choice would deal damage to you"); if (duration == Duration.EndOfTurn) { - sb.append(" this turn"); + sb.append(" this turn"); } sb.append(", prevent that damage"); return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java index 77ebb1377df..0a378703ab4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java @@ -123,9 +123,12 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect throw new UnsupportedOperationException("TargetController type not supported."); } sb.append("puts the top "); - sb.append(CardUtil.numberToText(numberCards.toString(), "a")); - sb.append(" card"); - sb.append(numberCards.toString().equals("1") ? "" : "s"); + if(numberCards.toString().equals("1")) { + sb.append("card"); + } else { + sb.append(CardUtil.numberToText(numberCards.toString())); + sb.append(" cards"); + } sb.append(" of his or her library into his or her graveyard"); return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java index 08ca0c506f2..ac2bee4e1da 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java @@ -80,6 +80,6 @@ public class TapAllTargetPlayerControlsEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "tap all " + filter.toString() + " target " + mode.getTargets().get(0).getMessage() + " controls"; + return "tap all " + filter.toString() + " target " + mode.getTargets().get(0).getTargetName() + " controls"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java index 95d6d6e524b..6b371b0dc49 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java @@ -48,7 +48,7 @@ public class SpellsCostIncreasementAllEffect extends CostModificationEffectImpl private int amount; public SpellsCostIncreasementAllEffect(int amount) { - this(new FilterCard("All Spells "), amount); + this(new FilterCard("Spells"), amount); } public SpellsCostIncreasementAllEffect(FilterCard filter, int amount) { diff --git a/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java b/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java index 293ad9adb2a..a6cf1ce515b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java @@ -56,7 +56,7 @@ public class DefenderAbility extends StaticAbility implements MageSingleton { @Override public String getRule() { - return "Defender"; + return "defender"; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java b/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java index c22dde451aa..7a12b664265 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java @@ -56,7 +56,7 @@ public class LifelinkAbility extends StaticAbility implements MageSingleton { @Override public String getRule() { - return "Lifelink (Damage dealt by this creature also causes you to gain that much life.)"; + return "lifelink (Damage dealt by this creature also causes you to gain that much life.)"; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 5f1b480406e..46471af292f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -89,7 +89,7 @@ public class ProtectionAbility extends StaticAbility { @Override public String getRule() { - return "Protection from " + filter.getMessage() + (removeAuras ? "" : ". This effect doesn't remove auras."); + return "protection from " + filter.getMessage() + (removeAuras ? "" : ". This effect doesn't remove auras."); } public boolean canTarget(MageObject source, Game game) { diff --git a/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java b/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java index 604f1645040..f31280d1bf3 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java @@ -65,7 +65,7 @@ public class TotemArmorAbility extends SimpleStaticAbility { @Override public String getRule() { - return "Totem Armor (If enchanted creature would be destroyed, instead remove all damage from it and destroy this Aura.)"; + return "Totem armor (If enchanted creature would be destroyed, instead remove all damage from it and destroy this Aura.)"; } } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index c724734fa89..e6ccc996578 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -179,7 +179,13 @@ public final class StaticFilters { static { FILTER_CONTROLLED_CREATURE.setLockedFilter(true); + } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_CREATURES = new FilterControlledCreaturePermanent("creatures you control"); + + static { + FILTER_CONTROLLED_CREATURES.setLockedFilter(true); } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_A_CREATURE = new FilterControlledCreaturePermanent("a creature you control"); static { From bf3682fa77d708cda2f5e1e67307e28abba23a29 Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Thu, 22 Feb 2018 00:09:51 +0100 Subject: [PATCH 042/133] Move confirmation OptionPane from SessionImpl to ConsolePanel --- .../main/java/mage/remote/SessionImpl.java | 54 ++++++------------- .../mage/server/console/ConsolePanel.java | 42 +++++++++++++-- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 187b8179c1c..1f7bca2c0cb 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -32,7 +32,6 @@ import java.lang.reflect.UndeclaredThrowableException; import java.net.*; import java.util.*; import java.util.concurrent.TimeUnit; -import javax.swing.*; import mage.MageException; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; @@ -1437,12 +1436,9 @@ public class SessionImpl implements Session { @Override public boolean endUserSession(String userSessionId) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to end userSessionId " + userSessionId + '?', "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.endUserSession(sessionId, userSessionId); - return true; - } + if (isConnected()) { + server.endUserSession(sessionId, userSessionId); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1455,12 +1451,9 @@ public class SessionImpl implements Session { @Override public boolean muteUserChat(String userName, long durationMinutes) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to mute user " + userName + " for " + durationMinutes + " minutes?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.muteUser(sessionId, userName, durationMinutes); - return true; - } + if (isConnected()) { + server.muteUser(sessionId, userName, durationMinutes); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1473,12 +1466,9 @@ public class SessionImpl implements Session { @Override public boolean setActivation(String userName, boolean active) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to set active to " + active + " for user: " + userName + '?', "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.setActivation(sessionId, userName, active); - return true; - } + if (isConnected()) { + server.setActivation(sessionId, userName, active); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1491,20 +1481,9 @@ public class SessionImpl implements Session { @Override public boolean toggleActivation(String userName) { try { - if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to active?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - return setActivation(userName, true); - } - if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to INactive?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - return setActivation(userName, false); - } - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to toggle activation for user: " + userName + '?', "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.toggleActivation(sessionId, userName); - return true; - } + if (isConnected()) { + server.toggleActivation(sessionId, userName); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1517,12 +1496,9 @@ public class SessionImpl implements Session { @Override public boolean lockUser(String userName, long durationMinute) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to lock user: " + userName + " for " + durationMinute + " minutes?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.lockUser(sessionId, userName, durationMinute); - return true; - } + if (isConnected()) { + server.lockUser(sessionId, userName, durationMinute); + return true; } } catch (MageException ex) { handleMageException(ex); diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java index dd5e8b70a7e..af9afb46662 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java @@ -345,22 +345,54 @@ public class ConsolePanel extends javax.swing.JPanel { private void btnEndSessionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEndSessionActionPerformed int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().endUserSession((String) tableUserModel.getValueAt(row, TableUserModel.POS_GAME_INFO)); + String userSessionId = (String) tableUserModel.getValueAt(row, TableUserModel.POS_GAME_INFO); + + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to end userSessionId " + userSessionId + '?', "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().endUserSession(userSessionId); + } }//GEN-LAST:event_btnEndSessionActionPerformed private void btnMuteUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMuteUserActionPerformed - int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().muteUserChat((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME), ((Number) spinnerMuteDurationMinutes.getValue()).longValue()); + int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); + String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME); + long durationMinute = ((Number) spinnerMuteDurationMinutes.getValue()).longValue(); + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to mute user: " + userName + " for " + durationMinute + " minutes?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().muteUserChat(userName, durationMinute); + } }//GEN-LAST:event_btnMuteUserActionPerformed private void btnDeActivateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeActivateActionPerformed int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().toggleActivation((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME)); + String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME); + + if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to active?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().setActivation(userName, true); + return; + } + if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to inactive?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().setActivation(userName, false); + return; + } + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to toggle activation for user: " + userName + '?', "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().toggleActivation(userName); + return; + } + } }//GEN-LAST:event_btnDeActivateActionPerformed private void btnLockUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLockUserActionPerformed int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().lockUser((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME), ((Number) spinnerMuteDurationMinutes.getValue()).longValue()); + String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME); + long durationMinute = ((Number) spinnerMuteDurationMinutes.getValue()).longValue(); + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to lock user: " + userName + " for " + durationMinute + " minutes?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().lockUser(userName, durationMinute); + } }//GEN-LAST:event_btnLockUserActionPerformed private void btnRemoveTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveTableActionPerformed From 62b6ee30eb939eaa2774148bde91fde663772f9b Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 21 Feb 2018 17:18:35 -0600 Subject: [PATCH 043/133] - Added requested card Telekinetic Bonds --- .../src/mage/cards/t/TelekineticBonds.java | 66 +++++++++++++++++++ Mage.Sets/src/mage/sets/Judgment.java | 1 + .../DiscardsACardPlayerTriggeredAbility.java | 57 ++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TelekineticBonds.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/t/TelekineticBonds.java b/Mage.Sets/src/mage/cards/t/TelekineticBonds.java new file mode 100644 index 00000000000..7f77b779872 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TelekineticBonds.java @@ -0,0 +1,66 @@ +/* + * 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.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DiscardsACardPlayerTriggeredAbility; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.MayTapOrUntapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetPermanent; + +/** + * + * @author jeffwadsworth + */ +public class TelekineticBonds extends CardImpl { + + public TelekineticBonds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}{U}"); + + + // Whenever a player discards a card, you may pay {1}{U}. If you do, you may tap or untap target permanent. + Ability ability = new DiscardsACardPlayerTriggeredAbility(new DoIfCostPaid(new MayTapOrUntapTargetEffect(), new ManaCostsImpl("{1}{U}")), true); + ability.addTarget(new TargetPermanent()); + this.addAbility(ability); + + } + + public TelekineticBonds(final TelekineticBonds card) { + super(card); + } + + @Override + public TelekineticBonds copy() { + return new TelekineticBonds(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Judgment.java b/Mage.Sets/src/mage/sets/Judgment.java index 9007e49c9e7..fa99c1a1b31 100644 --- a/Mage.Sets/src/mage/sets/Judgment.java +++ b/Mage.Sets/src/mage/sets/Judgment.java @@ -163,6 +163,7 @@ public class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Swelter", 101, Rarity.UNCOMMON, mage.cards.s.Swelter.class)); cards.add(new SetCardInfo("Swirling Sandstorm", 102, Rarity.COMMON, mage.cards.s.SwirlingSandstorm.class)); cards.add(new SetCardInfo("Sylvan Safekeeper", 133, Rarity.RARE, mage.cards.s.SylvanSafekeeper.class)); + cards.add(new SetCardInfo("Telekinetic Bonds", 52, Rarity.RARE, mage.cards.t.TelekineticBonds.class)); cards.add(new SetCardInfo("Test of Endurance", 29, Rarity.RARE, mage.cards.t.TestOfEndurance.class)); cards.add(new SetCardInfo("Thriss, Nantuko Primus", 134, Rarity.RARE, mage.cards.t.ThrissNantukoPrimus.class)); cards.add(new SetCardInfo("Toxic Stench", 74, Rarity.COMMON, mage.cards.t.ToxicStench.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java new file mode 100644 index 00000000000..5c4ac7641cd --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ + + public class DiscardsACardPlayerTriggeredAbility extends TriggeredAbilityImpl { + + private SetTargetPointer setTargetPointer; + + public DiscardsACardPlayerTriggeredAbility(Effect effect, boolean isOptional) { + this(effect, isOptional, SetTargetPointer.NONE); + } + + public DiscardsACardPlayerTriggeredAbility(Effect effect, boolean isOptional, SetTargetPointer setTargetPointer) { + super(Zone.BATTLEFIELD, effect, isOptional); + this.setTargetPointer = setTargetPointer; + } + + public DiscardsACardPlayerTriggeredAbility(final DiscardsACardPlayerTriggeredAbility ability) { + super(ability); + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DiscardsACardPlayerTriggeredAbility copy() { + return new DiscardsACardPlayerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return true; + } + + @Override + public String getRule() { + return "Whenever player discards a card, " + super.getRule(); + } +} \ No newline at end of file From 75e028a1c30f1ac0d16f276fd62176f965bb5fdf Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 22 Feb 2018 08:06:03 -0600 Subject: [PATCH 044/133] - Added requested card Penance --- Mage.Sets/src/mage/cards/p/Penance.java | 115 ++++++++++++++++++ .../PutCardFromHandOnTopOfLibraryCost.java | 60 +++++++++ 2 files changed, 175 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/Penance.java create mode 100644 Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java diff --git a/Mage.Sets/src/mage/cards/p/Penance.java b/Mage.Sets/src/mage/cards/p/Penance.java new file mode 100644 index 00000000000..45616fedf34 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Penance.java @@ -0,0 +1,115 @@ +/* + * 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.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.TargetSource; + +/** + * + * @author jeffwadsworth + */ +public class Penance extends CardImpl { + + public Penance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Put a card from your hand on top of your library: The next time a black or red source of your choice would deal damage this turn, prevent that damage. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PenanceEffect(), new PutCardFromHandOnTopOfLibraryCost())); + + } + + public Penance(final Penance card) { + super(card); + } + + @Override + public Penance copy() { + return new Penance(this); + } +} + +class PenanceEffect extends PreventionEffectImpl { + + private final TargetSource target; + + public PenanceEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "The next time a black or red source of your choice would deal damage to you this turn, prevent that damage."; + this.target = new TargetSource(); + } + + public PenanceEffect(final PenanceEffect effect) { + super(effect); + this.target = effect.target.copy(); + } + + @Override + public PenanceEffect copy() { + return new PenanceEffect(this); + } + + @Override + public void init(Ability source, Game game) { + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + this.used = true; + this.discard(); // only one use + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!this.used + && super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getControllerId()) + && event.getSourceId().equals(target.getFirstTarget())) { + return (game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.BLACK) + || game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.RED)); + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java new file mode 100644 index 00000000000..44fa2a8a5e9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.costs.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author jeffwadsworth + */ + +public class PutCardFromHandOnTopOfLibraryCost extends CostImpl { + + public PutCardFromHandOnTopOfLibraryCost() { + this.text = "Put a card from your hand on top of your library"; + } + + public PutCardFromHandOnTopOfLibraryCost(PutCardFromHandOnTopOfLibraryCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + TargetCardInHand targetCardInHand = new TargetCardInHand(); + targetCardInHand.setRequired(false); + Card card; + if (targetCardInHand.canChoose(controllerId, game) + && controller.choose(Outcome.PreventDamage, targetCardInHand, sourceId, game)) { + card = game.getCard(targetCardInHand.getFirstTarget()); + paid = controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.HAND, true, true); + } + return paid; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + return (controller != null + && !controller.getHand().isEmpty()); + } + + @Override + public PutCardFromHandOnTopOfLibraryCost copy() { + return new PutCardFromHandOnTopOfLibraryCost(this); + } +} + From 88dd301f828d0000f0daef49bebc6f04dd3c2713 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 22 Feb 2018 08:09:44 -0600 Subject: [PATCH 045/133] - Added Penance to the Exodus set --- Mage.Sets/src/mage/sets/Exodus.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Exodus.java b/Mage.Sets/src/mage/sets/Exodus.java index 851bd916498..7c4066522ad 100644 --- a/Mage.Sets/src/mage/sets/Exodus.java +++ b/Mage.Sets/src/mage/sets/Exodus.java @@ -123,6 +123,7 @@ public class Exodus extends ExpansionSet { cards.add(new SetCardInfo("Pandemonium", 93, Rarity.RARE, mage.cards.p.Pandemonium.class)); cards.add(new SetCardInfo("Peace of Mind", 13, Rarity.UNCOMMON, mage.cards.p.PeaceOfMind.class)); cards.add(new SetCardInfo("Pegasus Stampede", 14, Rarity.UNCOMMON, mage.cards.p.PegasusStampede.class)); + cards.add(new SetCardInfo("Penance", 15, Rarity.UNCOMMON, mage.cards.p.Penance.class)); cards.add(new SetCardInfo("Pit Spawn", 70, Rarity.RARE, mage.cards.p.PitSpawn.class)); cards.add(new SetCardInfo("Plaguebearer", 71, Rarity.RARE, mage.cards.p.Plaguebearer.class)); cards.add(new SetCardInfo("Plated Rootwalla", 116, Rarity.COMMON, mage.cards.p.PlatedRootwalla.class)); From c02c5a175ba25842fe673ded1b00295c6fba55a9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 22 Feb 2018 17:46:23 +0100 Subject: [PATCH 046/133] * Fixed some target pointer handling. --- .../src/mage/cards/a/ActOfAuthority.java | 9 ++- .../src/mage/cards/a/AdarkarValkyrie.java | 2 +- Mage.Sets/src/mage/cards/a/AnkhOfMishra.java | 18 +++--- .../src/mage/cards/a/ArbiterOfTheIdeal.java | 41 ++++++------- .../src/mage/cards/a/ArchonOfRedemption.java | 57 ++++--------------- .../src/mage/cards/a/ArrogantBloodlord.java | 11 ++-- .../src/main/java/mage/abilities/Ability.java | 12 ++++ .../main/java/mage/abilities/AbilityImpl.java | 13 +++++ .../java/mage/game/stack/StackAbility.java | 10 +++- .../targetpointer/FirstTargetPointer.java | 20 +++++-- .../target/targetpointer/FixedTarget.java | 22 +++---- .../targetpointer/SecondTargetPointer.java | 16 +++++- .../target/targetpointer/TargetPointer.java | 6 ++ .../targetpointer/ThirdTargetPointer.java | 11 ++++ 14 files changed, 140 insertions(+), 108 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java index 913dc43fbba..38dcd6ca161 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java +++ b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java @@ -94,9 +94,12 @@ class ActOfAuthorityEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent targetPermanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetPermanent != null && new ExileTargetEffect().apply(game, source)) { - ContinuousEffect effect = new ActOfAuthorityGainControlEffect(Duration.Custom, targetPermanent.getControllerId()); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); - game.addEffect(effect, source); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null) { + ContinuousEffect effect = new ActOfAuthorityGainControlEffect(Duration.Custom, targetPermanent.getControllerId()); + effect.setTargetPointer(new FixedTarget(sourcePermanent, game)); + game.addEffect(effect, source); + } return true; } return false; diff --git a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java index ae3abfebab8..0fccaadd0dd 100644 --- a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java +++ b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java @@ -109,7 +109,7 @@ class AdarkarValkyrieEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(new FixedTarget(this.getTargetPointer().getFirst(game, source))); + DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(getTargetPointer().getFixedTarget(game, source)); game.addDelayedTriggeredAbility(delayedAbility, source); return false; } diff --git a/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java b/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java index 38272e1e8ef..a31aa9dbf4c 100644 --- a/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java +++ b/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java @@ -45,16 +45,16 @@ import mage.target.targetpointer.FixedTarget; /** * * @author KholdFuzion - + * */ public class AnkhOfMishra extends CardImpl { - + public AnkhOfMishra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Whenever a land enters the battlefield, Ankh of Mishra deals 2 damage to that land's controller. this.addAbility(new AnkhOfMishraAbility()); - + } public AnkhOfMishra(final AnkhOfMishra card) { @@ -70,16 +70,16 @@ public class AnkhOfMishra extends CardImpl { class AnkhOfMishraAbility extends TriggeredAbilityImpl { public AnkhOfMishraAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(2)); + super(Zone.BATTLEFIELD, new DamageTargetEffect(2)); } AnkhOfMishraAbility(final AnkhOfMishraAbility ability) { - super(ability); + super(ability); } @Override public AnkhOfMishraAbility copy() { - return new AnkhOfMishraAbility(this); + return new AnkhOfMishraAbility(this); } @Override @@ -104,6 +104,6 @@ class AnkhOfMishraAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a land enters the battlefield, Ankh of Mishra deals 2 damage to that land's controller."; + return "Whenever a land enters the battlefield, {this} deals 2 damage to that land's controller."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java b/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java index 31453ff15bf..de172fddb18 100644 --- a/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java +++ b/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java @@ -29,6 +29,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -53,7 +54,7 @@ import mage.target.targetpointer.FixedTarget; public class ArbiterOfTheIdeal extends CardImpl { public ArbiterOfTheIdeal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); this.subtype.add(SubType.SPHINX); this.power = new MageInt(4); @@ -79,6 +80,7 @@ public class ArbiterOfTheIdeal extends CardImpl { class ArbiterOfTheIdealEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard(); + static { filter.add(Predicates.or( new CardTypePredicate(CardType.ARTIFACT), @@ -102,32 +104,25 @@ class ArbiterOfTheIdealEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } - - if (player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); - Cards cards = new CardsImpl(); - cards.add(card); - player.revealCards("Arbiter of the Ideal", cards, game); - - if (card != null) { - if (filter.match(card, game) && player.chooseUse(outcome, new StringBuilder("Put ").append(card.getName()).append("onto battlefield?").toString(), source, game)) { - card.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), source.getControllerId()); - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - permanent.addCounters(new Counter("Manifestation"), source, game); - ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT); - effect.setTargetPointer(new FixedTarget(permanent.getId())); - game.addEffect(effect, source); - } + Card card = controller.getLibrary().getFromTop(game); + if (card != null) { + controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); + if (filter.match(card, game) && controller.chooseUse(outcome, "Put " + card.getName() + "onto battlefield?", source, game)) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + permanent.addCounters(new Counter("Manifestation"), source, game); + ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); } } - return true; } - - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java index 6b3507d3487..50342b73b40 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java @@ -29,23 +29,18 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @author Loki @@ -53,7 +48,7 @@ import mage.target.targetpointer.FixedTarget; public class ArchonOfRedemption extends CardImpl { public ArchonOfRedemption(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.ARCHON); this.power = new MageInt(3); @@ -75,8 +70,9 @@ public class ArchonOfRedemption extends CardImpl { } class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { + ArchonOfRedemptionTriggeredAbility() { - super(Zone.BATTLEFIELD, new ArchonOfRedemptionEffect(), true); + super(Zone.BATTLEFIELD, null, true); } ArchonOfRedemptionTriggeredAbility(final ArchonOfRedemptionTriggeredAbility ability) { @@ -95,15 +91,13 @@ class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanent(targetId); - if (permanent.getControllerId().equals(this.controllerId) + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent.getControllerId().equals(getControllerId()) && permanent.isCreature() - && (targetId.equals(this.getSourceId()) - || (permanent.getAbilities().contains(FlyingAbility.getInstance()) && !targetId.equals(this.getSourceId())))) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } + && (permanent.getId().equals(getSourceId()) + || (permanent.getAbilities().contains(FlyingAbility.getInstance())))) { + this.getEffects().clear(); + this.addEffect(new GainLifeEffect(permanent.getPower().getValue())); return true; } return false; @@ -111,35 +105,6 @@ class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power"; + return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power."; } } - -class ArchonOfRedemptionEffect extends OneShotEffect { - ArchonOfRedemptionEffect() { - super(Outcome.GainLife); - } - - ArchonOfRedemptionEffect(final ArchonOfRedemptionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(targetPointer.getFirst(game, source)); - Player player = game.getPlayer(source.getControllerId()); - if (p == null) { - p = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); - } - if (p != null && player != null) { - player.gainLife(p.getPower().getValue(), game); - return true; - } - return false; - } - - @Override - public ArchonOfRedemptionEffect copy() { - return new ArchonOfRedemptionEffect(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java b/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java index e6c11100593..0aa16e9cfe5 100644 --- a/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java +++ b/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java @@ -53,7 +53,7 @@ import mage.target.targetpointer.FixedTarget; public class ArrogantBloodlord extends CardImpl { public ArrogantBloodlord(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.KNIGHT); @@ -104,11 +104,8 @@ class ArrogantBloodlordTriggeredAbility extends TriggeredAbilityImpl { && Objects.equals(blocked, arrogantBloodlord)) { return true; } - if (blocker != null && Objects.equals(blocker, arrogantBloodlord) - && game.getPermanent(event.getTargetId()).getPower().getValue() < 2) { - return true; - } - return false; + return blocker != null && Objects.equals(blocker, arrogantBloodlord) + && game.getPermanent(event.getTargetId()).getPower().getValue() < 2; } @Override @@ -133,7 +130,7 @@ class ArrogantBloodlordEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); - delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(source.getSourceId())); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); game.addDelayedTriggeredAbility(delayedAbility, source); return true; } diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index d6baff95d43..d0a8f783aa9 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -45,6 +45,7 @@ import mage.constants.Zone; import mage.game.Controllable; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.Targets; @@ -535,6 +536,17 @@ public interface Ability extends Controllable, Serializable { */ MageObject getSourceObjectIfItStillExists(Game game); + /** + * Returns the permanent that actually existed while the ability triggerd or + * an ability was activated only if it has not changed zone meanwhile. If + * not set yet, the current permanent if one exists will be retrieved from + * the game and returned. + * + * @param game + * @return + */ + Permanent getSourcePermanentIfItStillExists(Game game); + String getTargetDescription(Targets targets, Game game); void setCanFizzle(boolean canFizzle); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 23f81762129..25528d242b6 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1211,6 +1211,19 @@ public abstract class AbilityImpl implements Ability { return null; } + @Override + public Permanent getSourcePermanentIfItStillExists(Game game) { + if (sourceObject == null) { + setSourceObject(game.getObject(getSourceId()), game); + } + if (sourceObject instanceof Permanent) { + if (game.getState().getZoneChangeCounter(getSourceId()) == getSourceObjectZoneChangeCounter()) { + return (Permanent) sourceObject; + } + } + return null; + } + @Override public int getSourceObjectZoneChangeCounter() { return sourceObjectZoneChangeCounter; diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 07509f57863..00eabb629a5 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -50,6 +50,7 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.Targets; @@ -529,12 +530,17 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public MageObject getSourceObject(Game game) { - return game.getBaseObject(getSourceId()); + return this.ability.getSourceObject(game); } @Override public MageObject getSourceObjectIfItStillExists(Game game) { - throw new UnsupportedOperationException("Not supported."); + return this.ability.getSourceObjectIfItStillExists(game); + } + + @Override + public Permanent getSourcePermanentIfItStillExists(Game game) { + return this.ability.getSourcePermanentIfItStillExists(game); } @Override diff --git a/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java index 01d2b3c7db8..82241949ebf 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java @@ -35,7 +35,7 @@ public class FirstTargetPointer implements TargetPointer { Card card = game.getCard(target); if (card != null) { this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); - } + } } } } @@ -66,9 +66,9 @@ public class FirstTargetPointer implements TargetPointer { if (zoneChangeCounter.containsKey(targetId)) { Card card = game.getCard(targetId); if (card != null && zoneChangeCounter.containsKey(targetId) - && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { - // because if dies trigger has to trigger as permanent has already moved zone, we have to check if target was on the battlefield immed. before - // but no longer if new permanent is already on the battlefield + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + // because if dies trigger has to trigger as permanent has already moved zone, we have to check if target was on the battlefield immed. before + // but no longer if new permanent is already on the battlefield Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); if (permanent == null || permanent.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { return null; @@ -82,4 +82,16 @@ public class FirstTargetPointer implements TargetPointer { public TargetPointer copy() { return new FirstTargetPointer(this); } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } + } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java index 397e4e7de3d..c931962322d 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java @@ -1,15 +1,14 @@ package mage.target.targetpointer; -import mage.abilities.Ability; -import mage.cards.Card; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; - import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; public class FixedTarget implements TargetPointer { @@ -65,10 +64,7 @@ public class FixedTarget implements TargetPointer { public void init(Game game, Ability source) { if (!initialized) { initialized = true; - Card card = game.getCard(targetId); - if (card != null) { - this.zoneChangeCounter = card.getZoneChangeCounter(game); - } + this.zoneChangeCounter = game.getState().getZoneChangeCounter(targetId); } } @@ -121,4 +117,10 @@ public class FixedTarget implements TargetPointer { return permanent; } + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + init(game, source); + return this; + } + } diff --git a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java index db301a70553..4917726dfa6 100644 --- a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java @@ -1,11 +1,10 @@ package mage.target.targetpointer; +import java.util.*; import mage.abilities.Ability; import mage.cards.Card; import mage.game.Game; -import java.util.*; - public class SecondTargetPointer implements TargetPointer { private Map zoneChangeCounter = new HashMap<>(); @@ -59,7 +58,7 @@ public class SecondTargetPointer implements TargetPointer { if (zoneChangeCounter.containsKey(targetId)) { Card card = game.getCard(targetId); if (card != null && zoneChangeCounter.containsKey(targetId) - && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { return null; } } @@ -72,4 +71,15 @@ public class SecondTargetPointer implements TargetPointer { public TargetPointer copy() { return new SecondTargetPointer(this); } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java index 0b77cd2bf67..6dd021c9ac5 100644 --- a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java @@ -7,8 +7,14 @@ import mage.abilities.Ability; import mage.game.Game; public interface TargetPointer extends Serializable { + void init(Game game, Ability source); + List getTargets(Game game, Ability source); + UUID getFirst(Game game, Ability source); + TargetPointer copy(); + + FixedTarget getFixedTarget(Game game, Ability source); } diff --git a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java index 2fdbf05037f..206bfe2eb9e 100644 --- a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java @@ -84,4 +84,15 @@ public class ThirdTargetPointer implements TargetPointer { public TargetPointer copy() { return new ThirdTargetPointer(this); } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } } From 7bbe01b25aa00409c2afe198917d23f4c1bda55a Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 06:35:13 +0000 Subject: [PATCH 047/133] Updated Vodalian War Machine watcher with MageObjectReference --- .../src/mage/cards/v/VodalianWarMachine.java | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 09e17dcfd92..7a972a8726e 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -132,9 +133,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId()) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId()).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), game).contains(permanent.getId())) { permanent.destroy(source.getSourceId(), game, false); } } @@ -148,7 +149,7 @@ class VodalianWarMachineEffect extends OneShotEffect { class VodalianWarMachineWatcher extends Watcher { - public Map> tappedMerfolkIds = new HashMap<>(); + public Map> tappedMerfolkIds = new HashMap<>(); public VodalianWarMachineWatcher() { super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); @@ -164,33 +165,38 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId) { - return tappedMerfolkIds.get(sourceId); + public Set getTappedMerfolkIds(UUID sourceId, Game game) { + MageObjectReference mor = new MageObjectReference(sourceId, game); + return tappedMerfolkIds.get(mor); } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { if (event.getSourceId() != null) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null) { - Ability ability = stackAbility.getStackAbility(); - if (ability != null) { - for (Cost cost : ability.getCosts()) { - if (cost instanceof TapTargetCost && cost.isPaid()) { - TapTargetCost tapCost = (TapTargetCost) cost; - if (tapCost.getTarget().isChosen()) { - Set toAdd; - if (tappedMerfolkIds.get(event.getSourceId()) == null) { - toAdd = new HashSet<>(); - } else { - toAdd = tappedMerfolkIds.get(event.getSourceId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (sourcePermanent != null) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null) { + Ability ability = stackAbility.getStackAbility(); + if (ability != null) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapTargetCost && cost.isPaid()) { + TapTargetCost tapCost = (TapTargetCost) cost; + if (tapCost.getTarget().isChosen()) { + MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game) + 1, game); + Set toAdd; + if (tappedMerfolkIds.get(mor) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = tappedMerfolkIds.get(mor); + } + for (UUID targetId : tapCost.getTarget().getTargets()) { + toAdd.add(targetId); + } + tappedMerfolkIds.put(mor, toAdd); + break; } - for (UUID targetId : tapCost.getTarget().getTargets()) { - toAdd.add(targetId); - } - tappedMerfolkIds.put(event.getSourceId(), toAdd); - break; } } } From 5cb1d75a1d3eab8acffb674ddeeccaed480558ca Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 07:10:19 +0000 Subject: [PATCH 048/133] Some more changes Now it catches even instances of being exiled from owner's GY while the ability is still on the stack --- .../src/mage/cards/v/VodalianWarMachine.java | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 7a972a8726e..75c3b50aab7 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -39,6 +39,7 @@ import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -58,6 +59,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.target.common.TargetControlledCreaturePermanent; @@ -93,7 +95,7 @@ public class VodalianWarMachine extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. - this.addAbility(new DiesTriggeredAbility(new VodalianWarMachineEffect(), false), new VodalianWarMachineWatcher()); + this.addAbility(new VodalianWarMachineTriggeredAbility(), new VodalianWarMachineWatcher()); } public VodalianWarMachine(final VodalianWarMachine card) { @@ -106,7 +108,51 @@ public class VodalianWarMachine extends CardImpl { } } +class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { + + public VodalianWarMachineTriggeredAbility() { + super(new VodalianWarMachineEffect(), false); + } + + public VodalianWarMachineTriggeredAbility(VodalianWarMachineTriggeredAbility ability) { + super(ability); + } + + @Override + public VodalianWarMachineTriggeredAbility copy() { + return new VodalianWarMachineTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent before = ((ZoneChangeEvent) event).getTarget(); + if (before == null) { + return false; + } + if (super.checkTrigger(event, game)) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget().isTransformable()) { + if (!zEvent.getTarget().getAbilities().contains(this)) { + return false; + } + } + for (Effect effect : getEffects()) { + effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); + if (effect instanceof VodalianWarMachineEffect) { + VodalianWarMachineEffect effectToSet = (VodalianWarMachineEffect) effect; + effectToSet.counter = before.getZoneChangeCounter(game); + } + } + return true; + } + return false; + } + +} + class VodalianWarMachineEffect extends OneShotEffect { + + protected int counter; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); @@ -121,6 +167,7 @@ class VodalianWarMachineEffect extends OneShotEffect { public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { super(effect); + counter = effect.counter; } @Override @@ -133,9 +180,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), game) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), game).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game).contains(permanent.getId())) { permanent.destroy(source.getSourceId(), game, false); } } @@ -165,8 +212,8 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId, Game game) { - MageObjectReference mor = new MageObjectReference(sourceId, game); + public Set getTappedMerfolkIds(UUID sourceId, int counter, Game game) { + MageObjectReference mor = new MageObjectReference(sourceId, counter, game); return tappedMerfolkIds.get(mor); } @@ -184,7 +231,7 @@ class VodalianWarMachineWatcher extends Watcher { if (cost instanceof TapTargetCost && cost.isPaid()) { TapTargetCost tapCost = (TapTargetCost) cost; if (tapCost.getTarget().isChosen()) { - MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game) + 1, game); + MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game), game); Set toAdd; if (tappedMerfolkIds.get(mor) == null) { toAdd = new HashSet<>(); From 03454d326196d33411ab3b989e6ead643c1f83c4 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:40:12 +0000 Subject: [PATCH 049/133] Wood Elemental edit --- Mage.Sets/src/mage/cards/w/WoodElemental.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/w/WoodElemental.java b/Mage.Sets/src/mage/cards/w/WoodElemental.java index 492ce5485d7..ac6bc424c7e 100644 --- a/Mage.Sets/src/mage/cards/w/WoodElemental.java +++ b/Mage.Sets/src/mage/cards/w/WoodElemental.java @@ -70,7 +70,7 @@ public class WoodElemental extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new WoodElementalEffect())); // Wood Elemental's power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); } public WoodElemental(final WoodElemental card) { @@ -123,7 +123,7 @@ class WoodElementalEffect extends OneShotEffect { targetPermanent.sacrifice(source.getSourceId(), game); } } - game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.Custom, SubLayer.SetPT_7b), source); return true; } } From 38930c707948156c6265af82bc22237b250b2860 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:41:10 +0000 Subject: [PATCH 050/133] Minion of the Wastes --- Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java index 35064a7591b..2714adcea44 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java @@ -105,7 +105,7 @@ class MinionOfTheWastesEffect extends OneShotEffect { controller.loseLife(payAmount, game, false); game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) .append(" pays ").append(payAmount).append(" life").toString()); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); return true; } return false; From 2a26ef24a536cc5d80dd22586b3fb4c327560db3 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:41:27 +0000 Subject: [PATCH 051/133] Nameless Race edit --- Mage.Sets/src/mage/cards/n/NamelessRace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/n/NamelessRace.java b/Mage.Sets/src/mage/cards/n/NamelessRace.java index 0f413268ebf..615f1a48fc4 100644 --- a/Mage.Sets/src/mage/cards/n/NamelessRace.java +++ b/Mage.Sets/src/mage/cards/n/NamelessRace.java @@ -128,7 +128,7 @@ class NamelessRaceEffect extends OneShotEffect { controller.loseLife(payAmount, game, false); game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) .append(" pays ").append(payAmount).append(" life").toString()); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); return true; } return false; From a2b6f83a3e52c7c52bea7e02c9800a17efdbc0a1 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 23 Feb 2018 13:02:22 +0400 Subject: [PATCH 052/133] Fixed compile error --- .../src/main/java/mage/server/console/ConsolePanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java index af9afb46662..4b729539d87 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java @@ -381,7 +381,6 @@ public class ConsolePanel extends javax.swing.JPanel { JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { ConsoleFrame.getSession().toggleActivation(userName); return; - } } }//GEN-LAST:event_btnDeActivateActionPerformed From 5b0e71021dccb0cf5590a23e768a59b033856726 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 23 Feb 2018 16:15:59 +0400 Subject: [PATCH 053/133] Fixed compile error on getFixedTarget and failed test on new lower names --- Mage.Sets/src/mage/cards/p/Progenitus.java | 5 ----- .../src/mage/cards/t/TeferisProtection.java | 7 +------ .../test/cards/single/lrw/CairnWandererTest.java | 16 ++++++++-------- .../mage/target/targetpointer/FixedTargets.java | 9 +++++++++ .../targetpointer/SecondTargetPointer.java | 1 - 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/Progenitus.java b/Mage.Sets/src/mage/cards/p/Progenitus.java index ecfc73fa509..afec039d10f 100644 --- a/Mage.Sets/src/mage/cards/p/Progenitus.java +++ b/Mage.Sets/src/mage/cards/p/Progenitus.java @@ -93,11 +93,6 @@ class ProgenitusProtectionAbility extends ProtectionAbility { return new ProgenitusProtectionAbility(this); } - @Override - public String getRule() { - return "Protection from everything"; - } - @Override public boolean canTarget(MageObject source, Game game) { return false; diff --git a/Mage.Sets/src/mage/cards/t/TeferisProtection.java b/Mage.Sets/src/mage/cards/t/TeferisProtection.java index 98701104c96..ec5de97c6b7 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisProtection.java +++ b/Mage.Sets/src/mage/cards/t/TeferisProtection.java @@ -141,12 +141,7 @@ class TeferisProtectionAbility extends ProtectionAbility { public TeferisProtectionAbility copy() { return new TeferisProtectionAbility(this); } - - @Override - public String getRule() { - return "Protection from everything"; - } - + @Override public boolean canTarget(MageObject source, Game game) { return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java index 2f3e6851fe3..6f96643b134 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java @@ -80,28 +80,28 @@ public class CairnWandererTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerA, "Typhoid Rats"); // Testing HasteAbility. - addCard(Zone.GRAVEYARD, playerA, "Raging Goblin"); + addCard(Zone.GRAVEYARD, playerB, "Raging Goblin"); // Testing LandwalkAbility. - addCard(Zone.GRAVEYARD, playerA, "Zodiac Rooster"); + addCard(Zone.GRAVEYARD, playerB, "Zodiac Rooster"); // Testing LifelinkAbility. - addCard(Zone.GRAVEYARD, playerA, "Trained Caracal"); + addCard(Zone.GRAVEYARD, playerB, "Trained Caracal"); // Testing ProtectionAbility. - addCard(Zone.GRAVEYARD, playerA, "Progenitus"); + addCard(Zone.GRAVEYARD, playerB, "Progenitus"); // Testing ReachAbility. - addCard(Zone.GRAVEYARD, playerA, "Tree Monkey"); + addCard(Zone.GRAVEYARD, playerB, "Tree Monkey"); // Testing TrampleAbility. - addCard(Zone.GRAVEYARD, playerA, "Defiant Elf"); + addCard(Zone.GRAVEYARD, playerB, "Defiant Elf"); // Testing ShroudAbility. - addCard(Zone.GRAVEYARD, playerA, "Elvish Lookout"); + addCard(Zone.GRAVEYARD, playerB, "Elvish Lookout"); // Testing VigilanceAbility. - addCard(Zone.GRAVEYARD, playerA, "Veteran Cavalier"); + addCard(Zone.GRAVEYARD, playerB, "Veteran Cavalier"); execute(); diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index bb231a51c71..d5c099526b2 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -87,4 +87,13 @@ public class FixedTargets implements TargetPointer { return new FixedTargets(this); } + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java index 4917726dfa6..9d2779b4efd 100644 --- a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java @@ -80,6 +80,5 @@ public class SecondTargetPointer implements TargetPointer { return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); } return null; - } } From ecb95db541e5b86554cd1d1d1ed6afc67b9d73be Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 13:36:23 +0000 Subject: [PATCH 054/133] Eye for an Eye rewrite (fixes #4103) --- Mage.Sets/src/mage/cards/e/EyeForAnEye.java | 103 ++++++++++---------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/EyeForAnEye.java b/Mage.Sets/src/mage/cards/e/EyeForAnEye.java index 08245b25ab3..9c62933fbf9 100644 --- a/Mage.Sets/src/mage/cards/e/EyeForAnEye.java +++ b/Mage.Sets/src/mage/cards/e/EyeForAnEye.java @@ -28,24 +28,22 @@ package mage.cards.e; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; +import mage.target.TargetSource; /** - * - * @author MarcoMarin + * + * @author L_J */ public class EyeForAnEye extends CardImpl { @@ -53,8 +51,7 @@ public class EyeForAnEye extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{W}"); // The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and Eye for an Eye deals that much damage to that source's controller. - this.addAbility(new EyeForAnEyeTriggeredAbility(new EyeForAnEyeEffect())); - + this.getSpellAbility().addEffect(new EyeForAnEyeEffect()); } public EyeForAnEye(final EyeForAnEye card) { @@ -67,48 +64,19 @@ public class EyeForAnEye extends CardImpl { } } -class EyeForAnEyeTriggeredAbility extends TriggeredAbilityImpl { +class EyeForAnEyeEffect extends ReplacementEffectImpl { - public EyeForAnEyeTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - public EyeForAnEyeTriggeredAbility(final EyeForAnEyeTriggeredAbility ability) { - super(ability); - } - - @Override - public EyeForAnEyeTriggeredAbility copy() { - return new EyeForAnEyeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject sourceObject = game.getObject(event.getSourceId()); - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(sourceObject.getId()))); - return true; - } - - @Override - public String getRule() { - return "The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and {this} deals that much damage to that source's controller."; - } -} - -class EyeForAnEyeEffect extends OneShotEffect { + private final TargetSource damageSource; public EyeForAnEyeEffect() { - super(Outcome.Damage); + super(Duration.EndOfTurn, Outcome.RedirectDamage); + staticText = "The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and {this} deals that much damage to that source's controller"; + this.damageSource = new TargetSource(); } public EyeForAnEyeEffect(final EyeForAnEyeEffect effect) { super(effect); + this.damageSource = effect.damageSource.copy(); } @Override @@ -116,15 +84,48 @@ class EyeForAnEyeEffect extends OneShotEffect { return new EyeForAnEyeEffect(this); } + @Override + public void init(Ability source, Game game) { + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + @Override public boolean apply(Game game, Ability source) { - Integer damageAmount = (Integer) this.getValue("damageAmount"); - UUID targetId = this.targetPointer.getFirst(game, source); - if (damageAmount != null && targetId != null) { - Player player = game.getPlayer(targetId); - if (player != null) { - player.damage(damageAmount, targetId, game, false, true); + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + DamageEvent damageEvent = (DamageEvent) event; + if (controller != null) { + controller.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects()); + UUID sourceControllerId = game.getControllerId(damageEvent.getSourceId()); + if (sourceControllerId != null) { + Player sourceController = game.getPlayer(sourceControllerId); + if (sourceController != null) { + sourceController.damage(damageEvent.getAmount(), source.getSourceId(), game, false, true); return true; + } + } + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + DamageEvent damageEvent = (DamageEvent) event; + if (controller != null) { + if (controller.getId() == damageEvent.getTargetId() && damageEvent.getSourceId().equals(damageSource.getFirstTarget())) { + this.discard(); + return true; } } return false; From e5ef545d8f8f3a818386bfbc9d5325d9e75d3bbe Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 23 Feb 2018 18:21:08 +0400 Subject: [PATCH 055/133] * UI: fixed wrong feedback panel color for non active turn; --- Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java | 1 - Mage.Client/src/main/java/mage/client/game/HelperPanel.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 5992f5a424c..de2eb915056 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -64,7 +64,6 @@ public class FeedbackPanel extends javax.swing.JPanel { private static final Logger LOGGER = Logger.getLogger(FeedbackPanel.class); public enum FeedbackMode { - INFORM, QUESTION, CONFIRM, CANCEL, SELECT, END } diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index 04ccc0db76a..1bd86d7a3da 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -394,7 +394,7 @@ public class HelperPanel extends JPanel { } } else { // inform about other players - this.setOpaque(false); + this.mainPanel.setOpaque(false); } if (buttons.size() == 0) { From ac09be4b2b6651793e91d96ab60563d414a6cd90 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 14:45:01 +0000 Subject: [PATCH 056/133] Fixed Torment of Hailfire interaction with Sigarda (fixes #4452) --- .../src/mage/cards/t/TormentOfHailfire.java | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java index 504c68648cc..7855e56b7a3 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java @@ -86,31 +86,37 @@ class TormentOfHailfireEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int repeat = source.getManaCostsToPay().getX(); - for (int i = 0; i < repeat; i++) { + for (int i = 1; i <= repeat; i++) { for (UUID opponentId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); - if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", - "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - if (opponent.choose(outcome, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - continue; + boolean hasChosen = false; + while (!hasChosen) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); + if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", + "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); + if (opponent.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + if (permanent.sacrifice(source.getSourceId(), game)) { + hasChosen = true; + continue; + } + } } } + if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", + "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { + opponent.discardOne(false, source, game); + hasChosen = true; + continue; + } + opponent.loseLife(3, game, false); + hasChosen = true; } - if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", - "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { - opponent.discardOne(false, source, game); - continue; - } - opponent.loseLife(3, game, false); } } - } return true; } From a98f0f03c7ec4255950036287ff3721401f79b30 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 14:46:40 +0000 Subject: [PATCH 057/133] Code cleanup --- .../src/mage/cards/t/TormentOfHailfire.java | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java index 7855e56b7a3..209582a7bf1 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java @@ -87,36 +87,31 @@ class TormentOfHailfireEffect extends OneShotEffect { if (controller != null) { int repeat = source.getManaCostsToPay().getX(); for (int i = 1; i <= repeat; i++) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) { - boolean hasChosen = false; - while (!hasChosen) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); - if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", - "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - if (opponent.choose(outcome, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - if (permanent.sacrifice(source.getSourceId(), game)) { - hasChosen = true; - continue; - } + for (UUID opponentId : game.getOpponents(source.getControllerId())) {z + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); + if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", + "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); + if (opponent.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + if (permanent.sacrifice(source.getSourceId(), game)) { + continue; } } } - if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", - "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { - opponent.discardOne(false, source, game); - hasChosen = true; - continue; - } - opponent.loseLife(3, game, false); - hasChosen = true; } + if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", + "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { + opponent.discardOne(false, source, game); + continue; + } + opponent.loseLife(3, game, false); } } + } return true; } From 6c2e66cd57022c2c964d615731f40a2b55636e0c Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 14:48:08 +0000 Subject: [PATCH 058/133] Typo --- Mage.Sets/src/mage/cards/t/TormentOfHailfire.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java index 209582a7bf1..79e9e389ec3 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java @@ -87,7 +87,7 @@ class TormentOfHailfireEffect extends OneShotEffect { if (controller != null) { int repeat = source.getManaCostsToPay().getX(); for (int i = 1; i <= repeat; i++) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) {z + for (UUID opponentId : game.getOpponents(source.getControllerId())) { Player opponent = game.getPlayer(opponentId); if (opponent != null) { int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); From ecf7ca0a5b8ffecd7d9249dc334fec418bec9f21 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 23 Feb 2018 16:00:49 +0100 Subject: [PATCH 059/133] * City of Solitude - added a specific message fo rthe rule changing effect. --- .../src/mage/cards/c/CityOfSolitude.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CityOfSolitude.java b/Mage.Sets/src/mage/cards/c/CityOfSolitude.java index 03cef0637f7..dbcc7129ba6 100644 --- a/Mage.Sets/src/mage/cards/c/CityOfSolitude.java +++ b/Mage.Sets/src/mage/cards/c/CityOfSolitude.java @@ -28,6 +28,7 @@ package mage.cards.c; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -48,7 +49,7 @@ import mage.game.events.GameEvent.EventType; public class CityOfSolitude extends CardImpl { public CityOfSolitude(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Players can cast spells and activate abilities only during their own turns. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityOfSolitudeEffect())); @@ -74,12 +75,22 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl { CityOfSolitudeEffect(final CityOfSolitudeEffect effect) { super(effect); } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.CAST_SPELL || event.getType() == EventType.ACTIVATE_ABILITY; } - + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject sourceObject = game.getObject(source.getSourceId()); + MageObject eventObject = game.getObject(event.getSourceId()); + if (sourceObject != null && eventObject != null) { + return "You can cast or activate anability of " + eventObject.getIdName() + " only during your own turns (" + sourceObject.getIdName() + "). "; + } + return null; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { return !game.getActivePlayerId().equals(event.getPlayerId()); @@ -89,4 +100,4 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl { public CityOfSolitudeEffect copy() { return new CityOfSolitudeEffect(this); } -} \ No newline at end of file +} From 3a5924270dfd9e2ccc95be320c9a7453dd0b1b18 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 23 Feb 2018 11:41:57 -0600 Subject: [PATCH 060/133] - Added requested card Protective Sphere --- .../src/mage/cards/p/ProtectiveSphere.java | 134 +++ Mage.Sets/src/mage/sets/Invasion.java | 775 +++++++++--------- 2 files changed, 522 insertions(+), 387 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/p/ProtectiveSphere.java diff --git a/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java b/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java new file mode 100644 index 00000000000..61b81042325 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java @@ -0,0 +1,134 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.Mana; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetSource; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public class ProtectiveSphere extends CardImpl { + + public ProtectiveSphere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // {1}, Pay 1 life: Prevent all damage that would be dealt to you this turn by a source of your choice that shares a color with the mana spent on this activation cost. + Ability ability = new SimpleActivatedAbility(new ProtectiveSphereEffect(), new ManaCostsImpl("{1}")); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + + } + + public ProtectiveSphere(final ProtectiveSphere card) { + super(card); + } + + @Override + public ProtectiveSphere copy() { + return new ProtectiveSphere(this); + } +} + +class ProtectiveSphereEffect extends PreventionEffectImpl { + + private final TargetSource target; + private static Mana manaUsed; + private static List colorsOfChosenSource = new ArrayList<>(); + + public ProtectiveSphereEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "Prevent all damage that would be dealt to you this turn by a source of your choice that shares a color with the mana spent on this activation cost."; + this.target = new TargetSource(); + } + + public ProtectiveSphereEffect(final ProtectiveSphereEffect effect) { + super(effect); + this.target = effect.target.copy(); + } + + @Override + public ProtectiveSphereEffect copy() { + return new ProtectiveSphereEffect(this); + } + + @Override + public void init(Ability source, Game game) { + target.setNotTarget(true); + target.setRequired(false); + Player controller = game.getPlayer(source.getControllerId()); + Permanent protectiveSphere = game.getPermanent(source.getSourceId()); + if (controller != null + && protectiveSphere != null) { + game.getState().setValue("ProtectiveSphere" + source.getSourceId().toString(), source.getManaCostsToPay().getUsedManaToPay()); //store the mana used to pay + protectiveSphere.addInfo("MANA USED", CardUtil.addToolTipMarkTags("Last mana used for protective ability: " + source.getManaCostsToPay().getUsedManaToPay()), game); + } + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + manaUsed = (Mana) game.getState().getValue("ProtectiveSphere" + source.getSourceId().toString()); + if (super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getControllerId()) + && event.getSourceId().equals(target.getFirstTarget())) { + colorsOfChosenSource = game.getObject(target.getFirstTarget()).getColor(game).getColors(); + if (colorsOfChosenSource.stream().anyMatch((c) -> (manaUsed.getColor(c.getColoredManaSymbol()) > 0))) { + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Invasion.java b/Mage.Sets/src/mage/sets/Invasion.java index 5e88651b8ff..8e7acf64f84 100644 --- a/Mage.Sets/src/mage/sets/Invasion.java +++ b/Mage.Sets/src/mage/sets/Invasion.java @@ -1,387 +1,388 @@ -/* - * 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.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public class Invasion extends ExpansionSet { - - private static final Invasion instance = new Invasion(); - - public static Invasion getInstance() { - return instance; - } - - private Invasion() { - super("Invasion", "INV", ExpansionSet.buildDate(2000, 9, 2), SetType.EXPANSION); - this.blockName = "Invasion"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Absorb", 226, Rarity.RARE, mage.cards.a.Absorb.class)); - cards.add(new SetCardInfo("Addle", 91, Rarity.UNCOMMON, mage.cards.a.Addle.class)); - cards.add(new SetCardInfo("Aether Rift", 227, Rarity.RARE, mage.cards.a.AetherRift.class)); - cards.add(new SetCardInfo("Aggressive Urge", 181, Rarity.COMMON, mage.cards.a.AggressiveUrge.class)); - cards.add(new SetCardInfo("Agonizing Demise", 92, Rarity.COMMON, mage.cards.a.AgonizingDemise.class)); - cards.add(new SetCardInfo("Alabaster Leech", 1, Rarity.RARE, mage.cards.a.AlabasterLeech.class)); - cards.add(new SetCardInfo("Alloy Golem", 297, Rarity.UNCOMMON, mage.cards.a.AlloyGolem.class)); - cards.add(new SetCardInfo("Ancient Kavu", 136, Rarity.COMMON, mage.cards.a.AncientKavu.class)); - cards.add(new SetCardInfo("Ancient Spring", 319, Rarity.COMMON, mage.cards.a.AncientSpring.class)); - cards.add(new SetCardInfo("Andradite Leech", 93, Rarity.RARE, mage.cards.a.AndraditeLeech.class)); - cards.add(new SetCardInfo("Angelic Shield", 228, Rarity.UNCOMMON, mage.cards.a.AngelicShield.class)); - cards.add(new SetCardInfo("Angel of Mercy", 2, Rarity.UNCOMMON, mage.cards.a.AngelOfMercy.class)); - cards.add(new SetCardInfo("Annihilate", 94, Rarity.UNCOMMON, mage.cards.a.Annihilate.class)); - cards.add(new SetCardInfo("Archaeological Dig", 320, Rarity.UNCOMMON, mage.cards.a.ArchaeologicalDig.class)); - cards.add(new SetCardInfo("Ardent Soldier", 3, Rarity.COMMON, mage.cards.a.ArdentSoldier.class)); - cards.add(new SetCardInfo("Armadillo Cloak", 229, Rarity.COMMON, mage.cards.a.ArmadilloCloak.class)); - cards.add(new SetCardInfo("Armored Guardian", 230, Rarity.RARE, mage.cards.a.ArmoredGuardian.class)); - cards.add(new SetCardInfo("Artifact Mutation", 231, Rarity.RARE, mage.cards.a.ArtifactMutation.class)); - cards.add(new SetCardInfo("Assault // Battery", 295, Rarity.UNCOMMON, mage.cards.a.AssaultBattery.class)); - cards.add(new SetCardInfo("Atalya, Samite Master", 4, Rarity.RARE, mage.cards.a.AtalyaSamiteMaster.class)); - cards.add(new SetCardInfo("Aura Mutation", 232, Rarity.RARE, mage.cards.a.AuraMutation.class)); - cards.add(new SetCardInfo("Aura Shards", 233, Rarity.UNCOMMON, mage.cards.a.AuraShards.class)); - cards.add(new SetCardInfo("Backlash", 234, Rarity.UNCOMMON, mage.cards.b.Backlash.class)); - cards.add(new SetCardInfo("Barrin's Spite", 235, Rarity.RARE, mage.cards.b.BarrinsSpite.class)); - cards.add(new SetCardInfo("Barrin's Unmaking", 46, Rarity.COMMON, mage.cards.b.BarrinsUnmaking.class)); - cards.add(new SetCardInfo("Benalish Emissary", 5, Rarity.UNCOMMON, mage.cards.b.BenalishEmissary.class)); - cards.add(new SetCardInfo("Benalish Heralds", 6, Rarity.UNCOMMON, mage.cards.b.BenalishHeralds.class)); - cards.add(new SetCardInfo("Benalish Lancer", 7, Rarity.COMMON, mage.cards.b.BenalishLancer.class)); - cards.add(new SetCardInfo("Benalish Trapper", 8, Rarity.COMMON, mage.cards.b.BenalishTrapper.class)); - cards.add(new SetCardInfo("Bind", 182, Rarity.RARE, mage.cards.b.Bind.class)); - cards.add(new SetCardInfo("Blazing Specter", 236, Rarity.RARE, mage.cards.b.BlazingSpecter.class)); - cards.add(new SetCardInfo("Blinding Light", 9, Rarity.UNCOMMON, mage.cards.b.BlindingLight.class)); - cards.add(new SetCardInfo("Blind Seer", 47, Rarity.RARE, mage.cards.b.BlindSeer.class)); - cards.add(new SetCardInfo("Bloodstone Cameo", 298, Rarity.UNCOMMON, mage.cards.b.BloodstoneCameo.class)); - cards.add(new SetCardInfo("Blurred Mongoose", 183, Rarity.RARE, mage.cards.b.BlurredMongoose.class)); - cards.add(new SetCardInfo("Bog Initiate", 95, Rarity.COMMON, mage.cards.b.BogInitiate.class)); - cards.add(new SetCardInfo("Breaking Wave", 48, Rarity.RARE, mage.cards.b.BreakingWave.class)); - cards.add(new SetCardInfo("Breath of Darigaaz", 138, Rarity.UNCOMMON, mage.cards.b.BreathOfDarigaaz.class)); - cards.add(new SetCardInfo("Callous Giant", 139, Rarity.RARE, mage.cards.c.CallousGiant.class)); - cards.add(new SetCardInfo("Canopy Surge", 184, Rarity.UNCOMMON, mage.cards.c.CanopySurge.class)); - cards.add(new SetCardInfo("Capashen Unicorn", 10, Rarity.COMMON, mage.cards.c.CapashenUnicorn.class)); - cards.add(new SetCardInfo("Captain Sisay", 237, Rarity.RARE, mage.cards.c.CaptainSisay.class)); - cards.add(new SetCardInfo("Cauldron Dance", 238, Rarity.UNCOMMON, mage.cards.c.CauldronDance.class)); - cards.add(new SetCardInfo("Chaotic Strike", 140, Rarity.UNCOMMON, mage.cards.c.ChaoticStrike.class)); - cards.add(new SetCardInfo("Charging Troll", 239, Rarity.UNCOMMON, mage.cards.c.ChargingTroll.class)); - cards.add(new SetCardInfo("Chromatic Sphere", 299, Rarity.UNCOMMON, mage.cards.c.ChromaticSphere.class)); - cards.add(new SetCardInfo("Cinder Shade", 240, Rarity.UNCOMMON, mage.cards.c.CinderShade.class)); - cards.add(new SetCardInfo("Coalition Victory", 241, Rarity.RARE, mage.cards.c.CoalitionVictory.class)); - cards.add(new SetCardInfo("Coastal Tower", 321, Rarity.UNCOMMON, mage.cards.c.CoastalTower.class)); - cards.add(new SetCardInfo("Collapsing Borders", 141, Rarity.RARE, mage.cards.c.CollapsingBorders.class)); - cards.add(new SetCardInfo("Collective Restraint", 49, Rarity.RARE, mage.cards.c.CollectiveRestraint.class)); - cards.add(new SetCardInfo("Cremate", 96, Rarity.UNCOMMON, mage.cards.c.Cremate.class)); - cards.add(new SetCardInfo("Crimson Acolyte", 11, Rarity.COMMON, mage.cards.c.CrimsonAcolyte.class)); - cards.add(new SetCardInfo("Crosis's Attendant", 300, Rarity.UNCOMMON, mage.cards.c.CrosissAttendant.class)); - cards.add(new SetCardInfo("Crosis, the Purger", 242, Rarity.RARE, mage.cards.c.CrosisThePurger.class)); - cards.add(new SetCardInfo("Crown of Flames", 142, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); - cards.add(new SetCardInfo("Crusading Knight", 12, Rarity.RARE, mage.cards.c.CrusadingKnight.class)); - cards.add(new SetCardInfo("Crypt Angel", 97, Rarity.RARE, mage.cards.c.CryptAngel.class)); - cards.add(new SetCardInfo("Cursed Flesh", 98, Rarity.COMMON, mage.cards.c.CursedFlesh.class)); - cards.add(new SetCardInfo("Darigaaz's Attendant", 301, Rarity.UNCOMMON, mage.cards.d.DarigaazsAttendant.class)); - cards.add(new SetCardInfo("Darigaaz, the Igniter", 243, Rarity.RARE, mage.cards.d.DarigaazTheIgniter.class)); - cards.add(new SetCardInfo("Defiling Tears", 99, Rarity.UNCOMMON, mage.cards.d.DefilingTears.class)); - cards.add(new SetCardInfo("Devouring Strossus", 101, Rarity.RARE, mage.cards.d.DevouringStrossus.class)); - cards.add(new SetCardInfo("Dismantling Blow", 14, Rarity.COMMON, mage.cards.d.DismantlingBlow.class)); - cards.add(new SetCardInfo("Disrupt", 51, Rarity.UNCOMMON, mage.cards.d.Disrupt.class)); - cards.add(new SetCardInfo("Distorting Wake", 52, Rarity.RARE, mage.cards.d.DistortingWake.class)); - cards.add(new SetCardInfo("Divine Presence", 15, Rarity.RARE, mage.cards.d.DivinePresence.class)); - cards.add(new SetCardInfo("Do or Die", 102, Rarity.RARE, mage.cards.d.DoOrDie.class)); - cards.add(new SetCardInfo("Drake-Skull Cameo", 302, Rarity.UNCOMMON, mage.cards.d.DrakeSkullCameo.class)); - cards.add(new SetCardInfo("Dream Thrush", 53, Rarity.COMMON, mage.cards.d.DreamThrush.class)); - cards.add(new SetCardInfo("Dredge", 103, Rarity.UNCOMMON, mage.cards.d.Dredge.class)); - cards.add(new SetCardInfo("Dromar's Attendant", 303, Rarity.UNCOMMON, mage.cards.d.DromarsAttendant.class)); - cards.add(new SetCardInfo("Dromar, the Banisher", 244, Rarity.RARE, mage.cards.d.DromarTheBanisher.class)); - cards.add(new SetCardInfo("Dueling Grounds", 245, Rarity.RARE, mage.cards.d.DuelingGrounds.class)); - cards.add(new SetCardInfo("Duskwalker", 104, Rarity.COMMON, mage.cards.d.Duskwalker.class)); - cards.add(new SetCardInfo("Elfhame Palace", 322, Rarity.UNCOMMON, mage.cards.e.ElfhamePalace.class)); - cards.add(new SetCardInfo("Elfhame Sanctuary", 185, Rarity.UNCOMMON, mage.cards.e.ElfhameSanctuary.class)); - cards.add(new SetCardInfo("Elvish Champion", 186, Rarity.RARE, mage.cards.e.ElvishChampion.class)); - cards.add(new SetCardInfo("Empress Galina", 54, Rarity.RARE, mage.cards.e.EmpressGalina.class)); - cards.add(new SetCardInfo("Exclude", 56, Rarity.COMMON, mage.cards.e.Exclude.class)); - cards.add(new SetCardInfo("Exotic Curse", 105, Rarity.COMMON, mage.cards.e.ExoticCurse.class)); - cards.add(new SetCardInfo("Explosive Growth", 187, Rarity.COMMON, mage.cards.e.ExplosiveGrowth.class)); - cards.add(new SetCardInfo("Fact or Fiction", 57, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); - cards.add(new SetCardInfo("Faerie Squadron", 58, Rarity.COMMON, mage.cards.f.FaerieSquadron.class)); - cards.add(new SetCardInfo("Fertile Ground", 188, Rarity.COMMON, mage.cards.f.FertileGround.class)); - cards.add(new SetCardInfo("Firebrand Ranger", 143, Rarity.UNCOMMON, mage.cards.f.FirebrandRanger.class)); - cards.add(new SetCardInfo("Firescreamer", 106, Rarity.COMMON, mage.cards.f.Firescreamer.class)); - cards.add(new SetCardInfo("Fires of Yavimaya", 246, Rarity.UNCOMMON, mage.cards.f.FiresOfYavimaya.class)); - cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Frenzied Tilling", 247, Rarity.COMMON, mage.cards.f.FrenziedTilling.class)); - cards.add(new SetCardInfo("Galina's Knight", 248, Rarity.COMMON, mage.cards.g.GalinasKnight.class)); - cards.add(new SetCardInfo("Geothermal Crevice", 323, Rarity.COMMON, mage.cards.g.GeothermalCrevice.class)); - cards.add(new SetCardInfo("Ghitu Fire", 144, Rarity.RARE, mage.cards.g.GhituFire.class)); - cards.add(new SetCardInfo("Glimmering Angel", 17, Rarity.COMMON, mage.cards.g.GlimmeringAngel.class)); - cards.add(new SetCardInfo("Global Ruin", 18, Rarity.RARE, mage.cards.g.GlobalRuin.class)); - cards.add(new SetCardInfo("Goblin Spy", 145, Rarity.UNCOMMON, mage.cards.g.GoblinSpy.class)); - cards.add(new SetCardInfo("Goham Djinn", 107, Rarity.UNCOMMON, mage.cards.g.GohamDjinn.class)); - cards.add(new SetCardInfo("Halam Djinn", 146, Rarity.UNCOMMON, mage.cards.h.HalamDjinn.class)); - cards.add(new SetCardInfo("Hanna, Ship's Navigator", 249, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); - cards.add(new SetCardInfo("Harrow", 189, Rarity.COMMON, mage.cards.h.Harrow.class)); - cards.add(new SetCardInfo("Harsh Judgment", 19, Rarity.RARE, mage.cards.h.HarshJudgment.class)); - cards.add(new SetCardInfo("Hate Weaver", 108, Rarity.UNCOMMON, mage.cards.h.HateWeaver.class)); - cards.add(new SetCardInfo("Heroes' Reunion", 250, Rarity.UNCOMMON, mage.cards.h.HeroesReunion.class)); - cards.add(new SetCardInfo("Holy Day", 20, Rarity.COMMON, mage.cards.h.HolyDay.class)); - cards.add(new SetCardInfo("Hooded Kavu", 147, Rarity.COMMON, mage.cards.h.HoodedKavu.class)); - cards.add(new SetCardInfo("Horned Cheetah", 251, Rarity.UNCOMMON, mage.cards.h.HornedCheetah.class)); - cards.add(new SetCardInfo("Hunting Kavu", 252, Rarity.UNCOMMON, mage.cards.h.HuntingKavu.class)); - cards.add(new SetCardInfo("Hypnotic Cloud", 109, Rarity.COMMON, mage.cards.h.HypnoticCloud.class)); - cards.add(new SetCardInfo("Irrigation Ditch", 324, Rarity.COMMON, mage.cards.i.IrrigationDitch.class)); - cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jade Leech", 190, Rarity.RARE, mage.cards.j.JadeLeech.class)); - cards.add(new SetCardInfo("Juntu Stakes", 304, Rarity.RARE, mage.cards.j.JuntuStakes.class)); - cards.add(new SetCardInfo("Kangee, Aerie Keeper", 253, Rarity.RARE, mage.cards.k.KangeeAerieKeeper.class)); - cards.add(new SetCardInfo("Kavu Aggressor", 148, Rarity.COMMON, mage.cards.k.KavuAggressor.class)); - cards.add(new SetCardInfo("Kavu Chameleon", 191, Rarity.UNCOMMON, mage.cards.k.KavuChameleon.class)); - cards.add(new SetCardInfo("Kavu Climber", 192, Rarity.COMMON, mage.cards.k.KavuClimber.class)); - cards.add(new SetCardInfo("Kavu Lair", 193, Rarity.RARE, mage.cards.k.KavuLair.class)); - cards.add(new SetCardInfo("Kavu Monarch", 149, Rarity.RARE, mage.cards.k.KavuMonarch.class)); - cards.add(new SetCardInfo("Kavu Runner", 150, Rarity.UNCOMMON, mage.cards.k.KavuRunner.class)); - cards.add(new SetCardInfo("Kavu Scout", 151, Rarity.COMMON, mage.cards.k.KavuScout.class)); - cards.add(new SetCardInfo("Kavu Titan", 194, Rarity.RARE, mage.cards.k.KavuTitan.class)); - cards.add(new SetCardInfo("Keldon Necropolis", 325, Rarity.RARE, mage.cards.k.KeldonNecropolis.class)); - cards.add(new SetCardInfo("Liberate", 21, Rarity.UNCOMMON, mage.cards.l.Liberate.class)); - cards.add(new SetCardInfo("Lightning Dart", 152, Rarity.UNCOMMON, mage.cards.l.LightningDart.class)); - cards.add(new SetCardInfo("Llanowar Cavalry", 195, Rarity.COMMON, mage.cards.l.LlanowarCavalry.class)); - cards.add(new SetCardInfo("Llanowar Elite", 196, Rarity.COMMON, mage.cards.l.LlanowarElite.class)); - cards.add(new SetCardInfo("Llanowar Knight", 254, Rarity.COMMON, mage.cards.l.LlanowarKnight.class)); - cards.add(new SetCardInfo("Llanowar Vanguard", 197, Rarity.COMMON, mage.cards.l.LlanowarVanguard.class)); - cards.add(new SetCardInfo("Lobotomy", 255, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); - cards.add(new SetCardInfo("Lotus Guardian", 305, Rarity.RARE, mage.cards.l.LotusGuardian.class)); - cards.add(new SetCardInfo("Mana Maze", 59, Rarity.RARE, mage.cards.m.ManaMaze.class)); - cards.add(new SetCardInfo("Maniacal Rage", 155, Rarity.COMMON, mage.cards.m.ManiacalRage.class)); - cards.add(new SetCardInfo("Manipulate Fate", 60, Rarity.UNCOMMON, mage.cards.m.ManipulateFate.class)); - cards.add(new SetCardInfo("Marauding Knight", 110, Rarity.RARE, mage.cards.m.MaraudingKnight.class)); - cards.add(new SetCardInfo("Metathran Transport", 62, Rarity.UNCOMMON, mage.cards.m.MetathranTransport.class)); - cards.add(new SetCardInfo("Metathran Zombie", 63, Rarity.COMMON, mage.cards.m.MetathranZombie.class)); - cards.add(new SetCardInfo("Meteor Storm", 256, Rarity.RARE, mage.cards.m.MeteorStorm.class)); - cards.add(new SetCardInfo("Might Weaver", 198, Rarity.UNCOMMON, mage.cards.m.MightWeaver.class)); - cards.add(new SetCardInfo("Molimo, Maro-Sorcerer", 199, Rarity.RARE, mage.cards.m.MolimoMaroSorcerer.class)); - cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mourning", 111, Rarity.COMMON, mage.cards.m.Mourning.class)); - cards.add(new SetCardInfo("Nightscape Apprentice", 112, Rarity.COMMON, mage.cards.n.NightscapeApprentice.class)); - cards.add(new SetCardInfo("Nightscape Master", 113, Rarity.RARE, mage.cards.n.NightscapeMaster.class)); - cards.add(new SetCardInfo("Noble Panther", 257, Rarity.RARE, mage.cards.n.NoblePanther.class)); - cards.add(new SetCardInfo("Nomadic Elf", 200, Rarity.COMMON, mage.cards.n.NomadicElf.class)); - cards.add(new SetCardInfo("Obliterate", 156, Rarity.RARE, mage.cards.o.Obliterate.class)); - cards.add(new SetCardInfo("Obsidian Acolyte", 22, Rarity.COMMON, mage.cards.o.ObsidianAcolyte.class)); - cards.add(new SetCardInfo("Opt", 64, Rarity.COMMON, mage.cards.o.Opt.class)); - cards.add(new SetCardInfo("Ordered Migration", 258, Rarity.UNCOMMON, mage.cards.o.OrderedMigration.class)); - cards.add(new SetCardInfo("Orim's Touch", 23, Rarity.COMMON, mage.cards.o.OrimsTouch.class)); - cards.add(new SetCardInfo("Overabundance", 259, Rarity.RARE, mage.cards.o.Overabundance.class)); - cards.add(new SetCardInfo("Overload", 157, Rarity.COMMON, mage.cards.o.Overload.class)); - cards.add(new SetCardInfo("Pain // Suffering", 294, Rarity.UNCOMMON, mage.cards.p.PainSuffering.class)); - cards.add(new SetCardInfo("Phantasmal Terrain", 65, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); - cards.add(new SetCardInfo("Phyrexian Altar", 306, Rarity.RARE, mage.cards.p.PhyrexianAltar.class)); - cards.add(new SetCardInfo("Phyrexian Battleflies", 114, Rarity.COMMON, mage.cards.p.PhyrexianBattleflies.class)); - cards.add(new SetCardInfo("Phyrexian Delver", 115, Rarity.RARE, mage.cards.p.PhyrexianDelver.class)); - cards.add(new SetCardInfo("Phyrexian Infiltrator", 116, Rarity.RARE, mage.cards.p.PhyrexianInfiltrator.class)); - cards.add(new SetCardInfo("Phyrexian Lens", 307, Rarity.RARE, mage.cards.p.PhyrexianLens.class)); - cards.add(new SetCardInfo("Phyrexian Reaper", 117, Rarity.COMMON, mage.cards.p.PhyrexianReaper.class)); - cards.add(new SetCardInfo("Phyrexian Slayer", 118, Rarity.COMMON, mage.cards.p.PhyrexianSlayer.class)); - cards.add(new SetCardInfo("Pincer Spider", 201, Rarity.COMMON, mage.cards.p.PincerSpider.class)); - cards.add(new SetCardInfo("Plague Spitter", 119, Rarity.UNCOMMON, mage.cards.p.PlagueSpitter.class)); - cards.add(new SetCardInfo("Plague Spores", 260, Rarity.COMMON, mage.cards.p.PlagueSpores.class)); - cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Planar Portal", 308, Rarity.RARE, mage.cards.p.PlanarPortal.class)); - cards.add(new SetCardInfo("Pouncing Kavu", 158, Rarity.COMMON, mage.cards.p.PouncingKavu.class)); - cards.add(new SetCardInfo("Power Armor", 309, Rarity.UNCOMMON, mage.cards.p.PowerArmor.class)); - cards.add(new SetCardInfo("Prison Barricade", 25, Rarity.COMMON, mage.cards.p.PrisonBarricade.class)); - cards.add(new SetCardInfo("Probe", 66, Rarity.COMMON, mage.cards.p.Probe.class)); - cards.add(new SetCardInfo("Prohibit", 67, Rarity.COMMON, mage.cards.p.Prohibit.class)); - cards.add(new SetCardInfo("Pure Reflection", 27, Rarity.RARE, mage.cards.p.PureReflection.class)); - cards.add(new SetCardInfo("Pyre Zombie", 261, Rarity.RARE, mage.cards.p.PyreZombie.class)); - cards.add(new SetCardInfo("Quirion Elves", 203, Rarity.COMMON, mage.cards.q.QuirionElves.class)); - cards.add(new SetCardInfo("Quirion Sentinel", 204, Rarity.COMMON, mage.cards.q.QuirionSentinel.class)); - cards.add(new SetCardInfo("Quirion Trailblazer", 205, Rarity.COMMON, mage.cards.q.QuirionTrailblazer.class)); - cards.add(new SetCardInfo("Rage Weaver", 159, Rarity.UNCOMMON, mage.cards.r.RageWeaver.class)); - cards.add(new SetCardInfo("Raging Kavu", 262, Rarity.RARE, mage.cards.r.RagingKavu.class)); - cards.add(new SetCardInfo("Rainbow Crow", 69, Rarity.UNCOMMON, mage.cards.r.RainbowCrow.class)); - cards.add(new SetCardInfo("Rampant Elephant", 28, Rarity.COMMON, mage.cards.r.RampantElephant.class)); - cards.add(new SetCardInfo("Ravenous Rats", 120, Rarity.COMMON, mage.cards.r.RavenousRats.class)); - cards.add(new SetCardInfo("Razorfoot Griffin", 29, Rarity.COMMON, mage.cards.r.RazorfootGriffin.class)); - cards.add(new SetCardInfo("Reckless Assault", 263, Rarity.RARE, mage.cards.r.RecklessAssault.class)); - cards.add(new SetCardInfo("Reckless Spite", 121, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); - cards.add(new SetCardInfo("Recoil", 264, Rarity.COMMON, mage.cards.r.Recoil.class)); - cards.add(new SetCardInfo("Recover", 122, Rarity.COMMON, mage.cards.r.Recover.class)); - cards.add(new SetCardInfo("Repulse", 70, Rarity.COMMON, mage.cards.r.Repulse.class)); - cards.add(new SetCardInfo("Restock", 206, Rarity.RARE, mage.cards.r.Restock.class)); - cards.add(new SetCardInfo("Restrain", 30, Rarity.COMMON, mage.cards.r.Restrain.class)); - cards.add(new SetCardInfo("Reviving Dose", 31, Rarity.COMMON, mage.cards.r.RevivingDose.class)); - cards.add(new SetCardInfo("Reviving Vapors", 265, Rarity.UNCOMMON, mage.cards.r.RevivingVapors.class)); - cards.add(new SetCardInfo("Rewards of Diversity", 32, Rarity.UNCOMMON, mage.cards.r.RewardsOfDiversity.class)); - cards.add(new SetCardInfo("Reya Dawnbringer", 33, Rarity.RARE, mage.cards.r.ReyaDawnbringer.class)); - cards.add(new SetCardInfo("Riptide Crab", 266, Rarity.UNCOMMON, mage.cards.r.RiptideCrab.class)); - cards.add(new SetCardInfo("Rith's Attendant", 310, Rarity.UNCOMMON, mage.cards.r.RithsAttendant.class)); - cards.add(new SetCardInfo("Rith, the Awakener", 267, Rarity.RARE, mage.cards.r.RithTheAwakener.class)); - cards.add(new SetCardInfo("Rogue Kavu", 160, Rarity.COMMON, mage.cards.r.RogueKavu.class)); - cards.add(new SetCardInfo("Rooting Kavu", 207, Rarity.UNCOMMON, mage.cards.r.RootingKavu.class)); - cards.add(new SetCardInfo("Rout", 34, Rarity.RARE, mage.cards.r.Rout.class)); - cards.add(new SetCardInfo("Ruby Leech", 161, Rarity.RARE, mage.cards.r.RubyLeech.class)); - cards.add(new SetCardInfo("Ruham Djinn", 35, Rarity.UNCOMMON, mage.cards.r.RuhamDjinn.class)); - cards.add(new SetCardInfo("Sabertooth Nishoba", 268, Rarity.RARE, mage.cards.s.SabertoothNishoba.class)); - cards.add(new SetCardInfo("Salt Marsh", 326, Rarity.UNCOMMON, mage.cards.s.SaltMarsh.class)); - cards.add(new SetCardInfo("Samite Archer", 269, Rarity.UNCOMMON, mage.cards.s.SamiteArcher.class)); - cards.add(new SetCardInfo("Sapphire Leech", 71, Rarity.RARE, mage.cards.s.SapphireLeech.class)); - cards.add(new SetCardInfo("Saproling Symbiosis", 209, Rarity.RARE, mage.cards.s.SaprolingSymbiosis.class)); - cards.add(new SetCardInfo("Savage Offensive", 162, Rarity.COMMON, mage.cards.s.SavageOffensive.class)); - cards.add(new SetCardInfo("Scarred Puma", 163, Rarity.COMMON, mage.cards.s.ScarredPuma.class)); - cards.add(new SetCardInfo("Scavenged Weaponry", 123, Rarity.COMMON, mage.cards.s.ScavengedWeaponry.class)); - cards.add(new SetCardInfo("Scorching Lava", 164, Rarity.COMMON, mage.cards.s.ScorchingLava.class)); - cards.add(new SetCardInfo("Scouting Trek", 210, Rarity.UNCOMMON, mage.cards.s.ScoutingTrek.class)); - cards.add(new SetCardInfo("Seashell Cameo", 311, Rarity.UNCOMMON, mage.cards.s.SeashellCameo.class)); - cards.add(new SetCardInfo("Seer's Vision", 270, Rarity.UNCOMMON, mage.cards.s.SeersVision.class)); - cards.add(new SetCardInfo("Serpentine Kavu", 211, Rarity.COMMON, mage.cards.s.SerpentineKavu.class)); - cards.add(new SetCardInfo("Shackles", 37, Rarity.COMMON, mage.cards.s.Shackles.class)); - cards.add(new SetCardInfo("Shimmering Wings", 72, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); - cards.add(new SetCardInfo("Shivan Emissary", 166, Rarity.UNCOMMON, mage.cards.s.ShivanEmissary.class)); - cards.add(new SetCardInfo("Shivan Harvest", 167, Rarity.UNCOMMON, mage.cards.s.ShivanHarvest.class)); - cards.add(new SetCardInfo("Shivan Oasis", 327, Rarity.UNCOMMON, mage.cards.s.ShivanOasis.class)); - cards.add(new SetCardInfo("Shivan Zombie", 271, Rarity.COMMON, mage.cards.s.ShivanZombie.class)); - cards.add(new SetCardInfo("Shoreline Raider", 73, Rarity.COMMON, mage.cards.s.ShorelineRaider.class)); - cards.add(new SetCardInfo("Simoon", 272, Rarity.UNCOMMON, mage.cards.s.Simoon.class)); - cards.add(new SetCardInfo("Skittish Kavu", 168, Rarity.UNCOMMON, mage.cards.s.SkittishKavu.class)); - cards.add(new SetCardInfo("Skizzik", 169, Rarity.RARE, mage.cards.s.Skizzik.class)); - cards.add(new SetCardInfo("Sky Weaver", 74, Rarity.UNCOMMON, mage.cards.s.SkyWeaver.class)); - cards.add(new SetCardInfo("Sleeper's Robe", 273, Rarity.UNCOMMON, mage.cards.s.SleepersRobe.class)); - cards.add(new SetCardInfo("Slimy Kavu", 170, Rarity.COMMON, mage.cards.s.SlimyKavu.class)); - cards.add(new SetCardInfo("Slinking Serpent", 274, Rarity.UNCOMMON, mage.cards.s.SlinkingSerpent.class)); - cards.add(new SetCardInfo("Smoldering Tar", 275, Rarity.UNCOMMON, mage.cards.s.SmolderingTar.class)); - cards.add(new SetCardInfo("Soul Burn", 124, Rarity.COMMON, mage.cards.s.SoulBurn.class)); - cards.add(new SetCardInfo("Sparring Golem", 312, Rarity.UNCOMMON, mage.cards.s.SparringGolem.class)); - cards.add(new SetCardInfo("Spinal Embrace", 276, Rarity.RARE, mage.cards.s.SpinalEmbrace.class)); - cards.add(new SetCardInfo("Spirit of Resistance", 38, Rarity.RARE, mage.cards.s.SpiritOfResistance.class)); - cards.add(new SetCardInfo("Spirit Weaver", 39, Rarity.UNCOMMON, mage.cards.s.SpiritWeaver.class)); - cards.add(new SetCardInfo("Spite // Malice", 293, Rarity.UNCOMMON, mage.cards.s.SpiteMalice.class)); - cards.add(new SetCardInfo("Spreading Plague", 125, Rarity.RARE, mage.cards.s.SpreadingPlague.class)); - cards.add(new SetCardInfo("Stalking Assassin", 277, Rarity.RARE, mage.cards.s.StalkingAssassin.class)); - cards.add(new SetCardInfo("Stand // Deliver", 292, Rarity.UNCOMMON, mage.cards.s.StandDeliver.class)); - cards.add(new SetCardInfo("Sterling Grove", 278, Rarity.UNCOMMON, mage.cards.s.SterlingGrove.class)); - cards.add(new SetCardInfo("Stormscape Apprentice", 75, Rarity.COMMON, mage.cards.s.StormscapeApprentice.class)); - cards.add(new SetCardInfo("Stormscape Master", 76, Rarity.RARE, mage.cards.s.StormscapeMaster.class)); - cards.add(new SetCardInfo("Strength of Unity", 40, Rarity.COMMON, mage.cards.s.StrengthOfUnity.class)); - cards.add(new SetCardInfo("Stun", 172, Rarity.COMMON, mage.cards.s.Stun.class)); - cards.add(new SetCardInfo("Sulam Djinn", 212, Rarity.UNCOMMON, mage.cards.s.SulamDjinn.class)); - cards.add(new SetCardInfo("Sulfur Vent", 328, Rarity.COMMON, mage.cards.s.SulfurVent.class)); - cards.add(new SetCardInfo("Sunscape Apprentice", 41, Rarity.COMMON, mage.cards.s.SunscapeApprentice.class)); - cards.add(new SetCardInfo("Sunscape Master", 42, Rarity.RARE, mage.cards.s.SunscapeMaster.class)); - cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sway of Illusion", 77, Rarity.UNCOMMON, mage.cards.s.SwayOfIllusion.class)); - cards.add(new SetCardInfo("Tainted Well", 126, Rarity.COMMON, mage.cards.t.TaintedWell.class)); - cards.add(new SetCardInfo("Tangle", 213, Rarity.UNCOMMON, mage.cards.t.Tangle.class)); - cards.add(new SetCardInfo("Tectonic Instability", 173, Rarity.RARE, mage.cards.t.TectonicInstability.class)); - cards.add(new SetCardInfo("Teferi's Care", 43, Rarity.UNCOMMON, mage.cards.t.TeferisCare.class)); - cards.add(new SetCardInfo("Teferi's Moat", 279, Rarity.RARE, mage.cards.t.TeferisMoat.class)); - cards.add(new SetCardInfo("Teferi's Response", 78, Rarity.RARE, mage.cards.t.TeferisResponse.class)); - cards.add(new SetCardInfo("Tek", 313, Rarity.RARE, mage.cards.t.Tek.class)); - cards.add(new SetCardInfo("Temporal Distortion", 79, Rarity.RARE, mage.cards.t.TemporalDistortion.class)); - cards.add(new SetCardInfo("Thicket Elemental", 214, Rarity.RARE, mage.cards.t.ThicketElemental.class)); - cards.add(new SetCardInfo("Thornscape Apprentice", 215, Rarity.COMMON, mage.cards.t.ThornscapeApprentice.class)); - cards.add(new SetCardInfo("Thornscape Master", 216, Rarity.RARE, mage.cards.t.ThornscapeMaster.class)); - cards.add(new SetCardInfo("Thunderscape Apprentice", 174, Rarity.COMMON, mage.cards.t.ThunderscapeApprentice.class)); - cards.add(new SetCardInfo("Thunderscape Master", 175, Rarity.RARE, mage.cards.t.ThunderscapeMaster.class)); - cards.add(new SetCardInfo("Tidal Visionary", 80, Rarity.COMMON, mage.cards.t.TidalVisionary.class)); - cards.add(new SetCardInfo("Tigereye Cameo", 314, Rarity.UNCOMMON, mage.cards.t.TigereyeCameo.class)); - cards.add(new SetCardInfo("Tinder Farm", 329, Rarity.COMMON, mage.cards.t.TinderFarm.class)); - cards.add(new SetCardInfo("Tolarian Emissary", 81, Rarity.UNCOMMON, mage.cards.t.TolarianEmissary.class)); - cards.add(new SetCardInfo("Tower Drake", 82, Rarity.COMMON, mage.cards.t.TowerDrake.class)); - cards.add(new SetCardInfo("Tranquility", 217, Rarity.COMMON, mage.cards.t.Tranquility.class)); - cards.add(new SetCardInfo("Traveler's Cloak", 83, Rarity.COMMON, mage.cards.t.TravelersCloak.class)); - cards.add(new SetCardInfo("Treefolk Healer", 218, Rarity.UNCOMMON, mage.cards.t.TreefolkHealer.class)); - cards.add(new SetCardInfo("Trench Wurm", 127, Rarity.UNCOMMON, mage.cards.t.TrenchWurm.class)); - cards.add(new SetCardInfo("Treva's Attendant", 315, Rarity.UNCOMMON, mage.cards.t.TrevasAttendant.class)); - cards.add(new SetCardInfo("Treva, the Renewer", 280, Rarity.RARE, mage.cards.t.TrevaTheRenewer.class)); - cards.add(new SetCardInfo("Tribal Flames", 176, Rarity.COMMON, mage.cards.t.TribalFlames.class)); - cards.add(new SetCardInfo("Troll-Horn Cameo", 316, Rarity.UNCOMMON, mage.cards.t.TrollHornCameo.class)); - cards.add(new SetCardInfo("Tsabo's Assassin", 128, Rarity.RARE, mage.cards.t.TsabosAssassin.class)); - cards.add(new SetCardInfo("Tsabo's Decree", 129, Rarity.RARE, mage.cards.t.TsabosDecree.class)); - cards.add(new SetCardInfo("Tsabo's Web", 317, Rarity.RARE, mage.cards.t.TsabosWeb.class)); - cards.add(new SetCardInfo("Tsabo Tavoc", 281, Rarity.RARE, mage.cards.t.TsaboTavoc.class)); - cards.add(new SetCardInfo("Turf Wound", 177, Rarity.COMMON, mage.cards.t.TurfWound.class)); - cards.add(new SetCardInfo("Twilight's Call", 130, Rarity.RARE, mage.cards.t.TwilightsCall.class)); - cards.add(new SetCardInfo("Undermine", 282, Rarity.RARE, mage.cards.u.Undermine.class)); - cards.add(new SetCardInfo("Urborg Drake", 283, Rarity.UNCOMMON, mage.cards.u.UrborgDrake.class)); - cards.add(new SetCardInfo("Urborg Emissary", 131, Rarity.UNCOMMON, mage.cards.u.UrborgEmissary.class)); - cards.add(new SetCardInfo("Urborg Phantom", 132, Rarity.COMMON, mage.cards.u.UrborgPhantom.class)); - cards.add(new SetCardInfo("Urborg Shambler", 133, Rarity.UNCOMMON, mage.cards.u.UrborgShambler.class)); - cards.add(new SetCardInfo("Urborg Skeleton", 134, Rarity.COMMON, mage.cards.u.UrborgSkeleton.class)); - cards.add(new SetCardInfo("Urborg Volcano", 330, Rarity.UNCOMMON, mage.cards.u.UrborgVolcano.class)); - cards.add(new SetCardInfo("Urza's Filter", 318, Rarity.RARE, mage.cards.u.UrzasFilter.class)); - cards.add(new SetCardInfo("Urza's Rage", 178, Rarity.RARE, mage.cards.u.UrzasRage.class)); - cards.add(new SetCardInfo("Utopia Tree", 219, Rarity.RARE, mage.cards.u.UtopiaTree.class)); - cards.add(new SetCardInfo("Verdeloth the Ancient", 220, Rarity.RARE, mage.cards.v.VerdelothTheAncient.class)); - cards.add(new SetCardInfo("Verduran Emissary", 221, Rarity.UNCOMMON, mage.cards.v.VerduranEmissary.class)); - cards.add(new SetCardInfo("Viashino Grappler", 179, Rarity.COMMON, mage.cards.v.ViashinoGrappler.class)); - cards.add(new SetCardInfo("Vicious Kavu", 284, Rarity.UNCOMMON, mage.cards.v.ViciousKavu.class)); - cards.add(new SetCardInfo("Vile Consumption", 285, Rarity.RARE, mage.cards.v.VileConsumption.class)); - cards.add(new SetCardInfo("Vodalian Hypnotist", 84, Rarity.UNCOMMON, mage.cards.v.VodalianHypnotist.class)); - cards.add(new SetCardInfo("Vodalian Merchant", 85, Rarity.COMMON, mage.cards.v.VodalianMerchant.class)); - cards.add(new SetCardInfo("Vodalian Serpent", 86, Rarity.COMMON, mage.cards.v.VodalianSerpent.class)); - cards.add(new SetCardInfo("Vodalian Zombie", 286, Rarity.COMMON, mage.cards.v.VodalianZombie.class)); - cards.add(new SetCardInfo("Void", 287, Rarity.RARE, mage.cards.v.Void.class)); - cards.add(new SetCardInfo("Voracious Cobra", 288, Rarity.UNCOMMON, mage.cards.v.VoraciousCobra.class)); - cards.add(new SetCardInfo("Wallop", 223, Rarity.UNCOMMON, mage.cards.w.Wallop.class)); - cards.add(new SetCardInfo("Wandering Stream", 224, Rarity.COMMON, mage.cards.w.WanderingStream.class)); - cards.add(new SetCardInfo("Wash Out", 87, Rarity.UNCOMMON, mage.cards.w.WashOut.class)); - cards.add(new SetCardInfo("Wax // Wane", 296, Rarity.UNCOMMON, mage.cards.w.WaxWane.class)); - cards.add(new SetCardInfo("Wayfaring Giant", 44, Rarity.UNCOMMON, mage.cards.w.WayfaringGiant.class)); - cards.add(new SetCardInfo("Whip Silk", 225, Rarity.COMMON, mage.cards.w.WhipSilk.class)); - cards.add(new SetCardInfo("Wings of Hope", 289, Rarity.COMMON, mage.cards.w.WingsOfHope.class)); - cards.add(new SetCardInfo("Winnow", 45, Rarity.RARE, mage.cards.w.Winnow.class)); - cards.add(new SetCardInfo("Worldly Counsel", 89, Rarity.COMMON, mage.cards.w.WorldlyCounsel.class)); - cards.add(new SetCardInfo("Yavimaya Barbarian", 290, Rarity.COMMON, mage.cards.y.YavimayaBarbarian.class)); - cards.add(new SetCardInfo("Yavimaya Kavu", 291, Rarity.UNCOMMON, mage.cards.y.YavimayaKavu.class)); - cards.add(new SetCardInfo("Yawgmoth's Agenda", 135, Rarity.RARE, mage.cards.y.YawgmothsAgenda.class)); - cards.add(new SetCardInfo("Zanam Djinn", 90, Rarity.UNCOMMON, mage.cards.z.ZanamDjinn.class)); - cards.add(new SetCardInfo("Zap", 180, Rarity.COMMON, mage.cards.z.Zap.class)); - } -} +/* + * 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.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public class Invasion extends ExpansionSet { + + private static final Invasion instance = new Invasion(); + + public static Invasion getInstance() { + return instance; + } + + private Invasion() { + super("Invasion", "INV", ExpansionSet.buildDate(2000, 9, 2), SetType.EXPANSION); + this.blockName = "Invasion"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Absorb", 226, Rarity.RARE, mage.cards.a.Absorb.class)); + cards.add(new SetCardInfo("Addle", 91, Rarity.UNCOMMON, mage.cards.a.Addle.class)); + cards.add(new SetCardInfo("Aether Rift", 227, Rarity.RARE, mage.cards.a.AetherRift.class)); + cards.add(new SetCardInfo("Aggressive Urge", 181, Rarity.COMMON, mage.cards.a.AggressiveUrge.class)); + cards.add(new SetCardInfo("Agonizing Demise", 92, Rarity.COMMON, mage.cards.a.AgonizingDemise.class)); + cards.add(new SetCardInfo("Alabaster Leech", 1, Rarity.RARE, mage.cards.a.AlabasterLeech.class)); + cards.add(new SetCardInfo("Alloy Golem", 297, Rarity.UNCOMMON, mage.cards.a.AlloyGolem.class)); + cards.add(new SetCardInfo("Ancient Kavu", 136, Rarity.COMMON, mage.cards.a.AncientKavu.class)); + cards.add(new SetCardInfo("Ancient Spring", 319, Rarity.COMMON, mage.cards.a.AncientSpring.class)); + cards.add(new SetCardInfo("Andradite Leech", 93, Rarity.RARE, mage.cards.a.AndraditeLeech.class)); + cards.add(new SetCardInfo("Angelic Shield", 228, Rarity.UNCOMMON, mage.cards.a.AngelicShield.class)); + cards.add(new SetCardInfo("Angel of Mercy", 2, Rarity.UNCOMMON, mage.cards.a.AngelOfMercy.class)); + cards.add(new SetCardInfo("Annihilate", 94, Rarity.UNCOMMON, mage.cards.a.Annihilate.class)); + cards.add(new SetCardInfo("Archaeological Dig", 320, Rarity.UNCOMMON, mage.cards.a.ArchaeologicalDig.class)); + cards.add(new SetCardInfo("Ardent Soldier", 3, Rarity.COMMON, mage.cards.a.ArdentSoldier.class)); + cards.add(new SetCardInfo("Armadillo Cloak", 229, Rarity.COMMON, mage.cards.a.ArmadilloCloak.class)); + cards.add(new SetCardInfo("Armored Guardian", 230, Rarity.RARE, mage.cards.a.ArmoredGuardian.class)); + cards.add(new SetCardInfo("Artifact Mutation", 231, Rarity.RARE, mage.cards.a.ArtifactMutation.class)); + cards.add(new SetCardInfo("Assault // Battery", 295, Rarity.UNCOMMON, mage.cards.a.AssaultBattery.class)); + cards.add(new SetCardInfo("Atalya, Samite Master", 4, Rarity.RARE, mage.cards.a.AtalyaSamiteMaster.class)); + cards.add(new SetCardInfo("Aura Mutation", 232, Rarity.RARE, mage.cards.a.AuraMutation.class)); + cards.add(new SetCardInfo("Aura Shards", 233, Rarity.UNCOMMON, mage.cards.a.AuraShards.class)); + cards.add(new SetCardInfo("Backlash", 234, Rarity.UNCOMMON, mage.cards.b.Backlash.class)); + cards.add(new SetCardInfo("Barrin's Spite", 235, Rarity.RARE, mage.cards.b.BarrinsSpite.class)); + cards.add(new SetCardInfo("Barrin's Unmaking", 46, Rarity.COMMON, mage.cards.b.BarrinsUnmaking.class)); + cards.add(new SetCardInfo("Benalish Emissary", 5, Rarity.UNCOMMON, mage.cards.b.BenalishEmissary.class)); + cards.add(new SetCardInfo("Benalish Heralds", 6, Rarity.UNCOMMON, mage.cards.b.BenalishHeralds.class)); + cards.add(new SetCardInfo("Benalish Lancer", 7, Rarity.COMMON, mage.cards.b.BenalishLancer.class)); + cards.add(new SetCardInfo("Benalish Trapper", 8, Rarity.COMMON, mage.cards.b.BenalishTrapper.class)); + cards.add(new SetCardInfo("Bind", 182, Rarity.RARE, mage.cards.b.Bind.class)); + cards.add(new SetCardInfo("Blazing Specter", 236, Rarity.RARE, mage.cards.b.BlazingSpecter.class)); + cards.add(new SetCardInfo("Blinding Light", 9, Rarity.UNCOMMON, mage.cards.b.BlindingLight.class)); + cards.add(new SetCardInfo("Blind Seer", 47, Rarity.RARE, mage.cards.b.BlindSeer.class)); + cards.add(new SetCardInfo("Bloodstone Cameo", 298, Rarity.UNCOMMON, mage.cards.b.BloodstoneCameo.class)); + cards.add(new SetCardInfo("Blurred Mongoose", 183, Rarity.RARE, mage.cards.b.BlurredMongoose.class)); + cards.add(new SetCardInfo("Bog Initiate", 95, Rarity.COMMON, mage.cards.b.BogInitiate.class)); + cards.add(new SetCardInfo("Breaking Wave", 48, Rarity.RARE, mage.cards.b.BreakingWave.class)); + cards.add(new SetCardInfo("Breath of Darigaaz", 138, Rarity.UNCOMMON, mage.cards.b.BreathOfDarigaaz.class)); + cards.add(new SetCardInfo("Callous Giant", 139, Rarity.RARE, mage.cards.c.CallousGiant.class)); + cards.add(new SetCardInfo("Canopy Surge", 184, Rarity.UNCOMMON, mage.cards.c.CanopySurge.class)); + cards.add(new SetCardInfo("Capashen Unicorn", 10, Rarity.COMMON, mage.cards.c.CapashenUnicorn.class)); + cards.add(new SetCardInfo("Captain Sisay", 237, Rarity.RARE, mage.cards.c.CaptainSisay.class)); + cards.add(new SetCardInfo("Cauldron Dance", 238, Rarity.UNCOMMON, mage.cards.c.CauldronDance.class)); + cards.add(new SetCardInfo("Chaotic Strike", 140, Rarity.UNCOMMON, mage.cards.c.ChaoticStrike.class)); + cards.add(new SetCardInfo("Charging Troll", 239, Rarity.UNCOMMON, mage.cards.c.ChargingTroll.class)); + cards.add(new SetCardInfo("Chromatic Sphere", 299, Rarity.UNCOMMON, mage.cards.c.ChromaticSphere.class)); + cards.add(new SetCardInfo("Cinder Shade", 240, Rarity.UNCOMMON, mage.cards.c.CinderShade.class)); + cards.add(new SetCardInfo("Coalition Victory", 241, Rarity.RARE, mage.cards.c.CoalitionVictory.class)); + cards.add(new SetCardInfo("Coastal Tower", 321, Rarity.UNCOMMON, mage.cards.c.CoastalTower.class)); + cards.add(new SetCardInfo("Collapsing Borders", 141, Rarity.RARE, mage.cards.c.CollapsingBorders.class)); + cards.add(new SetCardInfo("Collective Restraint", 49, Rarity.RARE, mage.cards.c.CollectiveRestraint.class)); + cards.add(new SetCardInfo("Cremate", 96, Rarity.UNCOMMON, mage.cards.c.Cremate.class)); + cards.add(new SetCardInfo("Crimson Acolyte", 11, Rarity.COMMON, mage.cards.c.CrimsonAcolyte.class)); + cards.add(new SetCardInfo("Crosis's Attendant", 300, Rarity.UNCOMMON, mage.cards.c.CrosissAttendant.class)); + cards.add(new SetCardInfo("Crosis, the Purger", 242, Rarity.RARE, mage.cards.c.CrosisThePurger.class)); + cards.add(new SetCardInfo("Crown of Flames", 142, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); + cards.add(new SetCardInfo("Crusading Knight", 12, Rarity.RARE, mage.cards.c.CrusadingKnight.class)); + cards.add(new SetCardInfo("Crypt Angel", 97, Rarity.RARE, mage.cards.c.CryptAngel.class)); + cards.add(new SetCardInfo("Cursed Flesh", 98, Rarity.COMMON, mage.cards.c.CursedFlesh.class)); + cards.add(new SetCardInfo("Darigaaz's Attendant", 301, Rarity.UNCOMMON, mage.cards.d.DarigaazsAttendant.class)); + cards.add(new SetCardInfo("Darigaaz, the Igniter", 243, Rarity.RARE, mage.cards.d.DarigaazTheIgniter.class)); + cards.add(new SetCardInfo("Defiling Tears", 99, Rarity.UNCOMMON, mage.cards.d.DefilingTears.class)); + cards.add(new SetCardInfo("Devouring Strossus", 101, Rarity.RARE, mage.cards.d.DevouringStrossus.class)); + cards.add(new SetCardInfo("Dismantling Blow", 14, Rarity.COMMON, mage.cards.d.DismantlingBlow.class)); + cards.add(new SetCardInfo("Disrupt", 51, Rarity.UNCOMMON, mage.cards.d.Disrupt.class)); + cards.add(new SetCardInfo("Distorting Wake", 52, Rarity.RARE, mage.cards.d.DistortingWake.class)); + cards.add(new SetCardInfo("Divine Presence", 15, Rarity.RARE, mage.cards.d.DivinePresence.class)); + cards.add(new SetCardInfo("Do or Die", 102, Rarity.RARE, mage.cards.d.DoOrDie.class)); + cards.add(new SetCardInfo("Drake-Skull Cameo", 302, Rarity.UNCOMMON, mage.cards.d.DrakeSkullCameo.class)); + cards.add(new SetCardInfo("Dream Thrush", 53, Rarity.COMMON, mage.cards.d.DreamThrush.class)); + cards.add(new SetCardInfo("Dredge", 103, Rarity.UNCOMMON, mage.cards.d.Dredge.class)); + cards.add(new SetCardInfo("Dromar's Attendant", 303, Rarity.UNCOMMON, mage.cards.d.DromarsAttendant.class)); + cards.add(new SetCardInfo("Dromar, the Banisher", 244, Rarity.RARE, mage.cards.d.DromarTheBanisher.class)); + cards.add(new SetCardInfo("Dueling Grounds", 245, Rarity.RARE, mage.cards.d.DuelingGrounds.class)); + cards.add(new SetCardInfo("Duskwalker", 104, Rarity.COMMON, mage.cards.d.Duskwalker.class)); + cards.add(new SetCardInfo("Elfhame Palace", 322, Rarity.UNCOMMON, mage.cards.e.ElfhamePalace.class)); + cards.add(new SetCardInfo("Elfhame Sanctuary", 185, Rarity.UNCOMMON, mage.cards.e.ElfhameSanctuary.class)); + cards.add(new SetCardInfo("Elvish Champion", 186, Rarity.RARE, mage.cards.e.ElvishChampion.class)); + cards.add(new SetCardInfo("Empress Galina", 54, Rarity.RARE, mage.cards.e.EmpressGalina.class)); + cards.add(new SetCardInfo("Exclude", 56, Rarity.COMMON, mage.cards.e.Exclude.class)); + cards.add(new SetCardInfo("Exotic Curse", 105, Rarity.COMMON, mage.cards.e.ExoticCurse.class)); + cards.add(new SetCardInfo("Explosive Growth", 187, Rarity.COMMON, mage.cards.e.ExplosiveGrowth.class)); + cards.add(new SetCardInfo("Fact or Fiction", 57, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); + cards.add(new SetCardInfo("Faerie Squadron", 58, Rarity.COMMON, mage.cards.f.FaerieSquadron.class)); + cards.add(new SetCardInfo("Fertile Ground", 188, Rarity.COMMON, mage.cards.f.FertileGround.class)); + cards.add(new SetCardInfo("Firebrand Ranger", 143, Rarity.UNCOMMON, mage.cards.f.FirebrandRanger.class)); + cards.add(new SetCardInfo("Firescreamer", 106, Rarity.COMMON, mage.cards.f.Firescreamer.class)); + cards.add(new SetCardInfo("Fires of Yavimaya", 246, Rarity.UNCOMMON, mage.cards.f.FiresOfYavimaya.class)); + cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frenzied Tilling", 247, Rarity.COMMON, mage.cards.f.FrenziedTilling.class)); + cards.add(new SetCardInfo("Galina's Knight", 248, Rarity.COMMON, mage.cards.g.GalinasKnight.class)); + cards.add(new SetCardInfo("Geothermal Crevice", 323, Rarity.COMMON, mage.cards.g.GeothermalCrevice.class)); + cards.add(new SetCardInfo("Ghitu Fire", 144, Rarity.RARE, mage.cards.g.GhituFire.class)); + cards.add(new SetCardInfo("Glimmering Angel", 17, Rarity.COMMON, mage.cards.g.GlimmeringAngel.class)); + cards.add(new SetCardInfo("Global Ruin", 18, Rarity.RARE, mage.cards.g.GlobalRuin.class)); + cards.add(new SetCardInfo("Goblin Spy", 145, Rarity.UNCOMMON, mage.cards.g.GoblinSpy.class)); + cards.add(new SetCardInfo("Goham Djinn", 107, Rarity.UNCOMMON, mage.cards.g.GohamDjinn.class)); + cards.add(new SetCardInfo("Halam Djinn", 146, Rarity.UNCOMMON, mage.cards.h.HalamDjinn.class)); + cards.add(new SetCardInfo("Hanna, Ship's Navigator", 249, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); + cards.add(new SetCardInfo("Harrow", 189, Rarity.COMMON, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("Harsh Judgment", 19, Rarity.RARE, mage.cards.h.HarshJudgment.class)); + cards.add(new SetCardInfo("Hate Weaver", 108, Rarity.UNCOMMON, mage.cards.h.HateWeaver.class)); + cards.add(new SetCardInfo("Heroes' Reunion", 250, Rarity.UNCOMMON, mage.cards.h.HeroesReunion.class)); + cards.add(new SetCardInfo("Holy Day", 20, Rarity.COMMON, mage.cards.h.HolyDay.class)); + cards.add(new SetCardInfo("Hooded Kavu", 147, Rarity.COMMON, mage.cards.h.HoodedKavu.class)); + cards.add(new SetCardInfo("Horned Cheetah", 251, Rarity.UNCOMMON, mage.cards.h.HornedCheetah.class)); + cards.add(new SetCardInfo("Hunting Kavu", 252, Rarity.UNCOMMON, mage.cards.h.HuntingKavu.class)); + cards.add(new SetCardInfo("Hypnotic Cloud", 109, Rarity.COMMON, mage.cards.h.HypnoticCloud.class)); + cards.add(new SetCardInfo("Irrigation Ditch", 324, Rarity.COMMON, mage.cards.i.IrrigationDitch.class)); + cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jade Leech", 190, Rarity.RARE, mage.cards.j.JadeLeech.class)); + cards.add(new SetCardInfo("Juntu Stakes", 304, Rarity.RARE, mage.cards.j.JuntuStakes.class)); + cards.add(new SetCardInfo("Kangee, Aerie Keeper", 253, Rarity.RARE, mage.cards.k.KangeeAerieKeeper.class)); + cards.add(new SetCardInfo("Kavu Aggressor", 148, Rarity.COMMON, mage.cards.k.KavuAggressor.class)); + cards.add(new SetCardInfo("Kavu Chameleon", 191, Rarity.UNCOMMON, mage.cards.k.KavuChameleon.class)); + cards.add(new SetCardInfo("Kavu Climber", 192, Rarity.COMMON, mage.cards.k.KavuClimber.class)); + cards.add(new SetCardInfo("Kavu Lair", 193, Rarity.RARE, mage.cards.k.KavuLair.class)); + cards.add(new SetCardInfo("Kavu Monarch", 149, Rarity.RARE, mage.cards.k.KavuMonarch.class)); + cards.add(new SetCardInfo("Kavu Runner", 150, Rarity.UNCOMMON, mage.cards.k.KavuRunner.class)); + cards.add(new SetCardInfo("Kavu Scout", 151, Rarity.COMMON, mage.cards.k.KavuScout.class)); + cards.add(new SetCardInfo("Kavu Titan", 194, Rarity.RARE, mage.cards.k.KavuTitan.class)); + cards.add(new SetCardInfo("Keldon Necropolis", 325, Rarity.RARE, mage.cards.k.KeldonNecropolis.class)); + cards.add(new SetCardInfo("Liberate", 21, Rarity.UNCOMMON, mage.cards.l.Liberate.class)); + cards.add(new SetCardInfo("Lightning Dart", 152, Rarity.UNCOMMON, mage.cards.l.LightningDart.class)); + cards.add(new SetCardInfo("Llanowar Cavalry", 195, Rarity.COMMON, mage.cards.l.LlanowarCavalry.class)); + cards.add(new SetCardInfo("Llanowar Elite", 196, Rarity.COMMON, mage.cards.l.LlanowarElite.class)); + cards.add(new SetCardInfo("Llanowar Knight", 254, Rarity.COMMON, mage.cards.l.LlanowarKnight.class)); + cards.add(new SetCardInfo("Llanowar Vanguard", 197, Rarity.COMMON, mage.cards.l.LlanowarVanguard.class)); + cards.add(new SetCardInfo("Lobotomy", 255, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); + cards.add(new SetCardInfo("Lotus Guardian", 305, Rarity.RARE, mage.cards.l.LotusGuardian.class)); + cards.add(new SetCardInfo("Mana Maze", 59, Rarity.RARE, mage.cards.m.ManaMaze.class)); + cards.add(new SetCardInfo("Maniacal Rage", 155, Rarity.COMMON, mage.cards.m.ManiacalRage.class)); + cards.add(new SetCardInfo("Manipulate Fate", 60, Rarity.UNCOMMON, mage.cards.m.ManipulateFate.class)); + cards.add(new SetCardInfo("Marauding Knight", 110, Rarity.RARE, mage.cards.m.MaraudingKnight.class)); + cards.add(new SetCardInfo("Metathran Transport", 62, Rarity.UNCOMMON, mage.cards.m.MetathranTransport.class)); + cards.add(new SetCardInfo("Metathran Zombie", 63, Rarity.COMMON, mage.cards.m.MetathranZombie.class)); + cards.add(new SetCardInfo("Meteor Storm", 256, Rarity.RARE, mage.cards.m.MeteorStorm.class)); + cards.add(new SetCardInfo("Might Weaver", 198, Rarity.UNCOMMON, mage.cards.m.MightWeaver.class)); + cards.add(new SetCardInfo("Molimo, Maro-Sorcerer", 199, Rarity.RARE, mage.cards.m.MolimoMaroSorcerer.class)); + cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mourning", 111, Rarity.COMMON, mage.cards.m.Mourning.class)); + cards.add(new SetCardInfo("Nightscape Apprentice", 112, Rarity.COMMON, mage.cards.n.NightscapeApprentice.class)); + cards.add(new SetCardInfo("Nightscape Master", 113, Rarity.RARE, mage.cards.n.NightscapeMaster.class)); + cards.add(new SetCardInfo("Noble Panther", 257, Rarity.RARE, mage.cards.n.NoblePanther.class)); + cards.add(new SetCardInfo("Nomadic Elf", 200, Rarity.COMMON, mage.cards.n.NomadicElf.class)); + cards.add(new SetCardInfo("Obliterate", 156, Rarity.RARE, mage.cards.o.Obliterate.class)); + cards.add(new SetCardInfo("Obsidian Acolyte", 22, Rarity.COMMON, mage.cards.o.ObsidianAcolyte.class)); + cards.add(new SetCardInfo("Opt", 64, Rarity.COMMON, mage.cards.o.Opt.class)); + cards.add(new SetCardInfo("Ordered Migration", 258, Rarity.UNCOMMON, mage.cards.o.OrderedMigration.class)); + cards.add(new SetCardInfo("Orim's Touch", 23, Rarity.COMMON, mage.cards.o.OrimsTouch.class)); + cards.add(new SetCardInfo("Overabundance", 259, Rarity.RARE, mage.cards.o.Overabundance.class)); + cards.add(new SetCardInfo("Overload", 157, Rarity.COMMON, mage.cards.o.Overload.class)); + cards.add(new SetCardInfo("Pain // Suffering", 294, Rarity.UNCOMMON, mage.cards.p.PainSuffering.class)); + cards.add(new SetCardInfo("Phantasmal Terrain", 65, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); + cards.add(new SetCardInfo("Phyrexian Altar", 306, Rarity.RARE, mage.cards.p.PhyrexianAltar.class)); + cards.add(new SetCardInfo("Phyrexian Battleflies", 114, Rarity.COMMON, mage.cards.p.PhyrexianBattleflies.class)); + cards.add(new SetCardInfo("Phyrexian Delver", 115, Rarity.RARE, mage.cards.p.PhyrexianDelver.class)); + cards.add(new SetCardInfo("Phyrexian Infiltrator", 116, Rarity.RARE, mage.cards.p.PhyrexianInfiltrator.class)); + cards.add(new SetCardInfo("Phyrexian Lens", 307, Rarity.RARE, mage.cards.p.PhyrexianLens.class)); + cards.add(new SetCardInfo("Phyrexian Reaper", 117, Rarity.COMMON, mage.cards.p.PhyrexianReaper.class)); + cards.add(new SetCardInfo("Phyrexian Slayer", 118, Rarity.COMMON, mage.cards.p.PhyrexianSlayer.class)); + cards.add(new SetCardInfo("Pincer Spider", 201, Rarity.COMMON, mage.cards.p.PincerSpider.class)); + cards.add(new SetCardInfo("Plague Spitter", 119, Rarity.UNCOMMON, mage.cards.p.PlagueSpitter.class)); + cards.add(new SetCardInfo("Plague Spores", 260, Rarity.COMMON, mage.cards.p.PlagueSpores.class)); + cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Planar Portal", 308, Rarity.RARE, mage.cards.p.PlanarPortal.class)); + cards.add(new SetCardInfo("Pouncing Kavu", 158, Rarity.COMMON, mage.cards.p.PouncingKavu.class)); + cards.add(new SetCardInfo("Power Armor", 309, Rarity.UNCOMMON, mage.cards.p.PowerArmor.class)); + cards.add(new SetCardInfo("Prison Barricade", 25, Rarity.COMMON, mage.cards.p.PrisonBarricade.class)); + cards.add(new SetCardInfo("Probe", 66, Rarity.COMMON, mage.cards.p.Probe.class)); + cards.add(new SetCardInfo("Prohibit", 67, Rarity.COMMON, mage.cards.p.Prohibit.class)); + cards.add(new SetCardInfo("Protective Sphere", 26, Rarity.COMMON, mage.cards.p.ProtectiveSphere.class)); + cards.add(new SetCardInfo("Pure Reflection", 27, Rarity.RARE, mage.cards.p.PureReflection.class)); + cards.add(new SetCardInfo("Pyre Zombie", 261, Rarity.RARE, mage.cards.p.PyreZombie.class)); + cards.add(new SetCardInfo("Quirion Elves", 203, Rarity.COMMON, mage.cards.q.QuirionElves.class)); + cards.add(new SetCardInfo("Quirion Sentinel", 204, Rarity.COMMON, mage.cards.q.QuirionSentinel.class)); + cards.add(new SetCardInfo("Quirion Trailblazer", 205, Rarity.COMMON, mage.cards.q.QuirionTrailblazer.class)); + cards.add(new SetCardInfo("Rage Weaver", 159, Rarity.UNCOMMON, mage.cards.r.RageWeaver.class)); + cards.add(new SetCardInfo("Raging Kavu", 262, Rarity.RARE, mage.cards.r.RagingKavu.class)); + cards.add(new SetCardInfo("Rainbow Crow", 69, Rarity.UNCOMMON, mage.cards.r.RainbowCrow.class)); + cards.add(new SetCardInfo("Rampant Elephant", 28, Rarity.COMMON, mage.cards.r.RampantElephant.class)); + cards.add(new SetCardInfo("Ravenous Rats", 120, Rarity.COMMON, mage.cards.r.RavenousRats.class)); + cards.add(new SetCardInfo("Razorfoot Griffin", 29, Rarity.COMMON, mage.cards.r.RazorfootGriffin.class)); + cards.add(new SetCardInfo("Reckless Assault", 263, Rarity.RARE, mage.cards.r.RecklessAssault.class)); + cards.add(new SetCardInfo("Reckless Spite", 121, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); + cards.add(new SetCardInfo("Recoil", 264, Rarity.COMMON, mage.cards.r.Recoil.class)); + cards.add(new SetCardInfo("Recover", 122, Rarity.COMMON, mage.cards.r.Recover.class)); + cards.add(new SetCardInfo("Repulse", 70, Rarity.COMMON, mage.cards.r.Repulse.class)); + cards.add(new SetCardInfo("Restock", 206, Rarity.RARE, mage.cards.r.Restock.class)); + cards.add(new SetCardInfo("Restrain", 30, Rarity.COMMON, mage.cards.r.Restrain.class)); + cards.add(new SetCardInfo("Reviving Dose", 31, Rarity.COMMON, mage.cards.r.RevivingDose.class)); + cards.add(new SetCardInfo("Reviving Vapors", 265, Rarity.UNCOMMON, mage.cards.r.RevivingVapors.class)); + cards.add(new SetCardInfo("Rewards of Diversity", 32, Rarity.UNCOMMON, mage.cards.r.RewardsOfDiversity.class)); + cards.add(new SetCardInfo("Reya Dawnbringer", 33, Rarity.RARE, mage.cards.r.ReyaDawnbringer.class)); + cards.add(new SetCardInfo("Riptide Crab", 266, Rarity.UNCOMMON, mage.cards.r.RiptideCrab.class)); + cards.add(new SetCardInfo("Rith's Attendant", 310, Rarity.UNCOMMON, mage.cards.r.RithsAttendant.class)); + cards.add(new SetCardInfo("Rith, the Awakener", 267, Rarity.RARE, mage.cards.r.RithTheAwakener.class)); + cards.add(new SetCardInfo("Rogue Kavu", 160, Rarity.COMMON, mage.cards.r.RogueKavu.class)); + cards.add(new SetCardInfo("Rooting Kavu", 207, Rarity.UNCOMMON, mage.cards.r.RootingKavu.class)); + cards.add(new SetCardInfo("Rout", 34, Rarity.RARE, mage.cards.r.Rout.class)); + cards.add(new SetCardInfo("Ruby Leech", 161, Rarity.RARE, mage.cards.r.RubyLeech.class)); + cards.add(new SetCardInfo("Ruham Djinn", 35, Rarity.UNCOMMON, mage.cards.r.RuhamDjinn.class)); + cards.add(new SetCardInfo("Sabertooth Nishoba", 268, Rarity.RARE, mage.cards.s.SabertoothNishoba.class)); + cards.add(new SetCardInfo("Salt Marsh", 326, Rarity.UNCOMMON, mage.cards.s.SaltMarsh.class)); + cards.add(new SetCardInfo("Samite Archer", 269, Rarity.UNCOMMON, mage.cards.s.SamiteArcher.class)); + cards.add(new SetCardInfo("Sapphire Leech", 71, Rarity.RARE, mage.cards.s.SapphireLeech.class)); + cards.add(new SetCardInfo("Saproling Symbiosis", 209, Rarity.RARE, mage.cards.s.SaprolingSymbiosis.class)); + cards.add(new SetCardInfo("Savage Offensive", 162, Rarity.COMMON, mage.cards.s.SavageOffensive.class)); + cards.add(new SetCardInfo("Scarred Puma", 163, Rarity.COMMON, mage.cards.s.ScarredPuma.class)); + cards.add(new SetCardInfo("Scavenged Weaponry", 123, Rarity.COMMON, mage.cards.s.ScavengedWeaponry.class)); + cards.add(new SetCardInfo("Scorching Lava", 164, Rarity.COMMON, mage.cards.s.ScorchingLava.class)); + cards.add(new SetCardInfo("Scouting Trek", 210, Rarity.UNCOMMON, mage.cards.s.ScoutingTrek.class)); + cards.add(new SetCardInfo("Seashell Cameo", 311, Rarity.UNCOMMON, mage.cards.s.SeashellCameo.class)); + cards.add(new SetCardInfo("Seer's Vision", 270, Rarity.UNCOMMON, mage.cards.s.SeersVision.class)); + cards.add(new SetCardInfo("Serpentine Kavu", 211, Rarity.COMMON, mage.cards.s.SerpentineKavu.class)); + cards.add(new SetCardInfo("Shackles", 37, Rarity.COMMON, mage.cards.s.Shackles.class)); + cards.add(new SetCardInfo("Shimmering Wings", 72, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); + cards.add(new SetCardInfo("Shivan Emissary", 166, Rarity.UNCOMMON, mage.cards.s.ShivanEmissary.class)); + cards.add(new SetCardInfo("Shivan Harvest", 167, Rarity.UNCOMMON, mage.cards.s.ShivanHarvest.class)); + cards.add(new SetCardInfo("Shivan Oasis", 327, Rarity.UNCOMMON, mage.cards.s.ShivanOasis.class)); + cards.add(new SetCardInfo("Shivan Zombie", 271, Rarity.COMMON, mage.cards.s.ShivanZombie.class)); + cards.add(new SetCardInfo("Shoreline Raider", 73, Rarity.COMMON, mage.cards.s.ShorelineRaider.class)); + cards.add(new SetCardInfo("Simoon", 272, Rarity.UNCOMMON, mage.cards.s.Simoon.class)); + cards.add(new SetCardInfo("Skittish Kavu", 168, Rarity.UNCOMMON, mage.cards.s.SkittishKavu.class)); + cards.add(new SetCardInfo("Skizzik", 169, Rarity.RARE, mage.cards.s.Skizzik.class)); + cards.add(new SetCardInfo("Sky Weaver", 74, Rarity.UNCOMMON, mage.cards.s.SkyWeaver.class)); + cards.add(new SetCardInfo("Sleeper's Robe", 273, Rarity.UNCOMMON, mage.cards.s.SleepersRobe.class)); + cards.add(new SetCardInfo("Slimy Kavu", 170, Rarity.COMMON, mage.cards.s.SlimyKavu.class)); + cards.add(new SetCardInfo("Slinking Serpent", 274, Rarity.UNCOMMON, mage.cards.s.SlinkingSerpent.class)); + cards.add(new SetCardInfo("Smoldering Tar", 275, Rarity.UNCOMMON, mage.cards.s.SmolderingTar.class)); + cards.add(new SetCardInfo("Soul Burn", 124, Rarity.COMMON, mage.cards.s.SoulBurn.class)); + cards.add(new SetCardInfo("Sparring Golem", 312, Rarity.UNCOMMON, mage.cards.s.SparringGolem.class)); + cards.add(new SetCardInfo("Spinal Embrace", 276, Rarity.RARE, mage.cards.s.SpinalEmbrace.class)); + cards.add(new SetCardInfo("Spirit of Resistance", 38, Rarity.RARE, mage.cards.s.SpiritOfResistance.class)); + cards.add(new SetCardInfo("Spirit Weaver", 39, Rarity.UNCOMMON, mage.cards.s.SpiritWeaver.class)); + cards.add(new SetCardInfo("Spite // Malice", 293, Rarity.UNCOMMON, mage.cards.s.SpiteMalice.class)); + cards.add(new SetCardInfo("Spreading Plague", 125, Rarity.RARE, mage.cards.s.SpreadingPlague.class)); + cards.add(new SetCardInfo("Stalking Assassin", 277, Rarity.RARE, mage.cards.s.StalkingAssassin.class)); + cards.add(new SetCardInfo("Stand // Deliver", 292, Rarity.UNCOMMON, mage.cards.s.StandDeliver.class)); + cards.add(new SetCardInfo("Sterling Grove", 278, Rarity.UNCOMMON, mage.cards.s.SterlingGrove.class)); + cards.add(new SetCardInfo("Stormscape Apprentice", 75, Rarity.COMMON, mage.cards.s.StormscapeApprentice.class)); + cards.add(new SetCardInfo("Stormscape Master", 76, Rarity.RARE, mage.cards.s.StormscapeMaster.class)); + cards.add(new SetCardInfo("Strength of Unity", 40, Rarity.COMMON, mage.cards.s.StrengthOfUnity.class)); + cards.add(new SetCardInfo("Stun", 172, Rarity.COMMON, mage.cards.s.Stun.class)); + cards.add(new SetCardInfo("Sulam Djinn", 212, Rarity.UNCOMMON, mage.cards.s.SulamDjinn.class)); + cards.add(new SetCardInfo("Sulfur Vent", 328, Rarity.COMMON, mage.cards.s.SulfurVent.class)); + cards.add(new SetCardInfo("Sunscape Apprentice", 41, Rarity.COMMON, mage.cards.s.SunscapeApprentice.class)); + cards.add(new SetCardInfo("Sunscape Master", 42, Rarity.RARE, mage.cards.s.SunscapeMaster.class)); + cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sway of Illusion", 77, Rarity.UNCOMMON, mage.cards.s.SwayOfIllusion.class)); + cards.add(new SetCardInfo("Tainted Well", 126, Rarity.COMMON, mage.cards.t.TaintedWell.class)); + cards.add(new SetCardInfo("Tangle", 213, Rarity.UNCOMMON, mage.cards.t.Tangle.class)); + cards.add(new SetCardInfo("Tectonic Instability", 173, Rarity.RARE, mage.cards.t.TectonicInstability.class)); + cards.add(new SetCardInfo("Teferi's Care", 43, Rarity.UNCOMMON, mage.cards.t.TeferisCare.class)); + cards.add(new SetCardInfo("Teferi's Moat", 279, Rarity.RARE, mage.cards.t.TeferisMoat.class)); + cards.add(new SetCardInfo("Teferi's Response", 78, Rarity.RARE, mage.cards.t.TeferisResponse.class)); + cards.add(new SetCardInfo("Tek", 313, Rarity.RARE, mage.cards.t.Tek.class)); + cards.add(new SetCardInfo("Temporal Distortion", 79, Rarity.RARE, mage.cards.t.TemporalDistortion.class)); + cards.add(new SetCardInfo("Thicket Elemental", 214, Rarity.RARE, mage.cards.t.ThicketElemental.class)); + cards.add(new SetCardInfo("Thornscape Apprentice", 215, Rarity.COMMON, mage.cards.t.ThornscapeApprentice.class)); + cards.add(new SetCardInfo("Thornscape Master", 216, Rarity.RARE, mage.cards.t.ThornscapeMaster.class)); + cards.add(new SetCardInfo("Thunderscape Apprentice", 174, Rarity.COMMON, mage.cards.t.ThunderscapeApprentice.class)); + cards.add(new SetCardInfo("Thunderscape Master", 175, Rarity.RARE, mage.cards.t.ThunderscapeMaster.class)); + cards.add(new SetCardInfo("Tidal Visionary", 80, Rarity.COMMON, mage.cards.t.TidalVisionary.class)); + cards.add(new SetCardInfo("Tigereye Cameo", 314, Rarity.UNCOMMON, mage.cards.t.TigereyeCameo.class)); + cards.add(new SetCardInfo("Tinder Farm", 329, Rarity.COMMON, mage.cards.t.TinderFarm.class)); + cards.add(new SetCardInfo("Tolarian Emissary", 81, Rarity.UNCOMMON, mage.cards.t.TolarianEmissary.class)); + cards.add(new SetCardInfo("Tower Drake", 82, Rarity.COMMON, mage.cards.t.TowerDrake.class)); + cards.add(new SetCardInfo("Tranquility", 217, Rarity.COMMON, mage.cards.t.Tranquility.class)); + cards.add(new SetCardInfo("Traveler's Cloak", 83, Rarity.COMMON, mage.cards.t.TravelersCloak.class)); + cards.add(new SetCardInfo("Treefolk Healer", 218, Rarity.UNCOMMON, mage.cards.t.TreefolkHealer.class)); + cards.add(new SetCardInfo("Trench Wurm", 127, Rarity.UNCOMMON, mage.cards.t.TrenchWurm.class)); + cards.add(new SetCardInfo("Treva's Attendant", 315, Rarity.UNCOMMON, mage.cards.t.TrevasAttendant.class)); + cards.add(new SetCardInfo("Treva, the Renewer", 280, Rarity.RARE, mage.cards.t.TrevaTheRenewer.class)); + cards.add(new SetCardInfo("Tribal Flames", 176, Rarity.COMMON, mage.cards.t.TribalFlames.class)); + cards.add(new SetCardInfo("Troll-Horn Cameo", 316, Rarity.UNCOMMON, mage.cards.t.TrollHornCameo.class)); + cards.add(new SetCardInfo("Tsabo's Assassin", 128, Rarity.RARE, mage.cards.t.TsabosAssassin.class)); + cards.add(new SetCardInfo("Tsabo's Decree", 129, Rarity.RARE, mage.cards.t.TsabosDecree.class)); + cards.add(new SetCardInfo("Tsabo's Web", 317, Rarity.RARE, mage.cards.t.TsabosWeb.class)); + cards.add(new SetCardInfo("Tsabo Tavoc", 281, Rarity.RARE, mage.cards.t.TsaboTavoc.class)); + cards.add(new SetCardInfo("Turf Wound", 177, Rarity.COMMON, mage.cards.t.TurfWound.class)); + cards.add(new SetCardInfo("Twilight's Call", 130, Rarity.RARE, mage.cards.t.TwilightsCall.class)); + cards.add(new SetCardInfo("Undermine", 282, Rarity.RARE, mage.cards.u.Undermine.class)); + cards.add(new SetCardInfo("Urborg Drake", 283, Rarity.UNCOMMON, mage.cards.u.UrborgDrake.class)); + cards.add(new SetCardInfo("Urborg Emissary", 131, Rarity.UNCOMMON, mage.cards.u.UrborgEmissary.class)); + cards.add(new SetCardInfo("Urborg Phantom", 132, Rarity.COMMON, mage.cards.u.UrborgPhantom.class)); + cards.add(new SetCardInfo("Urborg Shambler", 133, Rarity.UNCOMMON, mage.cards.u.UrborgShambler.class)); + cards.add(new SetCardInfo("Urborg Skeleton", 134, Rarity.COMMON, mage.cards.u.UrborgSkeleton.class)); + cards.add(new SetCardInfo("Urborg Volcano", 330, Rarity.UNCOMMON, mage.cards.u.UrborgVolcano.class)); + cards.add(new SetCardInfo("Urza's Filter", 318, Rarity.RARE, mage.cards.u.UrzasFilter.class)); + cards.add(new SetCardInfo("Urza's Rage", 178, Rarity.RARE, mage.cards.u.UrzasRage.class)); + cards.add(new SetCardInfo("Utopia Tree", 219, Rarity.RARE, mage.cards.u.UtopiaTree.class)); + cards.add(new SetCardInfo("Verdeloth the Ancient", 220, Rarity.RARE, mage.cards.v.VerdelothTheAncient.class)); + cards.add(new SetCardInfo("Verduran Emissary", 221, Rarity.UNCOMMON, mage.cards.v.VerduranEmissary.class)); + cards.add(new SetCardInfo("Viashino Grappler", 179, Rarity.COMMON, mage.cards.v.ViashinoGrappler.class)); + cards.add(new SetCardInfo("Vicious Kavu", 284, Rarity.UNCOMMON, mage.cards.v.ViciousKavu.class)); + cards.add(new SetCardInfo("Vile Consumption", 285, Rarity.RARE, mage.cards.v.VileConsumption.class)); + cards.add(new SetCardInfo("Vodalian Hypnotist", 84, Rarity.UNCOMMON, mage.cards.v.VodalianHypnotist.class)); + cards.add(new SetCardInfo("Vodalian Merchant", 85, Rarity.COMMON, mage.cards.v.VodalianMerchant.class)); + cards.add(new SetCardInfo("Vodalian Serpent", 86, Rarity.COMMON, mage.cards.v.VodalianSerpent.class)); + cards.add(new SetCardInfo("Vodalian Zombie", 286, Rarity.COMMON, mage.cards.v.VodalianZombie.class)); + cards.add(new SetCardInfo("Void", 287, Rarity.RARE, mage.cards.v.Void.class)); + cards.add(new SetCardInfo("Voracious Cobra", 288, Rarity.UNCOMMON, mage.cards.v.VoraciousCobra.class)); + cards.add(new SetCardInfo("Wallop", 223, Rarity.UNCOMMON, mage.cards.w.Wallop.class)); + cards.add(new SetCardInfo("Wandering Stream", 224, Rarity.COMMON, mage.cards.w.WanderingStream.class)); + cards.add(new SetCardInfo("Wash Out", 87, Rarity.UNCOMMON, mage.cards.w.WashOut.class)); + cards.add(new SetCardInfo("Wax // Wane", 296, Rarity.UNCOMMON, mage.cards.w.WaxWane.class)); + cards.add(new SetCardInfo("Wayfaring Giant", 44, Rarity.UNCOMMON, mage.cards.w.WayfaringGiant.class)); + cards.add(new SetCardInfo("Whip Silk", 225, Rarity.COMMON, mage.cards.w.WhipSilk.class)); + cards.add(new SetCardInfo("Wings of Hope", 289, Rarity.COMMON, mage.cards.w.WingsOfHope.class)); + cards.add(new SetCardInfo("Winnow", 45, Rarity.RARE, mage.cards.w.Winnow.class)); + cards.add(new SetCardInfo("Worldly Counsel", 89, Rarity.COMMON, mage.cards.w.WorldlyCounsel.class)); + cards.add(new SetCardInfo("Yavimaya Barbarian", 290, Rarity.COMMON, mage.cards.y.YavimayaBarbarian.class)); + cards.add(new SetCardInfo("Yavimaya Kavu", 291, Rarity.UNCOMMON, mage.cards.y.YavimayaKavu.class)); + cards.add(new SetCardInfo("Yawgmoth's Agenda", 135, Rarity.RARE, mage.cards.y.YawgmothsAgenda.class)); + cards.add(new SetCardInfo("Zanam Djinn", 90, Rarity.UNCOMMON, mage.cards.z.ZanamDjinn.class)); + cards.add(new SetCardInfo("Zap", 180, Rarity.COMMON, mage.cards.z.Zap.class)); + } +} From cdbe1b0ad92715d7a4923131922c955629b30551 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 00:07:45 +0100 Subject: [PATCH 061/133] * Dinosaur Hunter - Fixed that its ability also triggered for non combat damage. --- .../src/mage/cards/d/DinosaurHunter.java | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java index 9711eb8b515..3e763804225 100644 --- a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java +++ b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java @@ -28,17 +28,14 @@ package mage.cards.d; import java.util.UUID; - import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.*; -import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -48,12 +45,6 @@ import mage.target.targetpointer.FixedTarget; */ public class DinosaurHunter extends CardImpl { - private static final FilterControlledCreaturePermanent filterAnotherDino = new FilterControlledCreaturePermanent(); - static { - filterAnotherDino.add(new AnotherPredicate()); - filterAnotherDino.add(new SubtypePredicate(SubType.DINOSAUR)); - } - public DinosaurHunter(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); @@ -78,11 +69,6 @@ public class DinosaurHunter extends CardImpl { class DinosaurHunterAbility extends TriggeredAbilityImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - static { - filter.add(new SubtypePredicate(SubType.DINOSAUR)); - } - DinosaurHunterAbility() { super(Zone.BATTLEFIELD, new DestroyTargetEffect()); } @@ -103,11 +89,13 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanet = game.getPermanent(event.getSourceId()); - Permanent targetPermanet = game.getPermanent(event.getTargetId()); - if (sourcePermanet != null && targetPermanet != null && event.getSourceId().equals(sourceId) && filter.match(targetPermanet, game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); - return true; + if (((DamageEvent) event).isCombatDamage() + && event.getSourceId().equals(getSourceId())) { + Permanent targetPermanet = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (targetPermanet.hasSubtype(SubType.DINOSAUR, game)) { + getEffects().get(0).setTargetPointer(new FixedTarget(targetPermanet, game)); + return true; + } } return false; } @@ -116,4 +104,4 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever {this} deals combat damage to a Dinosaur, destroy that creature."; } -} \ No newline at end of file +} From cdb2404bb63936ad778a3c4a043af9661375f5b8 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 23 Feb 2018 17:12:38 -0600 Subject: [PATCH 062/133] - Added requested card Mercadia's Downfall --- .../src/mage/cards/m/MercadiasDownfall.java | 112 ++++++++++++++++++ Mage.Sets/src/mage/sets/MercadianMasques.java | 1 + 2 files changed, 113 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MercadiasDownfall.java diff --git a/Mage.Sets/src/mage/cards/m/MercadiasDownfall.java b/Mage.Sets/src/mage/cards/m/MercadiasDownfall.java new file mode 100644 index 00000000000..c905d973c03 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MercadiasDownfall.java @@ -0,0 +1,112 @@ +/* + * 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.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SuperType; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; + +/** + * + * @author jeffwadsworth + */ +public class MercadiasDownfall extends CardImpl { + + private static String rule = "Each attacking creature gets +1/+0 until end of turn for each nonbasic land defending player controls"; + + public MercadiasDownfall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Each attacking creature gets +1/+0 until end of turn for each nonbasic land defending player controls. + this.getSpellAbility().addEffect(new BoostAllEffect(new DefendersNonBasicLandCount(), new StaticValue(0), Duration.EndOfTurn, new FilterAttackingCreature(), true, rule)); + + } + + public MercadiasDownfall(final MercadiasDownfall card) { + super(card); + } + + @Override + public MercadiasDownfall copy() { + return new MercadiasDownfall(this); + } + + class DefendersNonBasicLandCount implements DynamicValue { + + UUID defenderId; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + for (CombatGroup group : game.getCombat().getGroups()) { + defenderId = group.getDefenderId(); + if (group.isDefenderIsPlaneswalker()) { + Permanent permanent = game.getPermanent(defenderId); + if (permanent != null) { + defenderId = permanent.getControllerId(); + } + } + FilterLandPermanent filter = new FilterLandPermanent("nonbasic land"); + filter.add(Predicates.not(new SupertypePredicate(SuperType.BASIC))); + System.out.println("The number of nonbasic lands is " + game.getBattlefield().countAll(filter, defenderId, game)); + return game.getBattlefield().countAll(filter, defenderId, game); + } + return 0; + } + + @Override + public DynamicValue copy() { + return new DefendersNonBasicLandCount(); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "the number of nonbasic lands defending player controls"; + } + } + +} diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index 5ca90702ace..2b167bc1d48 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -219,6 +219,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Maggot Therapy", 145, Rarity.COMMON, mage.cards.m.MaggotTherapy.class)); cards.add(new SetCardInfo("Magistrate's Scepter", 304, Rarity.RARE, mage.cards.m.MagistratesScepter.class)); cards.add(new SetCardInfo("Magistrate's Veto", 204, Rarity.UNCOMMON, mage.cards.m.MagistratesVeto.class)); + cards.add(new SetCardInfo("Mercadia's Downfall", 205, Rarity.UNCOMMON, mage.cards.m.MercadiasDownfall.class)); cards.add(new SetCardInfo("Mercadian Bazaar", 321, Rarity.UNCOMMON, mage.cards.m.MercadianBazaar.class)); cards.add(new SetCardInfo("Midnight Ritual", 146, Rarity.RARE, mage.cards.m.MidnightRitual.class)); cards.add(new SetCardInfo("Misdirection", 87, Rarity.RARE, mage.cards.m.Misdirection.class)); From 9a95e49ac9c8a8a0305118bb35c9853ceb90d2eb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 01:00:23 +0100 Subject: [PATCH 063/133] * Fixed Stifle rule text and some other minor things related to TragetActivatedAbility. --- Mage.Sets/src/mage/cards/a/AyeshaTanaka.java | 36 +++---------- Mage.Sets/src/mage/cards/b/BrownOuphe.java | 35 +++---------- Mage.Sets/src/mage/cards/i/Interdict.java | 14 +++-- Mage.Sets/src/mage/cards/r/Rust.java | 33 ++---------- .../ability/ArtifactSourcePredicate.java | 51 +++++++++++++++++++ .../target/common/TargetActivatedAbility.java | 2 +- .../TargetActivatedOrTriggeredAbility.java | 2 +- 7 files changed, 80 insertions(+), 93 deletions(-) create mode 100644 Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java diff --git a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java index d16fa76af3b..f6e44e52830 100644 --- a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java +++ b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java @@ -37,16 +37,13 @@ import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.BandingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackAbility; -import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.filter.FilterAbility; +import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.target.common.TargetActivatedAbility; /** * @@ -54,26 +51,26 @@ import mage.target.common.TargetActivatedOrTriggeredAbility; */ public class AyeshaTanaka extends CardImpl { - private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); + private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source"); static { filter.add(new ArtifactSourcePredicate()); } public AyeshaTanaka(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}{U}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARTIFICER); this.power = new MageInt(2); this.toughness = new MageInt(2); - + // Banding this.addAbility(BandingAbility.getInstance()); // {T}: Counter target activated ability from an artifact source unless that ability's controller pays {W}. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new ManaCostsImpl("{W}")), new TapSourceCost()); - ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); + ability.addTarget(new TargetActivatedAbility(filter)); this.addAbility(ability); } @@ -86,22 +83,3 @@ public class AyeshaTanaka extends CardImpl { return new AyeshaTanaka(this); } } - -class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } - - @Override - public boolean apply(Ability input, Game game) { - if (input instanceof StackAbility) { - return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED; - } - return false; - } - - @Override - public String toString() { - return "Source(Artifact)"; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrownOuphe.java b/Mage.Sets/src/mage/cards/b/BrownOuphe.java index 1810831dd07..06bc31afbfd 100644 --- a/Mage.Sets/src/mage/cards/b/BrownOuphe.java +++ b/Mage.Sets/src/mage/cards/b/BrownOuphe.java @@ -27,6 +27,7 @@ */ package mage.cards.b; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -35,17 +36,12 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackAbility; -import mage.target.common.TargetActivatedOrTriggeredAbility; - -import java.util.UUID; +import mage.filter.FilterAbility; +import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.target.common.TargetActivatedAbility; /** * @@ -53,7 +49,7 @@ import java.util.UUID; */ public class BrownOuphe extends CardImpl { - private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); + private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source"); static { filter.add(new ArtifactSourcePredicate()); @@ -69,7 +65,7 @@ public class BrownOuphe extends CardImpl { // {1}{G}, {tap}: Counter target activated ability from an artifact source. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl<>("{1}{G}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); + ability.addTarget(new TargetActivatedAbility(filter)); this.addAbility(ability); } @@ -82,22 +78,3 @@ public class BrownOuphe extends CardImpl { return new BrownOuphe(this); } } - -class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } - - @Override - public boolean apply(Ability input, Game game) { - if (input instanceof StackAbility) { - return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED; - } - return false; - } - - @Override - public String toString() { - return "Source(Artifact)"; - } -} diff --git a/Mage.Sets/src/mage/cards/i/Interdict.java b/Mage.Sets/src/mage/cards/i/Interdict.java index 5fcc1c52446..4ae659f513c 100644 --- a/Mage.Sets/src/mage/cards/i/Interdict.java +++ b/Mage.Sets/src/mage/cards/i/Interdict.java @@ -28,6 +28,7 @@ package mage.cards.i; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; @@ -88,7 +89,7 @@ class InterdictPredicate implements Predicate { @Override public boolean apply(Ability input, Game game) { if (input instanceof StackAbility && input.getAbilityType() == AbilityType.ACTIVATED) { - Permanent sourceObject = game.getPermanentOrLKIBattlefield(input.getSourceId()); + MageObject sourceObject = input.getSourceObject(game); if (sourceObject != null) { return (sourceObject.isArtifact() || sourceObject.isEnchantment() @@ -120,9 +121,12 @@ class InterdictCounterEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); if (stackObject != null && game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game)) { - InterdictCantActivateEffect effect = new InterdictCantActivateEffect(); - effect.setTargetPointer(new FixedTarget(stackObject.getSourceId())); - game.getContinuousEffects().addEffect(effect, source); + Permanent sourcePermanent = stackObject.getStackAbility().getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null) { + InterdictCantActivateEffect effect = new InterdictCantActivateEffect(); + effect.setTargetPointer(new FixedTarget(sourcePermanent, game)); + game.getContinuousEffects().addEffect(effect, source); + } return true; } return false; @@ -143,7 +147,7 @@ class InterdictCantActivateEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { - return getTargetPointer().getFirst(game, source).equals(permanent.getId()); + return permanent.getId().equals(getTargetPointer().getFirst(game, source)); } @Override diff --git a/Mage.Sets/src/mage/cards/r/Rust.java b/Mage.Sets/src/mage/cards/r/Rust.java index 21dd121bf95..9bf2bac561a 100644 --- a/Mage.Sets/src/mage/cards/r/Rust.java +++ b/Mage.Sets/src/mage/cards/r/Rust.java @@ -28,17 +28,13 @@ package mage.cards.r; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; -import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackAbility; -import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.filter.FilterAbility; +import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.target.common.TargetActivatedAbility; /** * @@ -46,7 +42,7 @@ import mage.target.common.TargetActivatedOrTriggeredAbility; */ public class Rust extends CardImpl { - private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); + private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source"); static { filter.add(new ArtifactSourcePredicate()); @@ -57,7 +53,7 @@ public class Rust extends CardImpl { // Counter target activated ability from an artifact source. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetActivatedOrTriggeredAbility(filter)); + this.getSpellAbility().addTarget(new TargetActivatedAbility(filter)); } public Rust(final Rust card) { @@ -69,22 +65,3 @@ public class Rust extends CardImpl { return new Rust(this); } } - -class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } - - @Override - public boolean apply(Ability input, Game game) { - if (input instanceof StackAbility) { - return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED; - } - return false; - } - - @Override - public String toString() { - return "Source(Artifact)"; - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java new file mode 100644 index 00000000000..36ff6b00321 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java @@ -0,0 +1,51 @@ +/* + * 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.filter.predicate.ability; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class ArtifactSourcePredicate implements Predicate { + + @Override + public boolean apply(Ability input, Game game) { + MageObject sourceObject = input.getSourceObject(game); + return sourceObject != null && sourceObject.isArtifact(); + } + + @Override + public String toString() { + return "Source(Artifact)"; + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java index 00e65d3b11b..b107528f6e6 100644 --- a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java @@ -57,7 +57,7 @@ public class TargetActivatedAbility extends TargetObject { this.minNumberOfTargets = 1; this.maxNumberOfTargets = 1; this.zone = Zone.STACK; - this.targetName = "activated ability"; + this.targetName = filter.getMessage(); this.filter = filter; } diff --git a/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java b/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java index 8c9df2be202..3e3eeff5c21 100644 --- a/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java @@ -44,7 +44,7 @@ public class TargetActivatedOrTriggeredAbility extends TargetObject { protected final FilterStackObject filter; public TargetActivatedOrTriggeredAbility() { - this(new FilterStackObject()); + this(new FilterStackObject("activated or triggered ability")); } public TargetActivatedOrTriggeredAbility(FilterStackObject filter) { From dfb94469946385e7a17d44af775b9c8063b08027 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 10:26:11 +0100 Subject: [PATCH 064/133] * Some minor mainly fixed target changes. --- Mage.Sets/src/mage/cards/c/CoffinQueen.java | 38 +++++++++---------- .../src/mage/cards/l/LaquatussChampion.java | 7 +--- .../effects/common/DetainAllEffect.java | 30 +++++++-------- .../effects/common/RegenerateAllEffect.java | 2 +- .../abilities/keyword/PersistAbility.java | 3 -- 5 files changed, 33 insertions(+), 47 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CoffinQueen.java b/Mage.Sets/src/mage/cards/c/CoffinQueen.java index ca66fe8f4a0..c7ea4503d51 100644 --- a/Mage.Sets/src/mage/cards/c/CoffinQueen.java +++ b/Mage.Sets/src/mage/cards/c/CoffinQueen.java @@ -41,9 +41,9 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; @@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget; public class CoffinQueen extends CardImpl { public CoffinQueen(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); @@ -68,14 +68,14 @@ public class CoffinQueen extends CardImpl { // You may choose not to untap Coffin Queen during your untap step. this.addAbility(new SkipUntapOptionalAbility()); - + // {2}{B}, {tap}: Put target creature card from a graveyard onto the battlefield under your control. When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{B}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); - ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect()); + ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect()); this.addAbility(ability); - + } public CoffinQueen(final CoffinQueen card) { @@ -87,33 +87,30 @@ public class CoffinQueen extends CardImpl { return new CoffinQueen(this); } } + class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect { - + public CoffinQueenCreateDelayedTriggerEffect() { super(Outcome.Detriment); - this.staticText = "When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature"; + this.staticText = "When {this} becomes untapped or you lose control of {this}, exile that creature."; } - + public CoffinQueenCreateDelayedTriggerEffect(final CoffinQueenCreateDelayedTriggerEffect effect) { super(effect); } - + @Override public CoffinQueenCreateDelayedTriggerEffect copy() { return new CoffinQueenCreateDelayedTriggerEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Permanent controlledCreature = game.getPermanent(source.getFirstTarget()); if (controlledCreature != null) { DelayedTriggeredAbility delayedAbility = new CoffinQueenDelayedTriggeredAbility(); - delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature.getId())); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - delayedAbility.init(game); - game.addDelayedTriggeredAbility(delayedAbility); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature, game)); + game.addDelayedTriggeredAbility(delayedAbility, source); return true; } return false; @@ -123,7 +120,7 @@ class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect { class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility { CoffinQueenDelayedTriggeredAbility() { - super(new ExileTargetEffect(), Duration.EndOfGame, true); + super(new ExileTargetEffect(), Duration.EndOfGame, true); } CoffinQueenDelayedTriggeredAbility(CoffinQueenDelayedTriggeredAbility ability) { @@ -136,7 +133,6 @@ class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility { || event.getType() == EventType.UNTAPPED; } - @Override public boolean checkTrigger(GameEvent event, Game game) { if (EventType.LOST_CONTROL == event.getType() @@ -146,14 +142,14 @@ class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility { return EventType.UNTAPPED == event.getType() && event.getTargetId() != null && event.getTargetId().equals(getSourceId()); } - + @Override public CoffinQueenDelayedTriggeredAbility copy() { return new CoffinQueenDelayedTriggeredAbility(this); } - + @Override public String getRule() { - return "When {this} becomes untapped or you lose control of {this}, exile that creature"; + return "When {this} becomes untapped or you lose control of {this}, exile that creature."; } } diff --git a/Mage.Sets/src/mage/cards/l/LaquatussChampion.java b/Mage.Sets/src/mage/cards/l/LaquatussChampion.java index 0e91897a874..909d6a62314 100644 --- a/Mage.Sets/src/mage/cards/l/LaquatussChampion.java +++ b/Mage.Sets/src/mage/cards/l/LaquatussChampion.java @@ -35,7 +35,6 @@ import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.AdjustingSourceCosts; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeTargetEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.RegenerateSourceEffect; @@ -58,7 +57,7 @@ import mage.util.CardUtil; public class LaquatussChampion extends CardImpl { public LaquatussChampion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); this.subtype.add(SubType.NIGHTMARE); this.subtype.add(SubType.HORROR); @@ -126,9 +125,7 @@ class LaquatussChampionLeavesBattlefieldTriggeredAbility extends LeavesBattlefie String key = CardUtil.getCardZoneString("targetPlayer", this.getSourceId(), game, true); Object object = game.getState().getValue(key); if (object instanceof UUID) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget((UUID) object)); - } + this.getEffects().setTargetPointer(new FixedTarget((UUID) object)); return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java index 56005a87753..774304b6ea6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java @@ -24,19 +24,17 @@ * 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.abilities.effects.common; import java.util.ArrayList; import java.util.List; import java.util.UUID; - -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.filter.FilterPermanent; import mage.game.Game; @@ -48,7 +46,6 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - public class DetainAllEffect extends OneShotEffect { private FilterPermanent filter = new FilterPermanent(); @@ -56,7 +53,7 @@ public class DetainAllEffect extends OneShotEffect { public DetainAllEffect(FilterPermanent filter) { super(Outcome.Benefit); this.filter = filter; - this.staticText = new StringBuilder("detain ").append(filter.getMessage()).toString(); + this.staticText = "detain " + filter.getMessage(); } public DetainAllEffect(final DetainAllEffect effect) { @@ -73,10 +70,10 @@ public class DetainAllEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List detainedObjects = new ArrayList<>(); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers("Detained permanent: " + permanent.getName()); - FixedTarget fixedTarget = new FixedTarget(permanent.getId()); - fixedTarget.init(game, source); + } + FixedTarget fixedTarget = new FixedTarget(permanent, game); detainedObjects.add(fixedTarget); } @@ -103,23 +100,22 @@ class DetainAllRestrictionEffect extends RestrictionEffect { @Override public void init(Ability source, Game game) { super.init(source, game); - for(FixedTarget fixedTarget :this.detainedObjects) { + for (FixedTarget fixedTarget : this.detainedObjects) { Permanent permanent = game.getPermanent(fixedTarget.getFirst(game, source)); if (permanent != null) { - permanent.addInfo(new StringBuilder("detain").append(getId()).toString(),"[Detained]", game); + permanent.addInfo(new StringBuilder("detain").append(getId()).toString(), "[Detained]", game); } } } @Override public boolean isInactive(Ability source, Game game) { - if (game.getPhase().getStep().getType() == PhaseStep.UNTAP && game.getStep().getStepPart() == Step.StepPart.PRE) - { + if (game.getPhase().getStep().getType() == PhaseStep.UNTAP && game.getStep().getStepPart() == Step.StepPart.PRE) { if (game.getActivePlayerId().equals(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - for(FixedTarget fixedTarget :this.detainedObjects) { + for (FixedTarget fixedTarget : this.detainedObjects) { Permanent permanent = game.getPermanent(fixedTarget.getFirst(game, source)); if (permanent != null) { - permanent.addInfo(new StringBuilder("detain").append(getId()).toString(),"", game); + permanent.addInfo(new StringBuilder("detain").append(getId()).toString(), "", game); } } return true; @@ -130,7 +126,7 @@ class DetainAllRestrictionEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { - for(FixedTarget fixedTarget :this.detainedObjects) { + for (FixedTarget fixedTarget : this.detainedObjects) { UUID targetId = fixedTarget.getFirst(game, source); if (targetId != null && targetId.equals(permanent.getId())) { return true; @@ -148,7 +144,7 @@ class DetainAllRestrictionEffect extends RestrictionEffect { public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { return false; } - + @Override public boolean canUseActivatedAbilities(Permanent permanent, Ability source, Game game) { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java index f224789f661..2482ff947f2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java @@ -63,7 +63,7 @@ public class RegenerateAllEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { RegenerateTargetEffect regenEffect = new RegenerateTargetEffect(); - regenEffect.setTargetPointer(new FixedTarget(permanent.getId())); + regenEffect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(regenEffect, source); } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java index 085d893a495..bf4ca8188f0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java @@ -38,7 +38,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; public class PersistAbility extends DiesTriggeredAbility { @@ -66,8 +65,6 @@ public class PersistAbility extends DiesTriggeredAbility { if (super.checkTrigger(event, game)) { Permanent permanent = ((ZoneChangeEvent) event).getTarget(); if (permanent.getCounters(game).getCount(CounterType.M1M1) == 0) { - FixedTarget fixedTarget = new FixedTarget(permanent.getId()); - fixedTarget.init(game, this); return true; } } From 3d372e7100fce8ba9db50b75067ead72478843af Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 12:23:50 +0100 Subject: [PATCH 065/133] * Fixed bug of Defiant Vanguard not destroying aby creatures from blocking. Added test. --- .../src/mage/cards/d/DefiantVanguard.java | 4 +- .../cards/triggers/DefiantVanguardTest.java | 84 +++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java diff --git a/Mage.Sets/src/mage/cards/d/DefiantVanguard.java b/Mage.Sets/src/mage/cards/d/DefiantVanguard.java index 3b28b8d6dc4..0a5c3decb5f 100644 --- a/Mage.Sets/src/mage/cards/d/DefiantVanguard.java +++ b/Mage.Sets/src/mage/cards/d/DefiantVanguard.java @@ -124,7 +124,7 @@ class DefiantVanguardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent thisCreature = game.getPermanentOrLKIBattlefield(source.getId()); + Permanent thisCreature = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (controller != null && thisCreature != null) { BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName()); if (watcher != null) { @@ -136,10 +136,10 @@ class DefiantVanguardEffect extends OneShotEffect { } } } + thisCreature.destroy(source.getSourceId(), game, false); for (Permanent creature : toDestroy) { creature.destroy(source.getSourceId(), game, false); } - thisCreature.destroy(source.getSourceId(), game, false); return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java new file mode 100644 index 00000000000..dc4b99e66fc --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java @@ -0,0 +1,84 @@ +/* + * 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 org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DefiantVanguardTest extends CardTestPlayerBase { + + @Test + public void testAllDestroyed() { + // When Defiant Vanguard blocks, at end of combat, destroy it and all creatures it blocked this turn. + // {5}, {tap}: Search your library for a Rebel permanent card with converted mana cost 4 or less and put it onto the battlefield. Then shuffle your library. + addCard(Zone.BATTLEFIELD, playerA, "Defiant Vanguard", 1); // Creature {2}{W} 2/2 + + addCard(Zone.BATTLEFIELD, playerB, "Bane Alley Blackguard", 1); // Creature {1}{B} 1/3 + + attack(2, playerB, "Bane Alley Blackguard"); + block(2, playerA, "Defiant Vanguard", "Bane Alley Blackguard"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Defiant Vanguard", 1); + assertGraveyardCount(playerB, "Bane Alley Blackguard", 1); + } + + @Test + public void testSaveCreatureWithCloudshift() { + // When Defiant Vanguard blocks, at end of combat, destroy it and all creatures it blocked this turn. + // {5}, {tap}: Search your library for a Rebel permanent card with converted mana cost 4 or less and put it onto the battlefield. Then shuffle your library. + addCard(Zone.BATTLEFIELD, playerA, "Defiant Vanguard", 1); // Creature {2}{W} 2/2 + + addCard(Zone.BATTLEFIELD, playerB, "Bane Alley Blackguard", 1); // Creature {1}{B} 1/3 + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerB, "Cloudshift", 1); // Instant {W} + + attack(2, playerB, "Bane Alley Blackguard"); + block(2, playerA, "Defiant Vanguard", "Bane Alley Blackguard"); + + castSpell(2, PhaseStep.END_COMBAT, playerB, "Cloudshift", "Bane Alley Blackguard", "At end of combat, destroy it and all creatures it blocked this turn."); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Defiant Vanguard", 1); + assertGraveyardCount(playerB, "Cloudshift", 1); + assertPermanentCount(playerB, "Bane Alley Blackguard", 1); + + } + +} From 735a7668a1f2ff7d6d603ed1ae7ff096d4ce76f2 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:26:32 +0000 Subject: [PATCH 066/133] Implemented Honorable Passage --- .../src/mage/cards/h/HonorablePassage.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HonorablePassage.java diff --git a/Mage.Sets/src/mage/cards/h/HonorablePassage.java b/Mage.Sets/src/mage/cards/h/HonorablePassage.java new file mode 100644 index 00000000000..585d6256e09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HonorablePassage.java @@ -0,0 +1,108 @@ +/* + * 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.h; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author L_J + */ +public class HonorablePassage extends CardImpl { + + public HonorablePassage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + + // The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, Honorable Passage deals that much damage to the source's controller. + this.getSpellAbility().addEffect(new HonorablePassageEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + } + + public HonorablePassage(final HonorablePassage card) { + super(card); + } + + @Override + public HonorablePassage copy() { + return new HonorablePassage(this); + } +} + +class HonorablePassageEffect extends PreventNextDamageFromChosenSourceToTargetEffect { + + public HonorablePassageEffect() { + super(Duration.EndOfTurn); + } + + public HonorablePassageEffect(final HonorablePassageEffect effect) { + super(effect); + } + + @Override + public HonorablePassageEffect copy() { + return new HonorablePassageEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + PreventionEffectData preventEffectData = preventDamageAction(event, source, game); + if (preventEffectData.getPreventedDamage() > 0) { + MageObject sourceObject = game.getObject(event.getSourceId()); + if (sourceObject != null && sourceObject.getColor(game).isRed()) { + UUID sourceControllerId = game.getControllerId(event.getSourceId()); + if (sourceControllerId != null) { + Player sourceController = game.getPlayer(sourceControllerId); + if (sourceController != null) { + sourceController.damage(damage, source.getSourceId(), game, false, true); + } + } + } + this.used = true; + } + return false; + } + + @Override + public String getText(Mode mode) { + return "The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, {this} deals that much damage to the source's controller"; + } +} From 41e156ed320d2d90af651ca9dfd63e3bc9f0cf6a Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:27:44 +0000 Subject: [PATCH 067/133] Implemented Wind Shear --- Mage.Sets/src/mage/cards/w/WindShear.java | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WindShear.java diff --git a/Mage.Sets/src/mage/cards/w/WindShear.java b/Mage.Sets/src/mage/cards/w/WindShear.java new file mode 100644 index 00000000000..4d70f93c00f --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WindShear.java @@ -0,0 +1,69 @@ +/* + * 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.w; + +import java.util.UUID; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AbilityPredicate; + +/** + * + * @author L_J + */ +public class WindShear extends CardImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public WindShear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); + + // Attacking creatures with flying get -2/-2 and lose flying until end of turn. + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn, filter, false).setText("Attacking creatures with flying get -2/-2")); + this.getSpellAbility().addEffect(new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filter).setText("and lose flying until end of turn")); + } + + public WindShear(final WindShear card) { + super(card); + } + + @Override + public WindShear copy() { + return new WindShear(this); + } +} From 3c2acc407c3651c9d92c1ece1e1cb831b6eea661 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:28:49 +0000 Subject: [PATCH 068/133] Implemented Foreshadow --- Mage.Sets/src/mage/cards/f/Foreshadow.java | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/f/Foreshadow.java diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java new file mode 100644 index 00000000000..54db6503045 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java @@ -0,0 +1,110 @@ +/* + * 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.f; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author Quercitron & L_J + */ +public class Foreshadow extends CardImpl { + + public Foreshadow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + + // Choose a card name, then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card. + this.getSpellAbility().addEffect(new NameACardEffect(NameACardEffect.TypeOfName.ALL)); + this.getSpellAbility().addEffect(new ForeshadowEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Draw a card at the beginning of the next turn's upkeep. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); + } + + public Foreshadow(final Foreshadow card) { + super(card); + } + + @Override + public Foreshadow copy() { + return new Foreshadow(this); + } +} + +class ForeshadowEffect extends OneShotEffect { + + public ForeshadowEffect() { + super(Outcome.DrawCard); + this.staticText = ", then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card"; + } + + public ForeshadowEffect(final ForeshadowEffect effect) { + super(effect); + } + + @Override + public ForeshadowEffect copy() { + return new ForeshadowEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) { + Card card = targetPlayer.getLibrary().getFromTop(game); + if (card != null) { + controller.moveCards(card, Zone.GRAVEYARD, source, game); + if (card.getName().equals(cardName)) { + controller.drawCards(1, game); + } + } + return true; + } + return false; + } + +} From 22a49caf56dd8de4c1d3cfb45ed02107debfb841 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:29:43 +0000 Subject: [PATCH 069/133] Implemented Honorable Passage --- Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java index 119cb767c9c..d124325bccc 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java +++ b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java @@ -96,6 +96,7 @@ public class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); cards.add(new SetCardInfo("Grinning Totem", 110, Rarity.SPECIAL, mage.cards.g.GrinningTotem.class)); cards.add(new SetCardInfo("Hail Storm", 79, Rarity.SPECIAL, mage.cards.h.HailStorm.class)); + cards.add(new SetCardInfo("Honorable Passage", 9, Rarity.SPECIAL, mage.cards.h.HonorablePassage.class)); cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.COMMON, mage.cards.h.HuntingMoa.class)); cards.add(new SetCardInfo("Icatian Javelineers", 10, Rarity.SPECIAL, IcatianJavelineers.class)); cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.COMMON, mage.cards.j.JasmineBoreal.class)); From fa783708feee4724061824d3f1c6daf7641996df Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:30:16 +0000 Subject: [PATCH 070/133] Implemented cards --- Mage.Sets/src/mage/sets/Visions.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/Visions.java b/Mage.Sets/src/mage/sets/Visions.java index b537d3b33dd..e1458607a94 100644 --- a/Mage.Sets/src/mage/sets/Visions.java +++ b/Mage.Sets/src/mage/sets/Visions.java @@ -63,7 +63,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Brass-Talon Chimera", 142, Rarity.UNCOMMON, mage.cards.b.BrassTalonChimera.class)); cards.add(new SetCardInfo("Breathstealer's Crypt", 127, Rarity.RARE, mage.cards.b.BreathstealersCrypt.class)); cards.add(new SetCardInfo("Breezekeeper", 27, Rarity.COMMON, mage.cards.b.Breezekeeper.class)); - cards.add(new SetCardInfo("Brood of Cockroaches", 3, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); + cards.add(new SetCardInfo("Brood of Cockroaches", 3, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); cards.add(new SetCardInfo("Bull Elephant", 51, Rarity.COMMON, mage.cards.b.BullElephant.class)); cards.add(new SetCardInfo("Chronatog", 28, Rarity.RARE, mage.cards.c.Chronatog.class)); cards.add(new SetCardInfo("City of Solitude", 52, Rarity.RARE, mage.cards.c.CityOfSolitude.class)); @@ -96,6 +96,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Fireblast", 79, Rarity.COMMON, mage.cards.f.Fireblast.class)); cards.add(new SetCardInfo("Firestorm Hellkite", 130, Rarity.RARE, mage.cards.f.FirestormHellkite.class)); cards.add(new SetCardInfo("Flooded Shoreline", 32, Rarity.RARE, mage.cards.f.FloodedShoreline.class)); + cards.add(new SetCardInfo("Foreshadow", 33, Rarity.UNCOMMON, mage.cards.f.Foreshadow.class)); cards.add(new SetCardInfo("Freewind Falcon", 105, Rarity.COMMON, mage.cards.f.FreewindFalcon.class)); cards.add(new SetCardInfo("Funeral Charm", 11, Rarity.COMMON, mage.cards.f.FuneralCharm.class)); cards.add(new SetCardInfo("Giant Caterpillar", 58, Rarity.COMMON, mage.cards.g.GiantCaterpillar.class)); @@ -105,6 +106,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Griffin Canyon", 163, Rarity.RARE, mage.cards.g.GriffinCanyon.class)); cards.add(new SetCardInfo("Hearth Charm", 82, Rarity.COMMON, mage.cards.h.HearthCharm.class)); cards.add(new SetCardInfo("Helm of Awakening", 145, Rarity.UNCOMMON, mage.cards.h.HelmOfAwakening.class)); + cards.add(new SetCardInfo("Honorable Passage", 107, Rarity.UNCOMMON, mage.cards.h.HonorablePassage.class)); cards.add(new SetCardInfo("Hope Charm", 108, Rarity.COMMON, mage.cards.h.HopeCharm.class)); cards.add(new SetCardInfo("Hulking Cyclops", 84, Rarity.UNCOMMON, mage.cards.h.HulkingCyclops.class)); cards.add(new SetCardInfo("Impulse", 34, Rarity.COMMON, mage.cards.i.Impulse.class)); @@ -199,6 +201,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Warthog", 74, Rarity.COMMON, mage.cards.w.Warthog.class)); cards.add(new SetCardInfo("Waterspout Djinn", 50, Rarity.UNCOMMON, mage.cards.w.WaterspoutDjinn.class)); cards.add(new SetCardInfo("Wicked Reward", 25, Rarity.COMMON, mage.cards.w.WickedReward.class)); + cards.add(new SetCardInfo("Wind Shear", 75, Rarity.UNCOMMON, mage.cards.w.WindShear.class)); cards.add(new SetCardInfo("Zhalfirin Crusader", 125, Rarity.RARE, mage.cards.z.ZhalfirinCrusader.class)); } } From 4f17ea18c38316a436e46e216732704ece915d7b Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 13:47:54 +0000 Subject: [PATCH 071/133] Implemented Beacon of Destiny --- .../src/mage/cards/b/BeaconOfDestiny.java | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java diff --git a/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java b/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java new file mode 100644 index 00000000000..b93b478839b --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java @@ -0,0 +1,132 @@ +/* + * 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.b; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.RedirectionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.TargetSource; + +/** + * + * @author L_J + */ +public class BeaconOfDestiny extends CardImpl { + + public BeaconOfDestiny(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}: The next time a source of your choice would deal damage to you this turn, that damage is dealt to Beacon of Destiny instead. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BeaconOfDestinyEffect(), new TapSourceCost())); + } + + public BeaconOfDestiny(final BeaconOfDestiny card) { + super(card); + } + + @Override + public BeaconOfDestiny copy() { + return new BeaconOfDestiny(this); + } +} + +class BeaconOfDestinyEffect extends RedirectionEffect { + + private final TargetSource damageSource; + + public BeaconOfDestinyEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, true); + staticText = "The next time a source of your choice would deal damage to you this turn, that damage is dealt to {this} instead"; + this.damageSource = new TargetSource(); + } + + public BeaconOfDestinyEffect(final BeaconOfDestinyEffect effect) { + super(effect); + this.damageSource = effect.damageSource.copy(); + } + + @Override + public BeaconOfDestinyEffect copy() { + return new BeaconOfDestinyEffect(this); + } + + @Override + public void init(Ability source, Game game) { + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // check source + MageObject object = game.getObject(event.getSourceId()); + if (object == null) { + game.informPlayers("Couldn't find source of damage"); + return false; + } + + if (!object.getId().equals(damageSource.getFirstTarget()) + && (!(object instanceof Spell) || !((Spell) object).getSourceId().equals(damageSource.getFirstTarget()))) { + return false; + } + TargetPermanent target = new TargetPermanent(); + target.add(source.getSourceId(), game); + this.redirectTarget = target; + + // check player + Player player = game.getPlayer(event.getTargetId()); + if (player != null) { + if (player.getId().equals(source.getControllerId())) { + return true; + } + } + return false; + } + +} From e00a43f3f075f0cc4b725ac4cd765e1e4ab4e6f2 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 13:49:15 +0000 Subject: [PATCH 072/133] Implemented Mistform Wakecaster --- .../src/mage/cards/m/MistformWakecaster.java | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MistformWakecaster.java diff --git a/Mage.Sets/src/mage/cards/m/MistformWakecaster.java b/Mage.Sets/src/mage/cards/m/MistformWakecaster.java new file mode 100644 index 00000000000..2bf1bfe4abb --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MistformWakecaster.java @@ -0,0 +1,137 @@ +/* + * 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.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeSourceEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureTypeTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceCreatureType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public class MistformWakecaster extends CardImpl { + + public MistformWakecaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {1}: Mistform Wakecaster becomes the creature type of your choice until end of turn. + this.addAbility(new SimpleActivatedAbility(new BecomesChosenCreatureTypeSourceEffect(), new ManaCostsImpl("{1}"))); + + // {2}{U}{U}, {T}: Choose a creature type. Each creature you control becomes that type until end of turn. + Ability ability = new SimpleActivatedAbility(new BecomesChosenCreatureTypeControlledEffect(), new ManaCostsImpl("{2}{U}{U}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public MistformWakecaster(final MistformWakecaster card) { + super(card); + } + + @Override + public MistformWakecaster copy() { + return new MistformWakecaster(this); + } +} + +class BecomesChosenCreatureTypeControlledEffect extends OneShotEffect { + + public BecomesChosenCreatureTypeControlledEffect() { + super(Outcome.BoostCreature); + staticText = "Choose a creature type. Each creature you control becomes that type until end of turn"; + } + + public BecomesChosenCreatureTypeControlledEffect(final BecomesChosenCreatureTypeControlledEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getSourceId()); + String chosenType = ""; + if (player != null && card != null) { + Choice typeChoice = new ChoiceCreatureType(); + String msg = "Choose a creature type"; + typeChoice.setMessage(msg); + while (!player.choose(Outcome.BoostCreature, typeChoice, game)) { + if (!player.canRespond()) { + return false; + } + } + game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice()); + chosenType = typeChoice.getChoice(); + if (chosenType != null && !chosenType.isEmpty()) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game)) { + ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(Duration.EndOfTurn, SubType.byDescription(chosenType)); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + } + return true; + } + + } + return false; + } + + @Override + public Effect copy() { + return new BecomesChosenCreatureTypeControlledEffect(this); + } + +} From d3b4bd8d3263ddda1198d29c7a373bc7a80b9814 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 13:50:42 +0000 Subject: [PATCH 073/133] Implemented Whipgrass Entangler --- .../src/mage/cards/w/WhipgrassEntangler.java | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WhipgrassEntangler.java diff --git a/Mage.Sets/src/mage/cards/w/WhipgrassEntangler.java b/Mage.Sets/src/mage/cards/w/WhipgrassEntangler.java new file mode 100644 index 00000000000..d026b9cd903 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhipgrassEntangler.java @@ -0,0 +1,125 @@ +/* + * 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.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessPaysSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author emerald000 & L_J + */ +public class WhipgrassEntangler extends CardImpl { + + public WhipgrassEntangler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {1}{W}: Until end of turn, target creature gains "This creature can't attack or block unless its controller pays {1} for each Cleric on the battlefield." + Ability abilityToGain = new SimpleStaticAbility(Zone.BATTLEFIELD, new WhipgrassEntanglerCantAttackUnlessYouPayEffect()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new GainAbilityTargetEffect(abilityToGain, Duration.EndOfTurn).setText("Until end of turn, target creature gains \"This creature can't attack or block unless its controller pays {1} for each Cleric on the battlefield.\""), + new ManaCostsImpl("{1}{W}")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + } + + public WhipgrassEntangler(final WhipgrassEntangler card) { + super(card); + } + + @Override + public WhipgrassEntangler copy() { + return new WhipgrassEntangler(this); + } +} + +class WhipgrassEntanglerCantAttackUnlessYouPayEffect extends CantAttackBlockUnlessPaysSourceEffect { + + private static final FilterPermanent filter = new FilterPermanent("Cleric on the battlefield"); + + static { + filter.add(new SubtypePredicate(SubType.CLERIC)); + } + + WhipgrassEntanglerCantAttackUnlessYouPayEffect() { + super(new ManaCostsImpl("{0}"), RestrictType.ATTACK_AND_BLOCK); + staticText = "This creature can't attack or block unless its controller pays {1} for each Cleric on the battlefield"; + } + + WhipgrassEntanglerCantAttackUnlessYouPayEffect(WhipgrassEntanglerCantAttackUnlessYouPayEffect effect) { + super(effect); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getSourceId().equals(event.getSourceId()); + } + + @Override + public ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game) { + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (sourceObject != null) { + int payment = new PermanentsOnBattlefieldCount(filter).calculate(game, source, this); + if (payment > 0) { + return new ManaCostsImpl<>("{" + payment + '}'); + } + } + return null; + } + + @Override + public WhipgrassEntanglerCantAttackUnlessYouPayEffect copy() { + return new WhipgrassEntanglerCantAttackUnlessYouPayEffect(this); + } + +} From 459b81c9677a64179e9c782b31aef3239e2ff293 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 13:52:07 +0000 Subject: [PATCH 074/133] Implemented cards --- Mage.Sets/src/mage/sets/Legions.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Legions.java b/Mage.Sets/src/mage/sets/Legions.java index 2622f22cb0a..7dc39c620b7 100644 --- a/Mage.Sets/src/mage/sets/Legions.java +++ b/Mage.Sets/src/mage/sets/Legions.java @@ -61,6 +61,7 @@ public class Legions extends ExpansionSet { cards.add(new SetCardInfo("Aven Redeemer", 3, Rarity.COMMON, mage.cards.a.AvenRedeemer.class)); cards.add(new SetCardInfo("Aven Warhawk", 4, Rarity.UNCOMMON, mage.cards.a.AvenWarhawk.class)); cards.add(new SetCardInfo("Bane of the Living", 60, Rarity.RARE, mage.cards.b.BaneOfTheLiving.class)); + cards.add(new SetCardInfo("Beacon of Destiny", 5, Rarity.RARE, mage.cards.b.BeaconOfDestiny.class)); cards.add(new SetCardInfo("Berserk Murlodont", 117, Rarity.COMMON, mage.cards.b.BerserkMurlodont.class)); cards.add(new SetCardInfo("Blade Sliver", 88, Rarity.UNCOMMON, mage.cards.b.BladeSliver.class)); cards.add(new SetCardInfo("Blood Celebrant", 61, Rarity.COMMON, mage.cards.b.BloodCelebrant.class)); @@ -141,6 +142,7 @@ public class Legions extends ExpansionSet { cards.add(new SetCardInfo("Mistform Seaswift", 45, Rarity.COMMON, mage.cards.m.MistformSeaswift.class)); cards.add(new SetCardInfo("Mistform Sliver", 46, Rarity.COMMON, mage.cards.m.MistformSliver.class)); cards.add(new SetCardInfo("Mistform Ultimus", 47, Rarity.RARE, mage.cards.m.MistformUltimus.class)); + cards.add(new SetCardInfo("Mistform Wakecaster", 48, Rarity.UNCOMMON, mage.cards.m.MistformWakecaster.class)); cards.add(new SetCardInfo("Nantuko Vigilante", 132, Rarity.COMMON, mage.cards.n.NantukoVigilante.class)); cards.add(new SetCardInfo("Needleshot Gourna", 133, Rarity.COMMON, mage.cards.n.NeedleshotGourna.class)); cards.add(new SetCardInfo("Noxious Ghoul", 77, Rarity.UNCOMMON, mage.cards.n.NoxiousGhoul.class)); @@ -188,6 +190,7 @@ public class Legions extends ExpansionSet { cards.add(new SetCardInfo("Ward Sliver", 25, Rarity.UNCOMMON, mage.cards.w.WardSliver.class)); cards.add(new SetCardInfo("Warped Researcher", 56, Rarity.UNCOMMON, mage.cards.w.WarpedResearcher.class)); cards.add(new SetCardInfo("Weaver of Lies", 57, Rarity.RARE, mage.cards.w.WeaverOfLies.class)); + cards.add(new SetCardInfo("Whipgrass Entangler", 26, Rarity.COMMON, mage.cards.w.WhipgrassEntangler.class)); cards.add(new SetCardInfo("White Knight", 27, Rarity.UNCOMMON, mage.cards.w.WhiteKnight.class)); cards.add(new SetCardInfo("Willbender", 58, Rarity.UNCOMMON, mage.cards.w.Willbender.class)); cards.add(new SetCardInfo("Windborn Muse", 28, Rarity.RARE, mage.cards.w.WindbornMuse.class)); From 8238139192ec43ecbc28dfe73fb254e22f8e400f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 24 Feb 2018 19:52:25 +0400 Subject: [PATCH 075/133] * UI: added SVG icons support on x64 linux systems (see #4421, must cleanup xmage\mage-client\lib folder); --- Mage.Client/pom.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index c37a0646dee..db453f4cd3b 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -147,14 +147,9 @@ https://stackoverflow.com/questions/714243/sax2-driver-class-org-apache-crimson-parser-xmlreaderimpl-not-found-when-using --> - batik - batik-transcoder - 1.6-1 - - - crimson - crimson - 1.1.3 + org.apache.xmlgraphics + batik-transcoder + 1.7 From 800daff3043d30411cbf152fae5f3e1e5a875a44 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 17:10:13 +0100 Subject: [PATCH 076/133] * Fixed Vodalian War Machine watcher handling related to MageObjectReferences. --- .../src/mage/cards/v/VodalianWarMachine.java | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 75c3b50aab7..68cbfa766db 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -30,6 +30,7 @@ package mage.cards.v; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import mage.MageInt; @@ -39,7 +40,6 @@ import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -47,9 +47,9 @@ import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.WatcherScope; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; @@ -79,7 +79,7 @@ public class VodalianWarMachine extends CardImpl { } public VodalianWarMachine(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.WALL); this.power = new MageInt(0); this.toughness = new MageInt(4); @@ -90,10 +90,10 @@ public class VodalianWarMachine extends CardImpl { // Tap an untapped Merfolk you control: Vodalian War Machine can attack this turn as though it didn't have defender. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); this.addAbility(ability); - + // Tap an untapped Merfolk you control: Vodalian War Machine gets +2/+1 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); - + // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. this.addAbility(new VodalianWarMachineTriggeredAbility(), new VodalianWarMachineWatcher()); } @@ -136,13 +136,6 @@ class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { return false; } } - for (Effect effect : getEffects()) { - effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); - if (effect instanceof VodalianWarMachineEffect) { - VodalianWarMachineEffect effectToSet = (VodalianWarMachineEffect) effect; - effectToSet.counter = before.getZoneChangeCounter(game); - } - } return true; } return false; @@ -151,8 +144,6 @@ class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { } class VodalianWarMachineEffect extends OneShotEffect { - - protected int counter; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); @@ -167,7 +158,6 @@ class VodalianWarMachineEffect extends OneShotEffect { public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { super(effect); - counter = effect.counter; } @Override @@ -180,9 +170,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent, game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent, game).contains(new MageObjectReference(permanent, game))) { permanent.destroy(source.getSourceId(), game, false); } } @@ -196,7 +186,7 @@ class VodalianWarMachineEffect extends OneShotEffect { class VodalianWarMachineWatcher extends Watcher { - public Map> tappedMerfolkIds = new HashMap<>(); + public Map> tappedMerfolkIds = new HashMap<>(); public VodalianWarMachineWatcher() { super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); @@ -204,7 +194,10 @@ class VodalianWarMachineWatcher extends Watcher { public VodalianWarMachineWatcher(final VodalianWarMachineWatcher watcher) { super(watcher); - this.tappedMerfolkIds = watcher.tappedMerfolkIds; + // We have for sure to use copied collections, but there is no need to copy the MageObjectReference objects. + for (Entry> entry : watcher.tappedMerfolkIds.entrySet()) { + this.tappedMerfolkIds.put(entry.getKey(), new HashSet<>(entry.getValue())); + } } @Override @@ -212,9 +205,8 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId, int counter, Game game) { - MageObjectReference mor = new MageObjectReference(sourceId, counter, game); - return tappedMerfolkIds.get(mor); + public Set getTappedMerfolkIds(Permanent permanent, Game game) { + return tappedMerfolkIds.get(new MageObjectReference(permanent, game)); } @Override @@ -232,14 +224,14 @@ class VodalianWarMachineWatcher extends Watcher { TapTargetCost tapCost = (TapTargetCost) cost; if (tapCost.getTarget().isChosen()) { MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game), game); - Set toAdd; + Set toAdd; if (tappedMerfolkIds.get(mor) == null) { toAdd = new HashSet<>(); } else { toAdd = tappedMerfolkIds.get(mor); } for (UUID targetId : tapCost.getTarget().getTargets()) { - toAdd.add(targetId); + toAdd.add(new MageObjectReference(targetId, game)); } tappedMerfolkIds.put(mor, toAdd); break; From 309d4685e8771f0fd3ae3fbe830ee7cc9fdf5e4d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 17:28:55 +0100 Subject: [PATCH 077/133] * Cleaned up FixedTargets MageObjectReference handling. --- Mage/src/main/java/mage/MageObjectReference.java | 6 +----- .../mage/target/targetpointer/FixedTargets.java | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index 0c4cde8808f..9fd237f7c87 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -47,7 +47,7 @@ public class MageObjectReference implements Comparable, Ser private static final Logger logger = Logger.getLogger(MageObjectReference.class); private final UUID sourceId; - private int zoneChangeCounter; + private final int zoneChangeCounter; public MageObjectReference(MageObject mageObject, Game game) { this.sourceId = mageObject.getId(); @@ -167,8 +167,4 @@ public class MageObjectReference implements Comparable, Ser } return null; } - - public void setZoneChangeCounter(int zoneChangeCounter) { - this.zoneChangeCounter = zoneChangeCounter; - } } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index d5c099526b2..e220491074e 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -21,10 +21,12 @@ import mage.game.permanent.Permanent; public class FixedTargets implements TargetPointer { final ArrayList targets = new ArrayList<>(); + final ArrayList targetsNotInitialized = new ArrayList<>(); + private boolean initialized; public FixedTargets(UUID targetId) { - targets.add(new MageObjectReference(targetId)); + targetsNotInitialized.add(targetId); this.initialized = false; } @@ -46,6 +48,7 @@ public class FixedTargets implements TargetPointer { private FixedTargets(final FixedTargets fixedTargets) { this.targets.addAll(fixedTargets.targets); + this.targetsNotInitialized.addAll(fixedTargets.targetsNotInitialized); this.initialized = fixedTargets.initialized; } @@ -53,8 +56,8 @@ public class FixedTargets implements TargetPointer { public void init(Game game, Ability source) { if (!initialized) { initialized = true; - for (MageObjectReference mor : targets) { - mor.setZoneChangeCounter(game.getState().getZoneChangeCounter(mor.getSourceId())); + for (UUID targetId : targetsNotInitialized) { + targets.add(new MageObjectReference(targetId, game.getState().getZoneChangeCounter(targetId), game)); } } } @@ -87,6 +90,13 @@ public class FixedTargets implements TargetPointer { return new FixedTargets(this); } + /** + * Returns a fixed target for (and only) the first taget + * + * @param game + * @param source + * @return + */ @Override public FixedTarget getFixedTarget(Game game, Ability source) { this.init(game, source); From 4b89342c345ac274415af5409c6fd46b38373679 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 21:52:05 +0000 Subject: [PATCH 078/133] Added support for Blaze of Glory --- .../main/java/mage/game/combat/Combat.java | 102 ++++++++++++++++-- 1 file changed, 91 insertions(+), 11 deletions(-) diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 97463088660..52b77917ccb 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -750,6 +750,78 @@ public class Combat implements Serializable, Copyable { potentialBlockers.add(creature.getId()); } } + // check the mustBlockAllAttackers requirement for creatures already blocking (Blaze of Glory) ------------------------------- + if (effect.mustBlockAllAttackers(game)) { + // find all the attackers that the creature can block (and no restictions prevent this) + Set attackersToBlock = new HashSet<>(); + boolean mayBlock = false; + for (UUID attackingCreatureId : getAttackers()) { + if (creature.canBlock(attackingCreatureId, game)) { + Permanent attackingCreature = game.getPermanent(attackingCreatureId); + if (attackingCreature != null) { + // check if the attacker is already blocked by a max of blockers, so blocker can't block it also + if (attackingCreature.getMaxBlockedBy() != 0) { // 0 = no restriction about the number of possible blockers + int alreadyBlockingCreatures = 0; + for (CombatGroup group : getGroups()) { + if (group.getAttackers().contains(attackingCreatureId)) { + alreadyBlockingCreatures = group.getBlockers().size(); + break; + } + } + if (attackingCreature.getMaxBlockedBy() <= alreadyBlockingCreatures) { + continue; // Attacker can't be blocked by more blockers so check next attacker + } + } + // check restrictions of the creature to block that prevent it can be blocked (note L_J: not sure what this refers to...) + + // check if enough possible blockers are available, if true, mayBlock can be set to true + if (attackingCreature.getMinBlockedBy() > 1) { + int alreadyBlockingCreatures = 0; + for (CombatGroup group : getGroups()) { + if (group.getAttackers().contains(attackingCreatureId)) { + alreadyBlockingCreatures = group.getBlockers().size(); + break; + } + } + if (attackingCreature.getMinBlockedBy() >= alreadyBlockingCreatures) { + continue; // Attacker can't be blocked by the current blocker amount so check next attacker + } + } else { + attackersToBlock.add(attackingCreatureId); + } + } + } + } + if (!attackersToBlock.isEmpty()) { + for (UUID attackerId : attackersToBlock) { + if (!findGroup(attackerId).getBlockers().contains(creature.getId())) { + mayBlock = true; + break; + } + } + } + // if creature can block more attackers, inform human player or set blocks for AI player + if (mayBlock) { + if (controller.isHuman()) { + if (!game.isSimulation()) { + game.informPlayer(controller, "Creature should block all attackers it's able to this turn: " + creature.getIdName()); + } + } else { + Player defender = game.getPlayer(creature.getControllerId()); + if (defender != null) { + for (UUID attackingCreatureId : getAttackers()) { + if (creature.canBlock(attackingCreatureId, game) + && !findGroup(attackingCreatureId).getBlockers().contains(creature.getId()) + && attackersToBlock.contains(attackingCreatureId)) { + // TODO: might need to revisit this (calls some pickBlockerOrder instances even for a single blocker - damage distribution appears to be working correctly however) + defender.declareBlocker(defender.getId(), creature.getId(), attackingCreatureId, game); + } + } + } + } + return false; + } + } } } @@ -773,10 +845,9 @@ public class Combat implements Serializable, Copyable { } } - // check the mustBlockAny requirement ---------------------------------------- - if (effect.mustBlockAny(game)) { - // check that it can block at least one of the attackers - // and no restictions prevent this + // check the mustBlockAny requirement (and mustBlockAllAttackers for not blocking creatures) ---------------------------------------- + if (effect.mustBlockAny(game) || effect.mustBlockAllAttackers(game)) { + // check that it can block at least one of the attackers and no restictions prevent this boolean mayBlock = false; for (UUID attackingCreatureId : getAttackers()) { if (creature.canBlock(attackingCreatureId, game)) { @@ -792,15 +863,23 @@ public class Combat implements Serializable, Copyable { } } if (attackingCreature.getMaxBlockedBy() <= alreadyBlockingCreatures) { - // Attacker can't be blocked by more blockers so check next attacker - continue; + continue; // Attacker can't be blocked by more blockers so check next attacker } } - // check restrictions of the creature to block that prevent it can be blocked + // check restrictions of the creature to block that prevent it can be blocked (note L_J: not sure what this refers to...) + // check if enough possible blockers are available, if true, mayBlock can be set to true if (attackingCreature.getMinBlockedBy() > 1) { - // TODO: check if enough possible blockers are available, if true, mayBlock can be set to true - + int alreadyBlockingCreatures = 0; + for (CombatGroup group : getGroups()) { + if (group.getAttackers().contains(attackingCreatureId)) { + alreadyBlockingCreatures = group.getBlockers().size(); + break; + } + } + if (attackingCreature.getMinBlockedBy() >= alreadyBlockingCreatures) { + continue; // Attacker can't be blocked by the current blocker amount so check next attacker + } } else { mayBlock = true; break; @@ -808,7 +887,7 @@ public class Combat implements Serializable, Copyable { } } } - // if so inform human player or set block for AI player + // if creature can block, inform human player or set block for AI player if (mayBlock) { if (controller.isHuman()) { if (!game.isSimulation()) { @@ -818,7 +897,8 @@ public class Combat implements Serializable, Copyable { Player defender = game.getPlayer(creature.getControllerId()); if (defender != null) { for (UUID attackingCreatureId : getAttackers()) { - if (creature.canBlock(attackingCreatureId, game)) { + if (creature.canBlock(attackingCreatureId, game) + && !findGroup(attackingCreatureId).getBlockers().contains(creature.getId())) { defender.declareBlocker(defender.getId(), creature.getId(), attackingCreatureId, game); break; } From a204630180f186016ad1062ab6f87e9d0bc601c5 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:00:16 +0000 Subject: [PATCH 079/133] Implemented Blaze of Glory --- Mage.Sets/src/mage/cards/b/BlazeOfGlory.java | 127 +++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BlazeOfGlory.java diff --git a/Mage.Sets/src/mage/cards/b/BlazeOfGlory.java b/Mage.Sets/src/mage/cards/b/BlazeOfGlory.java new file mode 100644 index 00000000000..c8e22ca5631 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlazeOfGlory.java @@ -0,0 +1,127 @@ +/* + * 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.b; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.BeforeBlockersAreDeclaredCondition; +import mage.abilities.effects.RequirementEffect; +import mage.abilities.effects.common.combat.CanBlockAdditionalCreatureTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TurnPhase; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectPlayer; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class BlazeOfGlory extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(new BlazeOfGloryDefendingPlayerControlsPredicate()); + } + + public BlazeOfGlory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Cast Blaze of Glory only during combat before blockers are declared. + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(TurnPhase.COMBAT, BeforeBlockersAreDeclaredCondition.instance)); + + // Target creature defending player controls can block any number of creatures this turn. It blocks each attacking creature this turn if able. + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addEffect(new CanBlockAdditionalCreatureTargetEffect(Duration.EndOfTurn, 0)); + this.getSpellAbility().addEffect(new BlazeOfGloryRequirementEffect()); + } + + public BlazeOfGlory(final BlazeOfGlory card) { + super(card); + } + + @Override + public BlazeOfGlory copy() { + return new BlazeOfGlory(this); + } +} + +class BlazeOfGloryDefendingPlayerControlsPredicate implements ObjectPlayerPredicate> { + + @Override + public boolean apply(ObjectPlayer input, Game game) { + return game.getCombat().getPlayerDefenders(game).contains(input.getObject().getControllerId()); + } +} + +class BlazeOfGloryRequirementEffect extends RequirementEffect { + + public BlazeOfGloryRequirementEffect() { + super(Duration.EndOfTurn); + this.staticText = "It blocks each attacking creature this turn if able"; + } + + public BlazeOfGloryRequirementEffect(final BlazeOfGloryRequirementEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(targetPointer.getFirst(game, source)); + } + + @Override + public boolean mustAttack(Game game) { + return false; + } + + @Override + public boolean mustBlock(Game game) { + return true; + } + + @Override + public boolean mustBlockAllAttackers(Game game) { + return true; + } + + @Override + public BlazeOfGloryRequirementEffect copy() { + return new BlazeOfGloryRequirementEffect(this); + } + +} From 05123dafbf7c86dffa999836fd12d58d9a229c7a Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:13 +0000 Subject: [PATCH 080/133] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/LimitedEditionAlpha.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java index 4a87e3baacc..8b7de5a757d 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java @@ -44,6 +44,7 @@ public class LimitedEditionAlpha extends ExpansionSet { cards.add(new SetCardInfo("Black Lotus", 232, Rarity.RARE, mage.cards.b.BlackLotus.class)); cards.add(new SetCardInfo("Black Vise", 233, Rarity.UNCOMMON, mage.cards.b.BlackVise.class)); cards.add(new SetCardInfo("Black Ward", 189, Rarity.UNCOMMON, mage.cards.b.BlackWard.class)); + cards.add(new SetCardInfo("Blaze of Glory", 190, Rarity.RARE, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blessing", 191, Rarity.RARE, mage.cards.b.Blessing.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 50, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Blue Ward", 192, Rarity.UNCOMMON, mage.cards.b.BlueWard.class)); From 38a5bd01f213cfec9cba5f0a172d5e3a67a3cd46 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:17 +0000 Subject: [PATCH 081/133] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/LimitedEditionBeta.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java index aed946ed8a6..9b642609e4c 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java @@ -44,6 +44,7 @@ public class LimitedEditionBeta extends ExpansionSet { cards.add(new SetCardInfo("Black Lotus", 233, Rarity.RARE, mage.cards.b.BlackLotus.class)); cards.add(new SetCardInfo("Black Vise", 234, Rarity.UNCOMMON, mage.cards.b.BlackVise.class)); cards.add(new SetCardInfo("Black Ward", 5, Rarity.UNCOMMON, mage.cards.b.BlackWard.class)); + cards.add(new SetCardInfo("Blaze of Glory", 6, Rarity.RARE, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blessing", 7, Rarity.RARE, mage.cards.b.Blessing.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 50, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Blue Ward", 8, Rarity.UNCOMMON, mage.cards.b.BlueWard.class)); From 9b3ed94cd4716b81f6ef0a7a0abd4576a250e037 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:25 +0000 Subject: [PATCH 082/133] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/UnlimitedEdition.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/UnlimitedEdition.java b/Mage.Sets/src/mage/sets/UnlimitedEdition.java index c1213ef3314..a27c910c64f 100644 --- a/Mage.Sets/src/mage/sets/UnlimitedEdition.java +++ b/Mage.Sets/src/mage/sets/UnlimitedEdition.java @@ -44,6 +44,7 @@ public class UnlimitedEdition extends ExpansionSet { cards.add(new SetCardInfo("Black Lotus", 233, Rarity.RARE, mage.cards.b.BlackLotus.class)); cards.add(new SetCardInfo("Black Vise", 234, Rarity.UNCOMMON, mage.cards.b.BlackVise.class)); cards.add(new SetCardInfo("Black Ward", 189, Rarity.UNCOMMON, mage.cards.b.BlackWard.class)); + cards.add(new SetCardInfo("Blaze of Glory", 190, Rarity.RARE, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blessing", 191, Rarity.RARE, mage.cards.b.Blessing.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 50, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Blue Ward", 192, Rarity.UNCOMMON, mage.cards.b.BlueWard.class)); From a6048f497f6d8c2b4615c4dd1dd108bb89f89b42 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:31 +0000 Subject: [PATCH 083/133] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 03ff7d240ac..4ce69c55900 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -91,6 +91,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Bee Sting", 144, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); cards.add(new SetCardInfo("Bird Maiden", 110, Rarity.COMMON, mage.cards.b.BirdMaiden.class)); cards.add(new SetCardInfo("Black Knight", 71, Rarity.UNCOMMON, mage.cards.b.BlackKnight.class)); + cards.add(new SetCardInfo("Blaze of Glory", 7, Rarity.UNCOMMON, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 39, Rarity.UNCOMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Book of Rass", 183, Rarity.UNCOMMON, mage.cards.b.BookOfRass.class)); cards.add(new SetCardInfo("Bottle of Suleiman", 184, Rarity.RARE, mage.cards.b.BottleOfSuleiman.class)); From 4c88b8e6e3977ceb73768fe7de775dc8d2003916 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 10:39:33 +0100 Subject: [PATCH 084/133] * Fixed a bug of AttackedThisStepWatcher (fixes #4549). --- .../java/mage/watchers/common/PlayerAttackedStepWatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java index 03f56c9beb5..0fb1362cd22 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java @@ -44,7 +44,7 @@ public class PlayerAttackedStepWatcher extends Watcher { private final Map playerAttacked = new HashMap<>(); public PlayerAttackedStepWatcher() { - super(PlayerAttackedWatcher.class.getSimpleName(), WatcherScope.GAME); + super(PlayerAttackedStepWatcher.class.getSimpleName(), WatcherScope.GAME); } public PlayerAttackedStepWatcher(final PlayerAttackedStepWatcher watcher) { From d7b19a1866aca8cdb13adc2a8dbc0e066ba85ac7 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 25 Feb 2018 14:20:42 +0400 Subject: [PATCH 085/133] Fixed random game ends from 89b6aeacd6ef5c5827ad9033746c00e26197f943 --- Mage.Server/src/main/java/mage/server/TableController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index c45f07adc47..70157884723 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -990,7 +990,7 @@ public class TableController { || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { Optional user = UserManager.instance.getUser(userPlayerEntry.getKey()); - if (!user.isPresent() || user.get().isActive()) { + if (!user.isPresent() || !user.get().isActive()) { logger.warn("- Active user of match is missing: " + matchPlayer.getName()); logger.warn("-- matchId:" + match.getId()); logger.warn("-- userId:" + userPlayerEntry.getKey()); From 7e5f4f580d9c8239c20de1523aa2ea043d167bc2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 12:33:57 +0100 Subject: [PATCH 086/133] * Fixed wrong user active check for table health. --- Mage.Server/src/main/java/mage/server/TableController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index c45f07adc47..70157884723 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -990,7 +990,7 @@ public class TableController { || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { Optional user = UserManager.instance.getUser(userPlayerEntry.getKey()); - if (!user.isPresent() || user.get().isActive()) { + if (!user.isPresent() || !user.get().isActive()) { logger.warn("- Active user of match is missing: " + matchPlayer.getName()); logger.warn("-- matchId:" + match.getId()); logger.warn("-- userId:" + userPlayerEntry.getKey()); From 36ef666f0e4bbdb199c6e463589c0d8747880750 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 11:52:43 +0000 Subject: [PATCH 087/133] Implemented Harsh Justice --- Mage.Sets/src/mage/cards/h/HarshJustice.java | 170 +++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HarshJustice.java diff --git a/Mage.Sets/src/mage/cards/h/HarshJustice.java b/Mage.Sets/src/mage/cards/h/HarshJustice.java new file mode 100644 index 00000000000..bfbf9c70af7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HarshJustice.java @@ -0,0 +1,170 @@ +/* + * 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.h; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author LevelX2 & L_J + */ +public class HarshJustice extends CardImpl { + + public HarshJustice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Cast Harsh Justice only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // This turn, whenever an attacking creature deals combat damage to you, it deals that much damage to its controller. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new HarshJusticeTriggeredAbility())); + } + + public HarshJustice(final HarshJustice card) { + super(card); + } + + @Override + public HarshJustice copy() { + return new HarshJustice(this); + } +} + +class HarshJusticeTriggeredAbility extends DelayedTriggeredAbility { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creature"); + + static { + filter.add(new AttackingPredicate()); + } + + public HarshJusticeTriggeredAbility() { + super(new HarshJusticeEffect(), Duration.EndOfTurn, false); + } + + public HarshJusticeTriggeredAbility(final HarshJusticeTriggeredAbility ability) { + super(ability); + } + + @Override + public HarshJusticeTriggeredAbility copy() { + return new HarshJusticeTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player controller = game.getPlayer(this.getControllerId()); + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + Permanent damagePermanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); + if (controller != null && damagePermanent != null) { + if (damageEvent.isCombatDamage() && controller.getId().equals(damageEvent.getTargetId()) && filter.match(damagePermanent, game)) { + for (Effect effect : this.getEffects()) { + effect.setValue("damage", damageEvent.getAmount()); + effect.setValue("sourceId", damageEvent.getSourceId()); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "This turn, whenever an attacking creature deals combat damage to you, " + super.getRule(); + } +} + +class HarshJusticeEffect extends OneShotEffect { + + public HarshJusticeEffect() { + super(Outcome.Benefit); + this.staticText = "it deals that much damage to its controller"; + } + + public HarshJusticeEffect(final HarshJusticeEffect effect) { + super(effect); + } + + @Override + public HarshJusticeEffect copy() { + return new HarshJusticeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int damage = (Integer) this.getValue("damage"); + UUID sourceId = (UUID) this.getValue("sourceId"); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (sourceObject != null && damage > 0 && sourceId != null) { + Permanent targetObject = game.getPermanentOrLKIBattlefield(sourceId); + if (targetObject != null) { + Player controller = game.getPlayer(targetObject.getControllerId()); + if (controller != null) { + game.informPlayers(sourceObject.getLogName() + ": " + targetObject.getLogName() + " deals " + damage + " damage to " + controller.getLogName()); + controller.damage(damage, sourceId, game, false, true); + return true; + } + } + } + return false; + } +} From 44e5366c48a14bc51bf6238df1ab628687d3a512 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 11:53:58 +0000 Subject: [PATCH 088/133] Implemented Harsh Justice --- Mage.Sets/src/mage/sets/Portal.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index 8c15c619ed3..b672a059ab8 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -71,7 +71,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Armageddon", 167, Rarity.RARE, mage.cards.a.Armageddon.class)); cards.add(new SetCardInfo("Armored Pegasus", 168, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); cards.add(new SetCardInfo("Arrogant Vampire", 1, Rarity.UNCOMMON, mage.cards.a.ArrogantVampire.class)); - cards.add(new SetCardInfo("Assassin's Blade", 2, Rarity.UNCOMMON, mage.cards.a.AssassinsBlade.class)); + cards.add(new SetCardInfo("Assassin's Blade", 2, Rarity.UNCOMMON, mage.cards.a.AssassinsBlade.class)); cards.add(new SetCardInfo("Balance of Power", 42, Rarity.RARE, mage.cards.b.BalanceOfPower.class)); cards.add(new SetCardInfo("Baleful Stare", 43, Rarity.UNCOMMON, mage.cards.b.BalefulStare.class)); cards.add(new SetCardInfo("Bee Sting", 83, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); @@ -95,14 +95,14 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Cloud Dragon", 46, Rarity.RARE, mage.cards.c.CloudDragon.class)); cards.add(new SetCardInfo("Cloud Pirates", 47, Rarity.COMMON, mage.cards.c.CloudPirates.class)); cards.add(new SetCardInfo("Cloud Spirit", 48, Rarity.UNCOMMON, mage.cards.c.CloudSpirit.class)); - cards.add(new SetCardInfo("Command of Unsummoning", 49, Rarity.UNCOMMON, mage.cards.c.CommandOfUnsummoning.class)); + cards.add(new SetCardInfo("Command of Unsummoning", 49, Rarity.UNCOMMON, mage.cards.c.CommandOfUnsummoning.class)); cards.add(new SetCardInfo("Coral Eel", 50, Rarity.COMMON, mage.cards.c.CoralEel.class)); cards.add(new SetCardInfo("Craven Giant", 126, Rarity.COMMON, mage.cards.c.CravenGiant.class)); cards.add(new SetCardInfo("Craven Knight", 7, Rarity.COMMON, mage.cards.c.CravenKnight.class)); cards.add(new SetCardInfo("Cruel Bargain", 8, Rarity.RARE, mage.cards.c.CruelBargain.class)); cards.add(new SetCardInfo("Cruel Tutor", 9, Rarity.RARE, mage.cards.c.CruelTutor.class)); cards.add(new SetCardInfo("Deep-Sea Serpent", 52, Rarity.UNCOMMON, mage.cards.d.DeepSeaSerpent.class)); - cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); + cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); cards.add(new SetCardInfo("Deja Vu", 53, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Desert Drake", 127, Rarity.UNCOMMON, mage.cards.d.DesertDrake.class)); cards.add(new SetCardInfo("Devastation", 128, Rarity.RARE, mage.cards.d.Devastation.class)); @@ -143,6 +143,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Grizzly Bears", 94, Rarity.COMMON, mage.cards.g.GrizzlyBears.class)); cards.add(new SetCardInfo("Hand of Death", 18, Rarity.COMMON, mage.cards.h.HandOfDeath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hand of Death", 19, Rarity.COMMON, mage.cards.h.HandOfDeath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harsh Justice", 180, Rarity.RARE, mage.cards.h.HarshJustice.class)); cards.add(new SetCardInfo("Highland Giant", 137, Rarity.COMMON, mage.cards.h.HighlandGiant.class)); cards.add(new SetCardInfo("Hill Giant", 138, Rarity.COMMON, mage.cards.h.HillGiant.class)); cards.add(new SetCardInfo("Horned Turtle", 58, Rarity.COMMON, mage.cards.h.HornedTurtle.class)); @@ -217,7 +218,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Sacred Knight", 186, Rarity.COMMON, mage.cards.s.SacredKnight.class)); cards.add(new SetCardInfo("Sacred Nectar", 187, Rarity.COMMON, mage.cards.s.SacredNectar.class)); cards.add(new SetCardInfo("Scorching Spear", 154, Rarity.COMMON, mage.cards.s.ScorchingSpear.class)); - cards.add(new SetCardInfo("Scorching Winds", 155, Rarity.UNCOMMON, mage.cards.s.ScorchingWinds.class)); + cards.add(new SetCardInfo("Scorching Winds", 155, Rarity.UNCOMMON, mage.cards.s.ScorchingWinds.class)); cards.add(new SetCardInfo("Seasoned Marshal", 188, Rarity.UNCOMMON, mage.cards.s.SeasonedMarshal.class)); cards.add(new SetCardInfo("Serpent Assassin", 31, Rarity.RARE, mage.cards.s.SerpentAssassin.class)); cards.add(new SetCardInfo("Serpent Warrior", 32, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); @@ -252,7 +253,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Tidal Surge", 75, Rarity.COMMON, mage.cards.t.TidalSurge.class)); cards.add(new SetCardInfo("Time Ebb", 76, Rarity.COMMON, mage.cards.t.TimeEbb.class)); cards.add(new SetCardInfo("Touch of Brilliance", 77, Rarity.COMMON, mage.cards.t.TouchOfBrilliance.class)); - cards.add(new SetCardInfo("Treetop Defense", 116, Rarity.RARE, mage.cards.t.TreetopDefense.class)); + cards.add(new SetCardInfo("Treetop Defense", 116, Rarity.RARE, mage.cards.t.TreetopDefense.class)); cards.add(new SetCardInfo("Undying Beast", 36, Rarity.COMMON, mage.cards.u.UndyingBeast.class)); cards.add(new SetCardInfo("Untamed Wilds", 117, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); cards.add(new SetCardInfo("Valorous Charge", 196, Rarity.UNCOMMON, mage.cards.v.ValorousCharge.class)); From aa4997607f188efce10cb37a1cd3ea50b4b72d94 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 11:54:31 +0000 Subject: [PATCH 089/133] Implemented Harsh Justice --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 4ce69c55900..b67d9200c2f 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -170,6 +170,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Grapeshot Catapult", 204, Rarity.UNCOMMON, mage.cards.g.GrapeshotCatapult.class)); cards.add(new SetCardInfo("Gravebind", 84, Rarity.COMMON, mage.cards.g.Gravebind.class)); cards.add(new SetCardInfo("Guardian Beast", 85, Rarity.RARE, mage.cards.g.GuardianBeast.class)); + cards.add(new SetCardInfo("Harsh Justice", 13, Rarity.RARE, mage.cards.h.HarshJustice.class)); cards.add(new SetCardInfo("Hasran Ogress", 86, Rarity.COMMON, mage.cards.h.HasranOgress.class)); cards.add(new SetCardInfo("Healing Salve", 14, Rarity.COMMON, mage.cards.h.HealingSalve.class)); cards.add(new SetCardInfo("Horn of Deafening", 205, Rarity.UNCOMMON, mage.cards.h.HornOfDeafening.class)); From 2c28f64294288a33faa60cd65d74105aefc2d7bc Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 14:26:02 +0000 Subject: [PATCH 090/133] CantBeBlockedByCreaturesAllEffect duration bugfix --- .../common/combat/CantBeBlockedByCreaturesAllEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java index fa528cfcdf2..11dfb626a5e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java @@ -45,7 +45,7 @@ public class CantBeBlockedByCreaturesAllEffect extends RestrictionEffect { private final FilterCreaturePermanent filterCreatures; public CantBeBlockedByCreaturesAllEffect(FilterCreaturePermanent filterCreatures, FilterCreaturePermanent filterBlockedBy, Duration duration) { - super(Duration.WhileOnBattlefield); + super(duration); this.filterCreatures = filterCreatures; this.filterBlockedBy = filterBlockedBy; staticText = new StringBuilder(filterCreatures.getMessage()).append(" can't be blocked ") From d3eeaa28a4235edef18b91e07f33cadfbb924dce Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:20:31 +0000 Subject: [PATCH 091/133] Implemented Deep Wood --- Mage.Sets/src/mage/cards/d/DeepWood.java | 110 +++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DeepWood.java diff --git a/Mage.Sets/src/mage/cards/d/DeepWood.java b/Mage.Sets/src/mage/cards/d/DeepWood.java new file mode 100644 index 00000000000..bb1d3a14051 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeepWood.java @@ -0,0 +1,110 @@ +/* + * 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.d; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.events.DamagePlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author L_J + */ +public class DeepWood extends CardImpl { + + public DeepWood(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Cast Deep Wood only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Prevent all damage that would be dealt to you this turn by attacking creatures. + this.getSpellAbility().addEffect(new DeepWoodEffect()); + } + + public DeepWood(final DeepWood card) { + super(card); + } + + @Override + public DeepWood copy() { + return new DeepWood(this); + } +} + +class DeepWoodEffect extends PreventionEffectImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + + DeepWoodEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false); + staticText = "Prevent all damage that would be dealt to you this turn by attacking creatures"; + } + + DeepWoodEffect(final DeepWoodEffect effect) { + super(effect); + } + + @Override + public DeepWoodEffect copy() { + return new DeepWoodEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) { + DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; + if (event.getTargetId().equals(source.getControllerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); + if (permanent != null && filter.match(permanent, game)) { + return true; + } + } + } + return false; + } +} From 65428726dac8334d8e3f2d614b8a794f21ae47c9 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:21:00 +0000 Subject: [PATCH 092/133] Implemented Dread Charge --- Mage.Sets/src/mage/cards/d/DreadCharge.java | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DreadCharge.java diff --git a/Mage.Sets/src/mage/cards/d/DreadCharge.java b/Mage.Sets/src/mage/cards/d/DreadCharge.java new file mode 100644 index 00000000000..e7c8819cbf9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreadCharge.java @@ -0,0 +1,73 @@ +/* + * 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.d; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; + +/** + * + * @author L_J + */ +public class DreadCharge extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Black creatures you control"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("except by black creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + filter2.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public DreadCharge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); + + // Black creatures you control can't be blocked this turn except by black creatures. + this.getSpellAbility().addEffect(new CantBeBlockedByCreaturesAllEffect(filter, filter2, Duration.EndOfTurn)); + } + + public DreadCharge(final DreadCharge card) { + super(card); + } + + @Override + public DreadCharge copy() { + return new DreadCharge(this); + } +} From 5152d34c417a1482e7cccf8dc4fc20a59a29e58d Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:21:05 +0000 Subject: [PATCH 093/133] Implemented Cruel Fate --- Mage.Sets/src/mage/cards/c/CruelFate.java | 142 ++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CruelFate.java diff --git a/Mage.Sets/src/mage/cards/c/CruelFate.java b/Mage.Sets/src/mage/cards/c/CruelFate.java new file mode 100644 index 00000000000..ce16987c8f5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CruelFate.java @@ -0,0 +1,142 @@ +/* + * 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.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 & L_J + */ +public class CruelFate extends CardImpl { + + public CruelFate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}"); + + // Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of his or her library in any order. + this.getSpellAbility().addEffect(new CruelFateEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + } + + public CruelFate(final CruelFate card) { + super(card); + } + + @Override + public CruelFate copy() { + return new CruelFate(this); + } +} + +class CruelFateEffect extends OneShotEffect { + + public CruelFateEffect() { + super(Outcome.DrawCard); + this.staticText = "Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of his or her library in any order"; + } + + public CruelFateEffect(final CruelFateEffect effect) { + super(effect); + } + + @Override + public CruelFateEffect copy() { + return new CruelFateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card sourceCard = game.getCard(source.getSourceId()); + if (sourceCard != null) { + Player you = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null && you != null) { + Cards cards = new CardsImpl(); + int count = Math.min(player.getLibrary().size(), 5); + for (int i = 0; i < count; i++) { + Card card = player.getLibrary().removeFromTop(game); + if (card != null) { + cards.add(card); + } + } + + you.lookAtCards(sourceCard.getIdName(), cards, game); + + // card to put into opponent's graveyard + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put into target opponent's graveyard")); + if (player.canRespond()) { + if (cards.size() > 1) { + you.choose(Outcome.Detriment, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); + if (card != null) { + cards.remove(card); + card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + } + } + else if (cards.size() == 1) { + Card card = cards.get(cards.iterator().next(), game); + card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + } + } + // cards to put on the top of opponent's library + TargetCard target2 = new TargetCard(Zone.LIBRARY, new FilterCard("card to put on the top of target opponent's library")); + while (player.canRespond() && cards.size() > 1) { + you.choose(Outcome.Neutral, cards, target2, game); + Card card = cards.get(target2.getFirstTarget(), game); + if (card != null) { + cards.remove(card); + card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + } + target2.clearChosen(); + } + if (cards.size() == 1) { + Card card = cards.get(cards.iterator().next(), game); + card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + } + return true; + } + } + return false; + } +} From b347ab3eb8743bc161fca559742a8f6a429afcd0 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:22:41 +0000 Subject: [PATCH 094/133] Implemented Temporary Truce --- .../src/mage/cards/t/TemporaryTruce.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TemporaryTruce.java diff --git a/Mage.Sets/src/mage/cards/t/TemporaryTruce.java b/Mage.Sets/src/mage/cards/t/TemporaryTruce.java new file mode 100644 index 00000000000..0efb4ea8dd1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TemporaryTruce.java @@ -0,0 +1,95 @@ +/* + * 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.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public class TemporaryTruce extends CardImpl { + + public TemporaryTruce(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); + + // Each player may draw up to two cards. For each card less than two a player draws this way, that player gains 2 life. + this.getSpellAbility().addEffect(new TemporaryTruceEffect()); + } + + public TemporaryTruce(final TemporaryTruce card) { + super(card); + } + + @Override + public TemporaryTruce copy() { + return new TemporaryTruce(this); + } +} + +class TemporaryTruceEffect extends OneShotEffect { + + TemporaryTruceEffect() { + super(Outcome.DrawCard); + this.staticText = "Each player may draw up to two cards. For each card less than two a player draws this way, that player gains 2 life"; + } + + TemporaryTruceEffect(final TemporaryTruceEffect effect) { + super(effect); + } + + @Override + public TemporaryTruceEffect copy() { + return new TemporaryTruceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + int cardsToDraw = player.getAmount(0, 2, "Draw how many cards?", game); + player.drawCards(cardsToDraw, game); + player.gainLife((2 - cardsToDraw) * 2, game); + } + } + return true; + } + return false; + } +} From 7e83497ba46e94b2c70ae0798ab003bb9fd89861 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:24:30 +0000 Subject: [PATCH 095/133] Implemented cards --- Mage.Sets/src/mage/sets/Portal.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index b672a059ab8..36adbfaa9b7 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -100,14 +100,17 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Craven Giant", 126, Rarity.COMMON, mage.cards.c.CravenGiant.class)); cards.add(new SetCardInfo("Craven Knight", 7, Rarity.COMMON, mage.cards.c.CravenKnight.class)); cards.add(new SetCardInfo("Cruel Bargain", 8, Rarity.RARE, mage.cards.c.CruelBargain.class)); + cards.add(new SetCardInfo("Cruel Fate", 51, Rarity.RARE, mage.cards.c.CruelFate.class)); cards.add(new SetCardInfo("Cruel Tutor", 9, Rarity.RARE, mage.cards.c.CruelTutor.class)); cards.add(new SetCardInfo("Deep-Sea Serpent", 52, Rarity.UNCOMMON, mage.cards.d.DeepSeaSerpent.class)); + cards.add(new SetCardInfo("Deep Wood", 86, Rarity.UNCOMMON, mage.cards.d.DeepWood.class)); cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); cards.add(new SetCardInfo("Deja Vu", 53, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Desert Drake", 127, Rarity.UNCOMMON, mage.cards.d.DesertDrake.class)); cards.add(new SetCardInfo("Devastation", 128, Rarity.RARE, mage.cards.d.Devastation.class)); cards.add(new SetCardInfo("Devoted Hero", 175, Rarity.COMMON, mage.cards.d.DevotedHero.class)); cards.add(new SetCardInfo("Djinn of the Lamp", 54, Rarity.RARE, mage.cards.d.DjinnOfTheLamp.class)); + cards.add(new SetCardInfo("Dread Charge", 10, Rarity.RARE, mage.cards.d.DreadCharge.class)); cards.add(new SetCardInfo("Dread Reaper", 11, Rarity.RARE, mage.cards.d.DreadReaper.class)); cards.add(new SetCardInfo("Dry Spell", 12, Rarity.UNCOMMON, DrySpell.class)); cards.add(new SetCardInfo("Earthquake", 129, Rarity.RARE, mage.cards.e.Earthquake.class)); @@ -246,6 +249,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Sylvan Tutor", 114, Rarity.RARE, mage.cards.s.SylvanTutor.class)); cards.add(new SetCardInfo("Symbol of Unsummoning", 71, Rarity.COMMON, mage.cards.s.SymbolOfUnsummoning.class)); cards.add(new SetCardInfo("Taunt", 72, Rarity.RARE, mage.cards.t.Taunt.class)); + cards.add(new SetCardInfo("Temporary Truce", 195, Rarity.RARE, mage.cards.t.TemporaryTruce.class)); cards.add(new SetCardInfo("Theft of Dreams", 73, Rarity.UNCOMMON, mage.cards.t.TheftOfDreams.class)); cards.add(new SetCardInfo("Thing from the Deep", 74, Rarity.RARE, mage.cards.t.ThingFromTheDeep.class)); cards.add(new SetCardInfo("Thundering Wurm", 115, Rarity.RARE, mage.cards.t.ThunderingWurm.class)); From 5fac17c0d4e1a40bb2ff030a4423456a50baa1b1 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:24:55 +0000 Subject: [PATCH 096/133] Implemented Deep Wood --- Mage.Sets/src/mage/sets/PortalSecondAge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/PortalSecondAge.java b/Mage.Sets/src/mage/sets/PortalSecondAge.java index f014ac1968b..c393575a31c 100644 --- a/Mage.Sets/src/mage/sets/PortalSecondAge.java +++ b/Mage.Sets/src/mage/sets/PortalSecondAge.java @@ -95,6 +95,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Dakmor Sorceress", 11, Rarity.RARE, mage.cards.d.DakmorSorceress.class)); cards.add(new SetCardInfo("Dark Offering", 12, Rarity.UNCOMMON, mage.cards.d.DarkOffering.class)); cards.add(new SetCardInfo("Deathcoil Wurm", 65, Rarity.RARE, mage.cards.d.DeathcoilWurm.class)); + cards.add(new SetCardInfo("Deep Wood", 66, Rarity.UNCOMMON, mage.cards.d.DeepWood.class)); cards.add(new SetCardInfo("Deja Vu", 35, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Denizen of the Deep", 36, Rarity.RARE, mage.cards.d.DenizenOfTheDeep.class)); cards.add(new SetCardInfo("Earthquake", 94, Rarity.RARE, mage.cards.e.Earthquake.class)); From 2fceafda9388efa574947e1eb5463c9d151b76c1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 21:53:47 +0100 Subject: [PATCH 097/133] * Fixed a bug that mana in the mana pool could not be used to pay mana costs that could only be payed with "you may spend mana as thought" effects (fixes #2581). --- Mage.Sets/src/mage/cards/h/HostageTaker.java | 6 +-- .../cards/asthough/SpendOtherManaTest.java | 30 +++++++++++++++ Mage/src/main/java/mage/Mana.java | 38 ++++++++++++++++--- Mage/src/main/java/mage/players/ManaPool.java | 13 ++++++- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HostageTaker.java b/Mage.Sets/src/mage/cards/h/HostageTaker.java index 92605814ab2..73bc0f278c1 100644 --- a/Mage.Sets/src/mage/cards/h/HostageTaker.java +++ b/Mage.Sets/src/mage/cards/h/HostageTaker.java @@ -41,10 +41,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AsThoughEffectType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.ManaType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; @@ -118,8 +118,8 @@ class HostageTakerExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent card = game.getPermanent(targetPointer.getFirst(game, source)); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent card = game.getPermanent(getTargetPointer().getFirst(game, source)); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (permanent != null && card != null) { Player controller = game.getPlayer(card.getControllerId()); if (controller != null) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java index a71cef7116f..76d22a2f123 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java @@ -134,4 +134,34 @@ public class SpendOtherManaTest extends CardTestPlayerBase { assertHandCount(playerA, "Nissa, Voice of Zendikar", 0); assertPermanentCount(playerA, "Nissa, Voice of Zendikar", 1); } + + @Test + public void testUseSpendManaAsThoughWithManaFromPool() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // Creature {1}{W} + + // When Hostage Taker enters the battlefield, exile another target artifact or creature until Hostage Taker leaves the battlefield. + // You may cast that card as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell. + addCard(Zone.HAND, playerA, "Hostage Taker"); // {2}{U}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hostage Taker"); + setChoice(playerA, "Silvercoat Lion"); + + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R} to your mana pool."); // red mana to pool + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R} to your mana pool."); // red mana to pool + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); // cast it from exile with red mana from pool + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Hostage Taker", 1); + assertTappedCount("Mountain", true, 4); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + + } + } diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index b2461d2b367..0e9aaf28253 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -131,6 +131,35 @@ public class Mana implements Comparable, Serializable, Copyable { } } + public Mana(final ManaType manaType) { + Objects.requireNonNull(manaType, "The passed in ManaType can not be null"); + switch (manaType) { + case GREEN: + green = 1; + break; + case RED: + red = 1; + break; + case BLACK: + black = 1; + break; + case BLUE: + blue = 1; + break; + case WHITE: + white = 1; + break; + case COLORLESS: + colorless = 1; + break; + case GENERIC: + generic = 1; + break; + default: + throw new IllegalArgumentException("Unknown manaType: " + manaType); + } + } + /** * Creates a {@link Mana} object with the passed in {@code num} of Red mana. * {@code num} can not be a negative value. Negative values will be logged @@ -222,13 +251,12 @@ public class Mana implements Comparable, Serializable, Copyable { } /** - * Creates a {@link Mana} object with the passed in {@code num} of Any - * mana. {@code num} can not be a negative value. Negative values will be - * logged and set to 0. + * Creates a {@link Mana} object with the passed in {@code num} of Any mana. + * {@code num} can not be a negative value. Negative values will be logged + * and set to 0. * * @param num value of Any mana to create. - * @return a {@link Mana} object with the passed in {@code num} of Any - * mana. + * @return a {@link Mana} object with the passed in {@code num} of Any mana. */ public static Mana AnyMana(int num) { return new Mana(0, 0, 0, 0, 0, 0, notNegative(num, "Any"), 0); diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index 62ddedf6d6b..565e96cac21 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -118,10 +118,19 @@ public class ManaPool implements Serializable { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } + ManaType possibleAsThoughtPoolManaType = null; if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && manaType != unlockedManaType) { // if automatic restricted payment and there is already mana in the pool // and the needed mana type was not unlocked, nothing will be paid - return false; + if (unlockedManaType != null) { + ManaPoolItem checkItem = new ManaPoolItem(); + checkItem.add(unlockedManaType, 1); + possibleAsThoughtPoolManaType = game.getContinuousEffects().asThoughMana(manaType, checkItem, ability.getSourceId(), ability, ability.getControllerId(), game); + } + // Check if it's possible to use mana as thought for the unlocked manatype in the mana pool for this ability + if (possibleAsThoughtPoolManaType == null || possibleAsThoughtPoolManaType != unlockedManaType) { + return false; // if it's not possible return + } } if (getConditional(manaType, ability, filter, game, costToPay) > 0) { @@ -138,7 +147,7 @@ public class ManaPool implements Serializable { } } } - if (manaType != unlockedManaType && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { + if (possibleAsThoughtPoolManaType == null && manaType != unlockedManaType && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { // no mana added beyond the stock so don't auto pay this continue; } From ba483dd87a94b8b5f627f44f66329f27925cc771 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 21:59:06 +0100 Subject: [PATCH 098/133] * Added a test for the new Blood Moon rule (set to @Ignored) because new rule is not implemented. --- .../cards/abilities/enters/BloodMoonTest.java | 168 +++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java index 52b8740aac1..d2844086f62 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java @@ -5,10 +5,13 @@ */ package org.mage.test.cards.abilities.enters; +import mage.constants.CardType; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -90,7 +93,7 @@ public class BloodMoonTest extends CardTestPlayerBase { * Spreading Seas was played turn 3 in a Steam Vents, Blood Moon turn 7 or * something * - * The enchanted Steam Vents was producing only U when ir should produce + * The enchanted Steam Vents was producing only U when it should produce * only R because of blood moon's time stamp. * * http://blogs.magicjudges.org/articles/2013/06/18/blood-moon-in-a-modern-environment/ @@ -238,4 +241,167 @@ public class BloodMoonTest extends CardTestPlayerBase { Assert.assertTrue("The mana the land can produce should be [{R}] but it's " + playerA.getManaAvailable(currentGame).toString(), playerA.getManaAvailable(currentGame).toString().equals("[{R}]")); } + + /** + * If Blood Moon enters the battlefield with an animated Mutavault in play, + * the Mutavault stays a 2/2 creature with all creature types and “gains” + * the land type mountain (it can also tap for R). However, once the turn + * ends, the Mutavault will loses both of its abilities and become a non + * basic mountain named Mutavault. + */ + @Test + public void testBloodMoonMutavault() { + + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + // {T}: Add {C} to your mana pool. + // {1}: Mutavault becomes a 2/2 creature with all creature types until end of turn. It's still a land. + addCard(Zone.BATTLEFIELD, playerA, "Mutavault", 1); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.HAND, playerB, "Blood Moon", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: "); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blood Moon"); + + setStopAt(2, PhaseStep.END_TURN); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertPowerToughness(playerA, "Mutavault", 2, 2); + assertType("Mutavault", CardType.LAND, SubType.MOUNTAIN); + assertType("Swamp", CardType.LAND, SubType.SWAMP); + + Assert.assertTrue("The mana the land can produce should be [{R}] but it's " + playerA.getManaAvailable(currentGame).toString(), playerA.getManaAvailable(currentGame).toString().equals("[{R}]")); + } + + @Test + public void testBloodMoonMutavaultEnd() { + + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + // {T}: Add {C} to your mana pool. + // {1}: Mutavault becomes a 2/2 creature with all creature types until end of turn. It's still a land. + addCard(Zone.BATTLEFIELD, playerA, "Mutavault", 1); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.HAND, playerB, "Blood Moon", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: "); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blood Moon"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertPowerToughness(playerA, "Mutavault", 0, 0); + assertType("Mutavault", CardType.LAND, SubType.MOUNTAIN); + assertType("Swamp", CardType.LAND, SubType.SWAMP); + + Assert.assertTrue("The mana the lands can produce should be [{B}{R}] but it's " + playerA.getManaAvailable(currentGame).toString(), playerA.getManaAvailable(currentGame).toString().equals("[{B}{R}]")); + } + + /** + * If Blood Moon is on the battlefield, Flagstones of Trokair will enter the + * battlefield as a legendary non-basic Mountain. If Flagstones of Trokair + * is put into the graveyard due to “Legends rule” or because it was + * destroyed, its ability doesn’t trigger, because it doesn’t exist: it + * won’t fetch you a Plains. + */ + @Test + public void testBloodMoonFlagstonesOfTrokair() { + // {T}: Add {W} to your mana pool. + // When Flagstones of Trokair is put into a graveyard from the battlefield, you may search + // your library for a Plains card and put it onto the battlefield tapped. If you do, shuffle your library. + addCard(Zone.HAND, playerA, "Flagstones of Trokair", 1); + addCard(Zone.LIBRARY, playerA, "Plains"); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.BATTLEFIELD, playerB, "Blood Moon"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + addCard(Zone.HAND, playerB, "Stone Rain"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flagstones of Trokair"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Stone Rain", "Flagstones of Trokair"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertGraveyardCount(playerB, "Stone Rain", 1); + + assertPermanentCount(playerA, "Flagstones of Trokair", 0); + assertGraveyardCount(playerA, "Flagstones of Trokair", 1); + + assertPermanentCount(playerA, 0); + } + + /** + * Because Blood Moon’s static ability operates only when it’s on the + * battlefield and begins affecting any nonbasic lands immediately. In fact, + * Blood Moon’s effect is so quick that once a non-basic land hits the + * battlefield it’s going to be affected. Therefore: + * + * If a nonbasic land’s has EtB triggered abilities, these will not trigger + * because the ability isn’t there (it’s gone). Effects that modify the way + * the land enters the battlefield are replacement effects. They are applied + * before the permanent enters the battlefield and taking into account + * continuous effects that already exist and would apply to the permanent. + * (see CR 614.12). + * + * 614.12 Some replacement effects modify how a permanent enters the + * battlefield. (See rules 614.1c-d.) Such effects may come from the + * permanent itself if they affect only that permanent (as opposed to a + * general subset of permanents that includes it). They may also come from + * other sources. To determine which replacement effects apply and how they + * apply, check the characteristics of the permanent as it would exist on + * the battlefield, taking into account replacement effects that have + * already modified how it enters the battlefield (see rule 616.1), + * continuous effects from the permanent's own static abilities that would + * apply to it once it's on the battlefield, and continuous effects that + * ..........................................=========================== + * already exist and would apply to the permanent. + * ================================================ + * + * Madblind Mountain enters the battlefield untapped, as a nonbasic Mountain + * with single mana ability and no other. + */ + @Test + @Ignore + public void testBloodMoonMadblindMountain() { + // {T}: Add {R} to your mana pool. + // Madblind Mountain enters the battlefield tapped. + // {R}, {tap}: Shuffle your library. Activate this ability only if you control two or more red permanents. + addCard(Zone.HAND, playerA, "Madblind Mountain", 1); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.BATTLEFIELD, playerB, "Blood Moon"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Madblind Mountain"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertPermanentCount(playerA, "Madblind Mountain", 1); + + assertTapped("Madblind Mountain", false); // it may not be tapped because the etB effect was removed by Blood Moon + + } } From 9e2fda2a77564605aae2055293b36554be114f22 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:38:05 +0000 Subject: [PATCH 099/133] Implemented Eunuchs' Intrigues --- .../src/mage/cards/e/EunuchsIntrigues.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java diff --git a/Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java b/Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java new file mode 100644 index 00000000000..7592c517448 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java @@ -0,0 +1,145 @@ +/* + * 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.e; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 & L_J + */ +public class EunuchsIntrigues extends CardImpl { + + public EunuchsIntrigues(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn. + this.getSpellAbility().addEffect(new EunuchsIntriguesEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public EunuchsIntrigues(final EunuchsIntrigues card) { + super(card); + } + + @Override + public EunuchsIntrigues copy() { + return new EunuchsIntrigues(this); + } +} + +class EunuchsIntriguesEffect extends OneShotEffect { + + EunuchsIntriguesEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn."; + } + + EunuchsIntriguesEffect(final EunuchsIntriguesEffect effect) { + super(effect); + } + + @Override + public EunuchsIntriguesEffect copy() { + return new EunuchsIntriguesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); + filter.add(new ControllerIdPredicate(player.getId())); + Target target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) { + player.chooseTarget(Outcome.DestroyPermanent, target, source, game); + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as his only creature able to block this turn"); + } + } + game.addEffect(new EunuchsIntriguesRestrictionEffect(target.getFirstTarget()), source); + return true; + } +} + +class EunuchsIntriguesRestrictionEffect extends RestrictionEffect { + + protected UUID targetId; + + public EunuchsIntriguesRestrictionEffect(UUID targetId) { + super(Duration.EndOfTurn); + this.targetId = targetId; + } + + public EunuchsIntriguesRestrictionEffect(final EunuchsIntriguesRestrictionEffect effect) { + super(effect); + targetId = effect.targetId; + } + + @Override + public EunuchsIntriguesRestrictionEffect copy() { + return new EunuchsIntriguesRestrictionEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + if (permanent.getControllerId().equals(source.getFirstTarget())) { + return true; + } + return false; + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { + if (targetId != null && blocker.getId().equals(targetId)) { + return true; + } + return false; + } +} From cb677fa9837f292b2b90e34871bd8ed9aef42bd5 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:38:47 +0000 Subject: [PATCH 100/133] Implemented Goblin War Cry --- Mage.Sets/src/mage/cards/g/GoblinWarCry.java | 145 +++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GoblinWarCry.java diff --git a/Mage.Sets/src/mage/cards/g/GoblinWarCry.java b/Mage.Sets/src/mage/cards/g/GoblinWarCry.java new file mode 100644 index 00000000000..a1d6a1939af --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoblinWarCry.java @@ -0,0 +1,145 @@ +/* + * 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.g; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 & L_J + */ +public class GoblinWarCry extends CardImpl { + + public GoblinWarCry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn. + this.getSpellAbility().addEffect(new GoblinWarCryEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public GoblinWarCry(final GoblinWarCry card) { + super(card); + } + + @Override + public GoblinWarCry copy() { + return new GoblinWarCry(this); + } +} + +class GoblinWarCryEffect extends OneShotEffect { + + GoblinWarCryEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn."; + } + + GoblinWarCryEffect(final GoblinWarCryEffect effect) { + super(effect); + } + + @Override + public GoblinWarCryEffect copy() { + return new GoblinWarCryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); + filter.add(new ControllerIdPredicate(player.getId())); + Target target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) { + player.chooseTarget(Outcome.DestroyPermanent, target, source, game); + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as his only creature able to block this turn"); + } + } + game.addEffect(new GoblinWarCryRestrictionEffect(target.getFirstTarget()), source); + return true; + } +} + +class GoblinWarCryRestrictionEffect extends RestrictionEffect { + + protected UUID targetId; + + public GoblinWarCryRestrictionEffect(UUID targetId) { + super(Duration.EndOfTurn); + this.targetId = targetId; + } + + public GoblinWarCryRestrictionEffect(final GoblinWarCryRestrictionEffect effect) { + super(effect); + targetId = effect.targetId; + } + + @Override + public GoblinWarCryRestrictionEffect copy() { + return new GoblinWarCryRestrictionEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + if (permanent.getControllerId().equals(source.getFirstTarget())) { + return true; + } + return false; + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { + if (targetId != null && blocker.getId().equals(targetId)) { + return true; + } + return false; + } +} From 47c3117dd322fee6ffb1f9a1a83f797bd37f5808 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:39:25 +0000 Subject: [PATCH 101/133] Implemented Heavy Fog --- Mage.Sets/src/mage/cards/h/HeavyFog.java | 110 +++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HeavyFog.java diff --git a/Mage.Sets/src/mage/cards/h/HeavyFog.java b/Mage.Sets/src/mage/cards/h/HeavyFog.java new file mode 100644 index 00000000000..e69e57ebf07 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeavyFog.java @@ -0,0 +1,110 @@ +/* + * 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.h; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.events.DamagePlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author L_J + */ +public class HeavyFog extends CardImpl { + + public HeavyFog(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Cast Deep Wood only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Prevent all damage that would be dealt to you this turn by attacking creatures. + this.getSpellAbility().addEffect(new HeavyFogEffect()); + } + + public HeavyFog(final HeavyFog card) { + super(card); + } + + @Override + public HeavyFog copy() { + return new HeavyFog(this); + } +} + +class HeavyFogEffect extends PreventionEffectImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + + HeavyFogEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false); + staticText = "Prevent all damage that would be dealt to you this turn by attacking creatures"; + } + + HeavyFogEffect(final HeavyFogEffect effect) { + super(effect); + } + + @Override + public HeavyFogEffect copy() { + return new HeavyFogEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) { + DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; + if (event.getTargetId().equals(source.getControllerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); + if (permanent != null && filter.match(permanent, game)) { + return true; + } + } + } + return false; + } +} From 2892573fd3870d84087a0a8aa0f12bc83f24f86c Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:39:57 +0000 Subject: [PATCH 102/133] Implemented Just Fate --- Mage.Sets/src/mage/cards/j/JustFate.java | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/j/JustFate.java diff --git a/Mage.Sets/src/mage/cards/j/JustFate.java b/Mage.Sets/src/mage/cards/j/JustFate.java new file mode 100644 index 00000000000..214396cd316 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JustFate.java @@ -0,0 +1,73 @@ +/* + * 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.j; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.target.common.TargetAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class JustFate extends CardImpl { + + public JustFate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Cast Just Fate only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Destroy target attacking creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public JustFate(final JustFate card) { + super(card); + } + + @Override + public JustFate copy() { + return new JustFate(this); + } +} From 3af8e0335092e1e524b745e2ecf3fc49833ffe09 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:40:30 +0000 Subject: [PATCH 103/133] Implemented Kongming's Contraptions --- .../mage/cards/k/KongmingsContraptions.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KongmingsContraptions.java diff --git a/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java b/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java new file mode 100644 index 00000000000..d03db0bfd11 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java @@ -0,0 +1,80 @@ +/* + * 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.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.condition.common.IsStepCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.common.FilterAttackingCreature; +import mage.target.TargetPermanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author L_J + */ +public class KongmingsContraptions extends CardImpl { + + public KongmingsContraptions(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {T}: Kongming's Contraptions deals 2 damage to target attacking creature. Activate this ability only during the declare attackers step and only if you've been attacked this step. + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost(), + new CompoundCondition("during the declare attackers step and only if you've been attacked this step", + new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), AttackedThisStepCondition.instance) + ); + ability.addTarget(new TargetPermanent(new FilterAttackingCreature())); + this.addAbility(ability, new PlayerAttackedStepWatcher()); + } + + public KongmingsContraptions(final KongmingsContraptions card) { + super(card); + } + + @Override + public KongmingsContraptions copy() { + return new KongmingsContraptions(this); + } + +} From 5104442d6d9156d1b7bda337fcab555413e02530 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:41:18 +0000 Subject: [PATCH 104/133] Implemented Warrior's Stand --- Mage.Sets/src/mage/cards/w/WarriorsStand.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WarriorsStand.java diff --git a/Mage.Sets/src/mage/cards/w/WarriorsStand.java b/Mage.Sets/src/mage/cards/w/WarriorsStand.java new file mode 100644 index 00000000000..52b4327e479 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WarriorsStand.java @@ -0,0 +1,72 @@ +/* + * 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.w; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.StaticFilters; +import mage.watchers.common.PlayerAttackedStepWatcher; +/** + * + * @author L_J + */ +public class WarriorsStand extends CardImpl { + + public WarriorsStand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Cast Warrior's Stand only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Creatures you control get +2/+2 until end of turn. + this.getSpellAbility().addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, false)); + } + + public WarriorsStand(final WarriorsStand card) { + super(card); + } + + @Override + public WarriorsStand copy() { + return new WarriorsStand(this); + } +} From 84f2d5cb272cda97b02ac87c9093abb486a9db45 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:42:07 +0000 Subject: [PATCH 105/133] Implemented cards --- Mage.Sets/src/mage/sets/PortalThreeKingdoms.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index bc64a4afe95..611ca804708 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -83,6 +83,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Dong Zhou, the Tyrant", 109, Rarity.RARE, mage.cards.d.DongZhouTheTyrant.class)); cards.add(new SetCardInfo("Eightfold Maze", 2, Rarity.RARE, mage.cards.e.EightfoldMaze.class)); cards.add(new SetCardInfo("Empty City Ruse", 3, Rarity.UNCOMMON, mage.cards.e.EmptyCityRuse.class)); + cards.add(new SetCardInfo("Eunuchs' Intrigues", 110, Rarity.UNCOMMON, mage.cards.e.EunuchsIntrigues.class)); cards.add(new SetCardInfo("Exhaustion", 42, Rarity.RARE, mage.cards.e.Exhaustion.class)); cards.add(new SetCardInfo("Extinguish", 43, Rarity.COMMON, mage.cards.e.Extinguish.class)); cards.add(new SetCardInfo("False Defeat", 4, Rarity.COMMON, mage.cards.f.FalseDefeat.class)); @@ -99,6 +100,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Ghostly Visit", 76, Rarity.COMMON, mage.cards.g.GhostlyVisit.class)); cards.add(new SetCardInfo("Guan Yu's 1,000-Li March", 7, Rarity.RARE, mage.cards.g.GuanYus1000LiMarch.class)); cards.add(new SetCardInfo("Guan Yu, Sainted Warrior", 6, Rarity.RARE, mage.cards.g.GuanYuSaintedWarrior.class)); + cards.add(new SetCardInfo("Heavy Fog", 136, Rarity.UNCOMMON, mage.cards.h.HeavyFog.class)); cards.add(new SetCardInfo("Huang Zhong, Shu General", 8, Rarity.RARE, mage.cards.h.HuangZhongShuGeneral.class)); cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 137, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); cards.add(new SetCardInfo("Hunting Cheetah", 138, Rarity.UNCOMMON, mage.cards.h.HuntingCheetah.class)); @@ -110,6 +112,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Island", 170, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 171, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kongming, \"Sleeping Dragon\"", 9, Rarity.RARE, mage.cards.k.KongmingSleepingDragon.class)); + cards.add(new SetCardInfo("Kongming's Contraptions", 10, Rarity.RARE, mage.cards.k.KongmingsContraptions.class)); cards.add(new SetCardInfo("Lady Sun", 45, Rarity.RARE, mage.cards.l.LadySun.class)); cards.add(new SetCardInfo("Lady Zhurong, Warrior Queen", 139, Rarity.RARE, mage.cards.l.LadyZhurongWarriorQueen.class)); cards.add(new SetCardInfo("Liu Bei, Lord of Shu", 11, Rarity.RARE, mage.cards.l.LiuBeiLordOfShu.class)); @@ -185,6 +188,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Virtuous Charge", 29, Rarity.COMMON, mage.cards.v.VirtuousCharge.class)); cards.add(new SetCardInfo("Volunteer Militia", 30, Rarity.COMMON, mage.cards.v.VolunteerMilitia.class)); cards.add(new SetCardInfo("Warrior's Oath", 124, Rarity.RARE, mage.cards.w.WarriorsOath.class)); + cards.add(new SetCardInfo("Warrior's Stand", 31, Rarity.UNCOMMON, mage.cards.w.WarriorsStand.class)); cards.add(new SetCardInfo("Wei Ambush Force", 85, Rarity.COMMON, mage.cards.w.WeiAmbushForce.class)); cards.add(new SetCardInfo("Wei Assassins", 86, Rarity.UNCOMMON, mage.cards.w.WeiAssassins.class)); cards.add(new SetCardInfo("Wei Elite Companions", 87, Rarity.UNCOMMON, mage.cards.w.WeiEliteCompanions.class)); From a87cbc12c76273257c1c4d0d62dac948beaf2f9a Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:42:46 +0000 Subject: [PATCH 106/133] Implemented cards --- Mage.Sets/src/mage/sets/PortalSecondAge.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mage.Sets/src/mage/sets/PortalSecondAge.java b/Mage.Sets/src/mage/sets/PortalSecondAge.java index c393575a31c..827a31692cd 100644 --- a/Mage.Sets/src/mage/sets/PortalSecondAge.java +++ b/Mage.Sets/src/mage/sets/PortalSecondAge.java @@ -117,6 +117,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Goblin Mountaineer", 101, Rarity.COMMON, mage.cards.g.GoblinMountaineer.class)); cards.add(new SetCardInfo("Goblin Piker", 102, Rarity.COMMON, mage.cards.g.GoblinPiker.class)); cards.add(new SetCardInfo("Goblin Raider", 103, Rarity.COMMON, mage.cards.g.GoblinRaider.class)); + cards.add(new SetCardInfo("Goblin War Cry", 104, Rarity.UNCOMMON, mage.cards.g.GoblinWarCry.class)); cards.add(new SetCardInfo("Goblin War Strike", 105, Rarity.COMMON, mage.cards.g.GoblinWarStrike.class)); cards.add(new SetCardInfo("Golden Bear", 67, Rarity.COMMON, mage.cards.g.GoldenBear.class)); cards.add(new SetCardInfo("Hand of Death", 14, Rarity.COMMON, mage.cards.h.HandOfDeath.class)); @@ -128,6 +129,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Island", 155, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 156, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jagged Lightning", 106, Rarity.UNCOMMON, mage.cards.j.JaggedLightning.class)); + cards.add(new SetCardInfo("Just Fate", 137, Rarity.RARE, mage.cards.j.JustFate.class)); cards.add(new SetCardInfo("Kiss of Death", 16, Rarity.UNCOMMON, mage.cards.k.KissOfDeath.class)); cards.add(new SetCardInfo("Lava Axe", 107, Rarity.COMMON, mage.cards.l.LavaAxe.class)); cards.add(new SetCardInfo("Lone Wolf", 71, Rarity.UNCOMMON, mage.cards.l.LoneWolf.class)); @@ -213,6 +215,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Vengeance", 147, Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); cards.add(new SetCardInfo("Volcanic Hammer", 119, Rarity.COMMON, mage.cards.v.VolcanicHammer.class)); cards.add(new SetCardInfo("Volunteer Militia", 148, Rarity.COMMON, mage.cards.v.VolunteerMilitia.class)); + cards.add(new SetCardInfo("Warrior's Stand", 149, Rarity.UNCOMMON, mage.cards.w.WarriorsStand.class)); cards.add(new SetCardInfo("Wildfire", 120, Rarity.RARE, mage.cards.w.Wildfire.class)); cards.add(new SetCardInfo("Wild Griffin", 150, Rarity.COMMON, mage.cards.w.WildGriffin.class)); cards.add(new SetCardInfo("Wild Ox", 90, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); From 5246b960059898180acc2cd0a944c3ff894ede1e Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:43:58 +0000 Subject: [PATCH 107/133] Implemented Heavy Fog --- Mage.Sets/src/mage/sets/MastersEditionIII.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIII.java b/Mage.Sets/src/mage/sets/MastersEditionIII.java index 6ba61343461..dfc7254072c 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIII.java @@ -137,6 +137,7 @@ public class MastersEditionIII extends ExpansionSet { cards.add(new SetCardInfo("Hammerheim", 207, Rarity.UNCOMMON, mage.cards.h.Hammerheim.class)); cards.add(new SetCardInfo("Hazezon Tamar", 151, Rarity.RARE, mage.cards.h.HazezonTamar.class)); cards.add(new SetCardInfo("Heal", 14, Rarity.COMMON, mage.cards.h.Heal.class)); + cards.add(new SetCardInfo("Heavy Fog", 122, Rarity.COMMON, mage.cards.h.HeavyFog.class)); cards.add(new SetCardInfo("Hellfire", 70, Rarity.RARE, mage.cards.h.Hellfire.class)); cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 123, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); cards.add(new SetCardInfo("Hunding Gjornersen", 152, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); From 2da32a767cb7a3b3c1455168b1ce1c9ee9d4e342 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:44:52 +0000 Subject: [PATCH 108/133] Implemented Just Fate --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index b67d9200c2f..1912527bc2f 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -183,6 +183,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Jade Monolith", 208, Rarity.RARE, mage.cards.j.JadeMonolith.class)); cards.add(new SetCardInfo("Juggernaut", 209, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class)); cards.add(new SetCardInfo("Junún Efreet", 88, Rarity.UNCOMMON, mage.cards.j.JununEfreet.class)); + cards.add(new SetCardInfo("Just Fate", 16, Rarity.COMMON, mage.cards.j.JustFate.class)); cards.add(new SetCardInfo("Kismet", 17, Rarity.RARE, mage.cards.k.Kismet.class)); cards.add(new SetCardInfo("Kormus Bell", 210, Rarity.RARE, mage.cards.k.KormusBell.class)); cards.add(new SetCardInfo("Kudzu", 159, Rarity.UNCOMMON, mage.cards.k.Kudzu.class)); From 576c1aec57ca67c3f61b03bd8e50189f1c4fa002 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 26 Feb 2018 12:47:18 +1100 Subject: [PATCH 109/133] Adding in way to render Zendikar full art lands. Things still to do: 1) Get everyone to download the full art Face images 2) Add in the collector number to the Face images (/FACE/ZEN/Island..jpg for example) 3) Add in the ability to draw say BFZ / HOU full art lands as well. 4) Maybe even add in an UST (Unstable) way of rendering lands (with the swoosh of opaque full art down the bottom). --- .../mage/card/arcane/CardPanelRenderImpl.java | 2 +- .../org/mage/card/arcane/CardRenderer.java | 51 +++-- .../mage/card/arcane/CardRendererUtils.java | 34 ++- .../mage/card/arcane/ModernCardRenderer.java | 209 ++++++++++++++---- .../card/arcane/ModernSplitCardRenderer.java | 58 ++--- 5 files changed, 270 insertions(+), 84 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java index 950069c3fbe..498778c2b3f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java @@ -302,7 +302,7 @@ public class CardPanelRenderImpl extends CardPanel { = new CardPanelAttributes(cardWidth, cardHeight, isChoosable(), isSelected()); // Draw card itself - cardRenderer.draw(g2d, attribs); + cardRenderer.draw(g2d, attribs, image); // Done g2d.dispose(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java index 35ec4fc2140..d32fa0cdd07 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java @@ -17,10 +17,10 @@ import mage.view.PermanentView; import java.awt.*; import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; import java.awt.image.RasterFormatException; import java.util.ArrayList; import java.util.List; +import java.awt.image.BufferedImage; /** * @author stravant@gmail.com @@ -201,7 +201,8 @@ public abstract class CardRenderer { // The Draw Method // The draw method takes the information caculated by the constructor // and uses it to draw to a concrete size of card and graphics. - public void draw(Graphics2D g, CardPanelAttributes attribs) { + public void draw(Graphics2D g, CardPanelAttributes attribs, BufferedImage image) { + // Pre template method layout, to calculate shared layout info layout(attribs.cardWidth, attribs.cardHeight); isSelected = attribs.isSelected; @@ -211,7 +212,7 @@ public abstract class CardRenderer { drawBorder(g); drawBackground(g); drawArt(g); - drawFrame(g); + drawFrame(g, image); if (!cardView.isAbility()) { drawOverlays(g); drawCounters(g); @@ -226,7 +227,7 @@ public abstract class CardRenderer { protected abstract void drawArt(Graphics2D g); - protected abstract void drawFrame(Graphics2D g); + protected abstract void drawFrame(Graphics2D g, BufferedImage image); // Template methods that are possible to override, but unlikely to be // overridden. @@ -462,22 +463,44 @@ public abstract class CardRenderer { } } else { StringBuilder sbType = new StringBuilder(); - for (SuperType superType : cardView.getSuperTypes()) { - sbType.append(superType).append(' '); - } - for (CardType cardType : cardView.getCardTypes()) { - sbType.append(cardType.toString()).append(' '); - } - if (!cardView.getSubTypes().isEmpty()) { - sbType.append("- "); - for (SubType subType : cardView.getSubTypes()) { - sbType.append(subType).append(' '); + String spType = getCardSuperTypeLine(); + String subType = getCardSubTypeLine(); + if (spType.equalsIgnoreCase("")) { + sbType.append(subType); + } else { + sbType.append(spType); + if (!subType.equalsIgnoreCase("")) { + sbType.append("- "); + sbType.append(subType); } } + return sbType.toString(); } } + protected String getCardSuperTypeLine() { + StringBuilder spType = new StringBuilder(); + for (SuperType superType : cardView.getSuperTypes()) { + spType.append(superType).append(' '); + } + for (CardType cardType : cardView.getCardTypes()) { + spType.append(cardType.toString()).append(' '); + } + return spType.toString(); + } + + protected String getCardSubTypeLine() { + StringBuilder subType = new StringBuilder(); + + if (!cardView.getSubTypes().isEmpty()) { + for (SubType sType : cardView.getSubTypes()) { + subType.append(sType).append(' '); + } + } + return subType.toString(); + } + // Set the card art image (CardPanel will give it to us when it // is loaded and ready) public void setArtImage(Image image) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index eca040234fb..f38d9b0f751 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -52,7 +52,7 @@ public final class CardRendererUtils { return bimage; } - private static Color abitbrighter(Color c) { + public static Color abitbrighter(Color c) { int r = c.getRed(); int g = c.getGreen(); int b = c.getBlue(); @@ -68,7 +68,7 @@ public final class CardRendererUtils { alpha); } - private static Color abitdarker(Color c) { + public static Color abitdarker(Color c) { int r = c.getRed(); int g = c.getGreen(); int b = c.getBlue(); @@ -107,6 +107,36 @@ public final class CardRendererUtils { g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); } + + public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { + g.setColor(new Color(0, 0, 0, 150)); + + g.drawOval(x - 1, y, bevel * 2, h); + g.setPaint(border); + g.drawOval(x, y, bevel * 2 - 1, h - 1); + g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); + g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); + g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); + + // The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4 + g.drawOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); + + g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); + g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); + g.setPaint(fill); + g.setPaint(fill); + g.setColor(abitbrighter(g.getColor())); + g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); + g.setPaint(fill); + g.setColor(abitdarker(g.getColor())); + g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); + + g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); + g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); + g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); + + g.fillOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); + } // Get the width of a mana cost rendered with ManaSymbols.draw public static int getManaCostWidth(String manaCost, int symbolSize) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index a75c12fe634..16c0fc996b3 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -7,6 +7,7 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; +import java.awt.geom.QuadCurve2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; @@ -312,6 +313,8 @@ public class ModernCardRenderer extends CardRenderer { Rectangle2D rect; if (useInventionFrame()) { rect = new Rectangle2D.Float(0, 0, 1, 1); + } else if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + rect = new Rectangle2D.Float(.079f, .11f, .84f, .84f); } else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f); } else { @@ -352,7 +355,7 @@ public class ModernCardRenderer extends CardRenderer { if (artImage != null && !cardView.isFaceDown()) { boolean useFaceArt = false; - if (faceArtImage != null) { + if (faceArtImage != null && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { useFaceArt = true; } @@ -395,17 +398,22 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); - } else { + } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { drawArtIntoRect(g, totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); + } else { + /* drawArtIntoRect(g, + totalContentInset + 1, totalContentInset + boxHeight, + contentWidth - 2, typeLineY, + sourceRect, shouldPreserveAspect);*/ } } } @Override - protected void drawFrame(Graphics2D g) { + protected void drawFrame(Graphics2D g, BufferedImage image) { // Get the card colors to base the frame on ObjectColor frameColors = getFrameObjectColor(); @@ -421,12 +429,13 @@ public class ModernCardRenderer extends CardRenderer { // Draw the main card content border g.setPaint(borderPaint); + if (cardView.getFrameStyle() == FrameStyle.KLD_INVENTION) { g.drawImage(FRAME_INVENTION, 0, 0, cardWidth, cardHeight, null); g.drawRect( totalContentInset, typeLineY, contentWidth - 1, cardHeight - borderWidth * 3 - typeLineY - 1); - } else { + } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { g.drawRect( totalContentInset, totalContentInset, contentWidth - 1, cardHeight - borderWidth * 3 - totalContentInset - 1); @@ -437,11 +446,13 @@ public class ModernCardRenderer extends CardRenderer { g.setPaint(new Color(255, 255, 255, 150)); } else { g.setPaint(textboxPaint); - } - g.fillRect( - totalContentInset + 1, typeLineY, - contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); + + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + g.fillRect( + totalContentInset + 1, typeLineY, + contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); + } // If it's a planeswalker, extend the textbox left border by some if (cardView.isPlanesWalker()) { @@ -451,7 +462,7 @@ public class ModernCardRenderer extends CardRenderer { cardWidth / 16, cardHeight - typeLineY - boxHeight - borderWidth * 3); } - if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION) { + if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { // Draw a shadow highlight at the right edge of the content frame g.setColor(new Color(0, 0, 0, 100)); g.fillRect( @@ -470,26 +481,31 @@ public class ModernCardRenderer extends CardRenderer { cardWidth - 2 * borderWidth, boxHeight, contentInset, borderPaint, boxColor); - // Draw the type line box - CardRendererUtils.drawRoundedBox(g, - borderWidth, typeLineY, - cardWidth - 2 * borderWidth, boxHeight, - contentInset, - borderPaint, boxColor); + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + CardRendererUtils.drawRoundedBox(g, + borderWidth, typeLineY, + cardWidth - 2 * borderWidth, boxHeight, + contentInset, + borderPaint, boxColor); - // Draw a small separator between the type line and box, and shadow - // at the left of the texbox, and above the name line - g.setColor(new Color(0, 0, 0, 150)); - g.fillRect( - totalContentInset - 1, totalContentInset - 1, - contentWidth + 1, 1); - g.fillRect( - totalContentInset + 1, typeLineY + boxHeight, - contentWidth - 2, 1); - g.fillRect( - cardWidth - totalContentInset - 1, typeLineY + boxHeight, - 1, cardHeight - borderWidth * 3 - typeLineY - boxHeight); + // Draw a small separator between the type line and box, and shadow + // at the left of the texbox, and above the name line + g.setColor(new Color(0, 0, 0, 150)); + g.fillRect( + totalContentInset - 1, totalContentInset - 1, + contentWidth + 1, 1); + g.fillRect( + totalContentInset + 1, typeLineY + boxHeight, + contentWidth - 2, 1); + g.fillRect( + cardWidth - totalContentInset - 1, typeLineY + boxHeight, + 1, cardHeight - borderWidth * 3 - typeLineY - boxHeight); + // Draw the type line + drawTypeLine(g, getCardTypeLine(), + totalContentInset, typeLineY, + contentWidth, boxHeight, true); + } // Draw the transform circle int nameOffset = drawTransformationCircle(g, borderPaint); @@ -502,20 +518,115 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + nameOffset, totalContentInset, contentWidth - nameOffset, boxHeight); - // Draw the type line - drawTypeLine(g, getCardTypeLine(), - totalContentInset, typeLineY, - contentWidth, boxHeight); - // Draw the textbox rules - drawRulesText(g, textboxKeywords, textboxRules, - totalContentInset + 2, typeLineY + boxHeight + 2, - contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + drawRulesText(g, textboxKeywords, textboxRules, + totalContentInset + 2, typeLineY + boxHeight + 2, + contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); + } else { + int x = totalContentInset; + int y = typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset; + int w = contentWidth - 2; + int h = boxHeight - 4; + + CardRendererUtils.drawZendikarLandBox(g, + x, y, w, h, + contentInset, + borderPaint, boxColor); + drawTypeLine(g, getCardSuperTypeLine(), + totalContentInset + 2, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + contentWidth / 2 - boxHeight, boxHeight - 4, false); + drawTypeLine(g, getCardSubTypeLine(), + totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + 3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true); + + // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... + int x2 = x; + int y2 = y; + int topxdelta = 45 * contentWidth / 1000; + int botxdelta = 58 * contentWidth / 1000; + int ctrlx = 0; + int ctrly = (totalContentInset + y2) / 2; + + drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, + x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, + cardWidth, cardHeight); + + QuadCurve2D q = new QuadCurve2D.Float(); + q.setCurve(x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2); + g.setColor(boxColor); + g.setPaint(borderPaint); + g.draw(q); + q.setCurve(x + topxdelta - 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta - 1, y2); + g.draw(q); + q.setCurve(x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2); + g.draw(q); + q.setCurve(x + contentWidth - topxdelta + 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta + 1, y2); + g.draw(q); + + g.setColor(Color.BLACK); + q.setCurve(x + topxdelta + 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta + 1, y2 - 1); + g.draw(q); + q.setCurve(x + contentWidth - topxdelta - 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta - 1, y2 - 1); + g.draw(q); + + drawRulesText(g, textboxKeywords, textboxRules, + x, y, + w, h); + } // Draw the bottom right stuff drawBottomRight(g, borderPaint, boxColor); } + public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int ctrlx, int ctrly, int w, int h, + int x2, int y2, int ctrlx2, int ctrly2, int w2, int h2, + int cardWidth, int cardHeight) { + + BufferedImage artToUse = faceArtImage; + if (faceArtImage == null) { + if (artImage == null) { + return; + } + artToUse = artImage; + } + int srcW = artToUse.getWidth(); + int srcH = artToUse.getHeight(); + + Rectangle2D rect = new Rectangle2D.Float(); + rect.setRect(0, 0, srcW, srcH); + g2.clip(rect); + + QuadCurve2D q = new QuadCurve2D.Float(); + q.setCurve(x, y, ctrlx, ctrly, w, h); + g2.setClip(null); + g2.clip(q); + //g2.drawImage(faceArtImage, x, y, (x2-x), (h2-y), null); + Rectangle2D r = q.getBounds2D(); + int minX = (int) r.getX(); + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + + QuadCurve2D q2 = new QuadCurve2D.Float(); + q2.setCurve(x2, y2, ctrlx2, ctrly2, w2, h2); + g2.setClip(null); + g2.clip(q2); + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + + Polygon p = new Polygon(); + //x = 10; + //y = 0; + p.addPoint(x, y); + p.addPoint(x2, y); + p.addPoint(w2, h2); + p.addPoint(w, h2); + p.addPoint(x, y); + g2.setClip(null); + g2.clip(p); + + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + g2.setClip(null); + } + // Draw the name line protected void drawNameLine(Graphics2D g, String baseName, String manaCost, int x, int y, int w, int h) { // Width of the mana symbols @@ -566,13 +677,13 @@ public class ModernCardRenderer extends CardRenderer { } // Draw the type line (color indicator, types, and expansion symbol) - protected void drawTypeLine(Graphics2D g, String baseTypeLine, int x, int y, int w, int h) { + protected void drawTypeLine(Graphics2D g, String baseTypeLine, int x, int y, int w, int h, boolean withSymbol) { // Draw expansion symbol - int expansionSymbolWidth; + int expansionSymbolWidth = 0; if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_SET_SYMBOL, "false").equals("false")) { if (cardView.isAbility()) { expansionSymbolWidth = 0; - } else { + } else if (withSymbol) { expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h); } } else { @@ -792,9 +903,20 @@ public class ModernCardRenderer extends CardRenderer { } // Basic mana draw mana symbol in textbox (for basic lands) - if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand()) { - drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); - return; + if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } else // Big circle in the middle for Zendikar lands + if (allRules.size() == 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } else { + if (allRules.size() > 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + } + return; + } } // Go through possible font sizes in descending order to find the best fit @@ -847,6 +969,11 @@ public class ModernCardRenderer extends CardRenderer { ManaSymbols.draw(g, symbs, x + (w - manaCostWidth) / 2, y + (h - symbHeight) / 2, symbHeight, Color.black, 2); } + private void drawBasicManaSymbol(Graphics2D g, int x, int y, int w, int h, String symbol) { + String symbs = symbol; + ManaSymbols.draw(g, symbs, x, y, w, Color.black, 2); + } + // Get the first line of the textbox, the keyword string private static String getKeywordRulesString(ArrayList keywords) { StringBuilder builder = new StringBuilder(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java index 98d01254c6d..0310d8e3acc 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java @@ -8,6 +8,7 @@ import mage.view.CardView; import java.awt.*; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; @@ -17,6 +18,7 @@ import java.util.List; public class ModernSplitCardRenderer extends ModernCardRenderer { private class HalfCardProps { + int x, y, w, h, cw, ch; String name; @@ -27,7 +29,11 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { ArrayList keywords = new ArrayList<>(); } - private static ArrayList ONLY_LAND_TYPE = new ArrayList() {{add(CardType.LAND);}}; + private static ArrayList ONLY_LAND_TYPE = new ArrayList() { + { + add(CardType.LAND); + } + }; // Right and left halves of the card content private HalfCardProps rightHalf = new HalfCardProps(); @@ -88,20 +94,20 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Decide size of divider if (isAftermath()) { dividerSize = borderWidth; - dividerAt = (int)(cardHeight*0.54); + dividerAt = (int) (cardHeight * 0.54); } else { - int availHeight = cardHeight - totalContentInset - 3*borderWidth; - dividerSize = borderWidth*2; - dividerAt = (int)(totalContentInset + availHeight * 0.5 - borderWidth); + int availHeight = cardHeight - totalContentInset - 3 * borderWidth; + dividerSize = borderWidth * 2; + dividerAt = (int) (totalContentInset + availHeight * 0.5 - borderWidth); } // Decide size of each halves box rightHalf.x = leftHalf.x = totalContentInset; - rightHalf.w = leftHalf.w = cardWidth - 2*totalContentInset; + rightHalf.w = leftHalf.w = cardWidth - 2 * totalContentInset; leftHalf.y = totalContentInset; leftHalf.h = dividerAt - totalContentInset; rightHalf.y = dividerAt + dividerSize; - rightHalf.h = cardHeight - rightHalf.y - borderWidth*3; + rightHalf.h = cardHeight - rightHalf.y - borderWidth * 3; // Content width / height (Exchanged from width / height if the card part is rotated) if (isAftermath()) { @@ -126,7 +132,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { private ObjectColor getColorFromManaCostHack(ManaCosts costs) { ObjectColor c = new ObjectColor(); List symbols = costs.getSymbols(); - for (String symbol: symbols) { + for (String symbol : symbols) { if (symbol.contains("W")) { c.setWhite(true); } else if (symbol.contains("U")) { @@ -154,18 +160,18 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw main part (most of card) g.fillRoundRect( borderWidth, borderWidth, - cardWidth - 2*borderWidth, leftHalf.h + contentInset - borderWidth - 2*cornerRadius + (cornerRadius - 1), + cardWidth - 2 * borderWidth, leftHalf.h + contentInset - borderWidth - 2 * cornerRadius + (cornerRadius - 1), cornerRadius - 1, cornerRadius - 1); // Draw the M15 rounded "swoosh" at the bottom g.fillRoundRect( - borderWidth, dividerAt - borderWidth - 4*cornerRadius, - cardWidth - 2*borderWidth, cornerRadius * 4, + borderWidth, dividerAt - borderWidth - 4 * cornerRadius, + cardWidth - 2 * borderWidth, cornerRadius * 4, cornerRadius * 2, cornerRadius * 2); // Draw the cutout into the "swoosh" for the textbox to lie over g.fillRect( - borderWidth + contentInset, dividerAt - 2*borderWidth, + borderWidth + contentInset, dividerAt - 2 * borderWidth, cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); } @@ -176,8 +182,8 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw the M15 rounded "swoosh"es at the top and bottom g.fillRoundRect( borderWidth, dividerAt + dividerSize + borderWidth, - cardWidth - 2*borderWidth, rightHalf.h - 2*borderWidth, - cornerRadius*2, cornerRadius*2); + cardWidth - 2 * borderWidth, rightHalf.h - 2 * borderWidth, + cornerRadius * 2, cornerRadius * 2); // Draw the cutout into the "swoosh" for the textbox to lie over g.fillRect( @@ -236,8 +242,8 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Background of textbox g.setPaint(textboxPaint); g.fillRect( - 1, typeLineY, - half.cw - 2, half.ch - typeLineY - 1); + 1, typeLineY, + half.cw - 2, half.ch - typeLineY - 1); // Draw the name line box CardRendererUtils.drawRoundedBox(g, @@ -261,7 +267,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw the type line drawTypeLine(g, half.typeLineString, 0, typeLineY, - half.cw, boxHeight - 4); + half.cw, boxHeight - 4, true); // Draw the textbox rules drawRulesText(g, half.keywords, half.rules, @@ -270,13 +276,13 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } private Graphics2D getUnmodifiedHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(leftHalf.x, leftHalf.y); return g2; } private Graphics2D getAftermathHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(rightHalf.x, rightHalf.y); g2.rotate(Math.PI / 2); g2.translate(0, -rightHalf.w); @@ -284,7 +290,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } private Graphics2D getLeftHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(leftHalf.x, leftHalf.y); g2.rotate(-Math.PI / 2); g2.translate(-leftHalf.cw, 0); @@ -292,7 +298,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } private Graphics2D getRightHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(rightHalf.x, rightHalf.y); g2.rotate(-Math.PI / 2); g2.translate(-rightHalf.cw, 0); @@ -300,13 +306,13 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } @Override - protected void drawFrame(Graphics2D g) { + protected void drawFrame(Graphics2D g, BufferedImage image) { if (isAftermath()) { - drawSplitHalfFrame(getUnmodifiedHalfContext(g), leftHalf, (int)(leftHalf.ch * TYPE_LINE_Y_FRAC)); + drawSplitHalfFrame(getUnmodifiedHalfContext(g), leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC)); drawSplitHalfFrame(getAftermathHalfContext(g), rightHalf, (rightHalf.ch - boxHeight) / 2); } else { - drawSplitHalfFrame(getLeftHalfContext(g), leftHalf, (int)(leftHalf.ch * TYPE_LINE_Y_FRAC)); - drawSplitHalfFrame(getRightHalfContext(g), rightHalf, (int)(rightHalf.ch * TYPE_LINE_Y_FRAC)); + drawSplitHalfFrame(getLeftHalfContext(g), leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC)); + drawSplitHalfFrame(getRightHalfContext(g), rightHalf, (int) (rightHalf.ch * TYPE_LINE_Y_FRAC)); if (isFuse()) { Graphics2D g2 = getRightHalfContext(g); int totalFuseBoxWidth = rightHalf.cw * 2 + 2 * borderWidth + dividerSize; @@ -319,7 +325,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { borderPaint, boxColor); drawNameLine(g2, "Fuse (You may cast both halves from your hand)", "", 0, rightHalf.ch, - totalFuseBoxWidth - 2*borderWidth, boxHeight); + totalFuseBoxWidth - 2 * borderWidth, boxHeight); } } } From afeb1c16ac962a16939a5235b5659b38646f2da1 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 26 Feb 2018 15:22:59 +1100 Subject: [PATCH 110/133] Adding in way to render Zendikar full art lands. Things still to do: 1) Get everyone to download the full art Face images 2) Add in the collector number to the Face images (/FACE/ZEN/Island..jpg for example) 3) Add in the ability to draw say BFZ / HOU full art lands as well. 4) Maybe even add in an UST (Unstable) way of rendering lands (with the swoosh of opaque full art down the bottom). --- .../java/org/mage/card/arcane/ModernCardRenderer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 16c0fc996b3..3f04098a7b6 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -584,14 +584,25 @@ public class ModernCardRenderer extends CardRenderer { int cardWidth, int cardHeight) { BufferedImage artToUse = faceArtImage; + boolean hadToUseFullArt = false; if (faceArtImage == null) { if (artImage == null) { return; } + hadToUseFullArt = true; artToUse = artImage; } int srcW = artToUse.getWidth(); int srcH = artToUse.getHeight(); + + if (hadToUseFullArt) { + // Get a box based on the standard scan from gatherer. + // Width = 185/223 pixels (centered) + // Height = 220/310, 38 pixels from top + int subx = 19 * srcW / 223; + int suby = 38 * srcH / 310; + artToUse = artImage.getSubimage(subx, suby, 185*srcW / 223, 220*srcH / 310); + } Rectangle2D rect = new Rectangle2D.Float(); rect.setRect(0, 0, srcW, srcH); From 1dd88b0ea77f5ac684cc8ab89617054980e5328e Mon Sep 17 00:00:00 2001 From: lcoviedo Date: Mon, 26 Feb 2018 12:27:19 +0000 Subject: [PATCH 111/133] spells cost reduction text --- Mage.Sets/src/mage/cards/t/TheImmortalSun.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java index 4b1fa19c8a7..8b303e3e38e 100644 --- a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java +++ b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java @@ -68,7 +68,7 @@ public class TheImmortalSun extends CardImpl { this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1) .setText("draw an additional card"), TargetController.YOU, false)); // Spells you cast cost {1} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCard("Spells you cast"), 1))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCard("Spells"), 1))); // Creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield))); } From aeb1fa6bb3e906218ee5176d0cecc0b546bb93a6 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 26 Feb 2018 16:48:38 +0400 Subject: [PATCH 112/133] Card text fixes --- Mage.Sets/src/mage/cards/c/ConduitOfRuin.java | 2 +- Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java | 2 +- Mage.Sets/src/mage/cards/j/JacesSanctum.java | 2 +- Mage.Sets/src/mage/cards/k/KrosanDrover.java | 5 ++++- Mage.Sets/src/mage/cards/m/ManaMatrix.java | 2 +- Mage.Sets/src/mage/cards/p/PlanarGate.java | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java index b24a933ae8b..5ea158c1f41 100644 --- a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java +++ b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java @@ -81,7 +81,7 @@ public class ConduitOfRuin extends CardImpl { // The first creature spell you cast each turn costs {2} less to cast. Effect effect = new SpellsCostReductionControllerEffect(filterCost, 2); - effect.setText("The first creature spell you cast each turn costs {2} less to cast"); + effect.setText("The first creature spell you cast each turn costs {2} less to cast."); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect), new ConduitOfRuinWatcher()); } diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java b/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java index 6c31a6de0a8..4fdc3378418 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java @@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; */ public class HeraldOfThePantheon extends CardImpl { - private static final FilterCard filter = new FilterCard("enchantment spells"); + private static final FilterCard filter = new FilterCard("Enchantment spells"); private static final FilterSpell filter2 = new FilterSpell("an enchantment spell"); static { diff --git a/Mage.Sets/src/mage/cards/j/JacesSanctum.java b/Mage.Sets/src/mage/cards/j/JacesSanctum.java index 717038e90b0..0acd517e84e 100644 --- a/Mage.Sets/src/mage/cards/j/JacesSanctum.java +++ b/Mage.Sets/src/mage/cards/j/JacesSanctum.java @@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; public class JacesSanctum extends CardImpl { - private static final FilterCard filter = new FilterCard("instant and sorcery spells"); + private static final FilterCard filter = new FilterCard("Instant and sorcery spells"); private static final FilterSpell filter2 = new FilterSpell("an instant or sorcery spell"); diff --git a/Mage.Sets/src/mage/cards/k/KrosanDrover.java b/Mage.Sets/src/mage/cards/k/KrosanDrover.java index b8237a2bebd..7bd3c7d3411 100644 --- a/Mage.Sets/src/mage/cards/k/KrosanDrover.java +++ b/Mage.Sets/src/mage/cards/k/KrosanDrover.java @@ -30,6 +30,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -61,7 +62,9 @@ public class KrosanDrover extends CardImpl { this.toughness = new MageInt(2); // Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 2))); + Effect effect = new SpellsCostReductionControllerEffect(filter, 2); + effect.setText("Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast."); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public KrosanDrover(final KrosanDrover card) { diff --git a/Mage.Sets/src/mage/cards/m/ManaMatrix.java b/Mage.Sets/src/mage/cards/m/ManaMatrix.java index e2e0fac2c55..1777b831fd3 100644 --- a/Mage.Sets/src/mage/cards/m/ManaMatrix.java +++ b/Mage.Sets/src/mage/cards/m/ManaMatrix.java @@ -44,7 +44,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; */ public class ManaMatrix extends CardImpl { - private static final FilterCard filter = new FilterCard("instant and enchantment spells"); + private static final FilterCard filter = new FilterCard("Instant and enchantment spells"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/p/PlanarGate.java b/Mage.Sets/src/mage/cards/p/PlanarGate.java index 8ef656f71f7..42cea9d0df4 100644 --- a/Mage.Sets/src/mage/cards/p/PlanarGate.java +++ b/Mage.Sets/src/mage/cards/p/PlanarGate.java @@ -46,7 +46,7 @@ public class PlanarGate extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}"); // Creature spells you cast cost up to {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCreatureCard("creature spells"), 2, true))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCreatureCard("Creature spells"), 2, true))); } public PlanarGate(final PlanarGate card) { From 5f98cb685e6dbfae3e45ab852863fd23798457e2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 26 Feb 2018 16:17:16 +0100 Subject: [PATCH 113/133] * Hushwing Gryff - Fixed that log message was sent out too often (fixes #4548). --- .../src/mage/cards/a/AmbuscadeShaman.java | 12 +++++------- Mage.Sets/src/mage/cards/h/HushwingGryff.java | 18 ++++++++++++------ Mage.Sets/src/mage/cards/t/TorporOrb.java | 16 +++++++++++----- .../test/cards/replacement/TorporOrbTest.java | 3 ++- .../java/mage/abilities/TriggeredAbility.java | 11 +++++++++++ 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java index ec6cf2b57af..ce6a6d5522a 100644 --- a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java +++ b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java @@ -51,17 +51,17 @@ import mage.target.targetpointer.FixedTarget; public class AmbuscadeShaman extends CardImpl { public AmbuscadeShaman(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ORC); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Ambuscade Shaman or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn. - Effect effect = new BoostTargetEffect(2,2, Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); effect.setText("that creature gets +2/+2 until end of turn"); this.addAbility(new AmbuscadeShamanTriggeredAbility(effect)); - + // Dash {3}{B} (You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)); this.addAbility(new DashAbility(this, "{3}{B}")); @@ -103,9 +103,7 @@ class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl { Permanent permanent = game.getPermanent(targetId); if (permanent.getControllerId().equals(this.controllerId) && permanent.isCreature()) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } return false; @@ -115,4 +113,4 @@ class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever {this} or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/h/HushwingGryff.java b/Mage.Sets/src/mage/cards/h/HushwingGryff.java index b3316468dcc..97eac840847 100644 --- a/Mage.Sets/src/mage/cards/h/HushwingGryff.java +++ b/Mage.Sets/src/mage/cards/h/HushwingGryff.java @@ -31,6 +31,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.keyword.FlashAbility; @@ -39,9 +40,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; @@ -55,7 +56,7 @@ import mage.game.permanent.Permanent; public class HushwingGryff extends CardImpl { public HushwingGryff(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HIPPOGRIFF); this.power = new MageInt(2); @@ -92,10 +93,15 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(event.getSourceId()); + MageObject enteringObject = game.getObject(event.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (mageObject != null && sourceObject != null) { - return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger"; + Ability ability = (Ability) getValue("targetAbility"); + if (enteringObject != null && sourceObject != null && ability != null) { + MageObject abilitObject = game.getObject(ability.getSourceId()); + if (abilitObject != null) { + return sourceObject.getLogName() + " prevented ability of " + abilitObject.getLogName() + + " to trigger for " + enteringObject.getLogName() + " entering the battlefield."; + } } return null; } @@ -111,7 +117,7 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { if (ability != null && ability.getAbilityType() == AbilityType.TRIGGERED) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); if (permanent != null && permanent.isCreature()) { - return true; + return (((TriggeredAbility) ability).checkTrigger(event, game)); } } return false; diff --git a/Mage.Sets/src/mage/cards/t/TorporOrb.java b/Mage.Sets/src/mage/cards/t/TorporOrb.java index 8aafc5c2971..8375b7c62af 100644 --- a/Mage.Sets/src/mage/cards/t/TorporOrb.java +++ b/Mage.Sets/src/mage/cards/t/TorporOrb.java @@ -30,6 +30,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.cards.CardImpl; @@ -51,7 +52,7 @@ import mage.game.permanent.Permanent; public class TorporOrb extends CardImpl { public TorporOrb(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Creatures entering the battlefield don't cause abilities to trigger. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TorporOrbEffect())); @@ -89,7 +90,7 @@ class TorporOrbEffect extends ContinuousRuleModifyingEffectImpl { if (ability != null && ability.getAbilityType() == AbilityType.TRIGGERED) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); if (permanent != null && permanent.isCreature()) { - return true; + return (((TriggeredAbility) ability).checkTrigger(event, game)); } } return false; @@ -97,10 +98,15 @@ class TorporOrbEffect extends ContinuousRuleModifyingEffectImpl { @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(event.getSourceId()); + MageObject enteringObject = game.getObject(event.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (mageObject != null && sourceObject != null) { - return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger"; + Ability ability = (Ability) getValue("targetAbility"); + if (enteringObject != null && sourceObject != null && ability != null) { + MageObject abilitObject = game.getObject(ability.getSourceId()); + if (abilitObject != null) { + return sourceObject.getLogName() + " prevented ability of " + abilitObject.getLogName() + + " to trigger for " + enteringObject.getLogName() + " entering the battlefield."; + } } return null; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java index c8694f3540b..36b5edb5309 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java @@ -41,7 +41,8 @@ public class TorporOrbTest extends CardTestPlayerBase { */ @Test public void testPitTweller() { - addCard(Zone.BATTLEFIELD, playerB, "Torpor Orb"); + // Creatures entering the battlefield don't cause abilities to trigger. + addCard(Zone.BATTLEFIELD, playerB, "Hushwing Gryff"); addCard(Zone.BATTLEFIELD, playerB, "Treacherous Pit-Dweller"); // 4/3 addCard(Zone.HAND, playerA, "Lightning Bolt"); diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index 638079c1e35..93ca77a32c5 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -50,6 +50,17 @@ public interface TriggeredAbility extends Ability { */ boolean checkEventType(GameEvent event, Game game); + /** + * This method checks if the event has to trigger the ability. It's + * important to do nothing unique within this method, that can't be done + * multiple times. Because some abilities call this to check if an ability + * is relevant (e.g. Torpor Orb), so the method is calle dmultiple times for + * the same event. + * + * @param event + * @param game + * @return + */ boolean checkTrigger(GameEvent event, Game game); boolean checkInterveningIfClause(Game game); From fe3a023c36e5e925ee257184ac0f43fca2b381f6 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 26 Feb 2018 11:18:17 -0600 Subject: [PATCH 114/133] - Fixed Djinn Illuminatus #4552 --- Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java b/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java index 94c30a7febe..4eb5d4f1f04 100644 --- a/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java +++ b/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java @@ -108,7 +108,8 @@ class DjinnIlluminatusGainReplicateEffect extends ContinuousEffectImpl { if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId()) - && djinn.getControllerId().equals(source.getControllerId())) { // verify that the controller of the djinn cast that spell + && djinn.getControllerId().equals(source.getControllerId()) // verify that the controller of the djinn cast that spell + && !stackObject.getManaCost().isEmpty()) { //handle cases like Ancestral Vision Spell spell = (Spell) stackObject; if (filter.match(stackObject, game)) { ReplicateAbility replicateAbility = replicateAbilities.computeIfAbsent(spell.getId(), k -> new ReplicateAbility(spell.getCard(), spell.getSpellAbility().getManaCosts().getText())); From a28e273dcc42368a50ac2d7e577fdf01af7981a9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Wed, 28 Feb 2018 00:02:14 +1100 Subject: [PATCH 115/133] Modifying zendikar curved box to use a path instead. (Will be easier to then draw BFZ lands similarly). --- .../mage/card/arcane/ModernCardRenderer.java | 98 ++++++++----------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 3f04098a7b6..51e79577593 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -7,7 +7,7 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; -import java.awt.geom.QuadCurve2D; +import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; @@ -550,25 +550,7 @@ public class ModernCardRenderer extends CardRenderer { drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, - cardWidth, cardHeight); - - QuadCurve2D q = new QuadCurve2D.Float(); - q.setCurve(x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2); - g.setColor(boxColor); - g.setPaint(borderPaint); - g.draw(q); - q.setCurve(x + topxdelta - 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta - 1, y2); - g.draw(q); - q.setCurve(x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2); - g.draw(q); - q.setCurve(x + contentWidth - topxdelta + 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta + 1, y2); - g.draw(q); - - g.setColor(Color.BLACK); - q.setCurve(x + topxdelta + 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta + 1, y2 - 1); - g.draw(q); - q.setCurve(x + contentWidth - topxdelta - 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta - 1, y2 - 1); - g.draw(q); + boxColor, borderPaint); drawRulesText(g, textboxKeywords, textboxRules, x, y, @@ -581,61 +563,57 @@ public class ModernCardRenderer extends CardRenderer { public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int ctrlx, int ctrly, int w, int h, int x2, int y2, int ctrlx2, int ctrly2, int w2, int h2, - int cardWidth, int cardHeight) { + Color boxColor, Paint paint) { BufferedImage artToUse = faceArtImage; boolean hadToUseFullArt = false; if (faceArtImage == null) { if (artImage == null) { return; - } + } hadToUseFullArt = true; artToUse = artImage; } int srcW = artToUse.getWidth(); int srcH = artToUse.getHeight(); - + if (hadToUseFullArt) { // Get a box based on the standard scan from gatherer. // Width = 185/223 pixels (centered) // Height = 220/310, 38 pixels from top - int subx = 19 * srcW / 223; - int suby = 38 * srcH / 310; - artToUse = artImage.getSubimage(subx, suby, 185*srcW / 223, 220*srcH / 310); + int subx = 19 * srcW / 223; + int suby = 38 * srcH / 310; + artToUse = artImage.getSubimage(subx, suby, 185 * srcW / 223, 220 * srcH / 310); } - Rectangle2D rect = new Rectangle2D.Float(); - rect.setRect(0, 0, srcW, srcH); - g2.clip(rect); + Path2D.Double curve = new Path2D.Double(); + curve.moveTo(x, y); + curve.quadTo(ctrlx, ctrly, w, h); + curve.lineTo(w2, h); + curve.quadTo(ctrlx2, ctrly2, x2, y); + curve.lineTo(x, y); - QuadCurve2D q = new QuadCurve2D.Float(); - q.setCurve(x, y, ctrlx, ctrly, w, h); - g2.setClip(null); - g2.clip(q); - //g2.drawImage(faceArtImage, x, y, (x2-x), (h2-y), null); - Rectangle2D r = q.getBounds2D(); + Path2D.Double innercurve = new Path2D.Double(); + innercurve.moveTo(x + 1, y + 1); + innercurve.quadTo(ctrlx + 1, ctrly + 1, w + 1, h - 1); + innercurve.lineTo(w2 - 1, h - 1); + innercurve.quadTo(ctrlx2 - 1, ctrly2 - 1, x2 - 1, y + 1); + innercurve.lineTo(x + 1, y + 1); + + Rectangle2D r = curve.getBounds2D(); int minX = (int) r.getX(); + + g2.setClip(innercurve); g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); - QuadCurve2D q2 = new QuadCurve2D.Float(); - q2.setCurve(x2, y2, ctrlx2, ctrly2, w2, h2); g2.setClip(null); - g2.clip(q2); - g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + g2.setColor(CardRendererUtils.abitdarker(boxColor)); + g2.setPaint(paint); + g2.draw(curve); - Polygon p = new Polygon(); - //x = 10; - //y = 0; - p.addPoint(x, y); - p.addPoint(x2, y); - p.addPoint(w2, h2); - p.addPoint(w, h2); - p.addPoint(x, y); - g2.setClip(null); - g2.clip(p); - - g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); - g2.setClip(null); + g2.setColor(Color.black); + //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); + g2.draw(innercurve); } // Draw the name line @@ -919,14 +897,16 @@ public class ModernCardRenderer extends CardRenderer { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands - if (allRules.size() == 1) { - drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); - return; - } else { - if (allRules.size() > 1) { - drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + { + if (allRules.size() == 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } else { + if (allRules.size() > 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + } + return; } - return; } } From 596ca5be867db81f46082827fe46c2dcb6de27de Mon Sep 17 00:00:00 2001 From: spjspj Date: Wed, 28 Feb 2018 01:25:29 +1100 Subject: [PATCH 116/133] Adding in way to render BFZ style full art lands (BFZ and HOU full art lands) Things still to do: 1) Get everyone to download the full art Face images ... 2) Add in the collector number to the Face images (/FACE/ZEN/Island..jpg for example) 4) Maybe even add in an UST (Unstable) way of rendering lands (with the swoosh of opaque full art down the bottom). --- .../mage/card/arcane/ModernCardRenderer.java | 120 ++++++++++++++---- 1 file changed, 95 insertions(+), 25 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 51e79577593..3cd5cbe0bd0 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -313,7 +313,7 @@ public class ModernCardRenderer extends CardRenderer { Rectangle2D rect; if (useInventionFrame()) { rect = new Rectangle2D.Float(0, 0, 1, 1); - } else if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + } else if (isZendikarFullArtLand()) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .84f); } else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f); @@ -332,6 +332,10 @@ public class ModernCardRenderer extends CardRenderer { return TYPE_LINE_Y_FRAC; } } + + private boolean isZendikarFullArtLand() { + return cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC; + } protected boolean isSourceArtFullArt() { int color = artImage.getRGB(0, artImage.getHeight() / 2); @@ -355,7 +359,7 @@ public class ModernCardRenderer extends CardRenderer { if (artImage != null && !cardView.isFaceDown()) { boolean useFaceArt = false; - if (faceArtImage != null && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (faceArtImage != null && !isZendikarFullArtLand()) { useFaceArt = true; } @@ -398,16 +402,11 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); - } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + } else if (!isZendikarFullArtLand()) { drawArtIntoRect(g, totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); - } else { - /* drawArtIntoRect(g, - totalContentInset + 1, totalContentInset + boxHeight, - contentWidth - 2, typeLineY, - sourceRect, shouldPreserveAspect);*/ } } } @@ -435,7 +434,7 @@ public class ModernCardRenderer extends CardRenderer { g.drawRect( totalContentInset, typeLineY, contentWidth - 1, cardHeight - borderWidth * 3 - typeLineY - 1); - } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + } else if (!isZendikarFullArtLand()) { g.drawRect( totalContentInset, totalContentInset, contentWidth - 1, cardHeight - borderWidth * 3 - totalContentInset - 1); @@ -448,7 +447,7 @@ public class ModernCardRenderer extends CardRenderer { g.setPaint(textboxPaint); } - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (!isZendikarFullArtLand()) { g.fillRect( totalContentInset + 1, typeLineY, contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); @@ -462,7 +461,7 @@ public class ModernCardRenderer extends CardRenderer { cardWidth / 16, cardHeight - typeLineY - boxHeight - borderWidth * 3); } - if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && !isZendikarFullArtLand()) { // Draw a shadow highlight at the right edge of the content frame g.setColor(new Color(0, 0, 0, 100)); g.fillRect( @@ -482,7 +481,7 @@ public class ModernCardRenderer extends CardRenderer { contentInset, borderPaint, boxColor); // Draw the type line box - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (!isZendikarFullArtLand()) { CardRendererUtils.drawRoundedBox(g, borderWidth, typeLineY, cardWidth - 2 * borderWidth, boxHeight, @@ -519,7 +518,7 @@ public class ModernCardRenderer extends CardRenderer { contentWidth - nameOffset, boxHeight); // Draw the textbox rules - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (!isZendikarFullArtLand()) { drawRulesText(g, textboxKeywords, textboxRules, totalContentInset + 2, typeLineY + boxHeight + 2, contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); @@ -540,17 +539,31 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, 3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true); - // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... - int x2 = x; - int y2 = y; - int topxdelta = 45 * contentWidth / 1000; - int botxdelta = 58 * contentWidth / 1000; - int ctrlx = 0; - int ctrly = (totalContentInset + y2) / 2; + if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... + int x2 = x; + int y2 = y; + int topxdelta = 45 * contentWidth / 1000; + int botxdelta = 58 * contentWidth / 1000; + int ctrlx = 0; + int ctrly = (totalContentInset + y2) / 2; - drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, - x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, - boxColor, borderPaint); + drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, + x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, + boxColor, borderPaint); + } else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) { + // Draw curved lines (BFZ land style) + int y2 = y; + int yb = totalContentInset + boxHeight; + int topxdelta = 45 * contentWidth / 1000; + int endydelta = 60 * (totalContentInset + y2) / 265; + int x2 = x + contentWidth; + + // Curve ends at 60 out of 265 + drawBFZCurvedFace(g, image, x, yb, x2, y2, + topxdelta, endydelta, + boxColor, borderPaint); + } drawRulesText(g, textboxKeywords, textboxRules, x, y, @@ -615,6 +628,63 @@ public class ModernCardRenderer extends CardRenderer { //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); g2.draw(innercurve); } + + public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, + int topxdelta, int endydelta, + Color boxColor, Paint paint) { + BufferedImage artToUse = faceArtImage; + boolean hadToUseFullArt = false; + if (faceArtImage == null) { + if (artImage == null) { + return; + } + hadToUseFullArt = true; + artToUse = artImage; + } + int srcW = artToUse.getWidth(); + int srcH = artToUse.getHeight(); + + if (hadToUseFullArt) { + // Get a box based on the standard scan from gatherer. + // Width = 185/223 pixels (centered) + // Height = 220/310, 38 pixels from top + int subx = 19 * srcW / 223; + int suby = 38 * srcH / 310; + artToUse = artImage.getSubimage(subx, suby, 185 * srcW / 223, 220 * srcH / 310); + } + + Path2D.Double curve = new Path2D.Double(); + curve.moveTo(x + topxdelta, y); + curve.quadTo(x, y + endydelta / 2, x, y + endydelta); + curve.lineTo(x, y2); + curve.lineTo(x2, y2); + curve.lineTo(x2, y + endydelta); + curve.quadTo(x2, y + endydelta / 2, x2 - topxdelta, y); + curve.lineTo(x + topxdelta, y); + + Path2D.Double innercurve = new Path2D.Double(); + innercurve.moveTo(x + topxdelta, y+1); + innercurve.quadTo(x+1, y + endydelta / 2, x+1, y + endydelta); + innercurve.lineTo(x+1, y2-1); + innercurve.lineTo(x2-1, y2-1); + innercurve.lineTo(x2-1, y + endydelta); + innercurve.quadTo(x2-1, y + endydelta / 2, x2 - topxdelta, y+1); + innercurve.lineTo(x + topxdelta, y+1); + + Rectangle2D r = curve.getBounds2D(); + int minX = (int) r.getX(); + + g2.setClip(curve); + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, y2 - y, null); + + g2.setClip(null); + g2.setColor(CardRendererUtils.abitdarker(boxColor)); + g2.setPaint(paint); + g2.draw(curve); + + g2.setColor(Color.black); + g2.draw(innercurve); + } // Draw the name line protected void drawNameLine(Graphics2D g, String baseName, String manaCost, int x, int y, int w, int h) { @@ -892,8 +962,8 @@ public class ModernCardRenderer extends CardRenderer { } // Basic mana draw mana symbol in textbox (for basic lands) - if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || isZendikarFullArtLand()) { + if (!isZendikarFullArtLand()) { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands From 8887cca3876df941fac42d9788a66cffea3c16fa Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 1 Mar 2018 00:32:30 +1100 Subject: [PATCH 117/133] slightly offcenter --- .../mage/card/arcane/CardRendererUtils.java | 17 ++++++------ .../mage/card/arcane/ModernCardRenderer.java | 27 ++++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index f38d9b0f751..3702f176cb2 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -51,7 +51,7 @@ public final class CardRendererUtils { // Return the buffered image return bimage; } - + public static Color abitbrighter(Color c) { int r = c.getRed(); int g = c.getGreen(); @@ -107,7 +107,7 @@ public final class CardRendererUtils { g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); } - + public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { g.setColor(new Color(0, 0, 0, 150)); @@ -117,25 +117,24 @@ public final class CardRendererUtils { g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); - + // The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4 - g.drawOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); - + g.drawOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); + g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); g.setPaint(fill); - g.setPaint(fill); g.setColor(abitbrighter(g.getColor())); g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); g.setPaint(fill); g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); - + g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); - - g.fillOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); + + g.fillOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); } // Get the width of a mana cost rendered with ManaSymbols.draw diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 3cd5cbe0bd0..08c74ab3244 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -332,7 +332,7 @@ public class ModernCardRenderer extends CardRenderer { return TYPE_LINE_Y_FRAC; } } - + private boolean isZendikarFullArtLand() { return cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC; } @@ -525,7 +525,7 @@ public class ModernCardRenderer extends CardRenderer { } else { int x = totalContentInset; int y = typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset; - int w = contentWidth - 2; + int w = contentWidth; int h = boxHeight - 4; CardRendererUtils.drawZendikarLandBox(g, @@ -558,7 +558,7 @@ public class ModernCardRenderer extends CardRenderer { int topxdelta = 45 * contentWidth / 1000; int endydelta = 60 * (totalContentInset + y2) / 265; int x2 = x + contentWidth; - + // Curve ends at 60 out of 265 drawBFZCurvedFace(g, image, x, yb, x2, y2, topxdelta, endydelta, @@ -628,8 +628,8 @@ public class ModernCardRenderer extends CardRenderer { //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); g2.draw(innercurve); } - - public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, + + public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, int topxdelta, int endydelta, Color boxColor, Paint paint) { BufferedImage artToUse = faceArtImage; @@ -663,13 +663,13 @@ public class ModernCardRenderer extends CardRenderer { curve.lineTo(x + topxdelta, y); Path2D.Double innercurve = new Path2D.Double(); - innercurve.moveTo(x + topxdelta, y+1); - innercurve.quadTo(x+1, y + endydelta / 2, x+1, y + endydelta); - innercurve.lineTo(x+1, y2-1); - innercurve.lineTo(x2-1, y2-1); - innercurve.lineTo(x2-1, y + endydelta); - innercurve.quadTo(x2-1, y + endydelta / 2, x2 - topxdelta, y+1); - innercurve.lineTo(x + topxdelta, y+1); + innercurve.moveTo(x + topxdelta, y + 1); + innercurve.quadTo(x + 1, y + endydelta / 2, x + 1, y + endydelta); + innercurve.lineTo(x + 1, y2 - 1); + innercurve.lineTo(x2 - 1, y2 - 1); + innercurve.lineTo(x2 - 1, y + endydelta); + innercurve.quadTo(x2 - 1, y + endydelta / 2, x2 - topxdelta, y + 1); + innercurve.lineTo(x + topxdelta, y + 1); Rectangle2D r = curve.getBounds2D(); int minX = (int) r.getX(); @@ -969,7 +969,8 @@ public class ModernCardRenderer extends CardRenderer { } else // Big circle in the middle for Zendikar lands { if (allRules.size() == 1) { - drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + // Size of mana symbol = 9/4 * h, 3/4h above line + drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else { if (allRules.size() > 1) { From 8c614a90066724963822ae6e07548f42092c5749 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 1 Mar 2018 00:49:43 +1100 Subject: [PATCH 118/133] slightly offcenter --- .../src/main/java/org/mage/card/arcane/ModernCardRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 08c74ab3244..c65b92e931b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -533,7 +533,7 @@ public class ModernCardRenderer extends CardRenderer { contentInset, borderPaint, boxColor); drawTypeLine(g, getCardSuperTypeLine(), - totalContentInset + 2, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + totalContentInset + contentInset, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, contentWidth / 2 - boxHeight, boxHeight - 4, false); drawTypeLine(g, getCardSubTypeLine(), totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, From 8c8b4ce019e67b69363de3097479edf1c8591288 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 28 Feb 2018 17:24:42 +0100 Subject: [PATCH 119/133] * Added a check if life of players is going down during infinite loop check. Is so no Draw check message is shown (fixes #4557). --- .../mage/cards/n/NivMizzetTheFiremind.java | 9 +- Mage.Sets/src/mage/cards/s/StuffyDoll.java | 6 +- .../java/org/mage/test/player/TestPlayer.java | 98 +++++++++++++++---- Mage/src/main/java/mage/game/GameImpl.java | 20 ++++ 4 files changed, 107 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java b/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java index e4b3cc805c8..486e8cfd29a 100644 --- a/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java +++ b/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java @@ -51,18 +51,23 @@ import mage.target.common.TargetCreatureOrPlayer; public class NivMizzetTheFiremind extends CardImpl { public NivMizzetTheFiremind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}{R}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DRAGON); this.subtype.add(SubType.WIZARD); - this.power = new MageInt(4); this.toughness = new MageInt(4); + + // Flying this.addAbility(FlyingAbility.getInstance()); + + // Whenever you draw a card, Niv-Mizzet, the Firemind deals 1 damage to target creature or player. Ability ability = new DrawCardControllerTriggeredAbility(new DamageTargetEffect(1), false); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); + + // {T}: Draw a card. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/s/StuffyDoll.java b/Mage.Sets/src/mage/cards/s/StuffyDoll.java index 5a7a8986ac9..99f01cfe7ee 100644 --- a/Mage.Sets/src/mage/cards/s/StuffyDoll.java +++ b/Mage.Sets/src/mage/cards/s/StuffyDoll.java @@ -41,8 +41,8 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -56,7 +56,7 @@ import mage.players.Player; public class StuffyDoll extends CardImpl { public StuffyDoll(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(0); this.toughness = new MageInt(1); @@ -67,7 +67,7 @@ public class StuffyDoll extends CardImpl { this.addAbility(IndestructibleAbility.getInstance()); // Whenever Stuffy Doll is dealt damage, it deals that much damage to the chosen player. this.addAbility(new StuffyDollTriggeredAbility()); - // {tap}: Stuffy Doll deals 1 damage to itself. + // {T}: Stuffy Doll deals 1 damage to itself. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageSelfEffect(1), new TapSourceCost())); } 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 417fc5a46d3..41d0c95c6de 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 @@ -740,7 +740,7 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Choice choice, Game game) { if (!choices.isEmpty()) { - if(choice.setChoiceByAnswers(choices, true)){ + if (choice.setChoiceByAnswers(choices, true)) { return true; } } @@ -2167,6 +2167,24 @@ public class TestPlayer implements Player { @Override public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { + switch (ability.getSpellAbilityType()) { + case SPLIT: + case SPLIT_FUSED: + case SPLIT_AFTERMATH: + if (!choices.isEmpty()) { + MageObject object = game.getObject(ability.getSourceId()); + if (object != null) { + LinkedHashMap useableAbilities = computerPlayer.getSpellAbilities(object, game.getState().getZone(object.getId()), game); + for (String choose : choices) { + for (ActivatedAbility actiavtedAbility : useableAbilities.values()) { + if (actiavtedAbility.getRule().startsWith(choose)) { + return (SpellAbility) actiavtedAbility; + } + } + } + } + } + } return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana); } @@ -2176,13 +2194,17 @@ public class TestPlayer implements Player { } @Override - public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { + public boolean choose(Outcome outcome, Target target, + UUID sourceId, Game game + ) { // needed to call here the TestPlayer because it's overwitten return choose(outcome, target, sourceId, game, null); } @Override - public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { + public boolean choose(Outcome outcome, Cards cards, + TargetCard target, Game game + ) { if (!choices.isEmpty()) { for (String choose2 : choices) { // TODO: More targetting to fix @@ -2212,58 +2234,78 @@ public class TestPlayer implements Player { } @Override - public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, + Ability source, Game game + ) { return computerPlayer.chooseTargetAmount(outcome, target, source, game); } @Override - public boolean chooseMulligan(Game game) { + public boolean chooseMulligan(Game game + ) { return computerPlayer.chooseMulligan(game); } @Override - public boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game) { + public boolean choosePile(Outcome outcome, String message, + List pile1, List pile2, + Game game + ) { return computerPlayer.choosePile(outcome, message, pile1, pile2, game); } @Override - public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) { + public boolean playMana(Ability ability, ManaCost unpaid, + String promptText, Game game + ) { groupsForTargetHandling = null; return computerPlayer.playMana(ability, unpaid, promptText, game); } @Override - public UUID chooseAttackerOrder(List attacker, Game game) { + public UUID chooseAttackerOrder(List attacker, Game game + ) { return computerPlayer.chooseAttackerOrder(attacker, game); } @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { + public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, + List blockerOrder, Game game + ) { return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); } @Override - public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { + public void assignDamage(int damage, List targets, + String singleTargetName, UUID sourceId, + Game game + ) { computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); } @Override - public void sideboard(Match match, Deck deck) { + public void sideboard(Match match, Deck deck + ) { computerPlayer.sideboard(match, deck); } @Override - public void construct(Tournament tournament, Deck deck) { + public void construct(Tournament tournament, Deck deck + ) { computerPlayer.construct(tournament, deck); } @Override - public void pickCard(List cards, Deck deck, Draft draft) { + public void pickCard(List cards, Deck deck, + Draft draft + ) { computerPlayer.pickCard(cards, deck, draft); } @Override - public boolean scry(int value, Ability source, Game game) { + public boolean scry(int value, Ability source, + Game game + ) { // Don't scry at the start of the game. if (game.getTurnNum() == 1 && game.getStep() == null) { return false; @@ -2272,37 +2314,51 @@ public class TestPlayer implements Player { } @Override - public boolean moveCards(Card card, Zone toZone, Ability source, Game game) { + public boolean moveCards(Card card, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(card, toZone, source, game); } @Override - public boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Card card, Zone toZone, + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + ) { return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @Override - public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) { + public boolean moveCards(Cards cards, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game) { + public boolean moveCards(Set cards, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Set cards, Zone toZone, + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + ) { return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @Override - public boolean hasDesignation(DesignationType designationName) { + public boolean hasDesignation(DesignationType designationName + ) { return computerPlayer.hasDesignation(designationName); } @Override - public void addDesignation(Designation designation) { + public void addDesignation(Designation designation + ) { computerPlayer.addDesignation(designation); } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index efb8984d58c..3c9714d00e9 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -156,9 +156,12 @@ public abstract class GameImpl implements Game, Serializable { private final int startLife; protected PlayerList playerList; + // infinite loop check (no copy of this attributes neccessary) private int infiniteLoopCounter; // used to check if the game is in an infinite loop private int lastNumberOfAbilitiesOnTheStack; // used to check how long no new ability was put to stack + private List lastPlayersLifes = null; // if life is going down, it's no infinite loop private final LinkedList stackObjectsCheck = new LinkedList<>(); // used to check if different sources used the stack + // used to set the counters a permanent adds the battlefield (if no replacement effect is used e.g. Persist) protected Map enterWithCounters = new HashMap<>(); // used to proceed player conceding requests @@ -1418,6 +1421,23 @@ public abstract class GameImpl implements Game, Serializable { protected void checkInfiniteLoop(UUID removedStackObjectSourceId) { if (stackObjectsCheck.contains(removedStackObjectSourceId) && getStack().size() >= lastNumberOfAbilitiesOnTheStack) { + // Create a list of players life + List newLastPlayersLifes = new ArrayList<>(); + for (Player player : this.getPlayers().values()) { + newLastPlayersLifes.add(player.getLife()); + } + // Check if a player is loosing life + if (lastPlayersLifes != null && lastPlayersLifes.size() == newLastPlayersLifes.size()) { + for (int i = 0; i < newLastPlayersLifes.size(); i++) { + if (newLastPlayersLifes.get(i) < lastPlayersLifes.get(i)) { + // player is loosing life + lastPlayersLifes = null; + infiniteLoopCounter = 0; // reset the infinite counter + } + } + } else { + lastPlayersLifes = newLastPlayersLifes; + } infiniteLoopCounter++; if (infiniteLoopCounter > 15) { Player controller = getPlayer(getControllerId(removedStackObjectSourceId)); From 60cc0525da861c14608beed6f2bd50cd31c4bfa9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 2 Mar 2018 18:22:09 +1100 Subject: [PATCH 120/133] Fix zendikar full art land drawings. - Realized curve fits part of an off-centered ellipse (a 295x700 sized ellipse, with the top left being 197 pixels off the top of the top of the curved image on a scanned ZEN forest with bounding rects of: 293x337) --- .../mage/card/arcane/ModernCardRenderer.java | 55 +++++++------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index c65b92e931b..b34eab7801e 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -7,6 +7,7 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; +import java.awt.geom.Arc2D; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; @@ -541,15 +542,10 @@ public class ModernCardRenderer extends CardRenderer { if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... - int x2 = x; + int x2 = x + contentWidth; int y2 = y; - int topxdelta = 45 * contentWidth / 1000; - int botxdelta = 58 * contentWidth / 1000; - int ctrlx = 0; - int ctrly = (totalContentInset + y2) / 2; - - drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, - x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, + int thisy = totalContentInset + boxHeight; + drawZendikarCurvedFace(g, image, x, thisy, x2, y2, boxColor, borderPaint); } else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) { // Draw curved lines (BFZ land style) @@ -574,8 +570,7 @@ public class ModernCardRenderer extends CardRenderer { drawBottomRight(g, borderPaint, boxColor); } - public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int ctrlx, int ctrly, int w, int h, - int x2, int y2, int ctrlx2, int ctrly2, int w2, int h2, + public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, Color boxColor, Paint paint) { BufferedImage artToUse = faceArtImage; @@ -600,33 +595,27 @@ public class ModernCardRenderer extends CardRenderer { } Path2D.Double curve = new Path2D.Double(); - curve.moveTo(x, y); - curve.quadTo(ctrlx, ctrly, w, h); - curve.lineTo(w2, h); - curve.quadTo(ctrlx2, ctrly2, x2, y); - curve.lineTo(x, y); - Path2D.Double innercurve = new Path2D.Double(); - innercurve.moveTo(x + 1, y + 1); - innercurve.quadTo(ctrlx + 1, ctrly + 1, w + 1, h - 1); - innercurve.lineTo(w2 - 1, h - 1); - innercurve.quadTo(ctrlx2 - 1, ctrly2 - 1, x2 - 1, y + 1); - innercurve.lineTo(x + 1, y + 1); + int ew = x2 - x; + int eh = 700 * (y2 - y) / 335; + Arc2D arc = new Arc2D.Double(x, y - 197 * eh / 700, ew, eh, 0, 360, Arc2D.OPEN); + Arc2D innerarc = new Arc2D.Double(x + 1, y - 197 * eh / 700 + 1, ew - 2, eh - 2, 0, 360, Arc2D.OPEN); + + curve.append(new Rectangle2D.Double(x, y, x2 - x, y2 - y), false); + g2.setClip(new Rectangle2D.Double(x, y, x2 - x, y2 - y)); + g2.setClip(arc); Rectangle2D r = curve.getBounds2D(); - int minX = (int) r.getX(); + g2.drawImage(artToUse, x, y, x2 - x, y2 - y, null); + g2.setClip(null); + g2.setClip(new Rectangle2D.Double(x, y, x2 - x, y2 - y)); - g2.setClip(innercurve); - g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + g2.setColor(CardRendererUtils.abitdarker(boxColor)); + g2.draw(arc); + g2.setColor(Color.black); + g2.draw(innerarc); g2.setClip(null); - g2.setColor(CardRendererUtils.abitdarker(boxColor)); - g2.setPaint(paint); - g2.draw(curve); - - g2.setColor(Color.black); - //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); - g2.draw(innercurve); } public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, @@ -967,8 +956,7 @@ public class ModernCardRenderer extends CardRenderer { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands - { - if (allRules.size() == 1) { + if (allRules.size() == 1) { // Size of mana symbol = 9/4 * h, 3/4h above line drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; @@ -978,7 +966,6 @@ public class ModernCardRenderer extends CardRenderer { } return; } - } } // Go through possible font sizes in descending order to find the best fit From ae99a1b4445719dfed96940cad9e0e957c2c4ffa Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 2 Mar 2018 21:42:16 +0400 Subject: [PATCH 121/133] Fixed tests --- Mage/src/main/java/mage/game/GameImpl.java | 1 + Mage/src/main/java/mage/players/PlayerImpl.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 3c9714d00e9..572a2a4307b 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1433,6 +1433,7 @@ public abstract class GameImpl implements Game, Serializable { // player is loosing life lastPlayersLifes = null; infiniteLoopCounter = 0; // reset the infinite counter + break; } } } else { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a61e8023f3b..b47fab6df90 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1274,7 +1274,7 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - protected LinkedHashMap getSpellAbilities(MageObject object, Zone zone, Game game) { + public LinkedHashMap getSpellAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); for (Ability ability : object.getAbilities()) { if (ability instanceof SpellAbility) { From 08f15416ba996d03717bcb450132b5d015883131 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 2 Mar 2018 22:06:24 +0400 Subject: [PATCH 122/133] * Add new full set Masters 25 (only Karona's Zealot must be implemented); --- .../plugins/card/dl/sources/GathererSets.java | 2 +- .../card/dl/sources/ScryfallImageSource.java | 2 +- .../src/main/resources/image.url.properties | 4 +- Mage.Sets/src/mage/sets/Masters25.java | 255 +++++++++++++++++- .../mage/cards/repository/CardRepository.java | 2 +- Utils/mtg-sets-data.txt | 2 +- 6 files changed, 259 insertions(+), 8 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index 38b5ff2b434..385204f822c 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -66,7 +66,7 @@ public class GathererSets implements Iterable { //"APAC" -- gatherer do not have that set, scrly have PALP //"ARENA" -- is't many set with different codes, not one "CLASH", "CP", "DD3GVL", "DPA", "EURO", "FNMP", "GPX", "GRC", "GUR", "H17", "JR", "MBP", "MGDC", "MLP", "MPRP", "MPS-AKH", "PTC", "S00", "S99", "SUS", "SWS", "UGIN", "UGL", "V10", "V17", "WMCQ", // need to fix - "H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "M25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok + "H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "A25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok // current testing }; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java index 6f664975ff9..aafb2d1568f 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java @@ -208,7 +208,7 @@ public enum ScryfallImageSource implements CardImageSource { supportedSets.add("RIX"); supportedSets.add("WMCQ"); supportedSets.add("PPRO"); -// supportedSets.add("A25"); + supportedSets.add("A25"); // supportedSets.add("DOM"); // supportedSets.add("M19"); diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index 63ffa1a49a9..3aba474b2fb 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -73,6 +73,6 @@ dd3evg=ddaevg dd3gvl=ddagvl dd3jvc=ddajvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK,M19,M25,DOM,H17 +ignore.urls=TOK,M19,DOM,H17 # sets ordered by release time (newest goes first) -token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file +token.lookup.order=M19,A25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Masters25.java b/Mage.Sets/src/mage/sets/Masters25.java index cb35f2c36ff..54c32786288 100644 --- a/Mage.Sets/src/mage/sets/Masters25.java +++ b/Mage.Sets/src/mage/sets/Masters25.java @@ -29,9 +29,10 @@ package mage.sets; /** * - * @author fireshoes + * @author JayDi85 */ import mage.cards.ExpansionSet; +import mage.constants.Rarity; import mage.constants.SetType; public class Masters25 extends ExpansionSet { @@ -43,7 +44,7 @@ public class Masters25 extends ExpansionSet { } private Masters25() { - super("Masters 25", "M25", ExpansionSet.buildDate(2018, 3, 16), SetType.SUPPLEMENTAL); + super("Masters 25", "A25", ExpansionSet.buildDate(2018, 3, 16), SetType.SUPPLEMENTAL); this.blockName = "Reprint"; this.hasBasicLands = false; this.hasBoosters = true; @@ -52,5 +53,255 @@ public class Masters25 extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + + cards.add(new SetCardInfo("Act of Heroism", 1, Rarity.COMMON, mage.cards.a.ActOfHeroism.class)); + cards.add(new SetCardInfo("Akroma, Angel of Wrath", 2, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfWrath.class)); + cards.add(new SetCardInfo("Akroma's Vengeance", 3, Rarity.RARE, mage.cards.a.AkromasVengeance.class)); + cards.add(new SetCardInfo("Angelic Page", 4, Rarity.UNCOMMON, mage.cards.a.AngelicPage.class)); + cards.add(new SetCardInfo("Armageddon", 5, Rarity.MYTHIC, mage.cards.a.Armageddon.class)); + cards.add(new SetCardInfo("Auramancer", 6, Rarity.COMMON, mage.cards.a.Auramancer.class)); + cards.add(new SetCardInfo("Cloudshift", 7, Rarity.COMMON, mage.cards.c.Cloudshift.class)); + cards.add(new SetCardInfo("Congregate", 8, Rarity.UNCOMMON, mage.cards.c.Congregate.class)); + cards.add(new SetCardInfo("Darien, King of Kjeldor", 9, Rarity.RARE, mage.cards.d.DarienKingOfKjeldor.class)); + cards.add(new SetCardInfo("Dauntless Cathar", 10, Rarity.COMMON, mage.cards.d.DauntlessCathar.class)); + cards.add(new SetCardInfo("Decree of Justice", 11, Rarity.RARE, mage.cards.d.DecreeOfJustice.class)); + cards.add(new SetCardInfo("Disenchant", 12, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Fencing Ace", 13, Rarity.COMMON, mage.cards.f.FencingAce.class)); + cards.add(new SetCardInfo("Fiend Hunter", 14, Rarity.UNCOMMON, mage.cards.f.FiendHunter.class)); + cards.add(new SetCardInfo("Geist of the Moors", 15, Rarity.COMMON, mage.cards.g.GeistOfTheMoors.class)); + cards.add(new SetCardInfo("Gods Willing", 16, Rarity.COMMON, mage.cards.g.GodsWilling.class)); + cards.add(new SetCardInfo("Griffin Protector", 17, Rarity.COMMON, mage.cards.g.GriffinProtector.class)); + //cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); + cards.add(new SetCardInfo("Knight of the Skyward Eye", 19, Rarity.COMMON, mage.cards.k.KnightOfTheSkywardEye.class)); + cards.add(new SetCardInfo("Kongming, 'Sleeping Dragon'", 20, Rarity.UNCOMMON, mage.cards.k.KongmingSleepingDragon.class)); + cards.add(new SetCardInfo("Kor Firewalker", 21, Rarity.UNCOMMON, mage.cards.k.KorFirewalker.class)); + cards.add(new SetCardInfo("Loyal Sentry", 22, Rarity.COMMON, mage.cards.l.LoyalSentry.class)); + cards.add(new SetCardInfo("Luminarch Ascension", 23, Rarity.RARE, mage.cards.l.LuminarchAscension.class)); + cards.add(new SetCardInfo("Lunarch Mantle", 24, Rarity.COMMON, mage.cards.l.LunarchMantle.class)); + cards.add(new SetCardInfo("Noble Templar", 25, Rarity.COMMON, mage.cards.n.NobleTemplar.class)); + cards.add(new SetCardInfo("Nyx-Fleece Ram", 26, Rarity.UNCOMMON, mage.cards.n.NyxFleeceRam.class)); + cards.add(new SetCardInfo("Ordeal of Heliod", 27, Rarity.UNCOMMON, mage.cards.o.OrdealOfHeliod.class)); + cards.add(new SetCardInfo("Pacifism", 28, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Path of Peace", 29, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); + cards.add(new SetCardInfo("Promise of Bunrei", 30, Rarity.UNCOMMON, mage.cards.p.PromiseOfBunrei.class)); + cards.add(new SetCardInfo("Renewed Faith", 31, Rarity.COMMON, mage.cards.r.RenewedFaith.class)); + cards.add(new SetCardInfo("Rest in Peace", 32, Rarity.RARE, mage.cards.r.RestInPeace.class)); + cards.add(new SetCardInfo("Savannah Lions", 33, Rarity.COMMON, mage.cards.s.SavannahLions.class)); + cards.add(new SetCardInfo("Squadron Hawk", 34, Rarity.COMMON, mage.cards.s.SquadronHawk.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 35, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 36, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class)); + cards.add(new SetCardInfo("Urbis Protector", 37, Rarity.UNCOMMON, mage.cards.u.UrbisProtector.class)); + cards.add(new SetCardInfo("Valor in Akros", 38, Rarity.UNCOMMON, mage.cards.v.ValorInAkros.class)); + cards.add(new SetCardInfo("Whitemane Lion", 39, Rarity.COMMON, mage.cards.w.WhitemaneLion.class)); + cards.add(new SetCardInfo("Accumulated Knowledge", 40, Rarity.COMMON, mage.cards.a.AccumulatedKnowledge.class)); + cards.add(new SetCardInfo("Arcane Denial", 41, Rarity.COMMON, mage.cards.a.ArcaneDenial.class)); + cards.add(new SetCardInfo("Bident of Thassa", 42, Rarity.RARE, mage.cards.b.BidentOfThassa.class)); + cards.add(new SetCardInfo("Blue Elemental Blast", 43, Rarity.UNCOMMON, mage.cards.b.BlueElementalBlast.class)); + cards.add(new SetCardInfo("Blue Sun's Zenith", 44, Rarity.RARE, mage.cards.b.BlueSunsZenith.class)); + cards.add(new SetCardInfo("Borrowing 100,000 Arrows", 45, Rarity.COMMON, mage.cards.b.Borrowing100000Arrows.class)); + cards.add(new SetCardInfo("Brainstorm", 46, Rarity.COMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brine Elemental", 47, Rarity.UNCOMMON, mage.cards.b.BrineElemental.class)); + cards.add(new SetCardInfo("Choking Tethers", 48, Rarity.COMMON, mage.cards.c.ChokingTethers.class)); + cards.add(new SetCardInfo("Coralhelm Guide", 49, Rarity.COMMON, mage.cards.c.CoralhelmGuide.class)); + cards.add(new SetCardInfo("Counterspell", 50, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Court Hussar", 51, Rarity.COMMON, mage.cards.c.CourtHussar.class)); + cards.add(new SetCardInfo("Curiosity", 52, Rarity.UNCOMMON, mage.cards.c.Curiosity.class)); + cards.add(new SetCardInfo("Cursecatcher", 53, Rarity.UNCOMMON, mage.cards.c.Cursecatcher.class)); + cards.add(new SetCardInfo("Dragon's Eye Savants", 54, Rarity.COMMON, mage.cards.d.DragonsEyeSavants.class)); + cards.add(new SetCardInfo("Exclude", 55, Rarity.UNCOMMON, mage.cards.e.Exclude.class)); + cards.add(new SetCardInfo("Fathom Seer", 56, Rarity.COMMON, mage.cards.f.FathomSeer.class)); + cards.add(new SetCardInfo("Flash", 57, Rarity.RARE, mage.cards.f.Flash.class)); + cards.add(new SetCardInfo("Freed from the Real", 58, Rarity.UNCOMMON, mage.cards.f.FreedFromTheReal.class)); + cards.add(new SetCardInfo("Genju of the Falls", 59, Rarity.UNCOMMON, mage.cards.g.GenjuOfTheFalls.class)); + cards.add(new SetCardInfo("Ghost Ship", 60, Rarity.COMMON, mage.cards.g.GhostShip.class)); + cards.add(new SetCardInfo("Horseshoe Crab", 61, Rarity.COMMON, mage.cards.h.HorseshoeCrab.class)); + cards.add(new SetCardInfo("Jace, the Mind Sculptor", 62, Rarity.MYTHIC, mage.cards.j.JaceTheMindSculptor.class)); + cards.add(new SetCardInfo("Jalira, Master Polymorphist", 63, Rarity.UNCOMMON, mage.cards.j.JaliraMasterPolymorphist.class)); + cards.add(new SetCardInfo("Man-o'-War", 64, Rarity.COMMON, mage.cards.m.ManOWar.class)); + cards.add(new SetCardInfo("Merfolk Looter", 65, Rarity.UNCOMMON, mage.cards.m.MerfolkLooter.class)); + cards.add(new SetCardInfo("Murder of Crows", 66, Rarity.UNCOMMON, mage.cards.m.MurderOfCrows.class)); + cards.add(new SetCardInfo("Mystic of the Hidden Way", 67, Rarity.COMMON, mage.cards.m.MysticOfTheHiddenWay.class)); + cards.add(new SetCardInfo("Pact of Negation", 68, Rarity.RARE, mage.cards.p.PactOfNegation.class)); + cards.add(new SetCardInfo("Phantasmal Bear", 69, Rarity.COMMON, mage.cards.p.PhantasmalBear.class)); + cards.add(new SetCardInfo("Reef Worm", 70, Rarity.RARE, mage.cards.r.ReefWorm.class)); + cards.add(new SetCardInfo("Retraction Helix", 71, Rarity.COMMON, mage.cards.r.RetractionHelix.class)); + cards.add(new SetCardInfo("Shoreline Ranger", 72, Rarity.COMMON, mage.cards.s.ShorelineRanger.class)); + cards.add(new SetCardInfo("Sift", 73, Rarity.COMMON, mage.cards.s.Sift.class)); + cards.add(new SetCardInfo("Totally Lost", 74, Rarity.COMMON, mage.cards.t.TotallyLost.class)); + cards.add(new SetCardInfo("Twisted Image", 75, Rarity.UNCOMMON, mage.cards.t.TwistedImage.class)); + cards.add(new SetCardInfo("Vendilion Clique", 76, Rarity.MYTHIC, mage.cards.v.VendilionClique.class)); + cards.add(new SetCardInfo("Vesuvan Shapeshifter", 77, Rarity.RARE, mage.cards.v.VesuvanShapeshifter.class)); + cards.add(new SetCardInfo("Willbender", 78, Rarity.UNCOMMON, mage.cards.w.Willbender.class)); + cards.add(new SetCardInfo("Ancient Craving", 79, Rarity.UNCOMMON, mage.cards.a.AncientCraving.class)); + cards.add(new SetCardInfo("Bloodhunter Bat", 80, Rarity.COMMON, mage.cards.b.BloodhunterBat.class)); + cards.add(new SetCardInfo("Caustic Tar", 81, Rarity.UNCOMMON, mage.cards.c.CausticTar.class)); + cards.add(new SetCardInfo("Dark Ritual", 82, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Deadly Designs", 83, Rarity.UNCOMMON, mage.cards.d.DeadlyDesigns.class)); + cards.add(new SetCardInfo("Death's-Head Buzzard", 84, Rarity.COMMON, mage.cards.d.DeathsHeadBuzzard.class)); + cards.add(new SetCardInfo("Diabolic Edict", 85, Rarity.COMMON, mage.cards.d.DiabolicEdict.class)); + cards.add(new SetCardInfo("Dirge of Dread", 86, Rarity.COMMON, mage.cards.d.DirgeOfDread.class)); + cards.add(new SetCardInfo("Disfigure", 87, Rarity.COMMON, mage.cards.d.Disfigure.class)); + cards.add(new SetCardInfo("Doomsday", 88, Rarity.MYTHIC, mage.cards.d.Doomsday.class)); + cards.add(new SetCardInfo("Dusk Legion Zealot", 89, Rarity.COMMON, mage.cards.d.DuskLegionZealot.class)); + cards.add(new SetCardInfo("Erg Raiders", 90, Rarity.COMMON, mage.cards.e.ErgRaiders.class)); + cards.add(new SetCardInfo("Fallen Angel", 91, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); + cards.add(new SetCardInfo("Hell's Caretaker", 92, Rarity.RARE, mage.cards.h.HellsCaretaker.class)); + cards.add(new SetCardInfo("Horror of the Broken Lands", 93, Rarity.COMMON, mage.cards.h.HorrorOfTheBrokenLands.class)); + cards.add(new SetCardInfo("Ihsan's Shade", 94, Rarity.UNCOMMON, mage.cards.i.IhsansShade.class)); + cards.add(new SetCardInfo("Laquatus's Champion", 95, Rarity.RARE, mage.cards.l.LaquatussChampion.class)); + cards.add(new SetCardInfo("Living Death", 96, Rarity.RARE, mage.cards.l.LivingDeath.class)); + cards.add(new SetCardInfo("Mesmeric Fiend", 97, Rarity.UNCOMMON, mage.cards.m.MesmericFiend.class)); + cards.add(new SetCardInfo("Murder", 98, Rarity.COMMON, mage.cards.m.Murder.class)); + cards.add(new SetCardInfo("Nezumi Cutthroat", 99, Rarity.COMMON, mage.cards.n.NezumiCutthroat.class)); + cards.add(new SetCardInfo("Phyrexian Ghoul", 100, Rarity.COMMON, mage.cards.p.PhyrexianGhoul.class)); + cards.add(new SetCardInfo("Phyrexian Obliterator", 101, Rarity.MYTHIC, mage.cards.p.PhyrexianObliterator.class)); + cards.add(new SetCardInfo("Plague Wind", 102, Rarity.RARE, mage.cards.p.PlagueWind.class)); + cards.add(new SetCardInfo("Ratcatcher", 103, Rarity.RARE, mage.cards.r.Ratcatcher.class)); + cards.add(new SetCardInfo("Ravenous Chupacabra", 104, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class)); + cards.add(new SetCardInfo("Relentless Rats", 105, Rarity.COMMON, mage.cards.r.RelentlessRats.class)); + cards.add(new SetCardInfo("Returned Phalanx", 106, Rarity.COMMON, mage.cards.r.ReturnedPhalanx.class)); + cards.add(new SetCardInfo("Ruthless Ripper", 107, Rarity.COMMON, mage.cards.r.RuthlessRipper.class)); + cards.add(new SetCardInfo("Street Wraith", 108, Rarity.UNCOMMON, mage.cards.s.StreetWraith.class)); + cards.add(new SetCardInfo("Supernatural Stamina", 109, Rarity.COMMON, mage.cards.s.SupernaturalStamina.class)); + cards.add(new SetCardInfo("Triskaidekaphobia", 110, Rarity.RARE, mage.cards.t.Triskaidekaphobia.class)); + cards.add(new SetCardInfo("Twisted Abomination", 111, Rarity.COMMON, mage.cards.t.TwistedAbomination.class)); + cards.add(new SetCardInfo("Undead Gladiator", 112, Rarity.UNCOMMON, mage.cards.u.UndeadGladiator.class)); + cards.add(new SetCardInfo("Unearth", 113, Rarity.COMMON, mage.cards.u.Unearth.class)); + cards.add(new SetCardInfo("Vampire Lacerator", 114, Rarity.COMMON, mage.cards.v.VampireLacerator.class)); + cards.add(new SetCardInfo("Will-o'-the-Wisp", 115, Rarity.UNCOMMON, mage.cards.w.WillOTheWisp.class)); + cards.add(new SetCardInfo("Zombify", 116, Rarity.UNCOMMON, mage.cards.z.Zombify.class)); + cards.add(new SetCardInfo("Zulaport Cutthroat", 117, Rarity.UNCOMMON, mage.cards.z.ZulaportCutthroat.class)); + cards.add(new SetCardInfo("Act of Treason", 118, Rarity.COMMON, mage.cards.a.ActOfTreason.class)); + cards.add(new SetCardInfo("Akroma, Angel of Fury", 119, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfFury.class)); + cards.add(new SetCardInfo("Balduvian Horde", 120, Rarity.COMMON, mage.cards.b.BalduvianHorde.class)); + cards.add(new SetCardInfo("Ball Lightning", 121, Rarity.RARE, mage.cards.b.BallLightning.class)); + cards.add(new SetCardInfo("Blood Moon", 122, Rarity.RARE, mage.cards.b.BloodMoon.class)); + cards.add(new SetCardInfo("Browbeat", 123, Rarity.UNCOMMON, mage.cards.b.Browbeat.class)); + cards.add(new SetCardInfo("Chandra's Outrage", 124, Rarity.COMMON, mage.cards.c.ChandrasOutrage.class)); + cards.add(new SetCardInfo("Chartooth Cougar", 125, Rarity.COMMON, mage.cards.c.ChartoothCougar.class)); + cards.add(new SetCardInfo("Cinder Storm", 126, Rarity.COMMON, mage.cards.c.CinderStorm.class)); + cards.add(new SetCardInfo("Crimson Mage", 127, Rarity.COMMON, mage.cards.c.CrimsonMage.class)); + cards.add(new SetCardInfo("Eidolon of the Great Revel", 128, Rarity.RARE, mage.cards.e.EidolonOfTheGreatRevel.class)); + cards.add(new SetCardInfo("Enthralling Victor", 129, Rarity.UNCOMMON, mage.cards.e.EnthrallingVictor.class)); + cards.add(new SetCardInfo("Fortune Thief", 130, Rarity.RARE, mage.cards.f.FortuneThief.class)); + cards.add(new SetCardInfo("Frenzied Goblin", 131, Rarity.COMMON, mage.cards.f.FrenziedGoblin.class)); + cards.add(new SetCardInfo("Genju of the Spires", 132, Rarity.UNCOMMON, mage.cards.g.GenjuOfTheSpires.class)); + cards.add(new SetCardInfo("Goblin War Drums", 133, Rarity.UNCOMMON, mage.cards.g.GoblinWarDrums.class)); + cards.add(new SetCardInfo("Hordeling Outburst", 134, Rarity.COMMON, mage.cards.h.HordelingOutburst.class)); + cards.add(new SetCardInfo("Humble Defector", 135, Rarity.UNCOMMON, mage.cards.h.HumbleDefector.class)); + cards.add(new SetCardInfo("Imperial Recruiter", 136, Rarity.MYTHIC, mage.cards.i.ImperialRecruiter.class)); + cards.add(new SetCardInfo("Ire Shaman", 137, Rarity.UNCOMMON, mage.cards.i.IreShaman.class)); + cards.add(new SetCardInfo("Izzet Chemister", 138, Rarity.RARE, mage.cards.i.IzzetChemister.class)); + cards.add(new SetCardInfo("Jackal Pup", 139, Rarity.COMMON, mage.cards.j.JackalPup.class)); + cards.add(new SetCardInfo("Kindle", 140, Rarity.COMMON, mage.cards.k.Kindle.class)); + cards.add(new SetCardInfo("Lightning Bolt", 141, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class)); + cards.add(new SetCardInfo("Magus of the Wheel", 142, Rarity.RARE, mage.cards.m.MagusOfTheWheel.class)); + cards.add(new SetCardInfo("Mogg Flunkies", 143, Rarity.COMMON, mage.cards.m.MoggFlunkies.class)); + cards.add(new SetCardInfo("Pillage", 144, Rarity.COMMON, mage.cards.p.Pillage.class)); + cards.add(new SetCardInfo("Pyre Hound", 145, Rarity.COMMON, mage.cards.p.PyreHound.class)); + cards.add(new SetCardInfo("Pyroclasm", 146, Rarity.UNCOMMON, mage.cards.p.Pyroclasm.class)); + cards.add(new SetCardInfo("Red Elemental Blast", 147, Rarity.UNCOMMON, mage.cards.r.RedElementalBlast.class)); + cards.add(new SetCardInfo("Simian Spirit Guide", 148, Rarity.UNCOMMON, mage.cards.s.SimianSpiritGuide.class)); + cards.add(new SetCardInfo("Skeletonize", 149, Rarity.COMMON, mage.cards.s.Skeletonize.class)); + cards.add(new SetCardInfo("Skirk Commando", 150, Rarity.COMMON, mage.cards.s.SkirkCommando.class)); + cards.add(new SetCardInfo("Soulbright Flamekin", 151, Rarity.COMMON, mage.cards.s.SoulbrightFlamekin.class)); + cards.add(new SetCardInfo("Spikeshot Goblin", 152, Rarity.UNCOMMON, mage.cards.s.SpikeshotGoblin.class)); + cards.add(new SetCardInfo("Thresher Lizard", 153, Rarity.COMMON, mage.cards.t.ThresherLizard.class)); + cards.add(new SetCardInfo("Trumpet Blast", 154, Rarity.COMMON, mage.cards.t.TrumpetBlast.class)); + cards.add(new SetCardInfo("Uncaged Fury", 155, Rarity.COMMON, mage.cards.u.UncagedFury.class)); + cards.add(new SetCardInfo("Zada, Hedron Grinder", 156, Rarity.UNCOMMON, mage.cards.z.ZadaHedronGrinder.class)); + cards.add(new SetCardInfo("Ainok Survivalist", 157, Rarity.COMMON, mage.cards.a.AinokSurvivalist.class)); + cards.add(new SetCardInfo("Ambassador Oak", 158, Rarity.COMMON, mage.cards.a.AmbassadorOak.class)); + cards.add(new SetCardInfo("Ancient Stirrings", 159, Rarity.UNCOMMON, mage.cards.a.AncientStirrings.class)); + cards.add(new SetCardInfo("Arbor Elf", 160, Rarity.COMMON, mage.cards.a.ArborElf.class)); + cards.add(new SetCardInfo("Azusa, Lost but Seeking", 161, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class)); + cards.add(new SetCardInfo("Broodhatch Nantuko", 162, Rarity.UNCOMMON, mage.cards.b.BroodhatchNantuko.class)); + cards.add(new SetCardInfo("Colossal Dreadmaw", 163, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class)); + cards.add(new SetCardInfo("Courser of Kruphix", 164, Rarity.RARE, mage.cards.c.CourserOfKruphix.class)); + cards.add(new SetCardInfo("Cultivate", 165, Rarity.COMMON, mage.cards.c.Cultivate.class)); + cards.add(new SetCardInfo("Echoing Courage", 166, Rarity.COMMON, mage.cards.e.EchoingCourage.class)); + cards.add(new SetCardInfo("Elvish Aberration", 167, Rarity.COMMON, mage.cards.e.ElvishAberration.class)); + cards.add(new SetCardInfo("Elvish Piper", 168, Rarity.RARE, mage.cards.e.ElvishPiper.class)); + cards.add(new SetCardInfo("Ember Weaver", 169, Rarity.COMMON, mage.cards.e.EmberWeaver.class)); + cards.add(new SetCardInfo("Epic Confrontation", 170, Rarity.COMMON, mage.cards.e.EpicConfrontation.class)); + cards.add(new SetCardInfo("Fierce Empath", 171, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class)); + cards.add(new SetCardInfo("Giant Growth", 172, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Invigorate", 173, Rarity.UNCOMMON, mage.cards.i.Invigorate.class)); + cards.add(new SetCardInfo("Iwamori of the Open Fist", 174, Rarity.UNCOMMON, mage.cards.i.IwamoriOfTheOpenFist.class)); + cards.add(new SetCardInfo("Kavu Climber", 175, Rarity.COMMON, mage.cards.k.KavuClimber.class)); + cards.add(new SetCardInfo("Kavu Predator", 176, Rarity.UNCOMMON, mage.cards.k.KavuPredator.class)); + cards.add(new SetCardInfo("Krosan Colossus", 177, Rarity.UNCOMMON, mage.cards.k.KrosanColossus.class)); + cards.add(new SetCardInfo("Krosan Tusker", 178, Rarity.UNCOMMON, mage.cards.k.KrosanTusker.class)); + cards.add(new SetCardInfo("Living Wish", 179, Rarity.RARE, mage.cards.l.LivingWish.class)); + cards.add(new SetCardInfo("Lull", 180, Rarity.COMMON, mage.cards.l.Lull.class)); + cards.add(new SetCardInfo("Master of the Wild Hunt", 181, Rarity.MYTHIC, mage.cards.m.MasterOfTheWildHunt.class)); + cards.add(new SetCardInfo("Nettle Sentinel", 182, Rarity.COMMON, mage.cards.n.NettleSentinel.class)); + cards.add(new SetCardInfo("Plummet", 183, Rarity.COMMON, mage.cards.p.Plummet.class)); + cards.add(new SetCardInfo("Presence of Gond", 184, Rarity.COMMON, mage.cards.p.PresenceOfGond.class)); + cards.add(new SetCardInfo("Protean Hulk", 185, Rarity.RARE, mage.cards.p.ProteanHulk.class)); + cards.add(new SetCardInfo("Rancor", 186, Rarity.UNCOMMON, mage.cards.r.Rancor.class)); + cards.add(new SetCardInfo("Regrowth", 187, Rarity.UNCOMMON, mage.cards.r.Regrowth.class)); + cards.add(new SetCardInfo("Stampede Driver", 188, Rarity.UNCOMMON, mage.cards.s.StampedeDriver.class)); + cards.add(new SetCardInfo("Summoner's Pact", 189, Rarity.RARE, mage.cards.s.SummonersPact.class)); + cards.add(new SetCardInfo("Timberpack Wolf", 190, Rarity.COMMON, mage.cards.t.TimberpackWolf.class)); + cards.add(new SetCardInfo("Tree of Redemption", 191, Rarity.MYTHIC, mage.cards.t.TreeOfRedemption.class)); + cards.add(new SetCardInfo("Utopia Sprawl", 192, Rarity.UNCOMMON, mage.cards.u.UtopiaSprawl.class)); + cards.add(new SetCardInfo("Vessel of Nascency", 193, Rarity.COMMON, mage.cards.v.VesselOfNascency.class)); + cards.add(new SetCardInfo("Wildheart Invoker", 194, Rarity.COMMON, mage.cards.w.WildheartInvoker.class)); + cards.add(new SetCardInfo("Woolly Loxodon", 195, Rarity.COMMON, mage.cards.w.WoollyLoxodon.class)); + cards.add(new SetCardInfo("Animar, Soul of Elements", 196, Rarity.MYTHIC, mage.cards.a.AnimarSoulOfElements.class)); + cards.add(new SetCardInfo("Baloth Null", 197, Rarity.UNCOMMON, mage.cards.b.BalothNull.class)); + cards.add(new SetCardInfo("Blightning", 198, Rarity.UNCOMMON, mage.cards.b.Blightning.class)); + cards.add(new SetCardInfo("Boros Charm", 199, Rarity.UNCOMMON, mage.cards.b.BorosCharm.class)); + cards.add(new SetCardInfo("Brion Stoutarm", 200, Rarity.RARE, mage.cards.b.BrionStoutarm.class)); + cards.add(new SetCardInfo("Cloudblazer", 201, Rarity.UNCOMMON, mage.cards.c.Cloudblazer.class)); + cards.add(new SetCardInfo("Conflux", 202, Rarity.RARE, mage.cards.c.Conflux.class)); + cards.add(new SetCardInfo("Eladamri's Call", 203, Rarity.RARE, mage.cards.e.EladamrisCall.class)); + cards.add(new SetCardInfo("Gisela, Blade of Goldnight", 204, Rarity.MYTHIC, mage.cards.g.GiselaBladeOfGoldnight.class)); + cards.add(new SetCardInfo("Grenzo, Dungeon Warden", 205, Rarity.RARE, mage.cards.g.GrenzoDungeonWarden.class)); + cards.add(new SetCardInfo("Hanna, Ship's Navigator", 206, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); + cards.add(new SetCardInfo("Lorescale Coatl", 207, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class)); + cards.add(new SetCardInfo("Mystic Snake", 208, Rarity.RARE, mage.cards.m.MysticSnake.class)); + cards.add(new SetCardInfo("Nicol Bolas", 209, Rarity.RARE, mage.cards.n.NicolBolas.class)); + cards.add(new SetCardInfo("Niv-Mizzet, the Firemind", 210, Rarity.RARE, mage.cards.n.NivMizzetTheFiremind.class)); + cards.add(new SetCardInfo("Notion Thief", 211, Rarity.RARE, mage.cards.n.NotionThief.class)); + cards.add(new SetCardInfo("Pernicious Deed", 212, Rarity.RARE, mage.cards.p.PerniciousDeed.class)); + cards.add(new SetCardInfo("Pillory of the Sleepless", 213, Rarity.UNCOMMON, mage.cards.p.PilloryOfTheSleepless.class)); + cards.add(new SetCardInfo("Prossh, Skyraider of Kher", 214, Rarity.MYTHIC, mage.cards.p.ProsshSkyraiderOfKher.class)); + cards.add(new SetCardInfo("Quicksilver Dagger", 215, Rarity.UNCOMMON, mage.cards.q.QuicksilverDagger.class)); + cards.add(new SetCardInfo("Ruric Thar, the Unbowed", 216, Rarity.RARE, mage.cards.r.RuricTharTheUnbowed.class)); + cards.add(new SetCardInfo("Shadowmage Infiltrator", 217, Rarity.UNCOMMON, mage.cards.s.ShadowmageInfiltrator.class)); + cards.add(new SetCardInfo("Stangg", 218, Rarity.UNCOMMON, mage.cards.s.Stangg.class)); + cards.add(new SetCardInfo("Vindicate", 219, Rarity.RARE, mage.cards.v.Vindicate.class)); + cards.add(new SetCardInfo("Watchwolf", 220, Rarity.UNCOMMON, mage.cards.w.Watchwolf.class)); + cards.add(new SetCardInfo("Assembly-Worker", 221, Rarity.COMMON, mage.cards.a.AssemblyWorker.class)); + cards.add(new SetCardInfo("Chalice of the Void", 222, Rarity.MYTHIC, mage.cards.c.ChaliceOfTheVoid.class)); + cards.add(new SetCardInfo("Coalition Relic", 223, Rarity.RARE, mage.cards.c.CoalitionRelic.class)); + cards.add(new SetCardInfo("Ensnaring Bridge", 224, Rarity.MYTHIC, mage.cards.e.EnsnaringBridge.class)); + cards.add(new SetCardInfo("Heavy Arbalest", 225, Rarity.UNCOMMON, mage.cards.h.HeavyArbalest.class)); + cards.add(new SetCardInfo("Nihil Spellbomb", 226, Rarity.COMMON, mage.cards.n.NihilSpellbomb.class)); + cards.add(new SetCardInfo("Perilous Myr", 227, Rarity.UNCOMMON, mage.cards.p.PerilousMyr.class)); + cards.add(new SetCardInfo("Primal Clay", 228, Rarity.COMMON, mage.cards.p.PrimalClay.class)); + cards.add(new SetCardInfo("Prophetic Prism", 229, Rarity.COMMON, mage.cards.p.PropheticPrism.class)); + cards.add(new SetCardInfo("Sai of the Shinobi", 230, Rarity.UNCOMMON, mage.cards.s.SaiOfTheShinobi.class)); + cards.add(new SetCardInfo("Self-Assembler", 231, Rarity.COMMON, mage.cards.s.SelfAssembler.class)); + cards.add(new SetCardInfo("Strionic Resonator", 232, Rarity.RARE, mage.cards.s.StrionicResonator.class)); + cards.add(new SetCardInfo("Sundering Titan", 233, Rarity.RARE, mage.cards.s.SunderingTitan.class)); + cards.add(new SetCardInfo("Swiftfoot Boots", 234, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Treasure Keeper", 235, Rarity.UNCOMMON, mage.cards.t.TreasureKeeper.class)); + cards.add(new SetCardInfo("Ash Barrens", 236, Rarity.UNCOMMON, mage.cards.a.AshBarrens.class)); + cards.add(new SetCardInfo("Cascade Bluffs", 237, Rarity.RARE, mage.cards.c.CascadeBluffs.class)); + cards.add(new SetCardInfo("Fetid Heath", 238, Rarity.RARE, mage.cards.f.FetidHeath.class)); + cards.add(new SetCardInfo("Flooded Grove", 239, Rarity.RARE, mage.cards.f.FloodedGrove.class)); + cards.add(new SetCardInfo("Haunted Fengraf", 240, Rarity.COMMON, mage.cards.h.HauntedFengraf.class)); + cards.add(new SetCardInfo("Mikokoro, Center of the Sea", 241, Rarity.RARE, mage.cards.m.MikokoroCenterOfTheSea.class)); + cards.add(new SetCardInfo("Mishra's Factory", 242, Rarity.UNCOMMON, mage.cards.m.MishrasFactory.class)); + cards.add(new SetCardInfo("Myriad Landscape", 243, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Pendelhaven", 244, Rarity.RARE, mage.cards.p.Pendelhaven.class)); + cards.add(new SetCardInfo("Quicksand", 245, Rarity.UNCOMMON, mage.cards.q.Quicksand.class)); + cards.add(new SetCardInfo("Rishadan Port", 246, Rarity.RARE, mage.cards.r.RishadanPort.class)); + cards.add(new SetCardInfo("Rugged Prairie", 247, Rarity.RARE, mage.cards.r.RuggedPrairie.class)); + cards.add(new SetCardInfo("Twilight Mire", 248, Rarity.RARE, mage.cards.t.TwilightMire.class)); + cards.add(new SetCardInfo("Zoetic Cavern", 249, Rarity.UNCOMMON, mage.cards.z.ZoeticCavern.class)); } } diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 02b9c5abf57..9538db973e0 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -58,7 +58,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 51; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 104; + private static final long CARD_CONTENT_VERSION = 105; private Dao cardDao; private Set classNames; diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index 9a6faa767d7..ea542b580f8 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -117,7 +117,7 @@ Magic 2013|M13| Magic 2014|M14| Magic 2015|M15| Core 2019|M19| -Masters 25|M25| +Masters 25|A25| Magic: The Gathering-Commander|CMD| Magic: The Gathering-Conspiracy|CNS| Media Inserts|MBP| From 8cf065e3458400baf560a7a7e8edd117bc7a0748 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 3 Mar 2018 23:51:47 +1100 Subject: [PATCH 123/133] For actual cards on modo, the full background is always printed to it (not just part of the top left corner of the background). --- .../mage/card/arcane/ModernCardRenderer.java | 81 ++++++++++++++---- .../cardrender/background_texture_vehicle.png | Bin 685 -> 19249 bytes 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index b34eab7801e..25264f7cde4 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -8,8 +8,10 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; import java.awt.geom.Arc2D; +import java.awt.geom.Area; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -70,6 +72,13 @@ public class ModernCardRenderer extends CardRenderer { BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage()); return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight())); } + + private static BufferedImage loadBackgroundImage(String name) { + URL url = ModernCardRenderer.class.getResource("/cardrender/background_texture_" + name + ".png"); + ImageIcon icon = new ImageIcon(url); + BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage()); + return img; + } private static BufferedImage loadFramePart(String name) { URL url = ModernCardRenderer.class.getResource("/cardrender/" + name + ".png"); @@ -99,6 +108,16 @@ public class ModernCardRenderer extends CardRenderer { public static final Paint BG_TEXTURE_ARTIFACT = loadBackgroundTexture("artifact"); public static final Paint BG_TEXTURE_LAND = loadBackgroundTexture("land"); public static final Paint BG_TEXTURE_VEHICLE = loadBackgroundTexture("vehicle"); + + public static final BufferedImage BG_IMG_WHITE = loadBackgroundImage("white"); + public static final BufferedImage BG_IMG_BLUE = loadBackgroundImage("blue"); + public static final BufferedImage BG_IMG_BLACK = loadBackgroundImage("black"); + public static final BufferedImage BG_IMG_RED = loadBackgroundImage("red"); + public static final BufferedImage BG_IMG_GREEN = loadBackgroundImage("green"); + public static final BufferedImage BG_IMG_GOLD = loadBackgroundImage("gold"); + public static final BufferedImage BG_IMG_ARTIFACT = loadBackgroundImage("artifact"); + public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land"); + public static final BufferedImage BG_IMG_VEHICLE = loadBackgroundImage("vehicle"); public static final BufferedImage FRAME_INVENTION = loadFramePart("invention_frame"); @@ -281,27 +300,30 @@ public class ModernCardRenderer extends CardRenderer { // Just draw a brown rectangle drawCardBack(g); } else { - BufferedImage bufferedImage = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB); - - // Set texture to paint with - g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes())); + BufferedImage bg = getBackgroundImage(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()); + if (bg == null) { + return; + } + int bgw = bg.getWidth(); + int bgh = bg.getHeight(); // Draw main part (most of card) - g.fillRoundRect( - borderWidth, borderWidth, + RoundRectangle2D rr = new RoundRectangle2D.Double(borderWidth, borderWidth, cardWidth - borderWidth * 2, cardHeight - borderWidth * 4 - cornerRadius * 2, cornerRadius - 1, cornerRadius - 1); + Area a = new Area(rr); - // Draw the M15 rounded "swoosh" at the bottom - g.fillRoundRect( - borderWidth, cardHeight - borderWidth * 4 - cornerRadius * 4, + RoundRectangle2D rr2 = new RoundRectangle2D.Double(borderWidth, cardHeight - borderWidth * 4 - cornerRadius * 4, cardWidth - borderWidth * 2, cornerRadius * 4, cornerRadius * 2, cornerRadius * 2); - - // Draw the cutout into the "swoosh" for the textbox to lie over - g.fillRect( - borderWidth + contentInset, cardHeight - borderWidth * 5, - cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); + a.add(new Area(rr2)); + + // Draw the M15 rounded "swoosh" at the bottom + Rectangle r = new Rectangle(borderWidth + contentInset, cardHeight - borderWidth * 5, cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); + a.add(new Area(r)); + g.setClip(a); + g.drawImage(bg, 0, 0, cardWidth, cardHeight, 0, 0, bgw, bgh, BOX_BLUE, null); + g.setClip(null); } } @@ -545,7 +567,7 @@ public class ModernCardRenderer extends CardRenderer { int x2 = x + contentWidth; int y2 = y; int thisy = totalContentInset + boxHeight; - drawZendikarCurvedFace(g, image, x, thisy, x2, y2, + drawZendikarCurvedFace(g, image, x, thisy, x2, y2, boxColor, borderPaint); } else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) { // Draw curved lines (BFZ land style) @@ -1249,7 +1271,34 @@ public class ModernCardRenderer extends CardRenderer { return new Color(71, 86, 101); } } - + + // Determine which background image to use from a set of colors + // and the current card. + protected static BufferedImage getBackgroundImage(ObjectColor colors, Collection types, SubTypeList subTypes) { + if (subTypes.contains(SubType.VEHICLE)) { + return BG_IMG_VEHICLE; + } else if (types.contains(CardType.LAND)) { + return BG_IMG_LAND; + } else if (types.contains(CardType.ARTIFACT)) { + return BG_IMG_ARTIFACT; + } else if (colors.isMulticolored()) { + return BG_IMG_GOLD; + } else if (colors.isWhite()) { + return BG_IMG_WHITE; + } else if (colors.isBlue()) { + return BG_IMG_BLUE; + } else if (colors.isBlack()) { + return BG_IMG_BLACK; + } else if (colors.isRed()) { + return BG_IMG_RED; + } else if (colors.isGreen()) { + return BG_IMG_GREEN; + } else { + // Colorless + return null; + } + } + // Get the box color for the given colors protected Color getBoxColor(ObjectColor colors, Collection types, boolean isNightCard) { if (cardView.isAbility()) { diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_vehicle.png b/Mage.Client/src/main/resources/cardrender/background_texture_vehicle.png index 2f58f34a36174e2509d793d5414726fc8157be6b..87282e34bc8e3e7e5d1a5e9545257de32c40cd3e 100644 GIT binary patch literal 19249 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 XvsaK>K~^c800000NkvXXu0mjf8eAxT From da76c7687da4baa9c2013ba76ccb50bc85c25a62 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 4 Mar 2018 02:17:21 +1100 Subject: [PATCH 124/133] Missing deovid type background --- .../mage/card/arcane/ModernCardRenderer.java | 5 +++-- .../cardrender/background_texture_colorless.png | Bin 0 -> 58324 bytes 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_colorless.png diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 25264f7cde4..67e5192061d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -118,7 +118,8 @@ public class ModernCardRenderer extends CardRenderer { public static final BufferedImage BG_IMG_ARTIFACT = loadBackgroundImage("artifact"); public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land"); public static final BufferedImage BG_IMG_VEHICLE = loadBackgroundImage("vehicle"); - + public static final BufferedImage BG_IMG_COLORLESS = loadBackgroundImage("colorless"); + public static final BufferedImage FRAME_INVENTION = loadFramePart("invention_frame"); public static final Color BORDER_WHITE = new Color(216, 203, 188); @@ -1295,7 +1296,7 @@ public class ModernCardRenderer extends CardRenderer { return BG_IMG_GREEN; } else { // Colorless - return null; + return BG_IMG_COLORLESS; } } diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_colorless.png b/Mage.Client/src/main/resources/cardrender/background_texture_colorless.png new file mode 100644 index 0000000000000000000000000000000000000000..9a27821dab6dd62804804b1cbf378dd3d2700fa9 GIT binary patch literal 58324 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 G)&BwicLyy1 literal 0 HcmV?d00001 From 9e001acfe27df103ea7514e9986356770518af92 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:42:43 +0000 Subject: [PATCH 125/133] Implemented Karona's Zealot --- Mage.Sets/src/mage/cards/k/KaronasZealot.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KaronasZealot.java diff --git a/Mage.Sets/src/mage/cards/k/KaronasZealot.java b/Mage.Sets/src/mage/cards/k/KaronasZealot.java new file mode 100644 index 00000000000..6e07f81e71d --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaronasZealot.java @@ -0,0 +1,75 @@ +/* + * 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.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class KaronasZealot extends CardImpl { + + public KaronasZealot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Morph {3}{W}{W} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{W}{W}"))); + + // When Karona's Zealot is turned face up, all damage that would be dealt to it this turn is dealt to target creature instead. + Ability ability = new TurnedFaceUpSourceTriggeredAbility(new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE, false) + .setText("all damage that would be dealt to it this turn is dealt to target creature instead")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public KaronasZealot(final KaronasZealot card) { + super(card); + } + + @Override + public KaronasZealot copy() { + return new KaronasZealot(this); + } +} From 0b3822ab7c72675582e8e57ec2f82dd2c11276b0 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:43:44 +0000 Subject: [PATCH 126/133] Implemented Karona's Zealot --- Mage.Sets/src/mage/sets/Masters25.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/Masters25.java b/Mage.Sets/src/mage/sets/Masters25.java index 54c32786288..83b55f60414 100644 --- a/Mage.Sets/src/mage/sets/Masters25.java +++ b/Mage.Sets/src/mage/sets/Masters25.java @@ -71,7 +71,7 @@ public class Masters25 extends ExpansionSet { cards.add(new SetCardInfo("Geist of the Moors", 15, Rarity.COMMON, mage.cards.g.GeistOfTheMoors.class)); cards.add(new SetCardInfo("Gods Willing", 16, Rarity.COMMON, mage.cards.g.GodsWilling.class)); cards.add(new SetCardInfo("Griffin Protector", 17, Rarity.COMMON, mage.cards.g.GriffinProtector.class)); - //cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); + cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); cards.add(new SetCardInfo("Knight of the Skyward Eye", 19, Rarity.COMMON, mage.cards.k.KnightOfTheSkywardEye.class)); cards.add(new SetCardInfo("Kongming, 'Sleeping Dragon'", 20, Rarity.UNCOMMON, mage.cards.k.KongmingSleepingDragon.class)); cards.add(new SetCardInfo("Kor Firewalker", 21, Rarity.UNCOMMON, mage.cards.k.KorFirewalker.class)); From 17c1800cb6a83187ea013e44f444413575afea0c Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:44:16 +0000 Subject: [PATCH 127/133] Implemented Karona's Zealot --- Mage.Sets/src/mage/sets/Scourge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index 8dff799906e..a58b86f8ffe 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -126,6 +126,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Guilty Conscience", 17, Rarity.COMMON, mage.cards.g.GuiltyConscience.class)); cards.add(new SetCardInfo("Hindering Touch", 37, Rarity.COMMON, mage.cards.h.HinderingTouch.class)); cards.add(new SetCardInfo("Hunting Pack", 121, Rarity.UNCOMMON, mage.cards.h.HuntingPack.class)); + cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); cards.add(new SetCardInfo("Karona, False God", 138, Rarity.RARE, mage.cards.k.KaronaFalseGod.class)); cards.add(new SetCardInfo("Krosan Drover", 122, Rarity.COMMON, mage.cards.k.KrosanDrover.class)); cards.add(new SetCardInfo("Krosan Warchief", 123, Rarity.UNCOMMON, mage.cards.k.KrosanWarchief.class)); From bb9e438e51b524d58988371fb0a61f0e31847c43 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:57:56 +0000 Subject: [PATCH 128/133] Added missing Scarecrow --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 52c88313134..e06cd8e06cd 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -241,6 +241,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Sandstorm", 164, Rarity.COMMON, mage.cards.s.Sandstorm.class)); cards.add(new SetCardInfo("Savannah Lions", 24, Rarity.UNCOMMON, mage.cards.s.SavannahLions.class)); cards.add(new SetCardInfo("Savannah", 250, Rarity.RARE, mage.cards.s.Savannah.class)); + cards.add(new SetCardInfo("Scarecrow", 225, Rarity.UNCOMMON, mage.cards.s.Scarecrow.class)); cards.add(new SetCardInfo("Scarwood Bandits", 165, Rarity.RARE, mage.cards.s.ScarwoodBandits.class)); cards.add(new SetCardInfo("Scavenger Folk", 166, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); cards.add(new SetCardInfo("Scavenging Ghoul", 95, Rarity.UNCOMMON, mage.cards.s.ScavengingGhoul.class)); From be70ecd7f3882c1dee1335f312c51ec1aa44b542 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 4 Mar 2018 17:57:51 +1100 Subject: [PATCH 129/133] Add a singleton search option for deck editor search --- .../mage/client/deckeditor/CardSelector.form | 26 +++++++++++++++++ .../mage/client/deckeditor/CardSelector.java | 27 ++++++++++++++++- .../predicate/other/CardTextPredicate.java | 29 +++++++++++++++++-- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form index e9b349e1117..17513f7a0da 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form @@ -645,6 +645,8 @@ + + @@ -659,6 +661,7 @@ + @@ -846,6 +849,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index d23a4098246..f6fc566cef3 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -232,7 +232,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene FilterCard filter = new FilterCard(); String name = jTextFieldSearch.getText().trim(); - filter.add(new CardTextPredicate(name, chkNames.isSelected(), chkTypes.isSelected(), chkRules.isSelected())); + filter.add(new CardTextPredicate(name, chkNames.isSelected(), chkTypes.isSelected(), chkRules.isSelected(), chkUnique.isSelected())); if (limited) { ArrayList> predicates = new ArrayList<>(); @@ -543,6 +543,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene chkNames = new javax.swing.JCheckBox(); chkTypes = new javax.swing.JCheckBox(); chkRules = new javax.swing.JCheckBox(); + chkUnique = new javax.swing.JCheckBox(); jButtonSearch = new javax.swing.JButton(); jButtonClean = new javax.swing.JButton(); cardCountLabel = new javax.swing.JLabel(); @@ -1062,6 +1063,22 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene chkRulesActionPerformed(evt); } }); + + chkUnique.setSelected(true); + chkUnique.setText("Unique"); + chkUnique.setToolTipText("Singleton results only."); + chkUnique.setFocusable(false); + chkUnique.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); + chkUnique.setMaximumSize(new java.awt.Dimension(69, 16)); + chkUnique.setMinimumSize(new java.awt.Dimension(69, 16)); + chkUnique.setPreferredSize(new java.awt.Dimension(69, 16)); + chkUnique.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + chkUnique.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + chkUniqueActionPerformed(evt); + } + }); + jButtonSearch.setText("Search"); jButtonSearch.setToolTipText("Performs the search."); @@ -1109,6 +1126,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene .addComponent(chkTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(chkRules, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkUnique, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(5, 5, 5) .addComponent(cardCountLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -1122,6 +1141,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene .addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(chkTypes, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(chkRules, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(chkUnique, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(chkNames, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(cardSelectorBottomPanelLayout.createSequentialGroup() .addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1341,6 +1361,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene // TODO add your handling code here: }//GEN-LAST:event_chkRulesActionPerformed + private void chkUniqueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRulesActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_chkRulesActionPerformed + private void btnExpansionSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExpansionSearchActionPerformed FastSearchUtil.showFastSearchForStringComboBox(cbExpansionSet, "Select set or expansion"); }//GEN-LAST:event_btnExpansionSearchActionPerformed @@ -1418,6 +1442,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene private javax.swing.JCheckBox chkPennyDreadful; private javax.swing.JCheckBox chkPiles; private javax.swing.JCheckBox chkRules; + private javax.swing.JCheckBox chkUnique; private javax.swing.JCheckBox chkTypes; private javax.swing.JButton jButtonAddToMain; private javax.swing.JButton jButtonAddToSideboard; diff --git a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java index 36cf356ce4f..503d290e054 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java @@ -27,6 +27,7 @@ */ package mage.filter.predicate.other; +import java.util.HashMap; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.SubType; @@ -44,21 +45,38 @@ public class CardTextPredicate implements Predicate { private final boolean inNames; private final boolean inTypes; private final boolean inRules; + private final boolean isUnique; + private HashMap seenCards = null; - public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules) { + public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules, boolean isUnique) { this.text = text; this.inNames = inNames; this.inTypes = inTypes; this.inRules = inRules; + this.isUnique = isUnique; + seenCards = new HashMap<>(); } @Override public boolean apply(Card input, Game game) { - if (text.isEmpty()) { + if (text.isEmpty() && !isUnique) { return true; } + + if (text.isEmpty() && isUnique) { + boolean found = !seenCards.keySet().contains(input.getName()); + seenCards.put(input.getName(), true); + return found; + } + // first check in card name if (inNames && input.getName().toLowerCase().contains(text.toLowerCase())) { + if (isUnique && seenCards.keySet().contains(input.getName())) { + return false; + } + if (isUnique) { + seenCards.put(input.getName(), true); + } return true; } @@ -105,11 +123,18 @@ public class CardTextPredicate implements Predicate { } } } + + if (found && isUnique && seenCards.keySet().contains(input.getName())) { + found = false; + } if (!found) { return false; } } + if (isUnique) { + seenCards.put(input.getName(), true); + } return true; } From d2c39a1339f2033df3689bb2ba1bdf1f1dada173 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:37:22 +0000 Subject: [PATCH 130/133] Included duration for BecomesChosenCreatureTypeTargetEffect --- ...BecomesChosenCreatureTypeTargetEffect.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java index 982f160fb66..ef3759eaa90 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java @@ -17,17 +17,24 @@ import mage.target.targetpointer.FixedTarget; public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { private final boolean nonWall; + private final Duration duration; public BecomesChosenCreatureTypeTargetEffect() { - this(false); + this(false, Duration.EndOfTurn); } public BecomesChosenCreatureTypeTargetEffect(boolean nonWall) { + this(nonWall, Duration.EndOfTurn); + } + + public BecomesChosenCreatureTypeTargetEffect(boolean nonWall, Duration duration) { super(Outcome.BoostCreature); this.nonWall = nonWall; - if (nonWall) { + this.duration = duration; + if(nonWall) { staticText = "choose a creature type other than Wall. Target creature becomes that type until end of turn"; - } else { + } + else { staticText = "target creature becomes the creature type of your choice until end of turn"; } @@ -36,6 +43,7 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { public BecomesChosenCreatureTypeTargetEffect(final BecomesChosenCreatureTypeTargetEffect effect) { super(effect); this.nonWall = effect.nonWall; + this.duration = effect.duration; } @Override @@ -46,21 +54,23 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { if (player != null && card != null) { Choice typeChoice = new ChoiceCreatureType(); String msg = "Choose a creature type"; - if (nonWall) { + if(nonWall) { msg += " other than Wall"; } typeChoice.setMessage(msg); - if (nonWall) { + if(nonWall) { typeChoice.getChoices().remove(SubType.WALL.getDescription()); } - if (!player.choose(Outcome.BoostCreature, typeChoice, game)) { - return false; + while (!player.choose(Outcome.BoostCreature, typeChoice, game)) { + if (!player.canRespond()) { + return false; + } } game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice()); chosenType = typeChoice.getChoice(); if (chosenType != null && !chosenType.isEmpty()) { // ADD TYPE TO TARGET - ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(Duration.EndOfTurn, SubType.byDescription(chosenType)); + ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(duration, SubType.byDescription(chosenType)); effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); game.addEffect(effect, source); return true; From 5bd58ca56e3ecbbcb08d10020e6c126798bbc790 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:38:38 +0000 Subject: [PATCH 131/133] Included duration for BecomesChosenCreatureTypeSourceEffect --- .../BecomesChosenCreatureTypeSourceEffect.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java index 56d90e8f218..4ee8f6462e4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java @@ -3,6 +3,7 @@ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; @@ -10,17 +11,28 @@ import mage.target.targetpointer.FixedTarget; public class BecomesChosenCreatureTypeSourceEffect extends OneShotEffect { + private final boolean nonWall; + private final Duration duration; + public BecomesChosenCreatureTypeSourceEffect() { - this(false); + this(false, Duration.EndOfTurn); } public BecomesChosenCreatureTypeSourceEffect(boolean nonWall) { + this(nonWall, Duration.EndOfTurn); + } + + public BecomesChosenCreatureTypeSourceEffect(boolean nonWall, Duration duration) { super(Outcome.BoostCreature); - staticText = "{this} becomes the creature type of your choice until end of turn."; + this.nonWall = nonWall; + this.duration = duration; + staticText = "{this} becomes the creature type of your choice" + (duration == Duration.EndOfTurn ? " until end of turn." : ""); } public BecomesChosenCreatureTypeSourceEffect(final BecomesChosenCreatureTypeSourceEffect effect) { super(effect); + this.nonWall = effect.nonWall; + this.duration = effect.duration; } @Override @@ -29,7 +41,7 @@ public class BecomesChosenCreatureTypeSourceEffect extends OneShotEffect { if (sourcePerm == null) { return false; } - Effect effect = new BecomesChosenCreatureTypeTargetEffect(); + Effect effect = new BecomesChosenCreatureTypeTargetEffect(nonWall, duration); effect.setTargetPointer(new FixedTarget(sourcePerm, game)); return effect.apply(game, source); } From 4f4028174e112cd53e21c30817e484e43669c17d Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:41:23 +0000 Subject: [PATCH 132/133] Implemented Proteus Machine --- .../src/mage/cards/p/ProteusMachine.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/ProteusMachine.java diff --git a/Mage.Sets/src/mage/cards/p/ProteusMachine.java b/Mage.Sets/src/mage/cards/p/ProteusMachine.java new file mode 100644 index 00000000000..6fbff759cc3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProteusMachine.java @@ -0,0 +1,70 @@ +/* + * 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.UUID; +import mage.MageInt; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeSourceEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.SubType; + +/** + * + * @author L_J + */ +public class ProteusMachine extends CardImpl { + + public ProteusMachine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + this.subtype.add(SubType.SHAPESHIFTER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Morph {0} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{0}"))); + + // When Proteus Machine is turned face up, it becomes the creature type of your choice. (This effect lasts indefinitely.) + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new BecomesChosenCreatureTypeSourceEffect(false, Duration.Custom))); + } + + public ProteusMachine(final ProteusMachine card) { + super(card); + } + + @Override + public ProteusMachine copy() { + return new ProteusMachine(this); + } +} From 9f6e52296941d507bcb8276657bcbfd986faa3f8 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:42:00 +0000 Subject: [PATCH 133/133] Implemented Proteus Machine --- Mage.Sets/src/mage/sets/Scourge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index a58b86f8ffe..3c5bf9920f1 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -146,6 +146,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Parallel Thoughts", 44, Rarity.RARE, mage.cards.p.ParallelThoughts.class)); cards.add(new SetCardInfo("Pemmin's Aura", 45, Rarity.UNCOMMON, mage.cards.p.PemminsAura.class)); cards.add(new SetCardInfo("Primitive Etchings", 126, Rarity.RARE, mage.cards.p.PrimitiveEtchings.class)); + cards.add(new SetCardInfo("Proteus Machine", 141, Rarity.UNCOMMON, mage.cards.p.ProteusMachine.class)); cards.add(new SetCardInfo("Putrid Raptor", 71, Rarity.UNCOMMON, mage.cards.p.PutridRaptor.class)); cards.add(new SetCardInfo("Pyrostatic Pillar", 100, Rarity.UNCOMMON, mage.cards.p.PyrostaticPillar.class)); cards.add(new SetCardInfo("Rain of Blades", 20, Rarity.UNCOMMON, mage.cards.r.RainOfBlades.class));