Merge pull request #1823 from Northmoc/prototype

BRO: Prototype
This commit is contained in:
Anthony Calosa
2022-11-08 19:25:58 +08:00
committed by GitHub
8 changed files with 81 additions and 4 deletions

View File

@@ -222,6 +222,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private long transformedTimestamp = 0; private long transformedTimestamp = 0;
private long convertedTimestamp = 0; private long convertedTimestamp = 0;
private long mutatedTimestamp = -1; private long mutatedTimestamp = -1;
private long prototypeTimestamp = -1;
private int timesMutated = 0; private int timesMutated = 0;
private boolean tributed = false; private boolean tributed = false;
private boolean embalmed = false; private boolean embalmed = false;
@@ -988,7 +989,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public boolean isCloned() { public boolean isCloned() {
return !clonedStates.isEmpty() && clonedStates.lastEntry().getKey() != mutatedTimestamp; return !clonedStates.isEmpty() && clonedStates.lastEntry().getKey() != mutatedTimestamp
&& clonedStates.lastEntry().getKey() != prototypeTimestamp;
} }
public final CardCollectionView getDevouredCards() { public final CardCollectionView getDevouredCards() {
@@ -2284,7 +2286,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("Blitz") || keyword.startsWith("Class") || keyword.startsWith("Blitz") || keyword.startsWith("Prototype")
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")) { || keyword.startsWith("Specialize") || keyword.equals("Ravenous")) {
// keyword parsing takes care of adding a proper description // keyword parsing takes care of adding a proper description
} else if(keyword.startsWith("Read ahead")) { } else if(keyword.startsWith("Read ahead")) {
@@ -6494,6 +6496,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
if (sa.isBestow()) { if (sa.isBestow()) {
animateBestow(); animateBestow();
} }
if (sa.hasParam("Prototype")) {
Long next = game.getNextTimestamp();
addCloneState(CardFactory.getCloneStates(this, this, sa), next);
prototypeTimestamp = next;
}
CardStateName stateName = sa.getCardStateName(); CardStateName stateName = sa.getCardStateName();
if (stateName != null && hasState(stateName) && this.getCurrentStateName() != stateName) { if (stateName != null && hasState(stateName) && this.getCurrentStateName() != stateName) {
setState(stateName, true); setState(stateName, true);

View File

@@ -24,6 +24,7 @@ import forge.ImageKeys;
import forge.StaticData; import forge.StaticData;
import forge.card.*; import forge.card.*;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.CardTraitBase; import forge.game.CardTraitBase;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
@@ -688,6 +689,14 @@ public class CardFactory {
colors = ColorSet.fromNames(sa.getParam("SetColor").split(",")); colors = ColorSet.fromNames(sa.getParam("SetColor").split(","));
} }
if (sa.hasParam("SetColorByManaCost")) {
if (sa.hasParam("SetManaCost")) {
colors = ColorSet.fromManaCost(new ManaCost(new ManaCostParser(sa.getParam("SetManaCost"))));
} else {
colors = ColorSet.fromManaCost(host.getManaCost());
}
}
// TODO handle Volrath's Shapeshifter // TODO handle Volrath's Shapeshifter
if (in.isFaceDown()) { if (in.isFaceDown()) {
@@ -736,7 +745,7 @@ public class CardFactory {
state.addColor(colors.getColor()); state.addColor(colors.getColor());
} }
if (sa.hasParam("SetColor")) { if (sa.hasParam("SetColor") || sa.hasParam("SetColorByManaCost")) {
state.setColor(colors.getColor()); state.setColor(colors.getColor());
} }
@@ -777,6 +786,10 @@ public class CardFactory {
state.setManaCost(ManaCost.NO_COST); state.setManaCost(ManaCost.NO_COST);
} }
if (sa.hasParam("SetManaCost")) {
state.setManaCost(new ManaCost(new ManaCostParser(sa.getParam("SetManaCost"))));
}
// SVars to add to clone // SVars to add to clone
if (sa.hasParam("AddSVars") || sa.hasParam("GainTextSVars")) { if (sa.hasParam("AddSVars") || sa.hasParam("GainTextSVars")) {
final String str = sa.getParamOrDefault("GainTextSVars", sa.getParam("AddSVars")); final String str = sa.getParamOrDefault("GainTextSVars", sa.getParam("AddSVars"));
@@ -900,7 +913,8 @@ public class CardFactory {
if (sa.hasParam("SetCreatureTypes")) { if (sa.hasParam("SetCreatureTypes")) {
state.removeIntrinsicKeyword("Changeling"); state.removeIntrinsicKeyword("Changeling");
} }
if (sa.hasParam("SetColor") || sa.hasParam("Embalm") || sa.hasParam("Eternalize")) { if (sa.hasParam("SetColor") || sa.hasParam("Embalm") || sa.hasParam("Eternalize")
|| sa.hasParam("SetColorByManaCost")) {
state.removeIntrinsicKeyword("Devoid"); state.removeIntrinsicKeyword("Devoid");
} }
} }

View File

@@ -3189,6 +3189,33 @@ public class CardFactoryUtil {
sa.setIntrinsic(intrinsic); sa.setIntrinsic(intrinsic);
sa.setAlternativeCost(AlternativeCost.Outlast); sa.setAlternativeCost(AlternativeCost.Outlast);
inst.addSpellAbility(sa); inst.addSpellAbility(sa);
} else if (keyword.startsWith("Prototype")) {
final String[] k = keyword.split(":");
if (k.length < 4) {
System.err.println("Malformed Prototype entry! - Card: " + card.toString());
return;
}
final Cost protoCost = new Cost(k[1], false);
final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(protoCost);
newSA.putParam("SetManaCost", k[1]);
newSA.putParam("SetColorByManaCost", "True");
newSA.putParam("SetPower", k[2]);
newSA.putParam("SetToughness", k[3]);
newSA.putParam("PrecostDesc", "Prototype");
newSA.putParam("Prototype", "True");
newSA.putParam("CostDesc", ManaCostParser.parse(k[1]));
// makes new SpellDescription
final StringBuilder sb = new StringBuilder();
sb.append(newSA.getCostDescription()).append("[").append(k[2]).append("/").append(k[3]).append("] ");
sb.append("(").append(inst.getReminderText()).append(")");
newSA.setDescription(sb.toString());
newSA.setAlternativeCost(AlternativeCost.Prototype);
newSA.setIntrinsic(intrinsic);
inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Prowl")) { } else if (keyword.startsWith("Prowl")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final Cost prowlCost = new Cost(k[1], false); final Cost prowlCost = new Cost(k[1], false);

View File

@@ -135,6 +135,7 @@ public enum Keyword {
POISONOUS("Poisonous", KeywordWithAmount.class, false, "Whenever this creature deals combat damage to a player, that player gets {%d:poison counter}."), POISONOUS("Poisonous", KeywordWithAmount.class, false, "Whenever this creature deals combat damage to a player, that player gets {%d:poison counter}."),
PRESENCE("Presence", KeywordWithType.class, false, "As an additional cost to cast this spell, you may reveal a %s card from your hand."), PRESENCE("Presence", KeywordWithType.class, false, "As an additional cost to cast this spell, you may reveal a %s card from your hand."),
PROTECTION("Protection", Protection.class, false, "This creature can't be blocked, targeted, dealt damage, or equipped/enchanted by %s."), PROTECTION("Protection", Protection.class, false, "This creature can't be blocked, targeted, dealt damage, or equipped/enchanted by %s."),
PROTOTYPE("Prototype", KeywordWithCost.class, false, "You may cast this spell with different mana cost, color, and size. It keeps its abilities and types."),
PROVOKE("Provoke", SimpleKeyword.class, false, "Whenever this creature attacks, you may have target creature defending player controls untap and block it if able."), PROVOKE("Provoke", SimpleKeyword.class, false, "Whenever this creature attacks, you may have target creature defending player controls untap and block it if able."),
PROWESS("Prowess", SimpleKeyword.class, false, "Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn."), PROWESS("Prowess", SimpleKeyword.class, false, "Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn."),
PROWL("Prowl", KeywordWithCost.class, false, "You may pay %s rather than pay this spell's mana cost if a player was dealt combat damage this turn by a source that, at the time it dealt that damage, was under your control and had any of this spell's creature types."), PROWL("Prowl", KeywordWithCost.class, false, "You may pay %s rather than pay this spell's mana cost if a player was dealt combat damage this turn by a source that, at the time it dealt that damage, was under your control and had any of this spell's creature types."),

View File

@@ -17,6 +17,7 @@ public enum AlternativeCost {
Mutate, Mutate,
Offering, Offering,
Outlast, // ActivatedAbility Outlast, // ActivatedAbility
Prototype,
Prowl, Prowl,
Spectacle, Spectacle,
Surge; Surge;

View File

@@ -0,0 +1,10 @@
Name:Combat Thresher
ManaCost:7
Types:Artifact Creature Construct
PT:3/3
K:Prototype:2 W:1:1
K:Double Strike
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw a card.
SVar:TrigDraw:DB$ Draw
DeckHints:Color$White
Oracle:Prototype {2}{W} — 1/1 (You may cast this spell with different mana cost, color, and size. It keeps its abilities and types.)\nDouble strike\nWhen Combat Thresher enters the battlefield, draw a card.

View File

@@ -0,0 +1,9 @@
Name:Cradle Clearcutter
ManaCost:6
Types:Artifact Creature Golem
PT:3/6
K:Prototype:2 G:1:3
A:AB$ Mana | Cost$ T | Produced$ G | Amount$ X | SpellDescription$ Add an amount of {G} equal to CARDNAME's power.
SVar:X:Count$CardPower
DeckHints:Color$Green
Oracle:Prototype {2}{G} - 1/3 (You may cast this spell with different mana cost, color, and size. It keeps its abilities and types.)\n{T}: Add an amount of {G} equal to Cradle Clearcutter's power.

View File

@@ -0,0 +1,8 @@
Name:Goring Warplow
ManaCost:6
Types:Artifact Creature Construct
PT:5/4
K:Prototype:1 B:1:1
K:Deathtouch
DeckHints:Color$Black
Oracle:Prototype {1}{B} — 1/1 (You may cast this spell with different mana cost, color, and size. It keeps its abilities and types.)\nDeathtouch