From 9d4a18f0c29e45eebe6884284e6a94894fa8861f Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Mon, 1 Apr 2013 09:56:12 +0000 Subject: [PATCH] Targeting code fixed to support fireballs and other spell with multiple targets. Some classes had references to ArrayList instead of list, this was also changed --- .gitattributes | 2 +- .../java/forge/card/ability/AbilityUtils.java | 2 +- .../java/forge/card/ability/ai/CounterAi.java | 6 +- .../forge/card/ability/ai/DamageDealAi.java | 9 +- .../java/forge/card/ability/ai/DrawAi.java | 4 +- .../ability/effects/ChangeZoneEffect.java | 8 +- .../effects/ControlExchangeEffect.java | 6 +- .../ability/effects/DestroyAllEffect.java | 2 +- .../cardfactory/CardFactoryCreatures.java | 1 - .../card/cardfactory/CardFactoryUtil.java | 2 +- .../java/forge/card/cost/CostTapType.java | 1 - .../card/replacement/ReplacementHandler.java | 2 +- .../forge/card/spellability/SpellAbility.java | 17 +- .../SpellAbilityRequirements.java | 68 +-- .../java/forge/card/spellability/Target.java | 6 +- .../card/spellability/TargetChoices.java | 13 +- ...argetSelection.java => TargetChooser.java} | 564 ++++++++---------- .../forge/card/trigger/TriggerHandler.java | 2 +- .../forge/control/input/InputMulligan.java | 4 +- src/main/java/forge/game/GameActionPlay.java | 23 +- src/main/java/forge/game/zone/MagicStack.java | 4 +- .../controllers/CEditorConstructed.java | 1 - 22 files changed, 326 insertions(+), 421 deletions(-) rename src/main/java/forge/card/spellability/{TargetSelection.java => TargetChooser.java} (53%) diff --git a/.gitattributes b/.gitattributes index 951e74cfb8d..a350675a54e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13766,7 +13766,7 @@ src/main/java/forge/card/spellability/SpellAbilityVariables.java svneol=native#t src/main/java/forge/card/spellability/SpellPermanent.java svneol=native#text/plain src/main/java/forge/card/spellability/Target.java svneol=native#text/plain src/main/java/forge/card/spellability/TargetChoices.java svneol=native#text/plain -src/main/java/forge/card/spellability/TargetSelection.java svneol=native#text/plain +src/main/java/forge/card/spellability/TargetChooser.java svneol=native#text/plain src/main/java/forge/card/spellability/package-info.java svneol=native#text/plain src/main/java/forge/card/staticability/StaticAbility.java svneol=native#text/plain src/main/java/forge/card/staticability/StaticAbilityCantAttackBlock.java -text diff --git a/src/main/java/forge/card/ability/AbilityUtils.java b/src/main/java/forge/card/ability/AbilityUtils.java index bb4071d6cbb..8db20cd141d 100644 --- a/src/main/java/forge/card/ability/AbilityUtils.java +++ b/src/main/java/forge/card/ability/AbilityUtils.java @@ -623,7 +623,7 @@ public class AbilityUtils { } else if (type.startsWith("Targeted")) { source = null; - ArrayList tgts = sa.findTargetedCards(); + List tgts = sa.findTargetedCards(); if (!tgts.isEmpty()) { source = tgts.get(0); } diff --git a/src/main/java/forge/card/ability/ai/CounterAi.java b/src/main/java/forge/card/ability/ai/CounterAi.java index 9e084635dfb..1600901e869 100644 --- a/src/main/java/forge/card/ability/ai/CounterAi.java +++ b/src/main/java/forge/card/ability/ai/CounterAi.java @@ -8,7 +8,7 @@ import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.Cost; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; -import forge.card.spellability.TargetSelection; +import forge.card.spellability.TargetChooser; import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilMana; @@ -50,7 +50,7 @@ public class CounterAi extends SpellAbilityAi { } tgt.resetTargets(); - if (TargetSelection.matchSpellAbility(sa, topSA, tgt)) { + if (TargetChooser.matchSpellAbility(sa, topSA, tgt)) { tgt.addTarget(topSA); } else { return false; @@ -120,7 +120,7 @@ public class CounterAi extends SpellAbilityAi { } tgt.resetTargets(); - if (TargetSelection.matchSpellAbility(sa, topSA, tgt)) { + if (TargetChooser.matchSpellAbility(sa, topSA, tgt)) { tgt.addTarget(topSA); } else { return false; diff --git a/src/main/java/forge/card/ability/ai/DamageDealAi.java b/src/main/java/forge/card/ability/ai/DamageDealAi.java index f554e23c259..b2f0ba01005 100644 --- a/src/main/java/forge/card/ability/ai/DamageDealAi.java +++ b/src/main/java/forge/card/ability/ai/DamageDealAi.java @@ -15,7 +15,7 @@ import forge.card.cost.Cost; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; -import forge.card.spellability.TargetSelection; +import forge.card.spellability.TargetChooser; import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCombat; @@ -103,8 +103,7 @@ public class DamageDealAi extends DamageAiBase { if (tgt != null && tgt.getTargetPlayers().isEmpty() && !sa.hasParam("DividedAsYouChoose")) { int actualPay = 0; final boolean noPrevention = sa.hasParam("NoPrevention"); - final ArrayList cards = tgt.getTargetCards(); - for (final Card c : cards) { + for (final Card c : tgt.getTargetCards()) { final int adjDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention); if ((adjDamage > actualPay) && (adjDamage <= dmg)) { actualPay = adjDamage; @@ -144,7 +143,7 @@ public class DamageDealAi extends DamageAiBase { final ArrayList objects = tgt.getTargets(); if (saMe.hasParam("TargetUnique")) { - objects.addAll(TargetSelection.getUniqueTargets(saMe)); + objects.addAll(TargetChooser.getUniqueTargets(saMe)); } for (final Object o : objects) { if (o instanceof Card) { @@ -472,7 +471,7 @@ public class DamageDealAi extends DamageAiBase { // If I can kill my target by paying less mana, do it int actualPay = 0; final boolean noPrevention = sa.hasParam("NoPrevention"); - final ArrayList cards = tgt.getTargetCards(); + final List cards = tgt.getTargetCards(); //target is a player if (cards.isEmpty()) { actualPay = dmg; diff --git a/src/main/java/forge/card/ability/ai/DrawAi.java b/src/main/java/forge/card/ability/ai/DrawAi.java index decde1c2179..d7cff15a780 100644 --- a/src/main/java/forge/card/ability/ai/DrawAi.java +++ b/src/main/java/forge/card/ability/ai/DrawAi.java @@ -18,7 +18,7 @@ */ package forge.card.ability.ai; -import java.util.ArrayList; +import java.util.List; import java.util.Random; import forge.Card; @@ -96,7 +96,7 @@ public class DrawAi extends SpellAbilityAi { } if (tgt != null) { - final ArrayList players = tgt.getTargetPlayers(); + final List players = tgt.getTargetPlayers(); if ((players.size() > 0) && players.get(0).isOpponentOf(ai)) { return true; } diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java index 96996ba07d1..fb0943e0caf 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java @@ -171,7 +171,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { final StringBuilder sbTargets = new StringBuilder(); - ArrayList tgts; + List tgts; if (sa.getTarget() != null) { tgts = sa.getTarget().getTargetCards(); } else { @@ -301,8 +301,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect { * a {@link forge.card.spellability.SpellAbility} object. */ private static void changeKnownOriginResolve(final SpellAbility sa) { - ArrayList tgtCards; - ArrayList sas; + List tgtCards; + List sas; final Target tgt = sa.getTarget(); final Player player = sa.getActivatingPlayer(); @@ -552,7 +552,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { final Target tgt = sa.getTarget(); if (tgt != null) { - final ArrayList players = tgt.getTargetPlayers(); + final List players = tgt.getTargetPlayers(); player = player != null ? player : players.get(0); if (players.contains(player) && !player.canBeTargetedBy(sa)) { return; diff --git a/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java b/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java index 5f63b87e90b..bca48c00ea9 100644 --- a/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java +++ b/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java @@ -1,6 +1,6 @@ package forge.card.ability.effects; -import java.util.ArrayList; +import java.util.List; import forge.Card; import forge.Singletons; @@ -21,7 +21,7 @@ public class ControlExchangeEffect extends SpellAbilityEffect { Card object1 = null; Card object2 = null; final Target tgt = sa.getTarget(); - ArrayList tgts = tgt.getTargetCards(); + List tgts = tgt.getTargetCards(); if (tgts.size() > 0) { object1 = tgts.get(0); } @@ -42,7 +42,7 @@ public class ControlExchangeEffect extends SpellAbilityEffect { Card object1 = null; Card object2 = null; final Target tgt = sa.getTarget(); - ArrayList tgts = tgt.getTargetCards(); + List tgts = tgt.getTargetCards(); if (tgts.size() > 0) { object1 = tgts.get(0); } diff --git a/src/main/java/forge/card/ability/effects/DestroyAllEffect.java b/src/main/java/forge/card/ability/effects/DestroyAllEffect.java index a184464791d..43297ef91c5 100644 --- a/src/main/java/forge/card/ability/effects/DestroyAllEffect.java +++ b/src/main/java/forge/card/ability/effects/DestroyAllEffect.java @@ -21,7 +21,7 @@ public class DestroyAllEffect extends SpellAbilityEffect { final StringBuilder sb = new StringBuilder(); final boolean noRegen = sa.hasParam("NoRegen"); - ArrayList tgtCards; + List tgtCards; final Target tgt = sa.getTarget(); if (tgt != null) { diff --git a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java index 5e2ca066ba2..6ed71f5c20c 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java @@ -34,7 +34,6 @@ import forge.CardPredicates.Presets; import forge.Command; import forge.CounterType; import forge.Singletons; -import forge.card.ability.AbilityFactory; import forge.card.cost.Cost; import forge.card.mana.ManaCost; import forge.card.spellability.Ability; diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index a5eff8cf730..3ded88f444a 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -2919,7 +2919,7 @@ public class CardFactoryUtil { } if (card.getController().isHuman()) { - game.getActionPlay().playSpellAbilityNoStack(card.getController(), origSA, false); + game.getActionPlay().playSpellAbilityNoStack(card.getController(), origSA); } else { ComputerUtil.playNoStack((AIPlayer) card.getController(), origSA, game); } diff --git a/src/main/java/forge/card/cost/CostTapType.java b/src/main/java/forge/card/cost/CostTapType.java index 7c1eb7e37df..b398e4ae07e 100644 --- a/src/main/java/forge/card/cost/CostTapType.java +++ b/src/main/java/forge/card/cost/CostTapType.java @@ -24,7 +24,6 @@ import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; -import forge.CardPredicates; import forge.CardPredicates.Presets; import forge.FThreads; import forge.card.ability.AbilityUtils; diff --git a/src/main/java/forge/card/replacement/ReplacementHandler.java b/src/main/java/forge/card/replacement/ReplacementHandler.java index 3fc979f6080..7da37539dbc 100644 --- a/src/main/java/forge/card/replacement/ReplacementHandler.java +++ b/src/main/java/forge/card/replacement/ReplacementHandler.java @@ -239,7 +239,7 @@ public class ReplacementHandler { Player player = replacementEffect.getHostCard().getController(); //player.getController().playNoStack() if (player.isHuman()) { - game.getActionPlay().playSpellAbilityNoStack(player, effectSA, false); + game.getActionPlay().playSpellAbilityNoStack(player, effectSA); } else { ComputerUtil.playNoStack((AIPlayer) player, effectSA, game); } diff --git a/src/main/java/forge/card/spellability/SpellAbility.java b/src/main/java/forge/card/spellability/SpellAbility.java index 9988f964f09..e3e27b0fe20 100644 --- a/src/main/java/forge/card/spellability/SpellAbility.java +++ b/src/main/java/forge/card/spellability/SpellAbility.java @@ -1053,7 +1053,7 @@ public abstract class SpellAbility implements ISpellAbility { if (this.targetCard == null) { final Target tgt = this.getTarget(); if (tgt != null) { - final ArrayList list = tgt.getTargetCards(); + final List list = tgt.getTargetCards(); if (!list.isEmpty()) { return list.get(0); @@ -1106,7 +1106,7 @@ public abstract class SpellAbility implements ISpellAbility { public Player getTargetPlayer() { final Target tgt = this.getTarget(); if (tgt != null) { - final ArrayList list = tgt.getTargetPlayers(); + final List list = tgt.getTargetPlayers(); if (!list.isEmpty()) { return list.get(0); @@ -1313,7 +1313,7 @@ public abstract class SpellAbility implements ISpellAbility { return false; } if (entity.isValid(this.getTarget().getValidTgts(), this.getActivatingPlayer(), this.getSourceCard()) - && (!this.getTarget().isUniqueTargets() || !TargetSelection.getUniqueTargets(this).contains(entity)) + && (!this.getTarget().isUniqueTargets() || !TargetChooser.getUniqueTargets(this).contains(entity)) && entity.canBeTargetedBy(this)) { return true; } @@ -1494,23 +1494,22 @@ public abstract class SpellAbility implements ISpellAbility { * * @return a {@link forge.card.spellability.SpellAbility} object. */ - public ArrayList findTargetedCards() { - - ArrayList list = new ArrayList(); + public List findTargetedCards() { Target tgt = this.getTarget(); // First search for targeted cards associated with current ability if (tgt != null && tgt.getTargetCards() != null && !tgt.getTargetCards().isEmpty()) { return tgt.getTargetCards(); } + List list = new ArrayList(); // Next search for source cards of targeted SAs associated with current ability - else if (tgt != null && tgt.getTargetSAs() != null && !tgt.getTargetSAs().isEmpty()) { + if (tgt != null && tgt.getTargetSAs() != null && !tgt.getTargetSAs().isEmpty()) { for (final SpellAbility ability : tgt.getTargetSAs()) { list.add(ability.getSourceCard()); } return list; } // Lastly Search parent SAs for target cards - else { + // Check for a parent that targets a card SpellAbility parent = this.getParentTargetingCard(); if (null != parent) { @@ -1523,7 +1522,7 @@ public abstract class SpellAbility implements ISpellAbility { list.add(ability.getSourceCard()); } } - } + return list; } diff --git a/src/main/java/forge/card/spellability/SpellAbilityRequirements.java b/src/main/java/forge/card/spellability/SpellAbilityRequirements.java index 731d81a1f8a..98e88306ea3 100644 --- a/src/main/java/forge/card/spellability/SpellAbilityRequirements.java +++ b/src/main/java/forge/card/spellability/SpellAbilityRequirements.java @@ -38,48 +38,36 @@ import forge.game.zone.Zone; * @version $Id$ */ public class SpellAbilityRequirements { - private SpellAbility ability = null; - private TargetSelection select = null; - private CostPayment payment = null; - private boolean isFree = false; + private final SpellAbility ability; + private final TargetChooser select; + private final CostPayment payment; + private boolean isFree; private boolean skipStack = false; - private boolean bCasting = false; - private Zone fromZone = null; - private Integer zonePosition = null; + private boolean isAlreadyTargeted = false; - - public final void setSkipStack(final boolean bSkip) { - this.skipStack = bSkip; - } - - public final void setFree(final boolean bFree) { - this.isFree = bFree; - } + public void setAlreadyTargeted() { isAlreadyTargeted = true; } + public final void setSkipStack() { this.skipStack = true; } + public void setFree() { this.isFree = true; } - - public SpellAbilityRequirements(final SpellAbility sa, final TargetSelection ts, final CostPayment cp) { + public SpellAbilityRequirements(final SpellAbility sa, final CostPayment cp) { this.ability = sa; - this.select = ts; + this.select = new TargetChooser(sa); this.payment = cp; } + public final void fillRequirements() { - this.fillRequirements(false); - } - - public final void fillRequirements(final boolean skipTargeting) { final GameState game = Singletons.getModel().getGame(); - - if ((this.ability instanceof Spell) && !this.bCasting) { - // remove from hand - this.bCasting = true; - if (!this.ability.getSourceCard().isCopiedSpell()) { - final Card c = this.ability.getSourceCard(); - this.fromZone = game.getZoneOf(c); - this.zonePosition = this.fromZone.getPosition(c); - this.ability.setSourceCard(game.getAction().moveToStack(c)); - } + // used to rollback + Zone fromZone = null; + int zonePosition = 0; + + final Card c = this.ability.getSourceCard(); + if (this.ability instanceof Spell && !c.isCopiedSpell()) { + fromZone = game.getZoneOf(c); + zonePosition = fromZone.getPosition(c); + this.ability.setSourceCard(game.getAction().moveToStack(c)); } // freeze Stack. No abilities should go onto the stack while I'm filling requirements. @@ -88,19 +76,19 @@ public class SpellAbilityRequirements { // Announce things like how many times you want to Multikick or the value of X if (!this.announceRequirements()) { this.select.setCancel(true); - rollbackAbility(); + rollbackAbility(fromZone, zonePosition); return; } // Skip to paying if parent ability doesn't target and has no // subAbilities. // (or trigger case where its already targeted) - if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) { - this.select.setRequirements(this); + boolean acceptsTargets = this.select.doesTarget() || this.ability.getSubAbility() != null; + if (!isAlreadyTargeted && acceptsTargets) { this.select.clearTargets(); this.select.chooseTargets(); if (this.select.isCanceled()) { - rollbackAbility(); + rollbackAbility(fromZone, zonePosition); return; } } @@ -114,7 +102,7 @@ public class SpellAbilityRequirements { } if (!paymentMade) { - rollbackAbility(); + rollbackAbility(fromZone, zonePosition); return; } @@ -134,7 +122,7 @@ public class SpellAbilityRequirements { } } - private void rollbackAbility() { + private void rollbackAbility(Zone fromZone, int zonePosition) { // cancel ability during target choosing final Card c = this.ability.getSourceCard(); @@ -143,9 +131,9 @@ public class SpellAbilityRequirements { c.setState(CardCharacteristicName.Original); } - if (this.bCasting && !c.isCopiedSpell()) { // and not a copy + if (fromZone != null) { // and not a copy // add back to where it came from - Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); + Singletons.getModel().getGame().getAction().moveTo(fromZone, c, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null); } if (this.select != null) { diff --git a/src/main/java/forge/card/spellability/Target.java b/src/main/java/forge/card/spellability/Target.java index c0b835643cd..99dc9117bbf 100644 --- a/src/main/java/forge/card/spellability/Target.java +++ b/src/main/java/forge/card/spellability/Target.java @@ -464,7 +464,7 @@ public class Target { * * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getTargetCards() { + public final List getTargetCards() { if (this.choice == null) { return new ArrayList(); } @@ -479,7 +479,7 @@ public class Target { * * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getTargetPlayers() { + public final List getTargetPlayers() { if (this.choice == null) { return new ArrayList(); } @@ -494,7 +494,7 @@ public class Target { * * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getTargetSAs() { + public final List getTargetSAs() { if (this.choice == null) { return new ArrayList(); } diff --git a/src/main/java/forge/card/spellability/TargetChoices.java b/src/main/java/forge/card/spellability/TargetChoices.java index 1167673b280..62e5f7ba9a3 100644 --- a/src/main/java/forge/card/spellability/TargetChoices.java +++ b/src/main/java/forge/card/spellability/TargetChoices.java @@ -18,6 +18,7 @@ package forge.card.spellability; import java.util.ArrayList; +import java.util.List; import forge.Card; import forge.game.player.Player; @@ -45,9 +46,9 @@ public class TargetChoices { } // Card or Player are legal targets. - private final ArrayList targetCards = new ArrayList(); - private final ArrayList targetPlayers = new ArrayList(); - private final ArrayList targetSAs = new ArrayList(); + private final List targetCards = new ArrayList(); + private final List targetPlayers = new ArrayList(); + private final List targetSAs = new ArrayList(); /** *

