From 89dcef5b6743809953391ab96e5c55853e3cbc50 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Mon, 20 Jan 2014 07:32:12 +0000 Subject: [PATCH] moved convoke cards selection code from ManaCostBeingPaid to dedicated input for human player --- .gitattributes | 1 + .../forge/game/mana/ManaCostBeingPaid.java | 89 +++------------ .../forge/game/player/PlayerController.java | 8 ++ .../forge/game/player/PlayerControllerAi.java | 22 ++++ .../gui/input/InputSelectCardsForConvoke.java | 105 ++++++++++++++++++ .../gui/player/PlayerControllerHuman.java | 48 ++++++-- .../util/PlayerControllerForTests.java | 16 +++ 7 files changed, 203 insertions(+), 86 deletions(-) create mode 100644 forge-gui/src/main/java/forge/gui/input/InputSelectCardsForConvoke.java diff --git a/.gitattributes b/.gitattributes index 26154e17289..a3e17f8cbee 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15387,6 +15387,7 @@ forge-gui/src/main/java/forge/gui/input/InputPayManaSimple.java svneol=native#te forge-gui/src/main/java/forge/gui/input/InputPayManaX.java -text forge-gui/src/main/java/forge/gui/input/InputPlaybackControl.java -text forge-gui/src/main/java/forge/gui/input/InputProliferate.java -text +forge-gui/src/main/java/forge/gui/input/InputSelectCardsForConvoke.java -text forge-gui/src/main/java/forge/gui/input/InputSelectCardsFromList.java -text forge-gui/src/main/java/forge/gui/input/InputSelectEntitiesFromList.java -text forge-gui/src/main/java/forge/gui/input/InputSelectManyBase.java -text diff --git a/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java b/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java index 7e2946cc81a..145c0f8d3e0 100644 --- a/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java +++ b/forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java @@ -20,10 +20,10 @@ package forge.game.mana; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; - import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -38,12 +38,10 @@ import forge.game.Game; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates; -import forge.game.card.CardUtil; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbility; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; import forge.util.TextUtil; import forge.util.maps.EnumMapToAmount; import forge.util.maps.MapToAmount; @@ -569,7 +567,7 @@ public class ManaCostBeingPaid { } } else if (spell.getSourceCard().hasKeyword("Convoke")) { - adjustCostByConvoke(sa, spell); + adjustCostByConvoke(sa); } } // isSpell @@ -617,81 +615,22 @@ public class ManaCostBeingPaid { } } // GetSpellCostChange - private void adjustCostByConvoke(final SpellAbility sa, final SpellAbility spell) { + private void adjustCostByConvoke(final SpellAbility sa) { - List untappedCreats = CardLists.filter(spell.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES); + List untappedCreats = CardLists.filter(sa.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES); untappedCreats = CardLists.filter(untappedCreats, CardPredicates.Presets.UNTAPPED); - while (!untappedCreats.isEmpty() && getConvertedManaCost() > 0) { - Card workingCard = null; - String chosenColor = null; - if (sa.getActivatingPlayer().isHuman()) { - workingCard = GuiChoose.oneOrNone("Tap for Convoke? " + toString(), untappedCreats); - if (null == workingCard) { - break; // that means "I'm done" - } - - List usableColors = getConvokableColors(workingCard); - if (!usableColors.isEmpty()) { - chosenColor = usableColors.size() == 1 ? usableColors.get(0) : GuiChoose.one("Convoke for which color?", usableColors); - } - } - else { - // TODO: AI to choose a creature to tap would go here - // Probably along with deciding how many creatures to tap - break; - } - untappedCreats.remove(workingCard); - - if (null == chosenColor) { - continue; - } - else if (chosenColor.equals("colorless")) { - decreaseColorlessMana(1); - } - else { - decreaseShard(ManaCostShard.valueOf(MagicColor.fromName(chosenColor)), 1); - } - - sa.addTappedForConvoke(workingCard); + Map convokedCards = sa.getActivatingPlayer().getController().chooseCardsForConvoke(sa, this.toManaCost(), untappedCreats); + + // Convoked creats are tapped here with triggers suppressed, + // Then again when payment is done(In InputPayManaCost.done()) with suppression cleared. + // This is to make sure that triggers go off at the right time + // AND that you can't use mana tapabilities of convoked creatures to pay the convoked cost. + for (final Entry conv : convokedCards.entrySet()) { + sa.addTappedForConvoke(conv.getKey()); + this.decreaseShard(conv.getValue(), 1); + conv.getKey().setTapped(true); } - - // Convoked creats are tapped here with triggers - // suppressed, - // Then again when payment is done(In - // InputPayManaCost.done()) with suppression cleared. - // This is to make sure that triggers go off at the - // right time - // AND that you can't use mana tapabilities of convoked - // creatures - // to pay the convoked cost. - for (final Card c : sa.getTappedForConvoke()) { - c.setTapped(true); - } - } - - /** - * Gets the convokable colors. - * - * @param cardToConvoke - * the card to convoke - * @param cost - * the cost - * @return the convokable colors - */ - private List getConvokableColors(final Card cardToConvoke) { - final ArrayList usableColors = new ArrayList(); - - if (getColorlessManaAmount() > 0) { - usableColors.add("colorless"); - } - ColorSet cs = CardUtil.getColors(cardToConvoke); - for (byte color : MagicColor.WUBRG) { - if (cs.hasAnyColor(color)) { - usableColors.add(MagicColor.toLongString(color)); - } - } - return usableColors; } private void adjustCostByOffering(final SpellAbility sa, final SpellAbility spell) { diff --git a/forge-gui/src/main/java/forge/game/player/PlayerController.java b/forge-gui/src/main/java/forge/game/player/PlayerController.java index 67853f7b3ff..7ef167e55e5 100644 --- a/forge-gui/src/main/java/forge/game/player/PlayerController.java +++ b/forge-gui/src/main/java/forge/game/player/PlayerController.java @@ -13,6 +13,8 @@ import com.google.common.base.Predicate; import com.google.common.collect.Multimap; import forge.card.ColorSet; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; import forge.deck.Deck; import forge.game.Game; import forge.game.GameEntity; @@ -195,6 +197,7 @@ public abstract class PlayerController { public abstract List chooseModeForAbility(SpellAbility sa, int min, int num); 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 List chooseColors(String message, SpellAbility sa, int min, int max, List options); @@ -221,4 +224,9 @@ public abstract class PlayerController { public Collection complainCardsCantPlayWell(Deck myDeck) { return null; } public abstract boolean payManaCost(CostPartMana costPartMana, PaymentDecision pd, SpellAbility sa); + + public abstract Map chooseCardsForConvoke(SpellAbility sa, ManaCost manaCost, List untappedCreats); + + + } \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java b/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java index d80be695983..08cbba87e43 100644 --- a/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java @@ -29,6 +29,8 @@ import forge.ai.ComputerUtilMana; import forge.ai.ability.CharmAi; import forge.card.ColorSet; import forge.card.MagicColor; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; import forge.deck.Deck; import forge.game.Game; import forge.game.GameEntity; @@ -535,6 +537,18 @@ public class PlayerControllerAi extends PlayerController { return new ImmutablePair(countersOnCard.get(random),"Remove"); } + + @Override + public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) { + final String c = ComputerUtilCard.getMostProminentColor(player.getCardsIn(ZoneType.Hand)); + byte chosenColorMask = MagicColor.fromName(c); + if ((colors.getColor() & chosenColorMask) != 0) { + return chosenColorMask; + } else { + return Iterables.getFirst(colors, (byte)0); + } + } + @Override public byte chooseColor(String message, SpellAbility sa, ColorSet colors) { final String c = ComputerUtilCard.getMostProminentColor(player.getCardsIn(ZoneType.Hand)); @@ -713,4 +727,12 @@ public class PlayerControllerAi extends PlayerController { return ComputerUtilMana.payManaCost(player, sa); } + @Override + public Map chooseCardsForConvoke(SpellAbility sa, ManaCost manaCost, + List untappedCreats) { + // TODO: AI to choose a creature to tap would go here + // Probably along with deciding how many creatures to tap + return new HashMap(); + } + } diff --git a/forge-gui/src/main/java/forge/gui/input/InputSelectCardsForConvoke.java b/forge-gui/src/main/java/forge/gui/input/InputSelectCardsForConvoke.java new file mode 100644 index 00000000000..7555649cce6 --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/input/InputSelectCardsForConvoke.java @@ -0,0 +1,105 @@ +package forge.gui.input; + +import java.awt.event.MouseEvent; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; +import forge.game.card.Card; +import forge.game.card.CardUtil; +import forge.game.mana.ManaCostBeingPaid; +import forge.game.player.Player; + +/** + * TODO: Write javadoc for this type. + * + */ +public final class InputSelectCardsForConvoke extends InputSelectManyBase { + private static final long serialVersionUID = -1779224307654698954L; + private final Map> chosenCards = new HashMap>(); + private final ManaCostBeingPaid remainingCost; + private final Player player; + + public InputSelectCardsForConvoke(Player p, ManaCost cost, List untapped) { + super(1, Math.min(cost.getCMC(), untapped.size())); + remainingCost = new ManaCostBeingPaid(cost); + player = p; + allowUnselect = true; + + } + + + protected String getMessage() { + return "Choose creatures to tap for convoke.\nRemaining mana cost is " + remainingCost.toString(); + } + + @Override + protected void onCardSelected(final Card card, final MouseEvent triggerEvent) { + + boolean entityWasSelected = chosenCards.containsKey(card); + if (entityWasSelected) { + ImmutablePair color = this.chosenCards.remove(card); + remainingCost.increaseShard(color.right, 1); + onSelectStateChanged(card, false); + } + else { + + byte chosenColor = player.getController().chooseColorAllowColorless("Convoke " + card.toString() + " for which color?", card, CardUtil.getColors(card)); + + if (remainingCost.getColorlessManaAmount() > 0 && (chosenColor == 0 || !remainingCost.needsColor(chosenColor))) { + registerConvoked(card, ManaCostShard.COLORLESS, chosenColor); + } else { + for (ManaCostShard shard : remainingCost.getDistinctShards()) { + if (shard.canBePaidWithManaOfColor(chosenColor)) { + registerConvoked(card, shard, chosenColor); + return; + } + } + showMessage("The colors provided by " + card.toString() + " you've chosen cannot be used to decrease the manacost of " + remainingCost.toString()); + flashIncorrectAction(); + } + + } + + refresh(); + } + + private void registerConvoked(Card card, ManaCostShard shard, byte chosenColor) { + remainingCost.decreaseShard(shard, 1); + chosenCards.put(card, ImmutablePair.of(chosenColor, shard)); + onSelectStateChanged(card, true); + } + + + @Override + protected final void onPlayerSelected(Player player, final MouseEvent triggerEvent) { + } + + public Map getConvokeMap() { + Map result = new HashMap(); + if( !hasCancelled() ) + for(Entry> c : chosenCards.entrySet()) + result.put(c.getKey(), c.getValue().right); + return result; + } + + + @Override + protected boolean hasEnoughTargets() { return true; } + + @Override + protected boolean hasAllTargets() { return false; } + + + @Override + public Collection getSelected() { + // TODO Auto-generated method stub + return chosenCards.keySet(); + } +} \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/gui/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/gui/player/PlayerControllerHuman.java index 4ec519957d8..f502bdb96bd 100644 --- a/forge-gui/src/main/java/forge/gui/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/gui/player/PlayerControllerHuman.java @@ -29,6 +29,8 @@ import com.google.common.collect.Multimap; import forge.Singletons; import forge.card.ColorSet; import forge.card.MagicColor; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; @@ -69,6 +71,7 @@ import forge.gui.input.InputBlock; import forge.gui.input.InputConfirmMulligan; import forge.gui.input.InputPassPriority; import forge.gui.input.InputProliferate; +import forge.gui.input.InputSelectCardsForConvoke; import forge.gui.input.InputSelectCardsFromList; import forge.gui.input.InputConfirm; import forge.gui.input.InputSelectEntitiesFromList; @@ -898,19 +901,35 @@ public class PlayerControllerHuman extends PlayerController { switch (cntColors) { case 0: return 0; case 1: return colors.getColor(); - default: - String[] colorNames = new String[cntColors]; - int i = 0; - for (byte b : colors) { - colorNames[i++] = MagicColor.toLongString(b); - } - if (colorNames.length > 2) { - return MagicColor.fromName(GuiChoose.one(message, colorNames)); - } - int idxChosen = GuiDialog.confirm(sa.getSourceCard(), message, colorNames) ? 0 : 1; - return MagicColor.fromName(colorNames[idxChosen]); + default: return chooseColorCommon(message, sa == null ? null : sa.getSourceCard(), colors, false); } } + + @Override + public byte chooseColorAllowColorless(String message, Card c, ColorSet colors) { + int cntColors = 1 + colors.countColors(); + switch (cntColors) { + case 1: return 0; + default: return chooseColorCommon(message, c, colors, true); + } + } + + private byte chooseColorCommon(String message, Card c, ColorSet colors, boolean withColorless) { + int cntColors = colors.countColors(); + if( withColorless ) cntColors++; + String[] colorNames = new String[cntColors]; + int i = 0; + if(withColorless) + colorNames[i++] = MagicColor.toLongString((byte)0); + for (byte b : colors) { + colorNames[i++] = MagicColor.toLongString(b); + } + if (colorNames.length > 2) { + return MagicColor.fromName(GuiChoose.one(message, colorNames)); + } + int idxChosen = GuiDialog.confirm(c, message, colorNames) ? 0 : 1; + return MagicColor.fromName(colorNames[idxChosen]); + } @Override public PaperCard chooseSinglePaperCard(SpellAbility sa, String message, Predicate cpp, String name) { @@ -1059,4 +1078,11 @@ public class PlayerControllerHuman extends PlayerController { // TODO Auto-generated method stub return HumanPlay.payManaCost(costPartMana, sa, player); } + + @Override + public Map chooseCardsForConvoke(SpellAbility sa, ManaCost manaCost, List untappedCreats) { + InputSelectCardsForConvoke inp = new InputSelectCardsForConvoke(player, manaCost, untappedCreats); + inp.showAndWait(); + return inp.getConvokeMap(); + } } diff --git a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index a93d27f433e..58f231b9937 100644 --- a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -22,6 +22,8 @@ import forge.ai.ability.DrawAi; import forge.ai.ability.GameWinAi; import forge.card.ColorSet; import forge.card.MagicColor; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; import forge.deck.Deck; import forge.game.Game; import forge.game.GameEntity; @@ -420,6 +422,12 @@ public class PlayerControllerForTests extends PlayerController { 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); + } + private List chooseItems(Collection items, int amount) { if (items == null || items.isEmpty()) { @@ -571,4 +579,12 @@ public class PlayerControllerForTests extends PlayerController { // TODO Auto-generated method stub return ComputerUtilMana.payManaCost(player, sa); } + + @Override + public Map chooseCardsForConvoke(SpellAbility sa, ManaCost manaCost, + List untappedCreats) { + // TODO: AI to choose a creature to tap would go here + // Probably along with deciding how many creatures to tap + return new HashMap(); + } }