mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
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:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
11
res/cardsfolder/c/comet_storm.txt
Normal file
11
res/cardsfolder/c/comet_storm.txt
Normal 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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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,76 +379,37 @@ 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()) {
|
|
||||||
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()) {
|
} 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 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();
|
final Player activating = sp.getActivatingPlayer();
|
||||||
|
|
||||||
if (activating.isHuman()) {
|
if (activating.isHuman()) {
|
||||||
sa.getSourceCard().addMultiKickerMagnitude(-1);
|
while(true) {
|
||||||
final Runnable paidCommand = new Runnable() {
|
int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude();
|
||||||
@Override
|
String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude );
|
||||||
public void run() {
|
InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(activating, prompt, sp.getMultiKickerManaCost());
|
||||||
abilityIncreaseMultikicker.resolve();
|
FThreads.setInputAndWait(toSet);
|
||||||
int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude();
|
if ( !toSet.isPaid() )
|
||||||
String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude );
|
break;
|
||||||
InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(activating, prompt, sp.getMultiKickerManaCost());
|
|
||||||
FThreads.setInputAndWait(toSet);
|
sa.getSourceCard().addMultiKickerMagnitude(1);
|
||||||
if ( toSet.isPaid() ) {
|
}
|
||||||
this.run();
|
|
||||||
} else
|
|
||||||
MagicStack.this.push(sa);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
paidCommand.run();
|
|
||||||
} else {
|
} else {
|
||||||
// computer
|
// computer
|
||||||
|
final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) {
|
||||||
|
@Override
|
||||||
|
public void resolve() {
|
||||||
|
this.getSourceCard().addMultiKickerMagnitude(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user