MagicStack - cleanup (made some methods private, inlined whatever is short and single-time used or was accessor to field)

AbilityUtils.resolve does not need extra parameter usedStack - Stack class can call finishResolving from its own code
HumanPlaySpellAbility - managed to simplify the code by groupping all prerequisites collection into a single exression
This commit is contained in:
Maxmtg
2013-06-01 21:40:11 +00:00
parent d55d7919dc
commit 60f3b788b4
16 changed files with 175 additions and 341 deletions

View File

@@ -1020,7 +1020,7 @@ public class AbilityUtils {
// BELOW ARE resove() METHOD AND ITS DEPENDANTS, CONSIDER MOVING TO DEDICATED CLASS // BELOW ARE resove() METHOD AND ITS DEPENDANTS, CONSIDER MOVING TO DEDICATED CLASS
// //
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
public static void resolve(final SpellAbility sa, final boolean usedStack) { public static void resolve(final SpellAbility sa) {
if (sa == null) { if (sa == null) {
return; return;
} }
@@ -1028,44 +1028,38 @@ public class AbilityUtils {
if (api == null) { if (api == null) {
sa.resolve(); sa.resolve();
if (sa.getSubAbility() != null) { if (sa.getSubAbility() != null) {
resolve(sa.getSubAbility(), usedStack); resolve(sa.getSubAbility());
} }
return; return;
} }
AbilityUtils.resolveApiAbility(sa, usedStack, sa.getActivatingPlayer().getGame()); AbilityUtils.resolveApiAbility(sa, sa.getActivatingPlayer().getGame());
} }
private static void resolveSubAbilities(final SpellAbility sa, boolean usedStack, final Game game) { private static void resolveSubAbilities(final SpellAbility sa, final Game game) {
final AbilitySub abSub = sa.getSubAbility(); final AbilitySub abSub = sa.getSubAbility();
if (abSub == null || sa.isWrapper()) { if (abSub == null || sa.isWrapper()) {
// every resolving spellAbility will end here
if (usedStack) {
SpellAbility root = sa.getRootAbility();
// static abilities will get refreshed from SBE check.
game.getStack().finishResolving(root, false);
}
return; return;
} }
game.getAction().checkStaticAbilities(); // this will refresh continuous abilities for players and permanents. game.getAction().checkStaticAbilities(); // this will refresh continuous abilities for players and permanents.
AbilityUtils.resolveApiAbility(abSub, usedStack, game); AbilityUtils.resolveApiAbility(abSub, game);
} }
private static void resolveApiAbility(final SpellAbility sa, boolean usedStack, final Game game) { private static void resolveApiAbility(final SpellAbility sa, final Game game) {
// check conditions // check conditions
if (sa.getConditions().areMet(sa)) { if (sa.getConditions().areMet(sa)) {
if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) { if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) {
sa.resolve(); sa.resolve();
} else { } else {
handleUnlessCost(sa, usedStack, game); handleUnlessCost(sa, game);
return; return;
} }
} }
resolveSubAbilities(sa, usedStack, game); resolveSubAbilities(sa, game);
} }
private static void handleUnlessCost(final SpellAbility sa, final boolean usedStack, final Game game) { private static void handleUnlessCost(final SpellAbility sa, final Game game) {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
String unlessCost = sa.getParam("UnlessCost"); String unlessCost = sa.getParam("UnlessCost");
unlessCost = unlessCost.trim(); unlessCost = unlessCost.trim();
@@ -1084,7 +1078,7 @@ public class AbilityUtils {
} else if (unlessCost.equals("RememberedCostMinus2")) { } else if (unlessCost.equals("RememberedCostMinus2")) {
if (source.getRemembered().isEmpty() || !(source.getRemembered().get(0) instanceof Card)) { if (source.getRemembered().isEmpty() || !(source.getRemembered().get(0) instanceof Card)) {
sa.resolve(); sa.resolve();
resolveSubAbilities(sa, usedStack, game); resolveSubAbilities(sa, game);
} }
Card rememberedCard = (Card) source.getRemembered().get(0); Card rememberedCard = (Card) source.getRemembered().get(0);
unlessCost = rememberedCard.getManaCost().toString(); unlessCost = rememberedCard.getManaCost().toString();
@@ -1124,10 +1118,7 @@ public class AbilityUtils {
} }
if ( paid && execSubsWhenPaid || !paid && execSubsWhenNotPaid ) { // switched refers only to main ability! if ( paid && execSubsWhenPaid || !paid && execSubsWhenNotPaid ) { // switched refers only to main ability!
resolveSubAbilities(sa, usedStack, game); resolveSubAbilities(sa, game);
} else if (usedStack) {
SpellAbility root = sa.getRootAbility();
game.getStack().finishResolving(root, false);
} }
} }

