diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 75d69b98518..419a33a0b53 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -138,16 +138,10 @@ public class SimulatedPlayer2 extends ComputerPlayer { options = optimizeOptions(game, options, ability); if (options.isEmpty()) { allActions.add(ability); -// simulateAction(game, previousActions, ability); } else { -// ExecutorService simulationExecutor = Executors.newFixedThreadPool(4); for (Ability option : options) { allActions.add(option); -// SimulationWorker worker = new SimulationWorker(game, this, previousActions, option); -// simulationExecutor.submit(worker); } -// simulationExecutor.shutdown(); -// while(!simulationExecutor.isTerminated()) {} } } } diff --git a/Mage.Sets/src/mage/sets/commander2014/DeployToTheFront.java b/Mage.Sets/src/mage/sets/commander2014/DeployToTheFront.java index 8d0206462e5..add6e65efa1 100644 --- a/Mage.Sets/src/mage/sets/commander2014/DeployToTheFront.java +++ b/Mage.Sets/src/mage/sets/commander2014/DeployToTheFront.java @@ -47,9 +47,8 @@ public class DeployToTheFront extends CardImpl { super(ownerId, 6, "Deploy to the Front", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{5}{W}{W}"); this.expansionSetCode = "C14"; - // Put X 1/1 white Soldier creature tokens onto the battlefield, where X is the number of creatures on the battlefield. - Effect effect = new CreateTokenEffect(new SoldierToken("C14"), new PermanentsOnBattlefieldCount(new FilterCreaturePermanent("the number of creatures on the battlefield"))); + Effect effect = new CreateTokenEffect(new SoldierToken(), new PermanentsOnBattlefieldCount(new FilterCreaturePermanent("the number of creatures on the battlefield"))); effect.setText("Put X 1/1 white Soldier creature tokens onto the battlefield, where X is the number of creatures on the battlefield"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java b/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java index 87dc7ad335c..f83349340d7 100644 --- a/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java +++ b/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java @@ -51,7 +51,6 @@ public class FirstResponse extends CardImpl { super(ownerId, 12, "First Response", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); this.expansionSetCode = "M15"; - // At the beginning of each upkeep, if you lost life last turn, put a 1/1 white Soldier creature token onto the battlefield. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new FirstResponseEffect(), TargetController.ANY, false), new PlayerLostLifeWatcher()); @@ -88,7 +87,7 @@ class FirstResponseEffect extends OneShotEffect { PlayerLostLifeWatcher watcher = (PlayerLostLifeWatcher) game.getState().getWatchers().get("PlayerLostLifeWatcher"); if (watcher != null) { if (watcher.getLiveLostLastTurn(source.getControllerId()) > 0) { - return new CreateTokenEffect(new SoldierToken("M15")).apply(game, source); + return new CreateTokenEffect(new SoldierToken()).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/WheelOfSunAndMoon.java b/Mage.Sets/src/mage/sets/shadowmoor/WheelOfSunAndMoon.java index d028787067d..e41ad1b2148 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/WheelOfSunAndMoon.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/WheelOfSunAndMoon.java @@ -102,7 +102,7 @@ class WheelOfSunAndMoonEffect extends ReplacementEffectImpl { public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.ZONE_CHANGE; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; @@ -110,8 +110,8 @@ class WheelOfSunAndMoonEffect extends ReplacementEffectImpl { Card card = game.getCard(event.getTargetId()); if (card != null) { Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null && - card.getOwnerId().equals(enchantment.getAttachedTo())) { + if (enchantment != null && enchantment.getAttachedTo() != null + && card.getOwnerId().equals(enchantment.getAttachedTo())) { return true; } } @@ -128,7 +128,7 @@ class WheelOfSunAndMoonEffect extends ReplacementEffectImpl { if (card != null) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; Cards cards = new CardsImpl(card); - controller.revealCards(sourceObject.getName(), cards, game); + controller.revealCards(sourceObject.getIdName(), cards, game); controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, zEvent.getFromZone(), false, true); return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java index 7b65f262232..31a037b721b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java @@ -29,6 +29,8 @@ package org.mage.test.AI.basic; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.filter.Filter; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBaseAI; @@ -88,8 +90,10 @@ public class TargetsAreChosenTest extends CardTestPlayerBaseAI { // Destroy two target artifacts. addCard(Zone.HAND, playerA, "Rack and Ruin"); // {2}{R} - addCard(Zone.BATTLEFIELD, playerB, "Mox Emerald", 2); - addCard(Zone.BATTLEFIELD, playerA, "Juggernaut"); + addCard(Zone.BATTLEFIELD, playerB, "Mox Emerald", 4); + addCard(Zone.BATTLEFIELD, playerA, "Juggernaut", 1); + addCard(Zone.BATTLEFIELD, playerA, "Composite Golem", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mox Emerald", 1); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -99,4 +103,123 @@ public class TargetsAreChosenTest extends CardTestPlayerBaseAI { } + /** + * Target only opponent creatures to tap + */ + @Test + public void testFrostBreath1() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. + addCard(Zone.HAND, playerA, "Frost Breath"); // {2}{U} + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Juggernaut", 1); + addCard(Zone.BATTLEFIELD, playerA, "Composite Golem", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Frost Breath", 1); + assertTapped("Silvercoat Lion", true); + assertTapped("Pillarfield Ox", true); + assertTapped("Juggernaut", false); + assertTapped("Composite Golem", false); + + } + + /** + * Target only opponent creatures also if more targets are possible + */ + @Test + public void testFrostBreath2() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. + addCard(Zone.HAND, playerA, "Frost Breath"); // {2}{U} + + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerA, "Juggernaut", 1); + addCard(Zone.BATTLEFIELD, playerA, "Composite Golem", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Frost Breath", 1); + assertTapped("Pillarfield Ox", true); + assertTapped("Juggernaut", false); + assertTapped("Composite Golem", false); + + } + + /** + * Spell is not cast if only own creatures can be targeted + */ + @Test + public void testFrostBreath3() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. + addCard(Zone.HAND, playerA, "Frost Breath"); // {2}{U} + + addCard(Zone.BATTLEFIELD, playerA, "Juggernaut", 1); + addCard(Zone.BATTLEFIELD, playerA, "Composite Golem", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Frost Breath", 0); + assertTapped("Juggernaut", false); + assertTapped("Composite Golem", false); + + } + + /** + * + */ + @Test + public void testNefashu() { + // Whenever Nefashu attacks, up to five target creatures each get -1/-1 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Nefashu"); // 5/3 + + addCard(Zone.BATTLEFIELD, playerB, "Bellows Lizard", 5); + // Whenever a creature an opponent controls dies, put a +1/+1 counter on Malakir Cullblade. + addCard(Zone.BATTLEFIELD, playerA, "Malakir Cullblade", 5); + + attack(3, playerA, "Nefashu"); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Bellows Lizard", 5); + assertPowerToughness(playerA, "Malakir Cullblade", 6, 6, Filter.ComparisonScope.All); + assertTapped("Nefashu", true); + + assertLife(playerB, 15); + } + + /** + * Test that AI counters creatire spell + */ + @Test + @Ignore // counter spells don't seem to be cast by AI + public void testRewind() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // Counter target spell. Untap up to four lands. + addCard(Zone.HAND, playerA, "Rewind"); // {2}{U}{U} + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + // Renown 1 (When this creature deals combat damage to a player, if it isn't renowned, put a +1/+1 counter on it and it becomes renowned.) + // {W}{W}: Tap target creature. + addCard(Zone.HAND, playerB, "Kytheon's Irregulars", 1); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Kytheon's Irregulars"); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Kytheon's Irregulars", 0); + assertGraveyardCount(playerB, "Kytheon's Irregulars", 1); + assertGraveyardCount(playerA, "Rewind", 1); + + assertTappedCount("Island", true, 0); + + } + } diff --git a/Mage/src/mage/game/permanent/token/BeastToken.java b/Mage/src/mage/game/permanent/token/BeastToken.java index 761f5e03c78..27c443fef1c 100644 --- a/Mage/src/mage/game/permanent/token/BeastToken.java +++ b/Mage/src/mage/game/permanent/token/BeastToken.java @@ -27,7 +27,9 @@ */ package mage.game.permanent.token; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Random; import mage.MageInt; import mage.constants.CardType; @@ -38,9 +40,15 @@ import mage.constants.CardType; */ public class BeastToken extends Token { + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12")); + } + public BeastToken() { super("Beast", "3/3 green Beast creature token"); - availableImageSetCodes.addAll(Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12")); + availableImageSetCodes = tokenImageSets; cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add("Beast"); @@ -59,4 +67,13 @@ public class BeastToken extends Token { this.setTokenType(2); } } + + public BeastToken(final BeastToken token) { + super(token); + } + + @Override + public BeastToken copy() { + return new BeastToken(this); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage/src/mage/game/permanent/token/ElfToken.java b/Mage/src/mage/game/permanent/token/ElfToken.java index 256c67e8cc9..06c4cbc1b91 100644 --- a/Mage/src/mage/game/permanent/token/ElfToken.java +++ b/Mage/src/mage/game/permanent/token/ElfToken.java @@ -27,7 +27,9 @@ */ package mage.game.permanent.token; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import mage.MageInt; import mage.constants.CardType; @@ -37,9 +39,15 @@ import mage.constants.CardType; */ public class ElfToken extends Token { + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("C14", "SHM", "EVG", "LRW", "ORI")); + } + public ElfToken() { super("Elf Warrior", "1/1 green Elf Warrior creature token"); - availableImageSetCodes.addAll(Arrays.asList("C14", "SHM", "EVG", "LRW", "ORI")); + availableImageSetCodes = tokenImageSets; cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add("Elf"); @@ -55,4 +63,13 @@ public class ElfToken extends Token { this.setTokenType(1); } } + + public ElfToken(final ElfToken token) { + super(token); + } + + @Override + public ElfToken copy() { + return new ElfToken(this); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage/src/mage/game/permanent/token/GoblinToken.java b/Mage/src/mage/game/permanent/token/GoblinToken.java index ffbbac6b0a1..7b2dfd151f0 100644 --- a/Mage/src/mage/game/permanent/token/GoblinToken.java +++ b/Mage/src/mage/game/permanent/token/GoblinToken.java @@ -39,7 +39,7 @@ public class GoblinToken extends Token { public GoblinToken() { super("Goblin", "1/1 red Goblin creature token"); - availableImageSetCodes.addAll(Arrays.asList("SOM", "M10", "C14", "KTK", "EVG", "DTK", "ORI")); + availableImageSetCodes.addAll(Arrays.asList("SOM", "M10", "C14", "KTK", "EVG", "DTK", "ORI", "DDG")); cardType.add(CardType.CREATURE); subtype.add("Goblin"); diff --git a/Mage/src/mage/game/permanent/token/SoldierToken.java b/Mage/src/mage/game/permanent/token/SoldierToken.java index 8cf3fa003f5..956f808b5b7 100644 --- a/Mage/src/mage/game/permanent/token/SoldierToken.java +++ b/Mage/src/mage/game/permanent/token/SoldierToken.java @@ -1,33 +1,36 @@ /* -* 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. -*/ - + * 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.game.permanent.token; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; import mage.MageInt; import mage.constants.CardType; @@ -37,18 +40,38 @@ import mage.constants.CardType; */ public class SoldierToken extends Token { - public SoldierToken() { - this("10E"); + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", "MM2", "MMA", "RTR", "SOM")); } - public SoldierToken(String setCode) { + + public SoldierToken() { super("Soldier", "1/1 white Soldier creature token"); - this.setOriginalExpansionSetCode(setCode); + availableImageSetCodes = tokenImageSets; + cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add("Soldier"); power = new MageInt(1); toughness = new MageInt(1); - + } + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + if (getOriginalExpansionSetCode().equals("THS")) { + this.setTokenType(new Random().nextInt(2) + 1); + } + } + + public SoldierToken(final SoldierToken token) { + super(token); + } + + @Override + public SoldierToken copy() { + return new SoldierToken(this); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage/src/mage/game/permanent/token/SquirrelToken.java b/Mage/src/mage/game/permanent/token/SquirrelToken.java index 88a35610725..c8b5e56fdb6 100644 --- a/Mage/src/mage/game/permanent/token/SquirrelToken.java +++ b/Mage/src/mage/game/permanent/token/SquirrelToken.java @@ -27,6 +27,9 @@ */ package mage.game.permanent.token; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import mage.MageInt; import mage.constants.CardType; @@ -36,12 +39,15 @@ import mage.constants.CardType; */ public class SquirrelToken extends Token { - public SquirrelToken() { - this("CNS"); + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("CNS")); } - public SquirrelToken(String setCode) { + + public SquirrelToken() { super("Squirrel", "1/1 green Squirrel creature token"); - setOriginalExpansionSetCode(setCode); + availableImageSetCodes = tokenImageSets; cardType.add(CardType.CREATURE); subtype.add("Squirrel"); diff --git a/Mage/src/mage/game/permanent/token/ThopterColorlessToken.java b/Mage/src/mage/game/permanent/token/ThopterColorlessToken.java index 81135864b56..840f15504be 100644 --- a/Mage/src/mage/game/permanent/token/ThopterColorlessToken.java +++ b/Mage/src/mage/game/permanent/token/ThopterColorlessToken.java @@ -27,7 +27,9 @@ */ package mage.game.permanent.token; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Random; import mage.MageInt; import mage.abilities.keyword.FlyingAbility; @@ -39,9 +41,15 @@ import mage.constants.CardType; */ public class ThopterColorlessToken extends Token { + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("EXO", "ORI")); + } + public ThopterColorlessToken() { super("Thopter", "1/1 colorless Thopter artifact creature token with flying"); - availableImageSetCodes.addAll(Arrays.asList("EXO", "ORI")); + availableImageSetCodes = tokenImageSets; cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); subtype.add("Thopter"); @@ -58,4 +66,14 @@ public class ThopterColorlessToken extends Token { this.setTokenType(new Random().nextInt(2) + 1); } } + + public ThopterColorlessToken(final ThopterColorlessToken token) { + super(token); + } + + @Override + public ThopterColorlessToken copy() { + return new ThopterColorlessToken(this); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/Mage/src/mage/game/permanent/token/Token.java b/Mage/src/mage/game/permanent/token/Token.java index 9e4906947ac..def3ce49c65 100644 --- a/Mage/src/mage/game/permanent/token/Token.java +++ b/Mage/src/mage/game/permanent/token/Token.java @@ -100,6 +100,7 @@ public class Token extends MageObjectImpl { this.originalCardNumber = token.originalCardNumber; this.originalExpansionSetCode = token.originalExpansionSetCode; this.copySourceCard = token.copySourceCard; // will never be changed + this.availableImageSetCodes = token.availableImageSetCodes; } public String getDescription() { @@ -216,6 +217,10 @@ public class Token extends MageObjectImpl { } else { setOriginalExpansionSetCode(availableImageSetCodes.get(new Random().nextInt(availableImageSetCodes.size()))); } + } else { + if (getOriginalExpansionSetCode() == null || getOriginalExpansionSetCode().isEmpty()) { + setOriginalExpansionSetCode(code); + } } } } diff --git a/Mage/src/mage/game/permanent/token/ZombieToken.java b/Mage/src/mage/game/permanent/token/ZombieToken.java index 5e716ade6aa..ab636da61fe 100644 --- a/Mage/src/mage/game/permanent/token/ZombieToken.java +++ b/Mage/src/mage/game/permanent/token/ZombieToken.java @@ -27,7 +27,9 @@ */ package mage.game.permanent.token; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Random; import mage.MageInt; import mage.constants.CardType; @@ -38,9 +40,15 @@ import mage.constants.CardType; */ public class ZombieToken extends Token { + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("10E", "M10", "M11", "M12", "M13", "M14", "M15", "MBS", "ALA", "ISD", "C14", "CNS", "MMA", "BNG", "KTK", "DTK", "ORI")); + } + public ZombieToken() { super("Zombie", "2/2 black Zombie creature token"); - availableImageSetCodes.addAll(Arrays.asList("10E", "M10", "M11", "M12", "M13", "M14", "M15", "MBS", "ALA", "ISD", "C14", "CNS", "MMA", "BNG", "KTK", "DTK", "ORI")); + availableImageSetCodes = tokenImageSets; cardType.add(CardType.CREATURE); color.setBlack(true); subtype.add("Zombie"); @@ -56,4 +64,12 @@ public class ZombieToken extends Token { } } + public ZombieToken(final ZombieToken token) { + super(token); + } + + @Override + public ZombieToken copy() { + return new ZombieToken(this); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index 3776a61446d..aa4b2a27047 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -376,26 +376,94 @@ public abstract class TargetImpl implements Target { return targets.size() > 0; } + /** + * Returns all possible different target combinations + * + * @param source + * @param game + * @return + */ @Override public List getTargetOptions(Ability source, Game game) { List options = new ArrayList<>(); - Set possibleTargets = possibleTargets(source.getSourceId(), source.getControllerId(), game); + List possibleTargets = new ArrayList<>(); + possibleTargets.addAll(possibleTargets(source.getSourceId(), source.getControllerId(), game)); possibleTargets.removeAll(getTargets()); - Iterator it = possibleTargets.iterator(); - while (it.hasNext()) { - UUID targetId = it.next(); + + // get the length of the array + // e.g. for {'A','B','C','D'} => N = 4 + int N = possibleTargets.size(); + // not enough targets, return no option + if (N < getNumberOfTargets()) { + return options; + } + // not target but that's allowed, return one empty option + if (N == 0) { TargetImpl target = this.copy(); - target.clearChosen(); - target.addTarget(targetId, source, game, true); - if (!target.isChosen()) { - Iterator it2 = possibleTargets.iterator(); - while (it2.hasNext() && !target.isChosen()) { - UUID nextTargetId = it2.next(); - target.addTarget(nextTargetId, source, game, true); - } + options.add(target); + return options; + } + int maxK = getMaxNumberOfTargets() - getTargets().size(); + if (maxK > 5) { // Prevent endless iteration with targets set to INTEGER.maxvalue + maxK = 5; + if (N > 10) { // not more than 252 combinations + maxK = 4; } - if (target.isChosen()) { - options.add(target); + if (N > 20) { // not more than 4845 combinations + maxK = 3; + } + } + if (N < maxK) { // less possible targets than the maximum allowed so reduce the max + maxK = N; + } + int minK = getNumberOfTargets(); + if (getNumberOfTargets() == 0) { // add option without targets if possible + TargetImpl target = this.copy(); + options.add(target); + minK = 1; + } + for (int K = minK; K <= maxK; K++) { + // get the combination by index + // e.g. 01 --> AB , 23 --> CD + int combination[] = new int[K]; + + // position of current index + // if (r = 1) r* + // index ==> 0 | 1 | 2 + // element ==> A | B | C + int r = 0; + int index = 0; + + while (r >= 0) { + // possible indexes for 1st position "r=0" are "0,1,2" --> "A,B,C" + // possible indexes for 2nd position "r=1" are "1,2,3" --> "B,C,D" + + // for r = 0 ==> index < (4+ (0 - 2)) = 2 + if (index <= (N + (r - K))) { + combination[r] = index; + + // if we are at the last position print and increase the index + if (r == K - 1) { + //add the new target option + TargetImpl target = this.copy(); + for (int i = 0; i < combination.length; i++) { + target.addTarget(possibleTargets.get(combination[i]), source, game, true); + } + options.add(target); + index++; + } else { + // select index for next position + index = combination[r] + 1; + r++; + } + } else { + r--; + if (r > 0) { + index = combination[r] + 1; + } else { + index = combination[0] + 1; + } + } } } return options;