simultaneous announces for X and multikicker work now.

Comet storm scripted, Strength of Tajuru scriptable.
possible side effect: AI can may become unable to cast spells with X in their cost. (see ComputerUtilMana.java:242)
This commit is contained in:
Maxmtg
2013-04-03 20:59:31 +00:00
parent d4d19f4f65
commit e3eb4dd972
12 changed files with 78 additions and 128 deletions

1
.gitattributes vendored
View File

@@ -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/coma_veil.txt svneol=native#text/plain
res/cardsfolder/c/combat_medic.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/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/command_of_unsummoning.txt svneol=native#text/plain
res/cardsfolder/c/commander_eesha.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 res/cardsfolder/c/commander_greven_il_vec.txt svneol=native#text/plain

View File

@@ -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

View File

@@ -4470,26 +4470,10 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addMultiKickerMagnitude(final int n) { public final void addMultiKickerMagnitude(final int n) {
this.multiKickerMagnitude += n; this.multiKickerMagnitude += n;
} }
/**
* <p>
* Setter for the field <code>multiKickerMagnitude</code>.
* </p>
*
* @param n
* a int.
*/
public final void setMultiKickerMagnitude(final int n) { public final void setMultiKickerMagnitude(final int n) {
this.multiKickerMagnitude = n; this.multiKickerMagnitude = n;
} }
/**
* <p>
* Getter for the field <code>multiKickerMagnitude</code>.
* </p>
*
* @return a int.
*/
public final int getMultiKickerMagnitude() { public final int getMultiKickerMagnitude() {
return this.multiKickerMagnitude; return this.multiKickerMagnitude;
} }

View File

@@ -316,7 +316,6 @@ public class CardFactoryCreatures {
c.addCounter(CounterType.P1P1, xCounters, true); c.addCounter(CounterType.P1P1, xCounters, true);
} }
}; };
spell.setXManaCost(1);
// Do not remove SpellAbilities created by AbilityFactory or // Do not remove SpellAbilities created by AbilityFactory or
// Keywords. // Keywords.
card.clearFirstSpell(); card.clearFirstSpell();

View File

