Merge pull request #4 from Northmoc/blitz

SNC: Blitz keyword
This commit is contained in:
Agetian
2022-04-12 11:51:27 +03:00
committed by GitHub
11 changed files with 88 additions and 7 deletions

View File

@@ -153,6 +153,8 @@ public class ForgeScript {
} else if (property.equals("hasTapCost")) { } else if (property.equals("hasTapCost")) {
Cost cost = sa.getPayCosts(); Cost cost = sa.getPayCosts();
return cost != null && cost.hasTapCost(); return cost != null && cost.hasTapCost();
} else if (property.equals("Blitz")) {
return sa.isBlitz();
} else if (property.equals("Buyback")) { } else if (property.equals("Buyback")) {
return sa.isBuyBackAbility(); return sa.isBuyBackAbility();
} else if (property.equals("Cycling")) { } else if (property.equals("Cycling")) {

View File

@@ -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 // Clean up the temporary AtEOT SVar
String endofTurn = c.getSVar("EndOfTurnLeavePlay"); 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"); c.removeSVar("EndOfTurnLeavePlay");
} }

View File

@@ -44,18 +44,40 @@ public class CopyPermanentEffect extends TokenEffectBase {
} }
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
final Player activator = sa.getActivatingPlayer();
final List<Card> tgtCards = getTargetCards(sa); final List<Card> tgtCards = getTargetCards(sa);
boolean justOne = tgtCards.size() == 1; 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)); sb.append(Lang.joinHomogenous(tgtCards));
if (sa.hasParam("AddKeywords")) {
if (addKWs) {
final List<String> keywords = Lists.newArrayList(); final List<String> keywords = Lists.newArrayList();
keywords.addAll(Arrays.asList(sa.getParam("AddKeywords").split(" & "))); 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(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")) { if (sa.hasParam("AtEOT")) {
String atEOT = sa.getParam("AtEOT"); String atEOT = sa.getParam("AtEOT");
String verb = "Sacrifice "; String verb = "Sacrifice ";

View File

@@ -45,6 +45,11 @@ public class PermanentEffect extends SpellAbilityEffect {
c.setSVar("EndOfTurnLeavePlay", "Dash"); c.setSVar("EndOfTurnLeavePlay", "Dash");
registerDelayedTrigger(sa, "Hand", Lists.newArrayList(c)); 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(); ZoneType newZone = c.getZone().getZoneType();
if (newZone != previousZone) { if (newZone != previousZone) {

View File

@@ -2160,7 +2160,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap") || keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling") || keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")
|| keyword.startsWith("Encore") || keyword.startsWith("Mutate") || keyword.startsWith("Dungeon") || 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 // keyword parsing takes care of adding a proper description
} else if (keyword.equals("Unblockable")) { } else if (keyword.equals("Unblockable")) {
sbLong.append(getName()).append(" can't be blocked.\r\n"); sbLong.append(getName()).append(" can't be blocked.\r\n");

View File

@@ -2706,6 +2706,26 @@ public class CardFactoryUtil {
sa.setAlternativeCost(AlternativeCost.Bestow); sa.setAlternativeCost(AlternativeCost.Bestow);
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
inst.addSpellAbility(sa); 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")) { } else if (keyword.startsWith("Class")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final int level = Integer.valueOf(k[1]); final int level = Integer.valueOf(k[1]);
@@ -3455,6 +3475,18 @@ public class CardFactoryUtil {
StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic); StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic);
st.setSVar("AffinityX", "Count$Valid " + t + ".YouCtrl"); 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); inst.addStaticAbility(st);
} else if (keyword.equals("Changeling")) { } else if (keyword.equals("Changeling")) {
String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" + String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" +

View File

@@ -1684,6 +1684,11 @@ public class CardProperty {
return false; return false;
} }
return card.getCastSA().isSurged(); return card.getCastSA().isSurged();
} else if (property.equals("blitzed")) {
if (card.getCastSA() == null) {
return false;
}
return card.getCastSA().isBlitz();
} else if (property.equals("dashed")) { } else if (property.equals("dashed")) {
if (card.getCastSA() == null) { if (card.getCastSA() == null) {
return false; return false;

View File

@@ -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."), 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."), 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."), 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."), 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."), 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."), 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."),

View File

@@ -3,6 +3,7 @@ package forge.game.spellability;
public enum AlternativeCost { public enum AlternativeCost {
Awaken, Awaken,
Bestow, Bestow,
Blitz,
Cycling, // ActivatedAbility Cycling, // ActivatedAbility
Dash, Dash,
Disturb, Disturb,

View File

@@ -1424,6 +1424,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return isAlternativeCost(AlternativeCost.Bestow); return isAlternativeCost(AlternativeCost.Bestow);
} }
public final boolean isBlitz() {
return isAlternativeCost(AlternativeCost.Blitz);
}
public final boolean isDash() { public final boolean isDash() {
return isAlternativeCost(AlternativeCost.Dash); return isAlternativeCost(AlternativeCost.Dash);
} }

View File

@@ -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.)