Merge branch 'roll_a_d20' into 'master'

Update RollDiceEffect to better support d20 mechanism in AFR

See merge request core-developers/forge!4944
This commit is contained in:
Michael Kamensky
2021-07-05 02:38:58 +00:00
31 changed files with 307 additions and 101 deletions

View File

@@ -28,6 +28,7 @@ import forge.card.CardStateName;
import forge.game.CardTraitBase;
import forge.game.IHasSVars;
import forge.game.ability.effects.CharmEffect;
import forge.game.ability.effects.RollDiceEffect;
import forge.game.card.Card;
import forge.game.card.CardState;
import forge.game.cost.Cost;
@@ -291,9 +292,12 @@ public final class AbilityFactory {
}
if (api == ApiType.RollDice) {
for (String param : mapParams.keySet()) {
if (param.startsWith("On") || param.equals("Else")) {
spellAbility.setAdditionalAbility(param, getSubAbility(state, mapParams.get(param), sVarHolder));
final String key = "ResultSubAbilities";
if (mapParams.containsKey(key)) {
String [] diceAbilities = mapParams.get(key).split(",");
for (String ab : diceAbilities) {
String [] kv = ab.split(":");
spellAbility.setAdditionalAbility(kv[0], getSubAbility(state, kv[1], sVarHolder));
}
}
}
@@ -311,7 +315,6 @@ public final class AbilityFactory {
}
sb.append(mapParams.get("SpellDescription"));
spellAbility.setDescription(sb.toString());
} else if (api == ApiType.Charm) {
spellAbility.setDescription(CharmEffect.makeFormatedDescription(spellAbility));
@@ -319,6 +322,12 @@ public final class AbilityFactory {
spellAbility.setDescription("");
}
if (api == ApiType.RollDice) {
spellAbility.setDescription(spellAbility.getDescription() + RollDiceEffect.makeFormatedDescription(spellAbility));
} else if (api == ApiType.Repeat) {
spellAbility.setDescription(spellAbility.getDescription() + spellAbility.getAdditionalAbility("RepeatSubAbility").getDescription());
}
initializeParams(spellAbility);
makeRestrictions(spellAbility);
makeConditions(spellAbility);

View File

@@ -740,7 +740,18 @@ public class AbilityUtils {
final SpellAbility root = sa.getRootAbility();
final String[] l = calcX[1].split("/");
final String m = CardFactoryUtil.extractOperators(calcX[1]);
final Integer count = (Integer) root.getTriggeringObject(AbilityKey.fromString(l[0]));
Integer count = null;
if (calcX[0].endsWith("Max")) {
@SuppressWarnings("unchecked")
Iterable<Integer> numbers = (Iterable<Integer>) root.getTriggeringObject(AbilityKey.fromString(l[0]));
for (Integer n : numbers) {
if (count == null || n > count) {
count = n;
}
}
} else {
count = (Integer) root.getTriggeringObject(AbilityKey.fromString(l[0]));
}
val = doXMath(ObjectUtils.firstNonNull(count, 0), m, card, ability);
}

View File

@@ -19,6 +19,28 @@ import forge.util.MyRandom;
public class RollDiceEffect extends SpellAbilityEffect {
public static String makeFormatedDescription(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
final String key = "ResultSubAbilities";
if (sa.hasParam(key)) {
String [] diceAbilities = sa.getParam(key).split(",");
for (String ab : diceAbilities) {
String [] kv = ab.split(":");
String desc = sa.getAdditionalAbility(kv[0]).getDescription();
if (!desc.isEmpty()) {
sb.append("\n\n").append(desc);
}
}
}
return sb.toString();
}
private static int getRollAdvange(final Player player) {
String str = "If you would roll one or more dice, instead roll that many dice plus one and ignore the lowest roll.";
return player.getKeywords().getAmount(str);
}
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
*/
@@ -32,9 +54,9 @@ public class RollDiceEffect extends SpellAbilityEffect {
} else {
stringBuilder.append(player).append(" rolls ");
}
stringBuilder.append(sa.getParamOrDefault("Amt", "a")).append(" ");
stringBuilder.append(sa.getParamOrDefault("Amount", "a")).append(" ");
stringBuilder.append(sa.getParamOrDefault("Sides", "6")).append("-sided ");
if (sa.getParamOrDefault("Amt", "1").equals("1")) {
if (sa.getParamOrDefault("Amount", "1").equals("1")) {
stringBuilder.append("die.");
} else {
stringBuilder.append("dice.");
@@ -42,6 +64,73 @@ public class RollDiceEffect extends SpellAbilityEffect {
return stringBuilder.toString();
}
private void rollDice(SpellAbility sa, Player player, int amount, int sides) {
final Card host = sa.getHostCard();
final int modifier = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Modifier", "0"), sa);
final int advantage = getRollAdvange(player);
amount += advantage;
int total = 0;
List<Integer> rolls = new ArrayList<>();
for (int i = 0; i < amount; i++) {
int roll = MyRandom.getRandom().nextInt(sides) + 1;
rolls.add(roll);
total += roll;
}
if (amount > 0) {
String message = Localizer.getInstance().getMessage("lblPlayerRolledResult", player, StringUtils.join(rolls, ", "));
player.getGame().getAction().notifyOfValue(sa, player, message, null);
}
// Ignore lowest rolls
if (advantage > 0) {
rolls.sort(null);
for (int i = advantage - 1; i >= 0; --i) {
total -= rolls.get(i);
rolls.remove(i);
}
}
// Run triggers
for (Integer roll : rolls) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, player);
runParams.put(AbilityKey.Result, roll);
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
}
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, player);
runParams.put(AbilityKey.Result, rolls);
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDieOnce, runParams, false);
total += modifier;
if (sa.hasParam("ResultSVar")) {
host.setSVar(sa.getParam("ResultSVar"), Integer.toString(total));
}
Map<String, SpellAbility> diceAbilities = sa.getAdditionalAbilities();
SpellAbility resultAbility = null;
for (Map.Entry<String, SpellAbility> e: diceAbilities.entrySet()) {
String diceKey = e.getKey();
if (diceKey.contains("-")) {
String [] ranges = diceKey.split("-");
if (Integer.parseInt(ranges[0]) <= total && Integer.parseInt(ranges[1]) >= total) {
resultAbility = e.getValue();
break;
}
} else if (StringUtils.isNumeric(diceKey) && Integer.parseInt(diceKey) == total) {
resultAbility = e.getValue();
break;
}
}
if (resultAbility != null) {
AbilityUtils.resolve(resultAbility);
} else if (sa.hasAdditionalAbility("Else")) {
AbilityUtils.resolve(sa.getAdditionalAbility("Else"));
}
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityEffect#resolve(forge.card.spellability.SpellAbility)
*/
@@ -49,42 +138,13 @@ public class RollDiceEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Amt", "1"), sa);
int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Amount", "1"), sa);
int sides = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Sides", "6"), sa);
final PlayerCollection playersToRoll = getTargetPlayers(sa);
for(Player player : playersToRoll) {
int total = 0;
List<Integer> rolls = new ArrayList<>();
for (int i = 0; i < amount; i++) {
int roll = MyRandom.getRandom().nextInt(sides) + 1;
rolls.add(roll);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, player);
runParams.put(AbilityKey.Result, roll);
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
total += roll;
}
if (amount > 0) {
String message = Localizer.getInstance().getMessage("lblPlayerRolledResult", player, StringUtils.join(rolls, ", "));
player.getGame().getAction().notifyOfValue(sa, player, message, null);
}
if (sa.hasParam("ResultSVar")) {
host.setSVar(sa.getParam("ResultSVar"), ""+total);
}
if (sa.hasAdditionalAbility("OnDoubles") && rolls.get(0).equals(rolls.get(1))) {
AbilityUtils.resolve(sa.getAdditionalAbility("OnDoubles"));
}
if (sa.hasAdditionalAbility("On"+total)) {
AbilityUtils.resolve(sa.getAdditionalAbility("On"+total));
} else if (sa.hasAdditionalAbility("Else")) {
AbilityUtils.resolve(sa.getAdditionalAbility("Else"));
}
rollDice(sa, player, amount, sides);
}
}
}

