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 extends SpellAbilityEffect> clsEffect;
private final Class extends SpellAbilityAi> 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);
}