@@ -47,7 +47,6 @@ import forge.card.ability.ApiType;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser; import forge.card.mana.ManaCostParser;
import forge.card.mana.ManaCostShard;
import forge.card.replacement.ReplacementEffect; import forge.card.replacement.ReplacementEffect;
import forge.card.replacement.ReplacementHandler; import forge.card.replacement.ReplacementHandler;
import forge.card.replacement.ReplacementLayer; import forge.card.replacement.ReplacementLayer;
@@ -2509,12 +2508,6 @@ public class CardFactoryUtil {
} }
} // Suspend } // 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) { if (CardFactoryUtil.hasKeyword(card, "Fading") != -1) {
final int n = CardFactoryUtil.hasKeyword(card, "Fading"); final int n = CardFactoryUtil.hasKeyword(card, "Fading");
if (n != -1) { if (n != -1) {

View File

@@ -124,6 +124,12 @@ public class CostPartMana extends CostPart {
byte xColor = MagicColor.fromName(ability.hasParam("XColor") ? ability.getParam("XColor") : "1"); byte xColor = MagicColor.fromName(ability.hasParam("XColor") ? ability.getParam("XColor") : "1");
toPay.increaseShard(ManaCostShard.valueOf(xColor), xCost); 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()) { if (!toPay.isPaid()) {

View File

@@ -155,11 +155,20 @@ public class HumanPlaySpellAbility {
String announce = ability.getParam("Announce"); String announce = ability.getParam("Announce");
if (announce != null) { if (announce != null) {
for(String aVar : announce.split(",")) { 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 ) if ( null == value )
return false; 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; return true;

View File

@@ -57,7 +57,6 @@ public abstract class SpellAbility implements ISpellAbility {
private ManaCost manaCost = null; private ManaCost manaCost = null;
private ManaCost multiKickerManaCost = null; private ManaCost multiKickerManaCost = null;
private ManaCost replicateManaCost = null; private ManaCost replicateManaCost = null;
private int xManaCost = 0;
private Player activatingPlayer = null; private Player activatingPlayer = null;
private String type = "Intrinsic"; // set to Intrinsic by default private String type = "Intrinsic"; // set to Intrinsic by default
@@ -290,29 +289,6 @@ public abstract class SpellAbility implements ISpellAbility {
this.replicateManaCost = spellManaCost; this.replicateManaCost = spellManaCost;
} }
/**
* <p>
* Getter for the field <code>xManaCost</code>.
* </p>
*
* @return a {@link java.lang.String} object.
*/
public int getXManaCost() {
return this.xManaCost;
}
/**
* <p>
* Setter for the field <code>xManaCost</code>.
* </p>
*
* @param cost
* a {@link java.lang.String} object.
*/
public final void setXManaCost(final int cost) {
this.xManaCost = cost;
}
/** /**
* <p> * <p>
* Getter for the field <code>activatingPlayer</code>. * Getter for the field <code>activatingPlayer</code>.
@@ -396,7 +372,7 @@ public abstract class SpellAbility implements ISpellAbility {
* @return a boolean. * @return a boolean.
*/ */
public boolean isMultiKicker() { public boolean isMultiKicker() {
return this.multiKickerManaCost != null; return this.multiKickerManaCost != null && !this.isAnnouncing("Multikicker");
} }
/** /**
@@ -423,17 +399,6 @@ public abstract class SpellAbility implements ISpellAbility {
} }
/**
* <p>
* isXCost.
* </p>
*
* @return a boolean.
*/
public boolean isXCost() {
return getXManaCost() > 0;
}
/** /**
* <p> * <p>
* setIsCycling. * setIsCycling.
@@ -1758,4 +1723,9 @@ public abstract class SpellAbility implements ISpellAbility {
} }
return false; return false;
} }
public boolean isXCost() {
CostPartMana cm = payCosts != null ? getPayCosts().getCostMana() : null;
return cm != null && cm.getAmountOfX() > 0;
}
} }

View File

@@ -219,11 +219,6 @@ public class WrappedAbility extends Ability implements ISpellAbility {
return sa.getTargetPlayer(); return sa.getTargetPlayer();
} }
@Override
public int getXManaCost() {
return sa.getXManaCost();
}
@Override @Override
public boolean isAbility() { public boolean isAbility() {
return sa.isAbility(); return sa.isAbility();

View File

@@ -63,6 +63,11 @@ public final class InputSelectTargets extends InputSyncronizedBase {
//sb.append(tgt.getTargetedString()).append("\n"); //sb.append(tgt.getTargetedString()).append("\n");
sb.append(tgt.getVTSelection()); 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()); showMessage(sb.toString());
// If reached Minimum targets, enable OK button // If reached Minimum targets, enable OK button

View File

@@ -16,12 +16,14 @@ import forge.Constant;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.cost.CostPayment; import forge.card.cost.CostPayment;
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.mana.ManaCostShard;
import forge.card.mana.ManaPool; import forge.card.mana.ManaPool;
import forge.card.spellability.Ability;
import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -237,6 +239,22 @@ public class ComputerUtilMana {
} // payManaCost() } // 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());
}
}
/** /**
* <p> * <p>
* payManaCost. * payManaCost.

View File

@@ -45,10 +45,8 @@ import forge.card.spellability.TargetChoices;
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;
import forge.control.input.InputPayManaX;
import forge.control.input.InputSelectCards; import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectCardsFromList; import forge.control.input.InputSelectCardsFromList;
import forge.control.input.InputSynchronized;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
@@ -381,41 +379,25 @@ public class MagicStack extends MyObservable {
} }
if (sp.getSourceCard().isCopiedSpell()) { if (sp.getSourceCard().isCopiedSpell()) {
this.push(sp); this.push(sp);
} else if (!sp.isMultiKicker() && !sp.isReplicate() && !sp.isXCost()) { } else if (!sp.isMultiKicker() && !sp.isReplicate()) {
this.push(sp); this.push(sp);
} else if ((sp.getPayCosts() != null) && !sp.isMultiKicker() && !sp.isReplicate()) { } else if (sp.isMultiKicker()) {
this.push(sp);
} else if (sp.isXCost()) {
// TODO: convert any X costs to use abCost so it happens earlier
final SpellAbility sa = sp; final SpellAbility sa = sp;
final int xCost = sa.getXManaCost(); final Player activating = sp.getActivatingPlayer();
Player player = sp.getSourceCard().getController();
if (player.isHuman()) { if (activating.isHuman()) {
InputSynchronized inp = new InputPayManaX(sa, xCost, true); while(true) {
FThreads.setInputAndWait(inp); int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude();
MagicStack.this.push(sa); 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 { } else {
// computer // 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()) { final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) {
@Override @Override
public void resolve() { public void resolve() {
@@ -423,34 +405,11 @@ public class MagicStack extends MyObservable {
} }
}; };
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();
} else {
// computer
while (ComputerUtilCost.canPayCost(abilityIncreaseMultikicker, activating)) { while (ComputerUtilCost.canPayCost(abilityIncreaseMultikicker, activating)) {
ComputerUtil.playNoStack((AIPlayer)activating, abilityIncreaseMultikicker, game); ComputerUtil.playNoStack((AIPlayer)activating, abilityIncreaseMultikicker, game);
} }
this.push(sa);
} }
this.push(sa);
} else if (sp.isReplicate()) { } else if (sp.isReplicate()) {
// TODO: convert multikicker/replicate support in abCost so this // TODO: convert multikicker/replicate support in abCost so this
// doesn't happen here // doesn't happen here