diff --git a/.gitattributes b/.gitattributes index e95c148e8ff..0e91d634b07 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1907,6 +1907,7 @@ res/cardsfolder/c/colossus_of_sardia.txt svneol=native#text/plain res/cardsfolder/c/coma_veil.txt svneol=native#text/plain res/cardsfolder/c/combat_medic.txt svneol=native#text/plain res/cardsfolder/c/combust.txt svneol=native#text/plain +res/cardsfolder/c/comet_storm.txt -text res/cardsfolder/c/command_of_unsummoning.txt svneol=native#text/plain res/cardsfolder/c/commander_eesha.txt svneol=native#text/plain res/cardsfolder/c/commander_greven_il_vec.txt svneol=native#text/plain diff --git a/res/cardsfolder/c/comet_storm.txt b/res/cardsfolder/c/comet_storm.txt new file mode 100644 index 00000000000..9596ffd8525 --- /dev/null +++ b/res/cardsfolder/c/comet_storm.txt @@ -0,0 +1,11 @@ +Name:Comet Storm +ManaCost:X R R +Types:Instant +A:SP$ DealDamage | Cost$ X R R | Announce$ Multikicker,X | ValidTgts$ Creature,Player | TgtPrompt$ Select target creature or player | NumDmg$ X | TargetMin$ TargetsNum | TargetMax$ TargetsNum | References$ X,TargetsNum | SpellDescription$ CARDNAME deals X damage to each target creature and/or player. +K:Multikicker 1 +SVar:TargetsNum:Count$TimesKicked/Plus.1 +SVar:Picture:http://www.wizards.com/global/images/magic/general/comet_storm.jpg +SVar:X:Count$xPaid +Oracle:Multikicker {1} (You may pay an additional {1} any number of times as you cast this spell.)\nChoose target creature or player, then choose another target creature or player for each time Comet Storm was kicked. Comet Storm deals X damage to each of them. +SetInfo:WWK Mythic +SetInfo:CMD Mythic diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index 351303af20a..168a1890016 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -4470,26 +4470,10 @@ public class Card extends GameEntity implements Comparable { public final void addMultiKickerMagnitude(final int n) { this.multiKickerMagnitude += n; } - - /** - *

- * Setter for the field multiKickerMagnitude. - *

- * - * @param n - * a int. - */ public final void setMultiKickerMagnitude(final int n) { this.multiKickerMagnitude = n; } - /** - *

- * Getter for the field multiKickerMagnitude. - *

- * - * @return a int. - */ public final int getMultiKickerMagnitude() { return this.multiKickerMagnitude; } diff --git a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java index eef6eaafcfe..86158d76789 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java @@ -316,7 +316,6 @@ public class CardFactoryCreatures { c.addCounter(CounterType.P1P1, xCounters, true); } }; - spell.setXManaCost(1); // Do not remove SpellAbilities created by AbilityFactory or // Keywords. card.clearFirstSpell(); diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index 98595d32235..891725d07a4 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -47,7 +47,6 @@ import forge.card.ability.ApiType; import forge.card.cost.Cost; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostParser; -import forge.card.mana.ManaCostShard; import forge.card.replacement.ReplacementEffect; import forge.card.replacement.ReplacementHandler; import forge.card.replacement.ReplacementLayer; @@ -2509,12 +2508,6 @@ public class CardFactoryUtil { } } // Suspend - int xCount = card.getManaCost().getShardCount(ManaCostShard.X); - if (xCount > 0) { - final SpellAbility sa = card.getSpellAbility()[0]; - sa.setXManaCost(xCount); - } // X - if (CardFactoryUtil.hasKeyword(card, "Fading") != -1) { final int n = CardFactoryUtil.hasKeyword(card, "Fading"); if (n != -1) { diff --git a/src/main/java/forge/card/cost/CostPartMana.java b/src/main/java/forge/card/cost/CostPartMana.java index f4422e3487c..05327d51dd9 100644 --- a/src/main/java/forge/card/cost/CostPartMana.java +++ b/src/main/java/forge/card/cost/CostPartMana.java @@ -124,6 +124,12 @@ public class CostPartMana extends CostPart { byte xColor = MagicColor.fromName(ability.hasParam("XColor") ? ability.getParam("XColor") : "1"); toPay.increaseShard(ManaCostShard.valueOf(xColor), xCost); } + int timesMultikicked = ability.getSourceCard().getMultiKickerMagnitude(); + if ( timesMultikicked > 0 && ability.isAnnouncing("Multikicker")) { + ManaCost mkCost = ability.getMultiKickerManaCost(); + for(int i = 0; i < timesMultikicked; i++) + toPay.combineManaCost(mkCost); + } if (!toPay.isPaid()) { diff --git a/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java b/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java index 17791afea16..9742dc39b39 100644 --- a/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java +++ b/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java @@ -155,11 +155,20 @@ public class HumanPlaySpellAbility { String announce = ability.getParam("Announce"); if (announce != null) { for(String aVar : announce.split(",")) { - Integer value = ability.getActivatingPlayer().getController().announceRequirements(ability, aVar, ability.getPayCosts().getCostMana().canXbe0()); + String varName = aVar.trim(); + + boolean allowZero = !("X".equalsIgnoreCase(varName)) || ability.getPayCosts().getCostMana().canXbe0(); + + Integer value = ability.getActivatingPlayer().getController().announceRequirements(ability, varName, allowZero); if ( null == value ) return false; - ability.setSVar(aVar, value.toString()); - ability.getSourceCard().setSVar(aVar, value.toString()); + + ability.setSVar(varName, value.toString()); + if( "Multikicker".equals(varName) ) { + ability.getSourceCard().setMultiKickerMagnitude(value); + } else { + ability.getSourceCard().setSVar(varName, value.toString()); + } } } return true; diff --git a/src/main/java/forge/card/spellability/SpellAbility.java b/src/main/java/forge/card/spellability/SpellAbility.java index d3e8468d0f2..6e653839a50 100644 --- a/src/main/java/forge/card/spellability/SpellAbility.java +++ b/src/main/java/forge/card/spellability/SpellAbility.java @@ -57,7 +57,6 @@ public abstract class SpellAbility implements ISpellAbility { private ManaCost manaCost = null; private ManaCost multiKickerManaCost = null; private ManaCost replicateManaCost = null; - private int xManaCost = 0; private Player activatingPlayer = null; private String type = "Intrinsic"; // set to Intrinsic by default @@ -290,29 +289,6 @@ public abstract class SpellAbility implements ISpellAbility { this.replicateManaCost = spellManaCost; } - /** - *

