Compare commits

..

1 Commits

Author SHA1 Message Date
Chris H
54b4f3edf1 Migrate TLA 2025-11-15 20:48:05 -05:00
80 changed files with 366 additions and 462 deletions

View File

@@ -45,9 +45,6 @@ In IntelliJ, if the SDK Manager is not already running, go to Tools > Android >
- Android SDK Build-tools 35.0.0
- Android 15 (API 35) SDK Platform
> [!CAUTION]
> Be careful about using unsupported api calls e.g. ``StringBuilder.isEmpty()``. Google's documentation for these is sometimes inaccurate.
### Proguard update
Standalone Proguard 7.6.0 is included with the project (proguard.jar) under forge-gui-android > tools and supports up to Java 23 (latest android uses Java 17).

View File

@@ -1,8 +1,6 @@
Forge provides an in-game console in adventure mode.
You can access (and close) the console while exploring by pressing F9 (or Fn-F9).
The equivalent method to access the console on mobile is to hold down the character image in the right top of the screen.
Holding the character image again will close the console (as will typing `exit`).
To scroll the console window, click and drag the text box.

View File

@@ -516,7 +516,7 @@ Opponent starting life +5
1. **Cost**: 5000
#### Blue Staff
1. **Effect**: Lets you fly for 5 shards
1. **Effect**: Let's you fly for 5 shards
1. **Location** : Capital cities
1. **Cost**: 5000

View File

@@ -1,18 +1,14 @@
AbilityFactory parses differently from the Keyword parser. Your Ability line will look more like this:
`A:<AB/SP/DB/ST>$ <AFSubclass> | <Necessary$ Parameters> | (<Separated$ By> | <Pipes$ Here>) | [Optional$ {Values} [Nested$ Dependency]]`
`A:{AB/SP/DB/ST}$ <AFSubclass> | {Necessary$ Parameters} | {Separated$ By} | {Pipes$ Here} | [Optional$ Values]`
The ability types are:
- **AB** for Activated Abilities
- **SP** for Spell
- **DB** for Drawback and many abilities that are subsidiary to other things, like replacements. They are only used to chain AFs together, and will never be the root AF
- **ST** for Static, this gets used in case the API should resolve without using the stack<br /> (e.g. the unique *Circling Vultures* special action is directly implemented in the script this way)
In most cases, each AF subclass implements both the Spell and Ability.
Much of the code is shared, so creating the data object will look very similar.
Syntax definitions like the above will use different symbols to separate the variable parts from the plaintext:
- angle brackets for mandatory parts
- square brackets for optional parts
- round brackets for grouping parts that are exclusive to each other
- curly brackets to denote the type of a param
- **AB** is for Activated Abilities
- **SP** is for Spell
- **DB** is for Drawback and many abilities that are subsidiary to other things, like replacements. They are only used to chain AFs together, and will never be the root AF
- **ST** is for Static, this gets used in case the API should resolve without using the stack<br /> (e.g. the unique *Circling Vultures* special action is directly implemented in the script this way)
>*NOTE:*
> - these factories are refactored from time to time (often to adapt to new sets), so while some entries could be slightly outdated, the base information should still be correct
@@ -24,7 +20,7 @@ Syntax definitions like the above will use different symbols to separate the var
## Cost / UnlessCost
`Cost$ {AbilityCost}` is the appropriate way to set the cost of the ability. Currently for spells, any additional costs including the original Mana cost need to appear in the Cost parameter in the AbilityFactory. For each card that uses it, the order in which the cost is paid will always be the same.
`Cost$ <AbilityCost>` is the appropriate way to set the cost of the ability. Currently for spells, any additional costs including the original Mana cost need to appear in the Cost parameter in the AbilityFactory. For each card that uses it, the order in which the cost is paid will always be the same.
Secondary abilities such as the DB executed by triggers or replacements (usually) don't need costs. (This is one reason to use DB over AB in these cases.)
@@ -519,6 +515,8 @@ Used in the script of *Karn Liberated*
## Goad
## Investigate
## Mana
For lands or other permanent to produce mana.
@@ -724,13 +722,7 @@ player chooses (eg: Burning of Xinye, or Imperial Edict).
## StoreSVar
## Tokens
### Amass
### Investigate
### Token
## Token
Token simply lets you create tokens of any type.
@@ -767,8 +759,6 @@ If possible split the SpellDescription of the effect so the part for the trigger
### ImmediateTrigger
TriggerAmount
## Turn structure
### AddPhase

View File

@@ -25,23 +25,23 @@ There are a few other properties that will appear in many cards. These are
| Property | Description
| - | -
|`A`|[Ability effect](Card-scripting-API/AbilityFactory.md)
|`AI`|RemoveDeck:<br />* `All`<br />This will prevent the card from appearing in random AI decks. It is applicable for cards the AI can't use at all and also for cards that the AI could use, but only ineffectively. The AI won't draft these cards.<br />* `Random`<br /> This will prevent the card from appearing in random decks. It is only applicable for cards that are too narrow for random decks like *Root Cage* or *Into the North*. The AI won't draft these cards.<br />* `NonCommander`<br />
|`Colors`|Color(s) of the card<br /><br />When a card's color is determined by a color indicator rather than shards in a mana cost, this property must be defined. If no identifier is needed, this property should be omitted.<br /><br />Example:<br />`Colors:red,green` - Since *Arlinn, Embraced by the Moon* has no mana cost (it's the back of a double-faced card), the red and green indicator must be included.
|`DeckHints`|AI-related hints for a deck including this card<br /><br />To improve synergy this will increase the rank of of all other cards that share some of its DeckHints types. The following types are supported:<br />* Color<br />* Keyword<br />* Name<br />* Type<br /><br />This helps with smoothing the selection so cards without these Entries won't be at an unfair disadvantage.<br /><br />The relevant code can be found in the [CardRanker](https://github.com/Card-Forge/forge/blob/master/forge-gui/src/main/java/forge/gamemodes/limited/CardRanker.java) class.
|`A`|[Ability effect](AbilityFactory)
|`AI`|RemoveDeck:<br />* `All`<br />This will prevent the card from appearing in random AI decks. It is applicable for cards the AI can't use at all like Dark Ritual and also for cards that the AI could use, but only ineffectively like Tortoise Formation. The AI won't draft these cards.<br />* `Random`<br /> This will prevent the card from appearing in random decks. It is only applicable for cards that are too narrow for random decks like Root Cage or Into the North. The AI won't draft these cards.<br />* `NonCommander`<br />
|`Colors`|Color(s) of the card<br /><br />When a card's color is determined by a color indicator rather than shards in a mana cost, this property must be defined. If no identifier is needed, this property should be omitted.<br /><br />* `Colors:red` - This is used on Kobolds of Kher Keep, which has a casting cost of {0} and requires a red indicator to make it red.<br /><br />* `Colors:red,green` - Since Arlinn, Embraced by the Moon has no casting cost (it's the back of a double-faced card), the red and green indicator must be included.
|`DeckHints`|AI-related hints for a deck including this card<br /><br />To improve synergy this will increase the rank of of all other cards that share some of its DeckHints types. This helps with smoothing the selection so cards without these Entries won't be at an unfair disadvantage.<br /><br />The relevant code can be found in the [CardRanker](https://github.com/Card-Forge/forge/blob/master/forge-gui/src/main/java/forge/gamemodes/limited/CardRanker.java) class.
|`DeckNeeds`|This can be considered a stronger variant when the AI should not put this card into its deck unless it has whatever other type is specified. The way this works is "inverted": it will directly decrease the rank of the card unless other cards are able to satisfy its types.<br />If a card demands more than one kind of type you can reuse it:<br />`DeckNeeds:Type$Human & Type$Warrior` will only find Human Warrior compared to `DeckNeeds:Type$Human\|Warrior` which is either
|`DeckHas`|Specifies that the deck now has a certain ability (like, token generation) so that the drafting/deckbuilding AI knows that it now meets requirements for DeckHints/DeckNeeds. This is useful since many of these are not deduced by parsing the abilities, so an explicit hint is necessary. If you want to create a new archetype make sure to tag at least roughly 50 cards to start with and balacing the Has/Needs ratio. Currently used values are:<br />* Counters<br />* Graveyard<br />* Token<br /><br />Using the generic types is also supported in case the implicit parsing wouldn't find it (TokenScript$ is also included).<br />It doesn't require exact matching to have an effect but cards that care about multiple entries for a given type will be judged higher if a card seems to provide even "more" synergy for it.<br /><br />Example:<br />*Chishiro* has two abilities so `DeckHas:Ability$Token & Ability$Counters` is used, therefore score for `DeckNeeds:Ability$Token\|Counters` is increased
|`DeckHas`|specifies that the deck now has a certain ability (like, token generation or counters) so that the drafting/deckbuilding AI knows that it now meets requirements for DeckHints/DeckNeeds. This is actually very useful since many of these (such as `Ability$Graveyard, Ability$Token, Ability$Counters`) are not deduced by parsing the abilities, so an explicit hint is necessary. Using the other types is also supported in case the implicit parsing wouldn't find it (TokenScript$ is also included).<br />It doesn't require exact matching to have an effect but cards that care about multiple entries for a given type will be judged higher if a card seems to provide even "more" synergy for it.<br />Example:<br />Chishiro has two abilities so `DeckHas:Ability$Token & Ability$Counters` is used, therefore score for `DeckNeeds:Ability$Token\|Counters` is increased
|`K`|Keyword (see below)
|`Loyalty`|Number of starting loyalty counters
|`ManaCost`|Cost to cast the card shown in mana shards<br /><br />This property is required. It has a single parameter that is a mana cost.<br /><br />* `ManaCost:no cost` for cards that cannot be cast<br />* `ManaCost:1 W W` sets the casting cost to {1}{W}{W}
|`Name`|Name of the card<br /><br />A string of text that serves as the name of the card. Note that the registered trademark symbol cannot be included, and this property must have at least one character.<br /><br />Example:<br />* `Name:A Display of My Dark Power` sets the card's name to "A Display of My Dark Power"
|`Oracle`|The current Oracle text used by the card.<br /><br />We actually have a Python Script that runs to be able to fill in this information, so don't worry about manually editing a lot of cards when Wizards decides to change the rules. <br /><br />This field is used by the Deck Editor to allow non-Legendary Creatures to be marked as potential commanders. Make sure "CARDNAME can be your commander." appears in the oracle text.
|`PT`|Power and toughness
|`R`|[Replacement effect](Card-scripting-API/Replacements.md)
|`S`|[Static ability](Card-scripting-API/Statics.md)
|`R`|[Replacement effect](Replacements)
|`S`|[Static ability](static-abilities)
|`SVar`|String variable. Used throughout scripting in a handful of different ways.
|`T`|[Triggered ability](Card-scripting-API/Triggers.md)
|`Text`|Additional text that needs to be displayed on the CardDetailPanel that doesn't have any spell/ability that generates a description for it, e.g. "CARDNAME can be your commander." or "X can't be 0.".
|`T`|[Triggered ability](Triggers)
|`Text`|Additional text that needs to be displayed on the CardDetailPanel that doesn't have any spell/ability that generates a description for it, for example "CARDNAME can be your commander." or "X can't be 0.".
|`Types`|Card types and subtypes<br /><br />Include all card types and subtypes, separated by spaces.<br /><br />Example:<br />* `Types:Enchantment Artifact Creature Golem` for a card that reads Enchantment Artifact Creature -- Golem
Rarity and Set info are now defined in edition definition files. These can be found at /res/reditions path.
@@ -198,6 +198,8 @@ equipped already (Kor Duelist).
`SVar:EndOfTurnLeavePlay:True`
`SVar:maxLevel:`
`SVar:HasCombatEffect:True`
`SVar:HasAttackEffect:True`

View File