@@ -199,7 +200,7 @@ public class TargetChoices { * * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getTargetCards() { + public final List getTargetCards() { return this.targetCards; } @@ -210,7 +211,7 @@ public class TargetChoices { * * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getTargetPlayers() { + public final List getTargetPlayers() { return this.targetPlayers; } @@ -221,7 +222,7 @@ public class TargetChoices { * * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getTargetSAs() { + public final List getTargetSAs() { return this.targetSAs; } diff --git a/src/main/java/forge/card/spellability/TargetSelection.java b/src/main/java/forge/card/spellability/TargetChooser.java similarity index 53% rename from src/main/java/forge/card/spellability/TargetSelection.java rename to src/main/java/forge/card/spellability/TargetChooser.java index 4ef190d8fa2..9eab1736b11 100644 --- a/src/main/java/forge/card/spellability/TargetSelection.java +++ b/src/main/java/forge/card/spellability/TargetChooser.java @@ -20,20 +20,23 @@ package forge.card.spellability; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; import forge.FThreads; -import forge.Singletons; +import forge.GameEntity; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; -import forge.control.input.InputSynchronized; import forge.control.input.InputSyncronizedBase; +import forge.game.GameState; import forge.game.player.Player; +import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; /** @@ -44,21 +47,24 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class TargetSelection { +public class TargetChooser { /** * TODO: Write javadoc for this type. * */ - public final class InputSelectTargets extends InputSyncronizedBase { - private final TargetSelection select; + public static final class InputSelectTargets extends InputSyncronizedBase { private final List choices; - private final ArrayList alreadyTargeted; - private final boolean targeted; + // some cards can be targeted several times (eg: distribute damage as you choose) + private final Map targetDepth = new HashMap(); private final Target tgt; private final SpellAbility sa; + private boolean bCancel = false; + private boolean bOk = false; private final boolean mandatory; private static final long serialVersionUID = -1091595663541356356L; + public final boolean hasCancelled() { return bCancel; } + public final boolean hasPressedOk() { return bOk; } /** * TODO: Write javadoc for Constructor. * @param select @@ -70,13 +76,10 @@ public class TargetSelection { * @param sa * @param mandatory */ - public InputSelectTargets(TargetSelection select, List choices, ArrayList alreadyTargeted, boolean targeted, Target tgt, SpellAbility sa, boolean mandatory) { + public InputSelectTargets(List choices, SpellAbility sa, boolean mandatory) { super(sa.getActivatingPlayer()); - this.select = select; this.choices = choices; - this.alreadyTargeted = alreadyTargeted; - this.targeted = targeted; - this.tgt = tgt; + this.tgt = sa.getTarget(); this.sa = sa; this.mandatory = mandatory; } @@ -84,12 +87,14 @@ public class TargetSelection { @Override public void showMessage() { final StringBuilder sb = new StringBuilder(); - sb.append("Targeted: "); - for (final Object o : alreadyTargeted) { - sb.append(o).append(" "); + sb.append("Targeted:\n"); + for (final Entry o : targetDepth.entrySet()) { + sb.append(o.getKey()); + if( o.getValue() > 1 ) + sb.append(" (").append(o.getValue()).append(" times)"); + sb.append("\n"); } - sb.append(tgt.getTargetedString()); - sb.append("\n"); + //sb.append(tgt.getTargetedString()).append("\n"); sb.append(tgt.getVTSelection()); showMessage(sb.toString()); @@ -114,110 +119,147 @@ public class TargetSelection { @Override public void selectButtonCancel() { - select.setCancel(true); + bCancel = true; this.done(); } @Override public void selectButtonOK() { + bOk = true; this.done(); } @Override public void selectCard(final Card card) { - // leave this in temporarily, there some seriously wrong things - // going on here - if (targeted && !card.canBeTargetedBy(sa)) { - CMatchUI.SINGLETON_INSTANCE.showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); - } else if (choices.contains(card)) { - if (tgt.isDividedAsYouChoose()) { - final int stillToDivide = tgt.getStillToDivide(); - int allocatedPortion = 0; - // allow allocation only if the max targets isn't reached and there are more candidates - if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) - && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { - final Integer[] choices = new Integer[stillToDivide]; - for (int i = 1; i <= stillToDivide; i++) { - choices[i - 1] = i; - } - String apiBasedMessage = "Distribute how much to "; - if (sa.getApi() == ApiType.DealDamage) { - apiBasedMessage = "Select how much damage to deal to "; - } else if (sa.getApi() == ApiType.PreventDamage) { - apiBasedMessage = "Select how much damage to prevent to "; - } else if (sa.getApi() == ApiType.PutCounter) { - apiBasedMessage = "Select how many counters to distribute to "; - } - final StringBuilder sb = new StringBuilder(); - sb.append(apiBasedMessage); - sb.append(card.toString()); - Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); - if (null == chosen) { - return; - } - allocatedPortion = chosen; - } else { // otherwise assign the rest of the damage/protection - allocatedPortion = stillToDivide; - } - tgt.setStillToDivide(stillToDivide - allocatedPortion); - tgt.addDividedAllocation(card, allocatedPortion); - } - tgt.addTarget(card); - this.done(); + if (!tgt.isUniqueTargets() && targetDepth.containsKey(card)) { + return; } + + // leave this in temporarily, there some seriously wrong things going on here + if (!card.canBeTargetedBy(sa)) { + showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); + return; + } + if (!choices.contains(card)) { + showMessage("This card is not a valid choice for some other reason besides (Shroud? Protection? Restrictions?)."); + return; + } + + if (tgt.isDividedAsYouChoose()) { + final int stillToDivide = tgt.getStillToDivide(); + int allocatedPortion = 0; + // allow allocation only if the max targets isn't reached and there are more candidates + if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) + && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { + final Integer[] choices = new Integer[stillToDivide]; + for (int i = 1; i <= stillToDivide; i++) { + choices[i - 1] = i; + } + String apiBasedMessage = "Distribute how much to "; + if (sa.getApi() == ApiType.DealDamage) { + apiBasedMessage = "Select how much damage to deal to "; + } else if (sa.getApi() == ApiType.PreventDamage) { + apiBasedMessage = "Select how much damage to prevent to "; + } else if (sa.getApi() == ApiType.PutCounter) { + apiBasedMessage = "Select how many counters to distribute to "; + } + final StringBuilder sb = new StringBuilder(); + sb.append(apiBasedMessage); + sb.append(card.toString()); + Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); + if (null == chosen) { + return; + } + allocatedPortion = chosen; + } else { // otherwise assign the rest of the damage/protection + allocatedPortion = stillToDivide; + } + tgt.setStillToDivide(stillToDivide - allocatedPortion); + tgt.addDividedAllocation(card, allocatedPortion); + } + addTarget(card); } // selectCard() @Override public void selectPlayer(final Player player) { - if (alreadyTargeted.contains(player)) { + if (!tgt.isUniqueTargets() && targetDepth.containsKey(player)) { return; } - if (sa.canTarget(player)) { - if (tgt.isDividedAsYouChoose()) { - final int stillToDivide = tgt.getStillToDivide(); - int allocatedPortion = 0; - // allow allocation only if the max targets isn't reached and there are more candidates - if ((alreadyTargeted.size() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) - && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { - final Integer[] choices = new Integer[stillToDivide]; - for (int i = 1; i <= stillToDivide; i++) { - choices[i - 1] = i; - } - String apiBasedMessage = "Distribute how much to "; - if (sa.getApi() == ApiType.DealDamage) { - apiBasedMessage = "Select how much damage to deal to "; - } else if (sa.getApi() == ApiType.PreventDamage) { - apiBasedMessage = "Select how much damage to prevent to "; - } - final StringBuilder sb = new StringBuilder(); - sb.append(apiBasedMessage); - sb.append(player.getName()); - Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); - if (null == chosen) { - return; - } - allocatedPortion = chosen; - } else { // otherwise assign the rest of the damage/protection - allocatedPortion = stillToDivide; - } - tgt.setStillToDivide(stillToDivide - allocatedPortion); - tgt.addDividedAllocation(player, allocatedPortion); - } - tgt.addTarget(player); - this.done(); + if (!sa.canTarget(player)) { + showMessage("Cannot target this player (Hexproof? Protection? Restrictions?)."); + return; } + + if (tgt.isDividedAsYouChoose()) { + final int stillToDivide = tgt.getStillToDivide(); + int allocatedPortion = 0; + // allow allocation only if the max targets isn't reached and there are more candidates + if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { + final Integer[] choices = new Integer[stillToDivide]; + for (int i = 1; i <= stillToDivide; i++) { + choices[i - 1] = i; + } + String apiBasedMessage = "Distribute how much to "; + if (sa.getApi() == ApiType.DealDamage) { + apiBasedMessage = "Select how much damage to deal to "; + } else if (sa.getApi() == ApiType.PreventDamage) { + apiBasedMessage = "Select how much damage to prevent to "; + } + final StringBuilder sb = new StringBuilder(); + sb.append(apiBasedMessage); + sb.append(player.getName()); + Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); + if (null == chosen) { + return; + } + allocatedPortion = chosen; + } else { // otherwise assign the rest of the damage/protection + allocatedPortion = stillToDivide; + } + tgt.setStillToDivide(stillToDivide - allocatedPortion); + tgt.addDividedAllocation(player, allocatedPortion); + } + addTarget(player); } + void addTarget(GameEntity ge) { + tgt.addTarget(ge); + Integer val = targetDepth.get(ge); + targetDepth.put(ge, val == null ? Integer.valueOf(1) : Integer.valueOf(val.intValue() + 1) ); + + if(hasAllTargets()) { + bOk = true; + this.done(); + } + else + this.showMessage(); + } + void done() { this.stop(); } + + boolean hasAllTargets() { + return tgt.isMaxTargetsChosen(sa.getSourceCard(), sa); + } } - private final Target target; private final SpellAbility ability; - private TargetSelection subSelection = null; + /** + *

+ * Constructor for Target_Selection. + *

+ * + * @param tgt + * a {@link forge.card.spellability.Target} object. + * @param sa + * a {@link forge.card.spellability.SpellAbility} object. + */ + public TargetChooser(final SpellAbility sa) { + this.ability = sa; + } /** *

@@ -227,7 +269,7 @@ public class TargetSelection { * @return a {@link forge.card.spellability.Target} object. */ public final Target getTgt() { - return this.target; + return this.ability.getTarget(); } /** @@ -252,20 +294,7 @@ public class TargetSelection { return this.ability.getSourceCard(); } - private SpellAbilityRequirements req = null; - - /** - *

- * setRequirements. - *

- * - * @param reqs - * a {@link forge.card.spellability.SpellAbilityRequirements} - * object. - */ - public final void setRequirements(final SpellAbilityRequirements reqs) { - this.req = reqs; - } + private TargetChooser subSelection = null; private boolean bCancel = false; private boolean bTargetingDone = false; @@ -290,44 +319,12 @@ public class TargetSelection { * @return a boolean. */ public final boolean isCanceled() { - if (this.bCancel) { - return this.bCancel; - } - - if (this.subSelection == null) { - return false; - } - - return this.subSelection.isCanceled(); + return this.bCancel || this.subSelection != null && this.subSelection.isCanceled(); } - /** - *

- * Constructor for Target_Selection. - *

- * - * @param tgt - * a {@link forge.card.spellability.Target} object. - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - */ - public TargetSelection(final Target tgt, final SpellAbility sa) { - this.target = tgt; - this.ability = sa; - } - - /** - *

- * doesTarget. - *

- * - * @return a boolean. - */ public final boolean doesTarget() { - if (this.target == null) { - return false; - } - return this.target.doesTarget(); + Target tg = getTgt(); + return tg != null && tg.doesTarget(); } /** @@ -336,55 +333,65 @@ public class TargetSelection { *

*/ public final void clearTargets() { - if (this.target != null) { - this.target.resetTargets(); - this.target.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability); + Target tg = getTgt(); + if (tg != null) { + tg.resetTargets(); + tg.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability); } } - /** - *

- * chooseTargets. - *

- * - * @return a boolean. - */ public final boolean chooseTargets() { + Target tgt = getTgt(); + final boolean canTarget = tgt.doesTarget(); + final int minTargets = canTarget ? tgt.getMinTargets(getCard(), ability) : 0; + final int maxTargets = canTarget ? tgt.getMaxTargets(getCard(), ability) : 0; + final int numTargeted = canTarget ? tgt.getNumTargeted() : 0; + + boolean hasEnoughTargets = minTargets == 0 || numTargeted >= minTargets; + boolean hasAllTargets = numTargeted == maxTargets && maxTargets > 0; + // if not enough targets chosen, reset and cancel Ability - if (this.bCancel || (this.bTargetingDone && !this.target.isMinTargetsChosen(this.getCard(), this.ability))) { - this.bCancel = true; - return false; - } + if (this.bTargetingDone && !hasEnoughTargets) this.bCancel = true; + if (this.bCancel) return false; + - if (!this.doesTarget() - || this.bTargetingDone && this.target.isMinTargetsChosen(this.getCard(), this.ability) - || this.target.isMaxTargetsChosen(this.getCard(), this.ability) - || this.target.isDividedAsYouChoose() && this.target.getStillToDivide() == 0) { + if (!canTarget || this.bTargetingDone && hasEnoughTargets || hasAllTargets || tgt.isDividedAsYouChoose() && tgt.getStillToDivide() == 0) { final AbilitySub abSub = this.ability.getSubAbility(); - - if (abSub == null) { - // if no more SubAbilities finish targeting + if (abSub == null) // if no more SubAbilities finish targeting return true; - } else { - // Has Sub Ability - this.subSelection = new TargetSelection(abSub.getTarget(), abSub); - this.subSelection.setRequirements(this.req); - this.subSelection.clearTargets(); - return this.subSelection.chooseTargets(); - } + + // Has Sub Ability + this.subSelection = new TargetChooser(abSub); + this.subSelection.clearTargets(); + return this.subSelection.chooseTargets(); } - if (!this.target.hasCandidates(this.ability, true) && !this.target.isMinTargetsChosen(this.getCard(), this.ability)) { + if (!tgt.hasCandidates(this.ability, true) && !hasEnoughTargets) { // Cancel ability if there aren't any valid Candidates this.bCancel = true; return false; } - - this.chooseValidInput(); - if ( !bCancel ) - return chooseTargets(); - - return false; + + final List zone = tgt.getZone(); + final boolean mandatory = tgt.getMandatory() && tgt.hasCandidates(this.ability, true); + + if (zone.size() == 1 && zone.get(0) == ZoneType.Stack) { + // If Zone is Stack, the choices are handled slightly differently + this.chooseCardFromStack(mandatory); + } else { + List validTargets = this.chooseValidInput(); + if (zone.size() == 1 && zone.get(0) == ZoneType.Battlefield) { + InputSelectTargets inp = new InputSelectTargets(validTargets, ability, mandatory); + FThreads.setInputAndWait(inp); + bCancel = inp.hasCancelled(); + bTargetingDone = inp.hasPressedOk(); + } else { + this.chooseCardFromList(validTargets, true, mandatory); + } + } + // some inputs choose cards 1-by-1 and need to be called again, + // moreover there are sub-abilities that also need targets + return chooseTargets(); } /** @@ -416,21 +423,15 @@ public class TargetSelection { *

* chooseValidInput. *

+ * @return */ - public final void chooseValidInput() { + public final List chooseValidInput() { final Target tgt = this.getTgt(); + final GameState game = ability.getActivatingPlayer().getGame(); final List zone = tgt.getZone(); - final boolean mandatory = this.target.getMandatory() ? this.target.hasCandidates(this.ability, true) : false; final boolean canTgtStack = zone.contains(ZoneType.Stack); - - if (canTgtStack && (zone.size() == 1)) { - // If Zone is Stack, the choices are handled slightly differently - this.chooseCardFromStack(mandatory); - return; - } - - List choices = CardLists.getTargetableCards(CardLists.getValidCards(Singletons.getModel().getGame().getCardsIn(zone), this.target.getValidTgts(), this.ability.getActivatingPlayer(), this.ability.getSourceCard()), this.ability); + List choices = CardLists.getTargetableCards(CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), this.ability.getActivatingPlayer(), this.ability.getSourceCard()), this.ability); if (canTgtStack) { // Since getTargetableCards doesn't have additional checks if one of the Zones is stack // Remove the activating card from targeting itself if its on the Stack @@ -450,7 +451,7 @@ public class TargetSelection { } // Remove cards already targeted - final ArrayList targeted = tgt.getTargetCards(); + final List targeted = tgt.getTargetCards(); for (final Card c : targeted) { if (choices.contains(c)) { choices.remove(c); @@ -486,7 +487,7 @@ public class TargetSelection { } // If all cards must have different controllers if (tgt.isDifferentControllers() && !targeted.isEmpty()) { - final List availableControllers = new ArrayList(Singletons.getModel().getGame().getPlayers()); + final List availableControllers = new ArrayList(game.getPlayers()); for (int i = 0; i < targeted.size(); i++) { availableControllers.remove(targeted.get(i).getController()); } @@ -512,20 +513,7 @@ public class TargetSelection { choices.clear(); } } - - if (!tgt.isUniqueTargets()) { - // Previously targeted objects needed to be used for same controller above, but causes problems - // if passed through with certain card functionality to inputTargetSpecific so resetting now - objects = new ArrayList(); - } - - if (zone.contains(ZoneType.Battlefield) && zone.size() == 1) { - InputSynchronized inp = new InputSelectTargets(this, choices, objects, true, this.target, this.ability, mandatory); - FThreads.setInputAndWait(inp); - bTargetingDone = !bCancel; - } else { - this.chooseCardFromList(choices, true, mandatory); - } + return choices; } // input_targetValid /** @@ -540,92 +528,63 @@ public class TargetSelection { * @param mandatory * a boolean. */ - public final void chooseCardFromList(final List choices, final boolean targeted, final boolean mandatory) { + private final void chooseCardFromList(final List choices, final boolean targeted, final boolean mandatory) { // Send in a list of valid cards, and popup a choice box to target - final Card dummy = new Card(); - dummy.setName("[FINISH TARGETING]"); - final SpellAbility sa = this.ability; - final String message = this.target.getVTSelection(); + final GameState game = ability.getActivatingPlayer().getGame(); - final Card divBattlefield = new Card(); - divBattlefield.setName("--CARDS ON BATTLEFIELD:--"); - final Card divExile = new Card(); - divExile.setName("--CARDS IN EXILE:--"); - final Card divGrave = new Card(); - divGrave.setName("--CARDS IN GRAVEYARD:--"); - final Card divLibrary = new Card(); - divLibrary.setName("--CARDS IN LIBRARY:--"); - final Card divStack = new Card(); - divStack.setName("--CARDS IN STACK:--"); - - List choicesZoneUnfiltered = choices; final List crdsBattle = new ArrayList(); final List crdsExile = new ArrayList(); final List crdsGrave = new ArrayList(); final List crdsLibrary = new ArrayList(); final List crdsStack = new ArrayList(); - for (final Card inZone : choicesZoneUnfiltered) { - if (Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield).contains(inZone)) { - crdsBattle.add(inZone); - } else if (Singletons.getModel().getGame().getCardsIn(ZoneType.Exile).contains(inZone)) { - crdsExile.add(inZone); - } else if (Singletons.getModel().getGame().getCardsIn(ZoneType.Graveyard).contains(inZone)) { - crdsGrave.add(inZone); - } else if (Singletons.getModel().getGame().getCardsIn(ZoneType.Library).contains(inZone)) { - crdsLibrary.add(inZone); - } else if (Singletons.getModel().getGame().getCardsIn(ZoneType.Stack).contains(inZone)) { - crdsStack.add(inZone); - } + for (final Card inZone : choices) { + Zone zz = game.getZoneOf(inZone); + if (zz.is(ZoneType.Battlefield)) crdsBattle.add(inZone); + else if (zz.is(ZoneType.Exile)) crdsExile.add(inZone); + else if (zz.is(ZoneType.Graveyard)) crdsGrave.add(inZone); + else if (zz.is(ZoneType.Library)) crdsLibrary.add(inZone); + else if (zz.is(ZoneType.Stack)) crdsStack.add(inZone); } - List choicesFiltered = new ArrayList(); - if (crdsBattle.size() >= 1) { - choicesFiltered.add(divBattlefield); + List choicesFiltered = new ArrayList(); + if (!crdsBattle.isEmpty()) { + choicesFiltered.add("--CARDS ON BATTLEFIELD:--"); choicesFiltered.addAll(crdsBattle); - crdsBattle.clear(); } - if (crdsExile.size() >= 1) { - choicesFiltered.add(divExile); + if (!crdsExile.isEmpty()) { + choicesFiltered.add("--CARDS IN EXILE:--"); choicesFiltered.addAll(crdsExile); - crdsExile.clear(); } - if (crdsGrave.size() >= 1) { - choicesFiltered.add(divGrave); + if (!crdsGrave.isEmpty()) { + choicesFiltered.add("--CARDS IN GRAVEYARD:--"); choicesFiltered.addAll(crdsGrave); - crdsGrave.clear(); } - if (crdsLibrary.size() >= 1) { - choicesFiltered.add(divLibrary); + if (!crdsLibrary.isEmpty()) { + choicesFiltered.add("--CARDS IN LIBRARY:--"); choicesFiltered.addAll(crdsLibrary); - crdsLibrary.clear(); } - if (crdsStack.size() >= 1) { - choicesFiltered.add(divStack); + if (!crdsStack.isEmpty()) { + choicesFiltered.add("--CARDS IN STACK:--"); choicesFiltered.addAll(crdsStack); - crdsStack.clear(); } - final Target tgt = this.getTgt(); - - final List choicesWithDone = choicesFiltered; - if (tgt.isMinTargetsChosen(sa.getSourceCard(), sa)) { + final String msgDone = "[FINISH TARGETING]"; + if (this.getTgt().isMinTargetsChosen(this.ability.getSourceCard(), this.ability)) { // is there a more elegant way of doing this? - choicesWithDone.add(dummy); + choicesFiltered.add(msgDone); } - - final Card check = GuiChoose.oneOrNone(message, choicesWithDone); - if (check != null) { - final Card c = check; - if (!c.equals(divBattlefield) && !c.equals(divExile) && !c.equals(divGrave) - && !c.equals(divLibrary) && !c.equals(divStack)) { - if (c.equals(dummy)) { - bTargetingDone = true; - } else { - tgt.addTarget(c); - } - } - } else { + + final Object chosen = GuiChoose.oneOrNone(getTgt().getVTSelection(), choicesFiltered); + if (chosen == null) { this.setCancel(true); + return; } + if (msgDone.equals(chosen)) { + bTargetingDone = true; + return; + } + + if (chosen instanceof Card ) + this.getTgt().addTarget(chosen); } /** @@ -636,14 +595,13 @@ public class TargetSelection { * @param mandatory * a boolean. */ - public final void chooseCardFromStack(final boolean mandatory) { - final Target tgt = this.target; + private final void chooseCardFromStack(final boolean mandatory) { + final Target tgt = this.getTgt(); final String message = tgt.getVTSelection(); - final TargetSelection select = this; final String doneDummy = "[FINISH TARGETING]"; // Find what's targetable, then allow human to choose - final ArrayList choosables = TargetSelection.getTargetableOnStack(this.ability, select.getTgt()); + final ArrayList choosables = getTargetableOnStack(); final HashMap map = new HashMap(); @@ -657,14 +615,11 @@ public class TargetSelection { map.put(doneDummy, null); } - String[] choices = new String[map.keySet().size()]; - choices = map.keySet().toArray(choices); - if (choices.length == 0) { - select.setCancel(true); + if (map.isEmpty()) { + setCancel(true); } else { - final String madeChoice = GuiChoose.oneOrNone(message, choices); - + final String madeChoice = GuiChoose.oneOrNone(message, map.keySet()); if (madeChoice != null) { if (madeChoice.equals(doneDummy)) { bTargetingDone = true; @@ -672,7 +627,7 @@ public class TargetSelection { tgt.addTarget(map.get(madeChoice)); } } else { - select.setCancel(true); + setCancel(true); } } } @@ -691,15 +646,16 @@ public class TargetSelection { * a {@link forge.card.spellability.Target} object. * @return a {@link java.util.ArrayList} object. */ - public static ArrayList getTargetableOnStack(final SpellAbility sa, final Target tgt) { + private ArrayList getTargetableOnStack() { final ArrayList choosables = new ArrayList(); - for (int i = 0; i < Singletons.getModel().getGame().getStack().size(); i++) { - choosables.add(Singletons.getModel().getGame().getStack().peekAbility(i)); + final GameState game = ability.getActivatingPlayer().getGame(); + for (int i = 0; i < game.getStack().size(); i++) { + choosables.add(game.getStack().peekAbility(i)); } for (int i = 0; i < choosables.size(); i++) { - if (!TargetSelection.matchSpellAbility(sa, choosables.get(i), tgt)) { + if (!TargetChooser.matchSpellAbility(ability, choosables.get(i), getTgt())) { choosables.remove(i); } } @@ -753,7 +709,7 @@ public class TargetSelection { boolean result = false; for (final Object o : matchTgt.getTargets()) { - if (TargetSelection.matchesValid(o, splitTargetRestrictions.split(","), sa)) { + if (TargetChooser.matchesValid(o, splitTargetRestrictions.split(","), sa)) { result = true; break; } @@ -764,26 +720,10 @@ public class TargetSelection { } } - if (!TargetSelection.matchesValidSA(topSA, tgt.getValidTgts(), sa)) { - return false; - } - - return true; + return topSA.getSourceCard().isValid(tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard()); } - /** - *

- * matchesValid. - *

- * - * @param o - * a {@link java.lang.Object} object. - * @param valids - * an array of {@link java.lang.String} objects. - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @return a boolean. - */ + private static boolean matchesValid(final Object o, final String[] valids, final SpellAbility sa) { final Card srcCard = sa.getSourceCard(); final Player activatingPlayer = sa.getActivatingPlayer(); @@ -801,24 +741,4 @@ public class TargetSelection { return false; } - - /** - *

- * matchesValidSA. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param valids - * an array of {@link java.lang.String} objects. - * @param source - * a {@link forge.card.spellability.SpellAbility} object. - * @return a boolean. - */ - private static boolean matchesValidSA(final SpellAbility sa, final String[] valids, final SpellAbility source) { - final Card srcCard = source.getSourceCard(); - final Player activatingPlayer = source.getActivatingPlayer(); - final Card c = sa.getSourceCard(); - return c.isValid(valids, activatingPlayer, srcCard); - } } diff --git a/src/main/java/forge/card/trigger/TriggerHandler.java b/src/main/java/forge/card/trigger/TriggerHandler.java index 1fc800f7e29..ed81f1b4009 100644 --- a/src/main/java/forge/card/trigger/TriggerHandler.java +++ b/src/main/java/forge/card/trigger/TriggerHandler.java @@ -419,7 +419,7 @@ public class TriggerHandler { if (regtrig.isStatic()) { if (wrapperAbility.getActivatingPlayer().isHuman()) { - game.getActionPlay().playSpellAbilityNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility, false); + game.getActionPlay().playSpellAbilityNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility); } else { wrapperAbility.doTrigger(isMandatory, (AIPlayer)wrapperAbility.getActivatingPlayer()); ComputerUtil.playNoStack((AIPlayer)wrapperAbility.getActivatingPlayer(), wrapperAbility, game); diff --git a/src/main/java/forge/control/input/InputMulligan.java b/src/main/java/forge/control/input/InputMulligan.java index be6961b84b8..45be101462c 100644 --- a/src/main/java/forge/control/input/InputMulligan.java +++ b/src/main/java/forge/control/input/InputMulligan.java @@ -78,7 +78,7 @@ public class InputMulligan extends InputBase { } sb.append("Do you want to Mulligan?"); - CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); + showMessage(sb.toString()); } /** {@inheritDoc} */ @@ -130,7 +130,7 @@ public class InputMulligan extends InputBase { if (GuiDialog.confirm(c, "Use " + c +"'s ability?")) { // If we ever let the AI memorize cards in the players // hand, this would be a place to do so. - game.getActionPlay().playSpellAbilityNoStack(p, effect, false); + game.getActionPlay().playSpellAbilityNoStack(p, effect); } } } diff --git a/src/main/java/forge/game/GameActionPlay.java b/src/main/java/forge/game/GameActionPlay.java index f44b02dff81..d822d19a906 100644 --- a/src/main/java/forge/game/GameActionPlay.java +++ b/src/main/java/forge/game/GameActionPlay.java @@ -21,7 +21,6 @@ import forge.card.mana.ManaCostShard; import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbilityRequirements; import forge.card.spellability.Target; -import forge.card.spellability.TargetSelection; import forge.card.staticability.StaticAbility; import forge.control.input.InputPayManaSimple; import forge.game.ai.ComputerUtilCard; @@ -73,11 +72,10 @@ public class GameActionPlay { if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) { CharmEffect.makeChoices(sa); } - final TargetSelection ts = new TargetSelection(sa.getTarget(), sa); final CostPayment payment = new CostPayment(sa.getPayCosts(), sa); - final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, ts, payment); - req.setFree(true); + final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, payment); + req.setFree(); req.fillRequirements(); } else { if (sa.isSpell()) { @@ -383,7 +381,6 @@ public class GameActionPlay { // System.out.println("Playing:" + sa.getDescription() + " of " + sa.getSourceCard() + " new = " + newAbility); if (newAbility) { - final TargetSelection ts = new TargetSelection(sa.getTarget(), sa); CostPayment payment = null; if (sa.getPayCosts() == null) { payment = new CostPayment(new Cost(sa.getSourceCard(), "0", sa.isAbility()), sa); @@ -391,7 +388,7 @@ public class GameActionPlay { payment = new CostPayment(sa.getPayCosts(), sa); } - final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, ts, payment); + final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, payment); req.fillRequirements(); } else { ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost()); @@ -426,20 +423,24 @@ public class GameActionPlay { * @param skipTargeting * a boolean. */ - public final void playSpellAbilityNoStack(final Player human, final SpellAbility sa, final boolean skipTargeting) { + public final void playSpellAbilityNoStack(final Player human, final SpellAbility sa) { + playSpellAbilityNoStack(human, sa, false); + } + public final void playSpellAbilityNoStack(final Player human, final SpellAbility sa, boolean useOldTargets) { sa.setActivatingPlayer(human); if (sa.getPayCosts() != null) { - final TargetSelection ts = new TargetSelection(sa.getTarget(), sa); final CostPayment payment = new CostPayment(sa.getPayCosts(), sa); if (!sa.isTrigger()) { payment.changeCost(); } - final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, ts, payment); - req.setSkipStack(true); - req.fillRequirements(skipTargeting); + final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, payment); + if( useOldTargets ) + req.setAlreadyTargeted(); + req.setSkipStack(); + req.fillRequirements(); } else { ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost()); if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) { diff --git a/src/main/java/forge/game/zone/MagicStack.java b/src/main/java/forge/game/zone/MagicStack.java index c22e38500dc..6dbd0b6ba8e 100644 --- a/src/main/java/forge/game/zone/MagicStack.java +++ b/src/main/java/forge/game/zone/MagicStack.java @@ -42,7 +42,7 @@ import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbilityStackInstance; import forge.card.spellability.Target; import forge.card.spellability.TargetChoices; -import forge.card.spellability.TargetSelection; +import forge.card.spellability.TargetChooser; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerType; import forge.control.input.InputPayManaExecuteCommands; @@ -885,7 +885,7 @@ public class MagicStack extends MyObservable { } else if (o instanceof SpellAbility) { final SpellAbility tgtSA = (SpellAbility) o; - invalidTarget = !(TargetSelection.matchSpellAbility(sa, tgtSA, tgt)); + invalidTarget = !(TargetChooser.matchSpellAbility(sa, tgtSA, tgt)); // TODO Remove target? if (invalidTarget) { choices.removeTarget(tgtSA); diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java index 0e001fade13..aecb26d2dc4 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorConstructed.java @@ -205,7 +205,6 @@ public final class CEditorConstructed extends ACEditorBase { /** * Switch between the main deck and the sideboard editor. */ - @SuppressWarnings("incomplete-switch") public void cycleEditorMode() { int curindex = allSections.indexOf(sectionMode);