mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Merge branch 'replicateConspire' into 'master'
Replicate rework Closes #1068 See merge request core-developers/forge!1929
This commit is contained in:
@@ -99,6 +99,8 @@ public class ComputerUtil {
|
||||
sa.resetPaidHash();
|
||||
}
|
||||
|
||||
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
CharmEffect.makeChoices(sa);
|
||||
}
|
||||
@@ -208,9 +210,9 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// this is used for AI's counterspells
|
||||
public static final boolean playStack(final SpellAbility sa, final Player ai, final Game game) {
|
||||
public static final boolean playStack(SpellAbility sa, final Player ai, final Game game) {
|
||||
sa.setActivatingPlayer(ai);
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai))
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai))
|
||||
return false;
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
@@ -220,6 +222,9 @@ public class ComputerUtil {
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||
|
||||
final Cost cost = sa.getPayCosts();
|
||||
if (cost == null) {
|
||||
ComputerUtilMana.payManaCost(ai, sa);
|
||||
@@ -249,13 +254,15 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
public static final boolean playSpellAbilityWithoutPayingManaCost(final Player ai, final SpellAbility sa, final Game game) {
|
||||
final SpellAbility newSA = sa.copyWithNoManaCost();
|
||||
SpellAbility newSA = sa.copyWithNoManaCost();
|
||||
newSA.setActivatingPlayer(ai);
|
||||
|
||||
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
newSA = GameActionUtil.addExtraKeywordCost(newSA);
|
||||
|
||||
final Card source = newSA.getHostCard();
|
||||
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(newSA);
|
||||
@@ -275,7 +282,7 @@ public class ComputerUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final void playNoStack(final Player ai, final SpellAbility sa, final Game game) {
|
||||
public static final void playNoStack(final Player ai, SpellAbility sa, final Game game) {
|
||||
sa.setActivatingPlayer(ai);
|
||||
// TODO: We should really restrict what doesn't use the Stack
|
||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
@@ -287,6 +294,8 @@ public class ComputerUtil {
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||
|
||||
final Cost cost = sa.getPayCosts();
|
||||
if (cost == null) {
|
||||
ComputerUtilMana.payManaCost(ai, sa);
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.mana.ManaConversionMatrix;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
@@ -1253,4 +1254,25 @@ public class PlayerControllerAi extends PlayerController {
|
||||
// Always true?
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt,
|
||||
int max) {
|
||||
// TODO: improve the logic depending on the keyword and the playability of the cost-modified SA (enough targets present etc.)
|
||||
int chosenAmount = 0;
|
||||
|
||||
Cost costSoFar = sa.getPayCosts() != null ? sa.getPayCosts().copy() : Cost.Zero;
|
||||
|
||||
for (int i = 0; i < max; i++) {
|
||||
costSoFar.add(cost);
|
||||
SpellAbility fullCostSa = sa.copyWithDefinedCost(costSoFar);
|
||||
if (ComputerUtilCost.canPayCost(fullCostSa, player)) {
|
||||
chosenAmount++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return chosenAmount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ import forge.game.card.CardPlayOption.PayManaCost;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -250,11 +252,6 @@ public final class GameActionUtil {
|
||||
if (keyword.startsWith("Buyback")) {
|
||||
final Cost cost = new Cost(keyword.substring(8), false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Buyback, cost));
|
||||
} else if (keyword.equals("Conspire")) {
|
||||
final String conspireCost = "tapXType<2/Creature.SharesColorWith/" +
|
||||
"untapped creature you control that shares a color with " + source.getName() + ">";
|
||||
final Cost cost = new Cost(conspireCost, false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Conspire, cost));
|
||||
} else if (keyword.startsWith("Entwine")) {
|
||||
String[] k = keyword.split(":");
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
@@ -301,15 +298,11 @@ public final class GameActionUtil {
|
||||
}
|
||||
final SpellAbility result = sa.copy();
|
||||
for (OptionalCostValue v : list) {
|
||||
// need to copy cost, otherwise it does alter the original
|
||||
result.setPayCosts(result.getPayCosts().copy().add(v.getCost()));
|
||||
result.getPayCosts().add(v.getCost());
|
||||
result.addOptionalCost(v.getType());
|
||||
|
||||
// add some extra logic, try to move it to other parts
|
||||
switch (v.getType()) {
|
||||
case Conspire:
|
||||
result.addConspireInstance();
|
||||
break;
|
||||
case Retrace:
|
||||
case Jumpstart:
|
||||
result.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
@@ -363,6 +356,71 @@ public final class GameActionUtil {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
public static SpellAbility addExtraKeywordCost(final SpellAbility sa) {
|
||||
if (!sa.isSpell() || sa.isCopied()) {
|
||||
return sa;
|
||||
}
|
||||
SpellAbility result = null;
|
||||
final Card host = sa.getHostCard();
|
||||
final PlayerController pc = sa.getActivatingPlayer().getController();
|
||||
|
||||
host.getGame().getAction().checkStaticAbilities(false);
|
||||
|
||||
boolean reset = false;
|
||||
|
||||
for (KeywordInterface ki : host.getKeywords()) {
|
||||
final String o = ki.getOriginal();
|
||||
if (o.equals("Conspire")) {
|
||||
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
|
||||
if (tr != null) {
|
||||
final String conspireCost = "tapXType<2/Creature.SharesColorWith/" +
|
||||
"untapped creature you control that shares a color with " + host.getName() + ">";
|
||||
final Cost cost = new Cost(conspireCost, false);
|
||||
String str = "Pay for Conspire? " + cost.toSimpleString();
|
||||
|
||||
boolean v = pc.addKeywordCost(sa, cost, ki, str);
|
||||
tr.setSVar("Conspire", v ? "1" : "0");
|
||||
|
||||
if (v) {
|
||||
if (result == null) {
|
||||
result = sa.copy();
|
||||
}
|
||||
result.getPayCosts().add(cost);
|
||||
reset = true;
|
||||
}
|
||||
}
|
||||
} else if (o.startsWith("Replicate")) {
|
||||
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
|
||||
if (tr != null) {
|
||||
String costStr = o.split(":")[1];
|
||||
final Cost cost = new Cost(costStr, false);
|
||||
|
||||
String str = "Choose Amount for Replicate: " + cost.toSimpleString();
|
||||
|
||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||
|
||||
tr.setSVar("ReplicateAmount", String.valueOf(v));
|
||||
tr.getOverridingAbility().setSVar("ReplicateAmount", String.valueOf(v));
|
||||
|
||||
for (int i = 0; i < v; i++) {
|
||||
if (result == null) {
|
||||
result = sa.copy();
|
||||
}
|
||||
result.getPayCosts().add(cost);
|
||||
reset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset active Trigger
|
||||
if (reset) {
|
||||
host.getGame().getTriggerHandler().resetActiveTriggers(false);
|
||||
}
|
||||
|
||||
return result != null ? result : sa;
|
||||
}
|
||||
|
||||
private static boolean hasUrzaLands(final Player p) {
|
||||
final CardCollectionView landsControlled = p.getCardsIn(ZoneType.Battlefield);
|
||||
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine")))
|
||||
|
||||
@@ -2361,11 +2361,12 @@ public class CardFactoryUtil {
|
||||
inst.addTrigger(parsedTrigger);
|
||||
inst.addTrigger(parsedTrigReturn);
|
||||
} else if (keyword.equals("Conspire")) {
|
||||
final String trigScript = "Mode$ SpellCast | ValidCard$ Card.Self | Conspire$ True | Secondary$ True | TriggerDescription$ Copy CARDNAME if its conspire cost was paid";
|
||||
final String trigScript = "Mode$ SpellCast | ValidCard$ Card.Self | CheckSVar$ Conspire | Secondary$ True | TriggerDescription$ Copy CARDNAME if its conspire cost was paid";
|
||||
final String abString = "DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Amount$ 1";
|
||||
|
||||
final Trigger conspireTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
|
||||
conspireTrigger.setOverridingAbility(AbilityFactory.getAbility(abString, card));
|
||||
conspireTrigger.setSVar("Conspire", "0");
|
||||
inst.addTrigger(conspireTrigger);
|
||||
} else if (keyword.startsWith("Cumulative upkeep")) {
|
||||
final String[] k = keyword.split(":");
|
||||
@@ -2942,6 +2943,17 @@ public class CardFactoryUtil {
|
||||
+ " exile CARDNAME. | Secondary$ True";
|
||||
final Trigger myTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||
inst.addTrigger(myTrigger);
|
||||
} else if (keyword.startsWith("Replicate")) {
|
||||
final String trigScript = "Mode$ SpellCast | ValidCard$ Card.Self | CheckSVar$ ReplicateAmount | Secondary$ True | TriggerDescription$ Copy CARDNAME for each time you paid its replicate cost";
|
||||
final String abString = "DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Amount$ ReplicateAmount";
|
||||
|
||||
final Trigger replicateTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
|
||||
final SpellAbility replicateAbility = AbilityFactory.getAbility(abString, card);
|
||||
replicateAbility.setSVar("ReplicateAmount", "0");
|
||||
replicateTrigger.setOverridingAbility(replicateAbility);
|
||||
replicateTrigger.setSVar("ReplicateAmount", "0");
|
||||
inst.addTrigger(replicateTrigger);
|
||||
|
||||
} else if (keyword.startsWith("Ripple")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String num = k[1];
|
||||
@@ -4225,9 +4237,6 @@ public class CardFactoryUtil {
|
||||
|
||||
sa.setTemporary(!intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
|
||||
} else if (keyword.startsWith("Replicate")) {
|
||||
card.getFirstSpellAbility().addAnnounceVar("Replicate");
|
||||
} else if (keyword.startsWith("Scavenge")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String manacost = k[1];
|
||||
|
||||
@@ -19,6 +19,7 @@ import forge.game.combat.Combat;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.mana.ManaConversionMatrix;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
@@ -47,7 +48,6 @@ public abstract class PlayerController {
|
||||
DeclareBlocker,
|
||||
Echo,
|
||||
Multikicker,
|
||||
Replicate,
|
||||
CumulativeUpkeep,
|
||||
}
|
||||
|
||||
@@ -182,6 +182,11 @@ public abstract class PlayerController {
|
||||
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
||||
public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose);
|
||||
|
||||
public abstract int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max);
|
||||
public boolean addKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt) {
|
||||
return chooseNumberForKeywordCost(sa, cost, keyword, prompt, 1) == 1;
|
||||
}
|
||||
|
||||
public abstract int chooseNumber(SpellAbility sa, String title, int min, int max);
|
||||
public abstract int chooseNumber(SpellAbility sa, String title, List<Integer> values, Player relatedPlayer);
|
||||
public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) {
|
||||
|
||||
@@ -5,7 +5,6 @@ package forge.game.spellability;
|
||||
*
|
||||
*/
|
||||
public enum OptionalCost {
|
||||
Conspire("Conspire"),
|
||||
Buyback("Buyback"),
|
||||
Entwine("Entwine"),
|
||||
Kicker1("Kicker"),
|
||||
|
||||
@@ -144,7 +144,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
private CardCollection tappedForConvoke = new CardCollection();
|
||||
private Card sacrificedAsOffering = null;
|
||||
private Card sacrificedAsEmerge = null;
|
||||
private int conspireInstances = 0;
|
||||
|
||||
private AbilityManaPart manaPart = null;
|
||||
|
||||
@@ -301,9 +300,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean canPlayWithOptionalCost(OptionalCostValue opt) {
|
||||
SpellAbility saCopy = this.copy();
|
||||
saCopy = GameActionUtil.addOptionalCosts(saCopy, Lists.newArrayList(opt));
|
||||
return saCopy.canPlay();
|
||||
return GameActionUtil.addOptionalCosts(this, Lists.newArrayList(opt)).canPlay();
|
||||
}
|
||||
|
||||
public boolean isPossible() {
|
||||
@@ -1700,19 +1697,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return ForgeScript.spellAbilityHasProperty(this, property, sourceController, source, spellAbility);
|
||||
}
|
||||
|
||||
// Methods enabling multiple instances of conspire
|
||||
public void addConspireInstance() {
|
||||
conspireInstances++;
|
||||
}
|
||||
|
||||
public void subtractConspireInstance() {
|
||||
conspireInstances--;
|
||||
}
|
||||
|
||||
public int getConspireInstances() {
|
||||
return conspireInstances;
|
||||
} // End of Conspire methods
|
||||
|
||||
public boolean isCumulativeupkeep() {
|
||||
return cumulativeupkeep;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import forge.game.card.CardLists;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
@@ -216,18 +215,6 @@ public class TriggerSpellAbilityCast extends Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("Conspire")) {
|
||||
if (!spellAbility.isOptionalCostPaid(OptionalCost.Conspire)) {
|
||||
return false;
|
||||
}
|
||||
if (spellAbility.getConspireInstances() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
spellAbility.subtractConspireInstance();
|
||||
//System.out.println("Conspire instances left = " + spellAbility.getConspireInstances());
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("Outlast")) {
|
||||
if (!spellAbility.isOutlast()) {
|
||||
return false;
|
||||
|
||||
@@ -38,7 +38,6 @@ import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameLogEntryType;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
@@ -58,7 +57,6 @@ import forge.game.player.PlayerController.ManaPaymentPurpose;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.AbilityStatic;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.Spell;
|
||||
@@ -319,35 +317,6 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
|
||||
// The ability is added to stack HERE
|
||||
si = push(sp);
|
||||
|
||||
if (sp.isSpell() && (source.hasStartOfKeyword("Replicate")
|
||||
|| ((source.isInstant() || source.isSorcery()) && Iterables.any(activator.getCardsIn(ZoneType.Battlefield),
|
||||
CardPredicates.hasKeyword("Each instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost."))))) {
|
||||
Integer magnitude = sp.getSVarInt("Replicate");
|
||||
if (magnitude == null) {
|
||||
magnitude = 0;
|
||||
final Cost costReplicate = new Cost(source.getManaCost(), false);
|
||||
boolean hasPaid = false;
|
||||
int replicateCMC = source.getManaCost().getCMC();
|
||||
do {
|
||||
String prompt = TextUtil.concatWithSpace("Replicate for", source.toString(),"\r\nTimes Replicated:", magnitude.toString(),"\r\n");
|
||||
hasPaid = activator.getController().payManaOptional(source, costReplicate, sp, prompt, ManaPaymentPurpose.Replicate);
|
||||
if (hasPaid) {
|
||||
magnitude++;
|
||||
totManaSpent += replicateCMC;
|
||||
}
|
||||
} while (hasPaid);
|
||||
}
|
||||
|
||||
// Replicate Trigger
|
||||
String effect = TextUtil.concatWithSpace("DB$ CopySpellAbility | Cost$ 0 | Defined$ Parent | Amount$", magnitude.toString());
|
||||
AbilitySub sa = (AbilitySub) AbilityFactory.getAbility(effect, source);
|
||||
sa.setParent(sp);
|
||||
sa.setDescription("Replicate - " + source);
|
||||
sa.setTrigger(true);
|
||||
sa.setCopied(true);
|
||||
addSimultaneousStackEntry(sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.mana.ManaConversionMatrix;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
@@ -695,4 +696,11 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt,
|
||||
int max) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:5 UR UR
|
||||
Types:Creature Djinn
|
||||
PT:3/5
|
||||
K:Flying
|
||||
K:Each instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost.
|
||||
S:Mode$ Continuous | AddKeyword$ Replicate:CardManaCost | Affected$ Instant.YouCtrl,Sorcery.YouCtrl | AffectedZone$ Stack | EffectZone$ Battlefield | Description$ Each instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost. (When you cast it, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/djinn_illuminatus.jpg
|
||||
Oracle:Flying\nEach instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost. (When you cast it, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Stream of Thought
|
||||
ManaCost:U
|
||||
Types:Sorcery
|
||||
A:SP$ Mill | Cost$ U | NumCards$ 4 | ValidTgts$ Player | TgtPrompt$ Choose a player | RememberMilled$ True | SubAbility$ DBChangeZone | SpellDescription$ Target player puts the top four cards of their library into their graveyard. You shuffle up to four cards from your graveyard into your library.
|
||||
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Library | DefinedPlayer$ You | Hidden$ True | ChangeNum$ 4 | ChangeType$ Card.YouOwn | Shuffle$ True
|
||||
K:Replicate:2 U U
|
||||
A:SP$ Mill | Cost$ U | NumCards$ 4 | ValidTgts$ Player | TgtPrompt$ Choose a player | SubAbility$ DBChangeZone | SpellDescription$ Target player puts the top four cards of their library into their graveyard. You shuffle up to four cards from your graveyard into your library.
|
||||
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Library | DefinedPlayer$ You | Hidden$ True | ChangeNum$ 4 | ChangeType$ Card.YouOwn | Shuffle$ True
|
||||
Oracle:Target player puts the top four cards of their library into their graveyard. You shuffle up to four cards from your graveyard into your library.\nReplicate {2}{U}{U} (When you cast this spell, copy it for each time you paid the replicate cost. You may choose new targets for the copies.)
|
||||
|
||||
@@ -4,6 +4,6 @@ Types:Legendary Creature Goblin Shaman
|
||||
PT:3/3
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ WortETB | TriggerDescription$ When CARDNAME enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens.
|
||||
SVar:WortETB:DB$ Token | TokenAmount$ 2 | TokenScript$ rg_1_1_goblin_warrior | TokenOwner$ You
|
||||
S:Mode$ Continuous | AddKeyword$ Conspire | Affected$ Instant.Green+YouCtrl,Instant.Red+YouCtrl,Sorcery.Red+YouCtrl,Sorcery.Green+YouCtrl | AffectedZone$ Stack,Graveyard,Hand,Library,Exile | EffectZone$ Battlefield | Description$ Each red or green instant or sorcery spell you cast has conspire. (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)
|
||||
S:Mode$ Continuous | AddKeyword$ Conspire | Affected$ Instant.Green+YouCtrl,Instant.Red+YouCtrl,Sorcery.Red+YouCtrl,Sorcery.Green+YouCtrl | AffectedZone$ Stack | EffectZone$ Battlefield | Description$ Each red or green instant or sorcery spell you cast has conspire. (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/wort_the_raidmother.jpg
|
||||
Oracle:When Wort, the Raidmother enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens.\nEach red or green instant or sorcery spell you cast has conspire. (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)
|
||||
|
||||
@@ -103,10 +103,8 @@ public class HumanPlay {
|
||||
|
||||
// System.out.println("Playing:" + sa.getDescription() + " of " + sa.getHostCard() + " new = " + newAbility);
|
||||
if (newAbility) {
|
||||
Cost abCost = sa.getPayCosts() == null ? new Cost("0", sa.isAbility()) : sa.getPayCosts();
|
||||
CostPayment payment = new CostPayment(abCost, sa);
|
||||
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa, payment);
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
if (!req.playAbility(true, false, false)) {
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
source.turnFaceDown(true);
|
||||
@@ -199,9 +197,8 @@ public class HumanPlay {
|
||||
}
|
||||
sa = AbilityUtils.addSpliceEffects(sa);
|
||||
}
|
||||
final CostPayment payment = new CostPayment(sa.getPayCosts(), sa);
|
||||
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa, payment);
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
req.playAbility(mayChooseNewTargets, true, false);
|
||||
}
|
||||
else {
|
||||
@@ -231,7 +228,7 @@ public class HumanPlay {
|
||||
sa.setActivatingPlayer(player);
|
||||
|
||||
if (sa.getPayCosts() != null) {
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa, new CostPayment(sa.getPayCosts(), sa));
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
|
||||
req.playAbility(!useOldTargets, false, true);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import forge.card.CardStateName;
|
||||
import forge.card.CardType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
@@ -53,13 +54,11 @@ import java.util.Map;
|
||||
*/
|
||||
public class HumanPlaySpellAbility {
|
||||
private final PlayerControllerHuman controller;
|
||||
private final SpellAbility ability;
|
||||
private final CostPayment payment;
|
||||
private SpellAbility ability;
|
||||
|
||||
public HumanPlaySpellAbility(final PlayerControllerHuman controller0, final SpellAbility ability0, final CostPayment payment0) {
|
||||
public HumanPlaySpellAbility(final PlayerControllerHuman controller0, final SpellAbility ability0) {
|
||||
controller = controller0;
|
||||
ability = ability0;
|
||||
payment = payment0;
|
||||
}
|
||||
|
||||
public final boolean playAbility(final boolean mayChooseTargets, final boolean isFree, final boolean skipStack) {
|
||||
@@ -116,6 +115,11 @@ public class HumanPlaySpellAbility {
|
||||
ability.resetPaidHash();
|
||||
}
|
||||
|
||||
ability = GameActionUtil.addExtraKeywordCost(ability);
|
||||
|
||||
Cost abCost = ability.getPayCosts() == null ? new Cost("0", ability.isAbility()) : ability.getPayCosts();
|
||||
CostPayment payment = new CostPayment(abCost, ability);
|
||||
|
||||
// TODO Apply this to the SAStackInstance instead of the Player
|
||||
if (manaTypeConversion) {
|
||||
AbilityUtils.applyManaColorConversion(payment, MagicColor.Constant.ANY_TYPE_CONVERSION);
|
||||
@@ -155,7 +159,7 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
if (!prerequisitesMet) {
|
||||
if (!ability.isTrigger()) {
|
||||
rollbackAbility(fromZone, fromState, zonePosition);
|
||||
rollbackAbility(fromZone, fromState, zonePosition, payment);
|
||||
if (ability.getHostCard().isMadness()) {
|
||||
// if a player failed to play madness cost, move the card to graveyard
|
||||
Card newCard = game.getAction().moveToGraveyard(c, null);
|
||||
@@ -240,7 +244,7 @@ public class HumanPlaySpellAbility {
|
||||
}
|
||||
}
|
||||
|
||||
private void rollbackAbility(final Zone fromZone, final CardStateName fromState, final int zonePosition) {
|
||||
private void rollbackAbility(final Zone fromZone, final CardStateName fromState, final int zonePosition, CostPayment payment) {
|
||||
// cancel ability during target choosing
|
||||
final Game game = ability.getActivatingPlayer().getGame();
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.mana.ManaConversionMatrix;
|
||||
import forge.game.player.*;
|
||||
@@ -2930,5 +2931,19 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
return InputConfirm.confirm(this, (SpellAbility)null, "Do you want to scry?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt,
|
||||
int max) {
|
||||
if (max <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (max == 1) {
|
||||
return InputConfirm.confirm(this, sa, prompt) ? 1 : 0;
|
||||
}
|
||||
|
||||
Integer v = getGui().getInteger(prompt, 0, max, 9);
|
||||
return v == null ? 0 : v.intValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user