View File

@@ -807,7 +807,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return description;
}
public void setDescription(final String s) {
originalDescription = s;
originalDescription = TextUtil.fastReplace(s, "VERT", "|");
description = originalDescription;
}

View File

@@ -17,7 +17,8 @@ public class TriggerRolledDie extends Trigger {
}
/** {@inheritDoc}
* @param runParams*/
* @param runParams
*/
@Override
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) {

View File

@@ -0,0 +1,41 @@
package forge.game.trigger;
import java.util.Map;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
public class TriggerRolledDieOnce extends Trigger {
public TriggerRolledDieOnce(final Map<String, String> params, final Card host, final boolean intrinsic) {
super(params, host, intrinsic);
}
/** {@inheritDoc}
* @param runParams
*/
@Override
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get(AbilityKey.Player), getParam("ValidPlayer").split(","),
this.getHostCard())) {
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Result, AbilityKey.Player);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
return Localizer.getInstance().getMessage("lblPlayer") + ": " + sa.getTriggeringObject(AbilityKey.Player) + ", " +
Localizer.getInstance().getMessage("lblResultIs", sa.getTriggeringObject(AbilityKey.Result));
}
}

View File

@@ -89,6 +89,7 @@ public enum TriggerType {
Regenerated(TriggerRegenerated.class),
Revealed(TriggerRevealed.class),
RolledDie(TriggerRolledDie.class),
RolledDieOnce(TriggerRolledDieOnce.class),
RoomEntered(TriggerEnteredRoom.class),
Sacrificed(TriggerSacrificed.class),
Scry(TriggerScry.class),

View File

@@ -52,6 +52,8 @@ public class MessageUtil {
: Localizer.getInstance().getMessage("lblPlayerActionFlip", flipper, Lang.joinVerb(flipper, value));
case Protection:
return Localizer.getInstance().getMessage("lblPlayerChooseValue", choser, value);
case RollDice:
return value;
case Vote:
String chooser = StringUtils.capitalize(mayBeYou(player, target));
return Localizer.getInstance().getMessage("lblPlayerVoteValue", chooser, value);

View File

@@ -3,7 +3,7 @@ ManaCost:1 R
Types:Creature Egg
PT:0/1
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRoll | TriggerDescription$ At the beginning of your upkeep, roll a six-sided die. If you roll a 6, sacrifice CARDNAME and create a 4/4 red Giant Bird creature token.
SVar:TrigRoll:DB$ RollDice | On6$ DBSac
SVar:TrigRoll:DB$ RollDice | ResultSubAbilities$ 6:DBSac
SVar:DBSac:DB$ Sacrifice | Defined$ Self | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenScript$ r_4_4_giant_chicken
DeckHas:Ability$Token

View File

@@ -6,9 +6,9 @@ K:ETBReplacement:Other:RollLoyal
SVar:RollLoyal:DB$ RollDice | Sides$ 4 | ResultSVar$ Result | SubAbility$ DBLoyalty | SpellDescription$ Add 1d4 loyalty counters to CARDNAME
SVar:DBLoyalty:DB$ PutCounter | Defined$ Self | CounterType$ LOYALTY | CounterNum$ Result | ETB$ True
A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | ValidTgts$ Opponent | TokenOwner$ Targeted | TokenScript$ b_1_1_skeleton_opp_life | Planeswalker$ True | SpellDescription$ Target opponent creates a 1/1 black Skeleton creature token with "When this creature dies, each opponent gains 2 life."
A:AB$ RollDice | Cost$ AddCounter<1/LOYALTY> | Sides$ 20 | On1$ DBSkipTurn | ResultSVar$ Result | Planeswalker$ True | SubAbility$ DBDraw | SpellDescription$ Roll a d20. If you roll a 1, skip your next turn. If you roll a 12 or higher, draw a card.
A:AB$ RollDice | Cost$ AddCounter<1/LOYALTY> | Sides$ 20 | ResultSubAbilities$ 1:DBSkipTurn,12-20:DBDraw | Planeswalker$ True | SpellDescription$ Roll a d20. If you roll a 1, skip your next turn. If you roll a 12 or higher, draw a card.
SVar:DBSkipTurn:DB$ SkipTurn | Defined$ You | NumTurns$ 1
SVar:DBDraw:DB$ Draw | ConditionCheckSVar$ Result | ConditionSVarCompare$ GE12
SVar:DBDraw:DB$ Draw
A:AB$ Token | Cost$ SubCounter<6/LOYALTY> | Planeswalker$ True | Ultimate$ True | TokenScript$ r_3_3_fighter_first_strike | SubAbility$ DBCleric | SpellDescription$ You get an adventuring party. (Your party is a 3/3 red Fighter with first strike, a 1/1 white Cleric with lifelink, a 2/2 black Rogue with hexproof, and a 1/1 blue Wizard with flying.)
SVar:DBCleric:DB$ Token | TokenScript$ w_1_1_cleric_lifelink | SubAbility$ DBRogue
SVar:DBRogue:DB$ Token | TokenScript$ b_2_2_rogue_hexproof | SubAbility$ DBWizard

View File

@@ -1,10 +1,10 @@
Name:Goblin Tutor
ManaCost:R
Types:Instant
A:SP$ RollDice | Cost$ R | On2$ GetGobTut | On3$ GetEnch | On4$ GetArtif | On5$ GetCreat| On6$ GetSpell | SpellDescription$ Roll a six-sided die. Search your library for the indicated card, reveal it, put it into your hand, then shuffle.\n2 — A card named Goblin Tutor\n3 — An enchantment\n4 — An artifact\n5 — A creature\n6 — An instant or sorcery
SVar:GetGobTut:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Card.namedGoblin Tutor | ChangeNum$ 1
SVar:GetEnch:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Enchantment | ChangeNum$ 1
SVar:GetArtif:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Artifact | ChangeNum$ 1
SVar:GetCreat:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Creature | ChangeNum$ 1
SVar:GetSpell:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Instant,Sorcery | ChangeNum$ 1
A:SP$ RollDice | Cost$ R | ResultSubAbilities$ 2:GetGobTut,3:GetEnch,4:GetArtif,5:GetCreat,6:GetSpell | SpellDescription$ Roll a six-sided die. If you roll a 1, CARDNAME has no effect. Otherwise, search your library for the indicated card, reveal it, put it into your hand, then shuffle.
SVar:GetGobTut:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Card.namedGoblin Tutor | ChangeNum$ 1 | SpellDescription$ 2 — A card named Goblin Tutor
SVar:GetEnch:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Enchantment | ChangeNum$ 1 | SpellDescription$ 3 — An enchantment
SVar:GetArtif:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Artifact | ChangeNum$ 1 | SpellDescription$ 4 — An artifact
SVar:GetCreat:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Creature | ChangeNum$ 1 | SpellDescription$ 5 — A creature
SVar:GetSpell:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Instant,Sorcery | ChangeNum$ 1 | SpellDescription$ 6 — An instant or sorcery
Oracle:Roll a six-sided die. If you roll a 1, Goblin Tutor has no effect. Otherwise, search your library for the indicated card, reveal it, put it into your hand, then shuffle.\n2 — A card named Goblin Tutor\n3 — An enchantment\n4 — An artifact\n5 — A creature\n6 — An instant or sorcery

View File

@@ -4,7 +4,7 @@ Types:Creature Hydra Dog
PT:0/0
K:ETBReplacement:Other:RollCounters
SVar:X:Count$xPaid
SVar:RollCounters:DB$ RollDice | Amt$ X | ETB$ True | ResultSVar$ Result | SubAbility$ DBCounters | SpellDescription$ As CARDNAME enters the battlefield, roll X six-sided dice. CARDNAME enters the battlefield with a number of +1/+1 counters on it equal to the total of those results.
SVar:RollCounters:DB$ RollDice | Amount$ X | ETB$ True | ResultSVar$ Result | SubAbility$ DBCounters | SpellDescription$ As CARDNAME enters the battlefield, roll X six-sided dice. CARDNAME enters the battlefield with a number of +1/+1 counters on it equal to the total of those results.
SVar:DBCounters:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ Result | ETB$ True
K:Reach
K:Trample

View File

@@ -3,7 +3,7 @@ ManaCost:1 B
Types:Creature Brainiac
PT:1/1
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRoll | TriggerDescription$ At the beginning of your upkeep, roll a six-sided die. On a 3 or 4, put a +1/+1 counter on Inhumaniac. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from Inhumaniac.
SVar:TrigRoll:DB$ RollDice | On3$ DBOne | On4$ DBOne | On5$ DBTwo | On6$ DBTwo | On1$ DBRemove
SVar:TrigRoll:DB$ RollDice | ResultSubAbilities$ 3-4:DBOne,5-6:DBTwo,1:DBRemove
SVar:DBOne:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
SVar:DBTwo:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 2
SVar:DBRemove:DB$ RemoveCounterAll | ValidCards$ Card.Self | CounterType$ P1P1 | AllCounters$ True

View File

@@ -1,14 +1,14 @@
Name:Jack-in-the-Mox
ManaCost:0
Types:Artifact
A:AB$ RollDice | Cost$ T | On1$ DBSac | On2$ AddW | On3$ AddU | On4$ AddB | On5$ AddR | On6$ AddG | InstantSpeed$ True | SubAbility$ Add0 | SpellDescription$ Roll a six-sided die. This ability has the indicated effect. 1—Sacrifice Jack-in-the-Mox and you lose 5 life. 2—Add {W}. 3—Add {U}. 4—Add {B}. 5-Add {R}. 6-Add {G}.
SVar:DBSac:DB$ Sacrifice | Defined$ Self | SubAbility$ DBLoseLife
A:AB$ RollDice | Cost$ T | ResultSubAbilities$ 1:DBSac,2:AddW,3:AddU,4:AddB,5:AddR,6:AddG | InstantSpeed$ True | SubAbility$ Add0 | SpellDescription$ Roll a six-sided die. This ability has the indicated effect.
SVar:DBSac:DB$ Sacrifice | Defined$ Self | SubAbility$ DBLoseLife | SpellDescription$ 1 — Sacrifice CARDNAME and you lose 5 life.
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 5
SVar:AddW:DB$ Mana | Produced$ W
SVar:AddU:DB$ Mana | Produced$ U
SVar:AddB:DB$ Mana | Produced$ B
SVar:AddR:DB$ Mana | Produced$ R
SVar:AddG:DB$ Mana | Produced$ G
SVar:AddW:DB$ Mana | Produced$ W | SpellDescription$ 2 — Add {W}.
SVar:AddU:DB$ Mana | Produced$ U | SpellDescription$ 3 — Add {U}.
SVar:AddB:DB$ Mana | Produced$ B | SpellDescription$ 4 — Add {B}.
SVar:AddR:DB$ Mana | Produced$ R | SpellDescription$ 5 — Add {R}.
SVar:AddG:DB$ Mana | Produced$ G | SpellDescription$ 6 — Add {G}.
SVar:Add0:DB$ Mana | Produced$ C | Amount$ 0
SVar:PlayMain1:TRUE
Oracle:{T}: Roll a six-sided die. This ability has the indicated effect.\n1 — Sacrifice Jack-in-the-Mox and you lose 5 life.\n2 — Add {W}.\n3 — Add {U}.\n4 — Add {B}.\n5 — Add {R}.\n6 — Add {G}.

View File

@@ -3,7 +3,7 @@ ManaCost:3 R
Types:Creature Cow
PT:3/3
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRoll | TriggerDescription$ At the beginning of your upkeep, roll a six-sided die. If you a roll a 1, sacrifice Krazy Kow and it deals 3 damage to each creature and each player.
SVar:TrigRoll:DB$ RollDice | On1$ DBSac
SVar:TrigRoll:DB$ RollDice | ResultSubAbilities$ 1:DBSac
SVar:DBSac:DB$ Sacrifice | Defined$ Self | SubAbility$ DBDamage
SVar:DBDamage:DB$ DamageAll | ValidCards$ Creature | ValidPlayers$ Player | NumDmg$ 3
Oracle:At the beginning of your upkeep, roll a six-sided die. If you a roll a 1, sacrifice Krazy Kow and it deals 3 damage to each creature and each player.

View File

@@ -4,6 +4,6 @@ Types:Artifact Equipment
K:Equip:2
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddAbility$ WandDamage | AddSVar$ DBWandDmg | Description$ Equipped creature has "{T}: This creature deals 1 damage to target player or planeswalker. Roll a six-sided die. On a 5 or higher, untap it."
SVar:WandDamage:AB$ DealDamage | Cost$ T | ValidTgts$ Planeswalker,Player | TgtPrompt$ Select target Planeswalker or player | NumDmg$ 1 | SubAbility$ DBRoll | SpellDescription$ This creature deals 1 damage to target player or planeswalker. Roll a six-sided die. On a 5 or higher, untap it.
SVar:DBRoll:DB$ RollDice | On5$ DBUntap | On6$ DBUntap
SVar:DBRoll:DB$ RollDice | ResultSubAbilities$ 5-6:DBUntap
SVar:DBUntap:DB$ Untap
Oracle:Equipped creature has "{T}: This creature deals 1 damage to target player or planeswalker. Roll a six-sided die. On a 5 or higher, untap it."\nEquip {2}

View File

@@ -2,7 +2,7 @@ Name:Mad Science Fair Project
ManaCost:3
Types:Artifact
A:AB$ Pump | Cost$ T | ValidTgts$ Player | SubAbility$ DBRoll
SVar:DBRoll:DB$ RollDice | On1$ AddC | On2$ AddC | On3$ AddC | Else$ AddAny | AILogic$ Main2 | SpellDescription$ {T}: Roll a six-sided die. On a 3 or lower, target player adds {C}. Otherwise, that player adds one mana of any color they choose.
SVar:DBRoll:DB$ RollDice | ResultSubAbilities$ 1-3:AddC,Else:AddAny | AILogic$ Main2 | SpellDescription$ {T}: Roll a six-sided die. On a 3 or lower, target player adds {C}. Otherwise, that player adds one mana of any color they choose.
SVar:AddC:DB$ Mana | Produced$ C | Defined$ Targeted
SVar:AddAny:DB$ Mana | Produced$ Any | Defined$ Targeted
SVar:PlayMain1:TRUE

View File

@@ -4,7 +4,7 @@ Types:Creature Bird Spirit
PT:1/1
K:Flying
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature | TriggerZones$ Battlefield | Execute$ TrigRoll | TriggerDescription$ Whenever a creature dies, you may roll a six-sided die. If you roll a 1, sacrifice CARDNAME. Otherwise, put a +1/+1 counter on CARDNAME.
SVar:TrigRoll:DB$ RollDice | On1$ DBSac | Else$ DBCounter
SVar:TrigRoll:DB$ RollDice | ResultSubAbilities$ 1:DBSac,Else:DBCounter
SVar:DBSac:DB$ Sacrifice | Defined$ Self
SVar:DBCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
DeckHas:Ability$Counters

View File

@@ -3,13 +3,13 @@ ManaCost:4 R
Types:Creature Beast
PT:5/6
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRollETB | TriggerDescription$ When Spark Fiend enters the battlefield, roll two six-sided dice. If you rolled 2, 3, or 12, sacrifice Spark Fiend. If you rolled 7 or 11, dont roll dice for Spark Fiend during any of your following upkeeps. If you rolled any other total, note that total.
SVar:TrigRollETB:DB$ RollDice | Amt$ 2 | On2$ DBSac | On3$ DBSac | On12$ DBSac | On7$ DBSafe | Else$ DBNote | ResultSVar$ Result
SVar:TrigRollETB:DB$ RollDice | Amount$ 2 | ResultSubAbilities$ 2-3:DBSac,12:DBSac,7:DBSafe,Else:DBNote | ResultSVar$ Result
SVar:DBSac:DB$ Sacrifice | Defined$ Self
SVar:Safe:Number$1
SVar:Noted:Number$0
SVar:DBSafe:DB$ StoreSVar | SVar$ Safe | Type$ Number | Expression$ 0
SVar:DBNote:DB$ StoreSVar | SVar$ Noted | Type$ CountSVar | Expression$ Result
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | CheckSVar$ Safe | SVarCompare$ NE0 | TriggerZones$ Battlefield | Execute$ TrigRoll | TriggerDescription$ At the beginning of your upkeep, roll two six-sided dice. If you rolled 7, sacrifice Spark Fiend. If you roll the noted total, dont roll dice for Spark Fiend during any of your following upkeeps. Otherwise, do nothing.
SVar:TrigRoll:DB$ RollDice | Amt$ 2 | On7$ DBSac | ResultSVar$ Result | SubAbility$ DBCheck
SVar:TrigRoll:DB$ RollDice | Amount$ 2 | ResultSubAbilities$ 7:DBSac | ResultSVar$ Result | SubAbility$ DBCheck
SVar:DBCheck:DB$ StoreSVar | SVar$ Safe | Type$ CountSVar | Expression$ Result/Minus.Noted
Oracle:When Spark Fiend enters the battlefield, roll two six-sided dice. If you rolled 2, 3, or 12, sacrifice Spark Fiend. If you rolled 7 or 11, don't roll dice for Spark Fiend during any of your following upkeeps. If you rolled any other total, note that total.\nAt the beginning of your upkeep, roll two six-sided dice. If you rolled 7, sacrifice Spark Fiend. If you roll the noted total, don't roll dice for Spark Fiend during any of your following upkeeps. Otherwise, do nothing.

View File

@@ -2,13 +2,14 @@ Name:Strategy, Schmategy
ManaCost:1 R
Types:Sorcery
A:SP$ StoreSVar | Cost$ 1 R | SVar$ Left | Type$ Number | Expression$ 1 | SubAbility$ DBRepeat
SVar:DBRepeat:DB$ Repeat | RepeatCheckSVar$ Left | RepeatSVarCompare$ GT0 | RepeatSubAbility$ Roll | StackDescription$ Roll a six-sided die. Strategy, Schmategy has the indicated effect. 1-Do nothing. 2-Destroy all artifacts. 3-Destroy all lands. 4-Strategy, Schmategy deals 3 damage to each creature and each player. 5-Each player discards their hand and draws seven cards. 6-Repeat this process two more times.
SVar:Roll:DB$ RollDice | On2$ DBArtif | On3$ DBLand | On4$ DBDamage | On5$ DBWheel | On6$ DBTwoMore | SubAbility$ DBDecr
SVar:DBArtif:DB$ DestroyAll | ValidCards$ Artifact
SVar:DBLand:DB$ DestroyAll | ValidCards$ Land
SVar:DBDamage:DB$ DamageAll | ValidCards$ Creature | ValidPlayers$ Player | NumDmg$ 3
SVar:DBWheel:DB$ Discard | Mode$ Hand | Defined$ Player | SubAbility$ DBEachDraw
SVar:DBRepeat:DB$ Repeat | RepeatCheckSVar$ Left | RepeatSVarCompare$ GT0 | RepeatSubAbility$ Roll | StackDescription$ Roll a six-sided die. CARDNAME has the indicated effect.
SVar:Roll:DB$ RollDice | ResultSubAbilities$ 1:DBNothing,2:DBArtif,3:DBLand,4:DBDamage,5:DBWheel,6:DBTwoMore | SubAbility$ DBDecr
SVar:DBNothing:DB$ Pump | SpellDescription$ 1 — Do nothing.
SVar:DBArtif:DB$ DestroyAll | ValidCards$ Artifact | SpellDescription$ 2 — Destroy all artifacts.
SVar:DBLand:DB$ DestroyAll | ValidCards$ Land | SpellDescription$ 3 — Destroy all lands.
SVar:DBDamage:DB$ DamageAll | ValidCards$ Creature | ValidPlayers$ Player | NumDmg$ 3 | SpellDescription$ 4 — CARDNAME deals 3 damage to each creature and each player.
SVar:DBWheel:DB$ Discard | Mode$ Hand | Defined$ Player | SubAbility$ DBEachDraw | SpellDescription$ 5 — Each player discards their hand and draws seven cards.
SVar:DBEachDraw:DB$ Draw | Defined$ Player | NumCards$ 7
SVar:DBTwoMore:DB$ StoreSVar | SVar$ Left | Type$ CountSVar | Expression$ Left/Plus.2
SVar:DBTwoMore:DB$ StoreSVar | SVar$ Left | Type$ CountSVar | Expression$ Left/Plus.2 | SpellDescription$ 6 — Repeat this process two more times.
SVar:DBDecr:DB$ StoreSVar | SVar$ Left | Type$ CountSVar | Expression$ Left/Minus.1
Oracle:Roll a six-sided die. Strategy, Schmategy has the indicated effect.\n1 — Do nothing.\n2 — Destroy all artifacts.\n3 — Destroy all lands.\n4 — Strategy, Schmategy deals 3 damage to each creature and each player.\n5 — Each player discards their hand and draws seven cards.\n6 — Repeat this process two more times.

View File

@@ -6,7 +6,7 @@ S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$
T:Mode$ DamageDone | ValidSource$ Creature.EquippedBy | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigLoop | TriggerZones$ Battlefield | TriggerDescription$ Whenever equipped creature deals combat damage to a player, create a 4/4 gold Dragon creature token with flying and roll a d20 (a twenty-sided die). If you roll a 20, repeat this process.
SVar:TrigLoop:DB$ Repeat | RepeatCheckSVar$ RepeatCheck | RepeatSVarCompare$ GT0 | RepeatSubAbility$ TrigToken | StackDescription$ Create a 4/4 gold Dragon creature token with flying and roll a d20. If you roll a 20, repeat this process.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_4_4_dragon_flying | TokenOwner$ You | SubAbility$ RollDie
SVar:RollDie:DB$ RollDice | Sides$ 20 | On20$ Win | Else$ Lose
SVar:RollDie:DB$ RollDice | Sides$ 20 | ResultSubAbilities$ 20:Win,Else:Lose
SVar:Win:DB$ StoreSVar | SVar$ RepeatCheck | Type$ Number | Expression$ 1
SVar:Lose:DB$ StoreSVar | SVar$ RepeatCheck | Type$ Number | Expression$ 0
SVar:RepeatCheck:Number$ 1

View File

@@ -2,11 +2,11 @@ Name:Urza's Science Fair Project
ManaCost:6
Types:Artifact Creature Construct
PT:4/4
A:AB$ RollDice | Cost$ 2 | On1$ M2 | On2$ Fog | On3$ Vig | On4$ FS | On5$ Fly | On6$ P2 | AILogic$ CombatEarly | SpellDescription$ Roll a six-sided die. CARDNAME gets the indicated result: 1-It gets -2/-2 until end of turn. 2-Prevent all combat damage it would deal this turn. 3-It gains vigilance until end of turn. 4-It gains first strike until end of turn. 5-It gains flying until end of turn. 6-It gets +2/+2 until end of turn.
SVar:M2:DB$ Pump | Defined$ Self | NumAtt$ -2 | NumDef$ -2
SVar:Fog:DB$ Pump | Defined$ Self | KW$ Prevent all combat damage that would be dealt by CARDNAME.
SVar:Vig:DB$ Pump | Defined$ Self | KW$ Vigilance
SVar:FS:DB$ Pump | Defined$ Self | KW$ First Strike
SVar:Fly:DB$ Pump | Defined$ Self | KW$ Flying
SVar:P2:DB$ Pump | Defined$ Self | NumAtt$ 2 | NumDef$ 2
A:AB$ RollDice | Cost$ 2 | ResultSubAbilities$ 1:M2,2:Fog,3:Vig,4:FS,5:Fly,6:P2 | AILogic$ CombatEarly | SpellDescription$ Roll a six-sided die. CARDNAME gets the indicated result.
SVar:M2:DB$ Pump | Defined$ Self | NumAtt$ -2 | NumDef$ -2 | SpellDescription$ 1 — It gets -2/-2 until end of turn.
SVar:Fog:DB$ Pump | Defined$ Self | KW$ Prevent all combat damage that would be dealt by CARDNAME. | SpellDescription$ 2 — Prevent all combat damage it would deal this turn.
SVar:Vig:DB$ Pump | Defined$ Self | KW$ Vigilance | SpellDescription$ 3 — It gains vigilance until end of turn.
SVar:FS:DB$ Pump | Defined$ Self | KW$ First Strike | SpellDescription$ 4 — It gains first strike until end of turn.
SVar:Fly:DB$ Pump | Defined$ Self | KW$ Flying | SpellDescription$ 5 — It gains flying until end of turn.
SVar:P2:DB$ Pump | Defined$ Self | NumAtt$ 2 | NumDef$ 2 | SpellDescription$ 6 — It gets +2/+2 until end of turn.
Oracle:{2}: Roll a six-sided die. Urza's Science Fair Project gets the indicated result.\n1 — It gets -2/-2 until end of turn.\n2 — Prevent all combat damage it would deal this turn.\n3 — It gains vigilance until end of turn.\n4 — It gains first strike until end of turn.\n5 — It gains flying until end of turn.\n6 — It gets +2/+2 until end of turn.

View File

@@ -0,0 +1,7 @@
Name:Critical Hit
ManaCost:1 R
Types:Instant
A:SP$ Pump | KW$ Double Strike | ValidTgts$ Creature
T:Mode$ RolledDie | TriggerZones$ Graveyard | ValidResult$ 20 | ValidPlayer$ You | Execute$ TrigReturn | TriggerDescription$ When you roll a natural 20, return CARDNAME from your graveyard to your hand. (A natural 20 is a roll that displays 20 on the die.)
SVar:TrigReturn:DB$ ChangeZone | Defined$ Self | Origin$ Graveyard | Destination$ Hand
Oracle:Target creature gains double strike until end of turn.\nWhen you roll a natural 20, return Critical Hit from your graveyard to your hand. (A natural 20 is a roll that displays 20 on the die.)

View File

@@ -0,0 +1,15 @@
Name:Delina, Wild Mage
ManaCost:3 R
Types:Legendary Creature Elf Shaman
PT:3/2
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBLoop | TriggerDescription$ Whenever CARDNAME attacks, choose target creature you control, then ABILITY
SVar:DBLoop:DB$ Repeat | ValidTgts$ Creature.YouCtrl | RepeatCheckSVar$ RepeatCheck | RepeatSVarCompare$ GT0 | RepeatSubAbility$ DBRollDice | RepeatOptional$ True
SVar:DBRollDice:DB$ RollDice | Sides$ 20 | ResultSubAbilities$ 1-14:DBCopy,15-20:DBCopyRepeat | SpellDescription$ roll a d20.
SVar:DBCopy:DB$ CopyPermanent | Defined$ Targeted | TokenTapped$ True | TokenAttacking$ True | NonLegendary$ True | AtEOT$ ExileCombat | SubAbility$ DBNotRepeat | SpellDescription$ 1-14 VERT Create a tapped and attacking token that's a copy of that creature, except it's not legendary and it has "Exile this creature at end of combat.
SVar:DBNotRepeat:DB$ StoreSVar | SVar$ RepeatCheck | Type$ Number | Expression$ 0
SVar:DBCopyRepeat:DB$ CopyPermanent | Defined$ Targeted | TokenTapped$ True | TokenAttacking$ True | NonLegendary$ True | AtEOT$ ExileCombat | SubAbility$ DBRepeat | SpellDescription$ 15-20 VERT Create one of those tokens. You may roll again.
SVar:DBRepeat:DB$ StoreSVar | SVar$ RepeatCheck | Type$ Number | Expression$ 1
SVar:RepeatCheck:Number$1
SVar:HasAttackEffect:TRUE
DeckHas:Ability$Token
Oracle:Whenever Delina, Wild Mage attacks, choose target creature you control, then roll a d20.\n1-14 | Create a tapped and attacking token that's a copy of that creature, except it's not legendary and it has "Exile this creature at end of combat."\n15-20 | Create one of those tokens. You may roll again.

View File

@@ -0,0 +1,9 @@
Name:Farideh, Devil's Chosen
ManaCost:2 U R
Types:Legendary Creature Tiefling Warlock
PT:3/3
T:Mode$ RolledDieOnce | TriggerZones$ Battlefield | ValidPlayer$ You | Execute$ TrigPump | TriggerDescription$ Dark One's Own Luck — Whenever you roll one or more dice, Farideh, Devil's Chosen gains flying and menace until end of turn. If any of those results was 10 or higher, draw a card.
SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ Flying & Menace | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | NumCards$ 1 | ConditionCheckSVar$ DiceResult | ConditionSVarCompare$ GE10
SVar:DiceResult:TriggerCountMax$Result
Oracle:Dark One's Own Luck — Whenever you roll one or more dice, Farideh, Devil's Chosen gains flying and menace until end of turn. If any of those results was 10 or higher, draw a card.

View File

@@ -0,0 +1,7 @@
Name:Feywild Trickster
ManaCost:2 U
Types:Creature Gnome Warlock
PT:2/2
T:Mode$ RolledDieOnce | TriggerZones$ Battlefield | ValidPlayer$ You | Execute$ TrigToken | TriggerDescription$ Whenever you roll one or more dice, create a 1/1 blue Faerie Dragon creature token with flying.
SVar:TrigToken:DB$ Token | TokenScript$ u_1_1_faerie_dragon_flying | TokenAmount$ 1 | TokenOwner$ You
Oracle:Whenever you roll one or more dice, create a 1/1 blue Faerie Dragon creature token with flying.

View File

@@ -0,0 +1,7 @@
Name:Pixie Guide
ManaCost:1 U
Types:Creature Faerie
PT:1/3
K:Flying
S:Mode$ Continuous | Affected$ You | AddKeyword$ If you would roll one or more dice, instead roll that many dice plus one and ignore the lowest roll. | Description$ Grant an Advantage — if you would roll one or more dice, instead roll that many dice plus one and ignore the lowest roll.
Oracle:Flying\nGrant an Advantage — if you would roll one or more dice, instead roll that many dice plus one and ignore the lowest roll.

View File

@@ -0,0 +1,16 @@
Name:The Deck of Many Things
ManaCost:5
Types:Legendary Artifact
A:AB$ RollDice | Cost$ 2 T | Sides$ 20 | Modifier$ CardsInHand | ResultSubAbilities$ Else:DBDiscard,1-9:DBReturn,10-19:DBDrawTwo,20:DBReanimate | SpellDescription$ Roll a d20 and subtract the number of cards in your hand. If the result is 0 or less, discard your hand.
SVar:CardsInHand:Count$InYourHand/Negative
SVar:DBDiscard:DB$ Discard | Mode$ Hand
SVar:DBReturn:DB$ ChangeZone | ChangeType$ Card.YouCtrl | ChangeNum$ 1 | Hidden$ True | Origin$ Graveyard | AtRandom$ True | Destination$ Hand | SpellDescription$ 1-9 VERT Return a card at random from your graveyard to your hand.
SVar:DBDrawTwo:DB$ Draw | NumCards$ 2 | SpellDescription$ 10-19 VERT Draw two cards.
SVar:DBReanimate:DB$ ChooseCard | Choices$ Creature | ChoiceZone$ Graveyard | Amount$ 1 | SubAbility$ DBChangeZone | SpellDescription$ 20 VERT Put a creature card from any graveyard onto the battlefield under your control. When that creature dies, its owner loses the game.
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ ChosenCard | GainControl$ True | RememberChanged$ True | SubAbility$ DBLoseEffect
SVar:DBLoseEffect:DB$ Effect | Triggers$ TrigDie | RememberObjects$ ChosenCard | Duration$ Permanent | ExileOnMoved$ Battlefield | SubAbility$ DBCleanup
SVar:TrigDie:Mode$ ChangesZone | ValidCard$ Creature.IsRemembered | Origin$ Battlefield | Destination$ Graveyard | Execute$ DBLose | TriggerDescription$ When that creature dies, its owner loses the game.
SVar:DBLose:DB$ LosesGame | Defined$ RememberedOwner
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
DeckHas:Ability$Discard & Ability$Graveyard
Oracle:{2}, {T}: Roll a d20 and subtract the number of cards in your hand. If the result is 0 or less, discard your hand.\n1-9 | Return a card at random from your graveyard to your hand.\n10-19 | Draw two cards.\n20 | Put a creature card from any graveyard onto the battlefield under your control. When that creature dies, its owner loses the game.

View File

@@ -0,0 +1,11 @@
Name:Wizard's Spellbook
ManaCost:5 U U
Types:Artifact
A:AB$ ChangeZone | Cost$ T | SorcerySpeed$ True | Origin$ Graveyard | Destination$ Exile | Mandatory$ True | ValidTgts$ Instant,Sorcery | TgtPrompt$ Select target instant or sorcery card in a graveyard | RememberChanged$ True | Imprint$ True | SubAbility$ DBRollDice | SpellDescription$ Exile target instant or sorcery card from a graveyard. Roll a d20. Activate only as a sorcery.
SVar:DBRollDice:DB$ RollDice | Sides$ 20 | ResultSubAbilities$ 1-9:PlayCopy,10-19:PlayCost1,20:PlayAny
SVar:PlayCopy:DB$ Play | Valid$ Card.IsRemembered+ExiledWithSource | ValidZone$ Exile | Amount$ All | CopyOnce$ True | CopyCard$ True | Optional$ True | SubAbility$ DBCleanup | SpellDescription$ 1-9 VERT Copy that card. You may cast the copy.
SVar:PlayCost1:DB$ Play | Valid$ Card.IsRemembered+ExiledWithSource | ValidZone$ Exile | Amount$ All | CopyOnce$ True | CopyCard$ True | Optional$ True | PlayCost$ 1 | SubAbility$ DBCleanup | SpellDescription$ 10-19 VERT Copy that card. You may cast the copy by paying {1} rather than paying its mana cost.
SVar:PlayAny:DB$ Play | Valid$ Card.IsImprinted+ExiledWithSource | ValidZone$ Exile | Amount$ All | CopyOnce$ True | CopyCard$ True | Optional$ True | WithoutManaCost$ True | SubAbility$ DBCleanup | SpellDescription$ 20 VERT Copy each card exiled with Wizard's Spellbook. You may cast any number of the copies without paying their mana costs.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
DeckHas:Ability$Graveyard
Oracle:{T}: Exile target instant or sorcery card from a graveyard. Roll a d20. Activate only as a sorcery.\n1-9 | Copy that card. You may cast the copy.\n10-19 | Copy that card. You may cast the copy by paying {1} rather than paying its mana cost.\n20 | Copy each card exiled with Wizard's Spellbook. You may cast any number of the copies without paying their mana costs.

View File

@@ -257,6 +257,7 @@ Tetravite:Tetravites
Thalakos:Thalakoses
Thopter:Thopters
Thrull:Thrulls
Tiefling:Tieflings
Treefolk:Treefolks
Trilobite:Trilobites
Triskelavite:Triskelavites

View File

@@ -0,0 +1,7 @@
Name:Faerie Dragon
ManaCost:no cost
Types:Creature Faerie Dragon
Colors:blue
PT:1/1
K:Flying
Oracle:Flying