diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 280c76a1027..9f4428ae97c 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -449,6 +449,9 @@ public class AiAttackController { return false; } + // TODO for multiplayer this should either add some heuristics + // so the other opponents attack power is also measured in + // or refactor it with aiLifeInDanger somehow if performance impact isn't too bad CardLists.sortByPowerDesc(oppList); for (Card attacker : oppList) { if (!ComputerUtilCombat.canAttackNextTurn(attacker)) { @@ -1037,7 +1040,7 @@ public class AiAttackController { && defendingOpponent != null && ComputerUtil.countUsefulCreatures(ai) > ComputerUtil.countUsefulCreatures(defendingOpponent) && ai.getLife() > defendingOpponent.getLife() - && !ComputerUtilCombat.lifeInDanger(ai, combat) + && !ComputerUtilCombat.lifeInDanger(ai, combat) // this isn't really doing anything unless the attacking player in combat isn't the AI (which currently isn't used like that) && (ComputerUtilMana.getAvailableManaEstimate(ai) > 0) || tradeIfTappedOut && (ComputerUtilMana.getAvailableManaEstimate(defendingOpponent) == 0) || MyRandom.percentTrue(extraChanceIfOppHasMana) && (!tradeIfLowerLifePressure || (ai.getLifeLostLastTurn() + ai.getLifeLostThisTurn() < diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 7b00f98b9bb..04e9397af74 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -160,9 +160,7 @@ public class AiController { all.add(ccvPlayerLibrary.get(0)); } - for (final Player opp : player.getOpponents()) { - all.addAll(opp.getCardsIn(ZoneType.Exile)); - } + all.addAll(player.getOpponents().getCardsIn(ZoneType.Exile)); final List spellAbilities = Lists.newArrayList(); for (final Card c : all) { @@ -703,7 +701,7 @@ public class AiController { // but should work in most circumstances to ensure safety in whatever the AI is using this for. if (manaSources.size() >= cost.getConvertedManaCost()) { for (Card c : manaSources) { - AiCardMemory.rememberCard(player, c, memSet); + memory.rememberCard(c, memSet); } return true; } @@ -839,7 +837,7 @@ public class AiController { // When processing a new SA, clear the previously remembered cards that have been marked to avoid re-entry // which might potentially cause a stack overflow. - AiCardMemory.clearMemorySet(this, AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY); + memory.clearMemorySet(AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY); // TODO before suspending some spells try to predict if relevant targets can be expected if (sa.getApi() != null) { @@ -1490,7 +1488,7 @@ public class AiController { predictedCombatNextTurn = null; // Reset priority mana reservation that's meant to work for one spell only - AiCardMemory.clearMemorySet(player, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL); + memory.clearMemorySet(AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL); if (useSimulation) { return singleSpellAbilityList(simPicker.chooseSpellAbilityToPlay(null)); diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 2b6f3500335..d18fe49da37 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -344,8 +344,7 @@ public class AiCostDecision extends CostDecisionMakerBase { } } chosen = chosen.subList(0, c); - } - else { + } else { chosen = ComputerUtil.choosePutToLibraryFrom(player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c, ability); } return chosen.isEmpty() ? null : PaymentDecision.card(chosen); @@ -363,8 +362,7 @@ public class AiCostDecision extends CostDecisionMakerBase { Card card; if (cost.getType().equals("Creature.YouCtrl")) { card = ComputerUtilCard.getWorstCreatureAI(typeList); - } - else { + } else { card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false); } return PaymentDecision.card(card); diff --git a/forge-ai/src/main/java/forge/ai/AiProfileUtil.java b/forge-ai/src/main/java/forge/ai/AiProfileUtil.java index e119d6b5ca0..a1848a9b558 100644 --- a/forge-ai/src/main/java/forge/ai/AiProfileUtil.java +++ b/forge-ai/src/main/java/forge/ai/AiProfileUtil.java @@ -119,8 +119,7 @@ public class AiProfileUtil { * @return ArrayList - an array of strings containing all * available profiles. */ - public static List getAvailableProfiles() - { + public static List getAvailableProfiles() { final List availableProfiles = new ArrayList<>(); final File dir = new File(AI_PROFILE_DIR); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 9ff0c3f8b41..f130e08e967 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -703,13 +703,13 @@ public class ComputerUtil { if (pow <= 0) { continue; } - totalPower += pow; if (pow >= amount) { // If the power of this creature matches the totalPower needed // Might as well only use this creature? tapList.clear(); } tapList.add(next); + totalPower = CardLists.getTotalPower(tapList, true, sa.hasParam("Crew")); if (totalPower >= amount) { break; } @@ -793,7 +793,7 @@ public class ComputerUtil { boolean exceptSelf = "ExceptSelf".equals(source.getParam("AILogic")); boolean removedSelf = false; - if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit")) { + if (isOptional && (source.hasParam("Devour") || source.hasParam("Exploit"))) { if (source.hasParam("Exploit")) { for (Trigger t : host.getTriggers()) { if (t.getMode() == TriggerType.Exploited) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java index 8766eff1f05..bfcad761181 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java @@ -72,10 +72,8 @@ public class ComputerUtilAbility { if (!player.getCardsIn(ZoneType.Library).isEmpty()) { all.add(player.getCardsIn(ZoneType.Library).get(0)); } - for (Player p : game.getPlayers()) { - all.addAll(p.getCardsIn(ZoneType.Exile)); - all.addAll(p.getCardsIn(ZoneType.Battlefield)); - } + all.addAll(game.getPlayers().getCardsIn(ZoneType.Exile)); + all.addAll(game.getPlayers().getCardsIn(ZoneType.Battlefield)); return all; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index eae57125e27..3d74f20b4fd 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1,7 +1,6 @@ package forge.ai; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -1361,7 +1360,7 @@ public class ComputerUtilCard { if (!Iterables.any(oppCreatures, CardPredicates.possibleBlockers(pumped))) { threat *= 2; } - if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0 ) { + if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0) { threat *= 4; //over-value self +attack for 0 power creatures which may be pumped further after attacking } chance += threat; @@ -1378,9 +1377,7 @@ public class ComputerUtilCard { && ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) { combatTrick = true; - final List kws = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) - : Lists.newArrayList(); - for (String kw : kws) { + for (String kw : keywords) { if (!kw.equals("Trample") && !kw.equals("First Strike") && !kw.equals("Double Strike")) { combatTrick = false; break; @@ -1565,7 +1562,7 @@ public class ComputerUtilCard { } //5. if the life of the computer is in danger, try to pump blockers blocking Tramplers - if (combat.isBlocking(c) && toughness > 0 ) { + if (combat.isBlocking(c) && toughness > 0) { List blockedBy = combat.getAttackersBlockedBy(c); boolean attackerHasTrample = false; for (Card b : blockedBy) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index b1b584302cd..ed8202a1422 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -417,6 +417,7 @@ public class ComputerUtilCombat { return false; } + // TODO check for replacement effect instead CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield); // Special cases: // AI can't lose in combat in presence of Worship (with creatures) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 990666fa459..23ffb4a9a98 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -795,6 +795,9 @@ public class ComputerUtilMana { if (toPay.isPhyrexian()) { cost.payPhyrexian(); + if (!test) { + sa.setSpendPhyrexianMana(true); + } } else if (lifeInsteadOfBlack) { cost.decreaseShard(ManaCostShard.BLACK, 1); } diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 1406126a762..616e66069e4 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -242,11 +242,11 @@ public class PlayerControllerAi extends PlayerController { SpellAbility selected; do { selected = chooseSingleSpellForEffect(remaining, sa, title, params); - if ( selected != null ) { + if (selected != null) { remaining.remove(selected); selecteds.add(selected); } - } while ( (selected != null ) && (selecteds.size() < num) ); + } while (selected != null && selecteds.size() < num); return selecteds; } @@ -544,12 +544,6 @@ public class PlayerControllerAi extends PlayerController { return getAi().chooseCardsToDelve(genericAmount, grave); } - @Override - public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional) { - // AI currently can't do this. But when it can it will need to be based on Ability API - return null; - } - @Override public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) { String [] splitUTypes = uType.split(","); @@ -1099,6 +1093,12 @@ public class PlayerControllerAi extends PlayerController { return brains.doTrigger(currentAbility, true); } + @Override + public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional) { + // AI currently can't do this. But when it can it will need to be based on Ability API + return null; + } + @Override public boolean chooseCardsPile(SpellAbility sa, CardCollectionView pile1, CardCollectionView pile2, String faceUp) { if (faceUp.equals("True")) { @@ -1341,8 +1341,7 @@ public class PlayerControllerAi extends PlayerController { } @Override - public List chooseOptionalCosts(SpellAbility chosen, - List optionalCostValues) { + public List chooseOptionalCosts(SpellAbility chosen, List optionalCostValues) { List chosenOptCosts = Lists.newArrayList(); Cost costSoFar = chosen.getPayCosts().copy(); diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 90a8d8a02fe..263e03e5b96 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -182,6 +182,8 @@ public class ForgeScript { return sa.hasParam("Daybound"); } else if (property.equals("Nightbound")) { return sa.hasParam("Nightbound"); + } else if (property.equals("paidPhyrexianMana")) { + return sa.getSpendPhyrexianMana(); } else if (property.equals("MayPlaySource")) { StaticAbility m = sa.getMayPlay(); if (m == null) { diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 4d93d16a262..08ac6758544 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1308,6 +1308,7 @@ public class GameAction { desCreats = new CardCollection(); } desCreats.add(c); + c.setHasBeenDealtDeathtouchDamage(false); checkAgain = true; break; } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index e216ebfb222..b8ec1f4eca3 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2147,6 +2147,11 @@ public class CardFactoryUtil { re.setOverridingAbility(saReturn); + inst.addReplacement(re); + } else if (keyword.equals("Compleated")) { + String sb = "etbCounter:LOYALTY:-2:ValidCard$ Card.CastSa Spell.paidPhyrexianMana:If life was paid, this planeswalker enters with two fewer loyalty counters"; + final ReplacementEffect re = makeEtbCounter(sb, card, intrinsic); + inst.addReplacement(re); } else if (keyword.startsWith("Dredge")) { final String dredgeAmount = keyword.split(":")[1]; diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index b596ce17c6c..137bcc5b8a3 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -101,7 +101,7 @@ public abstract class PlayerController { public final SpellAbility getAbilityToPlay(final Card hostCard, final List abilities) { return getAbilityToPlay(hostCard, abilities, null); } public abstract SpellAbility getAbilityToPlay(Card hostCard, List abilities, ITriggerEvent triggerEvent); - //public abstract void playFromSuspend(Card c); + @Deprecated public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets); public abstract void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets); @@ -130,13 +130,13 @@ public abstract class PlayerController { public abstract List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, int num, Map params); - public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title, - Map params); + public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title, Map params); public abstract List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer, Map params); public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message); public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner); + public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question); public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message); public abstract boolean confirmTrigger(WrappedAbility sa); public abstract Player chooseStartingPlayer(boolean isFirstGame); @@ -170,10 +170,11 @@ public abstract class PlayerController { /** p = target player, validCards - possible discards, min cards to discard */ public abstract CardCollectionView chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max); + public abstract CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa); + public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard); public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave); public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid); - public abstract CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa); public abstract List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand); public abstract Mana chooseManaFromPool(List manaChoices); @@ -183,7 +184,6 @@ public abstract class PlayerController { } public abstract Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer); - public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question); public abstract CardCollectionView getCardsToMulligan(Player firstPlayer); public abstract boolean mulliganKeepHand(Player player, int cardsToReturn); @@ -194,7 +194,6 @@ public abstract class PlayerController { public abstract List chooseSpellAbilityToPlay(); public abstract boolean playChosenSpellAbility(SpellAbility sa); - 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); @@ -213,6 +212,8 @@ public abstract class PlayerController { public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map params) { return chooseBinary(sa, question, kindOfChoice); } public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call); + + @Deprecated public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap); public abstract List chooseModeForAbility(SpellAbility sa, List possible, int min, int num, boolean allowRepeat); @@ -250,7 +251,6 @@ public abstract class PlayerController { public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, boolean effect) { return payManaCost(costPartMana, sa, prompt, null, effect); } - public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, ManaConversionMatrix matrix, boolean effect) { return payManaCost(costPartMana.getManaCostFor(sa), costPartMana, sa, prompt, matrix, effect); } @@ -261,7 +261,6 @@ public abstract class PlayerController { public abstract List chooseCardsForSplice(SpellAbility sa, List cards); public abstract String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message); - public abstract String chooseCardName(SpellAbility sa, List faces, String message); public abstract Card chooseDungeon(Player player, List dungeonCards, String message); diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 8cc1569dd8a..4df71c13d17 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -115,6 +115,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private Player targetingPlayer = null; private Pair controlledByPlayer = null; private ManaCostBeingPaid manaCostBeingPaid = null; + private boolean spentPhyrexian = false; private SpellAbility grantorOriginal = null; private StaticAbility grantorStatic = null; @@ -613,6 +614,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit payingMana.clear(); } + public final boolean getSpendPhyrexianMana() { + return this.spentPhyrexian; + } + public final void setSpendPhyrexianMana(boolean value) { + this.spentPhyrexian = value; + } + public final void applyPayingManaEffects() { Card host = getHostCard(); @@ -905,8 +913,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit // if alternate state is viewed while card uses original if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) { currentName = node.cardState.getName(); - } - else { + } else { currentName = node.getHostCard().getName(); } desc = CardTranslation.translateMultipleDescriptionText(desc, currentName); diff --git a/forge-gui/res/cardsfolder/b/bag_of_holding.txt b/forge-gui/res/cardsfolder/b/bag_of_holding.txt index 1833b22d7f3..026655ffea8 100644 --- a/forge-gui/res/cardsfolder/b/bag_of_holding.txt +++ b/forge-gui/res/cardsfolder/b/bag_of_holding.txt @@ -5,7 +5,7 @@ T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execut SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose -AI:RemoveDeck:All A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.noDiscard.minAdv2 | SpellDescription$ Return all cards exiled with CARDNAME to their owner's hand. +AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:Whenever you discard a card, exile that card from your graveyard.\n{2}, {T}: Draw a card, then discard a card.\n{4}, {T}, Sacrifice Bag of Holding: Return all cards exiled with Bag of Holding to their owner's hand. diff --git a/forge-gui/res/cardsfolder/c/carbonize.txt b/forge-gui/res/cardsfolder/c/carbonize.txt index 39cff6eba3b..17cd5453302 100644 --- a/forge-gui/res/cardsfolder/c/carbonize.txt +++ b/forge-gui/res/cardsfolder/c/carbonize.txt @@ -1,6 +1,6 @@ Name:Carbonize ManaCost:2 R Types:Instant -A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DB | ReplaceDyingDefined$ Targeted.Creature | SpellDescription$ CARDNAME deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. -SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature | StackDescription$ None +A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DB | ReplaceDyingDefined$ ThisTargetedCard.Creature | SpellDescription$ CARDNAME deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. +SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature | StackDescription$ None Oracle:Carbonize deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. diff --git a/forge-gui/res/cardsfolder/d/disintegrate.txt b/forge-gui/res/cardsfolder/d/disintegrate.txt index 91fe3100764..550182c28a2 100644 --- a/forge-gui/res/cardsfolder/d/disintegrate.txt +++ b/forge-gui/res/cardsfolder/d/disintegrate.txt @@ -1,7 +1,7 @@ Name:Disintegrate ManaCost:X R Types:Sorcery -A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DB | ReplaceDyingDefined$ Targeted.Creature | SpellDescription$ CARDNAME deals X damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead. -SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature +A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DB | ReplaceDyingDefined$ ThisTargetedCard.Creature | SpellDescription$ CARDNAME deals X damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead. +SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature SVar:X:Count$xPaid Oracle:Disintegrate deals X damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. diff --git a/forge-gui/res/cardsfolder/s/scorching_lava.txt b/forge-gui/res/cardsfolder/s/scorching_lava.txt index 3085bdf1987..17a9793554b 100644 --- a/forge-gui/res/cardsfolder/s/scorching_lava.txt +++ b/forge-gui/res/cardsfolder/s/scorching_lava.txt @@ -2,6 +2,6 @@ Name:Scorching Lava ManaCost:1 R Types:Instant K:Kicker:R -A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | ReplaceDyingDefined$ Targeted.Creature | ReplaceDyingCondition$ Kicked | SubAbility$ KickingLava | SpellDescription$ CARDNAME deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. -SVar:KickingLava:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature | Condition$ Kicked +A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | ReplaceDyingDefined$ ThisTargetedCard.Creature | ReplaceDyingCondition$ Kicked | SubAbility$ KickingLava | SpellDescription$ CARDNAME deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. +SVar:KickingLava:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature | Condition$ Kicked Oracle:Kicker {R} (You may pay an additional {R} as you cast this spell.)\nScorching Lava deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. diff --git a/forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt b/forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt new file mode 100644 index 00000000000..0c8b707251b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt @@ -0,0 +1,13 @@ +Name:Tamiyo, Compleated Sage +ManaCost:2 G PGU U +Types:Legendary Planeswalker Tamiyo +Loyalty:5 +K:Compleated +A:AB$ Tap | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Artifact,Creature | TgtPrompt$ Select up to one target artifact or creature | TargetMin$ 0 | TargetMax$ 1 | SubAbility$ DBPump | SpellDescription$ Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step. +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent +A:AB$ ChangeZone | Cost$ SubCounter | Planeswalker$ True | ValidTgts$ Permanent.nonLand+cmcEQX | TgtPrompt$ Select target nonland permanent card with mana value X | AILogic$ BestCard | Origin$ Graveyard | Destination$ Exile | SubAbility$ DBCopy | SpellDescription$ Exile target nonland permanent card with mana value X from your graveyard. +SVar:DBCopy:DB$ CopyPermanent | Defined$ Targeted | SpellDescription$ Create a token that's a copy of that card. +SVar:X:Count$xPaid +A:AB$ Token | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | TokenScript$ tamiyos_notebook | SpellDescription$ Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card." +DeckHas:Ability$Token & Type$Artifact +Oracle:Compleated ({PGU} can be paid with {G}, {U}, or 2 life. If life was paid, this planeswalker enters with two fewer loyalty counters.)\n[+1]: Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step.\n[−X]: Exile target nonland permanent card with mana value X from your graveyard. Create a token that's a copy of that card.\n[−7]: Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card." diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java index e8b414817e6..7df67e46d03 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java @@ -46,6 +46,7 @@ public class InputPayManaOfCostPayment extends InputPayMana { if (player == selected) { if (player.canPayLife(this.phyLifeToLose + 2, this.effect, saPaidFor)) { if (manaCost.payPhyrexian()) { + saPaidFor.setSpendPhyrexianMana(true); this.phyLifeToLose += 2; } else { if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) { diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 2b09d24bce5..33d65aba859 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -217,9 +217,9 @@ public class HumanPlaySpellAbility { fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null); ability.setHostCard(oldCard); ability.setXManaCostPaid(null); + ability.setSpendPhyrexianMana(false); if (ability.hasParam("Announce")) { - final String announce = ability.getParam("Announce"); - for (final String aVar : announce.split(",")) { + for (final String aVar : ability.getParam("Announce").split(",")) { final String varName = aVar.trim(); if (!varName.equals("X")) { ability.setSVar(varName, "0");