From f281287c69b1ccc43a73c68816044b5aa5df9bb8 Mon Sep 17 00:00:00 2001 From: Bug Hunter Date: Wed, 23 Jun 2021 16:32:30 +0000 Subject: [PATCH 1/3] Fix wrong calculated CMC for copied spells --- .../src/main/java/forge/game/ForgeScript.java | 11 +++++++++-- .../game/ability/SpellAbilityEffect.java | 1 - .../ability/effects/DamageDealEffect.java | 4 ++-- .../ability/effects/DestroyAllEffect.java | 1 - .../game/ability/effects/PumpEffect.java | 1 - .../src/main/java/forge/game/card/Card.java | 11 +++++++++-- .../java/forge/game/card/CardFactory.java | 3 +-- .../forge/game/keyword/KeywordInstance.java | 19 ++++++++----------- .../forge/game/spellability/SpellAbility.java | 1 - .../SpellAbilityStackInstance.java | 1 - .../cardsfolder/d/deekah_fractal_theorist.txt | 3 +-- .../res/cardsfolder/g/gluttonous_troll.txt | 2 +- .../res/cardsfolder/p/prismari_apprentice.txt | 3 +-- .../z/zaffai_thunder_conductor.txt | 6 +++--- 14 files changed, 35 insertions(+), 32 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index a92d4772e17..2d2e88ceb46 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -11,6 +11,7 @@ import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbility; +import forge.game.zone.ZoneType; import forge.util.Expressions; public class ForgeScript { @@ -122,7 +123,6 @@ public class ForgeScript { return Expressions.compare(y, property, x); } else return cardState.getTypeWithChanges().hasStringType(property); - } public static boolean spellAbilityHasProperty(SpellAbility sa, String property, Player sourceController, @@ -187,7 +187,14 @@ public class ForgeScript { } else if (property.equals("OppCtrl")) { return sa.getActivatingPlayer().isOpponentOf(sourceController); } else if (property.startsWith("cmc")) { - int y = sa.getPayCosts().getTotalMana().getCMC(); + int y = 0; + // spell was on the stack + if (sa.getCardState().getCard().isInZone(ZoneType.Stack)) { + y = sa.getHostCard().getCMC(); + } + else { + y = sa.getPayCosts().getTotalMana().getCMC(); + } int x = AbilityUtils.calculateAmount(spellAbility.getHostCard(), property.substring(5), spellAbility); if (!Expressions.compare(y, property, x)) { return false; diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 3cdd63bf904..3c00832542c 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -329,7 +329,6 @@ public abstract class SpellAbilityEffect { } protected static void addSelfTrigger(final SpellAbility sa, String location, final Card card) { - String trigStr = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield " + "| TriggerDescription$ At the beginning of the end step, " + location.toLowerCase() + " CARDNAME."; diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java index 8485f02f2d7..c0990697197 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java @@ -60,9 +60,9 @@ public class DamageDealEffect extends DamageBaseEffect { // if use targeting we show all targets and corresponding damage if (spellAbility.usesTargeting()) { if (spellAbility.hasParam("DivideEvenly")) { - stringBuilder.append("divided evenly (rounded down) to\n"); + stringBuilder.append("divided evenly (rounded down) to \n"); } else if (spellAbility.isDividedAsYouChoose()) { - stringBuilder.append("divided to\n"); + stringBuilder.append("divided to \n"); } else stringBuilder.append("to "); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java index 1160541e5fa..4f0acaa45be 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java @@ -45,7 +45,6 @@ public class DestroyAllEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { - final boolean noRegen = sa.hasParam("NoRegen"); final Card card = sa.getHostCard(); final Game game = sa.getActivatingPlayer().getGame(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java index ba98dfdccf3..0a1042cc0ae 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java @@ -150,7 +150,6 @@ public class PumpEffect extends SpellAbilityEffect { */ @Override protected String getStackDescription(final SpellAbility sa) { - final StringBuilder sb = new StringBuilder(); List tgts = Lists.newArrayList(); tgts.addAll(getCardsfromTargets(sa)); 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 75b1ac7f966..afebbeeeed1 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1262,8 +1262,15 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public final int getXManaCostPaid() { - if (getCastSA() != null) { - Integer paid = getCastSA().getXManaCostPaid(); + SpellAbility castSA; + if (getCopiedPermanent() != null) { + castSA = getCopiedPermanent().getCastSA(); + } + else { + castSA = getCastSA(); + } + if (castSA != null) { + Integer paid = castSA.getXManaCostPaid(); return paid == null ? 0 : paid; } return 0; diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index ebee7dfacc8..3557156406d 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -534,7 +534,6 @@ public class CardFactory { } public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki) { - if (from.getTargetRestrictions() != null) { to.setTargetRestrictions(from.getTargetRestrictions()); } @@ -562,7 +561,7 @@ public class CardFactory { to.setConditions((SpellAbilityCondition) from.getConditions().copy()); } - // do this after other abilties are copied + // do this after other abilities are copied if (p != null) { to.setActivatingPlayer(p, lki); } diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java index 58fb8f511a0..d740b3640e9 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java @@ -22,10 +22,9 @@ import io.sentry.event.BreadcrumbBuilder; public abstract class KeywordInstance> implements KeywordInterface { private Keyword keyword; private String original; - - + private boolean hidden; - + private List triggers = Lists.newArrayList(); private List replacements = Lists.newArrayList(); private List abilities = Lists.newArrayList(); @@ -53,7 +52,7 @@ public abstract class KeywordInstance> implements K public String getReminderText() { String result = formatReminderText(keyword.reminderText); Matcher m = Pattern.compile("\\{(\\w):(.+?)\\}").matcher(result); - + StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, Lang.nounWithNumeral(m.group(1), m.group(2))); @@ -75,7 +74,7 @@ public abstract class KeywordInstance> implements K } protected abstract void parse(String details); protected abstract String formatReminderText(String reminderText); - + /* * (non-Javadoc) @@ -181,7 +180,7 @@ public abstract class KeywordInstance> implements K public final void addTrigger(final Trigger trg) { triggers.add(trg); } - + /* * (non-Javadoc) * @see forge.game.keyword.KeywordInterface#addReplacement(forge.game.replacement.ReplacementEffect) @@ -197,7 +196,7 @@ public abstract class KeywordInstance> implements K public final void addSpellAbility(final SpellAbility s) { abilities.add(s); } - + /* * (non-Javadoc) * @see forge.game.keyword.KeywordInterface#addStaticAbility(forge.game.staticability.StaticAbility) @@ -205,9 +204,7 @@ public abstract class KeywordInstance> implements K public final void addStaticAbility(final StaticAbility st) { staticAbilities.add(st); } - - - + /* (non-Javadoc) * @see forge.game.keyword.KeywordInterface#getHidden() */ @@ -222,7 +219,7 @@ public abstract class KeywordInstance> implements K public void setHidden(boolean val) { hidden = val; } - + /* * (non-Javadoc) * @see forge.game.keyword.KeywordInterface#getTriggers() 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 a96884aaeb4..00994aa4a53 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -960,7 +960,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return this.isAlternativeCost(AlternativeCost.Foretold); } - /** * @return the aftermath */ diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java index 4ba13945094..524d41b5f53 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java @@ -111,7 +111,6 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView { ability.resetPaidHash(); splicedCards = sa.getSplicedCards(); - // TODO getXManaCostPaid should be on the SA, not the Card xManaPaid = sa.getXManaCostPaid(); // Triggering info diff --git a/forge-gui/res/cardsfolder/d/deekah_fractal_theorist.txt b/forge-gui/res/cardsfolder/d/deekah_fractal_theorist.txt index 334094f4506..19f68771b36 100644 --- a/forge-gui/res/cardsfolder/d/deekah_fractal_theorist.txt +++ b/forge-gui/res/cardsfolder/d/deekah_fractal_theorist.txt @@ -4,9 +4,8 @@ Types:Legendary Creature Human Wizard PT:3/3 T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is that spell's mana value. SVar:TrigToken:DB$ Token | TokenScript$ gu_0_0_fractal | RememberTokens$ True | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ X | SubAbility$ DBCleanup +SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ TriggerCount$CastSACMC | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:X:TriggerCount$CastSACMC A:AB$ Pump | Cost$ 3 U | KW$ HIDDEN Unblockable | TgtPrompt$ Select target creature token | ValidTgts$ Creature.token | SpellDescription$ Target creature token can't be blocked this turn. DeckHas:Ability$Token & Ability$Counters DeckNeeds:Type$Instant|Sorcery diff --git a/forge-gui/res/cardsfolder/g/gluttonous_troll.txt b/forge-gui/res/cardsfolder/g/gluttonous_troll.txt index 042a2e17169..7aacea986bb 100644 --- a/forge-gui/res/cardsfolder/g/gluttonous_troll.txt +++ b/forge-gui/res/cardsfolder/g/gluttonous_troll.txt @@ -9,5 +9,5 @@ SVar:X:PlayerCountOpponents$Amount SVar:PlayMain1:TRUE DeckHas:Ability$Token & Ability$LifeGain & Ability$Food & Ability$Sacrifice SVar:AIPreference:SacCost$Card.Food,Card.token,Card.cmcEQ1,Card.cmcEQ2 -A:AB$ Pump | Cost$ 1 G Sac<1/Permanent.Other/another nonland permanent> | Defined$ Self | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ CARDNAME gets +2/+2 until end of turn. +A:AB$ Pump | Cost$ 1 G Sac<1/Permanent.Other+nonLand/another nonland permanent> | Defined$ Self | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ CARDNAME gets +2/+2 until end of turn. Oracle:Trample\nWhen Gluttonous Troll enters the battlefield, create a number of Food tokens equal to the number of opponents you have. (Food tokens are artifacts with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")\n{1}{G}, Sacrifice another nonland permanent: Gluttonous Troll gets +2/+2 until end of turn. diff --git a/forge-gui/res/cardsfolder/p/prismari_apprentice.txt b/forge-gui/res/cardsfolder/p/prismari_apprentice.txt index c88bff92868..c82c2e70410 100644 --- a/forge-gui/res/cardsfolder/p/prismari_apprentice.txt +++ b/forge-gui/res/cardsfolder/p/prismari_apprentice.txt @@ -4,8 +4,7 @@ Types:Creature Human Shaman PT:2/2 T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, CARDNAME can't be blocked this turn. If that spell has mana value 5 or greater, put a +1/+1 counter on CARDNAME. SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ HIDDEN Unblockable | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE5 +SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ TriggerCount$CastSACMC | ConditionSVarCompare$ GE5 DeckNeeds:Type$Instant|Sorcery DeckHas:Ability$Counters -SVar:X:TriggeredCard$CardManaCost Oracle:Magecraft — Whenever you cast or copy an instant or sorcery spell, Prismari Apprentice can't be blocked this turn. If that spell has mana value 5 or greater, put a +1/+1 counter on Prismari Apprentice. diff --git a/forge-gui/res/cardsfolder/z/zaffai_thunder_conductor.txt b/forge-gui/res/cardsfolder/z/zaffai_thunder_conductor.txt index a876145ae45..d478c4f47e1 100644 --- a/forge-gui/res/cardsfolder/z/zaffai_thunder_conductor.txt +++ b/forge-gui/res/cardsfolder/z/zaffai_thunder_conductor.txt @@ -2,11 +2,11 @@ Name:Zaffai, Thunder Conductor ManaCost:2 U R Types:Legendary Creature Human Shaman PT:1/4 -T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ DBScry | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, scry 1. If that spell's mana value is 5 or greater, create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 ore greater, CARDNAME deals 10 damage to an opponent chosen at random. +T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ DBScry | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, scry 1. If that spell's mana value is 5 or greater, create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 or greater, CARDNAME deals 10 damage to an opponent chosen at random. SVar:DBScry:DB$ Scry | ScryNum$ 1 | SubAbility$ DBToken -SVar:DBToken:DB$ Token | TokenScript$ ur_4_4_elemental | TokenOwner$ You | ConditionPresent$ Card.cmcGE5 | ConditionDefined$ TriggeredCard | ConditionCompare$ GE1 | SubAbility$ DBChoose +SVar:DBToken:DB$ Token | TokenScript$ ur_4_4_elemental | TokenOwner$ You | ConditionCheckSVar$ TriggerCount$CastSACMC | ConditionSVarCompare$ GE5 | SubAbility$ DBChoose SVar:DBChoose:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | Random$ True | SubAbility$ DBDamage -SVar:DBDamage:DB$ DealDamage | NumDmg$ 10 | Defined$ ChosenPlayer | ConditionPresent$ Card.cmcGE10 | ConditionDefined$ TriggeredCard | ConditionCompare$ GE1 +SVar:DBDamage:DB$ DealDamage | NumDmg$ 10 | Defined$ ChosenPlayer | ConditionCheckSVar$ TriggerCount$CastSACMC | ConditionSVarCompare$ GE10 DeckHas:Ability$Token SVar:BuffedBy:Instant,Sorcery DeckHints:Type$Instant|Sorcery From 9681ce5f9bcf990ac17405b9b89eab3e9cab0ee9 Mon Sep 17 00:00:00 2001 From: Lyu Zong-Hong Date: Thu, 24 Jun 2021 20:10:05 +0900 Subject: [PATCH 2/3] Fix Aerial Caravan --- forge-gui/res/cardsfolder/a/aerial_caravan.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/forge-gui/res/cardsfolder/a/aerial_caravan.txt b/forge-gui/res/cardsfolder/a/aerial_caravan.txt index 021b754fcc4..88da381804d 100644 --- a/forge-gui/res/cardsfolder/a/aerial_caravan.txt +++ b/forge-gui/res/cardsfolder/a/aerial_caravan.txt @@ -3,9 +3,8 @@ ManaCost:4 U U Types:Creature Human Soldier PT:4/3 K:Flying -A:AB$ Dig | Cost$ 1 U U | Defined$ You | DigNum$ 1 | Destination$ Exile | RememberChanged$ True | SubAbility$ DBEffect | AILogic$ ExileAndPlayUntilEOT | SpellDescription$ Exile the top card of your library. Until end of turn, you may play that card. (Reveal the card as you exile it.) +A:AB$ Dig | Cost$ 1 U U | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | AILogic$ ExileAndPlayUntilEOT | SpellDescription$ Exile the top card of your library. Until end of turn, you may play that card. (Reveal the card as you exile it.) SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ExileOnMoved$ Exile SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play remembered card. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:Picture:http://www.wizards.com/global/images/magic/general/aerial_caravan.jpg Oracle:Flying\n{1}{U}{U}: Exile the top card of your library. Until end of turn, you may play that card. (Reveal the card as you exile it.) From 4876fb0212cf4d07d4692c675bc4adba29935e95 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Fri, 25 Jun 2021 12:16:25 +0000 Subject: [PATCH 3/3] Fixes Cause for ETBReplacementEffect --- .../java/forge/game/ability/effects/ETBReplacementEffect.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java index 846cafbbe82..15978d35795 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java @@ -18,6 +18,8 @@ public class ETBReplacementEffect extends SpellAbilityEffect { Map params = AbilityKey.newMap(); params.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI)); params.put(AbilityKey.ReplacementEffect, sa.getReplacementEffect()); - sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), sa, params); + final SpellAbility root = sa.getRootAbility(); + SpellAbility cause = (SpellAbility) root.getReplacingObject(AbilityKey.Cause); + sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), cause, params); } } \ No newline at end of file