- * Getter for the field xManaCost. - *

- * - * @return a {@link java.lang.String} object. - */ - public int getXManaCost() { - return this.xManaCost; - } - - /** - *

- * Setter for the field xManaCost. - *

- * - * @param cost - * a {@link java.lang.String} object. - */ - public final void setXManaCost(final int cost) { - this.xManaCost = cost; - } - /** *

* Getter for the field activatingPlayer. @@ -396,7 +372,7 @@ public abstract class SpellAbility implements ISpellAbility { * @return a boolean. */ public boolean isMultiKicker() { - return this.multiKickerManaCost != null; + return this.multiKickerManaCost != null && !this.isAnnouncing("Multikicker"); } /** @@ -423,17 +399,6 @@ public abstract class SpellAbility implements ISpellAbility { } - /** - *

- * isXCost. - *

- * - * @return a boolean. - */ - public boolean isXCost() { - return getXManaCost() > 0; - } - /** *

* setIsCycling. @@ -1757,5 +1722,10 @@ public abstract class SpellAbility implements ISpellAbility { return true; } return false; + } + + public boolean isXCost() { + CostPartMana cm = payCosts != null ? getPayCosts().getCostMana() : null; + return cm != null && cm.getAmountOfX() > 0; } } diff --git a/src/main/java/forge/card/trigger/WrappedAbility.java b/src/main/java/forge/card/trigger/WrappedAbility.java index 30770fc53ef..0722ab7054f 100644 --- a/src/main/java/forge/card/trigger/WrappedAbility.java +++ b/src/main/java/forge/card/trigger/WrappedAbility.java @@ -219,11 +219,6 @@ public class WrappedAbility extends Ability implements ISpellAbility { return sa.getTargetPlayer(); } - @Override - public int getXManaCost() { - return sa.getXManaCost(); - } - @Override public boolean isAbility() { return sa.isAbility(); diff --git a/src/main/java/forge/control/input/InputSelectTargets.java b/src/main/java/forge/control/input/InputSelectTargets.java index 70bfe4d5710..629c1cc95cd 100644 --- a/src/main/java/forge/control/input/InputSelectTargets.java +++ b/src/main/java/forge/control/input/InputSelectTargets.java @@ -62,6 +62,11 @@ public final class InputSelectTargets extends InputSyncronizedBase { } //sb.append(tgt.getTargetedString()).append("\n"); sb.append(tgt.getVTSelection()); + + int maxTargets = tgt.getMaxTargets(sa.getSourceCard(), sa); + int targeted = tgt.getNumTargeted(); + if(maxTargets > 1) + sb.append("\n(").append(maxTargets - targeted).append(" more can be targeted)"); showMessage(sb.toString()); diff --git a/src/main/java/forge/game/ai/ComputerUtilMana.java b/src/main/java/forge/game/ai/ComputerUtilMana.java index 343242388d2..bf8effe3422 100644 --- a/src/main/java/forge/game/ai/ComputerUtilMana.java +++ b/src/main/java/forge/game/ai/ComputerUtilMana.java @@ -16,12 +16,14 @@ import forge.Constant; import forge.card.MagicColor; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; +import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.Cost; import forge.card.cost.CostPayment; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostShard; import forge.card.mana.ManaPool; +import forge.card.spellability.Ability; import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; @@ -65,7 +67,7 @@ public class ComputerUtilMana { manapool.clearManaPaid(sa, test); return true; } - + // get map of mana abilities final Map> manaAbilityMap = ComputerUtilMana.mapManaSources(ai, checkPlayable); // initialize ArrayList list for mana needed @@ -237,6 +239,22 @@ public class ComputerUtilMana { } // payManaCost() + // TODO: this code is disconnected now, it was moved here from MagicStack, where X cost is not processed any more + public static void computerPayX(final SpellAbility sa, AIPlayer player, int xCost) { + final int neededDamage = CardFactoryUtil.getNeededXDamage(sa); + final Ability ability = new Ability(sa.getSourceCard(), ManaCost.get(xCost)) { + @Override + public void resolve() { + sa.getSourceCard().addXManaCostPaid(1); + } + }; + + while (ComputerUtilCost.canPayCost(ability, player) && (neededDamage != sa.getSourceCard().getXManaCostPaid())) { + ComputerUtil.playNoStack(player, ability, player.getGame()); + } + + } + /** *

* payManaCost. diff --git a/src/main/java/forge/game/zone/MagicStack.java b/src/main/java/forge/game/zone/MagicStack.java index e14a936792a..c188cb9433f 100644 --- a/src/main/java/forge/game/zone/MagicStack.java +++ b/src/main/java/forge/game/zone/MagicStack.java @@ -45,10 +45,8 @@ import forge.card.spellability.TargetChoices; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerType; import forge.control.input.InputPayManaExecuteCommands; -import forge.control.input.InputPayManaX; import forge.control.input.InputSelectCards; import forge.control.input.InputSelectCardsFromList; -import forge.control.input.InputSynchronized; import forge.game.GameActionUtil; import forge.game.GameState; import forge.game.ai.ComputerUtil; @@ -381,76 +379,37 @@ public class MagicStack extends MyObservable { } if (sp.getSourceCard().isCopiedSpell()) { this.push(sp); - } else if (!sp.isMultiKicker() && !sp.isReplicate() && !sp.isXCost()) { + } else if (!sp.isMultiKicker() && !sp.isReplicate()) { this.push(sp); - } else if ((sp.getPayCosts() != null) && !sp.isMultiKicker() && !sp.isReplicate()) { - this.push(sp); - } else if (sp.isXCost()) { - // TODO: convert any X costs to use abCost so it happens earlier - final SpellAbility sa = sp; - final int xCost = sa.getXManaCost(); - Player player = sp.getSourceCard().getController(); - if (player.isHuman()) { - InputSynchronized inp = new InputPayManaX(sa, xCost, true); - FThreads.setInputAndWait(inp); - MagicStack.this.push(sa); - } else { - // computer - final int neededDamage = CardFactoryUtil.getNeededXDamage(sa); - final Ability ability = new Ability(sp.getSourceCard(), ManaCost.get(xCost)) { - @Override - public void resolve() { - final Card crd = this.getSourceCard(); - crd.addXManaCostPaid(1); - } - }; - - while (ComputerUtilCost.canPayCost(ability, player) && (neededDamage != sa.getSourceCard().getXManaCostPaid())) { - ComputerUtil.playNoStack((AIPlayer)player, ability, game); - } - this.push(sa); - } } else if (sp.isMultiKicker()) { - // TODO: convert multikicker support in abCost so this doesn't - // happen here - // both X and multi is not supported yet - final SpellAbility sa = sp; - final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) { - @Override - public void resolve() { - this.getSourceCard().addMultiKickerMagnitude(1); - } - }; - final Player activating = sp.getActivatingPlayer(); if (activating.isHuman()) { - sa.getSourceCard().addMultiKickerMagnitude(-1); - final Runnable paidCommand = new Runnable() { - @Override - public void run() { - abilityIncreaseMultikicker.resolve(); - int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude(); - String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude ); - InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(activating, prompt, sp.getMultiKickerManaCost()); - FThreads.setInputAndWait(toSet); - if ( toSet.isPaid() ) { - this.run(); - } else - MagicStack.this.push(sa); - } - }; - paidCommand.run(); + while(true) { + int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude(); + String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude ); + InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(activating, prompt, sp.getMultiKickerManaCost()); + FThreads.setInputAndWait(toSet); + if ( !toSet.isPaid() ) + break; + + sa.getSourceCard().addMultiKickerMagnitude(1); + } } else { // computer + final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) { + @Override + public void resolve() { + this.getSourceCard().addMultiKickerMagnitude(1); + } + }; while (ComputerUtilCost.canPayCost(abilityIncreaseMultikicker, activating)) { ComputerUtil.playNoStack((AIPlayer)activating, abilityIncreaseMultikicker, game); } - - this.push(sa); } + this.push(sa); } else if (sp.isReplicate()) { // TODO: convert multikicker/replicate support in abCost so this // doesn't happen here