@@ -1,23 +0,0 @@
There are two major groups of static abilities:
# Statics for the main 7 layers
Syntax:
`S:Mode$ <Continuous> | <Affected$ {Valid Player/Card}> | <Layer-specific$ Params> | [Description$ {String}]`
Here's an example for layer 7c:
`Affected$ Creature.YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control get +1/+1.`
See [StaticAbility.generateLayer()](https://github.com/Card-Forge/forge/blob/master/forge-game/src/main/java/forge/game/staticability/StaticAbility.java) for the full list of params on each Layer.
*Note:* Layer 1 is currently only implemented as a resolving effect instead.
# Statics for the concluding "game rules layer" ([CR 613.11](https://yawgatog.com/resources/magic-rules/#R61311))
The available effects are defined here: [StaticAbilityMode](https://github.com/Card-Forge/forge/blob/master/forge-game/src/main/java/forge/game/staticability/StaticAbilityMode.java).
*Note:* some rules-modifying parts are still coded via `Continuous` mode for now, e.g. `SetMaxHandSize$ {Integer}`.
## Combat
## Costs

View File

@@ -34,7 +34,7 @@ The "Ancestors" column is basically there to list currently inactive developers
## Forge Script DSL
| Concept | Owners | Ancestors | Example tasks |
| - | - | - | - |
| [Card Scripting](cardscripting) | TRT, Northmoc, Simisays, Fulgur14, Dracontes | a lot | - implement new Sets<br>- review script PR<br>- clean up outdated elements<br>- apply Oracle updates |
| [Card Scripting](cardscripting) | TRT, Northmoc, Simisays, Fulgur14, Dracontes | a lot | - implement new Sets<br>- clean up outdated elements<br>- apply Oracle updates |
| ForgeScribe | | Austinino | |
## Modes

View File

@@ -38,7 +38,7 @@
- [Ability effects](Card-scripting-API/AbilityFactory.md)
- [Triggers](Card-scripting-API/Triggers.md)
- [Replacements](Card-scripting-API/Replacements.md)
- [Statics](Card-scripting-API/Statics.md)
- Statics
- [Costs](Card-scripting-API/Costs.md)
- [Affected / Targets](Card-scripting-API/Targeting.md)
- [Restrictions / Conditions](Card-scripting-API/Restrictions.md)

View File

@@ -810,13 +810,13 @@ public class AiController {
return reserveManaSources(sa, phaseType, enemy, true, null);
}
public boolean reserveManaSources(SpellAbility sa, PhaseType phaseType, boolean enemy, boolean forNextSpell, SpellAbility exceptForThisSa) {
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa.getPayCosts(), sa, player, true, 0, false);
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa.getPayCosts(), sa, true, 0, false);
CardCollection manaSources = ComputerUtilMana.getManaSourcesToPayCost(cost, sa, player);
// used for chained spells where two spells need to be cast in succession
if (exceptForThisSa != null) {
manaSources.removeAll(ComputerUtilMana.getManaSourcesToPayCost(
ComputerUtilMana.calculateManaCost(exceptForThisSa.getPayCosts(), exceptForThisSa, player, true, 0, false),
ComputerUtilMana.calculateManaCost(exceptForThisSa.getPayCosts(), exceptForThisSa, true, 0, false),
exceptForThisSa, player));
}
@@ -1726,7 +1726,6 @@ public class AiController {
return future.get(game.getAITimeout(), TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
try {
e.printStackTrace();
t.stop();
} catch (UnsupportedOperationException ex) {
// Android and Java 20 dropped support to stop so sadly thread will keep running

View File

@@ -1285,6 +1285,20 @@ public class ComputerUtil {
}
} // AntiBuffedBy
// Plane cards that give Haste (e.g. Sokenzan)
if (ai.getGame().getRules().hasAppliedVariant(GameType.Planechase)) {
for (Card c : ai.getGame().getActivePlanes()) {
for (StaticAbility s : c.getStaticAbilities()) {
if (s.hasParam("AddKeyword")
&& s.getParam("AddKeyword").contains("Haste")
&& "Creature".equals(s.getParam("Affected"))
&& card.isCreature()) {
return true;
}
}
}
}
final CardCollectionView vengevines = ai.getCardsIn(ZoneType.Graveyard, "Vengevine");
if (!vengevines.isEmpty()) {
final CardCollectionView creatures = ai.getCardsIn(ZoneType.Hand);
@@ -1444,15 +1458,16 @@ public class ComputerUtil {
for (StaticAbility stAb : c.getStaticAbilities()) {
if (stAb.checkMode(StaticAbilityMode.Continuous) && stAb.hasParam("AddKeyword")
&& stAb.getParam("AddKeyword").contains("Haste")) {
if (c.isEquipment() && c.getEquipping() == null) {
return true;
}
final String affected = stAb.getParam("Affected");
if (affected.startsWith("Creature") && (affected.contains("YouCtrl") || !affected.contains("."))) {
if (affected.contains("Creature.YouCtrl")
|| affected.contains("Other+YouCtrl")) {
return true;
}
if (affected.contains("Creature.PairedWith") && !c.isPaired()) {
} else if (affected.contains("Creature.PairedWith") && !c.isPaired()) {
return true;
}
}
@@ -1467,7 +1482,8 @@ public class ComputerUtil {
}
final String valid = params.get("ValidCard");
if (valid.contains("Creature.YouCtrl") || valid.contains("Other+YouCtrl") ) {
if (valid.contains("Creature.YouCtrl")
|| valid.contains("Other+YouCtrl") ) {
final SpellAbility sa = t.getOverridingAbility();
if (sa != null && sa.getApi() == ApiType.Pump && sa.hasParam("KW")

View File

@@ -509,16 +509,16 @@ public class ComputerUtilCost {
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param payer
* @param player
* a {@link forge.game.player.Player} object.
* @return a boolean.
*/
public static boolean canPayCost(final SpellAbility sa, final Player payer, final boolean effect) {
return canPayCost(sa.getPayCosts(), sa, payer, effect);
public static boolean canPayCost(final SpellAbility sa, final Player player, final boolean effect) {
return canPayCost(sa.getPayCosts(), sa, player, effect);
}
public static boolean canPayCost(final Cost cost, final SpellAbility sa, final Player payer, final boolean effect) {
public static boolean canPayCost(final Cost cost, final SpellAbility sa, final Player player, final boolean effect) {
if (sa.getActivatingPlayer() == null) {
sa.setActivatingPlayer(payer); // complaints on NPE had came before this line was added.
sa.setActivatingPlayer(player); // complaints on NPE had came before this line was added.
}
// Check for stuff like Nether Void
@@ -527,14 +527,14 @@ public class ComputerUtilCost {
boolean cannotBeCountered = !sa.isCounterableBy(null);
if (sa instanceof Spell) {
for (Card c : payer.getGame().getCardsIn(ZoneType.Battlefield)) {
for (Card c : player.getGame().getCardsIn(ZoneType.Battlefield)) {
final String snem = c.getSVar("AI_SpellsNeedExtraMana");
if (!StringUtils.isBlank(snem)) {
if (cannotBeCountered && c.getName().equals("Nether Void")) {
continue;
}
String[] parts = TextUtil.split(snem, ' ');
boolean meetsRestriction = parts.length == 1 || payer.isValid(parts[1], c.getController(), c, sa);
boolean meetsRestriction = parts.length == 1 || player.isValid(parts[1], c.getController(), c, sa);
if(!meetsRestriction)
continue;
@@ -545,7 +545,7 @@ public class ComputerUtilCost {
}
}
}
for (Card c : payer.getCardsIn(ZoneType.Command)) {
for (Card c : player.getCardsIn(ZoneType.Command)) {
if (cannotBeCountered) {
continue;
}
@@ -567,7 +567,7 @@ public class ComputerUtilCost {
if (part.convertAmount() != null && part.convertAmount() == sa.getHostCard().getCurrentLoyalty()) {
// refuse to pay if opponent has no creature threats or
// 50% chance otherwise
if (payer.getOpponents().getCreaturesInPlay().isEmpty()
if (player.getOpponents().getCreaturesInPlay().isEmpty()
|| MyRandom.getRandom().nextFloat() < .5f) {
return false;
}
@@ -591,7 +591,7 @@ public class ComputerUtilCost {
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
// don't use API converter since it might have special part logic not meant for Ward cost
SpellAbilityAi topAI = new SpellAbilityAi() {};
if (!topAI.willPayCosts(payer, sa, wardCost, sa.getHostCard())) {
if (!topAI.willPayCosts(player, sa, wardCost, sa.getHostCard())) {
return false;
}
if (wardCost.hasManaCost()) {
@@ -606,7 +606,7 @@ public class ComputerUtilCost {
if (sa.getHostCard().hasKeyword(Keyword.CASUALTY)) {
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostSacrifice) {
CardCollection valid = CardLists.getValidCards(payer.getCardsIn(ZoneType.Battlefield), part.getType().split(";"),
CardCollection valid = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), part.getType().split(";"),
sa.getActivatingPlayer(), sa.getHostCard(), sa);
valid = CardLists.filter(valid, CardPredicates.hasSVar("AIDontSacToCasualty").negate());
if (valid.isEmpty()) {
@@ -618,8 +618,8 @@ public class ComputerUtilCost {
}
// TODO both of these call CostAdjustment.adjust, try to reuse instead
return ComputerUtilMana.canPayManaCost(cost, sa, payer, extraManaNeeded, effect)
&& CostPayment.canPayAdditionalCosts(cost, sa, effect, payer);
return ComputerUtilMana.canPayManaCost(cost, sa, player, extraManaNeeded, effect)
&& CostPayment.canPayAdditionalCosts(cost, sa, effect, player);
}
public static Set<String> getAvailableManaColors(Player ai, Card additionalLand) {

View File

@@ -69,7 +69,7 @@ public class ComputerUtilMana {
return payManaCost(cost, sa, ai, false, 0, true, effect);
}
private static boolean payManaCost(final Cost cost, final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable, final boolean effect) {
ManaCostBeingPaid manaCost = calculateManaCost(cost, sa, ai, test, extraMana, effect);
ManaCostBeingPaid manaCost = calculateManaCost(cost, sa, test, extraMana, effect);
return payManaCost(manaCost, sa, ai, test, checkPlayable, effect);
}
@@ -77,7 +77,7 @@ public class ComputerUtilMana {
* Return the number of colors used for payment for Converge
*/
public static int getConvergeCount(final SpellAbility sa, final Player ai) {
ManaCostBeingPaid cost = calculateManaCost(sa.getPayCosts(), sa, ai, true, 0, false);
ManaCostBeingPaid cost = calculateManaCost(sa.getPayCosts(), sa, true, 0, false);
if (payManaCost(cost, sa, ai, true, true, false)) {
return cost.getSunburst();
}
@@ -1291,7 +1291,7 @@ public class ComputerUtilMana {
* @param extraMana extraMana
* @return ManaCost
*/
public static ManaCostBeingPaid calculateManaCost(final Cost cost, final SpellAbility sa, final Player payer, final boolean test, final int extraMana, final boolean effect) {
public static ManaCostBeingPaid calculateManaCost(final Cost cost, final SpellAbility sa, final boolean test, final int extraMana, final boolean effect) {
Card host = sa.getHostCard();
Zone castFromBackup = null;
if (test && sa.isSpell() && !host.isInZone(ZoneType.Stack)) {
@@ -1302,10 +1302,6 @@ public class ComputerUtilMana {
Cost payCosts;
if (test) {
payCosts = CostAdjustment.adjust(cost, sa, effect);
// prevent asking Human when only predicting
if (!payer.getController().isAI()) {
sa.setMaxWaterbend(null);
}
} else {
// when not testing CostPayment already handled raise
payCosts = cost;
@@ -1349,7 +1345,7 @@ public class ComputerUtilMana {
}
}
CostAdjustment.adjust(manaCost, sa, payer, null, test, effect);
CostAdjustment.adjust(manaCost, sa, null, test, effect);
if ("NumTimes".equals(sa.getParam("Announce"))) { // e.g. the Adversary cycle
ManaCost mkCost = sa.getPayCosts().getTotalMana();

View File

@@ -963,8 +963,6 @@ public abstract class GameState {
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
}
spellDef = spellDef.replace("^", ":"); // alternate marker for when : is the name of the card
Card c = null;
if (StringUtils.isNumeric(spellDef)) {

View File

@@ -86,7 +86,6 @@ public enum SpellApiToAi {
.put(ApiType.DrainMana, DrainManaAi.class)
.put(ApiType.Draw, DrawAi.class)
.put(ApiType.EachDamage, DamageEachAi.class)
.put(ApiType.Earthbend, EarthbendAi.class)
.put(ApiType.Effect, EffectAi.class)
.put(ApiType.Encode, EncodeAi.class)
.put(ApiType.Endure, EndureAi.class)

View File

@@ -21,7 +21,6 @@ import forge.game.player.PlayerPredicates;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
@@ -106,12 +105,7 @@ public class CountersPutAi extends CountersAi {
}
}
}
int maxLevel = 0;
for (StaticAbility st : source.getStaticAbilities()) {
if (st.toString().startsWith("LEVEL ")) {
maxLevel = Math.max(maxLevel, Integer.parseInt(st.toString().substring(6, 7)));
}
}
int maxLevel = Integer.parseInt(sa.getParam("MaxLevel"));
return source.getCounters(CounterEnumType.LEVEL) < maxLevel;
}

View File

@@ -1,38 +0,0 @@
package forge.ai.ability;
import forge.ai.AiAbilityDecision;
import forge.ai.AiPlayDecision;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class EarthbendAi extends SpellAbilityAi {
@Override
protected AiAbilityDecision canPlay(Player aiPlayer, SpellAbility sa) {
CardCollection nonAnimatedLands = CardLists.filter(aiPlayer.getLandsInPlay(), CardPredicates.NON_CREATURES);
if (nonAnimatedLands.isEmpty()) {
return new AiAbilityDecision(0, AiPlayDecision.AnotherTime);
}
Card bestToAnimate = ComputerUtilCard.getBestLandToAnimate(nonAnimatedLands);
sa.getTargets().add(bestToAnimate);
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}
@Override
protected AiAbilityDecision doTriggerNoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
AiAbilityDecision decision = canPlay(aiPlayer, sa);
if (decision.willingToPlay() || mandatory) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
}

View File

@@ -108,7 +108,7 @@ public class PermanentAi extends SpellAbilityAi {
if ("SacToReduceCost".equals(sa.getParam("AILogic"))) {
// reset X to better calculate
sa.setXManaCostPaid(0);
ManaCostBeingPaid paidCost = ComputerUtilMana.calculateManaCost(sa.getPayCosts(), sa, ai, true, 0, false);
ManaCostBeingPaid paidCost = ComputerUtilMana.calculateManaCost(sa.getPayCosts(), sa, true, 0, false);
int generic = paidCost.getGenericManaAmount();
// Set PayX here to maximum value.

View File

@@ -66,8 +66,10 @@ public class UntapAi extends SpellAbilityAi {
if (pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai)) {
// If the defined card is tapped, or if there are no defined cards, we can play this ability
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
} else {
// Otherwise, we can't play this ability
return new AiAbilityDecision(0, AiPlayDecision.MissingNeededCards);
}
return new AiAbilityDecision(0, AiPlayDecision.MissingNeededCards);
}
@Override

View File

@@ -1066,8 +1066,10 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
public String getNormalizedName(final String cardName) {
String cardName1 = cardName;
// normalize Names first
return normalizedNames.getOrDefault(cardName, cardName);
cardName1 = normalizedNames.getOrDefault(cardName1, cardName1);
return cardName1;
}
@Override

View File

@@ -702,7 +702,7 @@ public class GameAction {
eff.addRemembered(copied);
// refresh needed for canEnchant checks
checkStaticAbilities(false, Sets.newHashSet(copied), new CardCollection(copied));
game.getAction().checkStaticAbilities(false, Sets.newHashSet(copied), new CardCollection(copied));
return eff;
}
private void cleanStaticEffect(Card eff, Card copied) {
@@ -809,7 +809,8 @@ public class GameAction {
}
}
// CR 720.4a Move card in maingame if take card from subgame
// Move card in maingame if take card from subgame
// 720.4a
if (zoneFrom != null && zoneFrom.is(ZoneType.Sideboard) && game.getMaingame() != null) {
Card maingameCard = c.getOwner().getMappingMaingameCard(c);
if (maingameCard != null) {
@@ -1239,7 +1240,7 @@ public class GameAction {
}
private StaticAbility findStaticAbilityToApply(StaticAbilityLayer layer, List<StaticAbility> staticsForLayer, CardCollectionView preList, Map<StaticAbility, CardCollectionView> affectedPerAbility,
Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies) {
Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies) {
if (staticsForLayer.size() == 1) {
return staticsForLayer.get(0);
}
@@ -2079,7 +2080,7 @@ public class GameAction {
}
if (showRevealDialog) {
final String message = Localizer.getInstance().getMessage("lblSacrifice");
reveal(result, ZoneType.Graveyard, c.getOwner(), false, message, false);
game.getAction().reveal(result, ZoneType.Graveyard, c.getOwner(), false, message, false);
}
}
for (Map.Entry<Player, Collection<Card>> e : lki.asMap().entrySet()) {
@@ -2538,9 +2539,19 @@ public class GameAction {
game.getTriggerHandler().runTrigger(TriggerType.TakesInitiative, runParams, false);
}
// Make scry an action function so that it can be used for mulligans (with a null cause)
// Assumes that the list of players is in APNAP order, which should be the case
// Optional here as well to handle the way that mulligans do the choice
// 701.17. Scry
// 701.17a To "scry N" means to look at the top N cards of your library, then put any number of them
// on the bottom of your library in any order and the rest on top of your library in any order.
// 701.17b If a player is instructed to scry 0, no scry event occurs. Abilities that trigger whenever a
// player scries won't trigger.
// 701.17c If multiple players scry at once, each of those players looks at the top cards of their library
// at the same time. Those players decide in APNAP order (see rule 101.4) where to put those
// cards, then those cards move at the same time.
public void scry(final List<Player> players, int numScry, SpellAbility cause) {
if (numScry <= 0) {
// CR 701.22b If a player is instructed to scry 0, no scry event occurs.
return;
}
@@ -2566,9 +2577,12 @@ public class GameAction {
if (playerScry > 0) {
actualPlayers.put(p, playerScry);
// no real need to separate out the look if there is only one player scrying
// reveal the top N library cards to the player (only)
// no real need to separate out the look if
// there is only one player scrying
if (players.size() > 1) {
revealTo(p.getCardsIn(ZoneType.Library, playerScry), p);
final CardCollection topN = new CardCollection(p.getCardsIn(ZoneType.Library, playerScry));
revealTo(topN, p);
}
}
}
@@ -2609,6 +2623,7 @@ public class GameAction {
}
if (cause != null) {
// set up triggers (but not actually do them until later)
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(p);
runParams.put(AbilityKey.ScryNum, numLookedAt);
runParams.put(AbilityKey.ScryBottom, toBottom == null ? 0 : toBottom.size());
@@ -2639,7 +2654,7 @@ public class GameAction {
if (showRevealDialog) {
final String message = Localizer.getInstance().getMessage("lblMilledCards");
final boolean addSuffix = !toZoneStr.isEmpty();
reveal(milledPlayer, destination, p, false, message, addSuffix);
game.getAction().reveal(milledPlayer, destination, p, false, message, addSuffix);
}
game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, p + " milled " +
Lang.joinHomogenous(milledPlayer) + toZoneStr + ".");
@@ -2656,7 +2671,7 @@ public class GameAction {
}
public void dealDamage(final boolean isCombat, final CardDamageMap damageMap, final CardDamageMap preventMap,
final GameEntityCounterTable counterTable, final SpellAbility cause) {
final GameEntityCounterTable counterTable, final SpellAbility cause) {
// Clear assigned damage if is combat
if (isCombat) {
for (Map.Entry<GameEntity, Map<Card, Integer>> et : damageMap.columnMap().entrySet()) {

View File

@@ -57,6 +57,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
/**
* <p>
* GameActionUtil class.
@@ -858,6 +859,8 @@ public final class GameActionUtil {
}
} else if (sa.getApi() == ApiType.ManaReflected) {
baseMana = abMana.getExpressChoice();
} else if (abMana.isSpecialMana()) {
baseMana = abMana.getExpressChoice();
} else {
baseMana = abMana.mana(sa);
}

View File

@@ -1,5 +1,6 @@
package forge.game.ability.effects;
import com.google.common.collect.Lists;
import forge.game.Game;

View File

@@ -5,7 +5,6 @@ import static forge.util.TextUtil.toManaString;
import java.util.List;
import java.util.Map;
import forge.game.card.CardUtil;
import forge.util.Lang;
import org.apache.commons.lang3.StringUtils;
@@ -18,11 +17,14 @@ import forge.game.GameActionUtil;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.keyword.Keyword;
import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Localizer;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
@@ -39,8 +41,8 @@ public class ManaEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
final Game game = host.getGame();
final Card card = sa.getHostCard();
final Game game = card.getGame();
final AbilityManaPart abMana = sa.getManaPart();
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
final Player activator = sa.getActivatingPlayer();
@@ -61,13 +63,13 @@ public class ManaEffect extends SpellAbilityEffect {
final Player chooser;
if (sa.hasParam("Chooser")) {
chooser = AbilityUtils.getDefinedPlayers(host, sa.getParam("Chooser"), sa).get(0);
chooser = AbilityUtils.getDefinedPlayers(card, sa.getParam("Chooser"), sa).get(0);
} else {
chooser = p;
}
if (abMana.isComboMana()) {
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(host, sa.getParam("Amount"), sa) : 1;
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1;
if (amount <= 0)
continue;
@@ -115,7 +117,7 @@ public class ManaEffect extends SpellAbilityEffect {
byte chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
differentChoice && (colorsNeeded == null || colorsNeeded.length <= nMana) ? fullOptions : colorOptions);
if (chosenColor == 0)
throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + host.getName());
throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + card.getName());
if (differentChoice) {
fullOptions = ColorSet.fromMask(fullOptions.getColor() - chosenColor);
@@ -157,14 +159,99 @@ public class ManaEffect extends SpellAbilityEffect {
colorMenu = mask == 0 ? ColorSet.WUBRG : ColorSet.fromMask(mask);
byte val = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
if (0 == val) {
throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + p + " color mana choice is empty for " + host.getName());
throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + p + " color mana choice is empty for " + card.getName());
}
game.getAction().notifyOfValue(sa, host, MagicColor.toSymbol(val), p);
game.getAction().notifyOfValue(sa, card, MagicColor.toSymbol(val), p);
abMana.setExpressChoice(MagicColor.toShortString(val));
}
else if (abMana.isSpecialMana()) {
handleSpecialMana(chooser, abMana, sa, true);
String type = abMana.getOrigProduced().split("Special ")[1];
if (type.equals("EnchantedManaCost")) {
Card enchanted = card.getEnchantingCard();
if (enchanted == null)
continue;
StringBuilder sb = new StringBuilder();
int generic = enchanted.getManaCost().getGenericCost();
for (ManaCostShard s : enchanted.getManaCost()) {
ColorSet cs = ColorSet.fromMask(s.getColorMask());
byte chosenColor;
if (cs.isColorless())
continue;
if (s.isOr2Generic()) { // CR 106.8
chosenColor = chooser.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), card, cs);
if (chosenColor == MagicColor.COLORLESS) {
generic += 2;
continue;
}
}
else if (cs.isMonoColor())
chosenColor = s.getColorMask();
else /* (cs.isMulticolor()) */ {
chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
}
sb.append(MagicColor.toShortString(chosenColor));
sb.append(' ');
}
if (generic > 0) {
sb.append(generic);
}
abMana.setExpressChoice(sb.toString().trim());
} else if (type.equals("LastNotedType")) {
final StringBuilder sb = new StringBuilder();
int nMana = 0;
for (Object o : card.getRemembered()) {
if (o instanceof String) {
sb.append(o);
nMana++;
}
}
if (nMana == 0) {
return;
}
abMana.setExpressChoice(sb.toString());
} else if (type.startsWith("EachColorAmong")) {
final String res = type.split("_")[1];
final boolean defined = type.startsWith("EachColorAmongDefined");
final ZoneType zone = defined || type.startsWith("EachColorAmong_") ? ZoneType.Battlefield :
ZoneType.smartValueOf(type.split("_")[0].substring(14));
final CardCollection list = defined ? AbilityUtils.getDefinedCards(card, res, sa) :
CardLists.getValidCards(card.getGame().getCardsIn(zone), res, activator, card, sa);
byte colors = 0;
for (Card c : list) {
colors |= c.getColor().getColor();
}
if (colors == 0) return;
abMana.setExpressChoice(ColorSet.fromMask(colors));
} else if (type.startsWith("EachColoredManaSymbol")) {
final String res = type.split("_")[1];
StringBuilder sb = new StringBuilder();
for (Card c : AbilityUtils.getDefinedCards(card, res, sa)) {
for (ManaCostShard s : c.getManaCost()) {
ColorSet cs = ColorSet.fromMask(s.getColorMask());
if (cs.isColorless())
continue;
sb.append(' ');
if (cs.isMonoColor())
sb.append(MagicColor.toShortString(s.getColorMask()));
else /* (cs.isMulticolor()) */ {
byte chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
sb.append(MagicColor.toShortString(chosenColor));
}
}
}
abMana.setExpressChoice(sb.toString().trim());
} else if (type.startsWith("DoubleManaInPool")) {
StringBuilder sb = new StringBuilder();
for (byte color : ManaAtom.MANATYPES) {
sb.append(StringUtils.repeat(MagicColor.toShortString(color) + " ", p.getManaPool().getAmountOfColor(color)));
}
abMana.setExpressChoice(sb.toString().trim());
}
}
String mana = GameActionUtil.generatedMana(sa);
@@ -174,7 +261,7 @@ public class ManaEffect extends SpellAbilityEffect {
String msg = "AbilityFactoryMana::manaResolve() - special mana effect is empty for";
Breadcrumb bread = new Breadcrumb(msg);
bread.setData("Card", host.getName());
bread.setData("Card", card.getName());
bread.setData("SA", sa.toString());
Sentry.addBreadcrumb(bread);
@@ -194,89 +281,6 @@ public class ManaEffect extends SpellAbilityEffect {
}
}
public static void handleSpecialMana(Player chooser, AbilityManaPart abMana, SpellAbility sa, boolean resolve) {
String type = abMana.getOrigProduced().split("Special ")[1];
Card host = sa.getHostCard();
if (resolve) {
if (type.equals("EnchantedManaCost")) {
Card enchanted = host.getEnchantingCard();
if (enchanted == null)
return;
StringBuilder sb = new StringBuilder();
int generic = enchanted.getManaCost().getGenericCost();
for (ManaCostShard s : enchanted.getManaCost()) {
ColorSet cs = ColorSet.fromMask(s.getColorMask());
byte chosenColor;
if (cs.isColorless())
continue;
if (s.isOr2Generic()) { // CR 106.8
chosenColor = chooser.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), host, cs);
if (chosenColor == MagicColor.COLORLESS) {
generic += 2;
continue;
}
} else if (cs.isMonoColor())
chosenColor = s.getColorMask();
else /* (cs.isMulticolor()) */ {
chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
}
sb.append(MagicColor.toShortString(chosenColor));
sb.append(' ');
}
if (generic > 0) {
sb.append(generic);
}
abMana.setExpressChoice(sb.toString().trim());
} else if (type.startsWith("EachColoredManaSymbol")) {
final String res = type.split("_")[1];
StringBuilder sb = new StringBuilder();
for (Card c : AbilityUtils.getDefinedCards(host, res, sa)) {
for (ManaCostShard s : c.getManaCost()) {
ColorSet cs = ColorSet.fromMask(s.getColorMask());
if (cs.isColorless())
continue;
sb.append(' ');
if (cs.isMonoColor())
sb.append(MagicColor.toShortString(s.getColorMask()));
else /* (cs.isMulticolor()) */ {
byte chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
sb.append(MagicColor.toShortString(chosenColor));
}
}
}
abMana.setExpressChoice(sb.toString().trim());
} else if (type.startsWith("DoubleManaInPool")) {
StringBuilder sb = new StringBuilder();
for (byte color : ManaAtom.MANATYPES) {
sb.append(StringUtils.repeat(MagicColor.toShortString(color) + " ", chooser.getManaPool().getAmountOfColor(color)));
}
abMana.setExpressChoice(sb.toString().trim());
}
} else if (type.equals("LastNotedType")) {
// Jeweled Lotus
final StringBuilder sb = new StringBuilder();
for (Object o : host.getRemembered()) {
if (o instanceof String) {
sb.append(o);
}
}
String mana = sb.toString();
if (mana.isEmpty()) {
return;
}
abMana.setExpressChoice(mana);
} else if (type.startsWith("EachColorAmong")) {
final String res = type.split("_")[1];
ColorSet colors = CardUtil.getColorsFromCards(AbilityUtils.getDefinedCards(host, res, sa));
if (colors.isColorless()) return;
abMana.setExpressChoice(colors);
}
}
/**
* <p>
* manaStackDescription.

View File

@@ -3279,6 +3279,10 @@ public class CardFactoryUtil {
sb.append("AB$ PutCounter | Cost$ ").append(manacost).append(" | PrecostDesc$ Level up | CostDesc$ ");
sb.append(ManaCostParser.parse(manacost)).append(" | SorcerySpeed$ True | Secondary$ True");
sb.append("| CounterType$ LEVEL | StackDescription$ {p:You} levels up {c:Self}.");
if (card.hasSVar("maxLevel")) {
final String strMaxLevel = card.getSVar("maxLevel");
sb.append("| MaxLevel$ ").append(strMaxLevel);
}
sb.append(" | SpellDescription$ (").append(inst.getReminderText()).append(")");
final SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);

View File

@@ -41,9 +41,9 @@ public class CostAdjustment {
return cost;
}
final Player activator = sa.getActivatingPlayer();
final Player player = sa.getActivatingPlayer();
final Card host = sa.getHostCard();
final Game game = activator.getGame();
final Game game = player.getGame();
Cost result = cost.copy();
boolean isStateChangeToFaceDown = false;
@@ -56,7 +56,7 @@ public class CostAdjustment {
// Commander Tax there
if (host.isCommander() && host.getCastFrom() != null && ZoneType.Command.equals(host.getCastFrom().getZoneType())) {
int n = activator.getCommanderCast(host) * 2;
int n = player.getCommanderCast(host) * 2;
if (n > 0) {
result.add(new Cost(ManaCost.get(n), false));
}
@@ -152,13 +152,17 @@ public class CostAdjustment {
}
sub = sub.getSubAbility();
}
} else if (StringUtils.isNumeric(amount)) {
count = Integer.parseInt(amount);
} else if (st.hasParam("Relative")) {
// grab SVar here already to avoid potential collision when SA has one with same name
count = AbilityUtils.calculateAmount(hostCard, st.hasSVar(amount) ? st.getSVar(amount) : amount, sa);
} else {
count = AbilityUtils.calculateAmount(hostCard, amount, st);
if (StringUtils.isNumeric(amount)) {
count = Integer.parseInt(amount);
} else {
if (st.hasParam("Relative")) {
// grab SVar here already to avoid potential collision when SA has one with same name
count = AbilityUtils.calculateAmount(hostCard, st.hasSVar(amount) ? st.getSVar(amount) : amount, sa);
} else {
count = AbilityUtils.calculateAmount(hostCard, amount, st);
}
}
}
} else {
// Amount 1 as default
@@ -172,16 +176,15 @@ public class CostAdjustment {
// If cardsToDelveOut is null, will immediately exile the delved cards and remember them on the host card.
// Otherwise, will return them in cardsToDelveOut and the caller is responsible for doing the above.
public static boolean adjust(ManaCostBeingPaid cost, final SpellAbility sa, final Player payer, CardCollection cardsToDelveOut, boolean test, boolean effect) {
public static boolean adjust(ManaCostBeingPaid cost, final SpellAbility sa, CardCollection cardsToDelveOut, boolean test, boolean effect) {
if (effect) {
adjustCostByWaterbend(cost, sa, payer, test);
adjustCostByWaterbend(cost, sa, test);
}
if (effect || sa.isTrigger() || sa.isReplacementAbility()) {
return true;
}
final Player activator = sa.getActivatingPlayer();
final Game game = activator.getGame();
final Game game = sa.getActivatingPlayer().getGame();
final Card originalCard = sa.getHostCard();
boolean isStateChangeToFaceDown = false;
@@ -227,7 +230,7 @@ public class CostAdjustment {
}
while (!reduceAbilities.isEmpty()) {
StaticAbility choice = activator.getController().chooseSingleStaticAbility(Localizer.getInstance().getMessage("lblChooseCostReduction"), reduceAbilities);
StaticAbility choice = sa.getActivatingPlayer().getController().chooseSingleStaticAbility(Localizer.getInstance().getMessage("lblChooseCostReduction"), reduceAbilities);
reduceAbilities.remove(choice);
sumGeneric += applyReduceCostAbility(choice, sa, cost, sumGeneric);
}
@@ -260,8 +263,9 @@ public class CostAdjustment {
sa.getHostCard().clearDelved();
final CardZoneTable table = new CardZoneTable();
final CardCollection mutableGrave = new CardCollection(activator.getCardsIn(ZoneType.Graveyard));
final CardCollectionView toExile = activator.getController().chooseCardsToDelve(cost.getUnpaidShards(ManaCostShard.GENERIC), mutableGrave);
final Player pc = sa.getActivatingPlayer();
final CardCollection mutableGrave = new CardCollection(pc.getCardsIn(ZoneType.Graveyard));
final CardCollectionView toExile = pc.getController().chooseCardsToDelve(cost.getUnpaidShards(ManaCostShard.GENERIC), mutableGrave);
for (final Card c : toExile) {
cost.decreaseGenericMana(1);
if (cardsToDelveOut != null) {
@@ -280,18 +284,18 @@ public class CostAdjustment {
table.triggerChangesZoneAll(game, sa);
}
if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) {
adjustCostByConvokeOrImprovise(cost, sa, activator, false, true, test);
adjustCostByConvokeOrImprovise(cost, sa, false, true, test);
}
if (sa.getHostCard().hasKeyword(Keyword.IMPROVISE)) {
adjustCostByConvokeOrImprovise(cost, sa, activator, true, false, test);
adjustCostByConvokeOrImprovise(cost, sa, true, false, test);
}
} // isSpell
if (sa.hasParam("TapCreaturesForMana")) {
adjustCostByConvokeOrImprovise(cost, sa, activator, false, true, test);
adjustCostByConvokeOrImprovise(cost, sa, false, true, test);
}
adjustCostByWaterbend(cost, sa, payer, test);
adjustCostByWaterbend(cost, sa, test);
// Reset card state (if changed)
if (isStateChangeToFaceDown) {
@@ -303,6 +307,13 @@ public class CostAdjustment {
}
// GetSpellCostChange
private static void adjustCostByWaterbend(ManaCostBeingPaid cost, SpellAbility sa, boolean test) {
Integer maxWaterbend = sa.getMaxWaterbend();
if (maxWaterbend != null && maxWaterbend > 0) {
adjustCostByConvokeOrImprovise(cost, sa, true, true, test);
}
}
private static boolean adjustCostByAssist(ManaCostBeingPaid cost, final SpellAbility sa, boolean test) {
// 702.132a Assist is a static ability that modifies the rules of paying for the spell with assist (see rules 601.2g-h).
// If the total cost to cast a spell with assist includes a generic mana component, before you activate mana abilities while casting it, you may choose another player.
@@ -325,19 +336,13 @@ public class CostAdjustment {
return assistant.getController().helpPayForAssistSpell(cost, sa, genericLeft, requestedAmount);
}
private static void adjustCostByWaterbend(ManaCostBeingPaid cost, SpellAbility sa, Player payer, boolean test) {
Integer maxWaterbend = sa.getMaxWaterbend();
if (maxWaterbend != null && maxWaterbend > 0) {
adjustCostByConvokeOrImprovise(cost, sa, payer, true, true, test);
}
}
private static void adjustCostByConvokeOrImprovise(ManaCostBeingPaid cost, final SpellAbility sa, final Player payer, boolean artifacts, boolean creatures, boolean test) {
private static void adjustCostByConvokeOrImprovise(ManaCostBeingPaid cost, final SpellAbility sa, boolean artifacts, boolean creatures, boolean test) {
if (creatures && !artifacts) {
sa.clearTappedForConvoke();
}
CardCollectionView untappedCards = CardLists.filter(payer.getCardsIn(ZoneType.Battlefield),
final Player activator = sa.getActivatingPlayer();
CardCollectionView untappedCards = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield),
CardPredicates.CAN_TAP);
Integer maxReduction = null;
@@ -351,7 +356,7 @@ public class CostAdjustment {
untappedCards = CardLists.filter(untappedCards, CardPredicates.CREATURES);
}
Map<Card, ManaCostShard> convokedCards = payer.getController().chooseCardsForConvokeOrImprovise(sa,
Map<Card, ManaCostShard> convokedCards = activator.getController().chooseCardsForConvokeOrImprovise(sa,
cost.toManaCost(), untappedCards, artifacts, creatures, maxReduction);
CardCollection tapped = new CardCollection();
@@ -362,13 +367,13 @@ public class CostAdjustment {
}
cost.decreaseShard(conv.getValue(), 1);
if (!test) {
if (c.tap(true, sa, payer)) tapped.add(c);
if (c.tap(true, sa, activator)) tapped.add(c);
}
}
if (!tapped.isEmpty()) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Cards, tapped);
payer.getGame().getTriggerHandler().runTrigger(TriggerType.TapAll, runParams, false);
activator.getGame().getTriggerHandler().runTrigger(TriggerType.TapAll, runParams, false);
}
}

View File

@@ -235,7 +235,6 @@ public class ReplacementHandler {
Map<AbilityKey, Object> params = AbilityKey.newMap(runParams);
params.remove(AbilityKey.ReplacementResult);
// CR 614.16
if (params.containsKey(AbilityKey.EffectOnly)) {
params.put(AbilityKey.EffectOnly, true);
}

View File

@@ -30,7 +30,6 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.SpellAbilityEffect;
import forge.game.ability.effects.ManaEffect;
import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.cost.Cost;
@@ -516,11 +515,7 @@ public class AbilityManaPart implements java.io.Serializable {
}
String produced = this.getOrigProduced();
if (produced.contains("Chosen")) {
produced = produced.replace("Chosen", getChosenColor(sa));
}
if (isSpecialMana()) {
ManaEffect.handleSpecialMana(sa.getActivatingPlayer(), this, sa, false);
produced = getExpressChoice();
produced = produced.replace("Chosen", getChosenColor(sa, sa.getHostCard().getChosenColors()));
}
return produced;
}
@@ -656,7 +651,7 @@ public class AbilityManaPart implements java.io.Serializable {
}
// replace Chosen for Combo colors
if (origProduced.contains("Chosen")) {
origProduced = origProduced.replace("Chosen", getChosenColor(sa));
origProduced = origProduced.replace("Chosen", getChosenColor(sa, sa.getHostCard().getChosenColors()));
}
// replace Chosen for Spire colors
if (origProduced.contains("ColorID")) {
@@ -706,14 +701,14 @@ public class AbilityManaPart implements java.io.Serializable {
return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1);
}
public String getChosenColor(SpellAbility sa) {
public String getChosenColor(SpellAbility sa, Iterable<String> colors) {
if (sa == null) {
return "";
}
Card card = sa.getHostCard();
if (card != null) {
StringBuilder values = new StringBuilder();
for (String c : card.getChosenColors()) {
for (String c : colors) {
values.append(MagicColor.toShortString(c)).append(" ");
}
return values.toString().trim();

View File

@@ -2699,7 +2699,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
public void setMaxWaterbend(Cost cost) {
if (cost == null || cost.getMaxWaterbend() == null) {
maxWaterbend = 0;
return;
}
maxWaterbend = AbilityUtils.calculateAmount(getHostCard(), cost.getMaxWaterbend(), this);

View File

@@ -6,7 +6,7 @@ K:Flying
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigDig | TriggerDescription$ When NICKNAME enters, look at the top five cards of your library. You may put a creature card with mana value 4 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order.
SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | ChangeValid$ Creature.cmcLE4 | Optional$ True | DestinationZone$ Battlefield | DestinationZone2$ Library | LibraryPosition$ -1 | RestRandomOrder$ True
T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Origin$ Battlefield | Destination$ Any | Execute$ TrigDelayTransform | TriggerZones$ Battlefield | TriggerDescription$ When another creature you control leaves the battlefield, transform NICKNAME at the beginning of the next upkeep.
SVar:TrigDelayTransform:DB$ DelayedTrigger | Mode$ Phase | Phase$ Upkeep | Execute$ TrigTransform | TriggerDescription$ CARDNAME — Transform him at the beginning of the next upkeep
SVar:TrigDelayTransform:DB$ DelayedTrigger | Mode$ Phase | Phase$ Upkeep | Execute$ TrigTransform | TriggerDescription$ CARDNAME — Transform him at the beginning of the next end step.
SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform
AlternateMode:DoubleFaced
Oracle:Flying\nWhen Aang enters, look at the top five cards of your library. You may put a creature card with mana value 4 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order.\nWhen another creature you control leaves the battlefield, transform Aang at the beginning of the next upkeep.

View File

@@ -2,7 +2,7 @@ Name:Baboon Spirit
ManaCost:2 U
Types:Creature Monkey Spirit
PT:2/4
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Spirit.!token+Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever another nontoken Spirit you control enters, create a 1/1 colorless Spirit creature token with "This token can't block or be blocked by non-Spirit creatures."
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Spirit.!token+Other+YouCtrl | Execute$ TrigToken | TriggerDescription$ Whenever another nontoken Spirit you control enters, create a 1/1 colorless Spirit creature token with "This token can't block or be blocked by non-Spirit creatures."
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_1_1_spirit_spiritshadow
A:AB$ ChangeZone | Cost$ 3 U | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DelTrig | SpellDescription$ Exile another target creature you control. Return it to the battlefield under its owner's control at the beginning of the next end step.
SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigBounce | RememberObjects$ RememberedLKI | TriggerDescription$ Return that card to the battlefield under its owner's control at the beginning of the next end step. | SubAbility$ DBCleanup

View File

@@ -3,6 +3,7 @@ ManaCost:1 G
Types:Creature Human Warrior
PT:2/2
K:Level up:2 G
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE3_LEVEL | PresentCompare$ EQ1 | Description$ LEVEL 1-3 4/4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 6 | SetToughness$ 6 | AddKeyword$ Trample | IsPresent$ Card.Self+counters_GE4_LEVEL | PresentCompare$ EQ1 | Description$ LEVEL 4+ 6/6 Trample
Oracle:Level up {2}{G} ({2}{G}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-3\n4/4\nLEVEL 4+\n6/6\nTrample

View File

@@ -2,6 +2,6 @@ Name:Bloom Tender
ManaCost:1 G
Types:Creature Elf Druid
PT:1/1
A:AB$ Mana | Cost$ T | Produced$ Special EachColorAmong_Valid Permanent.YouCtrl | SpellDescription$ For each color among permanents you control, add one mana of that color.
A:AB$ Mana | Cost$ T | Produced$ Special EachColorAmong_Permanent.YouCtrl | SpellDescription$ For each color among permanents you control, add one mana of that color.
AI:RemoveDeck:All
Oracle:{T}: For each color among permanents you control, add one mana of that color.

View File

@@ -3,6 +3,7 @@ ManaCost:2 R
Types:Creature Human Shaman
PT:2/2
K:Level up:3 R
SVar:maxLevel:3
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 3 | AddAbility$ Ping | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE2_LEVEL | Description$ LEVEL 1-2 2/3 {T}: CARDNAME deals 1 damage to any target.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 4 | AddAbility$ Bolt | IsPresent$ Card.Self+counters_GE3_LEVEL | Description$ LEVEL 3+ 2/4 {T}: CARDNAME deals 3 damage to any target.
SVar:Ping:AB$ DealDamage | Cost$ T | ValidTgts$ Any | NumDmg$ 1 | Secondary$ True | SpellDescription$ CARDNAME deals 1 damage to any target.

View File

@@ -3,6 +3,7 @@ ManaCost:W
Types:Creature Human Knight
PT:1/1
K:Level up:2
SVar:maxLevel:5
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 2 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE4_LEVEL | Description$ LEVEL 1-4 2/2
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 5 | SetToughness$ 5 | IsPresent$ Card.Self+counters_GE5_LEVEL | AddKeyword$ First Strike | Description$ LEVEL 5+ 5/5 First strike
Oracle:Level up {2} ({2}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-4\n2/2\nLEVEL 5+\n5/5\nFirst strike

View File

@@ -2,7 +2,7 @@ Name:Chakra Meditation
ManaCost:2 U
Types:Enchantment
T:Mode$ ChangesZone | ValidCard$ Card.Self | Destination$ Battlefield | Execute$ TrigReturn | TriggerDescription$ When this enchantment enters, return up to one target instant or sorcery card from your graveyard to your hand.
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Instant.YouOwn,Sorcery.YouOwn | TgtPrompt$ Select target instant or sorcery card in your graveyard | Origin$ Graveyard | TargetMin$ 0 | TargetMax$ 1 | Destination$ Hand
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Instant.YouOwn.Sorcery.YouOwn | TgtPrompt$ Select target instant or sorcery card in your graveyard | Origin$ Graveyard | TargetMin$ 0 | TargetMax$ 1 | Destination$ Hand
T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast an instant or sorcery spell, draw a card. Then discard a card unless there are three or more Lesson cards in your graveyard.
SVar:TrigDraw:DB$ Draw | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LT3

View File

@@ -3,6 +3,7 @@ ManaCost:U U
Types:Creature Merfolk Soldier
PT:2/2
K:Level up:1
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 3 | AddKeyword$ Flying | IsPresent$ Card.Self+counters_GE2_LEVEL+counters_LE3_LEVEL | PresentCompare$ EQ1 | Description$ LEVEL 2-3 3/3 Flying
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | AddKeyword$ Flying | AddStaticAbility$ SBoost | IsPresent$ Card.Self+counters_GE4_LEVEL | PresentCompare$ EQ1 | Description$ LEVEL 4+ 4/4 Flying,,,Other Merfolk creatures you control get +1/+1.
SVar:SBoost:Mode$ Continuous | Affected$ Creature.Merfolk+YouCtrl+Other | AddPower$ 1 | AddToughness$ 1 | IsPresent$ Card.Self+counters_GE4_LEVEL | PresentCompare$ EQ1

View File

@@ -3,6 +3,7 @@ ManaCost:1 U U
Types:Creature Human Wizard
PT:2/3
K:Level up:1 U
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 4 | AddAbility$ CopyOnce | IsPresent$ Card.Self+counters_GE2_LEVEL+counters_LT4_LEVEL | Description$ LEVEL 2-3 2/4 {U}{U}, {T}: Copy target instant or sorcery spell. You may choose new targets for the copy.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 5 | AddAbility$ CopyTwice | IsPresent$ Card.Self+counters_GE4_LEVEL | Description$ LEVEL 4+ 2/5 {U}{U}, {T}: Copy target instant or sorcery spell twice. You may choose new targets for the copies.
SVar:CopyOnce:AB$ CopySpellAbility | Cost$ U U T | ValidTgts$ Instant,Sorcery | AILogic$ Once | MayChooseTarget$ True | Secondary$ True | SpellDescription$ Copy target instant or sorcery spell. You may choose new targets for the copy.

View File

@@ -3,6 +3,7 @@ ManaCost:U
Types:Creature Merfolk Wizard
PT:0/1
K:Level up:1 U
SVar:maxLevel:3
S:Mode$ Continuous | Affected$ Card.Self | AddAbility$ Loot | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE2_LEVEL | Description$ LEVEL 1-2 {T}: Draw a card, then discard a card.
S:Mode$ Continuous | Affected$ Card.Self | AddAbility$ Draw | IsPresent$ Card.Self+counters_GE3_LEVEL | Description$ LEVEL 3+ {T}: Draw a card.
SVar:Loot:AB$ Draw | Cost$ T | Secondary$ True | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard

View File

@@ -5,6 +5,6 @@ PT:0/0
K:Vigilance
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | AddToughness$ X | Description$ CARDNAME gets +1/+1 for each color among permanents you control.
SVar:X:Count$Valid Permanent.YouCtrl$Colors
A:AB$ Mana | Cost$ T | Produced$ Special EachColorAmong_Valid Permanent.YouCtrl | SpellDescription$ For each color among permanents you control, add one mana of that color.
SVar:NoZeroToughnessAI:True
A:AB$ Mana | Cost$ T | Produced$ Special EachColorAmong_Permanent.YouCtrl | SpellDescription$ For each color among permanents you control, add one mana of that color.
AI:RemoveDeck:All
Oracle:Vigilance\nFaeburrow Elder gets +1/+1 for each color among permanents you control.\n{T}: For each color among permanents you control, add one mana of that color.

View File

@@ -3,6 +3,7 @@ ManaCost:B
Types:Creature Vampire Assassin
PT:1/1
K:Level up:1 B
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 2 | AddAbility$ LowLvl | IsPresent$ Card.Self+counters_GE2_LEVEL+counters_LE3_LEVEL | Description$ LEVEL 2-3 2/2 {B}, {T}: Target creature gets -2/-2 until end of turn.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | AddAbility$ HighLvl | IsPresent$ Card.Self+counters_GE4_LEVEL | Description$ LEVEL 4+ 4/4 {B}, {T}: Target creature gets -4/-4 until end of turn.
SVar:LowLvl:AB$ Pump | Cost$ B T | ValidTgts$ Creature | Secondary$ True | NumAtt$ -2 | NumDef$ -2 | IsCurse$ True | SpellDescription$ Target creature gets -2/-2 until end of turn.

View File

@@ -3,6 +3,7 @@ ManaCost:1 U
Types:Creature Human Rogue
PT:1/1
K:Level up:2 U
SVar:maxLevel:3
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 2 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE2_LEVEL | Description$ LEVEL 1-2 2/2 CARDNAME can't be blocked.
S:Mode$ CantBlockBy | ValidAttacker$ Card.Self | IsPresent$ Card.Self+counters_GE1_LEVEL
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 3 | AddKeyword$ Shroud | IsPresent$ Card.Self+counters_GE3_LEVEL | Description$ LEVEL 3+ 3/3 Shroud (This creature can't be the target of spells or abilities.),,,CARDNAME can't be blocked.

View File

@@ -3,6 +3,7 @@ ManaCost:1 U
Types:Creature Merfolk Soldier
PT:0/3
K:Level up:2
SVar:maxLevel:5
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 0 | SetToughness$ 6 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE4_LEVEL | Description$ LEVEL 1-4 0/6
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 6 | SetToughness$ 6 | AddKeyword$ Landwalk:Island | IsPresent$ Card.Self+counters_GE5_LEVEL | Description$ LEVEL 5+ 6/6 Islandwalk
Oracle:Level up {2} ({2}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-4\n0/6\nLEVEL 5+\n6/6\nIslandwalk (This creature can't be blocked as long as defending player controls an Island.)

View File

@@ -3,6 +3,7 @@ ManaCost:2 W
Types:Creature Human Cleric
PT:0/3
K:Level up:2 W
SVar:maxLevel:5
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 1 | SetToughness$ 4 | AddReplacementEffect$ RDamage1 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE4_LEVEL | Description$ LEVEL 1-4 1/4 If a source would deal damage to you or a creature you control, prevent 1 of that damage.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 5 | AddReplacementEffect$ RDamage2 | IsPresent$ Card.Self+counters_GE5_LEVEL | Description$ LEVEL 5+ 2/5 If a source would deal damage to you or a creature you control, prevent 2 of that damage.
SVar:RDamage1:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ You,Creature.YouCtrl | ReplaceWith$ DBReplace1 | PreventionEffect$ True | Secondary$ True | Description$ If a source would deal damage to you or a creature you control, prevent 1 of that damage.

View File

@@ -3,6 +3,7 @@ ManaCost:G
Types:Creature Snake
PT:2/1
K:Level up:1
SVar:maxLevel:8
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | AddKeyword$ Protection:Instant:instants | IsPresent$ Card.Self+counters_GE3_LEVEL+counters_LT8_LEVEL | Description$ LEVEL 3-7 4/4 Protection from instants
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 6 | SetToughness$ 6 | AddKeyword$ Protection from everything | IsPresent$ Card.Self+counters_GE8_LEVEL | Description$ LEVEL 8+ 6/6 Protection from everything
Oracle:Level up {1} ({1}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 3-7\n4/4\nProtection from instants\nLEVEL 8+\n6/6\nProtection from everything

View File

@@ -3,6 +3,7 @@ ManaCost:1 W
Types:Creature Human Soldier
PT:1/2
K:Level up:4
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 6 | AddKeyword$ Vigilance | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE3_LEVEL | Description$ LEVEL 1-3 2/6 Vigilance
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 10 | AddKeyword$ Vigilance | IsPresent$ Card.Self+counters_GE4_LEVEL | Description$ LEVEL 4+ 3/10 Vigilance
Oracle:Level up {4} ({4}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-3\n2/6\nVigilance\nLEVEL 4+\n3/10\nVigilance

View File

@@ -4,4 +4,5 @@ Types:Artifact
A:AB$ PutCounter | Cost$ 1 T | RememberCostMana$ True | CounterType$ CHARGE | CounterNum$ 1 | CheckSVar$ X | SVarCompare$ EQ0 | SpellDescription$ Put a charge counter on CARDNAME. Note the type of mana spent to pay this activation cost. Activate only if there are no charge counters on CARDNAME.
SVar:X:Count$CardCounters.CHARGE
A:AB$ Mana | Cost$ T SubCounter<1/CHARGE> | Produced$ Special LastNotedType | SpellDescription$ Add one mana of CARDNAME's last noted type.
AI:RemoveDeck:All
Oracle:{1}, {T}: Put a charge counter on Jeweled Amulet. Note the type of mana spent to pay this activation cost. Activate only if there are no charge counters on Jeweled Amulet.\n{T}, Remove a charge counter from Jeweled Amulet: Add one mana of Jeweled Amulet's last noted type.

View File

@@ -3,6 +3,7 @@ ManaCost:G
Types:Creature Elf Druid
PT:1/1
K:Level up:1 G
SVar:maxLevel:5
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 1 | SetToughness$ 2 | AddAbility$ ABMana | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE4_LEVEL | Description$ LEVEL 1-4 1/2 {T}: Add {G}{G}.
SVar:ABMana:AB$ Mana | Cost$ T | Produced$ G | Amount$ 2 | SpellDescription$ Add {G}{G}.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 1 | SetToughness$ 4 | IsPresent$ Card.Self+counters_GE5_LEVEL

View File

@@ -3,6 +3,7 @@ ManaCost:3 W
Types:Creature Human Knight
PT:2/4
K:Level up:2 W
SVar:maxLevel:5
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 6 | AddStaticAbility$ SPump1 | IsPresent$ Card.Self+counters_GE2_LEVEL+counters_LE4_LEVEL | Description$ LEVEL 2-4 3/6 Other creatures you control get +1/+1.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 8 | AddStaticAbility$ SPump2 | IsPresent$ Card.Self+counters_GE5_LEVEL | Description$ LEVEL 5+ 4/8 Other creatures you control get +2/+2.
SVar:SPump1:Mode$ Continuous | Affected$ Creature.YouCtrl+Other | AddPower$ 1 | AddToughness$ 1 | Secondary$ True | Description$ Other creatures you control get +1/+1.

View File

@@ -3,6 +3,7 @@ ManaCost:R R
Types:Creature Human Warrior
PT:2/2
K:Level up:R
SVar:maxLevel:8
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | AddKeyword$ Flying | IsPresent$ Card.Self+counters_GE4_LEVEL+counters_LE7_LEVEL | Description$ LEVEL 4-7 4/4 Flying
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 8 | SetToughness$ 8 | AddKeyword$ Flying & Trample | AddAbility$ Pump | IsPresent$ Card.Self+counters_GE8_LEVEL | Description$ LEVEL 8+ 8/8 Flying, trample,,,{R}: CARDNAME gets +1/+0 until end of turn.
SVar:Pump:AB$ Pump | Cost$ R | Secondary$ True | NumAtt$ +1 | SpellDescription$ CARDNAME gets +1/+0 until end of turn.

View File

@@ -3,6 +3,7 @@ ManaCost:1 G
Types:Creature Human Shaman
PT:1/1
K:Level up:1 G
SVar:maxLevel:6
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 1 | SetToughness$ 1 | AddAbility$ TokenLow | IsPresent$ Card.Self+counters_GE2_LEVEL+counters_LE5_LEVEL | Description$ LEVEL 2-5 1/1 {T}: Create a 3/3 green Elephant creature token.
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 1 | SetToughness$ 1 | AddAbility$ TokenHigh | IsPresent$ Card.Self+counters_GE6_LEVEL | Description$ LEVEL 6+ 1/1 {T}: Create two 3/3 green Elephant creature tokens.
SVar:TokenLow:AB$ Token | Cost$ T | TokenScript$ g_3_3_elephant | Secondary$ True | SpellDescription$ Create a 3/3 green Elephant creature token.

View File

@@ -3,6 +3,7 @@ ManaCost:1 W
Types:Creature Kor Knight
PT:2/2
K:Level up:3
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 3 | AddKeyword$ Flying | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE3_LEVEL | Description$ LEVEL 1-3 2/3 Flying
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | AddKeyword$ Flying & Vigilance | IsPresent$ Card.Self+counters_GE4_LEVEL | Description$ LEVEL 4+ 4/4 Flying, vigilance
Oracle:Level up {3} ({3}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-3\n2/3\nFlying\nLEVEL 4+\n4/4\nFlying, vigilance

View File

@@ -3,6 +3,7 @@ ManaCost:1 U
Types:Creature Human Wizard
PT:1/3
K:Level up:U
SVar:maxLevel:7
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 4 | IsPresent$ Card.Self+counters_GE4_LEVEL+counters_LE6_LEVEL | Description$ LEVEL 4-6 2/4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 5 | AddTrigger$ TriggerExtraTurn | IsPresent$ Card.Self+counters_GE7_LEVEL | Description$ LEVEL 7+ 3/5 At the beginning of each end step, if it's not your turn, take an extra turn after this one.
SVar:TriggerExtraTurn:Mode$ Phase | Phase$ End of Turn | NotPlayerTurn$ True | Execute$ TrigExtraTurn | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ At the beginning of each end step, if it's not your turn, take an extra turn after this one.

View File

@@ -3,6 +3,7 @@ ManaCost:3 R
Types:Creature Minotaur Shaman
PT:3/3
K:Level up:1 R
SVar:maxLevel:6
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 6 | SetToughness$ 6 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE5_LEVEL | Description$ LEVEL 1-5 6/6
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 6 | SetToughness$ 6 | AddTrigger$ TriggerDamage | AddSVar$ AE | IsPresent$ Card.Self+counters_GE6_LEVEL | Description$ LEVEL 6+ 6/6 Whenever CARDNAME attacks, it deals 6 damage to each creature defending player controls.
SVar:TriggerDamage:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks, it deals 6 damage to each creature defending player controls.

View File

@@ -3,6 +3,7 @@ ManaCost:2 B
Types:Creature Vampire Warrior
PT:3/2
K:Level up:2 B
SVar:maxLevel:3
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 3 | AddKeyword$ Deathtouch | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE2_LEVEL | Description$ LEVEL 1-2 4/3 Deathtouch
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 5 | SetToughness$ 4 | AddKeyword$ First Strike & Deathtouch | IsPresent$ Card.Self+counters_GE3_LEVEL | Description$ LEVEL 3+ 5/4 First strike, deathtouch
Oracle:Level up {2}{B} ({2}{B}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-2\n4/3\nDeathtouch\nLEVEL 3+\n5/4\nFirst strike, deathtouch

View File

@@ -3,6 +3,7 @@ ManaCost:1 B
Types:Creature Zombie Warrior
PT:1/1
K:Level up:3
SVar:maxLevel:4
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 2 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE3_LEVEL | Description$ LEVEL 1-3 4/2
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 7 | SetToughness$ 3 | AddAbility$ Regen | IsPresent$ Card.Self+counters_GE4_LEVEL | Description$ LEVEL 4+ 7/3 {B}: Regenerate CARDNAME.
SVar:Regen:AB$ Regenerate | Cost$ B | Secondary$ True | SpellDescription$ Regenerate CARDNAME.

View File

@@ -3,7 +3,7 @@ ManaCost:2 B B
Types:Artifact Vehicle
PT:4/4
K:Flying
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE1 | Execute$ TrigCopy | TriggerDescription$ At the beginning of your end step, if you sacrificed a permanent this turn, create a token that's a copy of this Vehicle.
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE1 | Execute$ TrigCopy | TriggerDescription$ At the beginning of your end step, if you sacrificed a permanent this turn, create a token that's a copy of this Vehicle.
SVar:TrigCopy:DB$ CopyPermanent | Defined$ Self | NumCopies$ 1
S:Mode$ Continuous | Affected$ Card.Self | IsPresent$ Permanent.YouCtrl+namedPhoenix Fleet Airship | PresentCompare$ GE8 | AddType$ Artifact & Creature | Description$ As long as you control eight or more permanents named Phoenix Fleet Airship, this Vehicle is an artifact creature.
K:Crew:1

View File

@@ -1,4 +1,4 @@
Name:Platypus-Bear
Name:Platypus Bear
ManaCost:1 GU
Types:Creature Platypus Bear
PT:2/3

View File

@@ -1,6 +0,0 @@
Name:Ruthless Waterbender
ManaCost:1 B
Types:Creature Human Soldier Ally
PT:1/3
A:AB$ Pump | Cost$ Waterbend<2> | Defined$ Self | NumAtt$ +1 | NumDef$ +1 | PlayerTurn$ True | SpellDescription$ This creature gets +1/+1 until end of turn. Activate only during your turn. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
Oracle:Waterbend {2}: This creature gets +1/+1 until end of turn. Activate only during your turn. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)

View File

@@ -3,6 +3,7 @@ ManaCost:U
Types:Creature Merfolk Wizard
PT:1/1
K:Level up:3
SVar:maxLevel:3
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 2 | SetToughness$ 2 | AddKeyword$ Flying | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE2_LEVEL | Description$ LEVEL 1-2 2/2 Flying
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 2 | AddKeyword$ Flying | IsPresent$ Card.Self+counters_GE3_LEVEL | Description$ LEVEL 3+ 4/2 Flying
Oracle:Level up {3} ({3}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 1-2\n2/2\nFlying\nLEVEL 3+\n4/2\nFlying

View File

@@ -3,6 +3,7 @@ ManaCost:W
Types:Creature Human Knight
PT:1/1
K:Level up:W
SVar:maxLevel:7
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 3 | AddKeyword$ First Strike | IsPresent$ Card.Self+counters_GE2_LEVEL+counters_LE6_LEVEL | Description$ LEVEL 2-6 3/3 First strike
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 4 | SetToughness$ 4 | AddKeyword$ Double Strike | IsPresent$ Card.Self+counters_GE7_LEVEL | Description$ LEVEL 7+ 4/4 Double strike
Oracle:Level up {W} ({W}: Put a level counter on this. Level up only as a sorcery.)\nLEVEL 2-6\n3/3\nFirst strike\nLEVEL 7+\n4/4\nDouble strike

View File

@@ -1,4 +1,4 @@
Name:Suki, Kyoshi Captain
Name:Suki, Kyoshi Warrior
ManaCost:2 W
Types:Legendary Creature Human Warrior Ally
PT:3/3

View File

@@ -21,5 +21,5 @@ K:Vigilance
K:Haste
S:Mode$ Continuous | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of colors among the exiled cards used to craft it.
SVar:X:ExiledWith$Colors
A:AB$ Mana | Cost$ T | Produced$ Special EachColorAmong_ExiledWith | SpellDescription$ For each color among the exiled cards used to craft CARDNAME, add one mana of that color.
A:AB$ Mana | Cost$ T | Produced$ Special EachColorAmongDefined_ExiledWith | SpellDescription$ For each color among the exiled cards used to craft CARDNAME, add one mana of that color.
Oracle:Flying, vigilance, haste\nSunbird Effigy's power and toughness are each equal to the number of colors among the exiled cards used to craft it.\n{T}: For each color among the exiled cards used to craft Sunbird Effigy, add one mana of that color.

View File

@@ -6,5 +6,5 @@ SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True
K:ETBReplacement:Other:ChooseColor
SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters, choose a color.
A:AB$ Mana | Cost$ T | Produced$ Chosen | SpellDescription$ Add one mana of the chosen color.
A:AB$ Mana | Cost$ 1 T | Produced$ Special EachColorAmong_Valid Permanent.YouCtrl+MonoColor | SpellDescription$ For each color among monocolored permanents you control, add one mana of that color.
A:AB$ Mana | Cost$ 1 T | Produced$ Special EachColorAmong_Permanent.YouCtrl+MonoColor | SpellDescription$ For each color among monocolored permanents you control, add one mana of that color.
Oracle:Tarnation Vista enters tapped. As it enters, choose a color.\n{T}: Add one mana of the chosen color.\n{1}, {T}: For each color among monocolored permanents you control, add one mana of that color.

View File

@@ -4,7 +4,7 @@ Types:Legendary Artifact Vehicle
PT:6/3
K:Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigImmediateTrigger | TriggerDescription$ When CARDNAME enters, you may tap it. When you do, destroy target creature with power 4 or less.
SVar:TrigImmediateTrigger:AB$ ImmediateTrigger | Cost$ tapXType<1/Card.Self/CARDNAME> | Execute$ TrigDestroy | TriggerDescription$ When you do, destroy target creature with power 4 or less.
SVar:TrigImmediateTrigger:DB$ ImmediateTrigger | Cost$ tapXType<1/Card.Self/CARDNAME> | Execute$ TrigDestroy
SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Creature.powerLE4 | TgtPrompt$ Select target creature with power 4 or less
A:AB$ AnimateAll | Cost$ 1 | ValidCards$ Permanent.OppCtrl | RemoveKeywords$ Hexproof & Indestructible | SpellDescription$ Permanents your opponents control lose hexproof and indestructible until end of turn.
K:Crew:2

View File

@@ -3,6 +3,7 @@ ManaCost:1 W W
Types:Creature Human Cleric Avatar
PT:3/3
K:Level up:1
SVar:maxLevel:12
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 6 | SetToughness$ 6 | AddKeyword$ Lifelink | IsPresent$ Card.Self+counters_GE6_LEVEL+counters_LE11_LEVEL | Description$ LEVEL 6-11 6/6 Lifelink
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 9 | SetToughness$ 9 | AddKeyword$ Indestructible & Lifelink | IsPresent$ Card.Self+counters_GE12_LEVEL | Description$ LEVEL 12+ 9/9 Lifelink, indestructible
DeckHas:Ability$LifeGain

View File

@@ -2,6 +2,7 @@ Name:Under-Construction Skyscraper
ManaCost:no cost
Types:Land
K:Level up:1
SVar:maxLevel:8
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
DeckHas:Ability$Mana.Colorless
S:Mode$ Continuous | Affected$ Card.Self | AddAbility$ ABManaAbzan | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LE7_LEVEL | Description$ LEVEL 1-7 {T}: Add {W}, {B}, {G}, or {C}.

View File

@@ -1,7 +1,7 @@
Name:United Front
ManaCost:X W W
Types:Sorcery
A:SP$ Token | TokenAmount$ X | TokenScript$ w_1_1_ally | TokenOwner$ You | SubAbility$ DBPutCounterAll | SpellDescription$ Create X 1/1 white Ally creature tokens, then put a +1/+1 counter on each creature you control.
A:SP$ Token | TokenAmount$ X | TokenScript$ w_1_1_soldier | TokenOwner$ You | SubAbility$ DBPutCounterAll | SpellDescription$ Create X 1/1 white Ally creature tokens, then put a +1/+1 counter on each creature you control.
SVar:DBPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ None
SVar:X:Count$xPaid
Oracle:Create X 1/1 white Ally creature tokens, then put a +1/+1 counter on each creature you control.

View File

@@ -3,7 +3,7 @@ ManaCost:3 R
Types:Sorcery
A:SP$ Charm | MinCharmNum$ 1 | CharmNum$ Count$Compare Y GE1.2.1 | Choices$ DBWheel,DBFlames | AdditionalDescription$ . If you control a commander as you cast this spell, you may choose both instead.
SVar:DBWheel:DB$ Discard | Mode$ Hand | Defined$ Player | Optional$ True | RememberDiscardingPlayers$ True | SubAbility$ DBDraw | SpellDescription$ If embark gets more votes or the vote is tied, each player may discard their hand and draw seven cards.
SVar:DBDraw:DB$ Draw | Defined$ Remembered | NumCards$ 5 | SubAbility$ DBCleanup
SVar:DBDraw:DB$ Draw | Defined$ Remembered | NumCards$ 7 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:DBFlames:DB$ PumpAll | ValidCards$ Instant.YouCtrl,Sorcery.YouCtrl | KW$ Flashback | PumpZone$ Graveyard | SpellDescription$ Each instant and sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost.
SVar:Y:Count$Valid Card.IsCommander+YouCtrl

View File

@@ -4,5 +4,6 @@ Types:Creature Elf Druid
PT:2/2
A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ X | SpellDescription$ Add X mana of any one color, where X is the number of Elves on the battlefield.
SVar:X:Count$Valid Elf
DeckHints:Type$Elf
AI:RemoveDeck:All
AI:RemoveDeck:Random
Oracle:{T}: Add X mana of any one color, where X is the number of Elves on the battlefield.

View File

@@ -4,5 +4,5 @@ Types:Legendary Creature Spirit Ally
PT:3/3
K:Flying
K:Vigilance
A:AB$ Play | Cost$ Waterbend<5> T | Valid$ Card | ValidZone$ Hand | ValidSA$ Spell.nonCreature+YouOwn | WithoutManaCost$ True | Amount$ 1 | Controller$ You | Optional$ True | SpellDescription$ You may cast a noncreature spell from your hand without paying its mana cost.
A:AB$ Play | Cost$ Waterbend<5> T | ValidZone$ Hand | ValidSA$ Spell.nonCreature+YouOwn | WithoutManaCost$ True | Amount$ 1 | Controller$ You | Optional$ True | SpellDescription$ You may cast a noncreature spell from your hand without paying its mana cost.
Oracle:Flying, vigilance\nWaterbend {5}, {T}: You may cast a noncreature spell from your hand without paying its mana cost.

View File

@@ -3,6 +3,7 @@ ManaCost:B
Types:Creature Human Warrior
PT:1/1
K:Level up:4
SVar:maxLevel:3
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 3 | SetToughness$ 3 | IsPresent$ Card.Self+counters_GE1_LEVEL+counters_LT3_LEVEL | Description$ LEVEL 1-2 3/3
S:Mode$ Continuous | Affected$ Card.Self | SetPower$ 5 | SetToughness$ 5 | IsPresent$ Card.Self+counters_GE3_LEVEL | AddStaticAbility$ SCantBlock | Description$ LEVEL 3+ 5/5 CARDNAME can't be blocked except by black creatures.
SVar:SCantBlock:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.nonBlack | Secondary$ True | Description$ CARDNAME can't be blocked except by black creatures.

View File

@@ -6,6 +6,8 @@ Type=Expansion
ScryfallCode=TLE
[cards]
[borderless]
1 M Brought Back @Viacom
2 M Drannith Magistrate @Viacom ${"flavorName": "Mayor Tong of Chin Village"}
3 M Empty City Ruse @Viacom
@@ -68,13 +70,15 @@ ScryfallCode=TLE
60 M Treetop Village @Viacom
61 M Valakut, the Molten Pinnacle @Viacom ${"flavorName": "Volcano of Roku's Island"}
[borderless]
[black sun invasion]
62 R Appa, the Vigilant @Fahmi Fauzi
63 R Katara's Reversal @Fahmi Fauzi
64 R Fire Nation Turret @Fahmi Fauzi
65 R Swampbenders @Fahmi Fauzi
66 R Sokka's Charge @Fahmi Fauzi
67 R Earthshape @Fahmi Fauzi
[tea time]
68 R Mai and Zuko @Brian Yuen
69 R Aang and Katara @Brian Yuen
70 R Toph, Greatest Earthbender @Brian Yuen
@@ -82,7 +86,7 @@ ScryfallCode=TLE
72 R Momo's Heist @Brian Yuen
73 R Uncle's Musings @Brian Yuen
[jumpstart]
[extended art]
74 M Aang, Airbending Master @Tomoyo Asatani
75 U Air Nomad Student @Tky
76 U The Duke, Rebel Sentry @Logan Feliciano
@@ -155,6 +159,8 @@ ScryfallCode=TLE
143 R Tale of Katara and Toph @Ichiko Milk Tei
144 R Tectonic Split @Mengxuan Li
145 M Toph, Earthbending Master @Phima
[jumpstart]
146 U Aang, A Lot to Learn @Tomoyo Asatani
147 U Hook Swords @Eliz Roxs
148 U Katara, Seeking Revenge @Yoshioka
@@ -180,6 +186,47 @@ ScryfallCode=TLE
168 U Inspiring Call @Alexander Forssberg
169 C Many Partings @Kazeto
170 U Suspicious Bookcase @Kotakan
171 M Aang, Airbending Master @Tomoyo Asatani
172 R Jet, Rebel Leader @Keiuu
173 R Monk Gyatso @Masateru Ikeda
174 M Sokka, Swordmaster @Jason Kiantoro
175 R Suki, Kyoshi Captain @Shiren
176 R Tale of Momo @Tetsuko
177 R Baboon Spirit @Daniel Romanovsky
178 R The Blue Spirit @Claudiu-Antoniu Magherusan
179 R Chakra Meditation @Hori Airi
180 M Katara, Waterbending Master @Yueko
181 R Tui and La, Moon and Ocean @Douzen
182 M Wan Shi Tong, All-Knowing @Axel Sauerwald
183 R Waterbender's Restoration @Phima
184 M Azula, Ruthless Firebender @Rose Benjamin
185 R Desperate Plea @Shiyu
186 M Fire Lord Ozai @Ryuichi Sakuma
187 R Fire Nation Occupation @Arthur Yuan
188 R Fire Nation Salvagers @Susumu Kuroi
189 R Lo and Li, Royal Advisors @Kieran Yanner
190 R Nyla, Shirshu Sleuth @Douzen
191 M Avatar Roku, Firebender @Toni Infante
192 R Chong and Lily, Nomads @Phima
193 R Fang, Roku's Companion @Douzen
194 R Iroh, Dragon of the West @Hokyoung Kim
195 R Lost in Memories @Tubaki Halsame
196 R Overwhelming Victory @Joshua Raphael
197 R Reckless Blaze @Nijimaarc
198 R Smellerbee, Rebel Fighter @Enishi
199 R Storm of Memories @Nijimaarc
200 M Zuko, Firebending Master @Kinota Eishi
201 M Avatar Kyoshi, Earthbender @Tetsuko
202 R Bison Whistle @Brandon L. Hunt
203 R The Cabbage Merchant @Patrick Gañas
204 R Crystalline Armor @Susumu Kuroi
205 M Hei Bai, Forest Guardian @TAPIOCA
206 R Master's Guidance @Yueko
207 R Tale of Katara and Toph @Ichiko Milk Tei
208 R Tectonic Split @Mengxuan Li
209 M Toph, Earthbending Master @Phima
[tutorial]
210 R Aang, Air Nomad @Jinho Bae
211 C Aang's Defense @Jo Cordisco
212 C Aardvark Sloth @Ionomycin
@@ -235,49 +282,6 @@ ScryfallCode=TLE
262 C Thriving Heath @Dom Lay
263 C Thriving Isle @Slawek Fedorczuk
264 C Thriving Moor @Dom Lay
[extended art]
171 M Aang, Airbending Master @Tomoyo Asatani
172 R Jet, Rebel Leader @Keiuu
173 R Monk Gyatso @Masateru Ikeda
174 M Sokka, Swordmaster @Jason Kiantoro
175 R Suki, Kyoshi Captain @Shiren
176 R Tale of Momo @Tetsuko
177 R Baboon Spirit @Daniel Romanovsky
178 R The Blue Spirit @Claudiu-Antoniu Magherusan
179 R Chakra Meditation @Hori Airi
180 M Katara, Waterbending Master @Yueko
181 R Tui and La, Moon and Ocean @Douzen
182 M Wan Shi Tong, All-Knowing @Axel Sauerwald
183 R Waterbender's Restoration @Phima
184 M Azula, Ruthless Firebender @Rose Benjamin
185 R Desperate Plea @Shiyu
186 M Fire Lord Ozai @Ryuichi Sakuma
187 R Fire Nation Occupation @Arthur Yuan
188 R Fire Nation Salvagers @Susumu Kuroi
189 R Lo and Li, Royal Advisors @Kieran Yanner
190 R Nyla, Shirshu Sleuth @Douzen
191 M Avatar Roku, Firebender @Toni Infante
192 R Chong and Lily, Nomads @Phima
193 R Fang, Roku's Companion @Douzen
194 R Iroh, Dragon of the West @Hokyoung Kim
195 R Lost in Memories @Tubaki Halsame
196 R Overwhelming Victory @Joshua Raphael
197 R Reckless Blaze @Nijimaarc
198 R Smellerbee, Rebel Fighter @Enishi
199 R Storm of Memories @Nijimaarc
200 M Zuko, Firebending Master @Kinota Eishi
201 M Avatar Kyoshi, Earthbender @Tetsuko
202 R Bison Whistle @Brandon L. Hunt
203 R The Cabbage Merchant @Patrick Gañas
204 R Crystalline Armor @Susumu Kuroi
205 M Hei Bai, Forest Guardian @TAPIOCA
206 R Master's Guidance @Yueko
207 R Tale of Katara and Toph @Ichiko Milk Tei
208 R Tectonic Split @Mengxuan Li
209 M Toph, Earthbending Master @Phima
[promo]
265 R Aang, Air Nomad @Jinho Bae
266 C Aang's Defense @Jo Cordisco
267 C Aardvark Sloth @Ionomycin
@@ -319,7 +323,7 @@ ScryfallCode=TLE
303 L Plains @Slawek Fedorczuk
304 L Plains @Slawek Fedorczuk
[bundle]
[commander bundle]
305 R Enlightened Tutor @Brigitte Roka & Clifton Stommel
306 R Flawless Maneuver @Irina Nordsol
307 R Fierce Guardianship @Ina Wong

View File

@@ -15,7 +15,7 @@ Base=Common:fromSheet("TLA cards")
[Common-Borderless]
Base=Common:fromSheet("TLA cards")
Replace=.0384F fromSheet("TLE cards")
Replace=.0384F fromSheet("TLE borderless")
[Uncommon]
Base=Uncommon:fromSheet("TLA cards")
@@ -32,7 +32,7 @@ Replace=.001F Rare:fromSheet("TLA battle pose")
Replace=.0005F Mythic:fromSheet("TLA battle pose")
Replace=.001F Rare:fromSheet("TLA elemental frame")
Replace=.0005F Mythic:fromSheet("TLA elemental frame")
Replace=.0005F Mythic:fromSheet("TLA borderless dfc saga")
Replace=.0005F Mythic:fromSheet("TLA borderless saga")
[Wildcard]
Base=Uncommon:fromSheet("TLA cards")
@@ -69,12 +69,12 @@ Replace=.0005F Mythic:fromSheet("TLA borderless dfc saga")
[Land]
Base=fromSheet("TLA allied lands")
Replace=0.20F BasicLand:fromSheet("TLA default basic")
Replace=0.10F BasicLand:fromSheet("TLA avatar journey basic")
Replace=0.10F BasicLand:fromSheet("TLA appa basic")
Replace=0.10F BasicLand:fromSheet("TLA Avatar journey basic")
Replace=0.10F BasicLand:fromSheet("TLA Appa basic")
Replace=0.10F fromSheet("TLA allied lands")+
Replace=0.05F BasicLand:fromSheet("TLA default basic")+
Replace=0.025F BasicLand:fromSheet("TLA avatar journey basic")+
Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
Replace=0.025F BasicLand:fromSheet("TLA Avatar journey basic")+
Replace=0.025F BasicLand:fromSheet("TLA Appa basic")+
[cards]
1 C Aang's Journey @Kotakan
@@ -376,7 +376,7 @@ Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
295 L Mountain @Maojin Lee
296 L Forest @Slawek Fedorczuk
[borderless]
[scene cards]
297 M Fated Firepower @Claudiu-Antoniu Magherusan
298 R Aang, Swift Savior @Claudiu-Antoniu Magherusan
299 U Fire Nation Attacks @Claudiu-Antoniu Magherusan
@@ -396,6 +396,8 @@ Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
313 R Fire Lord Azula @Dominik Mayer
314 R The Last Agni Kai @Dominik Mayer
315 R Fire Lord Zuko @Dominik Mayer
[borderless]
316 M Appa, Steadfast Guardian @Ilse Gort
317 R Momo, Friendly Flier @Filip Burburan
318 R Tiger-Seal @Andrea Piparo
@@ -416,18 +418,6 @@ Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
333 R Avatar Destiny @Flavio Girón
334 R Fire Lord Azula @JungShan
335 M Ozai, the Phoenix King @Sidharth Chaturvedi
354 M The Legend of Yangchen @Sija Hong
355 M The Legend of Kuruk @Barbara Rosiak
356 M The Rise of Sozin @Barbara Rosiak
357 M The Legend of Roku @Barbara Rosiak
358 M The Legend of Kyoshi @Sija Hong
359 R Aang, Swift Savior @Flavio Girón
360 R Fire Lord Zuko @Flavio Girón
361 R Katara, the Fearless @Flavio Girón
362 R Toph, the First Metalbender @Flavio Girón
363 M Avatar Aang @Bryan Konietzko
[showcase]
336 R Aang's Iceberg @Brigitte Roka & Clifton Stommel
337 M Secret of Bloodbending @Barbara Rosiak
338 R Yue, the Moon Spirit @Barbara Rosiak
@@ -446,8 +436,16 @@ Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
351 R Katara, Water Tribe's Hope @Chun Lo
352 R Sokka, Tenacious Tactician @Faustine Dumontier
353 R Toph, the First Metalbender @Barbara Rosiak
[extended art]
354 M The Legend of Yangchen @Sija Hong
355 M The Legend of Kuruk @Barbara Rosiak
356 M The Rise of Sozin @Barbara Rosiak
357 M The Legend of Roku @Barbara Rosiak
358 M The Legend of Kyoshi @Sija Hong
359 R Aang, Swift Savior @Flavio Girón
360 R Fire Lord Zuko @Flavio Girón
361 R Katara, the Fearless @Flavio Girón
362 R Toph, the First Metalbender @Flavio Girón
363 M Avatar Aang @Bryan Konietzko
364 R Airbender Ascension @Shiren
365 R Avatar's Wrath @Ainezu
366 R Hakoda, Selfless Commander @Rafater
@@ -477,32 +475,9 @@ Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
390 R Jasmine Dragon Tea Shop @Leanna Crossan
391 R Realm of Koh @Andreas Rocha
392 R Secret Tunnel @Alexander Forssberg
[promo]
393 R Firebending Student @Airi Yoshihisa
394 R Momo, Friendly Flier @Ryota Murayama
[scene cards]
1 Fated Firepower|TLA|[297]
1 Aang, Swift Savior|TLA|[298]
1 Fire Nation Attacks|TLA|[299]
1 Crashing Wave|TLA|[300]
1 Combustion Technique|TLA|[301]
1 Zuko, Conflicted|TLA|[302]
1 Azula, Cunning Usurper|TLA|[303]
1 Aang, at the Crossroads|TLA|[304]
1 Katara, the Fearless|TLA|[305]
1 Dai Li Agents|TLA|[306]
1 Earthbender Ascension|TLA|[307]
1 Avatar Aang|TLA|[308]
1 Sozin's Comet|TLA|[309]
1 Waterbender Ascension|TLA|[310]
1 Ozai, the Phoenix King|TLA|[311]
1 Firebender Ascension|TLA|[312]
1 Fire Lord Azula|TLA|[313]
1 The Last Agni Kai|TLA|[314]
1 Fire Lord Zuko|TLA|[315]
[field notes]
1 Appa, Steadfast Guardian|TLA|[316]
1 Momo, Friendly Flier|TLA|[317]
@@ -575,14 +550,14 @@ Replace=0.025F BasicLand:fromSheet("TLA appa basic")+
1 Mountain|TLA|[285]
1 Forest|TLA|[286]
[appa basic]
[Appa basic]
1 Plains|TLA|[287]
1 Island|TLA|[288]
1 Swamp|TLA|[289]
1 Mountain|TLA|[290]
1 Forest|TLA|[291]
[avatar journey basic]
[Avatar journey basic]
1 Plains|TLA|[292]
1 Island|TLA|[293]
1 Swamp|TLA|[294]

View File

@@ -1,16 +0,0 @@
[metadata]
Name:Possibility Storm - Marvel's Spider-Man #02
URL:https://i2.wp.com/www.possibilitystorm.com/wp-content/uploads/2025/09/latest-4-scaled.jpg?ssl=1
Goal:Win
Turns:1
Difficulty:Mythic
Description:Win this turn. Ensure your solution satisfies all possible opponent decisions and activations! Good luck!
[state]
turn=1
activeplayer=p0
activephase=MAIN1
p0life=20
p0hand=Hobgoblin, Mantled Marauder;Chameleon, Master of Disguise;Rocket-Powered Goblin Glider;Romantic Rendezvous;Swarm, Being of Bees;Pumpkin Bombardment
p0battlefield=Norman Osborn|Modal;Prowler, Clawed Thief;Island;Island;Swamp;Swamp;Mountain;Mountain
p1life=14
p1battlefield=Agatha's Soul Cauldron;Spider-Gwen, Free Spirit;Spider-Man, Brooklyn Visionary

View File

@@ -15,7 +15,6 @@ p0battlefield=Unyielding Gatekeeper|FaceDown;Mysterio's Phantasm|Id:1;Mysterio,
# FIXME: This is needed to set up the third Illusion token.
p0precast=Lightning Bolt->1
p1life=21
p1graveyard=
p1battlefield=Electro, Assaulting Battery|Id:2;Impostor Syndrome;Mountain;Mountain;Mountain;Mountain;Mountain
p1precast=Impostor Syndrome:TrigCopy->2;Impostor Syndrome:TrigCopy->2
p1graveyard=Electro, Assaulting Battery;Electro, Assaulting Battery
p1battlefield=Electro, Assaulting Battery;Impostor Syndrome;Mountain;Mountain;Mountain;Mountain;Mountain
removesummoningsickness=true

View File

@@ -1,21 +0,0 @@
[metadata]
Name:Possibility Storm - Marvel's Spider-Man #04
URL:https://i0.wp.com/www.possibilitystorm.com/wp-content/uploads/2025/11/latest-scaled.jpg?ssl=1
Goal:Win
Turns:1
Difficulty:Mythic
Description:Win this turn. Start in your Upkeep with Smile at Death's trigger on the stack. Your Evendos each start with no charge counters, and your creatures with no +1/+1 counters. Ensure your solution satisfies all possible blocks. Good luck!
[state]
turn=1
activeplayer=p1
activephase=CLEANUP
activephaseadvance=UPKEEP
p0life=20
p0hand=Goldvein Hydra;Flash Thompson, Spider-Fan;Virulent Silencer;Peter Parker's Camera;Relic's Roar
p0library=
p0graveyard=Cheering Crowd;Jackal, Genius Geneticist;Delney, Streetwise Lookout|Set:PWCS|Art:1
p0battlefield=Zimone, Paradox Sculptor;The Millennium Calendar|Counters:TIME=5;Loading Zone;Smile at Death;Urban Retreat;Urban Retreat;Urban Retreat;Urban Retreat;Evendo, Waking Haven;Evendo, Waking Haven
p1life=1000
p1battlefield=Summon: Knights of Round|Counters:LORE=4
p1precast=Summon^ Knights of Round:TrigToken;Summon^ Knights of Round:TrigToken;Summon^ Knights of Round:TrigToken;Summon^ Knights of Round:TrigToken
removesummoningsickness=true

View File

@@ -1,5 +1,5 @@
[metadata]
Name:Possibility Storm - Avatar: The Last Airbender #01
Name:Possibility Storm - Avatar the Last Airbender #01
URL:https://i2.wp.com/www.possibilitystorm.com/wp-content/uploads/2025/08/latest-3-scaled.jpg?ssl=1
Goal:Win
Turns:1

View File

@@ -1,18 +0,0 @@
[metadata]
Name:Possibility Storm - Avatar: The Last Airbender #02
URL:https://i1.wp.com/www.possibilitystorm.com/wp-content/uploads/2025/11/latest-1-scaled.jpg?ssl=1
Goal:Win
Turns:1
Difficulty:Mythic
Description:Win this turn. Your opponent cast Secret of Bloodbending (without additional cost) on their last turn, so they control your combat step. Ensure your solution satisfies all actions your opponent could take during your combat (including their blocks.)
[state]
turn=1
activeplayer=p0
activephase=MAIN1
p0life=20
p0hand=Hustle // Bustle;Deadly Precision;Lightning Strike;Carnage, Crimson Chaos|Set:PSPM|Art:1;Jet's Brainwashing
p0graveyard=Ball Lightning;Abandon Attachments;Flamewake Phoenix
p0battlefield=Juggernaut;Geyser Leaper;Fire Nation Cadets;Bespoke Battlegarb;Red Herring;Ominous Asylum;Ominous Asylum;Ominous Asylum;Peculiar Lighthouse;Peculiar Lighthouse
p1life=13
p1battlefield=Archangel of Tithes;Fog Bank
p1precast=Secret of Bloodbending->Human

View File

@@ -560,7 +560,7 @@ public class HumanPlay {
}
CardCollection cardsToDelve = new CardCollection();
CostAdjustment.adjust(toPay, ability, activator, cardsToDelve, false, effect);
CostAdjustment.adjust(toPay, ability, cardsToDelve, false, effect);
Card offering = null;
Card emerge = null;

View File

@@ -2942,11 +2942,13 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
}
final Player p = pOld;
final CardDb carddb = FModel.getMagicDb().getCommonCards();
final List<ICardFace> faces = Lists.newArrayList(carddb.getAllFaces());
List<CardFaceView> choices = new ArrayList<>();
CardFaceView cardFaceView;
for (ICardFace cardFace : carddb.getAllFaces()) {
for (ICardFace cardFace : faces) {
cardFaceView = new CardFaceView(CardTranslation.getTranslatedName(cardFace.getDisplayName()), cardFace.getName());
choices.add(cardFaceView);
}