View File

@@ -40,7 +40,7 @@ import forge.game.player.Player;
final AbilitySub abSub = sa.getSubAbility(); final AbilitySub abSub = sa.getSubAbility();
if (abSub != null) { if (abSub != null) {
sa.setUndoable(false); sa.setUndoable(false);
AbilityUtils.resolve(abSub, false); AbilityUtils.resolve(abSub);
} }
} }

View File

@@ -62,7 +62,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
} }
chosenSA.setActivatingPlayer(sa.getSourceCard().getController()); chosenSA.setActivatingPlayer(sa.getSourceCard().getController());
((AbilitySub) chosenSA).setParent(sa); ((AbilitySub) chosenSA).setParent(sa);
AbilityUtils.resolve(chosenSA, false); AbilityUtils.resolve(chosenSA);
} }
} }

View File

@@ -43,7 +43,7 @@ public class ClashEffect extends SpellAbilityEffect {
win.setActivatingPlayer(sa.getSourceCard().getController()); win.setActivatingPlayer(sa.getSourceCard().getController());
((AbilitySub) win).setParent(sa); ((AbilitySub) win).setParent(sa);
AbilityUtils.resolve(win, false); AbilityUtils.resolve(win);
} }
runParams.put("Won", "True"); runParams.put("Won", "True");
} else { } else {
@@ -53,7 +53,7 @@ public class ClashEffect extends SpellAbilityEffect {
otherwise.setActivatingPlayer(sa.getSourceCard().getController()); otherwise.setActivatingPlayer(sa.getSourceCard().getController());
((AbilitySub) otherwise).setParent(sa); ((AbilitySub) otherwise).setParent(sa);
AbilityUtils.resolve(otherwise, false); AbilityUtils.resolve(otherwise);
} }
runParams.put("Won", "False"); runParams.put("Won", "False");
} }

View File

@@ -126,7 +126,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
// better logic to pick a counter type and probably // better logic to pick a counter type and probably
// an initial target // an initial target
// find first nonzero counter on target // find first nonzero counter on target
for (Object key : tgtCounters.keySet()) { for (CounterType key : tgtCounters.keySet()) {
if (tgtCounters.get(key) > 0) { if (tgtCounters.get(key) > 0) {
chosenType = (CounterType) key; chosenType = (CounterType) key;
break; break;

View File

@@ -76,7 +76,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
heads.setActivatingPlayer(player); heads.setActivatingPlayer(player);
((AbilitySub) heads).setParent(sa); ((AbilitySub) heads).setParent(sa);
AbilityUtils.resolve(heads, false); AbilityUtils.resolve(heads);
} }
} else { } else {
if (sa.hasParam("TailsSubAbility")) { if (sa.hasParam("TailsSubAbility")) {
@@ -84,7 +84,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
tails.setActivatingPlayer(player); tails.setActivatingPlayer(player);
((AbilitySub) tails).setParent(sa); ((AbilitySub) tails).setParent(sa);
AbilityUtils.resolve(tails, false); AbilityUtils.resolve(tails);
} }
} }
} else { } else {
@@ -97,7 +97,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
win.setActivatingPlayer(player); win.setActivatingPlayer(player);
((AbilitySub) win).setParent(sa); ((AbilitySub) win).setParent(sa);
AbilityUtils.resolve(win, false); AbilityUtils.resolve(win);
} }
// runParams.put("Won","True"); // runParams.put("Won","True");
} else { } else {
@@ -109,7 +109,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
lose.setActivatingPlayer(player); lose.setActivatingPlayer(player);
((AbilitySub) lose).setParent(sa); ((AbilitySub) lose).setParent(sa);
AbilityUtils.resolve(lose, false); AbilityUtils.resolve(lose);
} }
// runParams.put("Won","False"); // runParams.put("Won","False");
} }

