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
This commit is contained in:
Maxmtg
2013-04-01 09:56:12 +00:00
parent 41f77ba745
commit 9d4a18f0c2
22 changed files with 326 additions and 421 deletions

2
.gitattributes vendored
View File

@@ -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/SpellPermanent.java svneol=native#text/plain
src/main/java/forge/card/spellability/Target.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/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/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/StaticAbility.java svneol=native#text/plain
src/main/java/forge/card/staticability/StaticAbilityCantAttackBlock.java -text src/main/java/forge/card/staticability/StaticAbilityCantAttackBlock.java -text

View File

@@ -623,7 +623,7 @@ public class AbilityUtils {
} else if (type.startsWith("Targeted")) { } else if (type.startsWith("Targeted")) {
source = null; source = null;
ArrayList<Card> tgts = sa.findTargetedCards(); List<Card> tgts = sa.findTargetedCards();
if (!tgts.isEmpty()) { if (!tgts.isEmpty()) {
source = tgts.get(0); source = tgts.get(0);
} }

View File

@@ -8,7 +8,7 @@ import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection; import forge.card.spellability.TargetChooser;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana; import forge.game.ai.ComputerUtilMana;
@@ -50,7 +50,7 @@ public class CounterAi extends SpellAbilityAi {
} }
tgt.resetTargets(); tgt.resetTargets();
if (TargetSelection.matchSpellAbility(sa, topSA, tgt)) { if (TargetChooser.matchSpellAbility(sa, topSA, tgt)) {
tgt.addTarget(topSA); tgt.addTarget(topSA);
} else { } else {
return false; return false;
@@ -120,7 +120,7 @@ public class CounterAi extends SpellAbilityAi {
} }
tgt.resetTargets(); tgt.resetTargets();
if (TargetSelection.matchSpellAbility(sa, topSA, tgt)) { if (TargetChooser.matchSpellAbility(sa, topSA, tgt)) {
tgt.addTarget(topSA); tgt.addTarget(topSA);
} else { } else {
return false; return false;

View File

@@ -15,7 +15,7 @@ import forge.card.cost.Cost;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection; import forge.card.spellability.TargetChooser;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat; import forge.game.ai.ComputerUtilCombat;
@@ -103,8 +103,7 @@ public class DamageDealAi extends DamageAiBase {
if (tgt != null && tgt.getTargetPlayers().isEmpty() && !sa.hasParam("DividedAsYouChoose")) { if (tgt != null && tgt.getTargetPlayers().isEmpty() && !sa.hasParam("DividedAsYouChoose")) {
int actualPay = 0; int actualPay = 0;
final boolean noPrevention = sa.hasParam("NoPrevention"); final boolean noPrevention = sa.hasParam("NoPrevention");
final ArrayList<Card> cards = tgt.getTargetCards(); for (final Card c : tgt.getTargetCards()) {
for (final Card c : cards) {
final int adjDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention); final int adjDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
if ((adjDamage > actualPay) && (adjDamage <= dmg)) { if ((adjDamage > actualPay) && (adjDamage <= dmg)) {
actualPay = adjDamage; actualPay = adjDamage;
@@ -144,7 +143,7 @@ public class DamageDealAi extends DamageAiBase {
final ArrayList<Object> objects = tgt.getTargets(); final ArrayList<Object> objects = tgt.getTargets();
if (saMe.hasParam("TargetUnique")) { if (saMe.hasParam("TargetUnique")) {
objects.addAll(TargetSelection.getUniqueTargets(saMe)); objects.addAll(TargetChooser.getUniqueTargets(saMe));
} }
for (final Object o : objects) { for (final Object o : objects) {
if (o instanceof Card) { 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 // If I can kill my target by paying less mana, do it
int actualPay = 0; int actualPay = 0;
final boolean noPrevention = sa.hasParam("NoPrevention"); final boolean noPrevention = sa.hasParam("NoPrevention");
final ArrayList<Card> cards = tgt.getTargetCards(); final List<Card> cards = tgt.getTargetCards();
//target is a player //target is a player
if (cards.isEmpty()) { if (cards.isEmpty()) {
actualPay = dmg; actualPay = dmg;

View File

@@ -18,7 +18,7 @@
*/ */
package forge.card.ability.ai; package forge.card.ability.ai;
import java.util.ArrayList; import java.util.List;
import java.util.Random; import java.util.Random;
import forge.Card; import forge.Card;
@@ -96,7 +96,7 @@ public class DrawAi extends SpellAbilityAi {
} }
if (tgt != null) { if (tgt != null) {
final ArrayList<Player> players = tgt.getTargetPlayers(); final List<Player> players = tgt.getTargetPlayers();
if ((players.size() > 0) && players.get(0).isOpponentOf(ai)) { if ((players.size() > 0) && players.get(0).isOpponentOf(ai)) {
return true; return true;
} }

View File

@@ -171,7 +171,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
final StringBuilder sbTargets = new StringBuilder(); final StringBuilder sbTargets = new StringBuilder();
ArrayList<Card> tgts; List<Card> tgts;
if (sa.getTarget() != null) { if (sa.getTarget() != null) {
tgts = sa.getTarget().getTargetCards(); tgts = sa.getTarget().getTargetCards();
} else { } else {
@@ -301,8 +301,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
private static void changeKnownOriginResolve(final SpellAbility sa) { private static void changeKnownOriginResolve(final SpellAbility sa) {
ArrayList<Card> tgtCards; List<Card> tgtCards;
ArrayList<SpellAbility> sas; List<SpellAbility> sas;
final Target tgt = sa.getTarget(); final Target tgt = sa.getTarget();
final Player player = sa.getActivatingPlayer(); final Player player = sa.getActivatingPlayer();
@@ -552,7 +552,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
final Target tgt = sa.getTarget(); final Target tgt = sa.getTarget();
if (tgt != null) { if (tgt != null) {
final ArrayList<Player> players = tgt.getTargetPlayers(); final List<Player> players = tgt.getTargetPlayers();
player = player != null ? player : players.get(0); player = player != null ? player : players.get(0);
if (players.contains(player) && !player.canBeTargetedBy(sa)) { if (players.contains(player) && !player.canBeTargetedBy(sa)) {
return; return;

View File

@@ -1,6 +1,6 @@
package forge.card.ability.effects; package forge.card.ability.effects;
import java.util.ArrayList; import java.util.List;
import forge.Card; import forge.Card;
import forge.Singletons; import forge.Singletons;
@@ -21,7 +21,7 @@ public class ControlExchangeEffect extends SpellAbilityEffect {
Card object1 = null; Card object1 = null;
Card object2 = null; Card object2 = null;
final Target tgt = sa.getTarget(); final Target tgt = sa.getTarget();
ArrayList<Card> tgts = tgt.getTargetCards(); List<Card> tgts = tgt.getTargetCards();
if (tgts.size() > 0) { if (tgts.size() > 0) {
object1 = tgts.get(0); object1 = tgts.get(0);
} }
@@ -42,7 +42,7 @@ public class ControlExchangeEffect extends SpellAbilityEffect {
Card object1 = null; Card object1 = null;
Card object2 = null; Card object2 = null;
final Target tgt = sa.getTarget(); final Target tgt = sa.getTarget();
ArrayList<Card> tgts = tgt.getTargetCards(); List<Card> tgts = tgt.getTargetCards();
if (tgts.size() > 0) { if (tgts.size() > 0) {
object1 = tgts.get(0); object1 = tgts.get(0);
} }

View File

@@ -21,7 +21,7 @@ public class DestroyAllEffect extends SpellAbilityEffect {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
final boolean noRegen = sa.hasParam("NoRegen"); final boolean noRegen = sa.hasParam("NoRegen");
ArrayList<Card> tgtCards; List<Card> tgtCards;
final Target tgt = sa.getTarget(); final Target tgt = sa.getTarget();
if (tgt != null) { if (tgt != null) {

View File

@@ -34,7 +34,6 @@ import forge.CardPredicates.Presets;
import forge.Command; import forge.Command;
import forge.CounterType; import forge.CounterType;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.spellability.Ability; import forge.card.spellability.Ability;

View File

@@ -2919,7 +2919,7 @@ public class CardFactoryUtil {
} }
if (card.getController().isHuman()) { if (card.getController().isHuman()) {
game.getActionPlay().playSpellAbilityNoStack(card.getController(), origSA, false); game.getActionPlay().playSpellAbilityNoStack(card.getController(), origSA);
} else { } else {
ComputerUtil.playNoStack((AIPlayer) card.getController(), origSA, game); ComputerUtil.playNoStack((AIPlayer) card.getController(), origSA, game);
} }

View File

@@ -24,7 +24,6 @@ import com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.FThreads; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;

View File

@@ -239,7 +239,7 @@ public class ReplacementHandler {
Player player = replacementEffect.getHostCard().getController(); Player player = replacementEffect.getHostCard().getController();
//player.getController().playNoStack() //player.getController().playNoStack()
if (player.isHuman()) { if (player.isHuman()) {
game.getActionPlay().playSpellAbilityNoStack(player, effectSA, false); game.getActionPlay().playSpellAbilityNoStack(player, effectSA);
} else { } else {
ComputerUtil.playNoStack((AIPlayer) player, effectSA, game); ComputerUtil.playNoStack((AIPlayer) player, effectSA, game);
} }

View File

@@ -1053,7 +1053,7 @@ public abstract class SpellAbility implements ISpellAbility {
if (this.targetCard == null) { if (this.targetCard == null) {
final Target tgt = this.getTarget(); final Target tgt = this.getTarget();
if (tgt != null) { if (tgt != null) {
final ArrayList<Card> list = tgt.getTargetCards(); final List<Card> list = tgt.getTargetCards();
if (!list.isEmpty()) { if (!list.isEmpty()) {
return list.get(0); return list.get(0);
@@ -1106,7 +1106,7 @@ public abstract class SpellAbility implements ISpellAbility {
public Player getTargetPlayer() { public Player getTargetPlayer() {
final Target tgt = this.getTarget(); final Target tgt = this.getTarget();
if (tgt != null) { if (tgt != null) {
final ArrayList<Player> list = tgt.getTargetPlayers(); final List<Player> list = tgt.getTargetPlayers();
if (!list.isEmpty()) { if (!list.isEmpty()) {
return list.get(0); return list.get(0);
@@ -1313,7 +1313,7 @@ public abstract class SpellAbility implements ISpellAbility {
return false; return false;
} }
if (entity.isValid(this.getTarget().getValidTgts(), this.getActivatingPlayer(), this.getSourceCard()) 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)) { && entity.canBeTargetedBy(this)) {
return true; return true;
} }
@@ -1494,23 +1494,22 @@ public abstract class SpellAbility implements ISpellAbility {
* *
* @return a {@link forge.card.spellability.SpellAbility} object. * @return a {@link forge.card.spellability.SpellAbility} object.
*/ */
public ArrayList<Card> findTargetedCards() { public List<Card> findTargetedCards() {
ArrayList<Card> list = new ArrayList<Card>();
Target tgt = this.getTarget(); Target tgt = this.getTarget();
// First search for targeted cards associated with current ability // First search for targeted cards associated with current ability
if (tgt != null && tgt.getTargetCards() != null && !tgt.getTargetCards().isEmpty()) { if (tgt != null && tgt.getTargetCards() != null && !tgt.getTargetCards().isEmpty()) {
return tgt.getTargetCards(); return tgt.getTargetCards();
} }
List<Card> list = new ArrayList<Card>();
// Next search for source cards of targeted SAs associated with current ability // 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()) { for (final SpellAbility ability : tgt.getTargetSAs()) {
list.add(ability.getSourceCard()); list.add(ability.getSourceCard());
} }
return list; return list;
} }
// Lastly Search parent SAs for target cards // Lastly Search parent SAs for target cards
else {
// Check for a parent that targets a card // Check for a parent that targets a card
SpellAbility parent = this.getParentTargetingCard(); SpellAbility parent = this.getParentTargetingCard();
if (null != parent) { if (null != parent) {
@@ -1523,7 +1522,7 @@ public abstract class SpellAbility implements ISpellAbility {
list.add(ability.getSourceCard()); list.add(ability.getSourceCard());
} }
} }
}
return list; return list;
} }

View File

@@ -38,48 +38,36 @@ import forge.game.zone.Zone;
* @version $Id$ * @version $Id$
*/ */
public class SpellAbilityRequirements { public class SpellAbilityRequirements {
private SpellAbility ability = null; private final SpellAbility ability;
private TargetSelection select = null; private final TargetChooser select;
private CostPayment payment = null; private final CostPayment payment;
private boolean isFree = false; private boolean isFree;
private boolean skipStack = false; private boolean skipStack = false;
private boolean bCasting = false; private boolean isAlreadyTargeted = false;
private Zone fromZone = null;
private Integer zonePosition = null;
public void setAlreadyTargeted() { isAlreadyTargeted = true; }
public final void setSkipStack(final boolean bSkip) { public final void setSkipStack() { this.skipStack = true; }
this.skipStack = bSkip; public void setFree() { this.isFree = true; }
}
public final void setFree(final boolean bFree) {
this.isFree = bFree;
}
public SpellAbilityRequirements(final SpellAbility sa, final CostPayment cp) {
public SpellAbilityRequirements(final SpellAbility sa, final TargetSelection ts, final CostPayment cp) {
this.ability = sa; this.ability = sa;
this.select = ts; this.select = new TargetChooser(sa);
this.payment = cp; this.payment = cp;
} }
public final void fillRequirements() { public final void fillRequirements() {
this.fillRequirements(false);
}
public final void fillRequirements(final boolean skipTargeting) {
final GameState game = Singletons.getModel().getGame(); 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); // used to rollback
this.zonePosition = this.fromZone.getPosition(c); Zone fromZone = null;
this.ability.setSourceCard(game.getAction().moveToStack(c)); 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. // 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 // Announce things like how many times you want to Multikick or the value of X
if (!this.announceRequirements()) { if (!this.announceRequirements()) {
this.select.setCancel(true); this.select.setCancel(true);
rollbackAbility(); rollbackAbility(fromZone, zonePosition);
return; return;
} }
// Skip to paying if parent ability doesn't target and has no // Skip to paying if parent ability doesn't target and has no
// subAbilities. // subAbilities.
// (or trigger case where its already targeted) // (or trigger case where its already targeted)
if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) { boolean acceptsTargets = this.select.doesTarget() || this.ability.getSubAbility() != null;
this.select.setRequirements(this); if (!isAlreadyTargeted && acceptsTargets) {
this.select.clearTargets(); this.select.clearTargets();
this.select.chooseTargets(); this.select.chooseTargets();
if (this.select.isCanceled()) { if (this.select.isCanceled()) {
rollbackAbility(); rollbackAbility(fromZone, zonePosition);
return; return;
} }
} }
@@ -114,7 +102,7 @@ public class SpellAbilityRequirements {
} }
if (!paymentMade) { if (!paymentMade) {
rollbackAbility(); rollbackAbility(fromZone, zonePosition);
return; return;
} }
@@ -134,7 +122,7 @@ public class SpellAbilityRequirements {
} }
} }
private void rollbackAbility() { private void rollbackAbility(Zone fromZone, int zonePosition) {
// cancel ability during target choosing // cancel ability during target choosing
final Card c = this.ability.getSourceCard(); final Card c = this.ability.getSourceCard();
@@ -143,9 +131,9 @@ public class SpellAbilityRequirements {
c.setState(CardCharacteristicName.Original); 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 // 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) { if (this.select != null) {

View File

@@ -464,7 +464,7 @@ public class Target {
* *
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<Card> getTargetCards() { public final List<Card> getTargetCards() {
if (this.choice == null) { if (this.choice == null) {
return new ArrayList<Card>(); return new ArrayList<Card>();
} }
@@ -479,7 +479,7 @@ public class Target {
* *
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<Player> getTargetPlayers() { public final List<Player> getTargetPlayers() {
if (this.choice == null) { if (this.choice == null) {
return new ArrayList<Player>(); return new ArrayList<Player>();
} }
@@ -494,7 +494,7 @@ public class Target {
* *
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<SpellAbility> getTargetSAs() { public final List<SpellAbility> getTargetSAs() {
if (this.choice == null) { if (this.choice == null) {
return new ArrayList<SpellAbility>(); return new ArrayList<SpellAbility>();
} }

View File

@@ -18,6 +18,7 @@
package forge.card.spellability; package forge.card.spellability;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import forge.Card; import forge.Card;
import forge.game.player.Player; import forge.game.player.Player;
@@ -45,9 +46,9 @@ public class TargetChoices {
} }
// Card or Player are legal targets. // Card or Player are legal targets.
private final ArrayList<Card> targetCards = new ArrayList<Card>(); private final List<Card> targetCards = new ArrayList<Card>();
private final ArrayList<Player> targetPlayers = new ArrayList<Player>(); private final List<Player> targetPlayers = new ArrayList<Player>();
private final ArrayList<SpellAbility> targetSAs = new ArrayList<SpellAbility>(); private final List<SpellAbility> targetSAs = new ArrayList<SpellAbility>();
/** /**
* <p> * <p>
@@ -199,7 +200,7 @@ public class TargetChoices {
* *
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<Card> getTargetCards() { public final List<Card> getTargetCards() {
return this.targetCards; return this.targetCards;
} }
@@ -210,7 +211,7 @@ public class TargetChoices {
* *
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<Player> getTargetPlayers() { public final List<Player> getTargetPlayers() {
return this.targetPlayers; return this.targetPlayers;
} }
@@ -221,7 +222,7 @@ public class TargetChoices {
* *
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<SpellAbility> getTargetSAs() { public final List<SpellAbility> getTargetSAs() {
return this.targetSAs; return this.targetSAs;
} }

View File

@@ -20,20 +20,23 @@ package forge.card.spellability;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.FThreads; import forge.FThreads;
import forge.Singletons; import forge.GameEntity;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
import forge.control.input.InputSynchronized;
import forge.control.input.InputSyncronizedBase; import forge.control.input.InputSyncronizedBase;
import forge.game.GameState;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** /**
@@ -44,21 +47,24 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class TargetSelection { public class TargetChooser {
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
* *
*/ */
public final class InputSelectTargets extends InputSyncronizedBase { public static final class InputSelectTargets extends InputSyncronizedBase {
private final TargetSelection select;
private final List<Card> choices; private final List<Card> choices;
private final ArrayList<Object> alreadyTargeted; // some cards can be targeted several times (eg: distribute damage as you choose)
private final boolean targeted; private final Map<GameEntity, Integer> targetDepth = new HashMap<GameEntity, Integer>();
private final Target tgt; private final Target tgt;
private final SpellAbility sa; private final SpellAbility sa;
private boolean bCancel = false;
private boolean bOk = false;
private final boolean mandatory; private final boolean mandatory;
private static final long serialVersionUID = -1091595663541356356L; private static final long serialVersionUID = -1091595663541356356L;
public final boolean hasCancelled() { return bCancel; }
public final boolean hasPressedOk() { return bOk; }
/** /**
* TODO: Write javadoc for Constructor. * TODO: Write javadoc for Constructor.
* @param select * @param select
@@ -70,13 +76,10 @@ public class TargetSelection {
* @param sa * @param sa
* @param mandatory * @param mandatory
*/ */
public InputSelectTargets(TargetSelection select, List<Card> choices, ArrayList<Object> alreadyTargeted, boolean targeted, Target tgt, SpellAbility sa, boolean mandatory) { public InputSelectTargets(List<Card> choices, SpellAbility sa, boolean mandatory) {
super(sa.getActivatingPlayer()); super(sa.getActivatingPlayer());
this.select = select;
this.choices = choices; this.choices = choices;
this.alreadyTargeted = alreadyTargeted; this.tgt = sa.getTarget();
this.targeted = targeted;
this.tgt = tgt;
this.sa = sa; this.sa = sa;
this.mandatory = mandatory; this.mandatory = mandatory;
} }
@@ -84,12 +87,14 @@ public class TargetSelection {
@Override @Override
public void showMessage() { public void showMessage() {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("Targeted: "); sb.append("Targeted:\n");
for (final Object o : alreadyTargeted) { for (final Entry<GameEntity, Integer> o : targetDepth.entrySet()) {
sb.append(o).append(" "); sb.append(o.getKey());
if( o.getValue() > 1 )
sb.append(" (").append(o.getValue()).append(" times)");
sb.append("\n");
} }
sb.append(tgt.getTargetedString()); //sb.append(tgt.getTargetedString()).append("\n");
sb.append("\n");
sb.append(tgt.getVTSelection()); sb.append(tgt.getVTSelection());
showMessage(sb.toString()); showMessage(sb.toString());
@@ -114,110 +119,147 @@ public class TargetSelection {
@Override @Override
public void selectButtonCancel() { public void selectButtonCancel() {
select.setCancel(true); bCancel = true;
this.done(); this.done();
} }
@Override @Override
public void selectButtonOK() { public void selectButtonOK() {
bOk = true;
this.done(); this.done();
} }
@Override @Override
public void selectCard(final Card card) { public void selectCard(final Card card) {
// leave this in temporarily, there some seriously wrong things if (!tgt.isUniqueTargets() && targetDepth.containsKey(card)) {
// going on here return;
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();
} }
// 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() } // selectCard()
@Override @Override
public void selectPlayer(final Player player) { public void selectPlayer(final Player player) {
if (alreadyTargeted.contains(player)) { if (!tgt.isUniqueTargets() && targetDepth.containsKey(player)) {
return; return;
} }
if (sa.canTarget(player)) { if (!sa.canTarget(player)) {
if (tgt.isDividedAsYouChoose()) { showMessage("Cannot target this player (Hexproof? Protection? Restrictions?).");
final int stillToDivide = tgt.getStillToDivide(); return;
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 (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() { void done() {
this.stop(); this.stop();
} }
boolean hasAllTargets() {
return tgt.isMaxTargetsChosen(sa.getSourceCard(), sa);
}
} }
private final Target target;
private final SpellAbility ability; private final SpellAbility ability;
private TargetSelection subSelection = null; /**
* <p>
* Constructor for Target_Selection.
* </p>
*
* @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;
}
/** /**
* <p> * <p>
@@ -227,7 +269,7 @@ public class TargetSelection {
* @return a {@link forge.card.spellability.Target} object. * @return a {@link forge.card.spellability.Target} object.
*/ */
public final Target getTgt() { public final Target getTgt() {
return this.target; return this.ability.getTarget();
} }
/** /**
@@ -252,20 +294,7 @@ public class TargetSelection {
return this.ability.getSourceCard(); return this.ability.getSourceCard();
} }
private SpellAbilityRequirements req = null; private TargetChooser subSelection = null;
/**
* <p>
* setRequirements.
* </p>
*
* @param reqs
* a {@link forge.card.spellability.SpellAbilityRequirements}
* object.
*/
public final void setRequirements(final SpellAbilityRequirements reqs) {
this.req = reqs;
}
private boolean bCancel = false; private boolean bCancel = false;
private boolean bTargetingDone = false; private boolean bTargetingDone = false;
@@ -290,44 +319,12 @@ public class TargetSelection {
* @return a boolean. * @return a boolean.
*/ */
public final boolean isCanceled() { public final boolean isCanceled() {
if (this.bCancel) { return this.bCancel || this.subSelection != null && this.subSelection.isCanceled();
return this.bCancel;
}
if (this.subSelection == null) {
return false;
}
return this.subSelection.isCanceled();
} }
/**
* <p>
* Constructor for Target_Selection.
* </p>
*
* @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;
}
/**
* <p>
* doesTarget.
* </p>
*
* @return a boolean.
*/
public final boolean doesTarget() { public final boolean doesTarget() {
if (this.target == null) { Target tg = getTgt();
return false; return tg != null && tg.doesTarget();
}
return this.target.doesTarget();
} }
/** /**
@@ -336,55 +333,65 @@ public class TargetSelection {
* </p> * </p>
*/ */
public final void clearTargets() { public final void clearTargets() {
if (this.target != null) { Target tg = getTgt();
this.target.resetTargets(); if (tg != null) {
this.target.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability); tg.resetTargets();
tg.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability);
} }
} }
/**
* <p>
* chooseTargets.
* </p>
*
* @return a boolean.
*/
public final boolean chooseTargets() { 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 not enough targets chosen, reset and cancel Ability
if (this.bCancel || (this.bTargetingDone && !this.target.isMinTargetsChosen(this.getCard(), this.ability))) { if (this.bTargetingDone && !hasEnoughTargets) this.bCancel = true;
this.bCancel = true; if (this.bCancel) return false;
return false;
}
if (!this.doesTarget() if (!canTarget || this.bTargetingDone && hasEnoughTargets || hasAllTargets || tgt.isDividedAsYouChoose() && tgt.getStillToDivide() == 0) {
|| this.bTargetingDone && this.target.isMinTargetsChosen(this.getCard(), this.ability)
|| this.target.isMaxTargetsChosen(this.getCard(), this.ability)
|| this.target.isDividedAsYouChoose() && this.target.getStillToDivide() == 0) {
final AbilitySub abSub = this.ability.getSubAbility(); 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; return true;
} else {
// Has Sub Ability // Has Sub Ability
this.subSelection = new TargetSelection(abSub.getTarget(), abSub); this.subSelection = new TargetChooser(abSub);
this.subSelection.setRequirements(this.req); this.subSelection.clearTargets();
this.subSelection.clearTargets(); return this.subSelection.chooseTargets();
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 // Cancel ability if there aren't any valid Candidates
this.bCancel = true; this.bCancel = true;
return false; return false;
} }
this.chooseValidInput(); final List<ZoneType> zone = tgt.getZone();
if ( !bCancel ) final boolean mandatory = tgt.getMandatory() && tgt.hasCandidates(this.ability, true);
return chooseTargets();
if (zone.size() == 1 && zone.get(0) == ZoneType.Stack) {
return false; // If Zone is Stack, the choices are handled slightly differently
this.chooseCardFromStack(mandatory);
} else {
List<Card> 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 {
* <p> * <p>
* chooseValidInput. * chooseValidInput.
* </p> * </p>
* @return
*/ */
public final void chooseValidInput() { public final List<Card> chooseValidInput() {
final Target tgt = this.getTgt(); final Target tgt = this.getTgt();
final GameState game = ability.getActivatingPlayer().getGame();
final List<ZoneType> zone = tgt.getZone(); final List<ZoneType> zone = tgt.getZone();
final boolean mandatory = this.target.getMandatory() ? this.target.hasCandidates(this.ability, true) : false;
final boolean canTgtStack = zone.contains(ZoneType.Stack); final boolean canTgtStack = zone.contains(ZoneType.Stack);
List<Card> choices = CardLists.getTargetableCards(CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), this.ability.getActivatingPlayer(), this.ability.getSourceCard()), this.ability);
if (canTgtStack && (zone.size() == 1)) {
// If Zone is Stack, the choices are handled slightly differently
this.chooseCardFromStack(mandatory);
return;
}
List<Card> choices = CardLists.getTargetableCards(CardLists.getValidCards(Singletons.getModel().getGame().getCardsIn(zone), this.target.getValidTgts(), this.ability.getActivatingPlayer(), this.ability.getSourceCard()), this.ability);
if (canTgtStack) { if (canTgtStack) {
// Since getTargetableCards doesn't have additional checks if one of the Zones is stack // 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 // Remove the activating card from targeting itself if its on the Stack
@@ -450,7 +451,7 @@ public class TargetSelection {
} }
// Remove cards already targeted // Remove cards already targeted
final ArrayList<Card> targeted = tgt.getTargetCards(); final List<Card> targeted = tgt.getTargetCards();
for (final Card c : targeted) { for (final Card c : targeted) {
if (choices.contains(c)) { if (choices.contains(c)) {
choices.remove(c); choices.remove(c);
@@ -486,7 +487,7 @@ public class TargetSelection {
} }
// If all cards must have different controllers // If all cards must have different controllers
if (tgt.isDifferentControllers() && !targeted.isEmpty()) { if (tgt.isDifferentControllers() && !targeted.isEmpty()) {
final List<Player> availableControllers = new ArrayList<Player>(Singletons.getModel().getGame().getPlayers()); final List<Player> availableControllers = new ArrayList<Player>(game.getPlayers());
for (int i = 0; i < targeted.size(); i++) { for (int i = 0; i < targeted.size(); i++) {
availableControllers.remove(targeted.get(i).getController()); availableControllers.remove(targeted.get(i).getController());
} }
@@ -512,20 +513,7 @@ public class TargetSelection {
choices.clear(); choices.clear();
} }
} }
return choices;
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<Object>();
}
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);
}
} // input_targetValid } // input_targetValid
/** /**
@@ -540,92 +528,63 @@ public class TargetSelection {
* @param mandatory * @param mandatory
* a boolean. * a boolean.
*/ */
public final void chooseCardFromList(final List<Card> choices, final boolean targeted, final boolean mandatory) { private final void chooseCardFromList(final List<Card> choices, final boolean targeted, final boolean mandatory) {
// Send in a list of valid cards, and popup a choice box to target // Send in a list of valid cards, and popup a choice box to target
final Card dummy = new Card(); final GameState game = ability.getActivatingPlayer().getGame();
dummy.setName("[FINISH TARGETING]");
final SpellAbility sa = this.ability;
final String message = this.target.getVTSelection();
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<Card> choicesZoneUnfiltered = choices;
final List<Card> crdsBattle = new ArrayList<Card>(); final List<Card> crdsBattle = new ArrayList<Card>();
final List<Card> crdsExile = new ArrayList<Card>(); final List<Card> crdsExile = new ArrayList<Card>();
final List<Card> crdsGrave = new ArrayList<Card>(); final List<Card> crdsGrave = new ArrayList<Card>();
final List<Card> crdsLibrary = new ArrayList<Card>(); final List<Card> crdsLibrary = new ArrayList<Card>();
final List<Card> crdsStack = new ArrayList<Card>(); final List<Card> crdsStack = new ArrayList<Card>();
for (final Card inZone : choicesZoneUnfiltered) { for (final Card inZone : choices) {
if (Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield).contains(inZone)) { Zone zz = game.getZoneOf(inZone);
crdsBattle.add(inZone); if (zz.is(ZoneType.Battlefield)) crdsBattle.add(inZone);
} else if (Singletons.getModel().getGame().getCardsIn(ZoneType.Exile).contains(inZone)) { else if (zz.is(ZoneType.Exile)) crdsExile.add(inZone);
crdsExile.add(inZone); else if (zz.is(ZoneType.Graveyard)) crdsGrave.add(inZone);
} else if (Singletons.getModel().getGame().getCardsIn(ZoneType.Graveyard).contains(inZone)) { else if (zz.is(ZoneType.Library)) crdsLibrary.add(inZone);
crdsGrave.add(inZone); else if (zz.is(ZoneType.Stack)) crdsStack.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);
}
} }
List<Card> choicesFiltered = new ArrayList<Card>(); List<Object> choicesFiltered = new ArrayList<Object>();
if (crdsBattle.size() >= 1) { if (!crdsBattle.isEmpty()) {
choicesFiltered.add(divBattlefield); choicesFiltered.add("--CARDS ON BATTLEFIELD:--");
choicesFiltered.addAll(crdsBattle); choicesFiltered.addAll(crdsBattle);
crdsBattle.clear();
} }
if (crdsExile.size() >= 1) { if (!crdsExile.isEmpty()) {
choicesFiltered.add(divExile); choicesFiltered.add("--CARDS IN EXILE:--");
choicesFiltered.addAll(crdsExile); choicesFiltered.addAll(crdsExile);
crdsExile.clear();
} }
if (crdsGrave.size() >= 1) { if (!crdsGrave.isEmpty()) {
choicesFiltered.add(divGrave); choicesFiltered.add("--CARDS IN GRAVEYARD:--");
choicesFiltered.addAll(crdsGrave); choicesFiltered.addAll(crdsGrave);
crdsGrave.clear();
} }
if (crdsLibrary.size() >= 1) { if (!crdsLibrary.isEmpty()) {
choicesFiltered.add(divLibrary); choicesFiltered.add("--CARDS IN LIBRARY:--");
choicesFiltered.addAll(crdsLibrary); choicesFiltered.addAll(crdsLibrary);
crdsLibrary.clear();
} }
if (crdsStack.size() >= 1) { if (!crdsStack.isEmpty()) {
choicesFiltered.add(divStack); choicesFiltered.add("--CARDS IN STACK:--");
choicesFiltered.addAll(crdsStack); choicesFiltered.addAll(crdsStack);
crdsStack.clear();
} }
final Target tgt = this.getTgt(); final String msgDone = "[FINISH TARGETING]";
if (this.getTgt().isMinTargetsChosen(this.ability.getSourceCard(), this.ability)) {
final List<Card> choicesWithDone = choicesFiltered;
if (tgt.isMinTargetsChosen(sa.getSourceCard(), sa)) {
// is there a more elegant way of doing this? // is there a more elegant way of doing this?
choicesWithDone.add(dummy); choicesFiltered.add(msgDone);
} }
final Card check = GuiChoose.oneOrNone(message, choicesWithDone); final Object chosen = GuiChoose.oneOrNone(getTgt().getVTSelection(), choicesFiltered);
if (check != null) { if (chosen == 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 {
this.setCancel(true); 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 * @param mandatory
* a boolean. * a boolean.
*/ */
public final void chooseCardFromStack(final boolean mandatory) { private final void chooseCardFromStack(final boolean mandatory) {
final Target tgt = this.target; final Target tgt = this.getTgt();
final String message = tgt.getVTSelection(); final String message = tgt.getVTSelection();
final TargetSelection select = this;
final String doneDummy = "[FINISH TARGETING]"; final String doneDummy = "[FINISH TARGETING]";
// Find what's targetable, then allow human to choose // Find what's targetable, then allow human to choose
final ArrayList<SpellAbility> choosables = TargetSelection.getTargetableOnStack(this.ability, select.getTgt()); final ArrayList<SpellAbility> choosables = getTargetableOnStack();
final HashMap<String, SpellAbility> map = new HashMap<String, SpellAbility>(); final HashMap<String, SpellAbility> map = new HashMap<String, SpellAbility>();
@@ -657,14 +615,11 @@ public class TargetSelection {
map.put(doneDummy, null); map.put(doneDummy, null);
} }
String[] choices = new String[map.keySet().size()];
choices = map.keySet().toArray(choices);
if (choices.length == 0) { if (map.isEmpty()) {
select.setCancel(true); setCancel(true);
} else { } else {
final String madeChoice = GuiChoose.oneOrNone(message, choices); final String madeChoice = GuiChoose.oneOrNone(message, map.keySet());
if (madeChoice != null) { if (madeChoice != null) {
if (madeChoice.equals(doneDummy)) { if (madeChoice.equals(doneDummy)) {
bTargetingDone = true; bTargetingDone = true;
@@ -672,7 +627,7 @@ public class TargetSelection {
tgt.addTarget(map.get(madeChoice)); tgt.addTarget(map.get(madeChoice));
} }
} else { } else {
select.setCancel(true); setCancel(true);
} }
} }
} }
@@ -691,15 +646,16 @@ public class TargetSelection {
* a {@link forge.card.spellability.Target} object. * a {@link forge.card.spellability.Target} object.
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public static ArrayList<SpellAbility> getTargetableOnStack(final SpellAbility sa, final Target tgt) { private ArrayList<SpellAbility> getTargetableOnStack() {
final ArrayList<SpellAbility> choosables = new ArrayList<SpellAbility>(); final ArrayList<SpellAbility> choosables = new ArrayList<SpellAbility>();
for (int i = 0; i < Singletons.getModel().getGame().getStack().size(); i++) { final GameState game = ability.getActivatingPlayer().getGame();
choosables.add(Singletons.getModel().getGame().getStack().peekAbility(i)); for (int i = 0; i < game.getStack().size(); i++) {
choosables.add(game.getStack().peekAbility(i));
} }
for (int i = 0; i < choosables.size(); 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); choosables.remove(i);
} }
} }
@@ -753,7 +709,7 @@ public class TargetSelection {
boolean result = false; boolean result = false;
for (final Object o : matchTgt.getTargets()) { for (final Object o : matchTgt.getTargets()) {
if (TargetSelection.matchesValid(o, splitTargetRestrictions.split(","), sa)) { if (TargetChooser.matchesValid(o, splitTargetRestrictions.split(","), sa)) {
result = true; result = true;
break; break;
} }
@@ -764,26 +720,10 @@ public class TargetSelection {
} }
} }
if (!TargetSelection.matchesValidSA(topSA, tgt.getValidTgts(), sa)) { return topSA.getSourceCard().isValid(tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
return false;
}
return true;
} }
/**
* <p>
* matchesValid.
* </p>
*
* @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) { private static boolean matchesValid(final Object o, final String[] valids, final SpellAbility sa) {
final Card srcCard = sa.getSourceCard(); final Card srcCard = sa.getSourceCard();
final Player activatingPlayer = sa.getActivatingPlayer(); final Player activatingPlayer = sa.getActivatingPlayer();
@@ -801,24 +741,4 @@ public class TargetSelection {
return false; return false;
} }
/**
* <p>
* matchesValidSA.
* </p>
*
* @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);
}
} }

View File

@@ -419,7 +419,7 @@ public class TriggerHandler {
if (regtrig.isStatic()) { if (regtrig.isStatic()) {
if (wrapperAbility.getActivatingPlayer().isHuman()) { if (wrapperAbility.getActivatingPlayer().isHuman()) {
game.getActionPlay().playSpellAbilityNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility, false); game.getActionPlay().playSpellAbilityNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility);
} else { } else {
wrapperAbility.doTrigger(isMandatory, (AIPlayer)wrapperAbility.getActivatingPlayer()); wrapperAbility.doTrigger(isMandatory, (AIPlayer)wrapperAbility.getActivatingPlayer());
ComputerUtil.playNoStack((AIPlayer)wrapperAbility.getActivatingPlayer(), wrapperAbility, game); ComputerUtil.playNoStack((AIPlayer)wrapperAbility.getActivatingPlayer(), wrapperAbility, game);

View File

@@ -78,7 +78,7 @@ public class InputMulligan extends InputBase {
} }
sb.append("Do you want to Mulligan?"); sb.append("Do you want to Mulligan?");
CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); showMessage(sb.toString());
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@@ -130,7 +130,7 @@ public class InputMulligan extends InputBase {
if (GuiDialog.confirm(c, "Use " + c +"'s ability?")) { if (GuiDialog.confirm(c, "Use " + c +"'s ability?")) {
// If we ever let the AI memorize cards in the players // If we ever let the AI memorize cards in the players
// hand, this would be a place to do so. // hand, this would be a place to do so.
game.getActionPlay().playSpellAbilityNoStack(p, effect, false); game.getActionPlay().playSpellAbilityNoStack(p, effect);
} }
} }
} }

View File

@@ -21,7 +21,6 @@ import forge.card.mana.ManaCostShard;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityRequirements; import forge.card.spellability.SpellAbilityRequirements;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection;
import forge.card.staticability.StaticAbility; import forge.card.staticability.StaticAbility;
import forge.control.input.InputPayManaSimple; import forge.control.input.InputPayManaSimple;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
@@ -73,11 +72,10 @@ public class GameActionPlay {
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) { if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
CharmEffect.makeChoices(sa); CharmEffect.makeChoices(sa);
} }
final TargetSelection ts = new TargetSelection(sa.getTarget(), sa);
final CostPayment payment = new CostPayment(sa.getPayCosts(), sa); final CostPayment payment = new CostPayment(sa.getPayCosts(), sa);
final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, ts, payment); final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, payment);
req.setFree(true); req.setFree();
req.fillRequirements(); req.fillRequirements();
} else { } else {
if (sa.isSpell()) { if (sa.isSpell()) {
@@ -383,7 +381,6 @@ public class GameActionPlay {
// System.out.println("Playing:" + sa.getDescription() + " of " + sa.getSourceCard() + " new = " + newAbility); // System.out.println("Playing:" + sa.getDescription() + " of " + sa.getSourceCard() + " new = " + newAbility);
if (newAbility) { if (newAbility) {
final TargetSelection ts = new TargetSelection(sa.getTarget(), sa);
CostPayment payment = null; CostPayment payment = null;
if (sa.getPayCosts() == null) { if (sa.getPayCosts() == null) {
payment = new CostPayment(new Cost(sa.getSourceCard(), "0", sa.isAbility()), sa); payment = new CostPayment(new Cost(sa.getSourceCard(), "0", sa.isAbility()), sa);
@@ -391,7 +388,7 @@ public class GameActionPlay {
payment = new CostPayment(sa.getPayCosts(), sa); payment = new CostPayment(sa.getPayCosts(), sa);
} }
final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, ts, payment); final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, payment);
req.fillRequirements(); req.fillRequirements();
} else { } else {
ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost()); ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost());
@@ -426,20 +423,24 @@ public class GameActionPlay {
* @param skipTargeting * @param skipTargeting
* a boolean. * 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); sa.setActivatingPlayer(human);
if (sa.getPayCosts() != null) { if (sa.getPayCosts() != null) {
final TargetSelection ts = new TargetSelection(sa.getTarget(), sa);
final CostPayment payment = new CostPayment(sa.getPayCosts(), sa); final CostPayment payment = new CostPayment(sa.getPayCosts(), sa);
if (!sa.isTrigger()) { if (!sa.isTrigger()) {
payment.changeCost(); payment.changeCost();
} }
final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, ts, payment); final SpellAbilityRequirements req = new SpellAbilityRequirements(sa, payment);
req.setSkipStack(true); if( useOldTargets )
req.fillRequirements(skipTargeting); req.setAlreadyTargeted();
req.setSkipStack();
req.fillRequirements();
} else { } else {
ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost()); ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost());
if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) { if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) {

View File

@@ -42,7 +42,7 @@ import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance; import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.spellability.TargetChoices; import forge.card.spellability.TargetChoices;
import forge.card.spellability.TargetSelection; import forge.card.spellability.TargetChooser;
import forge.card.trigger.Trigger; import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.control.input.InputPayManaExecuteCommands; import forge.control.input.InputPayManaExecuteCommands;
@@ -885,7 +885,7 @@ public class MagicStack extends MyObservable {
} }
else if (o instanceof SpellAbility) { else if (o instanceof SpellAbility) {
final SpellAbility tgtSA = (SpellAbility) o; final SpellAbility tgtSA = (SpellAbility) o;
invalidTarget = !(TargetSelection.matchSpellAbility(sa, tgtSA, tgt)); invalidTarget = !(TargetChooser.matchSpellAbility(sa, tgtSA, tgt));
// TODO Remove target? // TODO Remove target?
if (invalidTarget) { if (invalidTarget) {
choices.removeTarget(tgtSA); choices.removeTarget(tgtSA);

View File

@@ -205,7 +205,6 @@ public final class CEditorConstructed extends ACEditorBase<CardPrinted, Deck> {
/** /**
* Switch between the main deck and the sideboard editor. * Switch between the main deck and the sideboard editor.
*/ */
@SuppressWarnings("incomplete-switch")
public void cycleEditorMode() { public void cycleEditorMode() {
int curindex = allSections.indexOf(sectionMode); int curindex = allSections.indexOf(sectionMode);