diff --git a/src/main/java/forge/card/ability/SpellAbilityAi.java b/src/main/java/forge/card/ability/SpellAbilityAi.java index e05c1212bc1..6a7230cce24 100644 --- a/src/main/java/forge/card/ability/SpellAbilityAi.java +++ b/src/main/java/forge/card/ability/SpellAbilityAi.java @@ -118,7 +118,7 @@ public abstract class SpellAbilityAi { return options.get(0); } - public Player chooseSinglePlayer(Player ai, SpellAbility sa, List options, boolean isOptional) { + public Player chooseSinglePlayer(Player ai, SpellAbility sa, List options) { System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); return options.get(0); } diff --git a/src/main/java/forge/card/ability/ai/AttachAi.java b/src/main/java/forge/card/ability/ai/AttachAi.java index 4d1d1ca31db..dc19c85d5a9 100644 --- a/src/main/java/forge/card/ability/ai/AttachAi.java +++ b/src/main/java/forge/card/ability/ai/AttachAi.java @@ -29,6 +29,7 @@ import forge.game.phase.CombatUtil; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -675,7 +676,7 @@ public class AttachAi extends SpellAbilityAi { * the mandatory * @return true, if successful */ - public static boolean attachPreference(final SpellAbility sa, final Target tgt, final boolean mandatory) { + private static boolean attachPreference(final SpellAbility sa, final Target tgt, final boolean mandatory) { Object o; if (tgt.canTgtPlayer()) { o = attachToPlayerAIPreferences(sa.getActivatingPlayer(), sa, mandatory); @@ -1164,4 +1165,18 @@ public class AttachAi extends SpellAbilityAi { return true; } + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + return true; + } + + @Override + public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + return attachToCardAIPreferences(ai, sa, true); + } + + @Override + public Player chooseSinglePlayer(Player ai, SpellAbility sa, List options) { + return attachToPlayerAIPreferences(ai, sa, true); + } } diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 67b4d8df4e9..fab4b7a3996 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -1389,4 +1389,14 @@ public class ChangeZoneAi extends SpellAbilityAi { return ComputerUtilCard.getBestAI(options); } + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List) + */ + @Override + public Player chooseSinglePlayer(Player ai, SpellAbility sa, List options) { + // Currently only used by Curse of Misfortunes, so this branch should never get hit + // But just in case it does, just select the first option + return options.get(0); + } + } diff --git a/src/main/java/forge/card/ability/ai/ChoosePlayerAi.java b/src/main/java/forge/card/ability/ai/ChoosePlayerAi.java index 032e29d5f7c..e587b7c1ca6 100644 --- a/src/main/java/forge/card/ability/ai/ChoosePlayerAi.java +++ b/src/main/java/forge/card/ability/ai/ChoosePlayerAi.java @@ -1,5 +1,7 @@ package forge.card.ability.ai; +import java.util.List; + import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; import forge.game.player.Player; @@ -24,4 +26,29 @@ public class ChoosePlayerAi extends SpellAbilityAi { return canPlayAI(ai, sa); } + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List) + */ + @Override + public Player chooseSinglePlayer(Player ai, SpellAbility sa, List choices) { + Player chosen = null; + if ("Curse".equals(sa.getParam("AILogic"))) { + for (Player pc : choices) { + if (pc.isOpponentOf(ai)) { + chosen = pc; + break; + } + } + if (chosen == null) { + System.out.println("No good curse choices. Picking first available: " + choices.get(0)); + chosen = choices.get(0); + } + } else if ("Pump".equals(sa.getParam("AILogic"))) { + chosen = choices.contains(ai) ? ai : choices.get(0); + } else { + System.out.println("Default player choice logic."); + chosen = choices.contains(ai) ? ai : choices.get(0); + } + return chosen; + } } diff --git a/src/main/java/forge/card/ability/effects/AttachEffect.java b/src/main/java/forge/card/ability/effects/AttachEffect.java index 3576dbf9680..534ed756593 100644 --- a/src/main/java/forge/card/ability/effects/AttachEffect.java +++ b/src/main/java/forge/card/ability/effects/AttachEffect.java @@ -10,14 +10,11 @@ import forge.GameEntity; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; import forge.card.ability.SpellAbilityEffect; -import forge.card.ability.ai.AttachAi; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.Game; import forge.game.player.Player; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.GuiDialog; public class AttachEffect extends SpellAbilityEffect { @@ -46,12 +43,10 @@ public class AttachEffect extends SpellAbilityEffect { card = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa).get(0); } - final StringBuilder sb = new StringBuilder(); - sb.append("Do you want to attach " + card + " to " + targets + "?"); - if (sa.getActivatingPlayer().isHuman() && sa.hasParam("Optional") - && !GuiDialog.confirm(source, sb.toString())) { + final Player p = sa.getActivatingPlayer(); + String message = "Do you want to attach " + card + " to " + targets + "?"; + if ( sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message) ) return; - } // If Cast Targets will be checked on the Stack for (final Object o : targets) { @@ -158,14 +153,12 @@ public class AttachEffect extends SpellAbilityEffect { * @return the attach spell ability */ public static SpellAbility getAttachSpellAbility(final Card source) { - SpellAbility aura = null; for (final SpellAbility sa : source.getSpells()) { if (sa.getApi() == ApiType.Attach) { - aura = sa; - break; + return sa; } } - return aura; + return null; } /** @@ -187,50 +180,34 @@ public class AttachEffect extends SpellAbilityEffect { final Game game = source.getGame(); final Target tgt = aura.getTarget(); - if (source.getController().isHuman()) { - if (tgt.canTgtPlayer()) { - final ArrayList players = new ArrayList(); + Player p = source.getController(); + if (tgt.canTgtPlayer()) { + final ArrayList players = new ArrayList(); - for (Player player : game.getPlayers()) { - if (player.isValid(tgt.getValidTgts(), aura.getActivatingPlayer(), source)) { - players.add(player); - } - } - - final Player p = GuiChoose.one(source + " - Select a player to attach to.", players); - if (p != null) { - handleAura(source, p); - return true; - } - } else { - List list = game.getCardsIn(tgt.getZone()); - list = CardLists.getValidCards(list, tgt.getValidTgts(), aura.getActivatingPlayer(), source); - if (list.isEmpty()) { - return false; - } - - final Object o = GuiChoose.one(source + " - Select a card to attach to.", list); - if (o instanceof Card) { - handleAura(source, (Card) o); - //source.enchantEntity((Card) o); - return true; + for (Player player : game.getPlayers()) { + if (player.isValid(tgt.getValidTgts(), aura.getActivatingPlayer(), source)) { + players.add(player); } } - } + final Player pa = p.getController().chooseSinglePlayerForEffect(players, aura, source + " - Select a player to attach to."); + if (pa != null) { + handleAura(source, pa); + return true; + } + } else { + List list = game.getCardsIn(tgt.getZone()); + list = CardLists.getValidCards(list, tgt.getValidTgts(), aura.getActivatingPlayer(), source); + if (list.isEmpty()) { + return false; + } - else if (AttachAi.attachPreference(aura, tgt, true)) { - final Object o = aura.getTarget().getTargets().get(0); - if (o instanceof Card) { - //source.enchantEntity((Card) o); + final Card o = p.getController().chooseSingleCardForEffect(list, aura, source + " - Select a card to attach to."); + if (o != null) { handleAura(source, (Card) o); - return true; - } else if (o instanceof Player) { - //source.enchantEntity((Player) o); - handleAura(source, (Player) o); + //source.enchantEntity((Card) o); return true; } } - return false; } } diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java index 8c7eab14ed0..3999f68a7e2 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java @@ -17,7 +17,6 @@ import forge.card.spellability.SpellAbility; import forge.game.Game; import forge.game.player.Player; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; import forge.util.MyRandom; public class ChangeZoneAllEffect extends SpellAbilityEffect { @@ -65,10 +64,10 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { cards.addAll(p.getCardsIn(origin)); } } - if (sa.getActivatingPlayer().isHuman() && origin.contains(ZoneType.Library) - && sa.hasParam("Search")) { - GuiChoose.oneOrNone("Looking at the Library", - CardLists.getValidCards(cards, "Card.inZoneLibrary", sa.getActivatingPlayer(), sa.getSourceCard())); + + if (origin.contains(ZoneType.Library) && sa.hasParam("Search")) { + List libCards = CardLists.getValidCards(cards, "Card.inZoneLibrary", sa.getActivatingPlayer(), sa.getSourceCard()); + sa.getActivatingPlayer().getController().reveal("Looking at the Library", libCards, ZoneType.Library, sa.getActivatingPlayer()); } cards = AbilityUtils.filterListByType(cards, sa.getParam("ChangeType"), sa); diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java index 1ec18cad7a2..53655287910 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java @@ -813,19 +813,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { List list = AbilityUtils.getDefinedPlayers(card, sa.getParam("AttachedToPlayer"), sa); if (!list.isEmpty()) { - Player attachedTo = null; - - if (list.size() == 1) { - attachedTo = list.get(0); - } else { - if (player.isHuman()) { - attachedTo = GuiChoose.one(c + " - Select a player to attach to.", list); - } else { // AI player - // Currently only used by Curse of Misfortunes, so this branch should never get hit - // But just in case it does, just select the first option - attachedTo = list.get(0); - } - } + Player attachedTo = player.getController().chooseSinglePlayerForEffect(list, sa, c + " - Select a player to attach to."); if (c.isAura()) { if (c.isEnchanting()) { // If this Card is already Enchanting something, need diff --git a/src/main/java/forge/card/ability/effects/ChoosePlayerEffect.java b/src/main/java/forge/card/ability/effects/ChoosePlayerEffect.java index 6b95c55fb47..eefd4603e9c 100644 --- a/src/main/java/forge/card/ability/effects/ChoosePlayerEffect.java +++ b/src/main/java/forge/card/ability/effects/ChoosePlayerEffect.java @@ -8,7 +8,6 @@ import forge.card.ability.SpellAbilityEffect; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.player.Player; -import forge.gui.GuiChoose; public class ChoosePlayerEffect extends SpellAbilityEffect { @@ -39,31 +38,12 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { - Player chosen = null; - if (p.isHuman()) { - // Was if (sa.getActivatingPlayer().isHuman()) but defined player was being - // overwritten by activatingPlayer (or controller if no activator was set). - // Revert if it causes issues and remove Goblin Festival from card database. - chosen = GuiChoose.one(choiceDesc, choices); - } else { - if ("Curse".equals(sa.getParam("AILogic"))) { - for (Player pc : choices) { - if (pc.isOpponentOf(p)) { - chosen = pc; - break; - } - } - if (chosen == null) { - System.out.println("No good curse choices. Picking first available: " + choices.get(0)); - chosen = choices.get(0); - } - } else if ("Pump".equals(sa.getParam("AILogic"))) { - chosen = choices.contains(p) ? p : choices.get(0); - } else { - System.out.println("Default player choice logic."); - chosen = choices.contains(p) ? p : choices.get(0); - } - } + + // Was if (sa.getActivatingPlayer().isHuman()) but defined player was being + // overwritten by activatingPlayer (or controller if no activator was set). + // Revert if it causes issues and remove Goblin Festival from card database. + + Player chosen = choices.isEmpty() ? null : p.getController().chooseSinglePlayerForEffect(choices, sa, choiceDesc); if( null != chosen ) card.setChosenPlayer(chosen); diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index dcd169f03c4..5f711e100cb 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -93,8 +93,6 @@ public abstract class PlayerController { public abstract boolean playCascade(Card cascadedCard, Card sourceCard); public abstract void playSpellAbilityForFree(SpellAbility copySA); - - public abstract Deck sideboard(final Deck deck, GameType gameType); @@ -107,6 +105,7 @@ public abstract class PlayerController { 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 abstract Player chooseSinglePlayerForEffect(List options, SpellAbility sa, String title); public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message); public abstract boolean getWillPlayOnFirstTurn(boolean isFirstGame); public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message); @@ -142,4 +141,5 @@ public abstract class PlayerController { public abstract List chooseCardsToDiscardToMaximumHandSize(int numDiscard); public abstract boolean payManaOptional(Card card, Cost cost, String prompt, ManaPaymentPurpose purpose); + } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index bf780fa778f..0f15e3fc222 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -140,10 +140,19 @@ public class PlayerControllerAi extends PlayerController { if ( null == api ) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); } - return api.getAi().chooseSingleCard(player, sa, options, isOptional); } + @Override + public Player chooseSinglePlayerForEffect(List options, SpellAbility sa, String title) { + ApiType api = sa.getApi(); + if ( null == api ) { + throw new InvalidParameterException("SA is not api-based, this is not supported yet"); + } + return api.getAi().chooseSinglePlayer(player, sa, options); + } + + @Override public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { return getAi().confirmAction(sa, mode, message); diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 3ae3600a2b8..0a226024532 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -269,6 +269,16 @@ public class PlayerControllerHuman extends PlayerController { return options.get(0); } + @Override + public Player chooseSinglePlayerForEffect(List options, SpellAbility sa, String title) { + // Human is supposed to read the message and understand from it what to choose + if ( options.size() > 2 ) + return GuiChoose.one(title, options); + else + return options.get(0); + } + + /* (non-Javadoc) * @see forge.game.player.PlayerController#confirmAction(forge.card.spellability.SpellAbility, java.lang.String, java.lang.String) */