From ba93f2a43135c91ee0b5b8b9c0175f563b8c26d5 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Wed, 10 Jul 2013 22:07:08 +0000 Subject: [PATCH] AI: changed many calls to accept Collection instead of List GameAction - legend rule and planeswalker rule are updated to match changes introduced with "Magic 2014 Core Set" InputSelectCardsFromList also accepts any Collection, not just List PlayerControllerHuman - chooseSingleCardForEffect tries to use InputSelectCardsFromList when all cards are in Battlefield or own hand --- .gitattributes | 1 + src/main/java/forge/card/ability/ApiType.java | 5 +- .../forge/card/ability/SpellAbilityAi.java | 7 +- .../java/forge/card/ability/ai/AttachAi.java | 3 +- .../java/forge/card/ability/ai/BondAi.java | 4 +- .../forge/card/ability/ai/ChangeZoneAi.java | 3 +- .../forge/card/ability/ai/ChooseCardAi.java | 3 +- .../card/ability/ai/CopyPermanentAi.java | 3 +- .../java/forge/card/ability/ai/EncodeAi.java | 3 +- .../card/ability/ai/LegendaryRuleAi.java | 42 ++++++++ .../java/forge/card/ability/ai/PlayAi.java | 3 +- src/main/java/forge/control/InputQueue.java | 2 +- src/main/java/forge/game/GameAction.java | 100 ++++++++++-------- .../java/forge/game/ai/ComputerUtilCard.java | 25 +++-- .../forge/game/player/PlayerController.java | 4 +- .../forge/game/player/PlayerControllerAi.java | 2 +- .../game/player/PlayerControllerHuman.java | 31 ++++-- .../gui/input/InputSelectCardsFromList.java | 7 +- src/main/java/forge/gui/match/CMatchUI.java | 3 + 19 files changed, 167 insertions(+), 84 deletions(-) create mode 100644 src/main/java/forge/card/ability/ai/LegendaryRuleAi.java diff --git a/.gitattributes b/.gitattributes index d1cb32addc2..93909a42e0d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14251,6 +14251,7 @@ src/main/java/forge/card/ability/ai/FlipACoinAi.java -text src/main/java/forge/card/ability/ai/FogAi.java -text src/main/java/forge/card/ability/ai/GameLossAi.java -text src/main/java/forge/card/ability/ai/GameWinAi.java -text +src/main/java/forge/card/ability/ai/LegendaryRuleAi.java -text src/main/java/forge/card/ability/ai/LifeExchangeAi.java -text src/main/java/forge/card/ability/ai/LifeGainAi.java -text src/main/java/forge/card/ability/ai/LifeLoseAi.java -text diff --git a/src/main/java/forge/card/ability/ApiType.java b/src/main/java/forge/card/ability/ApiType.java index 5aa5cc66852..32ae4ff6de8 100644 --- a/src/main/java/forge/card/ability/ApiType.java +++ b/src/main/java/forge/card/ability/ApiType.java @@ -58,6 +58,7 @@ import forge.card.ability.ai.FlipACoinAi; import forge.card.ability.ai.FogAi; import forge.card.ability.ai.GameLossAi; import forge.card.ability.ai.GameWinAi; +import forge.card.ability.ai.LegendaryRuleAi; import forge.card.ability.ai.LifeExchangeAi; import forge.card.ability.ai.LifeGainAi; import forge.card.ability.ai.LifeLoseAi; @@ -220,8 +221,8 @@ public enum ApiType { WinsGame (GameWinEffect.class, GameWinAi.class), - - InternalEtbReplacement(ETBReplacementEffect.class, CanPlayAsDrawbackAi.class); + InternalEtbReplacement(ETBReplacementEffect.class, CanPlayAsDrawbackAi.class), + InternalLegendaryRule(CharmEffect.class, LegendaryRuleAi.class); // Charm has empty resolve blocks, may act as a dummy private final Class clsEffect; private final Class clsAi; diff --git a/src/main/java/forge/card/ability/SpellAbilityAi.java b/src/main/java/forge/card/ability/SpellAbilityAi.java index 5db6b4fad30..e5f4613c626 100644 --- a/src/main/java/forge/card/ability/SpellAbilityAi.java +++ b/src/main/java/forge/card/ability/SpellAbilityAi.java @@ -1,8 +1,11 @@ package forge.card.ability; +import java.util.Collection; import java.util.List; +import com.google.common.collect.Iterables; + import forge.Card; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; @@ -113,9 +116,9 @@ public abstract class SpellAbilityAi extends SaTargetRountines { return true; } - public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Collection options, boolean isOptional) { System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); - return options.get(0); + return Iterables.getFirst(options, null); } public Player chooseSinglePlayer(Player ai, SpellAbility sa, List options) { diff --git a/src/main/java/forge/card/ability/ai/AttachAi.java b/src/main/java/forge/card/ability/ai/AttachAi.java index 1b74e1937e5..64991bb59eb 100644 --- a/src/main/java/forge/card/ability/ai/AttachAi.java +++ b/src/main/java/forge/card/ability/ai/AttachAi.java @@ -1,6 +1,7 @@ package forge.card.ability.ai; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -1208,7 +1209,7 @@ public class AttachAi extends SpellAbilityAi { } @Override - public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Collection options, boolean isOptional) { return attachToCardAIPreferences(ai, sa, true); } diff --git a/src/main/java/forge/card/ability/ai/BondAi.java b/src/main/java/forge/card/ability/ai/BondAi.java index f33a946f0eb..2bb183596b0 100644 --- a/src/main/java/forge/card/ability/ai/BondAi.java +++ b/src/main/java/forge/card/ability/ai/BondAi.java @@ -17,7 +17,7 @@ */ package forge.card.ability.ai; -import java.util.List; +import java.util.Collection; import forge.Card; import forge.card.ability.SpellAbilityAi; @@ -52,7 +52,7 @@ public final class BondAi extends SpellAbilityAi { @Override - public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Collection options, boolean isOptional) { return ComputerUtilCard.getBestCreatureAI(options); } } diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 712878cd03a..56d20da67f5 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -1,6 +1,7 @@ package forge.card.ability.ai; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Random; @@ -1402,7 +1403,7 @@ public class ChangeZoneAi extends SpellAbilityAi { @Override - public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Collection options, boolean isOptional) { // Called when looking for creature to attach aura or equipment return ComputerUtilCard.getBestAI(options); } diff --git a/src/main/java/forge/card/ability/ai/ChooseCardAi.java b/src/main/java/forge/card/ability/ai/ChooseCardAi.java index 34da495dd95..8e686080eaf 100644 --- a/src/main/java/forge/card/ability/ai/ChooseCardAi.java +++ b/src/main/java/forge/card/ability/ai/ChooseCardAi.java @@ -1,5 +1,6 @@ package forge.card.ability.ai; +import java.util.Collection; import java.util.List; import com.google.common.base.Predicate; @@ -97,7 +98,7 @@ public class ChooseCardAi extends SpellAbilityAi { * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean) */ @Override - public Card chooseSingleCard(final Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection options, boolean isOptional) { final Card host = sa.getSourceCard(); final String logic = sa.getParam("AILogic"); Card choice = null; diff --git a/src/main/java/forge/card/ability/ai/CopyPermanentAi.java b/src/main/java/forge/card/ability/ai/CopyPermanentAi.java index 2874a30e4b0..5866723588e 100644 --- a/src/main/java/forge/card/ability/ai/CopyPermanentAi.java +++ b/src/main/java/forge/card/ability/ai/CopyPermanentAi.java @@ -1,5 +1,6 @@ package forge.card.ability.ai; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Random; @@ -126,7 +127,7 @@ public class CopyPermanentAi extends SpellAbilityAi { * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean) */ @Override - public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Collection options, boolean isOptional) { // Select a card to attach to return ComputerUtilCard.getBestAI(options); } diff --git a/src/main/java/forge/card/ability/ai/EncodeAi.java b/src/main/java/forge/card/ability/ai/EncodeAi.java index f1492030475..0127108c75d 100644 --- a/src/main/java/forge/card/ability/ai/EncodeAi.java +++ b/src/main/java/forge/card/ability/ai/EncodeAi.java @@ -17,6 +17,7 @@ */ package forge.card.ability.ai; +import java.util.Collection; import java.util.List; import com.google.common.base.Predicate; @@ -71,7 +72,7 @@ public final class EncodeAi extends SpellAbilityAi { * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean) */ @Override - public Card chooseSingleCard(final Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection options, boolean isOptional) { Card choice = null; // final String logic = sa.getParam("AILogic"); // if (logic == null) { diff --git a/src/main/java/forge/card/ability/ai/LegendaryRuleAi.java b/src/main/java/forge/card/ability/ai/LegendaryRuleAi.java new file mode 100644 index 00000000000..de084abf98c --- /dev/null +++ b/src/main/java/forge/card/ability/ai/LegendaryRuleAi.java @@ -0,0 +1,42 @@ +package forge.card.ability.ai; + +import java.util.Collection; + +import com.google.common.collect.Iterables; + +import forge.Card; +import forge.card.ability.SpellAbilityAi; +import forge.card.spellability.SpellAbility; +import forge.game.player.Player; + +/** + * TODO: Write javadoc for this type. + * + */ +public class LegendaryRuleAi extends SpellAbilityAi { + + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility) + */ + @Override + protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { + return false; // should not get here + } + + + @Override + public Card chooseSingleCard(Player ai, SpellAbility sa, Collection options, boolean isOptional) { + // Choose a single legendary/planeswalker card to keep + Card firstOption = Iterables.getFirst(options, null); + boolean choosingFromPlanewalkers = firstOption.isPlaneswalker(); + + if ( choosingFromPlanewalkers ) { + // AI decision making - should AI compare counters? + } else { + // AI decision making - should AI compare damage and debuffs? + } + + return firstOption; + } + +} diff --git a/src/main/java/forge/card/ability/ai/PlayAi.java b/src/main/java/forge/card/ability/ai/PlayAi.java index c2df3cf9868..8cc7419a9fd 100644 --- a/src/main/java/forge/card/ability/ai/PlayAi.java +++ b/src/main/java/forge/card/ability/ai/PlayAi.java @@ -1,6 +1,7 @@ package forge.card.ability.ai; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Random; @@ -112,7 +113,7 @@ public class PlayAi extends SpellAbilityAi { * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean) */ @Override - public Card chooseSingleCard(final Player ai, SpellAbility sa, List options, boolean isOptional) { + public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection options, boolean isOptional) { List tgtCards = CardLists.filter(options, new Predicate() { @Override public boolean apply(final Card c) { diff --git a/src/main/java/forge/control/InputQueue.java b/src/main/java/forge/control/InputQueue.java index f57aba03c0d..4c9e0780682 100644 --- a/src/main/java/forge/control/InputQueue.java +++ b/src/main/java/forge/control/InputQueue.java @@ -90,7 +90,7 @@ public class InputQueue extends Observable { input.awaitLatchRelease(); } - public void setInput(InputSynchronized input) { + void setInput(InputSynchronized input) { this.inputStack.push(input); syncPoint(); this.updateObservers(); diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index 749b9f953a8..2c2a65831b1 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -28,9 +28,10 @@ import java.util.Map.Entry; import java.util.Set; import com.google.common.base.Function; -import com.google.common.base.Predicate; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; import forge.Card; import forge.CardCharacteristicName; @@ -46,6 +47,7 @@ import forge.ITargetable; import forge.card.CardType; import forge.card.TriggerReplacementBase; import forge.card.ability.AbilityFactory; +import forge.card.ability.ApiType; import forge.card.ability.effects.AttachEffect; import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactoryUtil; @@ -54,6 +56,7 @@ import forge.card.mana.ManaCost; import forge.card.replacement.ReplacementResult; import forge.card.spellability.Ability; import forge.card.spellability.AbilityActivated; +import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.card.spellability.TargetRestrictions; import forge.card.staticability.StaticAbility; @@ -902,12 +905,14 @@ public class GameAction { game.getStack().chooseOrderOfSimultaneousStackEntryAll(); } - if (this.handleLegendRule()) { - checkAgain = true; - } - - if (this.handlePlaneswalkerRule()) { - checkAgain = true; + for(Player p : game.getPlayers() ) { + if (this.handleLegendRule(p)) { + checkAgain = true; + } + + if (this.handlePlaneswalkerRule(p)) { + checkAgain = true; + } } if (!checkAgain) { @@ -1139,15 +1144,13 @@ public class GameAction { * destroyPlaneswalkers. *

*/ - private boolean handlePlaneswalkerRule() { + private boolean handlePlaneswalkerRule(Player p) { // get all Planeswalkers - final List list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS); + final List list = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS); boolean recheck = false; - Card c; - for (int i = 0; i < list.size(); i++) { - c = list.get(i); - + final Multimap uniqueWalkers = ArrayListMultimap.create(); + for (Card c : list) { if (c.getCounters(CounterType.LOYALTY) <= 0) { moveToGraveyard(c); // Play the Destroy sound @@ -1155,22 +1158,29 @@ public class GameAction { recheck = true; } - final ArrayList types = c.getType(); - for (final String type : types) { - if (!CardType.isAPlaneswalkerType(type)) { - continue; - } - final List cl = CardLists.getType(list, type); - - if (cl.size() > 1) { - for (final Card crd : cl) { - moveToGraveyard(crd); - } - recheck = true; + for (final String type : c.getType()) { + if (CardType.isAPlaneswalkerType(type)) { + uniqueWalkers.put(type, c); } } } + + for(String key : uniqueWalkers.keySet()) + { + Collection duplicates = uniqueWalkers.get(key); + if ( duplicates.size() < 2) + continue; + + recheck = true; + + Card toKeep = p.getController().chooseSingleCardForEffect(duplicates, new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple planeswalkers of type \""+key+"\"in play.\n\nChoose one to stay on battlefield (the rest will be moved to graveyard)"); + for(Card c: duplicates) { + if ( c != toKeep ) + moveToGraveyard(c); + } + + } return recheck; } @@ -1179,8 +1189,8 @@ public class GameAction { * destroyLegendaryCreatures. *

*/ - private boolean handleLegendRule() { - final List a = CardLists.getType(game.getCardsIn(ZoneType.Battlefield), "Legendary"); + private boolean handleLegendRule(Player p) { + final List a = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Legendary"); if (a.isEmpty() || game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) { return false; } @@ -1189,24 +1199,26 @@ public class GameAction { if (yamazaki.size() == 2) { a.removeAll(yamazaki); } - while (!a.isEmpty()) { - List b = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals(a.get(0).getName())); - b = CardLists.getType(b, "Legendary"); - b = CardLists.filter(b, new Predicate() { - @Override - public boolean apply(final Card c) { - return !c.isFaceDown(); - } - }); - a.remove(0); - if (1 < b.size()) { - for (int i = 0; i < b.size(); i++) { - sacrificeDestroy(b.get(i)); - } - recheck = true; - // Play the Destroy sound - game.fireEvent(new GameEventCardDestroyed()); + + Multimap uniqueLegends = ArrayListMultimap.create(); + for(Card c : a) { + if ( !c.isFaceDown() ) + uniqueLegends.put(c.getName(), c); + } + + for(String name : uniqueLegends.keySet()) { + Collection cc = uniqueLegends.get(name); + if ( cc.size() < 2 ) + continue; + + recheck = true; + + Card toKeep = p.getController().chooseSingleCardForEffect(cc, new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple legendary creatures named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)"); + for(Card c: cc) { + if ( c != toKeep ) + sacrificeDestroy(c); } + game.fireEvent(new GameEventCardDestroyed()); } return recheck; diff --git a/src/main/java/forge/game/ai/ComputerUtilCard.java b/src/main/java/forge/game/ai/ComputerUtilCard.java index 67b1765caed..638df9b4b81 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCard.java +++ b/src/main/java/forge/game/ai/ComputerUtilCard.java @@ -1,6 +1,7 @@ package forge.game.ai; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -124,7 +125,7 @@ public class ComputerUtilCard { * a {@link forge.CardList} object. * @return a {@link forge.Card} object. */ - public static Card getBestLandAI(final List list) { + public static Card getBestLandAI(final Collection list) { final List land = CardLists.filter(list, CardPredicates.Presets.LANDS); if (land.isEmpty()) { return null; @@ -180,8 +181,7 @@ public class ComputerUtilCard { * a boolean. * @return a {@link forge.Card} object. */ - public static Card getCheapestPermanentAI(final List list, final SpellAbility spell, final boolean targeted) { - List all = list; + public static Card getCheapestPermanentAI(Collection all, final SpellAbility spell, final boolean targeted) { if (targeted) { all = CardLists.filter(all, new Predicate() { @Override @@ -190,17 +190,16 @@ public class ComputerUtilCard { } }); } - if (all.size() == 0) { + if (all.isEmpty()) { return null; } // get cheapest card: Card cheapest = null; - cheapest = all.get(0); - for (int i = 0; i < all.size(); i++) { - if (cheapest.getManaCost().getCMC() <= cheapest.getManaCost().getCMC()) { - cheapest = all.get(i); + for (Card c : all) { + if (cheapest == null || cheapest.getManaCost().getCMC() <= cheapest.getManaCost().getCMC()) { + cheapest = c; } } @@ -218,7 +217,7 @@ public class ComputerUtilCard { * a {@link forge.CardList} object. * @return a {@link forge.Card} object. */ - public static Card getBestAI(final List list) { + public static Card getBestAI(final Collection list) { // Get Best will filter by appropriate getBest list if ALL of the list // is of that type if (Iterables.all(list, CardPredicates.Presets.CREATURES)) @@ -239,7 +238,7 @@ public class ComputerUtilCard { * the list * @return the card */ - public static Card getBestCreatureAI(final List list) { + public static Card getBestCreatureAI(final Collection list) { return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.fnEvaluateCreature); } @@ -292,7 +291,7 @@ public class ComputerUtilCard { * a {@link forge.CardList} object. * @return a {@link forge.Card} object. */ - public static Card getWorstAI(final List list) { + public static Card getWorstAI(final Collection list) { return ComputerUtilCard.getWorstPermanentAI(list, false, false, false, false); } @@ -313,7 +312,7 @@ public class ComputerUtilCard { * a boolean. * @return a {@link forge.Card} object. */ - public static Card getWorstPermanentAI(final List list, final boolean biasEnch, final boolean biasLand, + public static Card getWorstPermanentAI(final Collection list, final boolean biasEnch, final boolean biasLand, final boolean biasArt, final boolean biasCreature) { if (list.size() == 0) { return null; @@ -628,7 +627,7 @@ public class ComputerUtilCard { * the all * @return the card */ - public static Card getMostExpensivePermanentAI(final List all) { + public static Card getMostExpensivePermanentAI(final Collection all) { Card biggest = null; int bigCMC = -1; diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 5befbd4ca25..12c16f30557 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -112,8 +112,8 @@ public abstract class PlayerController { // Specify a target of a spell (Spellskite) public abstract Pair chooseTarget(SpellAbility sa, List> allTargets); - public Card chooseSingleCardForEffect(List sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); } - public abstract Card chooseSingleCardForEffect(List sourceList, SpellAbility sa, String title, boolean isOptional); + public Card chooseSingleCardForEffect(Collection sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); } + public abstract Card chooseSingleCardForEffect(Collection sourceList, SpellAbility sa, String title, boolean isOptional); public abstract Player chooseSinglePlayerForEffect(List options, SpellAbility sa, String title); public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title); diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 739b6be6bcf..27afc49ac26 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -126,7 +126,7 @@ public class PlayerControllerAi extends PlayerController { @Override - public Card chooseSingleCardForEffect(List options, SpellAbility sa, String title, boolean isOptional) { + public Card chooseSingleCardForEffect(Collection options, SpellAbility sa, String title, boolean isOptional) { ApiType api = sa.getApi(); if ( null == api ) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index b41e7bb9047..b11e18c3aaf 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -37,6 +37,7 @@ import forge.game.Game; import forge.game.GameType; import forge.game.combat.Combat; import forge.game.phase.PhaseType; +import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; @@ -272,17 +273,33 @@ public class PlayerControllerHuman extends PlayerController { } @Override - public Card chooseSingleCardForEffect(List options, SpellAbility sa, String title, boolean isOptional) { + public Card chooseSingleCardForEffect(Collection options, SpellAbility sa, String title, boolean isOptional) { // Human is supposed to read the message and understand from it what to choose if (options.isEmpty()) return null; + if ( !isOptional && options.size() == 1 ) + return Iterables.getFirst(options, null); + + boolean canUseSelectCardsInput = true; + for(Card c : options) { + Zone cz = c.getZone(); + // can point at cards in own hand and anyone's battlefield + boolean canUiPointAtCards = cz != null && ( cz.is(ZoneType.Hand) && cz.getPlayer() == player || cz.is(ZoneType.Battlefield)); + if ( !canUiPointAtCards ) { + canUseSelectCardsInput = false; + break; + } + } - if ( isOptional ) - return GuiChoose.oneOrNone(title, options); - else if ( options.size() > 1 ) - return GuiChoose.one(title, options); - else - return options.get(0); + if ( canUseSelectCardsInput ) { + InputSelectCardsFromList input = new InputSelectCardsFromList(isOptional ? 0 : 1, 1, options); + input.setCancelAllowed(isOptional); + input.setMessage(title); + Singletons.getControl().getInputQueue().setInputAndWait(input); + return Iterables.getFirst(input.getSelected(), null); + } + + return isOptional ? GuiChoose.oneOrNone(title, options) : GuiChoose.one(title, options); } @Override diff --git a/src/main/java/forge/gui/input/InputSelectCardsFromList.java b/src/main/java/forge/gui/input/InputSelectCardsFromList.java index 8ba94ce160c..c35da9cf104 100644 --- a/src/main/java/forge/gui/input/InputSelectCardsFromList.java +++ b/src/main/java/forge/gui/input/InputSelectCardsFromList.java @@ -1,15 +1,14 @@ package forge.gui.input; -import java.util.List; - +import java.util.Collection; import forge.Card; public class InputSelectCardsFromList extends InputSelectCards { private static final long serialVersionUID = 6230360322294805986L; - private final List validChoices; + private final Collection validChoices; - public InputSelectCardsFromList(int min, int max, List validCards) { + public InputSelectCardsFromList(int min, int max, Collection validCards) { super(Math.min(min, validCards.size()), Math.min(max, validCards.size())); // to avoid hangs this.validChoices = validCards; diff --git a/src/main/java/forge/gui/match/CMatchUI.java b/src/main/java/forge/gui/match/CMatchUI.java index 74f1bbc7dde..ce5fb5f6e04 100644 --- a/src/main/java/forge/gui/match/CMatchUI.java +++ b/src/main/java/forge/gui/match/CMatchUI.java @@ -369,9 +369,12 @@ public enum CMatchUI { } } + private final static boolean LOG_UIEVENTS = false; // UI-related events should arrive here public void fireEvent(UiEvent uiEvent) { + if ( LOG_UIEVENTS ) + System.out.println("UI: " + uiEvent.toString() + " \t\t " + FThreads.debugGetStackTraceItem(4, true)); uiEvents.post(uiEvent); }