mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
CastTotalManaSpent Treasure
This commit is contained in:
@@ -1,26 +1,8 @@
|
||||
package forge.ai;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import forge.ai.ability.AnimateAi;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
@@ -34,20 +16,10 @@ import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostAdjustment;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.cost.CostPayEnergy;
|
||||
import forge.game.cost.CostPayment;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
@@ -67,6 +39,10 @@ import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ComputerUtilMana {
|
||||
private final static boolean DEBUG_MANA_PAYMENT = false;
|
||||
@@ -272,28 +248,87 @@ public class ComputerUtilMana {
|
||||
|
||||
public static SpellAbility chooseManaAbility(ManaCostBeingPaid cost, SpellAbility sa, Player ai, ManaCostShard toPay,
|
||||
Collection<SpellAbility> saList, boolean checkCosts) {
|
||||
Card saHost = sa.getHostCard();
|
||||
|
||||
// CastTotalManaSpent (AIPreference:ManaFrom$Type or AIManaPref$ Type)
|
||||
String manaSourceType = "";
|
||||
if (saHost.hasSVar("AIPreference")) {
|
||||
String condition = saHost.getSVar("AIPreference");
|
||||
if (condition.startsWith("ManaFrom")) {
|
||||
manaSourceType = TextUtil.split(condition, '$')[1];
|
||||
}
|
||||
} else if (sa.hasParam("AIManaPref")) {
|
||||
manaSourceType = sa.getParam("AIManaPref");
|
||||
}
|
||||
if (manaSourceType != "") {
|
||||
List<SpellAbility> filteredList = Lists.newArrayList(saList);
|
||||
switch (manaSourceType) {
|
||||
case "Snow":
|
||||
filteredList.sort(new Comparator<SpellAbility>() {
|
||||
@Override
|
||||
public int compare(SpellAbility ab1, SpellAbility ab2) {
|
||||
return ab1.getHostCard() != null && ab1.getHostCard().isSnow()
|
||||
&& ab2.getHostCard() != null && !ab2.getHostCard().isSnow() ? -1 : 1;
|
||||
}
|
||||
});
|
||||
saList = filteredList;
|
||||
break;
|
||||
case "Treasure":
|
||||
// Try to spend only one Treasure if possible
|
||||
filteredList.sort(new Comparator<SpellAbility>() {
|
||||
@Override
|
||||
public int compare(SpellAbility ab1, SpellAbility ab2) {
|
||||
return ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
|
||||
&& ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1;
|
||||
}
|
||||
});
|
||||
SpellAbility first = filteredList.get(0);
|
||||
if (first.getHostCard() != null && first.getHostCard().getType().hasSubtype("Treasure")) {
|
||||
saList.remove(first);
|
||||
List<SpellAbility> updatedList = Lists.newArrayList();
|
||||
updatedList.add(first);
|
||||
updatedList.addAll(saList);
|
||||
saList = updatedList;
|
||||
}
|
||||
break;
|
||||
case "TreasureMax":
|
||||
// Ok to spend as many Treasures as possible
|
||||
filteredList.sort(new Comparator<SpellAbility>() {
|
||||
@Override
|
||||
public int compare(SpellAbility ab1, SpellAbility ab2) {
|
||||
return ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
|
||||
&& ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1;
|
||||
}
|
||||
});
|
||||
saList = filteredList;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (final SpellAbility ma : saList) {
|
||||
if (ma.getHostCard() == sa.getHostCard()) {
|
||||
if (ma.getHostCard() == saHost) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.getHostCard() != null) {
|
||||
if (saHost != null) {
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
// For abilities like Genju of the Cedars, make sure that we're not activating the aura ability by tapping the enchanted card for mana
|
||||
if (sa.getHostCard().isAura() && "Enchanted".equals(sa.getParam("Defined"))
|
||||
&& ma.getHostCard() == sa.getHostCard().getEnchantingCard()
|
||||
if (saHost.isAura() && "Enchanted".equals(sa.getParam("Defined"))
|
||||
&& ma.getHostCard() == saHost.getEnchantingCard()
|
||||
&& ma.getPayCosts().hasTapCost()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a manland was previously animated this turn, do not tap it to animate another manland
|
||||
if (sa.getHostCard().isLand() && ma.getHostCard().isLand()
|
||||
if (saHost.isLand() && ma.getHostCard().isLand()
|
||||
&& ai.getController().isAI()
|
||||
&& AnimateAi.isAnimatedThisTurn(ai, ma.getHostCard())) {
|
||||
continue;
|
||||
}
|
||||
} else if (sa.getApi() == ApiType.Pump) {
|
||||
if ((sa.getHostCard().isInstant() || sa.getHostCard().isSorcery())
|
||||
if ((saHost.isInstant() || saHost.isSorcery())
|
||||
&& ma.getHostCard().isCreature()
|
||||
&& ai.getController().isAI()
|
||||
&& ma.getPayCosts().hasTapCost()
|
||||
@@ -303,7 +338,7 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
} else if (sa.getApi() == ApiType.Attach
|
||||
&& "AvoidPayingWithAttachTarget".equals(sa.getHostCard().getSVar("AIPaymentPreference"))) {
|
||||
&& "AvoidPayingWithAttachTarget".equals(saHost.getSVar("AIPaymentPreference"))) {
|
||||
// For cards like Genju of the Cedars, make sure we're not attaching to the same land that will
|
||||
// be tapped to pay its own cost if there's another untapped land like that available
|
||||
if (ma.getHostCard().equals(sa.getTargetCard())) {
|
||||
@@ -320,7 +355,7 @@ public class ComputerUtilMana {
|
||||
// Exception: when paying generic mana with Cavern of Souls, prefer the colored mana producing ability
|
||||
// to attempt to make the spell uncounterable when possible.
|
||||
if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls")
|
||||
&& sa.getHostCard().getType().getCreatureTypes().contains(ma.getHostCard().getChosenType())) {
|
||||
&& saHost.getType().getCreatureTypes().contains(ma.getHostCard().getChosenType())) {
|
||||
if (toPay == ManaCostShard.COLORLESS && cost.getUnpaidShards().contains(ManaCostShard.GENERIC)) {
|
||||
// Deprioritize Cavern of Souls, try to pay generic mana with it instead to use the NoCounter ability
|
||||
continue;
|
||||
|
||||
@@ -1832,25 +1832,26 @@ public class AbilityUtils {
|
||||
return doXMath(list.size(), expr, c, ctb);
|
||||
}
|
||||
|
||||
if (sq[0].startsWith("CastTotalManaSpent")) {
|
||||
return doXMath(c.getCastSA() != null ? c.getCastSA().getTotalManaSpent() : 0, expr, c, ctb);
|
||||
if (sq[0].equals("ResolvedThisTurn")) {
|
||||
return doXMath(sa.getResolvedThisTurn(), expr, c, ctb);
|
||||
}
|
||||
|
||||
if (sq[0].equals("CastTotalSnowManaSpent")) {
|
||||
if (sq[0].startsWith("TotalManaSpent ")) {
|
||||
final String[] k = sq[0].split(" ");
|
||||
int v = 0;
|
||||
if (c.getCastSA() != null) {
|
||||
for (Mana m : c.getCastSA().getPayingMana()) {
|
||||
if (m.isSnow()) {
|
||||
v += 1;
|
||||
if (sa.getRootAbility().getPayingMana() != null) {
|
||||
for (Mana m : sa.getRootAbility().getPayingMana()) {
|
||||
Card source = m.getSourceCard();
|
||||
if (source != null) {
|
||||
if (source.isValid(k[1].split(","), player, c, sa)) {
|
||||
v += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return doXMath(v, expr, c, ctb);
|
||||
}
|
||||
|
||||
if (sq[0].equals("ResolvedThisTurn")) {
|
||||
return doXMath(sa.getResolvedThisTurn(), expr, c, ctb);
|
||||
}
|
||||
} else {
|
||||
// fallback if ctb isn't a spellability
|
||||
if (sq[0].startsWith("LastStateBattlefield")) {
|
||||
@@ -1870,8 +1871,29 @@ public class AbilityUtils {
|
||||
if (sq[0].startsWith("xPaid")) {
|
||||
return doXMath(c.getXManaCostPaid(), expr, c, ctb);
|
||||
}
|
||||
|
||||
} // end SpellAbility
|
||||
|
||||
if (sq[0].equals("CastTotalManaSpent")) {
|
||||
return doXMath(c.getCastSA() != null ? c.getCastSA().getTotalManaSpent() : 0, expr, c, ctb);
|
||||
}
|
||||
|
||||
if (sq[0].startsWith("CastTotalManaSpent ")) {
|
||||
final String[] k = sq[0].split(" ");
|
||||
int v = 0;
|
||||
if (c.getCastSA() != null) {
|
||||
for (Mana m : c.getCastSA().getPayingMana()) {
|
||||
Card source = m.getSourceCard();
|
||||
if (source != null) {
|
||||
if (source.isValid(k[1].split(","), player, c, ctb)) {
|
||||
v += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return doXMath(v, expr, c, ctb);
|
||||
}
|
||||
|
||||
// Count$TargetedLifeTotal (targeted player's life total)
|
||||
// Not optimal but since xCount doesn't take SAs, we need to replicate while we have it
|
||||
// Probably would be best if xCount took an optional SA to use in these circumstances
|
||||
|
||||
@@ -5,6 +5,7 @@ PT:4/4
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters the battlefield, tap target artifact or creature an opponent controls. If {S} was spent to cast this spell, that permanent doesn't untap during its controller's next untap step. ({S} is mana from a snow source.)
|
||||
SVar:TrigTap:DB$ Tap | ValidTgts$ Artifact.OppCtrl,Creature.OppCtrl | TgtPrompt$ Choose target artifact or creature an opponent controls | SubAbility$ DBPump
|
||||
SVar:DBPump:DB$ Pump | Defined$ Targeted | ConditionCheckSVar$ S | ConditionSVarCompare$ GE1 | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent
|
||||
SVar:S:Count$CastTotalSnowManaSpent
|
||||
SVar:S:Count$CastTotalManaSpent Snow
|
||||
SVar:AIPreference:ManaFrom$Snow
|
||||
DeckNeeds:Type$Snow
|
||||
Oracle:When Berg Strider enters the battlefield, tap target artifact or creature an opponent controls. If {S} was spent to cast this spell, that permanent doesn't untap during its controller's next untap step. ({S} is mana from a snow source.)
|
||||
|
||||
@@ -2,9 +2,10 @@ Name:Blessing of Frost
|
||||
ManaCost:3 G
|
||||
Types:Snow Sorcery
|
||||
A:SP$ PutCounter | Cost$ 3 G | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose any number of creatures you control to distribute counters to | CounterType$ P1P1 | CounterNum$ X | ChoiceAmount$ X | MinChoiceAmount$ 1 | DividedAsYouChoose$ X | SubAbility$ DBDraw | SpellDescription$ Distribute X +1/+1 counters among any number of creatures you control, where X is the amount of {S} spent to cast this spell.
|
||||
SVar:X:Count$CastTotalSnowManaSpent
|
||||
SVar:X:Count$CastTotalManaSpent Snow
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ Y | SpellDescription$ Then draw a card for each creature you control with power 4 or greater.
|
||||
SVar:Y:Count$Valid Creature.powerGE4+YouCtrl
|
||||
SVar:AIPreference:ManaFrom$Snow
|
||||
DeckHints:Type$Snow
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Distribute X +1/+1 counters among any number of creatures you control, where X is the amount of {S} spent to cast this spell. Then draw a card for each creature you control with power 4 or greater.
|
||||
|
||||
@@ -5,7 +5,8 @@ A:SP$ Charm | Cost$ 4 B B | Choices$ DestroyCtrs,DestroyPWs | CharmNum$ 1 | Spel
|
||||
SVar:DestroyCtrs:DB$ DestroyAll | ValidCards$ Creature | SubAbility$ DBReturn | SpellDescription$ Destroy all creatures.
|
||||
SVar:DestroyPWs:DB$ DestroyAll | ValidCards$ Planeswalker | SubAbility$ DBReturn | SpellDescription$ Destroy all planeswalkers.
|
||||
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | Mandatory$ True | ChangeType$ Creature.YouOwn+cmcLEX,Planeswalker.YouOwn+cmcLEX | SpellDescription$ Then return a creature or planeswalker card with mana value X or less from your graveyard to the battlefield, where X is the amount of {S} spent to cast this spell. ({S} is mana from a snow source.)
|
||||
SVar:X:Count$CastTotalSnowManaSpent
|
||||
SVar:X:Count$CastTotalManaSpent Snow
|
||||
SVar:AIPreference:ManaFrom$Snow
|
||||
AI:RemoveDeck:Random
|
||||
DeckHints:Type$Snow
|
||||
Oracle:Choose one —\n• Destroy all creatures.\n• Destroy all planeswalkers.\nThen return a creature or planeswalker card with mana value X or less from your graveyard to the battlefield, where X is the amount of {S} spent to cast this spell. ({S} is mana from a snow source.)
|
||||
|
||||
@@ -3,6 +3,7 @@ ManaCost:3 U U
|
||||
Types:Snow Instant
|
||||
A:SP$ Scry | Cost$ 3 U U | ScryNum$ X | SubAbility$ DBDraw | SpellDescription$ Scry X, where X is the amount of {S} spent to cast this spell, then draw three cards. ({S} is mana from a snow source.)
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ 3
|
||||
SVar:X:Count$CastTotalSnowManaSpent
|
||||
SVar:X:Count$CastTotalManaSpent Snow
|
||||
SVar:AIPreference:ManaFrom$Snow
|
||||
DeckNeeds:Type$Snow
|
||||
Oracle:Scry X, where X is the amount of {S} spent to cast this spell, then draw three cards. ({S} is mana from a snow source.)
|
||||
|
||||
@@ -3,7 +3,8 @@ ManaCost:2 W
|
||||
Types:Snow Sorcery
|
||||
A:SP$ ChangeZone | Cost$ 2 W | Origin$ Library | Destination$ Hand | ChangeType$ Permanent.Snow,Legendary,Saga | ChangeNum$ 1 | SubAbility$ DBGainLife | SpellDescription$ Search your library for a snow permanent card, a legendary card, or a Saga card, reveal it, put it into your hand, then shuffle. You gain 1 life for each {S} spent to cast this spell.
|
||||
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X
|
||||
SVar:X:Count$CastTotalSnowManaSpent
|
||||
SVar:X:Count$CastTotalManaSpent Snow
|
||||
SVar:AIPreference:ManaFrom$Snow
|
||||
AI:RemoveDeck:Random
|
||||
DeckHints:Type$Legendary
|
||||
Oracle:Search your library for a snow permanent card, a legendary card, or a Saga card, reveal it, put it into your hand, then shuffle. You gain 1 life for each {S} spent to cast this spell. ({S} is mana from a snow source.)
|
||||
|
||||
@@ -3,6 +3,7 @@ ManaCost:1 R R
|
||||
Types:Snow Sorcery
|
||||
A:SP$ DealDamage | Cost$ 1 R R | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker. | NumDmg$ 4 | SubAbility$ DBColorlessMana | SpellDescription$ CARDNAME deals 4 damage to target creature or planeswalker. Add {C} for each {S} spent to cast this spell. Until end of turn, you don't lose this mana as steps and phases end. | StackDescription$ SpellDescription
|
||||
SVar:DBColorlessMana:DB$ Mana | Produced$ C | Amount$ X | PersistentMana$ True
|
||||
SVar:X:Count$CastTotalSnowManaSpent
|
||||
SVar:X:Count$CastTotalManaSpent Snow
|
||||
SVar:AIPreference:ManaFrom$Snow
|
||||
DeckHints:Type$Snow
|
||||
Oracle:Tundra Fumarole deals 4 damage to target creature or planeswalker. Add {C} for each {S} spent to cast this spell. Until end of turn, you don't lose this mana as steps and phases end. ({S} is mana from a snow source.)
|
||||
|
||||
10
forge-gui/res/cardsfolder/upcoming/forsworn_paladin.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/forsworn_paladin.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Forsworn Paladin
|
||||
ManaCost:B
|
||||
Types:Creature Human Knight
|
||||
PT:1/1
|
||||
K:Menace
|
||||
A:AB$ Token | Cost$ 1 B T PayLife<1> | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | TokenOwner$ You | SpellDescription$ Create a Treasure token.
|
||||
A:AB$ Pump | Cost$ 2 B | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | SubAbility$ DBDeathtouch | AIManaPref$ Treasure | SpellDescription$ Target creature gets +2/+0 until end of turn. If mana from a Treasure was spent to activate this ability, that creature also gains deathtouch until end of turn.
|
||||
SVar:DBDeathtouch:DB$ Pump | KW$ Deathtouch | Defined$ ParentTarget | ConditionCheckSVar$ TreasureCheck
|
||||
SVar:TreasureCheck:Count$TotalManaSpent Treasure
|
||||
Oracle:Menace\n{1}{B}, {T}, Pay 1 life: Create a Treasure token.\n{2}{B}: Target creature gets +2/+0 until end of turn. If mana from a Treasure was spent to activate this ability, that creature also gains deathtouch until end of turn.
|
||||
10
forge-gui/res/cardsfolder/upcoming/hired_hexblade.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/hired_hexblade.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Hired Hexblade
|
||||
ManaCost:1 B
|
||||
Types:Creature Elf Warlock
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ TreasureCheck | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, if mana from a treasure was spent to cast it, you draw a card and you lose 1 life.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife
|
||||
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1
|
||||
SVar:TreasureCheck:Count$CastTotalManaSpent Treasure
|
||||
SVar:AIPreference:ManaFrom$Treasure
|
||||
Oracle:When Hired Hexblade enters the battlefield, if mana from a treasure was spent to cast it, you draw a card and you lose 1 life.
|
||||
Reference in New Issue
Block a user