From b57e29ba21ac6c6272a47e2b89d4ce0db8e63049 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 27 Dec 2021 15:10:46 +0100 Subject: [PATCH] AlternateCost for Mirrodin Shards --- .../main/java/forge/game/CardTraitBase.java | 3 + .../main/java/forge/game/GameActionUtil.java | 82 ++++++++++++------- .../forge/game/ability/AbilityFactory.java | 10 +-- .../src/main/java/forge/game/card/Card.java | 2 +- .../src/main/java/forge/game/cost/Cost.java | 1 - .../forge/game/spellability/SpellAbility.java | 21 +++++ forge-gui/res/cardsfolder/c/crystal_shard.txt | 3 +- forge-gui/res/cardsfolder/g/granite_shard.txt | 4 +- .../res/cardsfolder/h/heartwood_shard.txt | 4 +- forge-gui/res/cardsfolder/p/pearl_shard.txt | 4 +- .../res/cardsfolder/s/skeleton_shard.txt | 4 +- .../src/main/java/forge/player/HumanPlay.java | 3 - 12 files changed, 83 insertions(+), 58 deletions(-) diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index 54c8034d645..a46892d530f 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -92,6 +92,9 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView, public String putParam(String key, String value) { return mapParams.put(key, value); } + public void removeParam(String key) { + mapParams.remove(key); + } /** *

* Getter for the field mapParams. diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 96a974d2179..367e1a0d975 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -482,40 +482,62 @@ public final class GameActionUtil { public static List getAdditionalCostSpell(final SpellAbility sa) { final List abilities = Lists.newArrayList(sa); - if (!sa.isSpell()) { - return abilities; - } - final Card source = sa.getHostCard(); - for (KeywordInterface inst : source.getKeywords()) { - final String keyword = inst.getOriginal(); - if (keyword.startsWith("AlternateAdditionalCost")) { - final List newAbilities = Lists.newArrayList(); - String[] costs = TextUtil.split(keyword, ':'); + if (sa.isSpell()) { + final Card source = sa.getHostCard(); + for (KeywordInterface inst : source.getKeywords()) { + final String keyword = inst.getOriginal(); + if (keyword.startsWith("AlternateAdditionalCost")) { + final List newAbilities = Lists.newArrayList(); + String[] costs = TextUtil.split(keyword, ':'); - final SpellAbility newSA = sa.copy(); - newSA.setBasicSpell(false); + final SpellAbility newSA = sa.copy(); + newSA.setBasicSpell(false); - final Cost cost1 = new Cost(costs[1], false); - newSA.setDescription(sa.getDescription() + " (Additional cost " + cost1.toSimpleString() + ")"); - newSA.setPayCosts(cost1.add(sa.getPayCosts())); - if (newSA.canPlay()) { - newAbilities.add(newSA); + final Cost cost1 = new Cost(costs[1], false); + newSA.setDescription(sa.getDescription() + " (Additional cost " + cost1.toSimpleString() + ")"); + newSA.setPayCosts(cost1.add(sa.getPayCosts())); + if (newSA.canPlay()) { + newAbilities.add(newSA); + } + + //second option + final SpellAbility newSA2 = sa.copy(); + newSA2.setBasicSpell(false); + + final Cost cost2 = new Cost(costs[2], false); + newSA2.setDescription(sa.getDescription() + " (Additional cost " + cost2.toSimpleString() + ")"); + newSA2.setPayCosts(cost2.add(sa.getPayCosts())); + if (newSA2.canPlay()) { + newAbilities.add(newSA2); + } + + abilities.clear(); + abilities.addAll(newAbilities); } - - //second option - final SpellAbility newSA2 = sa.copy(); - newSA2.setBasicSpell(false); - - final Cost cost2 = new Cost(costs[2], false); - newSA2.setDescription(sa.getDescription() + " (Additional cost " + cost2.toSimpleString() + ")"); - newSA2.setPayCosts(cost2.add(sa.getPayCosts())); - if (newSA2.canPlay()) { - newAbilities.add(newSA2); - } - - abilities.clear(); - abilities.addAll(newAbilities); } + } else if (sa.isActivatedAbility() && sa.hasParam("AlternateCost")) { + // need to be handled there because it needs to rebuilt the description for the original ability + + final List newAbilities = Lists.newArrayList(); + + SpellAbility newSA = sa.copy(); + newSA.removeParam("AlternateCost"); + newSA.rebuiltDescription(); + if (newSA.canPlay()) { + newAbilities.add(newSA); + } + + // set the cost to this directly to bypass non mana cost + Cost alternateCost = new Cost(sa.getParam("AlternateCost"), sa.isAbility()); + SpellAbility newSA2 = sa.copyWithDefinedCost(alternateCost); + newSA2.removeParam("AlternateCost"); + newSA2.rebuiltDescription(); + if (newSA2.canPlay()) { + newAbilities.add(newSA2); + } + + abilities.clear(); + abilities.addAll(newAbilities); } return abilities; } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index b96892c8fc9..b2359829155 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -301,15 +301,7 @@ public final class AbilityFactory { : spellAbility.getHostCard().getName(); spellAbility.setDescription(desc); } else if (mapParams.containsKey("SpellDescription")) { - final StringBuilder sb = new StringBuilder(); - - if (type != AbilityRecordType.SubAbility) { - // SubAbilities don't have Costs or Cost descriptors - sb.append(spellAbility.getCostDescription()); - } - - sb.append(mapParams.get("SpellDescription")); - spellAbility.setDescription(sb.toString()); + spellAbility.rebuiltDescription(); } else if (api == ApiType.Charm) { spellAbility.setDescription(CharmEffect.makeFormatedDescription(spellAbility)); } else { diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 70fc3cbee6b..b80d00c8571 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1962,7 +1962,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (mCost.isOnlyManaCost()) { sbLong.append(" "); } - sbLong.append(mCost.toString()).delete(sbLong.length() - 2, sbLong.length()); + sbLong.append(mCost.toString()); if (!mCost.isOnlyManaCost()) { sbLong.append("."); } diff --git a/forge-game/src/main/java/forge/game/cost/Cost.java b/forge-game/src/main/java/forge/game/cost/Cost.java index 7b194d31300..a17246b9711 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -776,7 +776,6 @@ public class Cost implements Serializable { cost.append("0"); } - cost.append(": "); return cost.toString(); } 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 c1263fc5a83..598623e4d9b 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -841,11 +841,32 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit sb.append(getParam("CostDesc")).append(" "); } else { sb.append(payCosts.toString()); + + // for cards like Crystal Shard with {3}, {T} or {U}, {T}: + if (hasParam("AlternateCost")) { + Cost alternateCost = new Cost(getParam("AlternateCost"), payCosts.isAbility()); + sb.append(" or ").append(alternateCost.toString()); + } + + if (payCosts.isAbility()) { + sb.append(": "); + } } + return sb.toString(); } } + public void rebuiltDescription() { + final StringBuilder sb = new StringBuilder(); + + // SubAbilities don't have Costs or Cost descriptors + sb.append(getCostDescription()); + + sb.append(getParam("SpellDescription")); + setDescription(sb.toString()); + } + /** {@inheritDoc} */ @Override public final String toString() { diff --git a/forge-gui/res/cardsfolder/c/crystal_shard.txt b/forge-gui/res/cardsfolder/c/crystal_shard.txt index 2ecbae16704..7848f615b7d 100644 --- a/forge-gui/res/cardsfolder/c/crystal_shard.txt +++ b/forge-gui/res/cardsfolder/c/crystal_shard.txt @@ -1,8 +1,7 @@ Name:Crystal Shard ManaCost:3 Types:Artifact -A:AB$ ChangeZone | Cost$ 3 T | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | UnlessCost$ 1 | UnlessPayer$ TargetedController | SpellDescription$ Return target creature to its owner's hand unless its controller pays {1}. -A:AB$ ChangeZone | Cost$ U T | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | UnlessCost$ 1 | UnlessPayer$ TargetedController | SpellDescription$ Return target creature to its owner's hand unless its controller pays {1}. +A:AB$ ChangeZone | Cost$ 3 T | AlternateCost$ U T | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | UnlessCost$ 1 | UnlessPayer$ TargetedController | SpellDescription$ Return target creature to its owner's hand unless its controller pays {1}. AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/crystal_shard.jpg Oracle:{3}, {T} or {U}, {T}: Return target creature to its owner's hand unless its controller pays {1}. diff --git a/forge-gui/res/cardsfolder/g/granite_shard.txt b/forge-gui/res/cardsfolder/g/granite_shard.txt index c964c4ae8e7..0a2368eb8a7 100644 --- a/forge-gui/res/cardsfolder/g/granite_shard.txt +++ b/forge-gui/res/cardsfolder/g/granite_shard.txt @@ -1,7 +1,5 @@ Name:Granite Shard ManaCost:3 Types:Artifact -A:AB$ DealDamage | Cost$ 3 T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target. -A:AB$ DealDamage | Cost$ R T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target. -SVar:Picture:http://www.wizards.com/global/images/magic/general/granite_shard.jpg +A:AB$ DealDamage | Cost$ 3 T | AlternateCost$ R T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target. Oracle:{3}, {T} or {R}, {T}: Granite Shard deals 1 damage to any target. diff --git a/forge-gui/res/cardsfolder/h/heartwood_shard.txt b/forge-gui/res/cardsfolder/h/heartwood_shard.txt index 421d25a5d03..f4bf7c29c0a 100644 --- a/forge-gui/res/cardsfolder/h/heartwood_shard.txt +++ b/forge-gui/res/cardsfolder/h/heartwood_shard.txt @@ -1,8 +1,6 @@ Name:Heartwood Shard ManaCost:3 Types:Artifact -A:AB$ Pump | Cost$ 3 T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Trample | SpellDescription$ Target creature gains trample until end of turn. -A:AB$ Pump | Cost$ G T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Trample | SpellDescription$ Target creature gains trample until end of turn. +A:AB$ Pump | Cost$ 3 T | AlternateCost$ G T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Trample | SpellDescription$ Target creature gains trample until end of turn. AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/heartwood_shard.jpg Oracle:{3}, {T} or {G}, {T}: Target creature gains trample until end of turn. diff --git a/forge-gui/res/cardsfolder/p/pearl_shard.txt b/forge-gui/res/cardsfolder/p/pearl_shard.txt index f2d5b5727f3..f9fdcefac0a 100644 --- a/forge-gui/res/cardsfolder/p/pearl_shard.txt +++ b/forge-gui/res/cardsfolder/p/pearl_shard.txt @@ -1,7 +1,5 @@ Name:Pearl Shard ManaCost:3 Types:Artifact -A:AB$ PreventDamage | Cost$ 3 T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | Amount$ 2 | SpellDescription$ Prevent the next 2 damage that would be dealt to any target this turn. -A:AB$ PreventDamage | Cost$ W T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | Amount$ 2 | SpellDescription$ Prevent the next 2 damage that would be dealt to any target this turn. -SVar:Picture:http://www.wizards.com/global/images/magic/general/pearl_shard.jpg +A:AB$ PreventDamage | Cost$ 3 T | AlternateCost$ W T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | Amount$ 2 | SpellDescription$ Prevent the next 2 damage that would be dealt to any target this turn. Oracle:{3}, {T} or {W}, {T}: Prevent the next 2 damage that would be dealt to any target this turn. diff --git a/forge-gui/res/cardsfolder/s/skeleton_shard.txt b/forge-gui/res/cardsfolder/s/skeleton_shard.txt index 61136dfa53b..947916cc51e 100644 --- a/forge-gui/res/cardsfolder/s/skeleton_shard.txt +++ b/forge-gui/res/cardsfolder/s/skeleton_shard.txt @@ -1,8 +1,6 @@ Name:Skeleton Shard ManaCost:3 Types:Artifact -A:AB$ ChangeZone | Cost$ 3 T | ValidTgts$ Creature.Artifact+YouCtrl | TgtPrompt$ Choose target artifact creature card in your graveyard | Origin$ Graveyard | Destination$ Hand | SpellDescription$ Return target artifact creature card from your graveyard to your hand. -A:AB$ ChangeZone | Cost$ B T | ValidTgts$ Creature.Artifact+YouCtrl | TgtPrompt$ Choose target artifact creature card in your graveyard | Origin$ Graveyard | Destination$ Hand | SpellDescription$ Return target artifact creature card from your graveyard to your hand. +A:AB$ ChangeZone | Cost$ 3 T | AlternateCost$ B T | ValidTgts$ Creature.Artifact+YouCtrl | TgtPrompt$ Choose target artifact creature card in your graveyard | Origin$ Graveyard | Destination$ Hand | SpellDescription$ Return target artifact creature card from your graveyard to your hand. AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/skeleton_shard.jpg Oracle:{3}, {T} or {B}, {T}: Return target artifact creature card from your graveyard to your hand. diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 87a976a6ade..f85ed88a6d0 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -139,9 +139,6 @@ public class HumanPlay { * @return an ArrayList. */ static SpellAbility chooseOptionalAdditionalCosts(Player p, final SpellAbility original) { - if (!original.isSpell()) { - return original; - } PlayerController c = p.getController();