From 74799d286b99314310a09af2fb681db972ea8367 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 14 Feb 2016 18:31:02 +0100 Subject: [PATCH] * Improved mana source check. Fixed #1513. --- .../java/mage/player/ai/ComputerPlayer.java | 148 ++++++++---------- .../mage/sets/newphyrexia/MyrSuperion.java | 1 - .../mage/test/cards/mana/ManaSourceTest.java | 67 ++++++++ .../java/org/mage/test/player/TestPlayer.java | 36 ++++- .../abilities/costs/mana/ManaCostsImpl.java | 47 +++--- Mage/src/main/java/mage/filter/Filter.java | 9 +- .../main/java/mage/filter/FilterAbility.java | 21 ++- .../src/main/java/mage/filter/FilterImpl.java | 5 +- .../main/java/mage/filter/FilterObject.java | 5 + .../java/mage/filter/FilterPermanent.java | 5 + .../main/java/mage/filter/FilterPlayer.java | 8 +- .../FilterControlledCreatureInPlay.java | 12 +- .../filter/common/FilterCreatureOrPlayer.java | 27 ++-- .../common/FilterPermanentOrPlayer.java | 13 +- .../common/FilterPlaneswalkerOrPlayer.java | 5 + .../filter/common/FilterSpellOrPermanent.java | 5 + Mage/src/main/java/mage/players/ManaPool.java | 10 +- .../main/java/mage/players/ManaPoolItem.java | 21 ++- .../main/java/mage/players/PlayerImpl.java | 19 ++- 19 files changed, 294 insertions(+), 170 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 187c0438957..fae82c5b679 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -343,11 +343,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.add(abilityControllerId, game); return true; } - } else { - if (target.canTarget(randomOpponentId, null, game)) { - target.add(randomOpponentId, game); - return true; - } + } else if (target.canTarget(randomOpponentId, null, game)) { + target.add(randomOpponentId, game); + return true; } if (!target.isRequired(sourceId, game)) { return false; @@ -378,11 +376,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.add(abilityControllerId, game); return true; } - } else { - if (target.canTarget(randomOpponentId, null, game)) { - target.add(randomOpponentId, game); - return true; - } + } else if (target.canTarget(randomOpponentId, null, game)) { + target.add(randomOpponentId, game); + return true; } if (!target.isRequired(sourceId, game) || target.getNumberOfTargets() == 0) { return false; @@ -580,11 +576,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.addTarget(abilityControllerId, source, game); return true; } - } else { - if (target.canTarget(getId(), randomOpponentId, source, game)) { - target.addTarget(randomOpponentId, source, game); - return true; - } + } else if (target.canTarget(getId(), randomOpponentId, source, game)) { + target.addTarget(randomOpponentId, source, game); + return true; } } @@ -606,11 +600,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.addTarget(abilityControllerId, source, game); return true; } - } else { - if (target.canTarget(getId(), randomOpponentId, source, game)) { - target.addTarget(randomOpponentId, source, game); - return true; - } + } else if (target.canTarget(getId(), randomOpponentId, source, game)) { + target.addTarget(randomOpponentId, source, game); + return true; } //if (!target.isRequired()) @@ -1024,10 +1016,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { playableNonInstant.add(card); } } - } else { - if (!playableInstant.contains(card) && !playableNonInstant.contains(card)) { - unplayable.put(mana.needed(avail), card); - } + } else if (!playableInstant.contains(card) && !playableNonInstant.contains(card)) { + unplayable.put(mana.needed(avail), card); } } } @@ -1045,7 +1035,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { option.add(Mana.GenericMana(3)); } } - if (abilityOptions.size() == 0) { + if (abilityOptions.isEmpty()) { playableAbilities.add(ability); } else { for (Mana mana : abilityOptions) { @@ -1063,7 +1053,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD)) { if (ability.canActivate(playerId, game)) { ManaOptions abilityOptions = ability.getManaCosts().getOptions(); - if (abilityOptions.size() == 0) { + if (abilityOptions.isEmpty()) { playableAbilities.add(ability); } else { for (Mana mana : abilityOptions) { @@ -1096,7 +1086,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { // log.info("paying for " + unpaid.getText()); boolean spendAnyMana = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game); ManaCost cost; - List producers; + List producers; if (unpaid instanceof ManaCosts) { ManaCosts manaCosts = (ManaCosts) unpaid; cost = manaCosts.get(manaCosts.size() - 1); @@ -1106,11 +1096,11 @@ public class ComputerPlayer extends PlayerImpl implements Player { producers = this.getAvailableManaProducers(game); producers.addAll(this.getAvailableManaProducersWithCost(game)); } - for (Permanent perm : producers) { + for (MageObject mageObject : producers) { // use color producing mana abilities with costs first that produce all color manas that are needed to pay // otherwise the computer may not be able to pay the cost for that source ManaAbility: - for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility manaAbility : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { int colored = 0; for (Mana mana : manaAbility.getNetMana(game)) { if (!unpaid.getMana().includesMana(mana)) { @@ -1131,9 +1121,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } - for (Permanent perm : producers) { + for (MageObject mageObject : producers) { // pay all colored costs first - for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility manaAbility : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { if (cost instanceof ColoredManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyMana) { @@ -1145,7 +1135,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // then pay hybrid - for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility manaAbility : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { if (cost instanceof HybridManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyMana) { @@ -1157,7 +1147,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // then pay mono hybrid - for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility manaAbility : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { if (cost instanceof MonoHybridManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyMana) { @@ -1169,7 +1159,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // pay colorless - for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility manaAbility : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { if (cost instanceof ColorlessManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyMana) { @@ -1181,7 +1171,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // finally pay generic - for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility manaAbility : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { if (cost instanceof GenericManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyMana) { @@ -1216,15 +1206,15 @@ public class ComputerPlayer extends PlayerImpl implements Player { * @param game * @return List */ - private List getSortedProducers(ManaCosts unpaid, Game game) { - List unsorted = this.getAvailableManaProducers(game); + private List getSortedProducers(ManaCosts unpaid, Game game) { + List unsorted = this.getAvailableManaProducers(game); unsorted.addAll(this.getAvailableManaProducersWithCost(game)); - Map scored = new HashMap<>(); - for (Permanent permanent : unsorted) { + Map scored = new HashMap<>(); + for (MageObject mageObject : unsorted) { int score = 0; for (ManaCost cost : unpaid) { Abilities: - for (ManaAbility ability : permanent.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + for (ManaAbility ability : mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { for (Mana netMana : ability.getNetMana(game)) { if (cost.testPay(netMana)) { score++; @@ -1234,29 +1224,29 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } if (score > 0) { // score mana producers that produce other mana types and have other uses higher - score += permanent.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game).size(); - score += permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD).size(); - if (!permanent.getCardType().contains(CardType.LAND)) { + score += mageObject.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game).size(); + score += mageObject.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD).size(); + if (!mageObject.getCardType().contains(CardType.LAND)) { score += 2; - } else if (permanent.getCardType().contains(CardType.CREATURE)) { + } else if (mageObject.getCardType().contains(CardType.CREATURE)) { score += 2; } } - scored.put(permanent, score); + scored.put(mageObject, score); } return sortByValue(scored); } - private List sortByValue(Map map) { - List> list = new LinkedList<>(map.entrySet()); - Collections.sort(list, new Comparator>() { + private List sortByValue(Map map) { + List> list = new LinkedList<>(map.entrySet()); + Collections.sort(list, new Comparator>() { @Override - public int compare(Entry o1, Entry o2) { + public int compare(Entry o1, Entry o2) { return (o1.getValue().compareTo(o2.getValue())); } }); - List result = new ArrayList<>(); - for (Entry entry : list) { + List result = new ArrayList<>(); + for (Entry entry : list) { result.add(entry.getKey()); } return result; @@ -1595,7 +1585,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - public List getAvailableManaProducers(Game game) { + public List getAvailableManaProducers(Game game) { return super.getAvailableManaProducers(game); } @@ -2174,11 +2164,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.add(randomOpponentId, game); return true; } - } else { - if (((TargetOpponent) target).canTarget(randomOpponentId, source, game)) { - target.add(randomOpponentId, game); - return true; - } + } else if (((TargetOpponent) target).canTarget(randomOpponentId, source, game)) { + target.add(randomOpponentId, game); + return true; } for (UUID currentId : game.getOpponents(abilityControllerId)) { if (source == null) { @@ -2186,11 +2174,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.add(currentId, game); return true; } - } else { - if (((TargetOpponent) target).canTarget(currentId, source, game)) { - target.add(currentId, game); - return true; - } + } else if (((TargetOpponent) target).canTarget(currentId, source, game)) { + target.add(currentId, game); + return true; } } return false; @@ -2222,29 +2208,27 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } + } else if (source == null) { + if (target.canTarget(randomOpponentId, game)) { + target.add(randomOpponentId, game); + return true; + } + if (target.isRequired(sourceId, game)) { + if (target.canTarget(abilityControllerId, game)) { + target.add(abilityControllerId, game); + return true; + } + } } else { - if (source == null) { - if (target.canTarget(randomOpponentId, game)) { - target.add(randomOpponentId, game); + if (target.canTarget(randomOpponentId, game)) { + target.add(randomOpponentId, game); + return true; + } + if (target.isRequired(sourceId, game)) { + if (target.canTarget(abilityControllerId, game)) { + target.add(abilityControllerId, game); return true; } - if (target.isRequired(sourceId, game)) { - if (target.canTarget(abilityControllerId, game)) { - target.add(abilityControllerId, game); - return true; - } - } - } else { - if (target.canTarget(randomOpponentId, game)) { - target.add(randomOpponentId, game); - return true; - } - if (target.isRequired(sourceId, game)) { - if (target.canTarget(abilityControllerId, game)) { - target.add(abilityControllerId, game); - return true; - } - } } } return false; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java index e7f2738eac5..ab112dd4795 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java @@ -29,7 +29,6 @@ package mage.sets.newphyrexia; import java.util.UUID; import mage.MageInt; -import mage.abilities.StaticAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.java new file mode 100644 index 00000000000..49ebed107a1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaSourceTest.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 org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ManaSourceTest extends CardTestPlayerBase { + + /** + * I can use Simian Spirit Guide's mana to cast Myr Superion, but it is a + * creature card and not a creature when it is in hand, so it's wrong. + */ + @Test + public void testCantCastWithCreatureCard() { + // Exile Simian Spirit Guide from your hand: Add {R} to your mana pool. + addCard(Zone.HAND, playerB, "Simian Spirit Guide", 1); + // Spend only mana produced by creatures to cast Myr Superion. + addCard(Zone.HAND, playerB, "Myr Superion", 1); // {2} + + addCard(Zone.BATTLEFIELD, playerB, "Manakin", 1); + + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Exile"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Myr Superion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertExileCount("Simian Spirit Guide", 1); + + assertPermanentCount(playerB, "Myr Superion", 0); + assertHandCount(playerB, "Myr Superion", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 2dc30510882..6ba7999fe62 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -392,14 +392,34 @@ public class TestPlayer implements Player { String command = action.getAction(); command = command.substring(command.indexOf("manaActivate:") + 13); String[] groups = command.split("\\$"); - List manaPerms = computerPlayer.getAvailableManaProducers(game); - for (Permanent perm : manaPerms) { - for (Ability manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { - if (manaAbility.toString().startsWith(groups[0])) { - Ability newManaAbility = manaAbility.copy(); - computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); - actions.remove(action); - return true; + List manaObjects = computerPlayer.getAvailableManaProducers(game); + for (MageObject mageObject : manaObjects) { + if (mageObject instanceof Permanent) { + for (Ability manaAbility : ((Permanent) mageObject).getAbilities(game).getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + if (manaAbility.toString().startsWith(groups[0])) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); + actions.remove(action); + return true; + } + } + } else if (mageObject instanceof Card) { + for (Ability manaAbility : ((Card) mageObject).getAbilities(game).getAvailableManaAbilities(game.getState().getZone(mageObject.getId()), game)) { + if (manaAbility.toString().startsWith(groups[0])) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); + actions.remove(action); + return true; + } + } + } else { + for (Ability manaAbility : mageObject.getAbilities().getAvailableManaAbilities(game.getState().getZone(mageObject.getId()), game)) { + if (manaAbility.toString().startsWith(groups[0])) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); + actions.remove(action); + return true; + } } } } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index 0de107d2e32..9d9b89d8746 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -127,7 +127,7 @@ public class ManaCostsImpl extends ArrayList implements M @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - if (this.size() == 0 || noMana) { + if (this.isEmpty() || noMana) { setPaid(); return true; } @@ -330,35 +330,28 @@ public class ManaCostsImpl extends ArrayList implements M if (symbol.length() == 1 || isNumeric(symbol)) { if (Character.isDigit(symbol.charAt(0))) { this.add(new GenericManaCost(Integer.valueOf(symbol))); - } else { - if (symbol.equals("S")) { - this.add(new SnowManaCost()); - } else if (symbol.equals("C")) { - this.add(new ColorlessManaCost(1)); - } else if (!symbol.equals("X")) { - this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); - } else { - // check X wasn't added before - if (modifierForX == 0) { - // count X occurence - for (String s : symbols) { - if (s.equals("X")) { - modifierForX++; - } - } - this.add(new VariableManaCost(modifierForX)); + } else if (symbol.equals("S")) { + this.add(new SnowManaCost()); + } else if (symbol.equals("C")) { + this.add(new ColorlessManaCost(1)); + } else if (!symbol.equals("X")) { + this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); + } else // check X wasn't added before + if (modifierForX == 0) { + // count X occurence + for (String s : symbols) { + if (s.equals("X")) { + modifierForX++; } } - //TODO: handle multiple {X} and/or {Y} symbols - } + this.add(new VariableManaCost(modifierForX)); + } //TODO: handle multiple {X} and/or {Y} symbols + } else if (Character.isDigit(symbol.charAt(0))) { + this.add((T) new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)))); + } else if (symbol.contains("P")) { + this.add((T) new PhyrexianManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); } else { - if (Character.isDigit(symbol.charAt(0))) { - this.add((T) new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)))); - } else if (symbol.contains("P")) { - this.add((T) new PhyrexianManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); - } else { - this.add((T) new HybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)), ColoredManaSymbol.lookup(symbol.charAt(2)))); - } + this.add((T) new HybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)), ColoredManaSymbol.lookup(symbol.charAt(2)))); } } } diff --git a/Mage/src/main/java/mage/filter/Filter.java b/Mage/src/main/java/mage/filter/Filter.java index dc155eef6c6..83c7d8ee528 100644 --- a/Mage/src/main/java/mage/filter/Filter.java +++ b/Mage/src/main/java/mage/filter/Filter.java @@ -24,8 +24,7 @@ * 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 java.io.Serializable; @@ -36,7 +35,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com * @author North - * + * * @param */ public interface Filter extends Serializable { @@ -64,9 +63,13 @@ public interface Filter extends Serializable { } boolean match(E o, Game game); + void add(Predicate predicate); + boolean checkObjectClass(Object object); + String getMessage(); + void setMessage(String message); Filter copy(); diff --git a/Mage/src/main/java/mage/filter/FilterAbility.java b/Mage/src/main/java/mage/filter/FilterAbility.java index 49ce90ad542..6051f26f581 100644 --- a/Mage/src/main/java/mage/filter/FilterAbility.java +++ b/Mage/src/main/java/mage/filter/FilterAbility.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,16 +20,16 @@ * 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.abilities.Ability; import mage.constants.AbilityType; import mage.constants.Zone; -import mage.abilities.Ability; import mage.filter.predicate.Predicate; import mage.game.Game; @@ -60,9 +60,14 @@ public class FilterAbility extends FilterImpl { return new AbilityTypePredicate(type); } + @Override + public boolean checkObjectClass(Object object) { + return object instanceof Ability; + } + private static final class AbilityZonePredicate implements Predicate { - private Zone zone; + private final Zone zone; public AbilityZonePredicate(Zone zone) { this.zone = zone; @@ -81,7 +86,7 @@ public class FilterAbility extends FilterImpl { private static final class AbilityTypePredicate implements Predicate { - private AbilityType type; + private final AbilityType type; public AbilityTypePredicate(AbilityType type) { this.type = type; diff --git a/Mage/src/main/java/mage/filter/FilterImpl.java b/Mage/src/main/java/mage/filter/FilterImpl.java index 7def209d6ef..5d6cd75e863 100644 --- a/Mage/src/main/java/mage/filter/FilterImpl.java +++ b/Mage/src/main/java/mage/filter/FilterImpl.java @@ -58,7 +58,10 @@ public abstract class FilterImpl implements Filter { @Override public boolean match(E e, Game game) { - return Predicates.and(predicates).apply(e, game); + if (checkObjectClass(e)) { + return Predicates.and(predicates).apply(e, game); + } + return false; } @Override diff --git a/Mage/src/main/java/mage/filter/FilterObject.java b/Mage/src/main/java/mage/filter/FilterObject.java index 3fc96e9c6b9..b0a67462338 100644 --- a/Mage/src/main/java/mage/filter/FilterObject.java +++ b/Mage/src/main/java/mage/filter/FilterObject.java @@ -41,6 +41,11 @@ public class FilterObject extends FilterImpl { return new FilterObject<>(this); } + @Override + public boolean checkObjectClass(Object object) { + return object instanceof MageObject; + } + public FilterObject(String name) { super(name); } diff --git a/Mage/src/main/java/mage/filter/FilterPermanent.java b/Mage/src/main/java/mage/filter/FilterPermanent.java index 5e14f969cc1..52fe9398b51 100644 --- a/Mage/src/main/java/mage/filter/FilterPermanent.java +++ b/Mage/src/main/java/mage/filter/FilterPermanent.java @@ -64,6 +64,11 @@ public class FilterPermanent extends FilterObject implements FilterIn this.add(new SubtypePredicate(subtype)); } + @Override + public boolean checkObjectClass(Object object) { + return object instanceof Permanent; + } + @Override public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { if (!this.match(permanent, game)) { diff --git a/Mage/src/main/java/mage/filter/FilterPlayer.java b/Mage/src/main/java/mage/filter/FilterPlayer.java index 8eecfdb8e17..54ce7fd4cf0 100644 --- a/Mage/src/main/java/mage/filter/FilterPlayer.java +++ b/Mage/src/main/java/mage/filter/FilterPlayer.java @@ -24,8 +24,7 @@ * 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 java.util.ArrayList; @@ -64,6 +63,11 @@ public class FilterPlayer extends FilterImpl { extraPredicates.add(predicate); } + @Override + public boolean checkObjectClass(Object object) { + return object instanceof Player; + } + public boolean match(Player player, UUID sourceId, UUID playerId, Game game) { if (!this.match(player, game)) { return false; diff --git a/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java b/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java index 5585eeb8e4d..50561504f21 100644 --- a/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java +++ b/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java @@ -24,8 +24,7 @@ * 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.common; import java.util.UUID; @@ -54,6 +53,11 @@ public class FilterControlledCreatureInPlay extends FilterImpl implement creatureFilter.add(new ControllerPredicate(TargetController.YOU)); } + @Override + public boolean checkObjectClass(Object object) { + return object instanceof Permanent; + } + public FilterControlledCreatureInPlay(final FilterControlledCreatureInPlay filter) { super(filter); this.creatureFilter = filter.creatureFilter.copy(); @@ -62,7 +66,7 @@ public class FilterControlledCreatureInPlay extends FilterImpl implement @Override public boolean match(Object o, Game game) { if (o instanceof Permanent) { - return creatureFilter.match((Permanent)o, game); + return creatureFilter.match((Permanent) o, game); } return false; } @@ -70,7 +74,7 @@ public class FilterControlledCreatureInPlay extends FilterImpl implement @Override public boolean match(Object o, UUID sourceId, UUID playerId, Game game) { if (o instanceof Permanent) { - return creatureFilter.match((Permanent)o, sourceId, playerId, game); + return creatureFilter.match((Permanent) o, sourceId, playerId, game); } return false; } diff --git a/Mage/src/main/java/mage/filter/common/FilterCreatureOrPlayer.java b/Mage/src/main/java/mage/filter/common/FilterCreatureOrPlayer.java index 14fc389fc7e..80a28be2eb9 100644 --- a/Mage/src/main/java/mage/filter/common/FilterCreatureOrPlayer.java +++ b/Mage/src/main/java/mage/filter/common/FilterCreatureOrPlayer.java @@ -24,19 +24,17 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.filter.common; +import java.util.UUID; +import mage.MageItem; import mage.filter.FilterImpl; import mage.filter.FilterInPlay; import mage.filter.FilterPlayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.MageItem; - -import java.util.UUID; /** * @@ -63,13 +61,17 @@ public class FilterCreatureOrPlayer extends FilterImpl implements Filt this.playerFilter = filter.playerFilter.copy(); } + @Override + public boolean checkObjectClass(Object object) { + return true; + } + @Override public boolean match(MageItem o, Game game) { if (o instanceof Player) { - return playerFilter.match((Player)o, game); - } - else if (o instanceof Permanent) { - return creatureFilter.match((Permanent)o, game); + return playerFilter.match((Player) o, game); + } else if (o instanceof Permanent) { + return creatureFilter.match((Permanent) o, game); } return false; } @@ -77,10 +79,9 @@ public class FilterCreatureOrPlayer extends FilterImpl implements Filt @Override public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) { if (o instanceof Player) { - return playerFilter.match((Player)o, sourceId, playerId, game); - } - else if (o instanceof Permanent) { - return creatureFilter.match((Permanent)o, sourceId, playerId, game); + return playerFilter.match((Player) o, sourceId, playerId, game); + } else if (o instanceof Permanent) { + return creatureFilter.match((Permanent) o, sourceId, playerId, game); } return false; } diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java index fd7a7405b76..af06e5bd174 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java @@ -24,10 +24,11 @@ * 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.common; +import java.util.UUID; +import mage.MageItem; import mage.filter.FilterImpl; import mage.filter.FilterInPlay; import mage.filter.FilterPermanent; @@ -36,9 +37,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import java.util.UUID; -import mage.MageItem; - /** * @author nantuko */ @@ -63,6 +61,11 @@ public class FilterPermanentOrPlayer extends FilterImpl implements Fil this.playerFilter = filter.playerFilter.copy(); } + @Override + public boolean checkObjectClass(Object object) { + return true; + } + @Override public boolean match(MageItem o, Game game) { if (o instanceof Player) { diff --git a/Mage/src/main/java/mage/filter/common/FilterPlaneswalkerOrPlayer.java b/Mage/src/main/java/mage/filter/common/FilterPlaneswalkerOrPlayer.java index 886db298e67..025ef208030 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPlaneswalkerOrPlayer.java +++ b/Mage/src/main/java/mage/filter/common/FilterPlaneswalkerOrPlayer.java @@ -73,6 +73,11 @@ public class FilterPlaneswalkerOrPlayer extends FilterImpl { this.playerFilter = filter.playerFilter.copy(); } + @Override + public boolean checkObjectClass(Object object) { + return true; + } + @Override public boolean match(Object o, Game game) { if (o instanceof Player) { diff --git a/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java b/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java index e0d6a093065..e9753a36a7f 100644 --- a/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java @@ -63,6 +63,11 @@ public class FilterSpellOrPermanent extends FilterImpl implements Filter this.spellFilter = filter.spellFilter.copy(); } + @Override + public boolean checkObjectClass(Object object) { + return true; + } + @Override public boolean match(Object o, Game game) { if (o instanceof Spell) { diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index 65c2cfa68d0..0655b19af9b 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -35,7 +35,6 @@ import java.util.List; import java.util.Set; import java.util.UUID; import mage.ConditionalMana; -import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.Cost; @@ -130,8 +129,7 @@ public class ManaPool implements Serializable { } for (ManaPoolItem mana : manaItems) { if (filter != null) { - MageObject sourceObject = game.getObject(mana.getSourceId()); - if (!filter.match(sourceObject, game)) { + if (!filter.match(mana.getSourceObject(), game)) { continue; } } @@ -170,7 +168,7 @@ public class ManaPool implements Serializable { if (mana.isConditional() && mana.getConditionalMana().get(manaType) > 0 && mana.getConditionalMana().apply(ability, game, mana.getSourceId(), costToPay)) { - if (filter == null || filter.match(game.getObject(mana.getSourceId()), game)) { + if (filter == null || filter.match(mana.getSourceObject(), game)) { return mana.getConditionalMana().get(manaType); } } @@ -378,13 +376,13 @@ public class ManaPool implements Serializable { Mana mana = manaToAdd.copy(); if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) { if (mana instanceof ConditionalMana) { - ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceId(), source.getOriginalId()); + ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game), source.getOriginalId()); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); } this.manaItems.add(item); } else { - ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceId(), source.getOriginalId(), mana.getFlag()); + ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceObject(game), source.getOriginalId(), mana.getFlag()); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); } diff --git a/Mage/src/main/java/mage/players/ManaPoolItem.java b/Mage/src/main/java/mage/players/ManaPoolItem.java index 2dcb9a92f51..0953dee5a3d 100644 --- a/Mage/src/main/java/mage/players/ManaPoolItem.java +++ b/Mage/src/main/java/mage/players/ManaPoolItem.java @@ -30,6 +30,7 @@ package mage.players; import java.io.Serializable; import java.util.UUID; import mage.ConditionalMana; +import mage.MageObject; import mage.Mana; import mage.constants.Duration; import mage.constants.ManaType; @@ -47,7 +48,7 @@ public class ManaPoolItem implements Serializable { private int black = 0; private int colorless = 0; private ConditionalMana conditionalMana; - private UUID sourceId; + private MageObject sourceObject; private UUID originalId; // originalId of the mana producing ability private boolean flag = false; private Duration duration; @@ -56,24 +57,24 @@ public class ManaPoolItem implements Serializable { public ManaPoolItem() { } - public ManaPoolItem(int red, int green, int blue, int white, int black, int colorless, UUID sourceId, UUID originalId, boolean flag) { + public ManaPoolItem(int red, int green, int blue, int white, int black, int colorless, MageObject sourceObject, UUID originalId, boolean flag) { this.red = red; this.green = green; this.blue = blue; this.white = white; this.black = black; this.colorless = colorless; - this.sourceId = sourceId; + this.sourceObject = sourceObject; this.originalId = originalId; this.flag = flag; this.duration = Duration.EndOfStep; } - public ManaPoolItem(ConditionalMana conditionalMana, UUID sourceId, UUID originalId) { + public ManaPoolItem(ConditionalMana conditionalMana, MageObject sourceObject, UUID originalId) { this.conditionalMana = conditionalMana; - this.sourceId = sourceId; + this.sourceObject = sourceObject; this.originalId = originalId; - this.conditionalMana.setManaProducerId(sourceId); + this.conditionalMana.setManaProducerId(sourceObject.getId()); this.conditionalMana.setManaProducerOriginalId(originalId); this.flag = conditionalMana.getFlag(); this.duration = Duration.EndOfStep; @@ -89,7 +90,7 @@ public class ManaPoolItem implements Serializable { if (item.conditionalMana != null) { this.conditionalMana = item.conditionalMana.copy(); } - this.sourceId = item.sourceId; + this.sourceObject = item.sourceObject; this.originalId = item.originalId; this.flag = item.flag; this.duration = item.duration; @@ -100,8 +101,12 @@ public class ManaPoolItem implements Serializable { return new ManaPoolItem(this); } + public MageObject getSourceObject() { + return sourceObject; + } + public UUID getSourceId() { - return sourceId; + return sourceObject.getId(); } public UUID getOriginalId() { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 5439bf3189d..9f3612fb827 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2336,8 +2336,8 @@ public abstract class PlayerImpl implements Player, Serializable { } // returns only mana producers that don't require mana payment - protected List getAvailableManaProducers(Game game) { - List result = new ArrayList<>(); + protected List getAvailableManaProducers(Game game) { + List result = new ArrayList<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { boolean canAdd = false; for (ManaAbility ability : permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD)) { @@ -2353,6 +2353,21 @@ public abstract class PlayerImpl implements Player, Serializable { result.add(permanent); } } + for (Card card : getHand().getCards(game)) { + boolean canAdd = false; + for (ManaAbility ability : card.getAbilities(game).getManaAbilities(Zone.HAND)) { + if (!ability.getManaCosts().isEmpty()) { + canAdd = false; + break; + } + if (ability.canActivate(playerId, game)) { + canAdd = true; + } + } + if (canAdd) { + result.add(card); + } + } return result; }