Merge pull request #2898 from tool4ever/cleanup_12

Cleanup & Fixes
This commit is contained in:
Anthony Calosa
2023-04-12 16:33:22 +08:00
committed by GitHub
10 changed files with 28 additions and 55 deletions

View File

@@ -969,21 +969,16 @@ public class AiController {
} }
private boolean canPlaySpellWithoutBuyback(Card card, SpellAbility sa) { private boolean canPlaySpellWithoutBuyback(Card card, SpellAbility sa) {
boolean wasteBuybackAllowed = false;
// About to lose game : allow
if (ComputerUtil.aiLifeInDanger(player, true, 0)) {
wasteBuybackAllowed = true;
}
int copies = CardLists.count(player.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(card.getName())); int copies = CardLists.count(player.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(card.getName()));
// Have two copies : allow // Have two copies : allow
if (copies >= 2) { if (copies >= 2) {
wasteBuybackAllowed = true; return true;
} }
int neededMana = 0; // About to lose game : allow
boolean dangerousRecurringCost = false; if (ComputerUtil.aiLifeInDanger(player, true, 0)) {
return true;
}
Cost costWithBuyback = sa.getPayCosts().copy(); Cost costWithBuyback = sa.getPayCosts().copy();
for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) { for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) {
@@ -991,22 +986,19 @@ public class AiController {
costWithBuyback.add(opt.getCost()); costWithBuyback.add(opt.getCost());
} }
} }
CostAdjustment.adjust(costWithBuyback, sa); costWithBuyback = CostAdjustment.adjust(costWithBuyback, sa);
if (costWithBuyback.getCostMana() != null) {
neededMana = costWithBuyback.getCostMana().getMana().getCMC();
}
if (costWithBuyback.hasSpecificCostType(CostPayLife.class) if (costWithBuyback.hasSpecificCostType(CostPayLife.class)
|| costWithBuyback.hasSpecificCostType(CostDiscard.class) || costWithBuyback.hasSpecificCostType(CostDiscard.class)
|| costWithBuyback.hasSpecificCostType(CostSacrifice.class)) { || costWithBuyback.hasSpecificCostType(CostSacrifice.class)) {
dangerousRecurringCost = true; // won't be able to afford buyback any time soon
// if Buyback cost includes sacrifice, life, discard
return true;
} }
// won't be able to afford buyback any time soon int neededMana = 0;
// if Buyback cost includes sacrifice, life, discard if (costWithBuyback.getCostMana() != null) {
if (dangerousRecurringCost) { neededMana = costWithBuyback.getCostMana().getMana().getCMC();
wasteBuybackAllowed = true;
} }
// Memory Crystal-like effects need special handling // Memory Crystal-like effects need special handling
for (Card c : game.getCardsIn(ZoneType.Battlefield)) { for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
for (StaticAbility s : c.getStaticAbilities()) { for (StaticAbility s : c.getStaticAbilities()) {
@@ -1022,10 +1014,10 @@ public class AiController {
int hasMana = ComputerUtilMana.getAvailableManaEstimate(player, false); int hasMana = ComputerUtilMana.getAvailableManaEstimate(player, false);
if (hasMana < neededMana - 1) { if (hasMana < neededMana - 1) {
wasteBuybackAllowed = true; return true;
} }
return wasteBuybackAllowed; return false;
} }
// not sure "playing biggest spell" matters? // not sure "playing biggest spell" matters?

View File

@@ -1871,10 +1871,9 @@ public class ComputerUtilCard {
public static int getMaxSAEnergyCostOnBattlefield(final Player ai) { public static int getMaxSAEnergyCostOnBattlefield(final Player ai) {
// returns the maximum energy cost of an ability that permanents on the battlefield under AI's control have // returns the maximum energy cost of an ability that permanents on the battlefield under AI's control have
CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield);
int maxEnergyCost = 0; int maxEnergyCost = 0;
for (Card c : otb) { for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
for (SpellAbility sa : c.getSpellAbilities()) { for (SpellAbility sa : c.getSpellAbilities()) {
CostPayEnergy energyCost = sa.getPayCosts().getCostEnergy(); CostPayEnergy energyCost = sa.getPayCosts().getCostEnergy();
if (energyCost != null) { if (energyCost != null) {

View File

@@ -669,8 +669,7 @@ public class AttachAi extends SpellAbilityAi {
// Prefer "tap to deal damage" // Prefer "tap to deal damage"
// TODO : Skip this one if triggers on combat damage only? // TODO : Skip this one if triggers on combat damage only?
for (SpellAbility sa2 : card.getSpellAbilities()) { for (SpellAbility sa2 : card.getSpellAbilities()) {
if (ApiType.DealDamage.equals(sa2.getApi()) if (ApiType.DealDamage.equals(sa2.getApi()) && sa2.usesTargeting() && sa2.getTargetRestrictions().canTgtPlayer()) {
&& (sa2.getTargetRestrictions().canTgtPlayer())) {
cardPriority += 300; cardPriority += 300;
} }
} }

View File

@@ -71,6 +71,9 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
} else if (top.getApi() == ApiType.CopySpellAbility) { } else if (top.getApi() == ApiType.CopySpellAbility) {
// Don't try to copy a copy ability, too complex for the AI to handle // Don't try to copy a copy ability, too complex for the AI to handle
return false; return false;
} else if (top.getApi() == ApiType.Mana) {
// would lead to Stack Overflow by trying to play this again
return false;
} else if (top.getApi() == ApiType.DestroyAll || top.getApi() == ApiType.SacrificeAll || top.getApi() == ApiType.ChangeZoneAll || top.getApi() == ApiType.TapAll || top.getApi() == ApiType.UnattachAll) { } else if (top.getApi() == ApiType.DestroyAll || top.getApi() == ApiType.SacrificeAll || top.getApi() == ApiType.ChangeZoneAll || top.getApi() == ApiType.TapAll || top.getApi() == ApiType.UnattachAll) {
if (!top.usesTargeting() || top.getActivatingPlayer().equals(aiPlayer)) { if (!top.usesTargeting() || top.getActivatingPlayer().equals(aiPlayer)) {
// If we activated a mass removal / mass tap / mass bounce / etc. spell, or if the opponent activated it but // If we activated a mass removal / mass tap / mass bounce / etc. spell, or if the opponent activated it but

View File

@@ -64,8 +64,7 @@ public class PoisonAi extends SpellAbilityAi {
return true; return true;
} else { } else {
// currently there are no optional Trigger // currently there are no optional Trigger
final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
sa);
if (players.isEmpty()) { if (players.isEmpty()) {
return false; return false;
} }

View File

@@ -553,25 +553,6 @@ public class CostAdjustment {
return false; return false;
} }
} }
if (st.hasParam("ValidSpellTarget")) {
SpellAbility curSa = sa;
boolean targetValid = false;
outer: while (curSa != null) {
if (!curSa.usesTargeting()) {
curSa = curSa.getSubAbility();
continue;
}
for (SpellAbility target : curSa.getTargets().getTargetSpells()) {
Card targetCard = target.getHostCard();
if (targetCard.isValid(st.getParam("ValidSpellTarget").split(","), controller, hostCard, curSa)) {
targetValid = true;
break outer;
}
}
curSa = curSa.getSubAbility();
}
return targetValid;
}
return true; return true;
} }
} }

View File

@@ -207,7 +207,6 @@ public class TriggerHandler {
public final void resetActiveTriggers() { public final void resetActiveTriggers() {
resetActiveTriggers(true); resetActiveTriggers(true);
} }
public final void resetActiveTriggers(boolean collect) { public final void resetActiveTriggers(boolean collect) {
if (collect) { if (collect) {
collectTriggerForWaiting(); collectTriggerForWaiting();
@@ -281,11 +280,11 @@ public class TriggerHandler {
} }
public final boolean runWaitingTriggers() { public final boolean runWaitingTriggers() {
final List<TriggerWaiting> waiting = new ArrayList<>(waitingTriggers); if (waitingTriggers.isEmpty()) {
waitingTriggers.clear();
if (waiting.isEmpty()) {
return false; return false;
} }
final List<TriggerWaiting> waiting = new ArrayList<>(waitingTriggers);
waitingTriggers.clear();
boolean haveWaiting = false; boolean haveWaiting = false;
for (final TriggerWaiting wt : waiting) { for (final TriggerWaiting wt : waiting) {

View File

@@ -2,6 +2,6 @@ Name:Kaervek's Torch
ManaCost:X R ManaCost:X R
Types:Sorcery Types:Sorcery
A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SpellDescription$ CARDNAME deals X damage to any target. A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SpellDescription$ CARDNAME deals X damage to any target.
S:Mode$ RaiseCost | ValidSpellTarget$ Card.Self | Activator$ Player | Type$ Spell | Amount$ 2 | EffectZone$ Stack | Description$ As long as CARDNAME is on the stack, spells that target it cost {2} more to cast. S:Mode$ RaiseCost | ValidTarget$ Spell.Self | Activator$ Player | Type$ Spell | Amount$ 2 | EffectZone$ Stack | Description$ As long as CARDNAME is on the stack, spells that target it cost {2} more to cast.
SVar:X:Count$xPaid SVar:X:Count$xPaid
Oracle:As long as Kaervek's Torch is on the stack, spells that target it cost {2} more to cast.\nKaervek's Torch deals X damage to any target. Oracle:As long as Kaervek's Torch is on the stack, spells that target it cost {2} more to cast.\nKaervek's Torch deals X damage to any target.

View File

@@ -2,7 +2,7 @@ Name:Rousing Refrain
ManaCost:3 R R ManaCost:3 R R
Types:Sorcery Types:Sorcery
K:Suspend:3:1 R K:Suspend:3:1 R
A:SP$ Mana | Cost$ 3 R R | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | SubAbility$ DBMana | AILogic$ ManaRitual | Produced$ R | Amount$ Z | PersistentMana$ True | Defined$ You | SubAbility$ DBChange | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, you don't lose this mana as steps and phases end. A:SP$ Mana | Cost$ 3 R R | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | SubAbility$ DBMana | AILogic$ ManaRitual | Produced$ R | Amount$ Z | PersistentMana$ True | Defined$ You | SubAbility$ DBChange | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, you don't lose this mana as steps and phases end.
SVar:Z:TargetedPlayer$CardsInHand SVar:Z:TargetedPlayer$CardsInHand
SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | WithCountersType$ TIME | WithCountersAmount$ 3 | SpellDescription$ Exile CARDNAME with three time counters on it. SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | WithCountersType$ TIME | WithCountersAmount$ 3 | SpellDescription$ Exile CARDNAME with three time counters on it.
Oracle:Add {R} for each card in target opponent's hand. Until end of turn, you don't lose this mana as steps and phases end. Exile Rousing Refrain with three time counters on it.\nSuspend 3—{1}{R} (Rather than cast this card from your hand, you may pay {1}{R} and exile it with three time counters on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.) Oracle:Add {R} for each card in target opponent's hand. Until end of turn, you don't lose this mana as steps and phases end. Exile Rousing Refrain with three time counters on it.\nSuspend 3—{1}{R} (Rather than cast this card from your hand, you may pay {1}{R} and exile it with three time counters on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.)

View File

@@ -6,8 +6,9 @@ A:AB$ Draw | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 1 | Sp
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
AI:RemoveDeck:All AI:RemoveDeck:All
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | KW$ First Strike & Trample | SpellDescription$ Target creature gets +2/+0 and gains first strike and trample until end of turn. A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | KW$ First Strike & Trample | SpellDescription$ Target creature gets +2/+0 and gains first strike and trample until end of turn.
A:AB$ Draw | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 4 | SubAbility$ DBTrigger | SpellDescription$ Draw four cards. When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. A:AB$ Draw | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 4 | RememberDrawn$ True | SubAbility$ DBTrigger | SpellDescription$ Draw four cards. When you do, CARDNAME deals damage to any target equal to the number of cards in your hand.
SVar:DBTrigger:DB$ ImmediateTrigger | Execute$ DBDamage | TriggerDescription$ When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. SVar:DBTrigger:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE4 | Execute$ DBDamage | TriggerDescription$ When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. | SubAbility$ DBCleanup
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Count$InYourHand SVar:X:Count$InYourHand
Oracle:[+1]: Draw a card, then discard a card.\n[+1]: Target creature gets +2/+0 and gains first strike and trample until end of turn.\n[-8]: Draw four cards. When you do, The Royal Scions deals damage to any target equal to the number of cards in your hand. Oracle:[+1]: Draw a card, then discard a card.\n[+1]: Target creature gets +2/+0 and gains first strike and trample until end of turn.\n[-8]: Draw four cards. When you do, The Royal Scions deals damage to any target equal to the number of cards in your hand.