From 39bfeb5d62fdc71c97aaf209eb4732bde389fd8e Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:12:15 -0400 Subject: [PATCH 01/11] SpellAbility add isBlitz boolean --- .../src/main/java/forge/game/spellability/SpellAbility.java | 4 ++++ 1 file changed, 4 insertions(+) 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 6560286585e..4740745cfa4 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1424,6 +1424,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return isAlternativeCost(AlternativeCost.Bestow); } + public final boolean isBlitz() { + return isAlternativeCost(AlternativeCost.Blitz); + } + public final boolean isDash() { return isAlternativeCost(AlternativeCost.Dash); } From a8a69ea77cd1b0e81eec6747cddfd3c158fd8546 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:13:06 -0400 Subject: [PATCH 02/11] Keyword.java add Blitz --- forge-game/src/main/java/forge/game/keyword/Keyword.java | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index 9912796aac4..8b4f3788d59 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -31,6 +31,7 @@ public enum Keyword { BANDING("Banding", SimpleKeyword.class, true, "Any creatures with banding, and up to one without, can attack in a band. Bands are blocked as a group. If any creatures with banding you control are blocking or being blocked by a creature, you divide that creature's combat damage, not its controller, among any of the creatures it's being blocked by or is blocking."), BATTLE_CRY("Battle cry", SimpleKeyword.class, false, "Whenever this creature attacks, each other attacking creature gets +1/+0 until end of turn."), BESTOW("Bestow", KeywordWithCost.class, false, "If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature."), + BLITZ("Blitz", KeywordWithCost.class, false, "If you cast this spell for its blitz cost, it gains haste and \"When this creature dies, draw a card.\" Sacrifice it at the beginning of the next end step."), BLOODTHIRST("Bloodthirst", KeywordWithAmount.class, false, "If an opponent was dealt damage this turn, this creature enters the battlefield with {%d:+1/+1 counter} on it."), BUSHIDO("Bushido", KeywordWithAmount.class, false, "Whenever this creature blocks or becomes blocked, it gets +%1$d/+%1$d until end of turn."), BUYBACK("Buyback", KeywordWithCost.class, false, "You may pay an additional %s as you cast this spell. If you do, put it into your hand instead of your graveyard as it resolves."), From 7c77465fc180a397e5cad692a248a40ac98142bb Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:13:37 -0400 Subject: [PATCH 03/11] GameAction.java add Blitz to Dash cleanup --- forge-game/src/main/java/forge/game/GameAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 47f0335df5b..a69075a3342 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -234,10 +234,10 @@ public class GameAction { } } - // Clean up the temporary Dash SVar when the Dashed card leaves the battlefield + // Clean up the temporary Dash/Blitz SVar when the card leaves the battlefield // Clean up the temporary AtEOT SVar String endofTurn = c.getSVar("EndOfTurnLeavePlay"); - if (fromBattlefield && (endofTurn.equals("Dash") || endofTurn.equals("AtEOT"))) { + if (fromBattlefield && (endofTurn.equals("Dash") || endofTurn.equals("Blitz") || endofTurn.equals("AtEOT"))) { c.removeSVar("EndOfTurnLeavePlay"); } From 5ed640b96a7b09a32dc5044aa3f79c647c9ed3c0 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:14:37 -0400 Subject: [PATCH 04/11] ForgeScript add "Blitz" --- forge-game/src/main/java/forge/game/ForgeScript.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 0e1a178063b..cab22a401f8 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -153,6 +153,8 @@ public class ForgeScript { } else if (property.equals("hasTapCost")) { Cost cost = sa.getPayCosts(); return cost != null && cost.hasTapCost(); + } else if (property.equals("Blitz")) { + return sa.isBlitz(); } else if (property.equals("Buyback")) { return sa.isBuyBackAbility(); } else if (property.equals("Cycling")) { From c2996b55769e27c8005d759b0c15e457f870b55d Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:15:20 -0400 Subject: [PATCH 05/11] CardProperty.java add "blitzed" --- forge-game/src/main/java/forge/game/card/CardProperty.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index ba2cd97540e..a85b096222e 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1680,6 +1680,11 @@ public class CardProperty { return false; } return card.getCastSA().isSurged(); + } else if (property.equals("blitzed")) { + if (card.getCastSA() == null) { + return false; + } + return card.getCastSA().isBlitz(); } else if (property.equals("dashed")) { if (card.getCastSA() == null) { return false; From 8645a292962103b5ef1deeeb8d0be9ef49b9e48f Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:16:30 -0400 Subject: [PATCH 06/11] CardFactoryUtil.java add Blitz alt cost and static --- .../java/forge/game/card/CardFactoryUtil.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) 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 145ca73b613..07b3475cbbe 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2693,6 +2693,26 @@ public class CardFactoryUtil { sa.setAlternativeCost(AlternativeCost.Bestow); sa.setIntrinsic(intrinsic); inst.addSpellAbility(sa); + } else if (keyword.startsWith("Blitz")) { + final String[] k = keyword.split(":"); + final Cost blitzCost = new Cost(k[1], false); + + final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(blitzCost); + + final StringBuilder desc = new StringBuilder(); + desc.append("Blitz ").append(blitzCost.toSimpleString()).append(" ("); + desc.append(inst.getReminderText()); + desc.append(")"); + + newSA.setDescription(desc.toString()); + + final StringBuilder sb = new StringBuilder(); + sb.append(card.getName()).append(" (Blitz)"); + newSA.setStackDescription(sb.toString()); + + newSA.setAlternativeCost(AlternativeCost.Blitz); + newSA.setIntrinsic(intrinsic); + inst.addSpellAbility(newSA); } else if (keyword.startsWith("Class")) { final String[] k = keyword.split(":"); final int level = Integer.valueOf(k[1]); @@ -3442,6 +3462,18 @@ public class CardFactoryUtil { StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic); st.setSVar("AffinityX", "Count$Valid " + t + ".YouCtrl"); + inst.addStaticAbility(st); + } else if (keyword.startsWith("Blitz")) { + String effect = "Mode$ Continuous | Affected$ Card.Self+blitzed | AddKeyword$ Haste | AddTrigger$ Dies"; + String trig = "Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | " + + "Execute$ TrigDraw | TriggerDescription$ When this creature dies, draw a card."; + String ab = "DB$ Draw | NumCards$ 1"; + + StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic); + + st.setSVar("Dies", trig); + st.setSVar("TrigDraw", ab); + inst.addStaticAbility(st); } else if (keyword.equals("Changeling")) { String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" + From e0953abbfdc11fe3e65dbccb10f00feed8acca6f Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:18:09 -0400 Subject: [PATCH 07/11] Card.java add Blitz keyword to "don't parse reminder text" list --- forge-game/src/main/java/forge/game/card/Card.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5a7b41c2d98..4c08c7357c4 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2153,7 +2153,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { || keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap") || keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling") || keyword.startsWith("Encore") || keyword.startsWith("Mutate") || keyword.startsWith("Dungeon") - || keyword.startsWith("Class") || keyword.startsWith("Saga")) { + || keyword.startsWith("Class") || keyword.startsWith("Saga") || keyword.startsWith("Blitz")) { // keyword parsing takes care of adding a proper description } else if (keyword.equals("Unblockable")) { sbLong.append(getName()).append(" can't be blocked.\r\n"); From 4b7af16c980febbb895d18dd3606e02d00c5726c Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 13:18:23 -0400 Subject: [PATCH 08/11] AlternativeCost add Blitz --- .../src/main/java/forge/game/spellability/AlternativeCost.java | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java index 6739271e7c2..e39bf7b8abe 100644 --- a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java +++ b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java @@ -3,6 +3,7 @@ package forge.game.spellability; public enum AlternativeCost { Awaken, Bestow, + Blitz, Cycling, // ActivatedAbility Dash, Disturb, From bd6d7d7722d69900a7294fd1dabd7a23e21b33d2 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 14:05:26 -0400 Subject: [PATCH 09/11] jaxis_the_troublemaker.txt --- .../res/cardsfolder/upcoming/jaxis_the_troublemaker.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 forge-gui/res/cardsfolder/upcoming/jaxis_the_troublemaker.txt diff --git a/forge-gui/res/cardsfolder/upcoming/jaxis_the_troublemaker.txt b/forge-gui/res/cardsfolder/upcoming/jaxis_the_troublemaker.txt new file mode 100644 index 00000000000..6396dd6a7db --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/jaxis_the_troublemaker.txt @@ -0,0 +1,9 @@ +Name:Jaxis, the Troublemaker +ManaCost:3 R +Types:Legendary Creature Human Warrior +PT:2/3 +A:AB$ CopyPermanent | Cost$ R T Discard<1/Card> | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | AddKeywords$ Haste | AddTriggers$ Dies | AddSVars$ TrigDraw | AtEOT$ Sacrifice | SorcerySpeed$ True | SpellDescription$ Create a token that's a copy of another target creature you control. It gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step. Activate only as a sorcery. +SVar:Dies:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When this creature dies, draw a card. +SVar:TrigDraw:DB$ Draw | NumCards$ 1 +K:Blitz:1 R +Oracle:{R}, {T}, Discard a card: Create a token that's a copy of another target creature you control. It gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step. Activate only as a sorcery.\nBlitz {1}{R} (If you cast this spell for its blitz cost, it gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step.) From 34eba498cd7ac7f7774f43541becc61fa4459bbd Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 14:06:45 -0400 Subject: [PATCH 10/11] CopyPermanentEffect more getStackDescription work --- .../ability/effects/CopyPermanentEffect.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index f60128d48d6..7ab85616188 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -44,18 +44,40 @@ public class CopyPermanentEffect extends TokenEffectBase { } final StringBuilder sb = new StringBuilder(); + final Player activator = sa.getActivatingPlayer(); final List tgtCards = getTargetCards(sa); boolean justOne = tgtCards.size() == 1; + boolean addKWs = sa.hasParam("AddKeywords"); + final int numCopies = sa.hasParam("NumCopies") ? + AbilityUtils.calculateAmount(host, sa.getParam("NumCopies"), sa) : 1; - sb.append("Copy "); + sb.append(activator).append(" creates ").append(Lang.nounWithNumeralExceptOne(numCopies, "token"); + sb.append(numCopies == 1 ? " that's a copy" : " that are copies").append(" of "); sb.append(Lang.joinHomogenous(tgtCards)); - if (sa.hasParam("AddKeywords")) { + + if (addKWs) { final List keywords = Lists.newArrayList(); keywords.addAll(Arrays.asList(sa.getParam("AddKeywords").split(" & "))); - sb.append(", except ").append(justOne ? "it has " : "they have "); + if (sa.getDescription().contains("except")) { + sb.append(", except ").append(justOne ? "it has " : "they have "); + } else { + sb.append(". ").append(justOne ? "It gains " : "They gain "); + } sb.append(Lang.joinHomogenous(keywords).toLowerCase()); } - sb.append("."); + + if (sa.hasParam("AddTriggers")) { + final String oDesc = sa.getDescription(); + final String trigStg = oDesc.substring(oDesc.indexOf("\""),oDesc.lastIndexOf("\"") + 1); + if (addKWs) { + sb.append(" and ").append(trigStg); + } else { + sb.append(". ").append(justOne ? "It gains " : "They gain ").append(trigStg); + } + } else { + sb.append("."); + } + if (sa.hasParam("AtEOT")) { String atEOT = sa.getParam("AtEOT"); String verb = "Sacrifice "; From d9ee7c88635f303b779ad5b265e249e80ea7a97b Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sun, 10 Apr 2022 14:09:03 -0400 Subject: [PATCH 11/11] PermanentEffect.java - Blitz DelayedTrigger --- .../java/forge/game/ability/effects/PermanentEffect.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java index dfd2c41b680..ddb356c4da1 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java @@ -45,6 +45,11 @@ public class PermanentEffect extends SpellAbilityEffect { c.setSVar("EndOfTurnLeavePlay", "Dash"); registerDelayedTrigger(sa, "Hand", Lists.newArrayList(c)); } + // similar for Blitz keyword + if (sa.isBlitz() && c.isInPlay()) { + c.setSVar("EndOfTurnLeavePlay", "Blitz"); + registerDelayedTrigger(sa, "Sacrifice", Lists.newArrayList(c)); + } ZoneType newZone = c.getZone().getZoneType(); if (newZone != previousZone) {