diff --git a/Mage.Sets/src/mage/cards/v/VindictiveLich.java b/Mage.Sets/src/mage/cards/v/VindictiveLich.java index 57a2da8d736..bf8182a2d25 100644 --- a/Mage.Sets/src/mage/cards/v/VindictiveLich.java +++ b/Mage.Sets/src/mage/cards/v/VindictiveLich.java @@ -1,106 +1,105 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.cards.v; - -import java.util.UUID; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.common.DiesTriggeredAbility; -import mage.abilities.effects.common.LoseLifeTargetEffect; -import mage.abilities.effects.common.SacrificeEffect; -import mage.abilities.effects.common.discard.DiscardTargetEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.filter.FilterOpponent; -import mage.filter.StaticFilters; -import mage.filter.predicate.mageobject.AnotherTargetPredicate; -import mage.target.Target; -import mage.target.common.TargetOpponent; - -/** - * - * @author anonymous - */ - - - public class VindictiveLich extends CardImpl { - -public VindictiveLich(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); - - this.subtype.add("Zombie"); - this.subtype.add("Wizard"); - this.power = new MageInt(4); - this.toughness = new MageInt(1); - - // When Vindictive Lich dies, choose one or more. Each mode must target a different player. - // *Target opponent sacrifices a creature. - Ability ability = new DiesTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent")); - ability.getModes().setMinModes(1); - ability.getModes().setMaxModes(3); - - FilterOpponent filter = new FilterOpponent(); - filter.add(new AnotherTargetPredicate(1)); - Target target = new TargetOpponent(filter, false); - target.setTargetTag(1); - ability.addTarget(target); - - // *Target opponent discards two cards. - Mode mode = new Mode(); - mode.getEffects().add(new DiscardTargetEffect(2, false)); - filter = new FilterOpponent(); - filter.add(new AnotherTargetPredicate(2)); - target = new TargetOpponent(filter, false); - target.setTargetTag(2); - mode.getTargets().add(target); - ability.addMode(mode); - - // *Target opponent loses 5 life. - mode = new Mode(); - mode.getEffects().add(new LoseLifeTargetEffect(2)); - filter = new FilterOpponent(); - filter.add(new AnotherTargetPredicate(2)); - target = new TargetOpponent(filter, false); - target.setTargetTag(3); - mode.getTargets().add(target); - ability.addMode(mode); - this.addAbility(ability); -} - -public VindictiveLich(final VindictiveLich card) { - super(card); -} - -@Override -public VindictiveLich copy() { - return new VindictiveLich(this); -} - -} +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterOpponent; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.target.Target; +import mage.target.common.TargetOpponent; + +/** + * + * @author anonymous + */ +public class VindictiveLich extends CardImpl { + + public VindictiveLich(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.ZOMBIE, SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(1); + + // When Vindictive Lich dies, choose one or more. Each mode must target a different player. + // *Target opponent sacrifices a creature. + Ability ability = new DiesTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent")); + ability.getModes().setMinModes(1); + ability.getModes().setMaxModes(3); + ability.getModes().setMaxModesFilter(new FilterOpponent("a different player")); + + FilterOpponent filter = new FilterOpponent("opponent (sacrifice)"); + filter.add(new AnotherTargetPredicate(1, true)); + Target target = new TargetOpponent(filter, false); + target.setTargetTag(1); + ability.addTarget(target); + + // *Target opponent discards two cards. + Mode mode = new Mode(); + mode.getEffects().add(new DiscardTargetEffect(2, false)); + filter = new FilterOpponent("opponent (discard)"); + filter.add(new AnotherTargetPredicate(2, true)); + target = new TargetOpponent(filter, false); + target.setTargetTag(2); + mode.getTargets().add(target); + ability.addMode(mode); + + // *Target opponent loses 5 life. + mode = new Mode(); + mode.getEffects().add(new LoseLifeTargetEffect(2)); + filter = new FilterOpponent("opponent (life loss)"); + filter.add(new AnotherTargetPredicate(3, true)); + target = new TargetOpponent(filter, false); + target.setTargetTag(3); + mode.getTargets().add(target); + ability.addMode(mode); + this.addAbility(ability); + } + + public VindictiveLich(final VindictiveLich card) { + super(card); + } + + @Override + public VindictiveLich copy() { + return new VindictiveLich(this); + } + +} diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index d8c8e42fd73..c06591cb3fd 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -38,6 +38,8 @@ import mage.abilities.costs.OptionalAdditionalModeSourceCosts; import mage.cards.Card; import mage.constants.Outcome; import mage.constants.TargetController; +import mage.filter.Filter; +import mage.filter.FilterPlayer; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; @@ -57,6 +59,7 @@ public class Modes extends LinkedHashMap { private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists private final LinkedHashMap duplicateModes = new LinkedHashMap<>(); private OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts = null; // only set if costs have to be paid + private Filter maxModesFilter = null; // calculates the max number of available modes public Modes() { this.currentMode = new Mode(); @@ -89,6 +92,7 @@ public class Modes extends LinkedHashMap { this.eachModeOnlyOnce = modes.eachModeOnlyOnce; this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce; this.optionalAdditionalModeSourceCosts = modes.optionalAdditionalModeSourceCosts; + this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed } public Modes copy() { @@ -151,6 +155,14 @@ public class Modes extends LinkedHashMap { this.maxModes = maxModes; } + public Filter getMaxModesFilter() { + return maxModesFilter; + } + + public void setMaxModesFilter(Filter maxModesFilter) { + this.maxModesFilter = maxModesFilter; + } + public int getMaxModes() { return this.maxModes; } @@ -230,7 +242,19 @@ public class Modes extends LinkedHashMap { // player chooses modes manually this.currentMode = null; - while (this.selectedModes.size() < this.getMaxModes()) { + int currentMaxModes = this.getMaxModes(); + if (getMaxModesFilter() != null) { + if (maxModesFilter instanceof FilterPlayer) { + currentMaxModes = 0; + for (UUID targetPlayerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player targetPlayer = game.getPlayer(targetPlayerId); + if (((FilterPlayer) maxModesFilter).match(targetPlayer, source.getSourceId(), source.getControllerId(), game)) { + currentMaxModes++; + } + } + } + } + while (this.selectedModes.size() < currentMaxModes) { Mode choice = player.chooseMode(this, source, game); if (choice == null) { if (isEachModeOnlyOnce()) { @@ -337,7 +361,9 @@ public class Modes extends LinkedHashMap { return this.getMode().getEffects().getText(this.getMode()); } StringBuilder sb = new StringBuilder(); - if (this.getMinModes() == 1 && this.getMaxModes() == 3) { + if (this.getMaxModesFilter() != null) { + sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage()); + } else if (this.getMinModes() == 1 && this.getMaxModes() == 3) { sb.append("choose one or more "); } else if (this.getMinModes() == 1 && this.getMaxModes() == 2) { sb.append("choose one or both "); diff --git a/Mage/src/main/java/mage/filter/FilterOpponent.java b/Mage/src/main/java/mage/filter/FilterOpponent.java index efffdb4b983..d413dee473b 100644 --- a/Mage/src/main/java/mage/filter/FilterOpponent.java +++ b/Mage/src/main/java/mage/filter/FilterOpponent.java @@ -1,53 +1,57 @@ -/* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. - */ -package mage.filter; - -import mage.constants.TargetController; -import mage.filter.predicate.other.PlayerPredicate; - -/** - * - * @author LevelX2 - */ -public class FilterOpponent extends FilterPlayer{ - - public FilterOpponent() { - super("opponent"); - add(new PlayerPredicate(TargetController.OPPONENT)); - } - - - public FilterOpponent(final FilterOpponent filter) { - super(filter); - - } - @Override - public FilterOpponent copy() { - return new FilterOpponent(this); - } -} +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. + */ +package mage.filter; + +import mage.constants.TargetController; +import mage.filter.predicate.other.PlayerPredicate; + +/** + * + * @author LevelX2 + */ +public class FilterOpponent extends FilterPlayer { + + public FilterOpponent() { + this("opponent"); + } + + public FilterOpponent(String text) { + super(text); + add(new PlayerPredicate(TargetController.OPPONENT)); + } + + public FilterOpponent(final FilterOpponent filter) { + super(filter); + + } + + @Override + public FilterOpponent copy() { + return new FilterOpponent(this); + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java index ee3a866e2f2..c21819b23e6 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/AnotherTargetPredicate.java @@ -27,7 +27,9 @@ */ package mage.filter.predicate.mageobject; +import java.util.UUID; import mage.MageItem; +import mage.abilities.Mode; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; @@ -45,24 +47,43 @@ import mage.target.Target; public class AnotherTargetPredicate implements ObjectSourcePlayerPredicate> { private final int targetTag; + private final boolean crossModalCheck; /** * * @param targetTag tag of the target the filter belongs to */ public AnotherTargetPredicate(int targetTag) { + this(targetTag, false); + } + + public AnotherTargetPredicate(int targetTag, boolean crossModalCheck) { this.targetTag = targetTag; + this.crossModalCheck = crossModalCheck; } @Override public boolean apply(ObjectSourcePlayer input, Game game) { StackObject source = game.getStack().getStackObject(input.getSourceId()); if (source != null && source.getStackAbility().getTargets() != null) { - for (Target target : source.getStackAbility().getTargets()) { - if (target.getTargetTag() > 0 // target is included in the target group to check - && target.getTargetTag() != targetTag // it's not the target of this predicate - && target.getTargets().contains(input.getObject().getId())) { // if the uuid already is used for another target in the group it's not allowed here - return false; + if (crossModalCheck) { + for (UUID modeId : source.getStackAbility().getModes().getSelectedModes()) { + Mode mode = source.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + if (target.getTargetTag() > 0 // target is included in the target group to check + && target.getTargetTag() != targetTag // it's not the target of this predicate + && target.getTargets().contains(input.getObject().getId())) { // if the uuid already is used for another target in the group it's not allowed here + return false; + } + } + } + } else { + for (Target target : source.getStackAbility().getTargets()) { + if (target.getTargetTag() > 0 // target is included in the target group to check + && target.getTargetTag() != targetTag // it's not the target of this predicate + && target.getTargets().contains(input.getObject().getId())) { // if the uuid already is used for another target in the group it's not allowed here + return false; + } } } }