View File

@@ -70,7 +70,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
source.addRemembered(card); source.addRemembered(card);
} }
AbilityUtils.resolve(repeat, false); AbilityUtils.resolve(repeat);
if (useImprinted) { if (useImprinted) {
source.removeImprinted(card); source.removeImprinted(card);
} else { } else {
@@ -84,7 +84,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
for (Player player : repeatPlayers) { for (Player player : repeatPlayers) {
source.addRemembered(player); source.addRemembered(player);
AbilityUtils.resolve(repeat, false); AbilityUtils.resolve(repeat);
source.removeRemembered(player); source.removeRemembered(player);
} }
} }
@@ -100,7 +100,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
sb.append("Number$").append(target.getCounters(type)); sb.append("Number$").append(target.getCounters(type));
source.setSVar("RepeatSVarCounter", type.getName().toUpperCase()); source.setSVar("RepeatSVarCounter", type.getName().toUpperCase());
source.setSVar("RepeatCounterAmount", sb.toString()); source.setSVar("RepeatCounterAmount", sb.toString());
AbilityUtils.resolve(repeat, false); AbilityUtils.resolve(repeat);
} }
} }
} }

View File

@@ -40,7 +40,7 @@ public class RepeatEffect extends SpellAbilityEffect {
//execute repeat ability at least once //execute repeat ability at least once
int count = 0; int count = 0;
do { do {
AbilityUtils.resolve(repeat, false); AbilityUtils.resolve(repeat);
count++; count++;
if (maxRepeat != null && maxRepeat <= count) { if (maxRepeat != null && maxRepeat <= count) {
// TODO Replace Infinite Loop Break with a game draw. Here are the scenarios that can cause this: // TODO Replace Infinite Loop Break with a game draw. Here are the scenarios that can cause this:

View File

@@ -140,7 +140,7 @@ public class TwoPilesEffect extends SpellAbilityEffect {
action.setActivatingPlayer(sa.getActivatingPlayer()); action.setActivatingPlayer(sa.getActivatingPlayer());
((AbilitySub) action).setParent(sa); ((AbilitySub) action).setParent(sa);
AbilityUtils.resolve(action, false); AbilityUtils.resolve(action);
} }
// take action on the chosen pile // take action on the chosen pile
@@ -160,7 +160,7 @@ public class TwoPilesEffect extends SpellAbilityEffect {
action.setActivatingPlayer(sa.getActivatingPlayer()); action.setActivatingPlayer(sa.getActivatingPlayer());
((AbilitySub) action).setParent(sa); ((AbilitySub) action).setParent(sa);
AbilityUtils.resolve(action, false); AbilityUtils.resolve(action);
} }
} }
} }

View File

