mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
Merge branch 'mdfc' into 'master'
Correctly evaluate modal faces See merge request core-developers/forge!5112
This commit is contained in:
@@ -750,7 +750,15 @@ public class AiController {
|
|||||||
return AiPlayDecision.CantAfford;
|
return AiPlayDecision.CantAfford;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state needs to be switched here so API checks evaluate the right face
|
||||||
|
if (sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||||
|
sa.getHostCard().setState(CardStateName.Modal, false);
|
||||||
|
}
|
||||||
AiPlayDecision canPlay = canPlaySa(sa); // this is the "heaviest" check, which also sets up targets, defines X, etc.
|
AiPlayDecision canPlay = canPlaySa(sa); // this is the "heaviest" check, which also sets up targets, defines X, etc.
|
||||||
|
if (sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||||
|
sa.getHostCard().setState(CardStateName.Original, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (canPlay != AiPlayDecision.WillPlay) {
|
if (canPlay != AiPlayDecision.WillPlay) {
|
||||||
return canPlay;
|
return canPlay;
|
||||||
}
|
}
|
||||||
@@ -810,8 +818,7 @@ public class AiController {
|
|||||||
if (!canPlay) {
|
if (!canPlay) {
|
||||||
return AiPlayDecision.CantPlayAi;
|
return AiPlayDecision.CantPlayAi;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Cost payCosts = sa.getPayCosts();
|
Cost payCosts = sa.getPayCosts();
|
||||||
if (payCosts != null) {
|
if (payCosts != null) {
|
||||||
ManaCost mana = payCosts.getTotalMana();
|
ManaCost mana = payCosts.getTotalMana();
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
return PaymentDecision.card(source);
|
return PaymentDecision.card(source);
|
||||||
}
|
}
|
||||||
if (cost.getType().equals("OriginalHost")) {
|
if (cost.getType().equals("OriginalHost")) {
|
||||||
return PaymentDecision.card(ability.getHostCard());
|
return PaymentDecision.card(ability.getOriginalHost());
|
||||||
}
|
}
|
||||||
if (cost.getAmount().equals("All")) {
|
if (cost.getAmount().equals("All")) {
|
||||||
// Does the AI want to use Sacrifice All?
|
// Does the AI want to use Sacrifice All?
|
||||||
@@ -600,7 +600,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
// currently if amount is bigger than one,
|
// currently if amount is bigger than one,
|
||||||
// it tries to remove all counters from one source and type at once
|
// it tries to remove all counters from one source and type at once
|
||||||
|
|
||||||
|
|
||||||
int toRemove = 0;
|
int toRemove = 0;
|
||||||
final GameEntityCounterTable table = new GameEntityCounterTable();
|
final GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
|
|
||||||
|
|||||||
@@ -2890,10 +2890,10 @@ public class ComputerUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
|
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost, boolean mandatory) {
|
||||||
// determine and target a card with a SA that the AI can afford and will play
|
// determine and target a card with a SA that the AI can afford and will play
|
||||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
Card targetSpellCard = null;
|
CardCollection targets = new CardCollection();
|
||||||
for (Card c : options) {
|
for (Card c : options) {
|
||||||
if (withoutPayingManaCost && c.getManaCost() != null && c.getManaCost().countX() > 0) {
|
if (withoutPayingManaCost && c.getManaCost() != null && c.getManaCost().countX() > 0) {
|
||||||
// The AI will otherwise cheat with the mana payment, announcing X > 0 for spells like Heat Ray when replaying them
|
// The AI will otherwise cheat with the mana payment, announcing X > 0 for spells like Heat Ray when replaying them
|
||||||
@@ -2913,18 +2913,19 @@ public class ComputerUtil {
|
|||||||
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
|
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
|
||||||
abTest.setActivatingPlayer(ai);
|
abTest.setActivatingPlayer(ai);
|
||||||
abTest.getRestrictions().setZone(c.getZone().getZoneType());
|
abTest.getRestrictions().setZone(c.getZone().getZoneType());
|
||||||
final boolean play = AiPlayDecision.WillPlay == aic.canPlaySa(abTest);
|
if (AiPlayDecision.WillPlay == aic.canPlaySa(abTest) && ComputerUtilCost.canPayCost(abTest, ai)) {
|
||||||
final boolean pay = ComputerUtilCost.canPayCost(abTest, ai);
|
targets.add(c);
|
||||||
if (play && pay) {
|
|
||||||
targetSpellCard = c;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetSpellCard == null) {
|
if (targets.isEmpty()) {
|
||||||
return false;
|
if (mandatory && !options.isEmpty()) {
|
||||||
|
targets = options;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sa.getTargets().add(targetSpellCard);
|
sa.getTargets().add(ComputerUtilCard.getBestAI(targets));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ public class ComputerUtilCost {
|
|||||||
}
|
}
|
||||||
// Check if the AI intends to play the card and if it can pay for it with the mana it has
|
// Check if the AI intends to play the card and if it can pay for it with the mana it has
|
||||||
boolean willPlay = ComputerUtil.hasReasonToPlayCardThisTurn(payer, c);
|
boolean willPlay = ComputerUtil.hasReasonToPlayCardThisTurn(payer, c);
|
||||||
boolean canPay = c.getManaCost().canBePaidWithAvaliable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
|
boolean canPay = c.getManaCost().canBePaidWithAvailable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
|
||||||
return canPay && willPlay;
|
return canPay && willPlay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1099,7 +1099,7 @@ public class SpecialCardAi {
|
|||||||
|
|
||||||
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
||||||
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
||||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvaliable(ColorSet.fromNames(
|
boolean canPayWithAvailableColors = cost.canBePaidWithAvailable(ColorSet.fromNames(
|
||||||
ComputerUtilCost.getAvailableManaColors(ai, sa.getHostCard())).getColor());
|
ComputerUtilCost.getAvailableManaColors(ai, sa.getHostCard())).getColor());
|
||||||
|
|
||||||
byte colorProfile = cost.getColorProfile();
|
byte colorProfile = cost.getColorProfile();
|
||||||
|
|||||||
@@ -233,10 +233,10 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
return ai.getCreaturesInPlay().size() >= i;
|
return ai.getCreaturesInPlay().size() >= i;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (logic.equals("CastFromGraveThisTurn")) {
|
} else if (logic.equals("ReplaySpell")) {
|
||||||
CardCollection list = new CardCollection(game.getCardsIn(ZoneType.Graveyard));
|
CardCollection list = new CardCollection(game.getCardsIn(ZoneType.Graveyard));
|
||||||
list = CardLists.getValidCards(list, sa.getTargetRestrictions().getValidTgts(), ai, sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, sa.getTargetRestrictions().getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (logic.equals("Bribe")) {
|
} else if (logic.equals("Bribe")) {
|
||||||
@@ -313,6 +313,5 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
|||||||
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(ai.getCardsIn(ZoneType.Hand), ai);
|
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(ai.getCardsIn(ZoneType.Hand), ai);
|
||||||
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
||||||
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
||||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvaliable(ColorSet.fromNames(
|
boolean canPayWithAvailableColors = cost.canBePaidWithAvailable(ColorSet.fromNames(
|
||||||
ComputerUtilCost.getAvailableManaColors(ai, (List<Card>)null)).getColor());
|
ComputerUtilCost.getAvailableManaColors(ai, (List<Card>)null)).getColor());
|
||||||
|
|
||||||
if (cost.getCMC() == 0 && cost.countX() == 0) {
|
if (cost.getCMC() == 0 && cost.countX() == 0) {
|
||||||
|
|||||||
@@ -40,33 +40,9 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
return false; // prevent infinite loop
|
return false; // prevent infinite loop
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection cards = null;
|
CardCollection cards = getPlayableCards(sa, ai);
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
if (cards.isEmpty()) {
|
||||||
if (tgt != null) {
|
return false;
|
||||||
ZoneType zone = tgt.getZone().get(0);
|
|
||||||
cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
|
||||||
if (cards.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (!sa.hasParam("Valid")) {
|
|
||||||
cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
|
||||||
if (cards.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cards != null & sa.hasParam("ValidSA")) {
|
|
||||||
final String valid[] = sa.getParam("ValidSA").split(",");
|
|
||||||
final Iterator<Card> itr = cards.iterator();
|
|
||||||
while (itr.hasNext()) {
|
|
||||||
final Card c = itr.next();
|
|
||||||
if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , c, sa))) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cards.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.getRules().hasAppliedVariant(GameType.MoJhoSto) && source.getName().equals("Jhoira of the Ghitu Avatar")) {
|
if (game.getRules().hasAppliedVariant(GameType.MoJhoSto) && source.getName().equals("Jhoira of the Ghitu Avatar")) {
|
||||||
@@ -85,25 +61,15 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that if a ValidZone is specified, there's at least something to choose from in that zone.
|
|
||||||
CardCollectionView validOpts = new CardCollection();
|
|
||||||
if (sa.hasParam("ValidZone")) {
|
|
||||||
validOpts = AbilityUtils.filterListByType(game.getCardsIn(ZoneType.valueOf(sa.getParam("ValidZone"))),
|
|
||||||
sa.getParam("Valid"), sa);
|
|
||||||
if (validOpts.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("ReplaySpell".equals(logic)) {
|
if ("ReplaySpell".equals(logic)) {
|
||||||
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"));
|
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"), false);
|
||||||
} else if (logic.startsWith("NeedsChosenCard")) {
|
} else if (logic.startsWith("NeedsChosenCard")) {
|
||||||
int minCMC = 0;
|
int minCMC = 0;
|
||||||
if (sa.getPayCosts().getCostMana() != null) {
|
if (sa.getPayCosts().getCostMana() != null) {
|
||||||
minCMC = sa.getPayCosts().getTotalMana().getCMC();
|
minCMC = sa.getPayCosts().getTotalMana().getCMC();
|
||||||
}
|
}
|
||||||
validOpts = CardLists.filter(validOpts, CardPredicates.greaterCMC(minCMC));
|
cards = CardLists.filter(cards, CardPredicates.greaterCMC(minCMC));
|
||||||
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null, null) != null;
|
return chooseSingleCard(ai, sa, cards, sa.hasParam("Optional"), null, null) != null;
|
||||||
} else if ("WithTotalCMC".equals(logic)) {
|
} else if ("WithTotalCMC".equals(logic)) {
|
||||||
// Try to play only when there are more than three playable cards.
|
// Try to play only when there are more than three playable cards.
|
||||||
if (cards.size() < 3)
|
if (cards.size() < 3)
|
||||||
@@ -154,6 +120,10 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("ReplaySpell".equals(sa.getParam("AILogic"))) {
|
||||||
|
return ComputerUtil.targetPlayableSpellCard(ai, getPlayableCards(sa, ai), sa, sa.hasParam("WithoutManaCost"), mandatory);
|
||||||
|
}
|
||||||
|
|
||||||
return checkApiLogic(ai, sa);
|
return checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,4 +188,36 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
});
|
});
|
||||||
return ComputerUtilCard.getBestAI(tgtCards);
|
return ComputerUtilCard.getBestAI(tgtCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CardCollection getPlayableCards(SpellAbility sa, Player ai) {
|
||||||
|
CardCollection cards = new CardCollection();
|
||||||
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
|
if (tgt != null) {
|
||||||
|
ZoneType zone = tgt.getZone().get(0);
|
||||||
|
cards = CardLists.getValidCards(ai.getGame().getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
||||||
|
} else if (!sa.hasParam("Valid")) {
|
||||||
|
cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cards != null & sa.hasParam("ValidSA")) {
|
||||||
|
final String valid[] = sa.getParam("ValidSA").split(",");
|
||||||
|
final Iterator<Card> itr = cards.iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
final Card c = itr.next();
|
||||||
|
if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , c, sa))) {
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that if a ValidZone is specified, there's at least something to choose from in that zone.
|
||||||
|
if (sa.hasParam("ValidZone")) {
|
||||||
|
cards = new CardCollection(AbilityUtils.filterListByType(ai.getGame().getCardsIn(ZoneType.valueOf(sa.getParam("ValidZone"))),
|
||||||
|
sa.getParam("Valid"), sa));
|
||||||
|
}
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("Snapcaster".equals(sa.getParam("AILogic"))) {
|
if ("Snapcaster".equals(sa.getParam("AILogic"))) {
|
||||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi {
|
|||||||
uncastableCMCThreshold = aic.getIntProperty(AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF);
|
uncastableCMCThreshold = aic.getIntProperty(AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player p = pc.getFirst(); // FIXME: is this always a single target spell?
|
Player p = pc.getFirst(); // currently always a single target spell
|
||||||
Card top = p.getCardsIn(ZoneType.Library).getFirst();
|
Card top = p.getCardsIn(ZoneType.Library).getFirst();
|
||||||
int landsOTB = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
int landsOTB = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
||||||
int cmc = top.isSplitCard() ? Math.min(top.getCMC(Card.SplitCMCMode.LeftSplitCMC), top.getCMC(Card.SplitCMCMode.RightSplitCMC))
|
int cmc = top.isSplitCard() ? Math.min(top.getCMC(Card.SplitCMCMode.LeftSplitCMC), top.getCMC(Card.SplitCMCMode.RightSplitCMC))
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
//if card face has no cost, assume castable only by mana of its defined color
|
//if card face has no cost, assume castable only by mana of its defined color
|
||||||
return face.getColor().hasNoColorsExcept(colorCode);
|
return face.getColor().hasNoColorsExcept(colorCode);
|
||||||
}
|
}
|
||||||
return face.getManaCost().canBePaidWithAvaliable(colorCode);
|
return face.getManaCost().canBePaidWithAvailable(colorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canCastWithAvailable(byte colorCode) {
|
public boolean canCastWithAvailable(byte colorCode) {
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostSh
|
|||||||
* @param colorCode
|
* @param colorCode
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean canBePaidWithAvaliable(byte colorCode) {
|
public boolean canBePaidWithAvailable(byte colorCode) {
|
||||||
for (ManaCostShard shard : shards) {
|
for (ManaCostShard shard : shards) {
|
||||||
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(colorCode)) {
|
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(colorCode)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
boolean isOptional = sa.hasParam("Optional");
|
boolean isOptional = sa.hasParam("Optional");
|
||||||
|
|
||||||
for (Player controller : controllers) {
|
for (Player controller : controllers) {
|
||||||
|
|
||||||
List<SpellAbility> copies = Lists.newArrayList();
|
List<SpellAbility> copies = Lists.newArrayList();
|
||||||
|
|
||||||
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
||||||
|
|||||||
@@ -6342,7 +6342,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isFaceDown() && isInZone(ZoneType.Exile)) {
|
if (isFaceDown() && isInZone(ZoneType.Exile)) {
|
||||||
for (final SpellAbility sa : getState(CardStateName.Original).getSpellAbilities()) {
|
for (final SpellAbility sa : oState.getSpellAbilities()) {
|
||||||
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ public class CardLists {
|
|||||||
*/
|
*/
|
||||||
public static void sortByCmcDesc(final List<Card> list) {
|
public static void sortByCmcDesc(final List<Card> list) {
|
||||||
Collections.sort(list, CmcComparatorInv);
|
Collections.sort(list, CmcComparatorInv);
|
||||||
} // sortByCmcDesc
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -133,7 +133,7 @@ public class CardLists {
|
|||||||
*/
|
*/
|
||||||
public static void sortByToughnessAsc(final List<Card> list) {
|
public static void sortByToughnessAsc(final List<Card> list) {
|
||||||
Collections.sort(list, ToughnessComparator);
|
Collections.sort(list, ToughnessComparator);
|
||||||
} // sortByToughnessAsc()
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -144,7 +144,7 @@ public class CardLists {
|
|||||||
*/
|
*/
|
||||||
public static void sortByToughnessDesc(final List<Card> list) {
|
public static void sortByToughnessDesc(final List<Card> list) {
|
||||||
Collections.sort(list, ToughnessComparatorInv);
|
Collections.sort(list, ToughnessComparatorInv);
|
||||||
} // sortByToughnessDesc()
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -155,7 +155,7 @@ public class CardLists {
|
|||||||
*/
|
*/
|
||||||
public static void sortByPowerAsc(final List<Card> list) {
|
public static void sortByPowerAsc(final List<Card> list) {
|
||||||
Collections.sort(list, PowerComparator);
|
Collections.sort(list, PowerComparator);
|
||||||
} // sortAttackLowFirst()
|
}
|
||||||
|
|
||||||
// the higher the attack the better
|
// the higher the attack the better
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +167,7 @@ public class CardLists {
|
|||||||
*/
|
*/
|
||||||
public static void sortByPowerDesc(final List<Card> list) {
|
public static void sortByPowerDesc(final List<Card> list) {
|
||||||
Collections.sort(list, Collections.reverseOrder(PowerComparator));
|
Collections.sort(list, Collections.reverseOrder(PowerComparator));
|
||||||
} // sortAttack()
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
|
|||||||
return this.parent;
|
return this.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public boolean canPlay() {
|
public boolean canPlay() {
|
||||||
@@ -72,10 +70,8 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final SpellAbilityEffect effect;
|
private final SpellAbilityEffect effect;
|
||||||
|
|
||||||
|
|
||||||
public AbilitySub(ApiType api0, final Card ca, final TargetRestrictions tgt, Map<String, String> params0) {
|
public AbilitySub(ApiType api0, final Card ca, final TargetRestrictions tgt, Map<String, String> params0) {
|
||||||
super(ca, Cost.Zero);
|
super(ca, Cost.Zero);
|
||||||
this.setTargetRestrictions(tgt);
|
this.setTargetRestrictions(tgt);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Colors:blue
|
|||||||
Types:Legendary Planeswalker Jace
|
Types:Legendary Planeswalker Jace
|
||||||
Loyalty:5
|
Loyalty:5
|
||||||
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumAtt$ -2 | IsCurse$ True | Duration$ UntilYourNextTurn | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Up to one target creature gets -2/-0 until your next turn.
|
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumAtt$ -2 | IsCurse$ True | Duration$ UntilYourNextTurn | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Up to one target creature gets -2/-0 until your next turn.
|
||||||
A:AB$ Effect | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | AILogic$ CastFromGraveThisTurn | ValidTgts$ Instant.YouCtrl,Sorcery.YouCtrl | TgtZone$ Graveyard | TgtPrompt$ Select target instant or sorcery card | RememberObjects$ Targeted | StaticAbilities$ Play | ExileOnMoved$ Graveyard | SubAbility$ DBEffect | SpellDescription$ You may cast target instant or sorcery card from your graveyard this turn. If that spell would be put into your graveyard this turn, exile it instead.
|
A:AB$ Effect | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | AILogic$ ReplaySpell | ValidTgts$ Instant.YouCtrl,Sorcery.YouCtrl | TgtZone$ Graveyard | TgtPrompt$ Select target instant or sorcery card | RememberObjects$ Targeted | StaticAbilities$ Play | ExileOnMoved$ Graveyard | SubAbility$ DBEffect | SpellDescription$ You may cast target instant or sorcery card from your graveyard this turn. If that spell would be put into your graveyard this turn, exile it instead.
|
||||||
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card.
|
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card.
|
||||||
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | ExileOnMoved$ Stack | ReplacementEffects$ ReplaceGraveyard
|
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | ExileOnMoved$ Stack | ReplacementEffects$ ReplaceGraveyard
|
||||||
SVar:ReplaceGraveyard:Event$ Moved | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile | Description$ If that card would be put into your graveyard this turn, exile it instead.
|
SVar:ReplaceGraveyard:Event$ Moved | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile | Description$ If that card would be put into your graveyard this turn, exile it instead.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Mission Briefing
|
|||||||
ManaCost:U U
|
ManaCost:U U
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Surveil | Cost$ U U | Amount$ 2 | SubAbility$ DBChooseCard | SpellDescription$ Surveil 2, then choose an instant or sorcery card in your graveyard. You may cast it this turn. If that spell would be put into your graveyard this turn, exile it instead. (To surveil 2, look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)
|
A:SP$ Surveil | Cost$ U U | Amount$ 2 | SubAbility$ DBChooseCard | SpellDescription$ Surveil 2, then choose an instant or sorcery card in your graveyard. You may cast it this turn. If that spell would be put into your graveyard this turn, exile it instead. (To surveil 2, look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)
|
||||||
SVar:DBChooseCard:DB$ ChooseCard | Choices$ Instant.YouCtrl,Sorcery.YouCtrl | ChoiceZone$ Graveyard | AILogic$ CastFromGraveThisTurn | Mandatory$ True | RememberChosen$ True | SubAbility$ DBEffect | SpellDescription$ You may cast that card this turn. If that card would be put into your graveyard this turn, exile it instead.
|
SVar:DBChooseCard:DB$ ChooseCard | Choices$ Instant.YouCtrl,Sorcery.YouCtrl | ChoiceZone$ Graveyard | Mandatory$ True | RememberChosen$ True | SubAbility$ DBEffect | SpellDescription$ You may cast that card this turn. If that card would be put into your graveyard this turn, exile it instead.
|
||||||
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ExileOnMoved$ Stack | ReplacementEffects$ ReplaceGraveyard
|
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ExileOnMoved$ Stack | ReplacementEffects$ ReplaceGraveyard
|
||||||
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card.
|
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card.
|
||||||
SVar:ReplaceGraveyard:Event$ Moved | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile | Description$ If that card would be put into your graveyard this turn, exile it instead.
|
SVar:ReplaceGraveyard:Event$ Moved | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile | Description$ If that card would be put into your graveyard this turn, exile it instead.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Artifact Equipment
|
|||||||
K:Equip:2
|
K:Equip:2
|
||||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddTrigger$ AttackTrigger | Description$ Invoke Duplicity — Equipped creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may sacrifice CARDNAME. If you do, create a token that's a copy of this creature."
|
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddTrigger$ AttackTrigger | Description$ Invoke Duplicity — Equipped creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may sacrifice CARDNAME. If you do, create a token that's a copy of this creature."
|
||||||
SVar:AttackTrigger:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever this creature deals combat damage to a player, you may sacrifice Trickster's Talisman. If you do, create a token that's a copy of this creature.
|
SVar:AttackTrigger:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever this creature deals combat damage to a player, you may sacrifice Trickster's Talisman. If you do, create a token that's a copy of this creature.
|
||||||
SVar:TrigCopy:AB$ CopyPermanent | Cost$ Sac<1/OriginalHost/Trickster's Talisman> | Defined$ Self | NumCopies$ 1 | AILogic$ DuplicatePerms
|
SVar:TrigCopy:AB$ CopyPermanent | Cost$ Sac<1/OriginalHost/Trickster's Talisman> | Defined$ Self | NumCopies$ 1
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
Oracle:Invoke Duplicity — Equipped creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may sacrifice Trickster's Talisman. If you do, create a token that's a copy of this creature."\nEquip {2}
|
Oracle:Invoke Duplicity — Equipped creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may sacrifice Trickster's Talisman. If you do, create a token that's a copy of this creature."\nEquip {2}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Strixhaven Stadium|The Mage Tower|And that's game, set, and match!
|
|||||||
Test of Endurance|The Test|So... did I pass?
|
Test of Endurance|The Test|So... did I pass?
|
||||||
Thassa's Oracle|The Prophecy of Victory|I see... nothing. We must've won.
|
Thassa's Oracle|The Prophecy of Victory|I see... nothing. We must've won.
|
||||||
The Cheese Stands Alone|The Cheese|It's cheesy, but hey, it works!
|
The Cheese Stands Alone|The Cheese|It's cheesy, but hey, it works!
|
||||||
The Deck of Many Things|Down on the Deck|Lucky draw!
|
The Deck of Many Things's Effect|Down on the Deck|Lucky draw!
|
||||||
Triskaidekaphobia|The Fear of 13|It's just a silly ancient superstition... right?
|
Triskaidekaphobia|The Fear of 13|It's just a silly ancient superstition... right?
|
||||||
Vorpal Sword|Snicker-Snack!|He left it dead, and with its head / He went galumphing back.
|
Vorpal Sword|Snicker-Snack!|He left it dead, and with its head / He went galumphing back.
|
||||||
Emblem - Vraska, Golgari Queen|The Flurry of Assassins|How good is your dodging?
|
Emblem - Vraska, Golgari Queen|The Flurry of Assassins|How good is your dodging?
|
||||||
|
|||||||
@@ -697,9 +697,9 @@ public enum ColumnDef {
|
|||||||
return !(((IPaperCard) i).getRules().getType().isArtifact() && (toColor(i).isColorless() ||
|
return !(((IPaperCard) i).getRules().getType().isArtifact() && (toColor(i).isColorless() ||
|
||||||
//If it isn't colorless, see if it can be paid with only white, only blue, only black.
|
//If it isn't colorless, see if it can be paid with only white, only blue, only black.
|
||||||
//No need to check others since three-color hybrid shards don't exist.
|
//No need to check others since three-color hybrid shards don't exist.
|
||||||
manaCost.canBePaidWithAvaliable(MagicColor.WHITE) &&
|
manaCost.canBePaidWithAvailable(MagicColor.WHITE) &&
|
||||||
manaCost.canBePaidWithAvaliable(MagicColor.BLUE) &&
|
manaCost.canBePaidWithAvailable(MagicColor.BLUE) &&
|
||||||
manaCost.canBePaidWithAvaliable(MagicColor.BLACK)))
|
manaCost.canBePaidWithAvailable(MagicColor.BLACK)))
|
||||||
? "0" + toSplitLast(i) : "1";
|
? "0" + toSplitLast(i) : "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,9 +757,9 @@ public enum ColumnDef {
|
|||||||
private static String toGoldFirst(final InventoryItem i) {
|
private static String toGoldFirst(final InventoryItem i) {
|
||||||
forge.card.mana.ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost();
|
forge.card.mana.ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost();
|
||||||
|
|
||||||
return !(manaCost.canBePaidWithAvaliable(MagicColor.WHITE) | manaCost.canBePaidWithAvaliable(MagicColor.BLUE) |
|
return !(manaCost.canBePaidWithAvailable(MagicColor.WHITE) | manaCost.canBePaidWithAvailable(MagicColor.BLUE) |
|
||||||
manaCost.canBePaidWithAvaliable(MagicColor.BLACK) | manaCost.canBePaidWithAvaliable(MagicColor.RED) |
|
manaCost.canBePaidWithAvailable(MagicColor.BLACK) | manaCost.canBePaidWithAvailable(MagicColor.RED) |
|
||||||
manaCost.canBePaidWithAvaliable(MagicColor.GREEN)) ? "0" : "1";
|
manaCost.canBePaidWithAvailable(MagicColor.GREEN)) ? "0" : "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -90,12 +90,12 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
|
|||||||
filename = filename0;
|
filename = filename0;
|
||||||
isLimitedFormat = isLimitedFormat0;
|
isLimitedFormat = isLimitedFormat0;
|
||||||
path = path0;
|
path = path0;
|
||||||
addSharedAchivements();
|
addSharedAchievements();
|
||||||
addAchievements();
|
addAchievements();
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addSharedAchivements() {
|
protected void addSharedAchievements() {
|
||||||
add(new GameWinStreak(10, 25, 50, 100));
|
add(new GameWinStreak(10, 25, 50, 100));
|
||||||
add(new MatchWinStreak(10, 25, 50, 100));
|
add(new MatchWinStreak(10, 25, 50, 100));
|
||||||
add(new TotalGameWins(250, 500, 1000, 2000));
|
add(new TotalGameWins(250, 500, 1000, 2000));
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class AltWinAchievements extends AchievementCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addSharedAchivements() {
|
protected void addSharedAchievements() {
|
||||||
//prevent including shared achievements
|
//prevent including shared achievements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class ChallengeAchievements extends AchievementCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addSharedAchivements() {
|
protected void addSharedAchievements() {
|
||||||
//prevent including shared achievements
|
//prevent including shared achievements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class PlaneswalkerAchievements extends AchievementCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addSharedAchivements() {
|
protected void addSharedAchievements() {
|
||||||
//prevent including shared achievements
|
//prevent including shared achievements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class PuzzleAchievements extends AchievementCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addSharedAchivements() {
|
protected void addSharedAchievements() {
|
||||||
//prevent including shared achievements
|
//prevent including shared achievements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user