From 4ea0e19f681da8fc440de1f4054bd5e30b4186d8 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 17:18:50 +0100 Subject: [PATCH 01/56] Implemented Raging River --- Mage.Sets/src/mage/cards/r/RagingRiver.java | 211 ++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RagingRiver.java diff --git a/Mage.Sets/src/mage/cards/r/RagingRiver.java b/Mage.Sets/src/mage/cards/r/RagingRiver.java new file mode 100644 index 00000000000..6c6844f96a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RagingRiver.java @@ -0,0 +1,211 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public class RagingRiver extends CardImpl { + + public RagingRiver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{R}"); + + // Whenever one or more creatures you control attack, each defending player divides all creatures without flying he or she controls into a "left" pile and a "right" pile. Then, for each attacking creature you control, choose "left" or "right." That creature can't be blocked this combat except by creatures with flying and creatures in a pile with the chosen label. + this.addAbility(new AttacksWithCreaturesTriggeredAbility(new RagingRiverEffect(), 1)); + } + + public RagingRiver(final RagingRiver card) { + super(card); + } + + @Override + public RagingRiver copy() { + return new RagingRiver(this); + } +} + +class RagingRiverEffect extends OneShotEffect { + + public RagingRiverEffect() { + super(Outcome.Detriment); + staticText = "each defending player divides all creatures without flying he or she controls into a \"left\" pile and a \"right\" pile. Then, for each attacking creature you control, choose \"left\" or \"right.\" That creature can't be blocked this combat except by creatures with flying and creatures in a pile with the chosen label"; + } + + public RagingRiverEffect(final RagingRiverEffect effect) { + super(effect); + } + + @Override + public RagingRiverEffect copy() { + return new RagingRiverEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + List left = new ArrayList<>(); + List right = new ArrayList<>(); + + for (UUID defenderId : game.getCombat().getPlayerDefenders(game)) { + Player defender = game.getPlayer(defenderId); + if (defender != null) { + List leftLog = new ArrayList<>(); + List rightLog = new ArrayList<>(); + FilterControlledCreaturePermanent filterBlockers = new FilterControlledCreaturePermanent("creatures without flying you control to assign to the \"left\" pile (creatures not chosen will be assigned to the \"right\" pile)"); + filterBlockers.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + Target target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filterBlockers, true); + if (target.canChoose(source.getSourceId(), defenderId, game)) { + if (defender.chooseTarget(Outcome.Neutral, target, source, game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), defenderId, game)) { + if (target.getTargets().contains(permanent.getId())) { + left.add(permanent); + leftLog.add(permanent); + } + else if (filterBlockers.match(permanent, source.getSourceId(), defenderId, game)) { + right.add(permanent); + rightLog.add(permanent); + } + } + } + + // it could be nice to invoke some graphic indicator of which creature is Left or Right in this spot + StringBuilder sb = new StringBuilder("Left pile of ").append(defender.getLogName()).append(": "); + int i = 0; + for (Permanent permanent : leftLog) { + i++; + sb.append(permanent.getLogName()); + if (i < leftLog.size()) { + sb.append(", "); + } + } + game.informPlayers(sb.toString()); + + sb = new StringBuilder("Right pile of ").append(defender.getLogName()).append(": "); + i = 0; + for (Permanent permanent : rightLog) { + i++; + sb.append(permanent.getLogName()); + if (i < rightLog.size()) { + sb.append(", "); + } + } + game.informPlayers(sb.toString()); + } + } + } + + for (UUID attackers : game.getCombat().getAttackers()) { + Permanent attacker = game.getPermanent(attackers); + if (attacker != null && attacker.getControllerId() == controller.getId()) { + CombatGroup combatGroup = game.getCombat().findGroup(attacker.getId()); + if (combatGroup != null) { + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + Player defender = game.getPlayer(combatGroup.getDefendingPlayerId()); + if (defender != null) { + if (left.isEmpty() && right.isEmpty() && game.getOpponents(controller.getId()).size() < 2) { + // shortcut in case of a single opponent not having any creatures (since he has no further way of acquiring blockers with left/right labels, + // and Portal Mage can't potentially redirect the creature into an opponent with labeled blockers) + filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } else { + List leftLog = new ArrayList<>(); + List rightLog = new ArrayList<>(); + + for (Permanent permanent : left) { + if (permanent.getControllerId() == defender.getId()) { + leftLog.add(permanent); + } + } + for (Permanent permanent : right) { + if (permanent.getControllerId() == defender.getId()) { + rightLog.add(permanent); + } + } + + if (controller.choosePile(outcome, attacker.getName() + ": attacking " + defender.getName(), leftLog, rightLog, game)) { + filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(left)))); + game.informPlayers(attacker.getLogName() + ": attacks left (" + defender.getLogName() + ")"); + } else { + filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(right)))); + game.informPlayers(attacker.getLogName() + ": attacks right (" + defender.getLogName() + ")"); + } + } + RestrictionEffect effect = new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfCombat); + effect.setTargetPointer(new FixedTarget(attacker.getId())); + game.addEffect(effect, source); + } + } + } + } + return true; + } + return false; + } +} + +class PermanentInListPredicate implements Predicate { + + private final List permanents; + + public PermanentInListPredicate(List permanents) { + this.permanents = permanents; + } + + @Override + public boolean apply(Permanent input, Game game) { + return permanents.contains(input); + } +} From 9a0429e5b044a6f692df446ad665c7f4f042fd2b Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 17:20:56 +0100 Subject: [PATCH 02/56] Implemented Raging River --- 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 5aa69d73d1d..f0e7f85908b 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java @@ -211,6 +211,7 @@ public class LimitedEditionAlpha extends ExpansionSet { cards.add(new SetCardInfo("Psionic Blast", 75, Rarity.UNCOMMON, mage.cards.p.PsionicBlast.class)); cards.add(new SetCardInfo("Psychic Venom", 76, Rarity.COMMON, mage.cards.p.PsychicVenom.class)); cards.add(new SetCardInfo("Purelace", 216, Rarity.RARE, mage.cards.p.Purelace.class)); + cards.add(new SetCardInfo("Raging River", 169, Rarity.RARE, mage.cards.r.RagingRiver.class)); cards.add(new SetCardInfo("Raise Dead", 31, Rarity.COMMON, mage.cards.r.RaiseDead.class)); cards.add(new SetCardInfo("Red Elemental Blast", 170, Rarity.COMMON, mage.cards.r.RedElementalBlast.class)); cards.add(new SetCardInfo("Red Ward", 217, Rarity.UNCOMMON, mage.cards.r.RedWard.class)); From 6ccedcce0fcefa0175783f6aef94ab0c1c1a2fd4 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 17:22:21 +0100 Subject: [PATCH 03/56] Implemented Raging River --- 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 54499c973cb..b2e9975a633 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java @@ -216,6 +216,7 @@ public class LimitedEditionBeta extends ExpansionSet { cards.add(new SetCardInfo("Psionic Blast", 75, Rarity.UNCOMMON, mage.cards.p.PsionicBlast.class)); cards.add(new SetCardInfo("Psychic Venom", 76, Rarity.COMMON, mage.cards.p.PsychicVenom.class)); cards.add(new SetCardInfo("Purelace", 218, Rarity.RARE, mage.cards.p.Purelace.class)); + cards.add(new SetCardInfo("Raging River", 170, Rarity.RARE, mage.cards.r.RagingRiver.class)); cards.add(new SetCardInfo("Raise Dead", 31, Rarity.COMMON, mage.cards.r.RaiseDead.class)); cards.add(new SetCardInfo("Red Elemental Blast", 171, Rarity.COMMON, mage.cards.r.RedElementalBlast.class)); cards.add(new SetCardInfo("Red Ward", 219, Rarity.UNCOMMON, mage.cards.r.RedWard.class)); From 4c6b4b9a7eee171921452396572071091ae84fb5 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 17:22:50 +0100 Subject: [PATCH 04/56] Implemented Raging River --- 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 7e25dad5e6c..872c6232f48 100644 --- a/Mage.Sets/src/mage/sets/UnlimitedEdition.java +++ b/Mage.Sets/src/mage/sets/UnlimitedEdition.java @@ -216,6 +216,7 @@ public class UnlimitedEdition extends ExpansionSet { cards.add(new SetCardInfo("Psionic Blast", 75, Rarity.UNCOMMON, mage.cards.p.PsionicBlast.class)); cards.add(new SetCardInfo("Psychic Venom", 76, Rarity.COMMON, mage.cards.p.PsychicVenom.class)); cards.add(new SetCardInfo("Purelace", 217, Rarity.RARE, mage.cards.p.Purelace.class)); + cards.add(new SetCardInfo("Raging River", 169, Rarity.RARE, mage.cards.r.RagingRiver.class)); cards.add(new SetCardInfo("Raise Dead", 31, Rarity.COMMON, mage.cards.r.RaiseDead.class)); cards.add(new SetCardInfo("Red Elemental Blast", 170, Rarity.COMMON, mage.cards.r.RedElementalBlast.class)); cards.add(new SetCardInfo("Red Ward", 218, Rarity.UNCOMMON, mage.cards.r.RedWard.class)); From 491eba3910adfd9e3de8e32af1f85b0d4639c3c7 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 21:42:31 +0100 Subject: [PATCH 05/56] Added new restriction to second target --- .../src/mage/cards/b/BalduvianWarlord.java | 82 +++++++++++++------ 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index bd0ee58372a..12f654c3aab 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -27,6 +27,8 @@ */ package mage.cards.b; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.UUID; import mage.MageInt; @@ -46,6 +48,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterBlockingCreature; +import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; @@ -126,36 +129,65 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { // Choose new creature to block if (permanent.isCreature()) { - TargetAttackingCreature target = new TargetAttackingCreature(1, 1, new FilterAttackingCreature(), true); - if (target.canChoose(source.getSourceId(), controller.getId(), game)) { - while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) { - controller.chooseTarget(outcome, target, source, game); - } - } else { - return true; - } - Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); - if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) { - CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId()); - if (chosenGroup != null) { - // 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 = true; - if (!chosenGroup.getBlockers().isEmpty()) { - notYetBlocked = false; + // according to the following mail response from MTG Rules Management: + // "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C" + // therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know) + + List list = new ArrayList<>(); + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) { + for (UUID attackingCreatureId : combatGroup.getAttackers()) { + Permanent targetsControllerAttacker = game.getPermanent(attackingCreatureId); + list.add(targetsControllerAttacker); } - chosenGroup.addBlocker(permanent.getId(), controller.getId(), game); - 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())); } } - return true; + Player targetsController = game.getPlayer(permanent.getControllerId()); + if (targetsController != null) { + FilterAttackingCreature filter = new FilterAttackingCreature("creature attacking " + targetsController.getLogName()); + filter.add(new PermanentInListPredicate(list)); + TargetAttackingCreature target = new TargetAttackingCreature(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), controller.getId(), game)) { + while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) { + controller.chooseTarget(outcome, target, source, game); + } + } else { + return true; + } + Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); + if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) { + CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId()); + if (chosenGroup != null) { + // 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); + 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())); + } + } + } + return true; } } return false; } } + +class PermanentInListPredicate implements Predicate { + + private final List permanents; + + public PermanentInListPredicate(List permanents) { + this.permanents = permanents; + } + + @Override + public boolean apply(Permanent input, Game game) { + return permanents.contains(input); + } +} From 6b977f230fc626e384f4ff8a92471a1219d884e8 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 21:43:19 +0100 Subject: [PATCH 06/56] Comment clarification --- 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 12f654c3aab..2da4a8907d7 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -129,7 +129,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { // Choose new creature to block if (permanent.isCreature()) { - // according to the following mail response from MTG Rules Management: + // according to the following mail response from MTG Rules Management about False Orders: // "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C" // therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know) From 40f28d1176d5b5b6de3d02993a779b7b3e61e3c9 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 19 Nov 2017 21:44:59 +0100 Subject: [PATCH 07/56] Added new restriction to second target --- Mage.Sets/src/mage/cards/f/FalseOrders.java | 76 +++++++++++++++------ 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/FalseOrders.java b/Mage.Sets/src/mage/cards/f/FalseOrders.java index 91abb17e933..f8ab78aba4c 100644 --- a/Mage.Sets/src/mage/cards/f/FalseOrders.java +++ b/Mage.Sets/src/mage/cards/f/FalseOrders.java @@ -27,6 +27,8 @@ */ package mage.cards.f; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.UUID; import mage.abilities.Ability; @@ -43,6 +45,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.Predicate; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Controllable; import mage.game.Game; @@ -140,37 +143,66 @@ class FalseOrdersUnblockEffect extends OneShotEffect { // Choose new creature to block if (permanent.isCreature()) { if (controller.chooseUse(Outcome.Benefit, "Do you want " + permanent.getLogName() + " to block an attacking creature?", source, game)) { - TargetAttackingCreature target = new TargetAttackingCreature(1, 1, new FilterAttackingCreature(), true); - if (target.canChoose(source.getSourceId(), controller.getId(), game)) { - while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) { - controller.chooseTarget(outcome, target, source, game); + // according to the following mail response from MTG Rules Management about False Orders: + // "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C" + // therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know) + + List list = new ArrayList<>(); + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) { + for (UUID attackingCreatureId : combatGroup.getAttackers()) { + Permanent targetsControllerAttacker = game.getPermanent(attackingCreatureId); + list.add(targetsControllerAttacker); + } } - } else { - return true; } - Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); - if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) { - CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId()); - if (chosenGroup != null) { - // 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 = true; - if (!chosenGroup.getBlockers().isEmpty()) { - notYetBlocked = false; + Player targetsController = game.getPlayer(permanent.getControllerId()); + if (targetsController != null) { + FilterAttackingCreature filter = new FilterAttackingCreature("creature attacking " + targetsController.getLogName()); + filter.add(new PermanentInListPredicate(list)); + TargetAttackingCreature target = new TargetAttackingCreature(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), controller.getId(), game)) { + while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) { + controller.chooseTarget(outcome, target, source, game); } - chosenGroup.addBlocker(permanent.getId(), controller.getId(), game); - if (notYetBlocked) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null)); + } else { + return true; + } + Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); + if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) { + CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId()); + if (chosenGroup != null) { + // 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); + 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())); } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); } } } - return true; + return true; } } return false; } } + +class PermanentInListPredicate implements Predicate { + + private final List permanents; + + public PermanentInListPredicate(List permanents) { + this.permanents = permanents; + } + + @Override + public boolean apply(Permanent input, Game game) { + return permanents.contains(input); + } +} From 9f24d4c7dc737b022914cdf39c5fcbbc55d16d52 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Mon, 20 Nov 2017 20:36:33 +0100 Subject: [PATCH 08/56] Implemented Remove Enchantments --- .../src/mage/cards/r/RemoveEnchantments.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RemoveEnchantments.java diff --git a/Mage.Sets/src/mage/cards/r/RemoveEnchantments.java b/Mage.Sets/src/mage/cards/r/RemoveEnchantments.java new file mode 100644 index 00000000000..61871e8f836 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RemoveEnchantments.java @@ -0,0 +1,130 @@ +/* + * 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.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.predicate.ObjectPlayer; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.other.OwnerPredicate; +import mage.filter.predicate.permanent.AttachedToControlledPermanentPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public class RemoveEnchantments extends CardImpl { + + private static final FilterPermanent filter1 = new FilterControlledEnchantmentPermanent(); + private static final FilterPermanent filter2 = new FilterPermanent(); + private static final FilterPermanent filter3 = new FilterPermanent(); + private static final FilterPermanent filter4 = new FilterControlledEnchantmentPermanent(); + private static final FilterPermanent filter5 = new FilterPermanent(); + private static final FilterPermanent filter6 = new FilterPermanent(); + static { + // all enchantments you both own and control + filter1.add(new OwnerPredicate(TargetController.YOU)); + // all Auras you own attached to permanents you control + filter2.add(new AttachedToControlledPermanentPredicate()); + filter2.add(new SubtypePredicate(SubType.AURA)); + filter2.add(new OwnerPredicate(TargetController.YOU)); + // all Auras you own attached to attacking creatures your opponents control + filter3.add(new AttachedToOpponentControlledAttackingCreaturePredicate()); + filter3.add(new SubtypePredicate(SubType.AURA)); + filter3.add(new OwnerPredicate(TargetController.YOU)); + // all other enchantments you control (i.e. that you don't own) + filter4.add(new OwnerPredicate(TargetController.NOT_YOU)); + // all other Auras attached to permanents you control (i.e. that you don't own) + filter5.add(new AttachedToControlledPermanentPredicate()); + filter5.add(new SubtypePredicate(SubType.AURA)); + filter5.add(new OwnerPredicate(TargetController.NOT_YOU)); + // all other Auras attached to attacking creatures your opponents control (i.e. that you don't own) + filter6.add(new AttachedToOpponentControlledAttackingCreaturePredicate()); + filter6.add(new SubtypePredicate(SubType.AURA)); + filter6.add(new OwnerPredicate(TargetController.NOT_YOU)); + } + + public RemoveEnchantments(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + + // Return to your hand all enchantments you both own and control, all Auras you own attached to permanents you control, and all Auras you own attached to attacking creatures your opponents control. Then destroy all other enchantments you control, all other Auras attached to permanents you control, and all other Auras attached to attacking creatures your opponents control. + this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter1).setText("Return to your hand all enchantments you both own and control,")); + this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter2).setText(" all Auras you own attached to permanents you control")); + this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter3).setText("and all Auras you own attached to attacking creatures your opponents control")); + this.getSpellAbility().addEffect(new DestroyAllEffect(filter4).setText("Then destroy all other enchantments you control,")); + this.getSpellAbility().addEffect(new DestroyAllEffect(filter5).setText(" all other Auras attached to permanents you control")); + this.getSpellAbility().addEffect(new DestroyAllEffect(filter6).setText("and all other Auras attached to attacking creatures your opponents control")); + } + + public RemoveEnchantments(final RemoveEnchantments card) { + super(card); + } + + @Override + public RemoveEnchantments copy() { + return new RemoveEnchantments(this); + } +} + +class AttachedToOpponentControlledAttackingCreaturePredicate implements ObjectPlayerPredicate> { + + @Override + public boolean apply(ObjectPlayer input, Game game) { + Permanent attachement = input.getObject(); + if (attachement != null) { + Permanent permanent = game.getPermanent(attachement.getAttachedTo()); + if (permanent != null) { + if (permanent.isCreature()) { + if (permanent.isAttacking()) { + if (!permanent.getControllerId().equals(input.getPlayerId()) && + game.getPlayer(input.getPlayerId()).hasOpponent(permanent.getControllerId(), game)) { + return true; + } + } + } + } + } + return false; + } + + @Override + public String toString() { + return "Attached to attacking creatures your opponents control"; + } +} From db90718ca96db6fab12b158e87cf26d6cd0df8eb Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Mon, 20 Nov 2017 20:36:45 +0100 Subject: [PATCH 09/56] Implemented Remove Enchantments --- Mage.Sets/src/mage/sets/Legends.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index 971fc3b3c2c..bfed26208a2 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -219,6 +219,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Red Mana Battery", 236, Rarity.UNCOMMON, mage.cards.r.RedManaBattery.class)); cards.add(new SetCardInfo("Reincarnation", 115, Rarity.UNCOMMON, mage.cards.r.Reincarnation.class)); cards.add(new SetCardInfo("Relic Barrier", 237, Rarity.UNCOMMON, mage.cards.r.RelicBarrier.class)); + cards.add(new SetCardInfo("Remove Enchantments", 202, Rarity.COMMON, mage.cards.r.RemoveEnchantments.class)); cards.add(new SetCardInfo("Remove Soul", 72, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); cards.add(new SetCardInfo("Reset", 73, Rarity.UNCOMMON, mage.cards.r.Reset.class)); cards.add(new SetCardInfo("Revelation", 116, Rarity.RARE, mage.cards.r.Revelation.class)); From 24699f33779c31b46f736124cd3cb32ca87bbb9b Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 00:34:30 +0100 Subject: [PATCH 10/56] Implemented Glyph of Destruction --- .../src/mage/cards/g/GlyphOfDestruction.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GlyphOfDestruction.java diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfDestruction.java b/Mage.Sets/src/mage/cards/g/GlyphOfDestruction.java new file mode 100644 index 00000000000..c0b8195354e --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GlyphOfDestruction.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.g; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyTargetAtBeginningOfNextEndStepEffect; +import mage.abilities.effects.common.PreventDamageToTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.BlockingPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author L_J + */ +public class GlyphOfDestruction extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("blocking Wall you control"); + + static { + filter.add(new SubtypePredicate(SubType.WALL)); + filter.add(new BlockingPredicate()); + } + + public GlyphOfDestruction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); + + // Target blocking Wall you control gets +10/+0 until end of combat. Prevent all damage that would be dealt to it this turn. Destroy it at the beginning of the next end step. + this.getSpellAbility().addEffect(new BoostTargetEffect(10, 0, Duration.EndOfCombat)); + this.getSpellAbility().addEffect(new PreventDamageToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE).setText("Prevent all damage that would be dealt to it this turn")); + this.getSpellAbility().addEffect(new DestroyTargetAtBeginningOfNextEndStepEffect().setText("Destroy it at the beginning of the next end step")); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(filter)); + } + + public GlyphOfDestruction(final GlyphOfDestruction card) { + super(card); + } + + @Override + public GlyphOfDestruction copy() { + return new GlyphOfDestruction(this); + } +} From 97d68a2c3734b0661d7aa7a321e85df28c55707c Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 00:35:27 +0100 Subject: [PATCH 11/56] Implemented Glyph of Destruction --- Mage.Sets/src/mage/sets/Legends.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index bfed26208a2..307a7e305cd 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -128,6 +128,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Ghosts of the Damned", 12, Rarity.COMMON, mage.cards.g.GhostsOfTheDamned.class)); 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("Gravity Sphere", 149, Rarity.RARE, mage.cards.g.GravitySphere.class)); cards.add(new SetCardInfo("Great Defender", 185, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); cards.add(new SetCardInfo("Greater Realm of Preservation", 187, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class)); From bbc5f4594b6c535e74d2dc6b2ace2bc16bd754c2 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 17:03:15 +0100 Subject: [PATCH 12/56] Implemented Imprison --- .../effects/common/DoIfCostPaid.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java index 02a31543f2e..75f36ec46ca 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java @@ -16,6 +16,7 @@ import mage.util.CardUtil; public class DoIfCostPaid extends OneShotEffect { protected Effects executingEffects = new Effects(); + protected Effects otherwiseEffects = new Effects(); // used for Imprison private final Cost cost; private String chooseUseText; private boolean optional; @@ -24,6 +25,11 @@ public class DoIfCostPaid extends OneShotEffect { this(effect, cost, null); } + public DoIfCostPaid(Effect effect, Effect effect2, Cost cost) { + this(effect, cost, null, true); + this.otherwiseEffects.add(effect2); + } + public DoIfCostPaid(Effect effect, Cost cost, String chooseUseText) { this(effect, cost, chooseUseText, true); } @@ -39,6 +45,7 @@ public class DoIfCostPaid extends OneShotEffect { public DoIfCostPaid(final DoIfCostPaid effect) { super(effect); this.executingEffects = effect.executingEffects.copy(); + this.otherwiseEffects = effect.otherwiseEffects.copy(); this.cost = effect.cost.copy(); this.chooseUseText = effect.chooseUseText; this.optional = effect.optional; @@ -80,6 +87,26 @@ public class DoIfCostPaid extends OneShotEffect { } player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek } + else if (!otherwiseEffects.isEmpty()) { + for (Effect effect : otherwiseEffects) { + effect.setTargetPointer(this.targetPointer); + if (effect instanceof OneShotEffect) { + result &= effect.apply(game, source); + } else { + game.addEffect((ContinuousEffect) effect, source); + } + } + } + } + else if (!otherwiseEffects.isEmpty()) { + for (Effect effect : otherwiseEffects) { + effect.setTargetPointer(this.targetPointer); + if (effect instanceof OneShotEffect) { + result &= effect.apply(game, source); + } else { + game.addEffect((ContinuousEffect) effect, source); + } + } } return result; } @@ -99,7 +126,7 @@ public class DoIfCostPaid extends OneShotEffect { if (!staticText.isEmpty()) { return staticText; } - return (optional ? "you may " : "") + getCostText() + ". If you do, " + executingEffects.getText(mode); + return (optional ? "you may " : "") + getCostText() + ". If you do, " + executingEffects.getText(mode) + (!otherwiseEffects.isEmpty() ? " If you don't, " + otherwiseEffects.getText(mode) : ""); } protected String getCostText() { From e10f91156e849bb9b481ef4ce52b540e14c0bbe7 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 17:04:22 +0100 Subject: [PATCH 13/56] Implemented Imprison --- Mage.Sets/src/mage/cards/i/Imprison.java | 191 +++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/Imprison.java diff --git a/Mage.Sets/src/mage/cards/i/Imprison.java b/Mage.Sets/src/mage/cards/i/Imprison.java new file mode 100644 index 00000000000..d59d602fbdd --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/Imprison.java @@ -0,0 +1,191 @@ +/* + * 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.i; + +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DestroySourceEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.RemoveFromCombatTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.BlockedByOnlyOneCreatureThisCombatWatcher; + +/** + * + * @author L_J + */ +public class Imprison extends CardImpl { + + public Imprison(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever a player activates an ability of enchanted creature with {T} in its activation cost that isn't a mana ability, you may pay {1}. If you do, counter that ability. If you don't, destroy Imprison. + this.addAbility(new ImprisonTriggeredAbility()); + + // Whenever enchanted creature attacks or blocks, you may pay {1}. If you do, tap the creature, remove it from combat, and creatures it was blocking that had become blocked by only that creature this combat become unblocked. If you don't, destroy Imprison. + this.addAbility(new AttacksOrBlocksEnchantedTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid(new ImprisonUnblockEffect(), new DestroySourceEffect(), new ManaCostsImpl("1")))); + } + + public Imprison(final Imprison card) { + super(card); + } + + @Override + public Imprison copy() { + return new Imprison(this); + } +} + +class ImprisonTriggeredAbility extends TriggeredAbilityImpl { + + ImprisonTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid(new CounterTargetEffect().setText("counter that ability"), new DestroySourceEffect(), new ManaCostsImpl("1"))); + } + + ImprisonTriggeredAbility(final ImprisonTriggeredAbility ability) { + super(ability); + } + + @Override + public ImprisonTriggeredAbility copy() { + return new ImprisonTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent enchantment = game.getPermanentOrLKIBattlefield(sourceId); + if (enchantment != null && enchantment.getAttachedTo() != null) { + Permanent enchantedPermanent = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); + if (event.getSourceId().equals(enchantedPermanent.getId())) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (!(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) { + String abilityText = stackAbility.getRule(true); + if (abilityText.contains("{T},") || abilityText.contains("{T}:") || abilityText.contains("{T} or")) { + getEffects().get(0).setTargetPointer(new FixedTarget(stackAbility.getId())); + return true; + } + } + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever a player activates an ability of enchanted creature with {T} in its activation cost that isn't a mana ability, " + super.getRule(); + } +} + +class ImprisonUnblockEffect extends OneShotEffect { + + public ImprisonUnblockEffect() { + super(Outcome.Benefit); + this.staticText = "tap the creature, remove it from combat, and creatures it was blocking that had become blocked by only that creature this combat become unblocked"; + } + + public ImprisonUnblockEffect(final ImprisonUnblockEffect effect) { + super(effect); + } + + @Override + public ImprisonUnblockEffect copy() { + return new ImprisonUnblockEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null && enchantment.getAttachedTo() != null) { + Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); + if (permanent != null) { + if (permanent.isCreature()) { + + // Tap the creature + permanent.tap(game); + + // Remove it from combat + Effect effect = new RemoveFromCombatTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.apply(game, source); + + // Make blocked creatures unblocked + BlockedByOnlyOneCreatureThisCombatWatcher watcher = (BlockedByOnlyOneCreatureThisCombatWatcher) game.getState().getWatchers().get(BlockedByOnlyOneCreatureThisCombatWatcher.class.getSimpleName()); + if (watcher != null) { + Set combatGroups = watcher.getBlockedOnlyByCreature(permanent.getId()); + if (combatGroups != null) { + for (CombatGroup combatGroup : combatGroups) { + if (combatGroup != null) { + combatGroup.setBlocked(false); + } + } + } + } + return true; + } + } + } + return false; + } +} From 82aae29fbcb153ae059ad884a938bc62eebd8a95 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 17:05:08 +0100 Subject: [PATCH 14/56] Implemented Imprison --- Mage.Sets/src/mage/sets/Legends.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index 307a7e305cd..53bd34f1b5b 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -149,6 +149,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Hunding Gjornersen", 271, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); cards.add(new SetCardInfo("Hyperion Blacksmith", 150, Rarity.UNCOMMON, mage.cards.h.HyperionBlacksmith.class)); cards.add(new SetCardInfo("Immolation", 151, Rarity.COMMON, mage.cards.i.Immolation.class)); + cards.add(new SetCardInfo("Imprison", 21, Rarity.RARE, mage.cards.i.Imprison.class)); cards.add(new SetCardInfo("In the Eye of Chaos", 61, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class)); cards.add(new SetCardInfo("Indestructible Aura", 190, Rarity.COMMON, mage.cards.i.IndestructibleAura.class)); cards.add(new SetCardInfo("Infernal Medusa", 22, Rarity.UNCOMMON, mage.cards.i.InfernalMedusa.class)); From 5c8c77cd28269a739444a7cf3293306d84fce499 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 17:22:36 +0100 Subject: [PATCH 15/56] Minor usability improvement --- Mage.Sets/src/mage/cards/r/RagingRiver.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/r/RagingRiver.java b/Mage.Sets/src/mage/cards/r/RagingRiver.java index 6c6844f96a6..7ea9bc47b32 100644 --- a/Mage.Sets/src/mage/cards/r/RagingRiver.java +++ b/Mage.Sets/src/mage/cards/r/RagingRiver.java @@ -156,9 +156,8 @@ class RagingRiverEffect extends OneShotEffect { FilterCreaturePermanent filter = new FilterCreaturePermanent(); Player defender = game.getPlayer(combatGroup.getDefendingPlayerId()); if (defender != null) { - if (left.isEmpty() && right.isEmpty() && game.getOpponents(controller.getId()).size() < 2) { - // shortcut in case of a single opponent not having any creatures (since he has no further way of acquiring blockers with left/right labels, - // and Portal Mage can't potentially redirect the creature into an opponent with labeled blockers) + if (left.isEmpty() && right.isEmpty()) { + // shortcut in case of no labeled blockers available filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); } else { List leftLog = new ArrayList<>(); From e02c69d4faa11684851161f13e2a239d547c12de Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 21 Nov 2017 21:00:10 +0100 Subject: [PATCH 16/56] Ydwen Efreet ability fix --- Mage.Sets/src/mage/cards/y/YdwenEfreet.java | 29 +++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/cards/y/YdwenEfreet.java b/Mage.Sets/src/mage/cards/y/YdwenEfreet.java index 93f9d4ba918..e0a2ad84df0 100644 --- a/Mage.Sets/src/mage/cards/y/YdwenEfreet.java +++ b/Mage.Sets/src/mage/cards/y/YdwenEfreet.java @@ -27,6 +27,7 @@ */ package mage.cards.y; +import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -38,13 +39,14 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.game.Game; +import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.players.Player; - +import mage.watchers.common.BlockedByOnlyOneCreatureThisCombatWatcher; /** * - * @author MarcoMarin + * @author MarcoMarin & L_J */ public class YdwenEfreet extends CardImpl { @@ -72,7 +74,7 @@ class YdwenEfreetEffect extends OneShotEffect { public YdwenEfreetEffect() { super(Outcome.Damage); - staticText = "flip a coin. If you lose the flip, remove {this} from combat and it can't block."; + staticText = "flip a coin. If you lose the flip, remove {this} from combat and it can't block. Creatures it was blocking that had become blocked by only {this} this combat become unblocked"; } public YdwenEfreetEffect(YdwenEfreetEffect effect) { @@ -84,14 +86,25 @@ class YdwenEfreetEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent creature = game.getPermanent(source.getSourceId()); if (controller != null && creature != null) { - if (controller.flipCoin(game)) { - return true; - } else { + if (!controller.flipCoin(game)) { creature.removeFromCombat(game); creature.setMaxBlocks(0); - return true; + + // Make blocked creatures unblocked + BlockedByOnlyOneCreatureThisCombatWatcher watcher = (BlockedByOnlyOneCreatureThisCombatWatcher) game.getState().getWatchers().get(BlockedByOnlyOneCreatureThisCombatWatcher.class.getSimpleName()); + if (watcher != null) { + Set combatGroups = watcher.getBlockedOnlyByCreature(creature.getId()); + if (combatGroups != null) { + for (CombatGroup combatGroup : combatGroups) { + if (combatGroup != null) { + combatGroup.setBlocked(false); + } + } + } } } + return true; + } return false; } @@ -99,4 +112,4 @@ class YdwenEfreetEffect extends OneShotEffect { public YdwenEfreetEffect copy() { return new YdwenEfreetEffect(this); } -} \ No newline at end of file +} From 877a98d3d5a57fbe4063a89ba16e6c11f7895188 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 02:08:00 +0100 Subject: [PATCH 17/56] Implemented Defensive Formation --- ...erAssignCombatDamageToBlockersAbility.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Mage/src/main/java/mage/abilities/common/ControllerAssignCombatDamageToBlockersAbility.java diff --git a/Mage/src/main/java/mage/abilities/common/ControllerAssignCombatDamageToBlockersAbility.java b/Mage/src/main/java/mage/abilities/common/ControllerAssignCombatDamageToBlockersAbility.java new file mode 100644 index 00000000000..9c81fdb450c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/ControllerAssignCombatDamageToBlockersAbility.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011 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.abilities.common; + +import mage.abilities.MageSingleton; +import mage.abilities.StaticAbility; + +import java.io.ObjectStreamException; + +import mage.constants.AbilityType; +import mage.constants.Zone; + +/** + * @author L_J + */ +public class ControllerAssignCombatDamageToBlockersAbility extends StaticAbility implements MageSingleton { + + private static final ControllerAssignCombatDamageToBlockersAbility instance = new ControllerAssignCombatDamageToBlockersAbility(); + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static ControllerAssignCombatDamageToBlockersAbility getInstance() { + return instance; + } + + private ControllerAssignCombatDamageToBlockersAbility() { + super(AbilityType.STATIC, Zone.BATTLEFIELD); + } + + @Override + public String getRule() { + return "Rather than the attacking player, you assign the combat damage of each creature attacking you. You can divide that creature's combat damage as you choose among any of the creatures blocking it."; + } + + @Override + public ControllerAssignCombatDamageToBlockersAbility copy() { + return instance; + } + +} From 8b188b1a3e6e74070d1c0ce442dc57a53bbe6baf Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 02:09:15 +0100 Subject: [PATCH 18/56] Implemented Defensive Formation --- .../src/mage/cards/d/DefensiveFormation.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DefensiveFormation.java diff --git a/Mage.Sets/src/mage/cards/d/DefensiveFormation.java b/Mage.Sets/src/mage/cards/d/DefensiveFormation.java new file mode 100644 index 00000000000..babba4f36e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefensiveFormation.java @@ -0,0 +1,58 @@ +/* + * 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.common.ControllerAssignCombatDamageToBlockersAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; + +/** + * + * @author L_J + */ +public class DefensiveFormation extends CardImpl { + + public DefensiveFormation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + + // Rather than the attacking player, you assign the combat damage of each creature attacking you. You can divide that creature's combat damage as you choose among any of the creatures blocking it. + this.addAbility(ControllerAssignCombatDamageToBlockersAbility.getInstance()); + } + + public DefensiveFormation(final DefensiveFormation card) { + super(card); + } + + @Override + public DefensiveFormation copy() { + return new DefensiveFormation(this); + } +} From 573f82c94bca403ddec74bcfee3bfaec06daebc5 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 02:10:21 +0100 Subject: [PATCH 19/56] Implemented Defensive Formation --- Mage.Sets/src/mage/sets/UrzasSaga.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/UrzasSaga.java b/Mage.Sets/src/mage/sets/UrzasSaga.java index e6c428107ea..d30d3794ed2 100644 --- a/Mage.Sets/src/mage/sets/UrzasSaga.java +++ b/Mage.Sets/src/mage/sets/UrzasSaga.java @@ -118,6 +118,7 @@ public class UrzasSaga extends ExpansionSet { cards.add(new SetCardInfo("Darkest Hour", 128, Rarity.RARE, mage.cards.d.DarkestHour.class)); cards.add(new SetCardInfo("Dark Hatchling", 126, Rarity.RARE, mage.cards.d.DarkHatchling.class)); cards.add(new SetCardInfo("Dark Ritual", 127, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Defensive Formation", 9, Rarity.UNCOMMON, mage.cards.d.DefensiveFormation.class)); cards.add(new SetCardInfo("Despondency", 129, Rarity.COMMON, mage.cards.d.Despondency.class)); cards.add(new SetCardInfo("Destructive Urge", 180, Rarity.UNCOMMON, mage.cards.d.DestructiveUrge.class)); cards.add(new SetCardInfo("Diabolic Servitude", 130, Rarity.UNCOMMON, mage.cards.d.DiabolicServitude.class)); @@ -362,7 +363,7 @@ public class UrzasSaga extends ExpansionSet { cards.add(new SetCardInfo("Vug Lizard", 227, Rarity.UNCOMMON, mage.cards.v.VugLizard.class)); cards.add(new SetCardInfo("War Dance", 282, Rarity.UNCOMMON, mage.cards.w.WarDance.class)); cards.add(new SetCardInfo("Wall of Junk", 315, Rarity.UNCOMMON, mage.cards.w.WallOfJunk.class)); - cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class)); + cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class)); cards.add(new SetCardInfo("Western Paladin", 168, Rarity.RARE, mage.cards.w.WesternPaladin.class)); cards.add(new SetCardInfo("Whetstone", 316, Rarity.RARE, mage.cards.w.Whetstone.class)); cards.add(new SetCardInfo("Whirlwind", 283, Rarity.RARE, mage.cards.w.Whirlwind.class)); From bb56568ea1454f5cb9f1d03c8466bc231022ee15 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 02:13:22 +0100 Subject: [PATCH 20/56] Implemented Defensive Formation --- .../java/mage/game/combat/CombatGroup.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index fc40f7e9582..277233119d8 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility; import mage.abilities.common.DamageAsThoughNotBlockedAbility; import mage.abilities.keyword.CantBlockAloneAbility; import mage.abilities.keyword.DeathtouchAbility; @@ -244,6 +245,12 @@ public class CombatGroup implements Serializable, Copyable { blocker.markDamage(damage, attacker.getId(), game, true, true); } else { Player player = game.getPlayer(attacker.getControllerId()); + for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation + if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) { + player = game.getPlayer(defendingPlayerId); + break; + } + } int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); blocker.markDamage(damageAssigned, attacker.getId(), game, true, true); damage -= damageAssigned; @@ -270,6 +277,12 @@ public class CombatGroup implements Serializable, Copyable { return; } Player player = game.getPlayer(attacker.getControllerId()); + for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation + if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) { + player = game.getPlayer(defendingPlayerId); + break; + } + } int damage = getDamageValueFromPermanent(attacker, game); if (canDamage(attacker, first)) { // must be set before attacker damage marking because of effects like Test of Faith @@ -463,6 +476,12 @@ public class CombatGroup implements Serializable, Copyable { return; } Player player = game.getPlayer(playerId); + for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation + if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) { + player = game.getPlayer(defendingPlayerId); + break; + } + } List blockerList = new ArrayList<>(blockers); blockerOrder.clear(); while (player.canRespond()) { From 813219df2d688b8a33c494f78e74b94d16e3108a Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 11:28:36 +0100 Subject: [PATCH 21/56] Some changes to how Defensive Formation handles lethal damage --- .../main/java/mage/game/combat/CombatGroup.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 277233119d8..f64863280fa 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -276,10 +276,12 @@ public class CombatGroup implements Serializable, Copyable { if (attacker == null) { return; } + boolean oldRuleDamage = false; Player player = game.getPlayer(attacker.getControllerId()); for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) { player = game.getPlayer(defendingPlayerId); + oldRuleDamage = true; break; } } @@ -297,6 +299,7 @@ public class CombatGroup implements Serializable, Copyable { } Map assigned = new HashMap<>(); if (blocked) { + boolean excessDamageToDefender = true; for (UUID blockerId : blockerOrder) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { @@ -311,12 +314,20 @@ public class CombatGroup implements Serializable, Copyable { damage = 0; break; } - int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); + int damageAssigned = 0; + if (!oldRuleDamage) { + damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); + } else { + damageAssigned = player.getAmount(0, damage, "Assign damage to " + blocker.getName(), game); + if (damageAssigned < lethalDamage) { + excessDamageToDefender = false; // all blockers need to have lethal damage assigned before it can trample over to the defender + } + } assigned.put(blockerId, damageAssigned); damage -= damageAssigned; } } - if (damage > 0 && hasTrample(attacker)) { + if (damage > 0 && hasTrample(attacker) && excessDamageToDefender) { defenderDamage(attacker, damage, game); } else if (!blockerOrder.isEmpty()) { // Assign the damge left to first blocker From a40e80e9e5ffe064a8e5abe2ed434609c74836c1 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 20:21:30 +0100 Subject: [PATCH 22/56] Created new watcher for Giant Turtle --- .../common/AttackedLastTurnWatcher.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java diff --git a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java new file mode 100644 index 00000000000..d809424a3b3 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.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.watchers.common; + +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.MageObjectReference; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +/** + * + * @author L_J + */ +public class AttackedLastTurnWatcher extends Watcher { + + public final Map> attackedLastTurnCreatures = new HashMap<>(); // Map> + public final Map> attackedThisTurnCreatures = new HashMap<>(); // dummy map for beginning of turn iteration purposes + + public AttackedLastTurnWatcher() { + super(AttackedLastTurnWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public AttackedLastTurnWatcher(final AttackedLastTurnWatcher watcher) { + super(watcher); + for (Entry> entry : watcher.attackedLastTurnCreatures.entrySet()) { + Set allAttackersCopy = new HashSet<>(); + allAttackersCopy.addAll(entry.getValue()); + attackedLastTurnCreatures.put(entry.getKey(), allAttackersCopy); + } + for (Entry> entry : watcher.attackedThisTurnCreatures.entrySet()) { + Set allAttackersCopy = new HashSet<>(); + allAttackersCopy.addAll(entry.getValue()); + attackedThisTurnCreatures.put(entry.getKey(), allAttackersCopy); + } + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { + UUID activePlayer = game.getActivePlayerId(); + if (attackedLastTurnCreatures.containsKey(activePlayer)) { + if (attackedThisTurnCreatures.containsKey(activePlayer)) { + attackedThisTurnCreatures.remove(activePlayer); + } else { + attackedLastTurnCreatures.remove(activePlayer); + } + } + } + if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { + UUID attackingPlayer = game.getCombat().getAttackingPlayerId(); + Set attackingCreatures = getAttackedLastTurnCreatures(attackingPlayer); + for (UUID attackerId : game.getCombat().getAttackers()) { + Permanent attacker = game.getPermanent(attackerId); + if (attacker != null) { + attackingCreatures.add(new MageObjectReference(attacker, game)); + } + } + attackedLastTurnCreatures.put(attackingPlayer, attackingCreatures); + attackedThisTurnCreatures.put(attackingPlayer, attackingCreatures); + } + } + + public Set getAttackedLastTurnCreatures(UUID combatPlayerId) { + if (attackedLastTurnCreatures.get(combatPlayerId) != null) { + return attackedLastTurnCreatures.get(combatPlayerId); + } + return new HashSet<>(); + } + + @Override + public AttackedLastTurnWatcher copy() { + return new AttackedLastTurnWatcher(this); + } + +} From ba96cb1e7e0c12c60e82e0179ff9049b8b3c1284 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 20:23:28 +0100 Subject: [PATCH 23/56] Fixed Giant Turtle's ability (fixes #4143) --- Mage.Sets/src/mage/cards/g/GiantTurtle.java | 60 +++++---------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GiantTurtle.java b/Mage.Sets/src/mage/cards/g/GiantTurtle.java index 350400ee20a..694e0f7f624 100644 --- a/Mage.Sets/src/mage/cards/g/GiantTurtle.java +++ b/Mage.Sets/src/mage/cards/g/GiantTurtle.java @@ -27,7 +27,6 @@ */ package mage.cards.g; -import java.util.HashSet; import java.util.Set; import java.util.UUID; import mage.MageInt; @@ -40,12 +39,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.watchers.Watcher; +import mage.watchers.common.AttackedLastTurnWatcher; /** * @@ -61,7 +58,7 @@ public class GiantTurtle extends CardImpl { this.toughness = new MageInt(4); // Giant Turtle can't attack if it attacked during your last turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GiantTurtleCantAttackEffect()), new GiantTurtleWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackIfAttackedLastTurnEffect()), new AttackedLastTurnWatcher()); } public GiantTurtle(final GiantTurtle card) { @@ -74,14 +71,14 @@ public class GiantTurtle extends CardImpl { } } -class GiantTurtleCantAttackEffect extends RestrictionEffect { +class CantAttackIfAttackedLastTurnEffect extends RestrictionEffect { - public GiantTurtleCantAttackEffect() { + public CantAttackIfAttackedLastTurnEffect() { super(Duration.WhileOnBattlefield); staticText = "{this} can't attack if it attacked during your last turn"; } - public GiantTurtleCantAttackEffect(final GiantTurtleCantAttackEffect effect) { + public CantAttackIfAttackedLastTurnEffect(final CantAttackIfAttackedLastTurnEffect effect) { super(effect); } @@ -92,51 +89,20 @@ class GiantTurtleCantAttackEffect extends RestrictionEffect { @Override public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) { - GiantTurtleWatcher watcher = (GiantTurtleWatcher) game.getState().getWatchers().get(GiantTurtleWatcher.class.getSimpleName()); - for (MageObjectReference mor : watcher.getAttackedLastTurnCreatures()) { - if (attacker.equals(mor.getPermanent(game)) && attacker.getZoneChangeCounter(game) == mor.getZoneChangeCounter()) { + AttackedLastTurnWatcher watcher = (AttackedLastTurnWatcher) game.getState().getWatchers().get(AttackedLastTurnWatcher.class.getSimpleName()); + if (watcher != null) { + Set attackingCreatures = watcher.getAttackedLastTurnCreatures(attacker.getControllerId()); + MageObjectReference mor = new MageObjectReference(attacker, game); + if (attackingCreatures.contains(mor)) { return false; } } - return false; + return true; } @Override - public GiantTurtleCantAttackEffect copy() { - return new GiantTurtleCantAttackEffect(this); + public CantAttackIfAttackedLastTurnEffect copy() { + return new CantAttackIfAttackedLastTurnEffect(this); } } - -class GiantTurtleWatcher extends Watcher { - - public final Set attackedLastTurnCreatures = new HashSet<>(); - - public GiantTurtleWatcher() { - super(GiantTurtleWatcher.class.getSimpleName(), WatcherScope.GAME); - } - - public GiantTurtleWatcher(final GiantTurtleWatcher watcher) { - super(watcher); - this.attackedLastTurnCreatures.addAll(watcher.attackedLastTurnCreatures); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { - this.attackedLastTurnCreatures.add(new MageObjectReference(event.getSourceId(), game)); - } - if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { - this.attackedLastTurnCreatures.clear(); - } - } - - public Set getAttackedLastTurnCreatures() { - return this.attackedLastTurnCreatures; - } - - @Override - public GiantTurtleWatcher copy() { - return new GiantTurtleWatcher(this); - } -} From ee340c56bc97a0ad6c6e497cf862b6aa3799ff1f Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 20:40:16 +0100 Subject: [PATCH 24/56] Implemented Halls of Mist --- Mage.Sets/src/mage/cards/h/HallsOfMist.java | 110 ++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HallsOfMist.java diff --git a/Mage.Sets/src/mage/cards/h/HallsOfMist.java b/Mage.Sets/src/mage/cards/h/HallsOfMist.java new file mode 100644 index 00000000000..2b61856aa98 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HallsOfMist.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.Set; +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.watchers.common.AttackedLastTurnWatcher; + +/** + * + * @author L_J + */ +public class HallsOfMist extends CardImpl { + + public HallsOfMist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Cumulative upkeep-Pay {1}. + this.addAbility(new CumulativeUpkeepAbility(new GenericManaCost(1))); + + // Creatures that attacked during their controller's last turn can't attack. + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackIfAttackedLastTurnEffect()); + ability.addWatcher(new AttackedLastTurnWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE, false).setText("Creatures that attacked during their controller's last turn can't attack"))); + } + + public HallsOfMist(final HallsOfMist card) { + super(card); + } + + @Override + public HallsOfMist copy() { + return new HallsOfMist(this); + } +} + +class CantAttackIfAttackedLastTurnEffect extends RestrictionEffect { + + public CantAttackIfAttackedLastTurnEffect() { + super(Duration.WhileOnBattlefield); + } + + public CantAttackIfAttackedLastTurnEffect(final CantAttackIfAttackedLastTurnEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(source.getSourceId()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) { + AttackedLastTurnWatcher watcher = (AttackedLastTurnWatcher) game.getState().getWatchers().get(AttackedLastTurnWatcher.class.getSimpleName()); + if (watcher != null) { + Set attackingCreatures = watcher.getAttackedLastTurnCreatures(attacker.getControllerId()); + MageObjectReference mor = new MageObjectReference(attacker, game); + if (attackingCreatures.contains(mor)) { + return false; + } + } + return true; + } + + @Override + public CantAttackIfAttackedLastTurnEffect copy() { + return new CantAttackIfAttackedLastTurnEffect(this); + } + +} From 571a8af65a5ea41be60f907ccf0ced25718b16fa Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 20:41:07 +0100 Subject: [PATCH 25/56] Implemented Halls of Mist --- Mage.Sets/src/mage/sets/IceAge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index aae48d75611..8e752f504bf 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -159,6 +159,7 @@ public class IceAge extends ExpansionSet { cards.add(new SetCardInfo("Gravebind", 17, Rarity.RARE, mage.cards.g.Gravebind.class)); cards.add(new SetCardInfo("Green Scarab", 252, Rarity.UNCOMMON, mage.cards.g.GreenScarab.class)); cards.add(new SetCardInfo("Hallowed Ground", 253, Rarity.UNCOMMON, mage.cards.h.HallowedGround.class)); + cards.add(new SetCardInfo("Halls of Mist", 332, Rarity.RARE, mage.cards.h.HallsOfMist.class)); cards.add(new SetCardInfo("Heal", 254, Rarity.COMMON, mage.cards.h.Heal.class)); cards.add(new SetCardInfo("Hecatomb", 18, Rarity.RARE, mage.cards.h.Hecatomb.class)); cards.add(new SetCardInfo("Hematite Talisman", 295, Rarity.UNCOMMON, mage.cards.h.HematiteTalisman.class)); From f05b80561116e17f5897a95e258f1889f153ec8a Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 21:02:13 +0100 Subject: [PATCH 26/56] Made Halls of Mist a blanket effect instead of granting one to each creature --- Mage.Sets/src/mage/cards/h/HallsOfMist.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HallsOfMist.java b/Mage.Sets/src/mage/cards/h/HallsOfMist.java index 2b61856aa98..4f171f1ae0c 100644 --- a/Mage.Sets/src/mage/cards/h/HallsOfMist.java +++ b/Mage.Sets/src/mage/cards/h/HallsOfMist.java @@ -34,14 +34,12 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.RestrictionEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.watchers.common.AttackedLastTurnWatcher; @@ -59,9 +57,7 @@ public class HallsOfMist extends CardImpl { this.addAbility(new CumulativeUpkeepAbility(new GenericManaCost(1))); // Creatures that attacked during their controller's last turn can't attack. - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackIfAttackedLastTurnEffect()); - ability.addWatcher(new AttackedLastTurnWatcher()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE, false).setText("Creatures that attacked during their controller's last turn can't attack"))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackIfAttackedLastTurnAllEffect()), new AttackedLastTurnWatcher()); } public HallsOfMist(final HallsOfMist card) { @@ -74,19 +70,20 @@ public class HallsOfMist extends CardImpl { } } -class CantAttackIfAttackedLastTurnEffect extends RestrictionEffect { +class CantAttackIfAttackedLastTurnAllEffect extends RestrictionEffect { - public CantAttackIfAttackedLastTurnEffect() { + public CantAttackIfAttackedLastTurnAllEffect() { super(Duration.WhileOnBattlefield); + this.staticText = "Creatures that attacked during their controller's last turn can't attack"; } - public CantAttackIfAttackedLastTurnEffect(final CantAttackIfAttackedLastTurnEffect effect) { + public CantAttackIfAttackedLastTurnAllEffect(final CantAttackIfAttackedLastTurnAllEffect effect) { super(effect); } @Override public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getId().equals(source.getSourceId()); + return permanent.isCreature(); } @Override @@ -103,8 +100,8 @@ class CantAttackIfAttackedLastTurnEffect extends RestrictionEffect { } @Override - public CantAttackIfAttackedLastTurnEffect copy() { - return new CantAttackIfAttackedLastTurnEffect(this); + public CantAttackIfAttackedLastTurnAllEffect copy() { + return new CantAttackIfAttackedLastTurnAllEffect(this); } } From 2a8e3a8c62352c85425e05c649d120128e7cb016 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 22:40:34 +0100 Subject: [PATCH 27/56] Fixed Tallowisp predicate (fixes #4162) --- Mage.Sets/src/mage/cards/t/Tallowisp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/Tallowisp.java b/Mage.Sets/src/mage/cards/t/Tallowisp.java index 497400e4175..47c0401a86a 100644 --- a/Mage.Sets/src/mage/cards/t/Tallowisp.java +++ b/Mage.Sets/src/mage/cards/t/Tallowisp.java @@ -53,7 +53,7 @@ import mage.target.common.TargetCardInLibrary; */ public class Tallowisp extends CardImpl { - private static final FilterCard filterAura = new FilterCard("Aura card"); + private static final FilterCard filterAura = new FilterCard("Aura card with enchant creature"); static { filterAura.add(new CardTypePredicate(CardType.ENCHANTMENT)); @@ -93,7 +93,7 @@ class TallowispAbilityPredicate implements Predicate { for (int i = 0; i < abilities.size(); i++) { if (abilities.get(i) instanceof EnchantAbility) { String enchantText = abilities.get(i).getRule(); - if (enchantText.startsWith("Enchant") && enchantText.contains("creature")) { + if (enchantText.contentEquals("Enchant creature")) { return true; } } From 23df4f3bbd648b6303e665ae7bceb2b4e7860936 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 22:41:37 +0100 Subject: [PATCH 28/56] Implemented Rootwater Shaman --- .../src/mage/cards/r/RootwaterShaman.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RootwaterShaman.java diff --git a/Mage.Sets/src/mage/cards/r/RootwaterShaman.java b/Mage.Sets/src/mage/cards/r/RootwaterShaman.java new file mode 100644 index 00000000000..9f0ecfaa066 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RootwaterShaman.java @@ -0,0 +1,106 @@ +/* + * 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.MageInt; +import mage.MageObject; +import mage.abilities.Abilities; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; +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.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class RootwaterShaman extends CardImpl { + + private static final FilterCard filter = new FilterCard("Aura spells with enchant creature"); + static { + filter.add(new SubtypePredicate(SubType.AURA)); + filter.add(new RootwaterShamanAbilityPredicate()); + } + + public RootwaterShaman(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + this.subtype.add(SubType.MERFOLK); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // You may cast Aura spells with enchant creature as though they had flash. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CastAsThoughItHadFlashAllEffect(Duration.WhileOnBattlefield, filter, false))); + } + + public RootwaterShaman(final RootwaterShaman card) { + super(card); + } + + @Override + public RootwaterShaman copy() { + return new RootwaterShaman(this); + } + +} + +class RootwaterShamanAbilityPredicate implements Predicate { + + public RootwaterShamanAbilityPredicate() { + } + + @Override + public boolean apply(MageObject input, Game game) { + Abilities abilities = input.getAbilities(); + for (int i = 0; i < abilities.size(); i++) { + if (abilities.get(i) instanceof EnchantAbility) { + String enchantText = abilities.get(i).getRule(); + if (enchantText.contentEquals("Enchant creature")) { + return true; + } + } + } + return false; + } + + @Override + public String toString() { + return "Aura card with enchant creature"; + } +} From 58f5dd59b93b59ed5cfccb080d37b7ad82399e30 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Wed, 22 Nov 2017 22:42:51 +0100 Subject: [PATCH 29/56] Implemented Rootwater Shaman --- 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 a42508ca057..c5810baed90 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -260,6 +260,7 @@ public class Tempest extends ExpansionSet { cards.add(new SetCardInfo("Rootwater Diver", 81, Rarity.UNCOMMON, mage.cards.r.RootwaterDiver.class)); cards.add(new SetCardInfo("Rootwater Hunter", 82, Rarity.COMMON, mage.cards.r.RootwaterHunter.class)); cards.add(new SetCardInfo("Rootwater Matriarch", 83, Rarity.RARE, mage.cards.r.RootwaterMatriarch.class)); + cards.add(new SetCardInfo("Rootwater Shaman", 84, Rarity.RARE, mage.cards.r.RootwaterShaman.class)); cards.add(new SetCardInfo("Ruby Medallion", 295, Rarity.RARE, mage.cards.r.RubyMedallion.class)); cards.add(new SetCardInfo("Sacred Guide", 250, Rarity.RARE, mage.cards.s.SacredGuide.class)); cards.add(new SetCardInfo("Sadistic Glee", 47, Rarity.COMMON, mage.cards.s.SadisticGlee.class)); From ce3176adf268e77c23652cb617ab1424d0fbca80 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 23 Nov 2017 09:32:14 +0100 Subject: [PATCH 30/56] Fix for Giant Turtle/Halls of Mist watcher --- .../mage/watchers/common/AttackedLastTurnWatcher.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java index d809424a3b3..6a2a777b891 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java @@ -81,7 +81,7 @@ public class AttackedLastTurnWatcher extends Watcher { } if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { UUID attackingPlayer = game.getCombat().getAttackingPlayerId(); - Set attackingCreatures = getAttackedLastTurnCreatures(attackingPlayer); + Set attackingCreatures = getAttackedThisTurnCreatures(attackingPlayer); for (UUID attackerId : game.getCombat().getAttackers()) { Permanent attacker = game.getPermanent(attackerId); if (attacker != null) { @@ -100,6 +100,13 @@ public class AttackedLastTurnWatcher extends Watcher { return new HashSet<>(); } + public Set getAttackedThisTurnCreatures(UUID combatPlayerId) { + if (attackedThisTurnCreatures.get(combatPlayerId) != null) { + return attackedThisTurnCreatures.get(combatPlayerId); + } + return new HashSet<>(); + } + @Override public AttackedLastTurnWatcher copy() { return new AttackedLastTurnWatcher(this); From 0e2096395a24a0bc5c6e08d35bc6b7f24c76c057 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 23 Nov 2017 09:56:07 +0100 Subject: [PATCH 31/56] Another watcher fix --- .../watchers/common/AttackedLastTurnWatcher.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java index 6a2a777b891..52b4d29b8d0 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java @@ -71,12 +71,13 @@ public class AttackedLastTurnWatcher extends Watcher { public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { UUID activePlayer = game.getActivePlayerId(); - if (attackedLastTurnCreatures.containsKey(activePlayer)) { - if (attackedThisTurnCreatures.containsKey(activePlayer)) { - attackedThisTurnCreatures.remove(activePlayer); - } else { - attackedLastTurnCreatures.remove(activePlayer); + if (attackedThisTurnCreatures.containsKey(activePlayer)) { + if (attackedThisTurnCreatures.get(activePlayer) != null) { + attackedLastTurnCreatures.put(activePlayer, getAttackedThisTurnCreatures(activePlayer)); } + attackedThisTurnCreatures.remove(activePlayer); + } else { // } else if (attackedLastTurnCreatures.containsKey(activePlayer)) { + attackedLastTurnCreatures.remove(activePlayer); } } if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { @@ -88,7 +89,6 @@ public class AttackedLastTurnWatcher extends Watcher { attackingCreatures.add(new MageObjectReference(attacker, game)); } } - attackedLastTurnCreatures.put(attackingPlayer, attackingCreatures); attackedThisTurnCreatures.put(attackingPlayer, attackingCreatures); } } From 19ef8f47f273c574b4ba62155b9dc021c0b264cd Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 23 Nov 2017 10:07:54 +0100 Subject: [PATCH 32/56] Final fix --- .../main/java/mage/watchers/common/AttackedLastTurnWatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java index 52b4d29b8d0..96fe628d622 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java @@ -80,7 +80,7 @@ public class AttackedLastTurnWatcher extends Watcher { attackedLastTurnCreatures.remove(activePlayer); } } - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { + if (event.getType() == GameEvent.EventType.DECLARED_ATTACKERS) { UUID attackingPlayer = game.getCombat().getAttackingPlayerId(); Set attackingCreatures = getAttackedThisTurnCreatures(attackingPlayer); for (UUID attackerId : game.getCombat().getAttackers()) { From 3a0ab7797c69a2aaf4792b120d25d1f5f5fe366e Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 23 Nov 2017 10:33:19 +0100 Subject: [PATCH 33/56] Implemented Scarecrow --- Mage.Sets/src/mage/cards/s/Scarecrow.java | 114 ++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/Scarecrow.java diff --git a/Mage.Sets/src/mage/cards/s/Scarecrow.java b/Mage.Sets/src/mage/cards/s/Scarecrow.java new file mode 100644 index 00000000000..294765d49b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Scarecrow.java @@ -0,0 +1,114 @@ +/* + * 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.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.keyword.FlyingAbility; +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.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.events.DamagePlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public class Scarecrow extends CardImpl { + + public Scarecrow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + this.subtype.add(SubType.SCARECROW); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {6}, {T}: Prevent all damage that would be dealt to you this turn by attacking creatures without flying. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScarecrowEffect(), new GenericManaCost(6)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + } + + public Scarecrow(final Scarecrow card) { + super(card); + } + + @Override + public Scarecrow copy() { + return new Scarecrow(this); + } +} + +class ScarecrowEffect extends PreventionEffectImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + ScarecrowEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false); + staticText = "Prevent all damage that would be dealt to you this turn by creatures with flying"; + } + + ScarecrowEffect(final ScarecrowEffect effect) { + super(effect); + } + + @Override + public ScarecrowEffect copy() { + return new ScarecrowEffect(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 ca51661aa4be8e372615ead8bd505efc4871d0e9 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 23 Nov 2017 10:35:15 +0100 Subject: [PATCH 34/56] Implemented Tower of Coireall --- .../src/mage/cards/t/TowerOfCoireall.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TowerOfCoireall.java diff --git a/Mage.Sets/src/mage/cards/t/TowerOfCoireall.java b/Mage.Sets/src/mage/cards/t/TowerOfCoireall.java new file mode 100644 index 00000000000..737d27b1f28 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TowerOfCoireall.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.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect; +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.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class TowerOfCoireall extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Walls"); + static { + filter.add(new SubtypePredicate(SubType.WALL)); + } + + public TowerOfCoireall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + + // {tap}: Target creature can't be blocked by Walls this turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfTurn).setText("Target creature can't be blocked by Walls this turn"), new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public TowerOfCoireall(final TowerOfCoireall card) { + super(card); + } + + @Override + public TowerOfCoireall copy() { + return new TowerOfCoireall(this); + } +} From b065c267355a1bfa39c405c3cb551579d4ba3905 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 23 Nov 2017 10:41:44 +0100 Subject: [PATCH 35/56] Implemented Scarecrow and Tower of Coireall --- Mage.Sets/src/mage/sets/TheDark.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/TheDark.java b/Mage.Sets/src/mage/sets/TheDark.java index a3586cc625e..2362c07852c 100644 --- a/Mage.Sets/src/mage/sets/TheDark.java +++ b/Mage.Sets/src/mage/sets/TheDark.java @@ -120,6 +120,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Riptide", 34, Rarity.COMMON, mage.cards.r.Riptide.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)); cards.add(new SetCardInfo("Scarwood Bandits", 48, Rarity.RARE, mage.cards.s.ScarwoodBandits.class)); cards.add(new SetCardInfo("Scarwood Goblins", 119, Rarity.COMMON, mage.cards.s.ScarwoodGoblins.class)); cards.add(new SetCardInfo("Scarwood Hag", 49, Rarity.UNCOMMON, mage.cards.s.ScarwoodHag.class)); @@ -133,6 +134,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Sunken City", 35, Rarity.COMMON, mage.cards.s.SunkenCity.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)); cards.add(new SetCardInfo("Tracker", 52, Rarity.RARE, mage.cards.t.Tracker.class)); cards.add(new SetCardInfo("Uncle Istvan", 16, Rarity.UNCOMMON, mage.cards.u.UncleIstvan.class)); cards.add(new SetCardInfo("Venom", 53, Rarity.COMMON, mage.cards.v.Venom.class)); From 453058bf96f61705ec19866cbf47a4655e469b4e Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 14:32:00 +0100 Subject: [PATCH 36/56] Implemented Deepwood Elder --- Mage.Sets/src/mage/cards/d/DeepwoodElder.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DeepwoodElder.java diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodElder.java b/Mage.Sets/src/mage/cards/d/DeepwoodElder.java new file mode 100644 index 00000000000..814006196c5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeepwoodElder.java @@ -0,0 +1,78 @@ +/* + * 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.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetAdjustment; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +/** + * + * @author L_J + */ +public class DeepwoodElder extends CardImpl { + + public DeepwoodElder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{G}"); + this.subtype.add(SubType.DRYAD); + this.subtype.add(SubType.SPELLSHAPER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {X}{G}{G}, {T}, Discard a card: X target lands become Forests until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesBasicLandTargetEffect(Duration.EndOfTurn, SubType.FOREST).setText("X target lands become Forests until end of turn"), new ManaCostsImpl("{X}{G}{G}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_LANDS)); + ability.setTargetAdjustment(TargetAdjustment.X_TARGETS); + this.addAbility(ability); + } + + public DeepwoodElder(final DeepwoodElder card) { + super(card); + } + + @Override + public DeepwoodElder copy() { + return new DeepwoodElder(this); + } +} From 6be9f0301e5e56ec61020fd73d1d612b82652b31 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 14:32:56 +0100 Subject: [PATCH 37/56] Implemented Deepwood Elder --- Mage.Sets/src/mage/sets/MercadianMasques.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index ad0e9aa438a..edc70272ee5 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -123,6 +123,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Deadly Insect", 238, Rarity.COMMON, mage.cards.d.DeadlyInsect.class)); cards.add(new SetCardInfo("Deathgazer", 130, Rarity.UNCOMMON, mage.cards.d.Deathgazer.class)); cards.add(new SetCardInfo("Deepwood Drummer", 239, Rarity.COMMON, mage.cards.d.DeepwoodDrummer.class)); + cards.add(new SetCardInfo("Deepwood Elder", 240, Rarity.RARE, mage.cards.d.DeepwoodElder.class)); cards.add(new SetCardInfo("Deepwood Ghoul", 131, Rarity.COMMON, mage.cards.d.DeepwoodGhoul.class)); cards.add(new SetCardInfo("Deepwood Legate", 132, Rarity.UNCOMMON, mage.cards.d.DeepwoodLegate.class)); cards.add(new SetCardInfo("Deepwood Tantiv", 241, Rarity.UNCOMMON, mage.cards.d.DeepwoodTantiv.class)); From ab96a6440344534884fb7d30b83cd54bd0163e68 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:36:19 +0100 Subject: [PATCH 38/56] Implemented Charm Peddler --- Mage.Sets/src/mage/cards/c/CharmPeddler.java | 75 ++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CharmPeddler.java diff --git a/Mage.Sets/src/mage/cards/c/CharmPeddler.java b/Mage.Sets/src/mage/cards/c/CharmPeddler.java new file mode 100644 index 00000000000..77bb606ceb6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CharmPeddler.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.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect; +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.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class CharmPeddler extends CardImpl { + + public CharmPeddler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SPELLSHAPER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {W}, {T}, Discard a card: The next time a source of your choice would deal damage to target creature this turn, prevent that damage. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventNextDamageFromChosenSourceToTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{W}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public CharmPeddler(final CharmPeddler card) { + super(card); + } + + @Override + public CharmPeddler copy() { + return new CharmPeddler(this); + } +} From f93276de39a15c5e088b33cfc1387eae63896e85 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:37:04 +0100 Subject: [PATCH 39/56] Implemented Inviolability --- Mage.Sets/src/mage/cards/i/Inviolability.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/Inviolability.java diff --git a/Mage.Sets/src/mage/cards/i/Inviolability.java b/Mage.Sets/src/mage/cards/i/Inviolability.java new file mode 100644 index 00000000000..61d6e2a3091 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/Inviolability.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.i; + +import java.util.UUID; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.PreventAllDamageToAttachedEffect; +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.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class Inviolability extends CardImpl { + + public Inviolability(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Prevent all damage that would be dealt to enchanted creature. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PreventAllDamageToAttachedEffect(Duration.WhileOnBattlefield, "enchanted creature", false))); + } + + public Inviolability(final Inviolability card) { + super(card); + } + + @Override + public Inviolability copy() { + return new Inviolability(this); + } +} From 169668497f654e96c174c4ee776193420f8cf49f Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:37:38 +0100 Subject: [PATCH 40/56] Implemented Renounce --- Mage.Sets/src/mage/cards/r/Renounce.java | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/Renounce.java diff --git a/Mage.Sets/src/mage/cards/r/Renounce.java b/Mage.Sets/src/mage/cards/r/Renounce.java new file mode 100644 index 00000000000..2a7a017dc60 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Renounce.java @@ -0,0 +1,102 @@ +/* + * 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.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author L_J + */ +public class Renounce extends CardImpl { + + public Renounce(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + + // Sacrifice any number of permanents. You gain 2 life for each permanent sacrificed this way. + this.getSpellAbility().addEffect(new RenounceEffect()); + } + + public Renounce(final Renounce card) { + super(card); + } + + @Override + public Renounce copy() { + return new Renounce(this); + } +} + +class RenounceEffect extends OneShotEffect { + + public RenounceEffect() { + super(Outcome.Neutral); + staticText = "Sacrifice any number of permanents. You gain 2 life for each permanent sacrificed this way"; + } + + public RenounceEffect(final RenounceEffect effect) { + super(effect); + } + + @Override + public RenounceEffect copy() { + return new RenounceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null){ + return false; + } + int amount = 0; + TargetControlledPermanent toSacrifice = new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterControlledPermanent(), true); + if(player.chooseTarget(Outcome.Sacrifice, toSacrifice, source, game)) { + for(Object uuid : toSacrifice.getTargets()){ + Permanent permanent = game.getPermanent((UUID)uuid); + if(permanent != null){ + permanent.sacrifice(source.getSourceId(), game); + amount++; + } + } + player.gainLife(amount * 2, game); + } + return true; + } +} From cb97c3ab035c02cfe2c63e5322c8d83303ae0ebe Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:38:42 +0100 Subject: [PATCH 41/56] Implemented Trap Runner --- Mage.Sets/src/mage/cards/t/TrapRunner.java | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TrapRunner.java diff --git a/Mage.Sets/src/mage/cards/t/TrapRunner.java b/Mage.Sets/src/mage/cards/t/TrapRunner.java new file mode 100644 index 00000000000..3754f1fdb21 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrapRunner.java @@ -0,0 +1,124 @@ +/* + * 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.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.common.AfterBlockersAreDeclaredCondition; +import mage.abilities.condition.common.IsPhaseCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +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.TurnPhase; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.predicate.permanent.BlockedPredicate; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public class TrapRunner extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("unblocked attacking creature"); + + static { + filter.add(new AttackingPredicate()); + filter.add(Predicates.not(new BlockedPredicate())); + } + + public TrapRunner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {T}: Target unblocked attacking creature becomes blocked. Activate this ability only during combat after blockers are declared. + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new TrapRunnerEffect(), new TapSourceCost(), + new CompoundCondition("during combat after blockers are declared", new IsPhaseCondition(TurnPhase.COMBAT), AfterBlockersAreDeclaredCondition.instance)); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public TrapRunner(final TrapRunner card) { + super(card); + } + + @Override + public TrapRunner copy() { + return new TrapRunner(this); + } + +} + +class TrapRunnerEffect extends OneShotEffect { + + public TrapRunnerEffect() { + super(Outcome.Benefit); + this.staticText = "Target unblocked attacking creature becomes blocked"; + } + + public TrapRunnerEffect(final TrapRunnerEffect effect) { + super(effect); + } + + @Override + public TrapRunnerEffect copy() { + return new TrapRunnerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller != null && permanent != null) { + CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); + if (combatGroup != null) { + combatGroup.setBlocked(true); + game.informPlayers(permanent.getLogName() + " has become blocked"); + return true; + } + } + return false; + } +} From e6867682fb8b9db86ec8cb5d23d1dcd612c44938 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:39:17 +0100 Subject: [PATCH 42/56] Implemented Saprazzan Breaker --- .../src/mage/cards/s/SaprazzanBreaker.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java b/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java new file mode 100644 index 00000000000..d69e733e2a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java @@ -0,0 +1,106 @@ +/* + * 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.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByAllSourceEffect; +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.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public class SaprazzanBreaker extends CardImpl { + + public SaprazzanBreaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {U}: Put the top card of your library into your graveyard. If that card is a land card, Saprazzan Breaker can't be blocked this turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SaprazzanBreakerEffect(), new ManaCostsImpl("{U}"))); + } + + public SaprazzanBreaker(final SaprazzanBreaker card) { + super(card); + } + + @Override + public SaprazzanBreaker copy() { + return new SaprazzanBreaker(this); + } +} + +class SaprazzanBreakerEffect extends OneShotEffect { + + public SaprazzanBreakerEffect() { + super(Outcome.Benefit); + this.staticText = "Put the top card of your library into your graveyard. If that card is a land card, {this} can't be blocked this turn"; + } + + public SaprazzanBreakerEffect(final SaprazzanBreakerEffect effect) { + super(effect); + } + + @Override + public SaprazzanBreakerEffect copy() { + return new SaprazzanBreakerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + Card card = player.getLibrary().removeFromTop(game); + if (card != null) { + player.moveCards(card, Zone.GRAVEYARD, source, game); + if (card.isLand()) { + game.addEffect(new CantBeBlockedByAllSourceEffect(new FilterCreaturePermanent(), Duration.EndOfCombat), source); + } + } + return true; + } + return false; + } +} From 7915e6e973c27bbc63f57611b814781bb9c40ae9 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:39:46 +0100 Subject: [PATCH 43/56] Implemented Saprazzan Outrigger --- .../src/mage/cards/s/SaprazzanOutrigger.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SaprazzanOutrigger.java diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanOutrigger.java b/Mage.Sets/src/mage/cards/s/SaprazzanOutrigger.java new file mode 100644 index 00000000000..890b834dbf0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaprazzanOutrigger.java @@ -0,0 +1,67 @@ +/* + * 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.DelayedTriggeredAbility; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.PutOnLibrarySourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author L_J + */ +public class SaprazzanOutrigger extends CardImpl { + + public SaprazzanOutrigger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + this.subtype.add(SubType.MERFOLK); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // When Saprazzan Outrigger attacks or blocks, put it on top of its owner's library at end of combat. + DelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(new PutOnLibrarySourceEffect(true, "put it on top of its owner's library at end of combat")); + this.addAbility(new AttacksOrBlocksTriggeredAbility(new CreateDelayedTriggeredAbilityEffect(ability, true), false)); + } + + public SaprazzanOutrigger(final SaprazzanOutrigger card) { + super(card); + } + + @Override + public SaprazzanOutrigger copy() { + return new SaprazzanOutrigger(this); + } +} From 3e465bc7f121f2fe85f7203838680ca8d75a1bb1 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:40:42 +0100 Subject: [PATCH 44/56] Implemented Hired Giant --- Mage.Sets/src/mage/cards/HiredGiant.java | 118 +++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/HiredGiant.java diff --git a/Mage.Sets/src/mage/cards/HiredGiant.java b/Mage.Sets/src/mage/cards/HiredGiant.java new file mode 100644 index 00000000000..98c0229f925 --- /dev/null +++ b/Mage.Sets/src/mage/cards/HiredGiant.java @@ -0,0 +1,118 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +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.filter.common.FilterLandCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author emerald000 & L_J + */ +public class HiredGiant extends CardImpl { + + public HiredGiant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + this.subtype.add(SubType.GIANT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When Hired Giant enters the battlefield, each other player may search his or her library for a land card and put that card onto the battlefield. Then each player who searched his or her library this way shuffles it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new HiredGiantEffect())); + } + + public HiredGiant(final HiredGiant card) { + super(card); + } + + @Override + public HiredGiant copy() { + return new HiredGiant(this); + } +} + +class HiredGiantEffect extends OneShotEffect { + + HiredGiantEffect() { + super(Outcome.Detriment); + this.staticText = "each other player may search his or her library for a land card and put that card onto the battlefield. Then each player who searched his or her library this way shuffles it"; + } + + HiredGiantEffect(final HiredGiantEffect effect) { + super(effect); + } + + @Override + public HiredGiantEffect copy() { + return new HiredGiantEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Set playersThatSearched = new HashSet<>(1); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + if (playerId != controller.getId()) { + Player player = game.getPlayer(playerId); + if (player != null && player.chooseUse(Outcome.PutCreatureInPlay, "Search your library for a land card and put it onto the battlefield?", source, game)) { + TargetCardInLibrary target = new TargetCardInLibrary(new FilterLandCard()); + if (player.searchLibrary(target, game)) { + Card targetCard = player.getLibrary().getCard(target.getFirstTarget(), game); + if (targetCard != null) { + player.moveCards(targetCard, Zone.BATTLEFIELD, source, game); + playersThatSearched.add(player); + } + } + } + } + } + for (Player player : playersThatSearched) { + player.shuffleLibrary(source, game); + } + return true; + } + return false; + } +} From 35b6285d537e65d1ab5b765a04566044d06d4215 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:41:03 +0100 Subject: [PATCH 45/56] Moved Hired Giant --- Mage.Sets/src/mage/cards/{ => h}/HiredGiant.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Mage.Sets/src/mage/cards/{ => h}/HiredGiant.java (100%) diff --git a/Mage.Sets/src/mage/cards/HiredGiant.java b/Mage.Sets/src/mage/cards/h/HiredGiant.java similarity index 100% rename from Mage.Sets/src/mage/cards/HiredGiant.java rename to Mage.Sets/src/mage/cards/h/HiredGiant.java From 33ab3a5f4719aa7ec518736b366bb8a6a7ab817a Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:43:53 +0100 Subject: [PATCH 46/56] Implemented cards --- Mage.Sets/src/mage/sets/MercadianMasques.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index edc70272ee5..92bb4bc0ab8 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -93,6 +93,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Ceremonial Guard", 182, Rarity.COMMON, mage.cards.c.CeremonialGuard.class)); cards.add(new SetCardInfo("Chambered Nautilus", 64, Rarity.UNCOMMON, mage.cards.c.ChamberedNautilus.class)); cards.add(new SetCardInfo("Charisma", 66, Rarity.RARE, mage.cards.c.Charisma.class)); + cards.add(new SetCardInfo("Charm Peddler", 6, Rarity.COMMON, mage.cards.c.CharmPeddler.class)); cards.add(new SetCardInfo("Charmed Griffin", 7, Rarity.UNCOMMON, mage.cards.c.CharmedGriffin.class)); cards.add(new SetCardInfo("Cho-Arrim Alchemist", 8, Rarity.RARE, mage.cards.c.ChoArrimAlchemist.class)); cards.add(new SetCardInfo("Cho-Arrim Bruiser", 9, Rarity.RARE, mage.cards.c.ChoArrimBruiser.class)); @@ -175,6 +176,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("High Market", 320, Rarity.RARE, mage.cards.h.HighMarket.class)); cards.add(new SetCardInfo("High Seas", 83, Rarity.UNCOMMON, mage.cards.h.HighSeas.class)); cards.add(new SetCardInfo("Highway Robber", 139, Rarity.COMMON, mage.cards.h.HighwayRobber.class)); + cards.add(new SetCardInfo("Hired Giant", 194, Rarity.UNCOMMON, mage.cards.h.HiredGiant.class)); cards.add(new SetCardInfo("Honor the Fallen", 21, Rarity.RARE, mage.cards.h.HonorTheFallen.class)); cards.add(new SetCardInfo("Hoodwink", 84, Rarity.COMMON, mage.cards.h.Hoodwink.class)); cards.add(new SetCardInfo("Horned Troll", 251, Rarity.COMMON, mage.cards.h.HornedTroll.class)); @@ -186,6 +188,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Instigator", 140, Rarity.RARE, mage.cards.i.Instigator.class)); cards.add(new SetCardInfo("Intimidation", 142, Rarity.UNCOMMON, mage.cards.i.Intimidation.class)); cards.add(new SetCardInfo("Invigorate", 254, Rarity.COMMON, mage.cards.i.Invigorate.class)); + cards.add(new SetCardInfo("Inviolability", 23, Rarity.COMMON, mage.cards.i.Inviolability.class)); cards.add(new SetCardInfo("Iron Lance", 300, Rarity.UNCOMMON, mage.cards.i.IronLance.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)); @@ -259,6 +262,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Rampart Crawler", 156, Rarity.COMMON, mage.cards.r.RampartCrawler.class)); cards.add(new SetCardInfo("Rappelling Scouts", 41, Rarity.RARE, mage.cards.r.RappellingScouts.class)); cards.add(new SetCardInfo("Remote Farm", 323, Rarity.COMMON, mage.cards.r.RemoteFarm.class)); + cards.add(new SetCardInfo("Renounce", 42, Rarity.UNCOMMON, mage.cards.r.Renounce.class)); cards.add(new SetCardInfo("Reverent Mantra", 44, Rarity.RARE, mage.cards.r.ReverentMantra.class)); cards.add(new SetCardInfo("Revive", 262, Rarity.UNCOMMON, mage.cards.r.Revive.class)); cards.add(new SetCardInfo("Righteous Aura", 45, Rarity.UNCOMMON, mage.cards.r.RighteousAura.class)); @@ -281,9 +285,11 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Sand Squid", 96, Rarity.RARE, mage.cards.s.SandSquid.class)); cards.add(new SetCardInfo("Sandstone Needle", 326, Rarity.COMMON, mage.cards.s.SandstoneNeedle.class)); cards.add(new SetCardInfo("Saprazzan Bailiff", 97, Rarity.RARE, mage.cards.s.SaprazzanBailiff.class)); + cards.add(new SetCardInfo("Saprazzan Breaker", 98, Rarity.UNCOMMON, mage.cards.s.SaprazzanBreaker.class)); cards.add(new SetCardInfo("Saprazzan Cove", 327, Rarity.UNCOMMON, mage.cards.s.SaprazzanCove.class)); cards.add(new SetCardInfo("Saprazzan Heir", 99, Rarity.RARE, mage.cards.s.SaprazzanHeir.class)); cards.add(new SetCardInfo("Saprazzan Legate", 100, Rarity.UNCOMMON, mage.cards.s.SaprazzanLegate.class)); + cards.add(new SetCardInfo("Saprazzan Outrigger", 101, Rarity.COMMON, mage.cards.s.SaprazzanOutrigger.class)); cards.add(new SetCardInfo("Saprazzan Raider", 102, Rarity.COMMON, mage.cards.s.SaprazzanRaider.class)); cards.add(new SetCardInfo("Saprazzan Skerry", 328, Rarity.COMMON, mage.cards.s.SaprazzanSkerry.class)); cards.add(new SetCardInfo("Scandalmonger", 158, Rarity.UNCOMMON, mage.cards.s.Scandalmonger.class)); @@ -341,6 +347,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Toymaker", 314, Rarity.UNCOMMON, mage.cards.t.Toymaker.class)); cards.add(new SetCardInfo("Trade Routes", 112, Rarity.RARE, mage.cards.t.TradeRoutes.class)); cards.add(new SetCardInfo("Tranquility", 280, Rarity.COMMON, mage.cards.t.Tranquility.class)); + cards.add(new SetCardInfo("Trap Runner", 55, Rarity.UNCOMMON, mage.cards.t.TrapRunner.class)); cards.add(new SetCardInfo("Tremor", 220, Rarity.COMMON, mage.cards.t.Tremor.class)); cards.add(new SetCardInfo("Two-Headed Dragon", 221, Rarity.RARE, mage.cards.t.TwoHeadedDragon.class)); cards.add(new SetCardInfo("Undertaker", 167, Rarity.COMMON, mage.cards.u.Undertaker.class)); From dc4320acba7705e821240dbacfa4bc4f033bfa60 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 19:57:52 +0100 Subject: [PATCH 47/56] Implemented Skyshroud War Beast --- .../src/mage/cards/s/SkyshroudWarBeast.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java diff --git a/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java new file mode 100644 index 00000000000..852765bb840 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java @@ -0,0 +1,119 @@ +/* + * 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.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ChooseOpponentEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author jeffwadsworth, fireshoes & L_J + */ +public class SkyshroudWarBeast extends CardImpl { + + public SkyshroudWarBeast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As Skyshroud War Beast enters the battlefield, choose an opponent. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseOpponentEffect(Outcome.Detriment))); + + // Skyshroud War Beast's power is equal to the number of tapped lands the chosen player controls. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new SkyshroudWarBeastCount(), Duration.WhileOnBattlefield, SubLayer.CharacteristicDefining_7a))); + } + + public SkyshroudWarBeast(final SkyshroudWarBeast card) { + super(card); + } + + @Override + public SkyshroudWarBeast copy() { + return new SkyshroudWarBeast(this); + } +} + +class SkyshroudWarBeastCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + if (sourceAbility != null) { + UUID playerId = (UUID) game.getState().getValue(sourceAbility.getSourceId() + ChooseOpponentEffect.VALUE_KEY); + Player chosenPlayer = game.getPlayer(playerId); + if (chosenPlayer != null) { + FilterLandPermanent filter = new FilterLandPermanent("nonbasic lands the chosen player controls"); + filter.add(Predicates.not(new SupertypePredicate(SuperType.BASIC))); + filter.add(new ControllerIdPredicate(playerId)); + return game.getBattlefield().count(filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); + } + } + return 0; + } + + @Override + public DynamicValue copy() { + return new SkyshroudWarBeastCount(); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "nonbasic lands the chosen player controls"; + } +} From 41f617066a2b5397ca28d60f5018e02a392b8610 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 19:58:34 +0100 Subject: [PATCH 48/56] Implemented Skyshroud War Beast --- 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 59c6cd1644f..63672df14d3 100644 --- a/Mage.Sets/src/mage/sets/Exodus.java +++ b/Mage.Sets/src/mage/sets/Exodus.java @@ -150,6 +150,7 @@ public class Exodus extends ExpansionSet { cards.add(new SetCardInfo("Shield Mate", 19, Rarity.COMMON, mage.cards.s.ShieldMate.class)); cards.add(new SetCardInfo("Skyshaper", 137, Rarity.UNCOMMON, mage.cards.s.Skyshaper.class)); cards.add(new SetCardInfo("Skyshroud Elite", 123, Rarity.UNCOMMON, mage.cards.s.SkyshroudElite.class)); + cards.add(new SetCardInfo("Skyshroud War Beast", 124, Rarity.RARE, mage.cards.s.SkyshroudWarBeast.class)); cards.add(new SetCardInfo("Slaughter", 74, Rarity.UNCOMMON, mage.cards.s.Slaughter.class)); cards.add(new SetCardInfo("Soltari Visionary", 20, Rarity.COMMON, mage.cards.s.SoltariVisionary.class)); cards.add(new SetCardInfo("Song of Serenity", 125, Rarity.UNCOMMON, mage.cards.s.SongOfSerenity.class)); From 85740aad36b8a5a1ff4077d97e4dfef38067afc8 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 06:01:58 +0100 Subject: [PATCH 49/56] Imported Dark Sphere from https://github.com/ThomasLerner/mage --- Mage.Sets/src/mage/cards/d/DarkSphere.java | 152 +++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DarkSphere.java diff --git a/Mage.Sets/src/mage/cards/d/DarkSphere.java b/Mage.Sets/src/mage/cards/d/DarkSphere.java new file mode 100644 index 00000000000..d08efb04fb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DarkSphere.java @@ -0,0 +1,152 @@ +/* + * 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 +/* + * 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.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +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.filter.FilterObject; +import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.TargetSource; + +/** + * + * @author ThomasLerner + */ +public class DarkSphere extends CardImpl { + + public DarkSphere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); + + + // {tap}, Sacrifice Dark Sphere: The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DarkSpherePreventionEffect(), new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + public DarkSphere(final DarkSphere card) { + super(card); + } + + @Override + public DarkSphere copy() { + return new DarkSphere(this); + } +} + + +class DarkSpherePreventionEffect extends ReplacementEffectImpl { + + private final TargetSource targetSource; + + public DarkSpherePreventionEffect() { + super(Duration.OneUse, Outcome.RedirectDamage); + this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down"; + this.targetSource = new TargetSource(new FilterObject("source of your choice")); + } + + public DarkSpherePreventionEffect(final DarkSpherePreventionEffect effect) { + super(effect); + this.targetSource = effect.targetSource.copy(); + } + + @Override + public DarkSpherePreventionEffect copy() { + return new DarkSpherePreventionEffect(this); + } + + @Override + public void init(Ability source, Game game) { + this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + DamageEvent damageEvent = (DamageEvent) event; + if (controller != null) { + controller.damage((int) Math.ceil(damageEvent.getAmount() / 2.0), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects()); + StringBuilder sb = new StringBuilder(sourceObject != null ? sourceObject.getLogName() : ""); + sb.append(": ").append(damageEvent.getAmount() / 2).append(" damage prevented"); + sb.append(" from ").append(controller.getLogName()); + game.informPlayers(sb.toString()); + discard(); // only one use + return true; + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getControllerId())) { + return true; + } + return false; + } + +} From 43f22cc256fb8bda87ea024b2b29a152843ca935 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 06:02:56 +0100 Subject: [PATCH 50/56] Imported Dark Sphere from https://github.com/ThomasLerner/mage --- Mage.Sets/src/mage/sets/TheDark.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/TheDark.java b/Mage.Sets/src/mage/sets/TheDark.java index 2362c07852c..4561ec28467 100644 --- a/Mage.Sets/src/mage/sets/TheDark.java +++ b/Mage.Sets/src/mage/sets/TheDark.java @@ -72,6 +72,7 @@ public class TheDark extends ExpansionSet { cards.add(new SetCardInfo("Coal Golem", 96, Rarity.UNCOMMON, mage.cards.c.CoalGolem.class)); cards.add(new SetCardInfo("Dance of Many", 21, Rarity.RARE, mage.cards.d.DanceOfMany.class)); cards.add(new SetCardInfo("Dark Heart of the Wood", 117, Rarity.COMMON, mage.cards.d.DarkHeartOfTheWood.class)); + cards.add(new SetCardInfo("Dark Sphere", 97, Rarity.RARE, mage.cards.d.DarkSphere.class)); cards.add(new SetCardInfo("Deep Water", 22, Rarity.COMMON, mage.cards.d.DeepWater.class)); cards.add(new SetCardInfo("Diabolic Machine", 98, Rarity.UNCOMMON, mage.cards.d.DiabolicMachine.class)); cards.add(new SetCardInfo("Drowned", 23, Rarity.COMMON, mage.cards.d.Drowned.class)); From 92e5d8de0aa16978bfc72694a25e031e185e5829 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 07:20:25 +0100 Subject: [PATCH 51/56] multiAttackerDamage fix --- Mage/src/main/java/mage/game/combat/CombatGroup.java | 6 +++++- 1 file changed, 5 insertions(+), 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 f64863280fa..5b98721b08b 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -330,7 +330,7 @@ public class CombatGroup implements Serializable, Copyable { if (damage > 0 && hasTrample(attacker) && excessDamageToDefender) { defenderDamage(attacker, damage, game); } else if (!blockerOrder.isEmpty()) { - // Assign the damge left to first blocker + // Assign the damage left to first blocker assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) + damage); } } @@ -413,6 +413,10 @@ public class CombatGroup implements Serializable, Copyable { damage -= damageAssigned; } } + if (damage > 0) { + // Assign the damage left to first attacker + assigned.put(attackerOrder.get(0), assigned.get(attackerOrder.get(0)) + damage); + } for (Map.Entry entry : assigned.entrySet()) { Permanent attacker = game.getPermanent(entry.getKey()); From 7cf73917e6cb7613fe413e1862664e3cc6d85521 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 07:47:39 +0100 Subject: [PATCH 52/56] Added mustBlockAllAttackers --- .../main/java/mage/abilities/effects/RequirementEffect.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java b/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java index 89afa2bd491..3830dd0b189 100644 --- a/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java @@ -27,6 +27,7 @@ */ package mage.abilities.effects; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.constants.Duration; @@ -98,6 +99,10 @@ public abstract class RequirementEffect extends ContinuousEffectImpl { return null; } + public List mustBlockAllAttackers(Ability source, Game game) { + return null; + } + /** * Player related check The player returned or controlled planeswalker must * be attacked with at least one attacker From a7514fc6711dcbd5784f075549c9cb07f747fa6d Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 07:49:30 +0100 Subject: [PATCH 53/56] Added mustBlockAllAttackers --- .../java/mage/abilities/effects/RequirementEffect.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java b/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java index 3830dd0b189..c52d1811706 100644 --- a/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/RequirementEffect.java @@ -27,7 +27,6 @@ */ package mage.abilities.effects; -import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.constants.Duration; @@ -79,6 +78,10 @@ public abstract class RequirementEffect extends ContinuousEffectImpl { public boolean mustBlockAny(Game game) { return false; } + + public boolean mustBlockAllAttackers(Game game) { + return false; + } /** * Defines the defender a attacker has to attack @@ -99,10 +102,6 @@ public abstract class RequirementEffect extends ContinuousEffectImpl { return null; } - public List mustBlockAllAttackers(Ability source, Game game) { - return null; - } - /** * Player related check The player returned or controlled planeswalker must * be attacked with at least one attacker From b035c436d5759996cd4a5f41bd99712f70df40e0 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 08:35:18 +0100 Subject: [PATCH 54/56] Another small fix --- Mage/src/main/java/mage/game/combat/CombatGroup.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 5b98721b08b..695dbed559f 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -566,6 +566,9 @@ public class CombatGroup implements Serializable, Copyable { if (attackers.contains(creatureId)) { attackers.remove(creatureId); result = true; + if (attackerOrder.contains(creatureId)) { + attackerOrder.remove(creatureId); + } } else if (blockers.contains(creatureId)) { blockers.remove(creatureId); result = true; From 200e5e07bf0395af3f02425a6a4b0b7999c23feb Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 08:48:21 +0100 Subject: [PATCH 55/56] Hollowhenge Spirit fix (partial fix for #4172) --- Mage/src/main/java/mage/game/combat/Combat.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index df50deaf891..45b7894b720 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -1201,6 +1201,7 @@ public class Combat implements Serializable, Copyable { for (CombatGroup group : groups) { result |= group.remove(creatureId); } + blockingGroups.remove(creatureId); if (result && withInfo) { game.informPlayers(creature.getLogName() + " removed from combat"); } From 32ef8a5047b413cc846cf7f98a2c3964715b5edd Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 25 Nov 2017 09:10:01 +0100 Subject: [PATCH 56/56] Small damage fix --- Mage/src/main/java/mage/game/combat/CombatGroup.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 695dbed559f..44a4e52f383 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -406,6 +406,7 @@ public class CombatGroup implements Serializable, Copyable { } if (lethalDamage >= damage) { assigned.put(attackerId, damage); + damage = 0; break; } int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + attacker.getName(), game);