@@ -64,48 +64,22 @@ public class HumanPlaySpellAbility {
// 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.
game.getStack().freezeStack(); game.getStack().freezeStack();
// Announce things like how many times you want to Multikick or the value of X // This line makes use of short-circuit evaluation of boolean values, that is each subsequent argument
if (!this.announceRequirements()) { // is only executed or evaluated if the first argument does not suffice to determine the value of the expression
boolean prerequisitesMet = this.announceValuesLikeX()
&& ( isAlreadyTargeted || setupTargets() )
&& ( isFree || this.payment.payCost(game) );
if (!prerequisitesMet) {
rollbackAbility(fromZone, zonePosition); rollbackAbility(fromZone, zonePosition);
return; return;
} }
// Skip to paying if parent ability doesn't target and has no
// subAbilities.
// (or trigger case where its already targeted)
if (!isAlreadyTargeted) {
SpellAbility beingTargeted = ability;
do {
Target tgt = beingTargeted.getTarget();
if( tgt != null && tgt.doesTarget()) {
clearTargets(beingTargeted);
final TargetSelection select = new TargetSelection(beingTargeted);
if (!select.chooseTargets() ) {
rollbackAbility(fromZone, zonePosition);
return;
}
}
beingTargeted = beingTargeted.getSubAbility();
} while (beingTargeted != null);
}
// Payment
boolean paymentMade = isFree;
if (!paymentMade) {
paymentMade = this.payment.payCost(game);
}
if (!paymentMade) {
rollbackAbility(fromZone, zonePosition);
return;
}
else {
if (isFree || this.payment.isFullyPaid()) { if (isFree || this.payment.isFullyPaid()) {
if (skipStack) { if (skipStack) {
AbilityUtils.resolve(this.ability, false); AbilityUtils.resolve(this.ability);
} else { } else {
this.enusureAbilityHasDescription(this.ability); this.enusureAbilityHasDescription(this.ability);
this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false); this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false);
@@ -114,9 +88,26 @@ public class HumanPlaySpellAbility {
// no worries here. The same thread must resolve, and by this moment ability will have been resolved already // no worries here. The same thread must resolve, and by this moment ability will have been resolved already
clearTargets(ability); clearTargets(ability);
//game.getAction().checkStateEffects(); }
}
private final boolean setupTargets() {
// Skip to paying if parent ability doesn't target and has no subAbilities.
// (or trigger case where its already targeted)
SpellAbility beingTargeted = ability;
do {
Target tgt = beingTargeted.getTarget();
if( tgt != null && tgt.doesTarget()) {
clearTargets(beingTargeted);
final TargetSelection select = new TargetSelection(beingTargeted);
if (!select.chooseTargets() ) {
return false;
} }
} }
beingTargeted = beingTargeted.getSubAbility();
} while (beingTargeted != null);
return true;
} }
public final void clearTargets(SpellAbility ability) { public final void clearTargets(SpellAbility ability) {
@@ -147,11 +138,10 @@ public class HumanPlaySpellAbility {
this.ability.resetOnceResolved(); this.ability.resetOnceResolved();
this.payment.refundPayment(); this.payment.refundPayment();
game.getStack().clearFrozen(); game.getStack().clearFrozen();
// Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability);
} }
private boolean announceRequirements() { private boolean announceValuesLikeX() {
// Announcing Requirements like Choosing X or Multikicker // Announcing Requirements like Choosing X or Multikicker
// SA Params as comma delimited list // SA Params as comma delimited list
String announce = ability.getParam("Announce"); String announce = ability.getParam("Announce");

View File

@@ -318,7 +318,7 @@ public class ComputerUtil {
pay.payComputerCosts(ai, game); pay.payComputerCosts(ai, game);
} }
AbilityUtils.resolve(sa, false); AbilityUtils.resolve(sa);
// destroys creatures if they have lethal damage, etc.. // destroys creatures if they have lethal damage, etc..
//game.getAction().checkStateEffects(); //game.getAction().checkStateEffects();

View File

@@ -185,7 +185,7 @@ public class ComputerUtilMana {
saPayment.getSourceCard().tap(); saPayment.getSourceCard().tap();
} }
AbilityUtils.resolve(saPayment, false); AbilityUtils.resolve(saPayment);
// subtract mana from mana pool // subtract mana from mana pool
manapool.payManaFromAbility(sa, cost, saPayment); manapool.payManaFromAbility(sa, cost, saPayment);

View File

@@ -477,8 +477,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
*/ */
private Player handleNextTurn() { private Player handleNextTurn() {
game.getStack().setCardsCastLastTurn(); game.getStack().onNextTurn();
game.getStack().clearCardsCastThisTurn();
for (final Player p1 : game.getPlayers()) { for (final Player p1 : game.getPlayers()) {
for (final ZoneType z : Player.ALL_ZONES) { for (final ZoneType z : Player.ALL_ZONES) {

View File

@@ -34,7 +34,6 @@ import forge.card.cost.CostSacrifice;
import forge.card.cost.CostTapType; import forge.card.cost.CostTapType;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.mana.ManaCostShard;
import forge.card.spellability.Ability; import forge.card.spellability.Ability;
import forge.card.spellability.HumanPlaySpellAbility; import forge.card.spellability.HumanPlaySpellAbility;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -210,8 +209,7 @@ public class HumanPlay {
sa.setSourceCard(game.getAction().moveToStack(c)); sa.setSourceCard(game.getAction().moveToStack(c));
} }
} }
boolean x = sa.getSourceCard().getManaCost().getShardCount(ManaCostShard.X) > 0; game.getStack().add(sa);
game.getStack().add(sa, x);
} }
} }
@@ -238,7 +236,7 @@ public class HumanPlay {
req.fillRequirements(useOldTargets, false, true); req.fillRequirements(useOldTargets, false, true);
} else { } else {
if (payManaCostIfNeeded(player, sa)) { if (payManaCostIfNeeded(player, sa)) {
AbilityUtils.resolve(sa, false); AbilityUtils.resolve(sa);
} }
} }

