mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
@@ -969,21 +969,16 @@ public class AiController {
|
||||
}
|
||||
|
||||
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()));
|
||||
// Have two copies : allow
|
||||
if (copies >= 2) {
|
||||
wasteBuybackAllowed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int neededMana = 0;
|
||||
boolean dangerousRecurringCost = false;
|
||||
// About to lose game : allow
|
||||
if (ComputerUtil.aiLifeInDanger(player, true, 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Cost costWithBuyback = sa.getPayCosts().copy();
|
||||
for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) {
|
||||
@@ -991,22 +986,19 @@ public class AiController {
|
||||
costWithBuyback.add(opt.getCost());
|
||||
}
|
||||
}
|
||||
CostAdjustment.adjust(costWithBuyback, sa);
|
||||
if (costWithBuyback.getCostMana() != null) {
|
||||
neededMana = costWithBuyback.getCostMana().getMana().getCMC();
|
||||
}
|
||||
costWithBuyback = CostAdjustment.adjust(costWithBuyback, sa);
|
||||
if (costWithBuyback.hasSpecificCostType(CostPayLife.class)
|
||||
|| costWithBuyback.hasSpecificCostType(CostDiscard.class)
|
||||
|| costWithBuyback.hasSpecificCostType(CostSacrifice.class)) {
|
||||
dangerousRecurringCost = true;
|
||||
}
|
||||
|
||||
// won't be able to afford buyback any time soon
|
||||
// if Buyback cost includes sacrifice, life, discard
|
||||
if (dangerousRecurringCost) {
|
||||
wasteBuybackAllowed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int neededMana = 0;
|
||||
if (costWithBuyback.getCostMana() != null) {
|
||||
neededMana = costWithBuyback.getCostMana().getMana().getCMC();
|
||||
}
|
||||
// Memory Crystal-like effects need special handling
|
||||
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (StaticAbility s : c.getStaticAbilities()) {
|
||||
@@ -1022,10 +1014,10 @@ public class AiController {
|
||||
|
||||
int hasMana = ComputerUtilMana.getAvailableManaEstimate(player, false);
|
||||
if (hasMana < neededMana - 1) {
|
||||
wasteBuybackAllowed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return wasteBuybackAllowed;
|
||||
return false;
|
||||
}
|
||||
|
||||
// not sure "playing biggest spell" matters?
|
||||
|
||||
@@ -1871,10 +1871,9 @@ public class ComputerUtilCard {
|
||||
|
||||
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
|
||||
CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield);
|
||||
int maxEnergyCost = 0;
|
||||
|
||||
for (Card c : otb) {
|
||||
for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
CostPayEnergy energyCost = sa.getPayCosts().getCostEnergy();
|
||||
if (energyCost != null) {
|
||||
|
||||
@@ -669,8 +669,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
// Prefer "tap to deal damage"
|
||||
// TODO : Skip this one if triggers on combat damage only?
|
||||
for (SpellAbility sa2 : card.getSpellAbilities()) {
|
||||
if (ApiType.DealDamage.equals(sa2.getApi())
|
||||
&& (sa2.getTargetRestrictions().canTgtPlayer())) {
|
||||
if (ApiType.DealDamage.equals(sa2.getApi()) && sa2.usesTargeting() && sa2.getTargetRestrictions().canTgtPlayer()) {
|
||||
cardPriority += 300;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
|
||||
} else if (top.getApi() == ApiType.CopySpellAbility) {
|
||||
// Don't try to copy a copy ability, too complex for the AI to handle
|
||||
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) {
|
||||
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
|
||||
|
||||
@@ -64,8 +64,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
return true;
|
||||
} else {
|
||||
// currently there are no optional Trigger
|
||||
final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"),
|
||||
sa);
|
||||
final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||
if (players.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -553,25 +553,6 @@ public class CostAdjustment {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,6 @@ public class TriggerHandler {
|
||||
public final void resetActiveTriggers() {
|
||||
resetActiveTriggers(true);
|
||||
}
|
||||
|
||||
public final void resetActiveTriggers(boolean collect) {
|
||||
if (collect) {
|
||||
collectTriggerForWaiting();
|
||||
@@ -281,11 +280,11 @@ public class TriggerHandler {
|
||||
}
|
||||
|
||||
public final boolean runWaitingTriggers() {
|
||||
final List<TriggerWaiting> waiting = new ArrayList<>(waitingTriggers);
|
||||
waitingTriggers.clear();
|
||||
if (waiting.isEmpty()) {
|
||||
if (waitingTriggers.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final List<TriggerWaiting> waiting = new ArrayList<>(waitingTriggers);
|
||||
waitingTriggers.clear();
|
||||
|
||||
boolean haveWaiting = false;
|
||||
for (final TriggerWaiting wt : waiting) {
|
||||
|
||||
@@ -2,6 +2,6 @@ Name:Kaervek's Torch
|
||||
ManaCost:X R
|
||||
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.
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
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$ 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.
|
||||
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.
|
||||
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 | 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:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user