mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28: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.resetPaidHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||||
|
|
||||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||||
CharmEffect.makeChoices(sa);
|
CharmEffect.makeChoices(sa);
|
||||||
}
|
}
|
||||||
@@ -208,9 +210,9 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this is used for AI's counterspells
|
// 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);
|
sa.setActivatingPlayer(ai);
|
||||||
if (!ComputerUtilCost.canPayCost(sa, ai))
|
if (!ComputerUtilCost.canPayCost(sa, ai))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
@@ -220,6 +222,9 @@ public class ComputerUtil {
|
|||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
if (cost == null) {
|
if (cost == null) {
|
||||||
ComputerUtilMana.payManaCost(ai, sa);
|
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) {
|
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);
|
newSA.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA)) {
|
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newSA = GameActionUtil.addExtraKeywordCost(newSA);
|
||||||
|
|
||||||
final Card source = newSA.getHostCard();
|
final Card source = newSA.getHostCard();
|
||||||
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
||||||
source.setCastSA(newSA);
|
source.setCastSA(newSA);
|
||||||
@@ -275,7 +282,7 @@ public class ComputerUtil {
|
|||||||
return true;
|
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);
|
sa.setActivatingPlayer(ai);
|
||||||
// TODO: We should really restrict what doesn't use the Stack
|
// TODO: We should really restrict what doesn't use the Stack
|
||||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||||
@@ -287,6 +294,8 @@ public class ComputerUtil {
|
|||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sa = GameActionUtil.addExtraKeywordCost(sa);
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
if (cost == null) {
|
if (cost == null) {
|
||||||
ComputerUtilMana.payManaCost(ai, sa);
|
ComputerUtilMana.payManaCost(ai, sa);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import forge.game.card.*;
|
|||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.*;
|
import forge.game.cost.*;
|
||||||
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaConversionMatrix;
|
import forge.game.mana.ManaConversionMatrix;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
@@ -1253,4 +1254,25 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
// Always true?
|
// Always true?
|
||||||
return 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.cost.Cost;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerController;
|
||||||
import forge.game.spellability.*;
|
import forge.game.spellability.*;
|
||||||
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -250,11 +252,6 @@ public final class GameActionUtil {
|
|||||||
if (keyword.startsWith("Buyback")) {
|
if (keyword.startsWith("Buyback")) {
|
||||||
final Cost cost = new Cost(keyword.substring(8), false);
|
final Cost cost = new Cost(keyword.substring(8), false);
|
||||||
costs.add(new OptionalCostValue(OptionalCost.Buyback, cost));
|
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")) {
|
} else if (keyword.startsWith("Entwine")) {
|
||||||
String[] k = keyword.split(":");
|
String[] k = keyword.split(":");
|
||||||
final Cost cost = new Cost(k[1], false);
|
final Cost cost = new Cost(k[1], false);
|
||||||
@@ -301,15 +298,11 @@ public final class GameActionUtil {
|
|||||||
}
|
}
|
||||||
final SpellAbility result = sa.copy();
|
final SpellAbility result = sa.copy();
|
||||||
for (OptionalCostValue v : list) {
|
for (OptionalCostValue v : list) {
|
||||||
// need to copy cost, otherwise it does alter the original
|
result.getPayCosts().add(v.getCost());
|
||||||
result.setPayCosts(result.getPayCosts().copy().add(v.getCost()));
|
|
||||||
result.addOptionalCost(v.getType());
|
result.addOptionalCost(v.getType());
|
||||||
|
|
||||||
// add some extra logic, try to move it to other parts
|
// add some extra logic, try to move it to other parts
|
||||||
switch (v.getType()) {
|
switch (v.getType()) {
|
||||||
case Conspire:
|
|
||||||
result.addConspireInstance();
|
|
||||||
break;
|
|
||||||
case Retrace:
|
case Retrace:
|
||||||
case Jumpstart:
|
case Jumpstart:
|
||||||
result.getRestrictions().setZone(ZoneType.Graveyard);
|
result.getRestrictions().setZone(ZoneType.Graveyard);
|
||||||
@@ -363,6 +356,71 @@ public final class GameActionUtil {
|
|||||||
return abilities;
|
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) {
|
private static boolean hasUrzaLands(final Player p) {
|
||||||
final CardCollectionView landsControlled = p.getCardsIn(ZoneType.Battlefield);
|
final CardCollectionView landsControlled = p.getCardsIn(ZoneType.Battlefield);
|
||||||
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine")))
|
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(parsedTrigger);
|
||||||
inst.addTrigger(parsedTrigReturn);
|
inst.addTrigger(parsedTrigReturn);
|
||||||
} else if (keyword.equals("Conspire")) {
|
} 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 String abString = "DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Amount$ 1";
|
||||||
|
|
||||||
final Trigger conspireTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
|
final Trigger conspireTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
|
||||||
conspireTrigger.setOverridingAbility(AbilityFactory.getAbility(abString, card));
|
conspireTrigger.setOverridingAbility(AbilityFactory.getAbility(abString, card));
|
||||||
|
conspireTrigger.setSVar("Conspire", "0");
|
||||||
inst.addTrigger(conspireTrigger);
|
inst.addTrigger(conspireTrigger);
|
||||||
} else if (keyword.startsWith("Cumulative upkeep")) {
|
} else if (keyword.startsWith("Cumulative upkeep")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
@@ -2942,6 +2943,17 @@ public class CardFactoryUtil {
|
|||||||
+ " exile CARDNAME. | Secondary$ True";
|
+ " exile CARDNAME. | Secondary$ True";
|
||||||
final Trigger myTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
final Trigger myTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||||
inst.addTrigger(myTrigger);
|
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")) {
|
} else if (keyword.startsWith("Ripple")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String num = k[1];
|
final String num = k[1];
|
||||||
@@ -4225,9 +4237,6 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
sa.setTemporary(!intrinsic);
|
sa.setTemporary(!intrinsic);
|
||||||
inst.addSpellAbility(sa);
|
inst.addSpellAbility(sa);
|
||||||
|
|
||||||
} else if (keyword.startsWith("Replicate")) {
|
|
||||||
card.getFirstSpellAbility().addAnnounceVar("Replicate");
|
|
||||||
} else if (keyword.startsWith("Scavenge")) {
|
} else if (keyword.startsWith("Scavenge")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String manacost = k[1];
|
final String manacost = k[1];
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import forge.game.combat.Combat;
|
|||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostPart;
|
import forge.game.cost.CostPart;
|
||||||
import forge.game.cost.CostPartMana;
|
import forge.game.cost.CostPartMana;
|
||||||
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaConversionMatrix;
|
import forge.game.mana.ManaConversionMatrix;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
@@ -47,7 +48,6 @@ public abstract class PlayerController {
|
|||||||
DeclareBlocker,
|
DeclareBlocker,
|
||||||
Echo,
|
Echo,
|
||||||
Multikicker,
|
Multikicker,
|
||||||
Replicate,
|
|
||||||
CumulativeUpkeep,
|
CumulativeUpkeep,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +182,11 @@ public abstract class PlayerController {
|
|||||||
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
||||||
public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose);
|
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, int min, int max);
|
||||||
public abstract int chooseNumber(SpellAbility sa, String title, List<Integer> values, Player relatedPlayer);
|
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) {
|
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 {
|
public enum OptionalCost {
|
||||||
Conspire("Conspire"),
|
|
||||||
Buyback("Buyback"),
|
Buyback("Buyback"),
|
||||||
Entwine("Entwine"),
|
Entwine("Entwine"),
|
||||||
Kicker1("Kicker"),
|
Kicker1("Kicker"),
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
private CardCollection tappedForConvoke = new CardCollection();
|
private CardCollection tappedForConvoke = new CardCollection();
|
||||||
private Card sacrificedAsOffering = null;
|
private Card sacrificedAsOffering = null;
|
||||||
private Card sacrificedAsEmerge = null;
|
private Card sacrificedAsEmerge = null;
|
||||||
private int conspireInstances = 0;
|
|
||||||
|
|
||||||
private AbilityManaPart manaPart = null;
|
private AbilityManaPart manaPart = null;
|
||||||
|
|
||||||
@@ -301,9 +300,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canPlayWithOptionalCost(OptionalCostValue opt) {
|
public boolean canPlayWithOptionalCost(OptionalCostValue opt) {
|
||||||
SpellAbility saCopy = this.copy();
|
return GameActionUtil.addOptionalCosts(this, Lists.newArrayList(opt)).canPlay();
|
||||||
saCopy = GameActionUtil.addOptionalCosts(saCopy, Lists.newArrayList(opt));
|
|
||||||
return saCopy.canPlay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPossible() {
|
public boolean isPossible() {
|
||||||
@@ -1700,19 +1697,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return ForgeScript.spellAbilityHasProperty(this, property, sourceController, source, spellAbility);
|
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() {
|
public boolean isCumulativeupkeep() {
|
||||||
return cumulativeupkeep;
|
return cumulativeupkeep;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import forge.game.card.CardLists;
|
|||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.OptionalCost;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.game.spellability.TargetChoices;
|
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 (hasParam("Outlast")) {
|
||||||
if (!spellAbility.isOutlast()) {
|
if (!spellAbility.isOutlast()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import forge.card.mana.ManaCost;
|
|||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameLogEntryType;
|
import forge.game.GameLogEntryType;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -58,7 +57,6 @@ import forge.game.player.PlayerController.ManaPaymentPurpose;
|
|||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementHandler;
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.AbilitySub;
|
|
||||||
import forge.game.spellability.AbilityStatic;
|
import forge.game.spellability.AbilityStatic;
|
||||||
import forge.game.spellability.OptionalCost;
|
import forge.game.spellability.OptionalCost;
|
||||||
import forge.game.spellability.Spell;
|
import forge.game.spellability.Spell;
|
||||||
@@ -319,35 +317,6 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
|
|
||||||
// The ability is added to stack HERE
|
// The ability is added to stack HERE
|
||||||
si = push(sp);
|
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.Cost;
|
||||||
import forge.game.cost.CostPart;
|
import forge.game.cost.CostPart;
|
||||||
import forge.game.cost.CostPartMana;
|
import forge.game.cost.CostPartMana;
|
||||||
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaConversionMatrix;
|
import forge.game.mana.ManaConversionMatrix;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
@@ -695,4 +696,11 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
return false;
|
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
|
Types:Creature Djinn
|
||||||
PT:3/5
|
PT:3/5
|
||||||
K:Flying
|
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
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/djinn_illuminatus.jpg
|
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.)
|
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
|
Name:Stream of Thought
|
||||||
ManaCost:U
|
ManaCost:U
|
||||||
Types:Sorcery
|
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
|
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.)
|
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
|
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.
|
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
|
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
|
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.)
|
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);
|
// System.out.println("Playing:" + sa.getDescription() + " of " + sa.getHostCard() + " new = " + newAbility);
|
||||||
if (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 (!req.playAbility(true, false, false)) {
|
||||||
if (flippedToCast && !castFaceDown) {
|
if (flippedToCast && !castFaceDown) {
|
||||||
source.turnFaceDown(true);
|
source.turnFaceDown(true);
|
||||||
@@ -199,9 +197,8 @@ public class HumanPlay {
|
|||||||
}
|
}
|
||||||
sa = AbilityUtils.addSpliceEffects(sa);
|
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);
|
req.playAbility(mayChooseNewTargets, true, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -231,7 +228,7 @@ public class HumanPlay {
|
|||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
|
|
||||||
if (sa.getPayCosts() != null) {
|
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);
|
req.playAbility(!useOldTargets, false, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import forge.card.CardStateName;
|
|||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -53,13 +54,11 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class HumanPlaySpellAbility {
|
public class HumanPlaySpellAbility {
|
||||||
private final PlayerControllerHuman controller;
|
private final PlayerControllerHuman controller;
|
||||||
private final SpellAbility ability;
|
private SpellAbility ability;
|
||||||
private final CostPayment payment;
|
|
||||||
|
|
||||||
public HumanPlaySpellAbility(final PlayerControllerHuman controller0, final SpellAbility ability0, final CostPayment payment0) {
|
public HumanPlaySpellAbility(final PlayerControllerHuman controller0, final SpellAbility ability0) {
|
||||||
controller = controller0;
|
controller = controller0;
|
||||||
ability = ability0;
|
ability = ability0;
|
||||||
payment = payment0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean playAbility(final boolean mayChooseTargets, final boolean isFree, final boolean skipStack) {
|
public final boolean playAbility(final boolean mayChooseTargets, final boolean isFree, final boolean skipStack) {
|
||||||
@@ -116,6 +115,11 @@ public class HumanPlaySpellAbility {
|
|||||||
ability.resetPaidHash();
|
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
|
// TODO Apply this to the SAStackInstance instead of the Player
|
||||||
if (manaTypeConversion) {
|
if (manaTypeConversion) {
|
||||||
AbilityUtils.applyManaColorConversion(payment, MagicColor.Constant.ANY_TYPE_CONVERSION);
|
AbilityUtils.applyManaColorConversion(payment, MagicColor.Constant.ANY_TYPE_CONVERSION);
|
||||||
@@ -155,7 +159,7 @@ public class HumanPlaySpellAbility {
|
|||||||
|
|
||||||
if (!prerequisitesMet) {
|
if (!prerequisitesMet) {
|
||||||
if (!ability.isTrigger()) {
|
if (!ability.isTrigger()) {
|
||||||
rollbackAbility(fromZone, fromState, zonePosition);
|
rollbackAbility(fromZone, fromState, zonePosition, payment);
|
||||||
if (ability.getHostCard().isMadness()) {
|
if (ability.getHostCard().isMadness()) {
|
||||||
// if a player failed to play madness cost, move the card to graveyard
|
// if a player failed to play madness cost, move the card to graveyard
|
||||||
Card newCard = game.getAction().moveToGraveyard(c, null);
|
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
|
// cancel ability during target choosing
|
||||||
final Game game = ability.getActivatingPlayer().getGame();
|
final Game game = ability.getActivatingPlayer().getGame();
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import forge.game.cost.Cost;
|
|||||||
import forge.game.cost.CostPart;
|
import forge.game.cost.CostPart;
|
||||||
import forge.game.cost.CostPartMana;
|
import forge.game.cost.CostPartMana;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaConversionMatrix;
|
import forge.game.mana.ManaConversionMatrix;
|
||||||
import forge.game.player.*;
|
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?");
|
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