View File

@@ -1313,14 +1313,14 @@ public class Player extends GameEntity implements Comparable<Player> {
SpellAbility saMill = AbilityFactory.getAbility(c.getSVar("MillOne"), c); SpellAbility saMill = AbilityFactory.getAbility(c.getSVar("MillOne"), c);
saMill.setActivatingPlayer(c.getController()); saMill.setActivatingPlayer(c.getController());
saMill.setTarget(target); saMill.setTarget(target);
AbilityUtils.resolve(saMill, false); AbilityUtils.resolve(saMill);
return drawn; // Draw is cancelled return drawn; // Draw is cancelled
} else { } else {
SpellAbility saDiscard = AbilityFactory.getAbility(c.getSVar("DiscardOne"), c); SpellAbility saDiscard = AbilityFactory.getAbility(c.getSVar("DiscardOne"), c);
saDiscard.setActivatingPlayer(c.getController()); saDiscard.setActivatingPlayer(c.getController());
saDiscard.setTarget(target); saDiscard.setTarget(target);
AbilityUtils.resolve(saDiscard, false); AbilityUtils.resolve(saDiscard);
} }
} }
} }

View File

@@ -132,7 +132,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
this.lastTurnCast.clear(); this.lastTurnCast.clear();
this.thisTurnCast.clear(); this.thisTurnCast.clear();
this.curResolvingCard = null; this.curResolvingCard = null;
this.getFrozenStack().clear(); this.frozenStack.clear();
this.updateObservers(); this.updateObservers();
} }
@@ -200,8 +200,8 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
this.frozen = false; this.frozen = false;
// Add all Frozen Abilities onto the stack // Add all Frozen Abilities onto the stack
while (!this.getFrozenStack().isEmpty()) { while (!this.frozenStack.isEmpty()) {
final SpellAbility sa = this.getFrozenStack().pop().getSpellAbility(); final SpellAbility sa = this.frozenStack.pop().getSpellAbility();
this.add(sa); this.add(sa);
} }
// Add all waiting triggers onto the stack // Add all waiting triggers onto the stack
@@ -222,7 +222,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
// TODO: frozen triggered abilities and undoable costs have nasty // TODO: frozen triggered abilities and undoable costs have nasty
// consequences // consequences
this.frozen = false; this.frozen = false;
this.getFrozenStack().clear(); this.frozenStack.clear();
} }
/** /**
@@ -234,8 +234,8 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
*/ */
public final void removeFromFrozenStack(SpellAbility sa) { public final void removeFromFrozenStack(SpellAbility sa) {
SpellAbilityStackInstance si = this.getInstanceFromSpellAbility(sa); SpellAbilityStackInstance si = this.getInstanceFromSpellAbility(sa);
this.getFrozenStack().remove(si); this.frozenStack.remove(si);
if (this.getFrozenStack().isEmpty()) { if (this.frozenStack.isEmpty()) {
clearFrozen(); clearFrozen();
} }
} }
@@ -266,37 +266,6 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
return this.bResolving; return this.bResolving;
} }
/**
* <p>
* add.
* </p>
*
* @param sp
* a {@link forge.card.spellability.SpellAbility} object.
* @param useX
* a boolean.
*/
public final void add(final SpellAbility sp, final boolean useX) {
if (!useX) {
this.add(sp);
} else {
// TODO: make working triggered abilities!
if (sp.isManaAbility() || (sp instanceof AbilityTriggered)) {
AbilityUtils.resolve(sp, false);
//sp.resolve();
} else {
this.push(sp);
/*
* if (sp.getTargetCard() != null)
* CardFactoryUtil.checkTargetingEffects(sp,
* sp.getTargetCard());
*/
}
}
}
/** /**
* <p> * <p>
* add. * add.
@@ -309,7 +278,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
FThreads.assertExecutedByEdt(false); FThreads.assertExecutedByEdt(false);
if (sp.isManaAbility()) { // Mana Abilities go straight through if (sp.isManaAbility()) { // Mana Abilities go straight through
AbilityUtils.resolve(sp, false); AbilityUtils.resolve(sp);
//sp.resolve(); //sp.resolve();
sp.resetOnceResolved(); sp.resetOnceResolved();
game.getGameLog().add(GameLogEntryType.MANA, sp.getSourceCard() + " - " + sp.getDescription()); game.getGameLog().add(GameLogEntryType.MANA, sp.getSourceCard() + " - " + sp.getDescription());
@@ -326,7 +295,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
if (this.frozen) { if (this.frozen) {
final SpellAbilityStackInstance si = new SpellAbilityStackInstance(sp); final SpellAbilityStackInstance si = new SpellAbilityStackInstance(sp);
this.getFrozenStack().push(si); this.frozenStack.push(si);
return; return;
} }
@@ -456,7 +425,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
} }
} }
if (!this.getSimultaneousStackEntryList().isEmpty()) { if (!this.simultaneousStackEntryList.isEmpty()) {
chooseOrderOfSimultaneousStackEntryAll(); chooseOrderOfSimultaneousStackEntryAll();
// Why should we pass priority after adding something to a stack? // Why should we pass priority after adding something to a stack?
// game.getPhaseHandler().passPriority(); // game.getPhaseHandler().passPriority();
@@ -520,9 +489,6 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
public final void resolveStack() { public final void resolveStack() {
// Resolving the Stack // Resolving the Stack
// TODO: change to use forge.view.FView?
//GuiDisplayUtil.updateGUI();
// freeze the stack while we're in the middle of resolving // freeze the stack while we're in the middle of resolving
this.freezeStack(); this.freezeStack();
this.setResolving(true); this.setResolving(true);
@@ -543,21 +509,24 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
if (thisHasFizzled) { // Fizzle if (thisHasFizzled) { // Fizzle
// TODO: Spell fizzles, what's the best way to alert player? // TODO: Spell fizzles, what's the best way to alert player?
Log.debug(source.getName() + " ability fizzles."); Log.debug(source.getName() + " ability fizzles.");
this.finishResolving(sa, true);
} else if (sa.getApi() != null) { } else if (sa.getApi() != null) {
AbilityUtils.handleRemembering(sa); AbilityUtils.handleRemembering(sa);
AbilityUtils.resolve(sa, true); AbilityUtils.resolve(sa);
} else { } else {
sa.resolve(); sa.resolve();
this.finishResolving(sa, false);
// do creatures ETB from here? // do creatures ETB from here?
} }
sa.getSourceCard().setXManaCostPaid(0); this.finishResolving(sa, thisHasFizzled);
game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled)); game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled));
if (source.hasStartOfKeyword("Haunt") && !source.isCreature() if (source.hasStartOfKeyword("Haunt") && !source.isCreature() && game.getZoneOf(source).is(ZoneType.Graveyard)) {
&& game.getZoneOf(source).is(ZoneType.Graveyard)) { handleHauntForNonPermanents(sa);
}
}
private void handleHauntForNonPermanents(final SpellAbility sa) {
final Card source = sa.getSourceCard();
final List<Card> creats = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES); final List<Card> creats = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
final Ability haunterDiesWork = new Ability(source, ManaCost.ZERO) { final Ability haunterDiesWork = new Ability(source, ManaCost.ZERO) {
@Override @Override
@@ -590,20 +559,50 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
} }
} }
} }
private final void finishResolving(final SpellAbility sa, final boolean fizzle) {
// remove SA and card from the stack
this.removeCardFromStack(sa, fizzle);
// SpellAbility is removed from the stack here
// temporarily removed removing SA after resolution
final SpellAbilityStackInstance si = this.getInstanceFromSpellAbility(sa);
if (si != null) {
this.remove(si);
} }
/** // After SA resolves we have to do a handful of things
* <p> this.setResolving(false);
* removeCardFromStack. this.unfreezeStack();
* </p> sa.resetOnceResolved();
*
* @param sa game.getAction().checkStateEffects();
* a {@link forge.card.spellability.SpellAbility} object.
* @param fizzle game.getPhaseHandler().onStackResolved();
* a boolean.
* @since 1.0.15 this.curResolvingCard = null;
*/
public final void removeCardFromStack(final SpellAbility sa, final boolean fizzle) { this.updateObservers();
// TODO: this is a huge hack. Why is this necessary?
// hostCard in AF is not the same object that's on the battlefield
// verified by System.identityHashCode(card);
final Card tmp = sa.getSourceCard();
tmp.setCanCounter(true); // reset mana pumped counter magic flag
if (tmp.getClones().size() > 0) {
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
if (c.equals(tmp)) {
c.setClones(tmp.getClones());
}
}
}
sa.getSourceCard().setXManaCostPaid(0);
}
private final void removeCardFromStack(final SpellAbility sa, final boolean fizzle) {
Card source = sa.getSourceCard(); Card source = sa.getSourceCard();
// do nothing // do nothing
@@ -644,54 +643,6 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
} }
} }
/**
* <p>
* finishResolving.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @param fizzle
* a boolean.
* @since 1.0.15
*/
public final void finishResolving(final SpellAbility sa, final boolean fizzle) {
// remove SA and card from the stack
this.removeCardFromStack(sa, fizzle);
// SpellAbility is removed from the stack here
// temporarily removed removing SA after resolution
this.remove(sa);
// After SA resolves we have to do a handful of things
this.setResolving(false);
this.unfreezeStack();
sa.resetOnceResolved();
game.getAction().checkStateEffects();
game.getPhaseHandler().onStackResolved();
this.curResolvingCard = null;
// TODO: change to use forge.view.FView?
//GuiDisplayUtil.updateGUI();
this.updateObservers();
// TODO: this is a huge hack. Why is this necessary?
// hostCard in AF is not the same object that's on the battlefield
// verified by System.identityHashCode(card);
final Card tmp = sa.getSourceCard();
tmp.setCanCounter(true); // reset mana pumped counter magic flag
if (tmp.getClones().size() > 0) {
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
if (c.equals(tmp)) {
c.setClones(tmp.getClones());
}
}
}
}
/** /**
* <p> * <p>
@@ -704,7 +655,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a boolean. * @return a boolean.
*/ */
public final boolean hasFizzled(final SpellAbility sa, final Card source, final boolean parentFizzled) { private final boolean hasFizzled(final SpellAbility sa, final Card source, final boolean parentFizzled) {
// Can't fizzle unless there are some targets // Can't fizzle unless there are some targets
boolean fizzle = false; boolean fizzle = false;
@@ -770,60 +721,16 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
return hasFizzled(sa.getSubAbility(), source, fizzle) && fizzle; return hasFizzled(sa.getSubAbility(), source, fizzle) && fizzle;
} }
/**
* <p>
* peekAbility.
* </p>
*
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public final SpellAbility peekAbility() { public final SpellAbility peekAbility() {
return this.stack.peekFirst().getSpellAbility(); return this.stack.peekFirst().getSpellAbility();
} }
/**
* <p>
* remove.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
*/
public final void remove(final SpellAbility sa) {
final SpellAbilityStackInstance si = this.getInstanceFromSpellAbility(sa);
if (si == null) {
return;
}
this.remove(si);
}
/**
* <p>
* remove.
* </p>
*
* @param si
* a {@link forge.card.spellability.SpellAbilityStackInstance}
* object.
*/
public final void remove(final SpellAbilityStackInstance si) { public final void remove(final SpellAbilityStackInstance si) {
this.stack.remove(si); this.stack.remove(si);
this.getFrozenStack().remove(si); this.frozenStack.remove(si);
this.updateObservers(); this.updateObservers();
} }
/**
* <p>
* getInstanceFromSpellAbility.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link forge.card.spellability.SpellAbilityStackInstance}
* object.
*/
public final SpellAbilityStackInstance getInstanceFromSpellAbility(final SpellAbility sa) { public final SpellAbilityStackInstance getInstanceFromSpellAbility(final SpellAbility sa) {
// TODO: Confirm this works! // TODO: Confirm this works!
for (final SpellAbilityStackInstance si : this.stack) { for (final SpellAbilityStackInstance si : this.stack) {
@@ -834,38 +741,19 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
return null; return null;
} }
/**
* <p>
* hasSimultaneousStackEntries.
* </p>
*
* @return a boolean.
*/
public final boolean hasSimultaneousStackEntries() { public final boolean hasSimultaneousStackEntries() {
return this.getSimultaneousStackEntryList().size() > 0; return !this.simultaneousStackEntryList.isEmpty();
} }
public final void clearSimultaneousStack() { public final void clearSimultaneousStack() {
this.simultaneousStackEntryList.clear(); this.simultaneousStackEntryList.clear();
} }
/**
* <p>
* addSimultaneousStackEntry.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
*/
public final void addSimultaneousStackEntry(final SpellAbility sa) { public final void addSimultaneousStackEntry(final SpellAbility sa) {
this.getSimultaneousStackEntryList().add(sa); this.simultaneousStackEntryList.add(sa);
} }
/**
* <p>
* chooseOrderOfSimultaneousStackEntryAll.
* </p>
*/
public final void chooseOrderOfSimultaneousStackEntryAll() { public final void chooseOrderOfSimultaneousStackEntryAll() {
final Player playerTurn = game.getPhaseHandler().getPlayerTurn(); final Player playerTurn = game.getPhaseHandler().getPlayerTurn();
@@ -879,33 +767,26 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
} }
} }
/**
* <p> private final void chooseOrderOfSimultaneousStackEntry(final Player activePlayer) {
* chooseOrderOfSimultaneousStackEntry. if (this.simultaneousStackEntryList.isEmpty()) {
* </p>
*
* @param activePlayer
* a {@link forge.game.player.Player} object.
*/
public final void chooseOrderOfSimultaneousStackEntry(final Player activePlayer) {
if (this.getSimultaneousStackEntryList().isEmpty()) {
return; return;
} }
final List<SpellAbility> activePlayerSAs = new ArrayList<SpellAbility>(); final List<SpellAbility> activePlayerSAs = new ArrayList<SpellAbility>();
for (int i = 0; i < this.getSimultaneousStackEntryList().size(); i++) { for (int i = 0; i < this.simultaneousStackEntryList.size(); i++) {
SpellAbility sa = this.getSimultaneousStackEntryList().get(i); SpellAbility sa = this.simultaneousStackEntryList.get(i);
Player activator = sa.getActivatingPlayer(); Player activator = sa.getActivatingPlayer();
if (activator == null) { if (activator == null) {
if (sa.getSourceCard().getController().equals(activePlayer)) { if (sa.getSourceCard().getController().equals(activePlayer)) {
activePlayerSAs.add(sa); activePlayerSAs.add(sa);
this.getSimultaneousStackEntryList().remove(i); this.simultaneousStackEntryList.remove(i);
i--; i--;
} }
} else { } else {
if (activator.equals(activePlayer)) { if (activator.equals(activePlayer)) {
activePlayerSAs.add(sa); activePlayerSAs.add(sa);
this.getSimultaneousStackEntryList().remove(i); this.simultaneousStackEntryList.remove(i);
i--; i--;
} }
} }
@@ -966,24 +847,6 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
return false; return false;
} }
/**
* Gets the simultaneous stack entry list.
*
* @return the simultaneousStackEntryList
*/
public final List<SpellAbility> getSimultaneousStackEntryList() {
return this.simultaneousStackEntryList;
}
/**
* Gets the frozen stack.
*
* @return the frozenStack
*/
public final Stack<SpellAbilityStackInstance> getFrozenStack() {
return this.frozenStack;
}
/** /**
* Accessor for the field thisTurnCast. * Accessor for the field thisTurnCast.
* *
@@ -996,16 +859,9 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
/** /**
* clearCardsCastThisTurn. * clearCardsCastThisTurn.
*/ */
public final void clearCardsCastThisTurn() { public final void onNextTurn() {
this.thisTurnCast.clear();
}
/**
*
* setCardsCastLastTurn.
*/
public final void setCardsCastLastTurn() {
this.lastTurnCast = new ArrayList<Card>(this.thisTurnCast); this.lastTurnCast = new ArrayList<Card>(this.thisTurnCast);
this.thisTurnCast.clear();
} }
/** /**