diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 05f05e329fa..9fb43d1d03c 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -11,6 +11,7 @@ import forge.LobbyPlayer; import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.ProtectAi; import forge.card.ColorSet; +import forge.card.ICardFace; import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; @@ -577,8 +578,8 @@ public class PlayerControllerAi extends PlayerController { } @Override - public PaperCard chooseSinglePaperCard(SpellAbility sa, String message, - Predicate cpp, String name) { + public ICardFace chooseSingleCardFace(SpellAbility sa, String message, + Predicate cpp, String name) { throw new UnsupportedOperationException("Should not be called for AI"); // or implement it if you know how } @@ -831,7 +832,7 @@ public class PlayerControllerAi extends PlayerController { } @Override - public String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message) { + public String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message) { if (sa.hasParam("AILogic")) { final String logic = sa.getParam("AILogic"); if (logic.equals("MostProminentInComputerDeck")) { @@ -850,7 +851,7 @@ public class PlayerControllerAi extends PlayerController { return ComputerUtilCard.getMostProminentCardName(cards); } } else { - CardCollectionView list = CardLists.filterControlledBy(game.getCardsInGame(), player.getOpponent()); + CardCollectionView list = CardLists.filterControlledBy(game.getCardsInGame(), player.getOpponents()); list = CardLists.filter(list, Predicates.not(Presets.LANDS)); if (!list.isEmpty()) { return list.get(0).getName(); @@ -889,4 +890,13 @@ public class PlayerControllerAi extends PlayerController { public void cancelAwaitNextInput() { // Do nothing } + + @Override + public String chooseCardName(SpellAbility sa, List faces, String message) { + ApiType api = sa.getApi(); + if (null == api) { + throw new InvalidParameterException("SA is not api-based, this is not supported yet"); + } + return SpellApiToAi.Converter.get(api).chooseCardName(player, sa, faces); + } } diff --git a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java index 9ce82a4a1af..230aa6f98b6 100644 --- a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java @@ -5,6 +5,7 @@ import java.util.List; import com.google.common.collect.Iterables; +import forge.card.ICardFace; import forge.game.GameEntity; import forge.game.card.Card; import forge.game.cost.Cost; @@ -269,6 +270,13 @@ public abstract class SpellAbilityAi { return Iterables.getFirst(options, null); } + public String chooseCardName(Player ai, SpellAbility sa, List faces) { + System.err.println("Warning: default (ie. inherited from base class) implementation of chooseCardName is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); + + final ICardFace face = Iterables.getFirst(faces, null); + return face == null ? "" : face.getName(); + } + protected static boolean isUselessCreature(Player ai, Card c) { if (c == null) { return true; diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java index 160db71bdeb..10452050f49 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java @@ -1,14 +1,27 @@ package forge.ai.ability; +import java.util.List; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import forge.StaticData; import forge.ai.ComputerUtil; import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilMana; import forge.ai.SpellAbilityAi; +import forge.card.CardDb; +import forge.card.CardRules; +import forge.card.CardSplitType; +import forge.card.CardStateName; +import forge.card.ICardFace; import forge.game.card.Card; +import forge.game.card.CardUtil; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; +import forge.item.PaperCard; public class ChooseCardNameAi extends SpellAbilityAi { @@ -68,4 +81,42 @@ public class ChooseCardNameAi extends SpellAbilityAi { return ComputerUtilCard.getBestAI(options); } + @Override + public String chooseCardName(Player ai, SpellAbility sa, List faces) { + // this function is only for "Alhammarret, High Arbiter" + + if (faces.isEmpty()) { + return ""; + } else if (faces.size() == 1) { + return Iterables.getFirst(faces, null).getName(); + } + + List cards = Lists.newArrayList(); + final CardDb cardDb = StaticData.instance().getCommonCards(); + + for (ICardFace face : faces) { + final CardRules rules = cardDb.getRules(face.getName()); + boolean isOther = rules.getOtherPart() == face; + final PaperCard paper = cardDb.getCard(rules.getName()); + final Card card = Card.fromPaperCard(paper, ai); + + if (rules.getSplitType() == CardSplitType.Split) { + Card copy = CardUtil.getLKICopy(card); + // for calcing i need only one split side + if (isOther) { + copy.getCurrentState().copyFrom(card, card.getState(CardStateName.RightSplit)); + } else { + copy.getCurrentState().copyFrom(card, card.getState(CardStateName.LeftSplit)); + } + copy.updateStateForView(); + + cards.add(copy); + } else if (!isOther) { + // other can't be cast that way, not need to prevent that + cards.add(card); + } + } + + return ComputerUtilCard.getBestAI(cards).getName(); + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java index e678d9da06a..ee3140d49a7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java @@ -6,8 +6,11 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import forge.StaticData; +import forge.card.CardFacePredicates; import forge.card.CardRules; import forge.card.CardRulesPredicates; +import forge.card.CardSplitType; +import forge.card.ICardFace; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -22,6 +25,8 @@ import forge.util.ComparableOp; import org.apache.commons.lang3.StringUtils; +import java.util.Collection; +import java.util.Collections; import java.util.List; public class ChooseCardNameEffect extends SpellAbilityEffect { @@ -60,17 +65,18 @@ public class ChooseCardNameEffect extends SpellAbilityEffect { if (randomChoice) { // Currently only used for Momir Avatar, if something else gets added here, make it more generic - Predicate baseRule = CardRulesPredicates.Presets.IS_CREATURE; String numericAmount = "X"; final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : AbilityUtils.calculateAmount(host, numericAmount, sa); - Predicate additionalRule = CardRulesPredicates.cmc(ComparableOp.EQUALS, validAmount); + // Momir needs PaperCard + Collection cards = StaticData.instance().getCommonCards().getUniqueCards(); + Predicate cpp = Predicates.and( + Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES), + Predicates.compose(CardRulesPredicates.cmc(ComparableOp.EQUALS, validAmount), PaperCard.FN_GET_RULES) + ); - List cards = Lists.newArrayList(StaticData.instance().getCommonCards().getUniqueCards()); - Predicate cpp = Predicates.and(Predicates.compose(baseRule, PaperCard.FN_GET_RULES), - Predicates.compose(additionalRule, PaperCard.FN_GET_RULES)); cards = Lists.newArrayList(Iterables.filter(cards, cpp)); if (!cards.isEmpty()) { chosen = Aggregates.random(cards).getName(); @@ -80,25 +86,38 @@ public class ChooseCardNameEffect extends SpellAbilityEffect { } else if (chooseFromDefined) { CardCollection choices = AbilityUtils.getDefinedCards(host, sa.getParam("ChooseFromDefinedCards"), sa); choices = CardLists.getValidCards(choices, valid, host.getController(), host); - Card c = p.getController().chooseSingleEntityForEffect(choices, sa, "Choose a card name"); - chosen = c != null ? c.getName() : ""; + List faces = Lists.newArrayList(); + // get Card + for (final Card c : choices) { + final CardRules rules = c.getRules(); + if (faces.contains(rules.getMainPart())) + continue; + faces.add(rules.getMainPart()); + // Alhammarret only allows Split for other faces + if (rules.getSplitType() == CardSplitType.Split) { + faces.add(rules.getOtherPart()); + } + } + Collections.sort(faces); + chosen = p.getController().chooseCardName(sa, faces, "Choose a card name"); } else { + // use CardFace because you might name a alternate name final String message = validDesc.equals("card") ? "Name a card" : "Name a " + validDesc + " card."; - - Predicate cpp = Predicates.alwaysTrue(); + + Predicate cpp = Predicates.alwaysTrue(); if ( StringUtils.containsIgnoreCase(valid, "nonland") ) { - cpp = Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES); + cpp = CardFacePredicates.Presets.IS_NON_LAND; } if ( StringUtils.containsIgnoreCase(valid, "nonbasic") ) { - cpp = Predicates.compose(Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND), PaperCard.FN_GET_RULES); + cpp = Predicates.not(CardFacePredicates.Presets.IS_BASIC_LAND); } - + if ( StringUtils.containsIgnoreCase(valid, "noncreature") ) { - cpp = Predicates.compose(Predicates.not(CardRulesPredicates.Presets.IS_CREATURE), PaperCard.FN_GET_RULES); + cpp = Predicates.not(CardFacePredicates.Presets.IS_CREATURE); } else if ( StringUtils.containsIgnoreCase(valid, "creature") ) { - cpp = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES); + cpp = CardFacePredicates.Presets.IS_CREATURE; } - + chosen = p.getController().chooseCardName(sa, cpp, valid, message); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index a95c07e2627..1967377b774 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -38,6 +38,7 @@ import forge.GameCommand; import forge.card.CardStateName; import forge.card.CardType; import forge.card.ColorSet; +import forge.card.ICardFace; import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostParser; @@ -74,7 +75,6 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.item.PaperCard; import forge.util.Aggregates; import forge.util.collect.FCollectionView; import forge.util.Lang; @@ -222,7 +222,7 @@ public class CardFactoryUtil { public static boolean handleHiddenAgenda(Player player, Card card) { SpellAbility sa = new SpellAbility.EmptySa(card); sa.getMapParams().put("AILogic", card.getSVar("AgendaLogic")); - Predicate cpp = Predicates.alwaysTrue(); + Predicate cpp = Predicates.alwaysTrue(); //Predicate pc = Predicates.in(player.getAllCards()); // TODO This would be better to send in the player's deck, not all cards String name = player.getController().chooseCardName(sa, cpp, "Card", "Name a card for " + card.getName()); diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index b11efb395b1..8818f906a0f 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -13,6 +13,7 @@ import com.google.common.collect.Multimap; import forge.LobbyPlayer; import forge.card.ColorSet; +import forge.card.ICardFace; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; @@ -189,15 +190,15 @@ public abstract class PlayerController { public abstract byte chooseColor(String message, SpellAbility sa, ColorSet colors); public abstract byte chooseColorAllowColorless(String message, Card c, ColorSet colors); - public abstract PaperCard chooseSinglePaperCard(SpellAbility sa, String message, Predicate cpp, String name); + public abstract ICardFace chooseSingleCardFace(SpellAbility sa, String message, Predicate cpp, String name); public abstract List chooseColors(String message, SpellAbility sa, int min, int max, List options); public abstract CounterType chooseCounterType(List options, SpellAbility sa, String prompt); public abstract boolean confirmPayment(CostPart costPart, String string); public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List possibleReplacers, Map runParams); public abstract String chooseProtectionType(String string, SpellAbility sa, List choices); - public abstract CardShields chooseRegenerationShield(Card c); - + public abstract CardShields chooseRegenerationShield(Card c); + // these 4 need some refining. public abstract boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView allPayers); public abstract void orderAndPlaySimultaneousSa(List activePlayerSAs); @@ -222,8 +223,9 @@ public abstract class PlayerController { public abstract Map chooseCardsForConvoke(SpellAbility sa, ManaCost manaCost, CardCollectionView untappedCreats); - public abstract String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message); + public abstract String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message); + public abstract String chooseCardName(SpellAbility sa, List faces, String message); // better to have this odd method than those if playerType comparison in ChangeZone public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider); diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 56abff9a67d..1d0f4bccbb0 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -22,6 +22,7 @@ import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.DrawAi; import forge.ai.ability.GameWinAi; import forge.card.ColorSet; +import forge.card.ICardFace; import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; @@ -82,117 +83,117 @@ import forge.util.MyRandom; * Test cases that need to override the default behaviour of this class should make sure to do so in a way that does not invalidate their correctness. */ public class PlayerControllerForTests extends PlayerController { - private PlayerActions playerActions; + private PlayerActions playerActions; - public PlayerControllerForTests(Game game, Player player, LobbyPlayer lobbyPlayer) { - super(game, player, lobbyPlayer); - } + public PlayerControllerForTests(Game game, Player player, LobbyPlayer lobbyPlayer) { + super(game, player, lobbyPlayer); + } - public void setPlayerActions(PlayerActions playerActions) { - this.playerActions = playerActions; - } + public void setPlayerActions(PlayerActions playerActions) { + this.playerActions = playerActions; + } - public PlayerActions getPlayerActions() { - return playerActions; - } + public PlayerActions getPlayerActions() { + return playerActions; + } - public Player getPlayer() { - return player; - } + public Player getPlayer() { + return player; + } - public Game getGame() { - return game; - } + public Game getGame() { + return game; + } - @Override - public void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets) { - throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); - } + @Override + public void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets) { + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); + } - @Override - public void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets) { - //TODO: eventually (when the real code is refactored) this should be handled normally... - if (effectSA.getDescription().equals("At the beginning of your upkeep, if you have exactly 1 life, you win the game.")) {//test_104_2b_effect_may_state_that_player_wins_the_game - HumanPlay.playSpellAbilityNoStack(null, player, effectSA, !mayChoseNewTargets); - return; - } - SpellAbilityAi sai = SpellApiToAi.Converter.get(effectSA.getApi()); - if ( - (effectSA.getHostCard().getName().equals("Nefarious Lich") && sai instanceof DrawAi) || - (effectSA.getHostCard().getName().equals("Laboratory Maniac") && sai instanceof GameWinAi) || - (effectSA.getHostCard().getName().equals("Nefarious Lich") && sai instanceof ChangeZoneAi) - ) {//test_104_3f_if_a_player_would_win_and_lose_simultaneously_he_loses - HumanPlay.playSpellAbilityNoStack(null, player, effectSA, !mayChoseNewTargets); - return; - } - throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); - } + @Override + public void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets) { + //TODO: eventually (when the real code is refactored) this should be handled normally... + if (effectSA.getDescription().equals("At the beginning of your upkeep, if you have exactly 1 life, you win the game.")) {//test_104_2b_effect_may_state_that_player_wins_the_game + HumanPlay.playSpellAbilityNoStack(null, player, effectSA, !mayChoseNewTargets); + return; + } + SpellAbilityAi sai = SpellApiToAi.Converter.get(effectSA.getApi()); + if ( + (effectSA.getHostCard().getName().equals("Nefarious Lich") && sai instanceof DrawAi) || + (effectSA.getHostCard().getName().equals("Laboratory Maniac") && sai instanceof GameWinAi) || + (effectSA.getHostCard().getName().equals("Nefarious Lich") && sai instanceof ChangeZoneAi) + ) {//test_104_3f_if_a_player_would_win_and_lose_simultaneously_he_loses + HumanPlay.playSpellAbilityNoStack(null, player, effectSA, !mayChoseNewTargets); + return; + } + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); + } - @Override - public List sideboard(Deck deck, GameType gameType) { - return null; // refused to side - } + @Override + public List sideboard(Deck deck, GameType gameType) { + return null; // refused to side + } - @Override - public Map assignCombatDamage(Card attacker, CardCollectionView blockers, int damageDealt, GameEntity defender, boolean overrideOrder) { - if (blockers.size() == 1 && damageDealt == 2 && ( - (attacker.getName().equals("Grizzly Bears") && blockers.get(0).getName().equals("Ajani's Sunstriker")) || - (attacker.getName().equals("Ajani's Sunstriker") && blockers.get(0).getName().equals("Grizzly Bears")) - )) {//test_104_3b_player_with_less_than_zero_life_loses_the_game_only_when_a_player_receives_priority_variant_with_combat - Map result = new HashMap(); - result.put(blockers.get(0), damageDealt); - return result; - } - throw new IllegalStateException("Erring on the side of caution here..."); - } + @Override + public Map assignCombatDamage(Card attacker, CardCollectionView blockers, int damageDealt, GameEntity defender, boolean overrideOrder) { + if (blockers.size() == 1 && damageDealt == 2 && ( + (attacker.getName().equals("Grizzly Bears") && blockers.get(0).getName().equals("Ajani's Sunstriker")) || + (attacker.getName().equals("Ajani's Sunstriker") && blockers.get(0).getName().equals("Grizzly Bears")) + )) {//test_104_3b_player_with_less_than_zero_life_loses_the_game_only_when_a_player_receives_priority_variant_with_combat + Map result = new HashMap(); + result.put(blockers.get(0), damageDealt); + return result; + } + throw new IllegalStateException("Erring on the side of caution here..."); + } - @Override - public Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero) { - throw new IllegalStateException("Erring on the side of caution here..."); - } + @Override + public Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero) { + throw new IllegalStateException("Erring on the side of caution here..."); + } - @Override - public CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message) { - return chooseItems(validTargets, min); - } + @Override + public CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message) { + return chooseItems(validTargets, min); + } - @Override - public CardCollectionView choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message) { - return chooseItems(validTargets, min); - } + @Override + public CardCollectionView choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message) { + return chooseItems(validTargets, min); + } - @Override - public TargetChoices chooseNewTargetsFor(SpellAbility ability) { - throw new IllegalStateException("Erring on the side of caution here..."); - } + @Override + public TargetChoices chooseNewTargetsFor(SpellAbility ability) { + throw new IllegalStateException("Erring on the side of caution here..."); + } - @Override - public Pair chooseTarget(SpellAbility sa, List> allTargets) { - return chooseItem(allTargets); - } + @Override + public Pair chooseTarget(SpellAbility sa, List> allTargets) { + return chooseItem(allTargets); + } - @Override - public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) { - return chooseItems(sourceList, max); - } + @Override + public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) { + return chooseItems(sourceList, max); + } - @Override - public T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) { + @Override + public T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) { if (delayedReveal != null) { reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix()); } - return chooseItem(optionList); - } + return chooseItem(optionList); + } - @Override - public SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title) { - return chooseItem(spells); - } + @Override + public SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title) { + return chooseItem(spells); + } - @Override - public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { - return true; - } + @Override + public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { + return true; + } @Override public boolean confirmBidAction(SpellAbility sa, @@ -200,12 +201,12 @@ public class PlayerControllerForTests extends PlayerController { return false; } - @Override - public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { - return true; - } + @Override + public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { + return true; + } - @Override + @Override public boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map triggerParams, boolean isMandatory) { return true; } @@ -215,244 +216,244 @@ public class PlayerControllerForTests extends PlayerController { return this.player; } - @Override - public CardCollection orderBlockers(Card attacker, CardCollection blockers) { - return blockers; - } + @Override + public CardCollection orderBlockers(Card attacker, CardCollection blockers) { + return blockers; + } - @Override - public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { - final CardCollection allBlockers = new CardCollection(oldBlockers); - allBlockers.add(blocker); - return allBlockers; - } + @Override + public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { + final CardCollection allBlockers = new CardCollection(oldBlockers); + allBlockers.add(blocker); + return allBlockers; + } - @Override - public CardCollection orderAttackers(Card blocker, CardCollection attackers) { - return attackers; - } + @Override + public CardCollection orderAttackers(Card blocker, CardCollection attackers) { + return attackers; + } - @Override - public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix) { - //nothing needs to be done here - } + @Override + public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix) { + //nothing needs to be done here + } - @Override - public void reveal(List cards, ZoneType zone, PlayerView owner, String messagePrefix) { - //nothing needs to be done here - } + @Override + public void reveal(List cards, ZoneType zone, PlayerView owner, String messagePrefix) { + //nothing needs to be done here + } - @Override - public void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value) { - //nothing needs to be done here - } + @Override + public void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value) { + //nothing needs to be done here + } - @Override - public ImmutablePair arrangeForScry(CardCollection topN) { - return ImmutablePair.of(topN, null); - } + @Override + public ImmutablePair arrangeForScry(CardCollection topN) { + return ImmutablePair.of(topN, null); + } - @Override - public boolean willPutCardOnTop(Card c) { - return false; - } + @Override + public boolean willPutCardOnTop(Card c) { + return false; + } - @Override - public CardCollectionView orderMoveToZoneList(CardCollectionView cards, ZoneType destinationZone) { - return cards; - } + @Override + public CardCollectionView orderMoveToZoneList(CardCollectionView cards, ZoneType destinationZone) { + return cards; + } - @Override - public CardCollection chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max) { - return chooseItems(validCards, min); - } + @Override + public CardCollection chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max) { + return chooseItems(validCards, min); + } - @Override - public void playMiracle(SpellAbility miracle, Card card) { - throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); - } + @Override + public void playMiracle(SpellAbility miracle, Card card) { + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); + } - @Override - public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) { - return CardCollection.EMPTY; - } + @Override + public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) { + return CardCollection.EMPTY; + } - @Override - public CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid) { - return chooseItems(valid, min); - } + @Override + public CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid) { + return chooseItems(valid, min); + } - @Override - public CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa) { - throw new IllegalStateException("Erring on the side of caution here..."); - } + @Override + public CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa) { + throw new IllegalStateException("Erring on the side of caution here..."); + } - @Override - public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) { - return usableFromOpeningHand; - } + @Override + public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) { + return usableFromOpeningHand; + } - @Override - public Mana chooseManaFromPool(List manaChoices) { - return chooseItem(manaChoices); - } + @Override + public Mana chooseManaFromPool(List manaChoices) { + return chooseItem(manaChoices); + } - @Override - public Pair chooseAndRemoveOrPutCounter(Card cardWithCounter) { - throw new IllegalStateException("Erring on the side of caution here..."); - } + @Override + public Pair chooseAndRemoveOrPutCounter(Card cardWithCounter) { + throw new IllegalStateException("Erring on the side of caution here..."); + } - @Override - public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) { - return true; - } + @Override + public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) { + return true; + } - @Override - public CardCollectionView getCardsToMulligan(Player firstPlayer) { - return null; - } + @Override + public CardCollectionView getCardsToMulligan(Player firstPlayer) { + return null; + } - @Override - public void declareAttackers(Player attacker, Combat combat) { - //Doing nothing is safe in most cases, but not all (creatures that must attack etc). TODO: introduce checks? - if (playerActions == null) { - return; - } - DeclareAttackersAction declareAttackers = playerActions.getNextActionIfApplicable(player, game, DeclareAttackersAction.class); - if (declareAttackers == null) { - return; - } + @Override + public void declareAttackers(Player attacker, Combat combat) { + //Doing nothing is safe in most cases, but not all (creatures that must attack etc). TODO: introduce checks? + if (playerActions == null) { + return; + } + DeclareAttackersAction declareAttackers = playerActions.getNextActionIfApplicable(player, game, DeclareAttackersAction.class); + if (declareAttackers == null) { + return; + } - //TODO: check that the chosen attack configuration is legal? (Including creatures that did not attack but should) - //TODO: check that the chosen attack configuration was a complete match to what was requested? - //TODO: banding (don't really care at the moment...) + //TODO: check that the chosen attack configuration is legal? (Including creatures that did not attack but should) + //TODO: check that the chosen attack configuration was a complete match to what was requested? + //TODO: banding (don't really care at the moment...) - for (Map.Entry playerAttackAssignment : declareAttackers.getPlayerAttackAssignments().entrySet()) { - Player defender = getPlayerBeingAttacked(game, player, playerAttackAssignment.getValue()); - attack(combat, playerAttackAssignment.getKey(), defender); - } - for (Map.Entry planeswalkerAttackAssignment: declareAttackers.getPlaneswalkerAttackAssignments().entrySet()) { - Card defender = CardSpecificationHandler.INSTANCE.find(game.getCardsInGame(), planeswalkerAttackAssignment.getKey()); - attack(combat, planeswalkerAttackAssignment.getKey(), defender); - } + for (Map.Entry playerAttackAssignment : declareAttackers.getPlayerAttackAssignments().entrySet()) { + Player defender = getPlayerBeingAttacked(game, player, playerAttackAssignment.getValue()); + attack(combat, playerAttackAssignment.getKey(), defender); + } + for (Map.Entry planeswalkerAttackAssignment: declareAttackers.getPlaneswalkerAttackAssignments().entrySet()) { + Card defender = CardSpecificationHandler.INSTANCE.find(game.getCardsInGame(), planeswalkerAttackAssignment.getKey()); + attack(combat, planeswalkerAttackAssignment.getKey(), defender); + } - if (!CombatUtil.validateAttackers(combat)) { - throw new IllegalStateException("Illegal attack declaration!"); - } - } + if (!CombatUtil.validateAttackers(combat)) { + throw new IllegalStateException("Illegal attack declaration!"); + } + } - private Player getPlayerBeingAttacked(Game game, Player attacker, PlayerSpecification defenderSpecification) { - if (defenderSpecification != null) { - return PlayerSpecificationHandler.INSTANCE.find(game.getPlayers(), defenderSpecification); - } - if (game.getPlayers().size() != 2) { - throw new IllegalStateException("Can't use implicit defender specification in this situation!"); - } - for (Player player : game.getPlayers()) { - if (!attacker.equals(player)) { - return player; - } - } - throw new IllegalStateException("Couldn't find implicit defender!"); - } + private Player getPlayerBeingAttacked(Game game, Player attacker, PlayerSpecification defenderSpecification) { + if (defenderSpecification != null) { + return PlayerSpecificationHandler.INSTANCE.find(game.getPlayers(), defenderSpecification); + } + if (game.getPlayers().size() != 2) { + throw new IllegalStateException("Can't use implicit defender specification in this situation!"); + } + for (Player player : game.getPlayers()) { + if (!attacker.equals(player)) { + return player; + } + } + throw new IllegalStateException("Couldn't find implicit defender!"); + } - private void attack(Combat combat, CardSpecification attackerSpecification, GameEntity defender) { - Card attacker = CardSpecificationHandler.INSTANCE.find(combat.getAttackingPlayer().getCreaturesInPlay(), attackerSpecification); - if (!CombatUtil.canAttack(attacker, defender)) { - throw new IllegalStateException(attacker + " can't attack " + defender); - } - combat.addAttacker(attacker, defender); - } + private void attack(Combat combat, CardSpecification attackerSpecification, GameEntity defender) { + Card attacker = CardSpecificationHandler.INSTANCE.find(combat.getAttackingPlayer().getCreaturesInPlay(), attackerSpecification); + if (!CombatUtil.canAttack(attacker, defender)) { + throw new IllegalStateException(attacker + " can't attack " + defender); + } + combat.addAttacker(attacker, defender); + } - @Override - public void declareBlockers(Player defender, Combat combat) { - //Doing nothing is safe in most cases, but not all (creatures that must block, attackers that must be blocked etc). TODO: legality checks? - if (playerActions == null) { - return; - } - DeclareBlockersAction declareBlockers = playerActions.getNextActionIfApplicable(player, game, DeclareBlockersAction.class); - if (declareBlockers == null) { - return; - } + @Override + public void declareBlockers(Player defender, Combat combat) { + //Doing nothing is safe in most cases, but not all (creatures that must block, attackers that must be blocked etc). TODO: legality checks? + if (playerActions == null) { + return; + } + DeclareBlockersAction declareBlockers = playerActions.getNextActionIfApplicable(player, game, DeclareBlockersAction.class); + if (declareBlockers == null) { + return; + } - //TODO: check that the chosen block configuration is 100% legal? - //TODO: check that the chosen block configuration was a 100% match to what was requested? - //TODO: where do damage assignment orders get handled? + //TODO: check that the chosen block configuration is 100% legal? + //TODO: check that the chosen block configuration was a 100% match to what was requested? + //TODO: where do damage assignment orders get handled? - for (Map.Entry> blockingAssignment : declareBlockers.getBlockingAssignments().asMap().entrySet()) { - Card attacker = CardSpecificationHandler.INSTANCE.find(combat.getAttackers(), blockingAssignment.getKey()); - for (CardSpecification blockerSpecification : blockingAssignment.getValue()) { - Card blocker = CardSpecificationHandler.INSTANCE.find(game, blockerSpecification); - if (!CombatUtil.canBlock(attacker, blocker)) { - throw new IllegalStateException(blocker + " can't block " + blocker); - } - combat.addBlocker(attacker, blocker); - } - } - String blockValidation = CombatUtil.validateBlocks(combat, player); - if (blockValidation != null) { - throw new IllegalStateException(blockValidation); - } - } + for (Map.Entry> blockingAssignment : declareBlockers.getBlockingAssignments().asMap().entrySet()) { + Card attacker = CardSpecificationHandler.INSTANCE.find(combat.getAttackers(), blockingAssignment.getKey()); + for (CardSpecification blockerSpecification : blockingAssignment.getValue()) { + Card blocker = CardSpecificationHandler.INSTANCE.find(game, blockerSpecification); + if (!CombatUtil.canBlock(attacker, blocker)) { + throw new IllegalStateException(blocker + " can't block " + blocker); + } + combat.addBlocker(attacker, blocker); + } + } + String blockValidation = CombatUtil.validateBlocks(combat, player); + if (blockValidation != null) { + throw new IllegalStateException(blockValidation); + } + } - @Override - public List chooseSpellAbilityToPlay() { - //TODO: This method has to return the spellability chosen by player - // It should not play the sa right from here. The code has been left as it is to quickly adapt to changed playercontroller interface - if (playerActions != null) { - CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable(player, game, CastSpellFromHandAction.class); - if (castSpellFromHand != null) { - castSpellFromHand.castSpellFromHand(player, game); - } + @Override + public List chooseSpellAbilityToPlay() { + //TODO: This method has to return the spellability chosen by player + // It should not play the sa right from here. The code has been left as it is to quickly adapt to changed playercontroller interface + if (playerActions != null) { + CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable(player, game, CastSpellFromHandAction.class); + if (castSpellFromHand != null) { + castSpellFromHand.castSpellFromHand(player, game); + } - ActivateAbilityAction activateAbilityAction = playerActions.getNextActionIfApplicable(player, game, ActivateAbilityAction.class); - if (activateAbilityAction != null) { - activateAbilityAction.activateAbility(player, game); - } - } - return null; - } + ActivateAbilityAction activateAbilityAction = playerActions.getNextActionIfApplicable(player, game, ActivateAbilityAction.class); + if (activateAbilityAction != null) { + activateAbilityAction.activateAbility(player, game); + } + } + return null; + } - @Override - public CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard) { - return chooseItems(player.getZone(ZoneType.Hand).getCards(), numDiscard); - } + @Override + public CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard) { + return chooseItems(player.getZone(ZoneType.Hand).getCards(), numDiscard); + } - @Override - public boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose) { - throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); - } + @Override + public boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose) { + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); + } - @Override - public int chooseNumber(SpellAbility sa, String title, int min, int max) { - return min; - } + @Override + public int chooseNumber(SpellAbility sa, String title, int min, int max) { + return min; + } - @Override - public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultVal) { - return true; - } + @Override + public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultVal) { + return true; + } - @Override - public boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call) { - return true; - } + @Override + public boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call) { + return true; + } - @Override - public Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap) { - return choiceMap.get(options.get(0)); - } + @Override + public Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap) { + return choiceMap.get(options.get(0)); + } - @Override - public List chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat) { - throw new IllegalStateException("Erring on the side of caution here..."); - } + @Override + public List chooseModeForAbility(SpellAbility sa, int min, int num, boolean allowRepeat) { + throw new IllegalStateException("Erring on the side of caution here..."); + } - @Override - public byte chooseColor(String message, SpellAbility sa, ColorSet colors) { - return Iterables.getFirst(colors, MagicColor.WHITE); - } - + @Override + public byte chooseColor(String message, SpellAbility sa, ColorSet colors) { + return Iterables.getFirst(colors, MagicColor.WHITE); + } + @Override public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) { return Iterables.getFirst(colors, (byte)0); @@ -488,11 +489,6 @@ public class PlayerControllerForTests extends PlayerController { return chooseItem(options); } - @Override - public PaperCard chooseSinglePaperCard(SpellAbility sa, String message, Predicate cpp, String name) { - throw new IllegalStateException("Erring on the side of caution here..."); - } - @Override public List chooseColors(String message, SpellAbility sa, int min, int max, List options) { throw new UnsupportedOperationException("No idea how a test player controller would choose colors"); @@ -594,10 +590,10 @@ public class PlayerControllerForTests extends PlayerController { // test this! } - @Override - public CardShields chooseRegenerationShield(Card c) { - return Iterables.getFirst(c.getShields(), null); - } + @Override + public CardShields chooseRegenerationShield(Card c) { + return Iterables.getFirst(c.getShields(), null); + } @Override public List chooseCardsYouWonToAddToDeck(List losses) { @@ -632,13 +628,6 @@ public class PlayerControllerForTests extends PlayerController { } - @Override - public String chooseCardName(SpellAbility sa, Predicate cpp, - String valid, String message) { - - return null; - } - @Override public Card chooseSingleCardForZoneChange(ZoneType destination, List origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, @@ -668,4 +657,23 @@ public class PlayerControllerForTests extends PlayerController { public void cancelAwaitNextInput() { // Not used by the controller for tests } + + @Override + public ICardFace chooseSingleCardFace(SpellAbility sa, String message, Predicate cpp, String name) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String chooseCardName(SpellAbility sa, List faces, String message) { + // TODO Auto-generated method stub + return null; + } + } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 95b989564bb..9ad3e85449d 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -38,7 +38,9 @@ import forge.GuiBase; import forge.LobbyPlayer; import forge.achievement.AchievementCollection; import forge.ai.GameState; +import forge.card.CardDb; import forge.card.ColorSet; +import forge.card.ICardFace; import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; @@ -1148,9 +1150,9 @@ public class PlayerControllerHuman } @Override - public PaperCard chooseSinglePaperCard(final SpellAbility sa, final String message, final Predicate cpp, final String name) { - final Iterable cardsFromDb = FModel.getMagicDb().getCommonCards().getUniqueCards(); - final List cards = Lists.newArrayList(Iterables.filter(cardsFromDb, cpp)); + public ICardFace chooseSingleCardFace(final SpellAbility sa, final String message, final Predicate cpp, final String name) { + final Iterable cardsFromDb = FModel.getMagicDb().getCommonCards().getAllFaces(); + final List cards = Lists.newArrayList(Iterables.filter(cardsFromDb, cpp)); Collections.sort(cards); return getGui().one(message, cards); } @@ -1372,9 +1374,10 @@ public class PlayerControllerHuman } @Override - public String chooseCardName(final SpellAbility sa, final Predicate cpp, final String valid, final String message) { + public String chooseCardName(final SpellAbility sa, final Predicate cpp, final String valid, final String message) { while (true) { - final PaperCard cp = chooseSinglePaperCard(sa, message, cpp, sa.getHostCard().getName()); + final ICardFace cardFace = chooseSingleCardFace(sa, message, cpp, sa.getHostCard().getName()); + final PaperCard cp = FModel.getMagicDb().getCommonCards().getCard(cardFace.getName()); final Card instanceForPlayer = Card.fromPaperCard(cp, player); // the Card instance for test needs a game to be tested if (instanceForPlayer.isValid(valid, sa.getHostCard().getController(), sa.getHostCard(), sa)) { return cp.getName(); @@ -1768,15 +1771,18 @@ public class PlayerControllerHuman return; } - final List cards = Lists.newArrayList(FModel.getMagicDb().getCommonCards().getUniqueCards()); - Collections.sort(cards); + final CardDb carddb = FModel.getMagicDb().getCommonCards(); + final List faces = Lists.newArrayList(carddb.getAllFaces()); + Collections.sort(faces); // use standard forge's list selection dialog - final IPaperCard c = getGui().oneOrNone("Name the card", cards); - if (c == null) { + final ICardFace f = getGui().oneOrNone("Name the card", faces); + if (f == null) { return; } + final PaperCard c = carddb.getUniqueByName(f.getName()); + game.getAction().invoke(new Runnable() { @Override public void run() { game.getAction().moveToHand(Card.fromPaperCard(c, p)); }}); @@ -1792,15 +1798,18 @@ public class PlayerControllerHuman return; } - final List cards = Lists.newArrayList(FModel.getMagicDb().getCommonCards().getUniqueCards()); - Collections.sort(cards); + final CardDb carddb = FModel.getMagicDb().getCommonCards(); + final List faces = Lists.newArrayList(carddb.getAllFaces()); + Collections.sort(faces); // use standard forge's list selection dialog - final IPaperCard c = getGui().oneOrNone("Name the card", cards); - if (c == null) { + final ICardFace f = getGui().oneOrNone("Name the card", faces); + if (f == null) { return; } + final PaperCard c = carddb.getUniqueByName(f.getName()); + game.getAction().invoke(new Runnable() { @Override public void run() { final Card forgeCard = Card.fromPaperCard(c, p); @@ -1931,4 +1940,10 @@ public class PlayerControllerHuman hand.reorder(game.getCard(card), index); player.updateZoneForView(hand); } + + @Override + public String chooseCardName(SpellAbility sa, List faces, String message) { + ICardFace face = getGui().one(message, faces); + return face == null ? "" : face.getName(); + } }