AI cleanup (#9210)

* Cherry pick priority improvement

* Align signatures
This commit is contained in:
tool4ever
2025-11-19 23:24:19 +01:00
committed by GitHub
parent 3c858c6b47
commit bd710765f1
78 changed files with 219 additions and 203 deletions

View File

@@ -938,10 +938,9 @@ public class ComputerUtil {
}
}
for (int ip = 0; ip < 6; ip++) { // priority 0 is the lowest, priority 5 the highest
final int priority = 6 - ip;
for (int prio = 6; prio > 0; prio--) {
for (Card card : remaining) {
if (card.hasSVar("SacMe") && Integer.parseInt(card.getSVar("SacMe")) == priority) {
if (card.hasSVar("SacMe") && Integer.parseInt(card.getSVar("SacMe")) == prio) {
return card;
}
}
@@ -1153,7 +1152,7 @@ public class ComputerUtil {
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
return true;
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
// Only play these main1 when the opponent has creatures (stealing and giving them haste)
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
return true;
}
@@ -1323,8 +1322,8 @@ public class ComputerUtil {
}
}
final CardCollectionView buffed = ai.getCardsIn(ZoneType.Battlefield);
boolean checkThreshold = sa.isSpell() && !ai.hasThreshold() && !source.isInZone(ZoneType.Graveyard);
final CardCollectionView buffed = ai.getCardsIn(ZoneType.Battlefield);
for (Card buffedCard : buffed) {
if (buffedCard.hasSVar("BuffedBy")) {
final String buffedby = buffedCard.getSVar("BuffedBy");
@@ -3068,16 +3067,16 @@ public class ComputerUtil {
}
// this function should be called by most API to give scripters the option of helping AI
public static CardCollection filterAITgts(SpellAbility sa, Player ai, CardCollection srcList, boolean alwaysStrict) {
public static CardCollection filterAITgts(SpellAbility sa, Player ai, CardCollection targetables, boolean alwaysStrict) {
// TODO support players
final Card source = sa.getHostCard();
if (source == null || !sa.hasParam("AITgts")) {
return srcList;
return targetables;
}
// TODO randomize the order, just so human can't predict in advance which of two equal cards AI might pick
CardCollection list;
CardCollection filtered;
String aiTgts = sa.getParam("AITgts");
if (aiTgts.startsWith("BetterThan")) {
int value = 0;
@@ -3097,28 +3096,28 @@ public class ComputerUtil {
value = ComputerUtilCard.evaluateCreature(source);
}
final int totalValue = value;
list = CardLists.filter(srcList, c -> ComputerUtilCard.evaluateCreature(c) > totalValue + 30);
filtered = CardLists.filter(targetables, c -> ComputerUtilCard.evaluateCreature(c) > totalValue + 30);
} else {
list = CardLists.getValidCards(srcList, aiTgts, sa.getActivatingPlayer(), source, sa);
filtered = CardLists.getValidCards(targetables, aiTgts, sa.getActivatingPlayer(), source, sa);
}
if (sa.hasParam("AITgtsStrict") || alwaysStrict) {
return list;
return filtered;
}
if (!list.isEmpty()) {
if (!filtered.isEmpty()) {
// try to fill up with other regular targets to increase chance of playing
for (Card tgt : srcList) {
if (list.size() >= sa.getMinTargets()) {
for (Card tgt : targetables) {
if (filtered.size() >= sa.getMinTargets()) {
break;
}
if (list.contains(tgt)) {
if (filtered.contains(tgt)) {
continue;
}
list.add(tgt);
filtered.add(tgt);
}
return list;
return filtered;
}
return srcList;
return targetables;
}
// Check if AI life is in danger/serious danger based on next expected combat

View File

@@ -3,6 +3,7 @@ package forge.ai;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
@@ -25,6 +26,10 @@ import forge.game.spellability.OptionalCost;
import forge.game.spellability.OptionalCostValue;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityMode;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
public class ComputerUtilAbility {
@@ -366,15 +371,32 @@ public class ComputerUtilAbility {
p -= 9;
}
// move snap-casted spells to front
if (source.isInZone(ZoneType.Graveyard)) {
if (sa.getMayPlay() != null && source.mayPlay(sa.getMayPlay()) != null) {
p += 50;
}
if (source.isInZone(ZoneType.Graveyard) && source.mayPlay(sa.getMayPlay()) != null) {
p += 50;
}
// if the profile specifies it, deprioritize Storm spells in an attempt to build up storm count
if (source.hasKeyword(Keyword.STORM) && ai.getController() instanceof PlayerControllerAi) {
p -= (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.PRIORITY_REDUCTION_FOR_STORM_SPELLS));
}
for (Trigger trig : source.getTriggers()) {
if (!"Battlefield".equals(trig.getParam("TriggerZones"))) {
continue;
}
final TriggerType mode = trig.getMode();
// benefit from Magecraft abilities
if ((mode == TriggerType.SpellCast || mode == TriggerType.SpellCastOrCopy) && "You".equals(sa.getParam("ValidActivatingPlayer"))) {
p += 1;
}
}
for (StaticAbility sta : source.getStaticAbilities()) {
final Set<StaticAbilityMode> mode = sta.getMode();
// reduce cost to enable more plays
if (mode.contains(StaticAbilityMode.ReduceCost) && "You".equals(sta.getParam("Activator"))) {
p += 1;
}
}
}
// use Surge and Prowl costs when able to

View File

@@ -1225,7 +1225,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView<Player> allPayers) {
if (SpellApiToAi.Converter.get(sa).willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers)) {
if (SpellApiToAi.Converter.get(sa).willPayUnlessCost(player, sa, cost, alreadyPaid, allPayers)) {
if (!ComputerUtilCost.canPayCost(cost, sa, player, true)) {
return false;
}
@@ -1586,7 +1586,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, List<OptionalCostValue> optionalCostValues) {
return SpellApiToAi.Converter.get(chosen).chooseOptionalCosts(chosen, player, optionalCostValues);
return SpellApiToAi.Converter.get(chosen).chooseOptionalCosts(player, chosen, optionalCostValues);
}
@Override

View File

@@ -247,7 +247,7 @@ public abstract class SpellAbilityAi {
*/
public AiAbilityDecision chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
final AbilitySub subAb = ab.getSubAbility();
AiAbilityDecision decision = SpellApiToAi.Converter.get(ab).chkDrawback(ab, aiPlayer);
AiAbilityDecision decision = SpellApiToAi.Converter.get(ab).chkDrawback(aiPlayer, ab);
if (!decision.willingToPlay()) {
return decision;
}
@@ -262,7 +262,7 @@ public abstract class SpellAbilityAi {
/**
* Handles the AI decision to play a sub-SpellAbility
*/
public AiAbilityDecision chkDrawback(final SpellAbility sa, final Player aiPlayer) {
public AiAbilityDecision chkDrawback(final Player aiPlayer, final SpellAbility sa) {
// sub-SpellAbility might use targets too
if (sa.usesTargeting()) {
// no Candidates, no adding to Stack
@@ -365,14 +365,14 @@ public abstract class SpellAbilityAi {
* <p>
* Evaluated costs are: life, discard, sacrifice and counter-removal
*/
protected boolean willPayCosts(final Player ai, final SpellAbility sa, final Cost cost, final Card source) {
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
protected boolean willPayCosts(final Player payer, final SpellAbility sa, final Cost cost, final Card source) {
if (!ComputerUtilCost.checkLifeCost(payer, cost, source, 4, sa)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source, sa)) {
if (!ComputerUtilCost.checkDiscardCost(payer, cost, source, sa)) {
return false;
}
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa)) {
if (!ComputerUtilCost.checkSacrificeCost(payer, cost, source, sa)) {
return false;
}
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source, sa)) {
@@ -381,7 +381,7 @@ public abstract class SpellAbilityAi {
return true;
}
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card source = sa.getHostCard();
final String aiLogic = sa.getParam("UnlessAI");
boolean payNever = "Never".equals(aiLogic);
@@ -400,7 +400,7 @@ public abstract class SpellAbilityAi {
&& (isMine || ComputerUtilCost.checkDiscardCost(payer, cost, source, sa));
}
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, Player player, List<OptionalCostValue> optionalCostValues) {
public List<OptionalCostValue> chooseOptionalCosts(Player payer, SpellAbility chosen, List<OptionalCostValue> optionalCostValues) {
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
Cost costSoFar = chosen.getPayCosts().copy();
@@ -420,7 +420,7 @@ public abstract class SpellAbilityAi {
}
}
if (ComputerUtilCost.canPayCost(fullCostSa, player, false)) {
if (ComputerUtilCost.canPayCost(fullCostSa, payer, false)) {
chosenOptCosts.add(opt);
costSoFar.add(opt.getCost());
}

View File

@@ -68,7 +68,7 @@ public class ActivateAbilityAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard();
if (!sa.usesTargeting()) {
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);

View File

@@ -234,7 +234,7 @@ public class AnimateAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
if (sa.usesTargeting()) {
sa.resetTargets();
return animateTgtAI(sa);
@@ -605,10 +605,10 @@ public class AnimateAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
if (sa.isKeyword(Keyword.RIOT)) {
return !SpecialAiLogic.preferHasteForRiot(sa, payer);
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -93,9 +93,9 @@ public class AssembleContraptionAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
if(getDeck(aiPlayer, sa).isEmpty())
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
return super.chkDrawback(sa, aiPlayer);
return super.chkDrawback(aiPlayer, sa);
}
}

View File

@@ -968,7 +968,7 @@ public class AttachAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(final SpellAbility sa, final Player ai) {
public AiAbilityDecision chkDrawback(final Player ai, final SpellAbility sa) {
if (sa.isTrigger() && sa.usesTargeting()) {
CardCollection targetables = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
CardCollection source = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Object"), sa);

View File

@@ -54,7 +54,7 @@ public class BecomesBlockedAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// TODO - implement AI
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}

View File

@@ -20,7 +20,7 @@ public class CannotPlayAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
}

View File

@@ -34,7 +34,7 @@ public class ChangeCombatantsAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
final String logic = sa.getParamOrDefault("AILogic", "");
if (logic.equals("WeakestOppExceptCtrl")) {

View File

@@ -45,23 +45,23 @@ public class ChangeZoneAi extends SpellAbilityAi {
// cards where multiple cards are fetched at once and they need to be coordinated
private static CardCollection multipleCardsToChoose = new CardCollection();
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
protected boolean willPayCosts(Player payer, SpellAbility sa, Cost cost, Card source) {
if (sa.isHidden()) {
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa)
if (!ComputerUtilCost.checkSacrificeCost(payer, cost, source, sa)
&& !"Battlefield".equals(sa.getParam("Destination")) && !source.isLand()) {
return false;
}
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
if (!ComputerUtilCost.checkLifeCost(payer, cost, source, 4, sa)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source, sa)) {
if (!ComputerUtilCost.checkDiscardCost(payer, cost, source, sa)) {
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostDiscard) {
CostDiscard cd = (CostDiscard) part;
// this is mainly for typecycling
if (!cd.payCostFromSource() || !ComputerUtil.isWorseThanDraw(ai, source)) {
if (!cd.payCostFromSource() || !ComputerUtil.isWorseThanDraw(payer, source)) {
return false;
}
}
@@ -80,7 +80,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
int amt = part.getAbilityAmount(sa);
needed += amt;
CardCollection toAdd = ComputerUtil.chooseExileFrom(ai, (CostExile) part, source, amt, sa, true);
CardCollection toAdd = ComputerUtil.chooseExileFrom(payer, (CostExile) part, source, amt, sa, true);
if (toAdd != null) {
payingCards.addAll(toAdd);
}
@@ -91,7 +91,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
return super.willPayCosts(ai, sa, cost, source);
return super.willPayCosts(payer, sa, cost, source);
}
@Override
@@ -198,13 +198,12 @@ public class ChangeZoneAi extends SpellAbilityAi {
* <p>
* changeZonePlayDrawbackAI.
* </p>
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
*
* @param sa a {@link SpellAbility} object.
* @return a boolean.
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
if (sa.isHidden()) {
return hiddenOriginPlayDrawbackAI(aiPlayer, sa);
}
@@ -2105,7 +2104,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card host = sa.getHostCard();
int lifeLoss = 0;
@@ -2132,6 +2131,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -237,15 +237,13 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
* <p>
* changeZoneAllPlayDrawbackAI.
* </p>
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param aiPlayer
* a {@link forge.game.player.Player} object.
*
*
* @param aiPlayer a {@link Player} object.
* @param sa a {@link SpellAbility} object.
* @return a boolean.
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// if putting cards from hand to library and parent is drawing cards
// make sure this will actually do something:

View File

@@ -134,7 +134,7 @@ public class ChooseCardAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if (sa.hasParam("AILogic") && !checkAiLogic(ai, sa, sa.getParam("AILogic"))) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}

View File

@@ -44,7 +44,7 @@ public class ChooseDirectionAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return canPlay(ai, sa);
}

View File

@@ -51,7 +51,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
AiAbilityDecision decision;
if (sa.isTrigger()) {
decision = doTriggerNoCost(aiPlayer, sa, sa.isMandatory());
@@ -99,7 +99,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
String unlessCost = sp.getParam("UnlessCost");
sp.setActivatingPlayer(sa.getActivatingPlayer());
Cost unless = new Cost(unlessCost, false);
if (SpellApiToAi.Converter.get(sp).willPayUnlessCost(sp, player, unless, false, new FCollection<>(player))
if (SpellApiToAi.Converter.get(sp).willPayUnlessCost(player, sp, unless, false, new FCollection<>(player))
&& ComputerUtilCost.canPayCost(unless, sp, player, true)) {
return sp;
}

View File

@@ -22,7 +22,7 @@ public class ChoosePlayerAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return canPlay(ai, sa);
}

View File

@@ -76,7 +76,7 @@ public class CloneAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// AI should only activate this during Human's turn
boolean chance = true;

View File

@@ -54,7 +54,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}
} else if (mandatory) {
AiAbilityDecision decision = chkDrawback(sa, aiPlayer);
AiAbilityDecision decision = chkDrawback(aiPlayer, sa);
if (sa.isTargetNumberValid()) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}
@@ -67,7 +67,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
if (!sa.usesTargeting()) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -299,12 +299,12 @@ public class ControlGainAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, final Player ai) {
public AiAbilityDecision chkDrawback(final Player ai, SpellAbility sa) {
final Game game = ai.getGame();
// Special card logic that is processed elsewhere
if (sa.hasParam("AILogic")) {
if (("DonateTargetPerm").equals(sa.getParam("AILogic"))) {
if ("DonateTargetPerm".equals(sa.getParam("AILogic"))) {
// Donate step 2 - target a donatable permanent.
return SpecialCardAi.Donate.considerDonatingPermanent(ai, sa);
}
@@ -346,7 +346,7 @@ public class ControlGainAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
// Pay to gain Control
if (sa.hasParam("UnlessSwitched")) {
final Card host = sa.getHostCard();
@@ -360,6 +360,6 @@ public class ControlGainAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -121,7 +121,7 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(final SpellAbility sa, final Player aiPlayer) {
public AiAbilityDecision chkDrawback(final Player aiPlayer, final SpellAbility sa) {
if ("ChainOfSmog".equals(sa.getParam("AILogic"))) {
return SpecialCardAi.ChainOfSmog.consider(aiPlayer, sa);
}
@@ -132,7 +132,7 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
AiAbilityDecision decision = canPlay(aiPlayer, sa);
if (!decision.willingToPlay()) {
if (sa.isMandatory()) {
return super.chkDrawback(sa, aiPlayer);
return super.chkDrawback(aiPlayer, sa);
}
}
return decision;
@@ -156,7 +156,7 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final String aiLogic = sa.getParam("UnlessAI");
if ("Never".equals(aiLogic)) { return false; }
@@ -172,6 +172,6 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
}
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -226,7 +226,7 @@ public class CounterAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return doTriggerNoCost(aiPlayer, sa, true);
}
@@ -345,7 +345,7 @@ public class CounterAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card source = sa.getHostCard();
final Game game = source.getGame();
List<SpellAbility> spells = AbilityUtils.getDefinedSpellAbilities(source, sa.getParamOrDefault("Defined", "Targeted"), sa);
@@ -388,6 +388,6 @@ public class CounterAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -204,7 +204,7 @@ public class CountersMoveAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if (sa.usesTargeting()) {
sa.resetTargets();
return moveTgtAI(ai, sa);

View File

@@ -93,7 +93,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if ("Always".equals(sa.getParam("AILogic"))) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -44,11 +44,11 @@ public class CountersPutAi extends CountersAi {
* forge.game.card.Card)
*/
@Override
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
protected boolean willPayCosts(Player payer, SpellAbility sa, Cost cost, Card source) {
final String type = sa.getParam("CounterType");
final String aiLogic = sa.getParamOrDefault("AILogic", "");
if (!super.willPayCosts(ai, sa, cost, source)) {
if (!super.willPayCosts(payer, sa, cost, source)) {
return false;
}
@@ -669,7 +669,7 @@ public class CountersPutAi extends CountersAi {
}
@Override
public AiAbilityDecision chkDrawback(final SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, final SpellAbility sa) {
final Game game = ai.getGame();
Card choice = null;
final String type = sa.getParam("CounterType");

View File

@@ -115,7 +115,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return canPlay(ai, sa);
}
/* (non-Javadoc)

View File

@@ -175,7 +175,7 @@ public class DamageAllAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard();
final String validP = sa.getParamOrDefault("ValidPlayers", "");

View File

@@ -38,7 +38,7 @@ import java.util.Map;
public class DamageDealAi extends DamageAiBase {
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
final SpellAbility root = sa.getRootAbility();
final String damage = sa.getParam("NumDmg");
Card source = sa.getHostCard();
@@ -1165,8 +1165,8 @@ public class DamageDealAi extends DamageAiBase {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid,
FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid,
FCollectionView<Player> payers) {
if (!payer.canLoseLife() || payer.cantLoseForZeroOrLessLife()) {
return false;
}
@@ -1185,6 +1185,6 @@ public class DamageDealAi extends DamageAiBase {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -44,7 +44,7 @@ public class DamageEachAi extends DamageAiBase {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// check AI life before playing this drawback?
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -87,7 +87,7 @@ public class DebuffAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if (!sa.usesTargeting()) {
// TODO - copied from AF_Pump.pumpDrawbackAI() - what should be here?
} else {

View File

@@ -15,7 +15,7 @@ import forge.game.zone.ZoneType;
public class DelayedTriggerAi extends SpellAbilityAi {
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if ("Always".equals(sa.getParam("AILogic"))) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -20,7 +20,7 @@ import forge.util.collect.FCollectionView;
public class DestroyAi extends SpellAbilityAi {
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return checkApiLogic(ai, sa);
}
@@ -454,7 +454,7 @@ public class DestroyAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card host = sa.getHostCard();
if (alreadyPaid) {
return false;
@@ -467,6 +467,6 @@ public class DestroyAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -32,7 +32,7 @@ public class DestroyAllAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return doMassRemovalLogic(aiPlayer, sa);
}
@@ -176,7 +176,7 @@ public class DestroyAllAi extends SpellAbilityAi {
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card source = sa.getHostCard();
if (payers.size() > 1) {
if (alreadyPaid) {
@@ -206,6 +206,6 @@ public class DestroyAllAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -100,7 +100,7 @@ public class DigAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// TODO: improve this check in ways that may be specific to a subability
return canPlay(aiPlayer, sa);
}

View File

@@ -196,7 +196,7 @@ public class DiscardAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
// Drawback AI improvements
// if parent draws cards, make sure cards in hand + cards drawn > 0
if (sa.usesTargeting()) {
@@ -219,7 +219,7 @@ public class DiscardAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card host = sa.getHostCard();
final String aiLogic = sa.getParam("UnlessAI");
if ("Never".equals(aiLogic)) { return false; }
@@ -268,6 +268,6 @@ public class DiscardAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -62,7 +62,7 @@ public class DrainManaAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
final Card source = sa.getHostCard();

View File

@@ -87,24 +87,24 @@ public class DrawAi extends SpellAbilityAi {
* forge.game.card.Card)
*/
@Override
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
if (!ComputerUtilCost.checkCreatureSacrificeCost(ai, cost, source, sa)) {
protected boolean willPayCosts(Player payer, SpellAbility sa, Cost cost, Card source) {
if (!ComputerUtilCost.checkCreatureSacrificeCost(payer, cost, source, sa)) {
return false;
}
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
if (!ComputerUtilCost.checkLifeCost(payer, cost, source, 4, sa)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source, sa)) {
AiCostDecision aiDecisions = new AiCostDecision(ai, sa, false);
if (!ComputerUtilCost.checkDiscardCost(payer, cost, source, sa)) {
AiCostDecision aiDecisions = new AiCostDecision(payer, sa, false);
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostDiscard) {
PaymentDecision decision = part.accept(aiDecisions);
if (null == decision)
return false;
for (Card discard : decision.cards) {
if (!ComputerUtil.isWorseThanDraw(ai, discard)) {
if (!ComputerUtil.isWorseThanDraw(payer, discard)) {
return false;
}
}
@@ -171,7 +171,7 @@ public class DrawAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if (targetAI(ai, sa, sa.isTrigger() && sa.getHostCard().isInPlay())) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}
@@ -558,7 +558,7 @@ public class DrawAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card host = sa.getHostCard();
final String aiLogic = sa.getParam("UnlessAI");
@@ -586,6 +586,6 @@ public class DrawAi extends SpellAbilityAi {
}
// TODO add logic for Discard + Draw Effects
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -640,7 +640,7 @@ public class EffectAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final String aiLogic = sa.getParam("UnlessAI");
if ("WillAttack".equals(aiLogic)) {
// TODO use AiController::getPredictedCombat
@@ -651,6 +651,6 @@ public class EffectAi extends SpellAbilityAi {
return false;
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -52,7 +52,7 @@ public final class EncodeAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -23,7 +23,7 @@ public class EndTurnAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) { return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); }
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) { return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); }
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)

View File

@@ -108,7 +108,7 @@ public class FightAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(final SpellAbility sa, final Player aiPlayer) {
public AiAbilityDecision chkDrawback(final Player aiPlayer, final SpellAbility sa) {
if ("Always".equals(sa.getParam("AILogic"))) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay); // e.g. Hunt the Weak, the AI logic was already checked through canFightAi
}

View File

@@ -62,7 +62,7 @@ public class FlipACoinAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return canPlay(ai, sa);
}
}

View File

@@ -141,7 +141,7 @@ public class FogAi extends SpellAbilityAi {
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
// AI should only activate this during Human's turn
boolean chance;
final Game game = ai.getGame();

View File

@@ -10,7 +10,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
// TODO: this class is largely reused from DelayedTriggerAi, consider updating
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
String logic = sa.getParamOrDefault("AILogic", "");
if (logic.equals("Always")) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);

View File

@@ -29,7 +29,7 @@ public class LearnAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return canPlay(aiPlayer, sa);
}

View File

@@ -26,27 +26,27 @@ public class LifeGainAi extends SpellAbilityAi {
* forge.game.card.Card)
*/
@Override
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
protected boolean willPayCosts(Player payer, SpellAbility sa, Cost cost, Card source) {
final Game game = source.getGame();
final PhaseHandler ph = game.getPhaseHandler();
final int life = ai.getLife();
final int life = payer.getLife();
boolean lifeCritical = life <= 5 || (ph.getPhase().isBefore(PhaseType.COMBAT_DAMAGE)
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat()));
&& ComputerUtilCombat.lifeInDanger(payer, game.getCombat()));
if (!lifeCritical) {
// return super.willPayCosts(ai, sa, cost, source);
if ("CriticalOnly".equals(sa.getParam("AILogic"))) {
return false;
}
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa, false)) {
if (!ComputerUtilCost.checkSacrificeCost(payer, cost, source, sa, false)) {
return false;
}
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
if (!ComputerUtilCost.checkLifeCost(payer, cost, source, 4, sa)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source, sa)) {
if (!ComputerUtilCost.checkDiscardCost(payer, cost, source, sa)) {
return false;
}
@@ -56,7 +56,7 @@ public class LifeGainAi extends SpellAbilityAi {
} else {
// don't sac possible blockers
if (!ph.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| !game.getCombat().getDefenders().contains(ai)) {
|| !game.getCombat().getDefenders().contains(payer)) {
boolean skipCheck = false;
// if it's a sac self cost and the effect source is not a
// creature, skip this check
@@ -64,7 +64,7 @@ public class LifeGainAi extends SpellAbilityAi {
skipCheck |= ComputerUtilCost.isSacrificeSelfCost(cost) && !source.isCreature();
if (!skipCheck) {
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa,false)) {
if (!ComputerUtilCost.checkSacrificeCost(payer, cost, source, sa,false)) {
return false;
}
}
@@ -217,7 +217,7 @@ public class LifeGainAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return doTriggerNoCost(ai, sa, true);
}

View File

@@ -28,7 +28,7 @@ public class LifeLoseAi extends SpellAbilityAi {
* SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
final Card source = sa.getHostCard();
@@ -67,24 +67,24 @@ public class LifeLoseAi extends SpellAbilityAi {
* forge.game.card.Card)
*/
@Override
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
protected boolean willPayCosts(Player payer, SpellAbility sa, Cost cost, Card source) {
final String amountStr = sa.getParam("LifeAmount");
int amount = 0;
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
// Set PayX here to maximum value.
amount = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
amount = ComputerUtilCost.getMaxXValue(sa, payer, sa.isTrigger());
} else {
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
}
// special logic for checkLifeCost
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, amount, sa)) {
if (!ComputerUtilCost.checkLifeCost(payer, cost, source, amount, sa)) {
return false;
}
// other cost as the same
return super.willPayCosts(ai, sa, cost, source);
return super.willPayCosts(payer, sa, cost, source);
}
/*
@@ -200,8 +200,8 @@ public class LifeLoseAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid,
FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid,
FCollectionView<Player> payers) {
if (!payer.canLoseLife() || payer.cantLoseForZeroOrLessLife()) {
return false;
}
@@ -224,7 +224,7 @@ public class LifeLoseAi extends SpellAbilityAi {
return true;
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
protected boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {

View File

@@ -157,7 +157,7 @@ public class MillAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return targetAI(aiPlayer, sa, true) ? new AiAbilityDecision(100, AiPlayDecision.WillPlay) : new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}

View File

@@ -46,7 +46,7 @@ public class MustBlockAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
if (sa.hasParam("DefinedAttacker")) {
// The AI can't handle "target creature blocks another target creature" abilities yet
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);

View File

@@ -80,7 +80,7 @@ public class PhasesAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {

View File

@@ -329,7 +329,7 @@ public class ProtectAi extends SpellAbilityAi {
} // protectTriggerAI
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if (sa.usesTargeting()) {
return protectTgtAI(ai, sa, false);
}

View File

@@ -665,7 +665,7 @@ public class PumpAi extends PumpAiBase {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
final SpellAbility root = sa.getRootAbility();
final Card source = sa.getHostCard();

View File

@@ -134,7 +134,7 @@ public class PumpAllAi extends PumpAiBase {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -16,7 +16,7 @@ public class RemoveFromCombatAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// AI should only activate this during Human's turn
if ("RemoveBestAttacker".equals(sa.getParam("AILogic"))) {

View File

@@ -43,7 +43,7 @@ public abstract class RevealAiBase extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
revealHandTargetAI(ai, sa, false);
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -158,7 +158,7 @@ public class RollPlanarDiceAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// for potential implementation of drawback checks?
return canPlay(aiPlayer, sa);
}

View File

@@ -32,7 +32,7 @@ public class SacrificeAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
// AI should only activate this during Human's turn
return sacrificeTgtAI(ai, sa, false);
@@ -211,7 +211,7 @@ public class SacrificeAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
// Icy Prison
if (payers.size() > 1) {
final Player p = sa.getActivatingPlayer();
@@ -221,6 +221,6 @@ public class SacrificeAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -29,7 +29,7 @@ public class SacrificeAllAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
//TODO: Add checks for bad outcome
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);

View File

@@ -68,7 +68,7 @@ public class ScryAi extends SpellAbilityAi {
} // scryTargetAI()
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return doTriggerNoCost(ai, sa, false);
}

View File

@@ -44,7 +44,7 @@ public class SetStateAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
// Gross generalization, but this always considers alternate states more powerful
return sa.getHostCard().isInAlternateState() ? new AiAbilityDecision(0, AiPlayDecision.CantPlayAi) : new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}

View File

@@ -34,7 +34,7 @@ public class ShuffleAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return shuffleTargetAI(sa);
}

View File

@@ -23,7 +23,7 @@ public class SkipTurnAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return canPlay(aiPlayer, sa);
}
}

View File

@@ -29,7 +29,7 @@ public class StoreSVarAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
// Join Forces cards
if (sa.hasParam("UnlessSwitched") && payers.size() > 1) {
final Player p = sa.getActivatingPlayer();
@@ -39,6 +39,6 @@ public class StoreSVarAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -29,7 +29,7 @@ public class SurveilAi extends SpellAbilityAi {
* @see forge.ai.SpellAbilityAi#chkAIDrawback(forge.game.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return doTriggerNoCost(ai, sa, false);
}

View File

@@ -18,6 +18,7 @@ import forge.game.zone.ZoneType;
import forge.util.collect.FCollectionView;
public class TapAi extends TapAiBase {
@Override
protected AiAbilityDecision checkApiLogic(Player ai, SpellAbility sa) {
final PhaseHandler phase = ai.getGame().getPhaseHandler();
@@ -43,7 +44,6 @@ public class TapAi extends TapAiBase {
}
final Card source = sa.getHostCard();
final Cost abCost = sa.getPayCosts();
final String aiLogic = sa.getParamOrDefault("AILogic", "");
if ("GoblinPolkaBand".equals(aiLogic)) {
@@ -52,7 +52,20 @@ public class TapAi extends TapAiBase {
return SpecialCardAi.Arena.consider(ai, sa);
}
if (!sa.usesTargeting()) {
if (sa.usesTargeting()) {
// X controls the minimum targets
if ("X".equals(sa.getTargetRestrictions().getMinTargets()) && sa.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
// TODO need to set XManaCostPaid for targets, maybe doesn't need PayX anymore?
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()));
}
sa.resetTargets();
if (tapPrefTargeting(ai, source, sa, false)) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
}
return new AiAbilityDecision(0, AiPlayDecision.TargetingFailed);
} else {
CardCollection untap;
if (sa.hasParam("CardChoices")) {
untap = CardLists.getValidCards(source.getGame().getCardsIn(ZoneType.Battlefield), sa.getParam("CardChoices"), ai, source, sa);
@@ -69,28 +82,13 @@ public class TapAi extends TapAiBase {
if (value > 0) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
} else {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
} else {
// X controls the minimum targets
if ("X".equals(sa.getTargetRestrictions().getMinTargets()) && sa.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
// TODO need to set XManaCostPaid for targets, maybe doesn't need PayX anymore?
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()));
}
sa.resetTargets();
if (tapPrefTargeting(ai, source, sa, false)) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
} else {
return new AiAbilityDecision(0, AiPlayDecision.TargetingFailed);
}
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
// Check for shocklands and similar ETB replacement effects
if (sa.hasParam("ETB")) {
final Card source = sa.getHostCard();
@@ -134,6 +132,6 @@ public class TapAi extends TapAiBase {
return true;
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -309,7 +309,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard();
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");

View File

@@ -409,7 +409,7 @@ public class TokenAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
final Card source = sa.getHostCard();
Player p = sa.getActivatingPlayer();
if (sa.isKeyword(Keyword.FABRICATE)) {
@@ -508,6 +508,6 @@ public class TokenAi extends SpellAbilityAi {
return true;
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -42,7 +42,7 @@ public class UnattachAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
return doTriggerNoCost(ai, sa, false);
}

View File

@@ -42,12 +42,12 @@ public class UntapAi extends SpellAbilityAi {
}
@Override
protected boolean willPayCosts(final Player ai, final SpellAbility sa, final Cost cost, final Card source) {
protected boolean willPayCosts(final Player payer, final SpellAbility sa, final Cost cost, final Card source) {
if (!ComputerUtilCost.checkAddM1M1CounterCost(cost, source)) {
return false;
}
return ComputerUtilCost.checkDiscardCost(ai, cost, source, sa);
return ComputerUtilCost.checkDiscardCost(payer, cost, source, sa);
}
@Override
@@ -102,7 +102,7 @@ public class UntapAi extends SpellAbilityAi {
}
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player ai) {
public AiAbilityDecision chkDrawback(Player ai, SpellAbility sa) {
if (!sa.usesTargeting()) {
// who cares if its already untapped, it's only a subability?
} else {
@@ -471,7 +471,7 @@ public class UntapAi extends SpellAbilityAi {
}
@Override
public boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
public boolean willPayUnlessCost(Player payer, SpellAbility sa, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
// Paralyze effects
if (sa.hasParam("UnlessSwitched")) {
final Card host = sa.getHostCard();
@@ -493,6 +493,6 @@ public class UntapAi extends SpellAbilityAi {
}
}
return super.willPayUnlessCost(sa, payer, cost, alreadyPaid, payers);
return super.willPayUnlessCost(payer, sa, cost, alreadyPaid, payers);
}
}

View File

@@ -44,7 +44,7 @@ public class VoteAi extends SpellAbilityAi {
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public AiAbilityDecision chkDrawback(SpellAbility sa, Player aiPlayer) {
public AiAbilityDecision chkDrawback(Player aiPlayer, SpellAbility sa) {
return canPlay(aiPlayer, sa);
}

View File

@@ -550,20 +550,20 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (sa.hasParam("Monstrosity")) {
gameCard.setMonstrous(true);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(gameCard);
// CR 701.37c
runParams.put(AbilityKey.MonstrosityAmount, counterAmount);
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false);
}
if (sa.isKeyword(Keyword.RENOWN)) {
gameCard.setRenowned(true);
game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned,
AbilityKey.mapFromCard(gameCard), false);
}
if (sa.hasParam("Adapt")) {
game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), false);
}
if (sa.isKeyword(Keyword.RENOWN)) {
gameCard.setRenowned(true);
game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, AbilityKey.mapFromCard(gameCard), false);
}
if (sa.isKeyword(Keyword.MENTOR)) {
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(gameCard);
runParams.put(AbilityKey.Source, sa.getHostCard());
runParams.put(AbilityKey.Source, card);
game.getTriggerHandler().runTrigger(TriggerType.Mentored, runParams, false);
}

View File

@@ -2,5 +2,5 @@ Name:Act of Treason
ManaCost:2 R
Types:Sorcery
A:SP$ GainControl | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.
SVar:PlayMain1:OPPONENTCREATURES
SVar:PlayMain1:ALWAYS
Oracle:Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. (It can attack and {T} this turn.)

View File

@@ -1,7 +1,7 @@
Name:Bloodsoaked Insight
ManaCost:5 BR BR
Types:Sorcery
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Activator$ You | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each 1 life your opponents have lost this turn.
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each 1 life your opponents have lost this turn.
SVar:X:Count$LifeOppsLostThisTurn
A:SP$ Dig | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top three cards of their library. Until the end of your next turn, you may play those cards. If you cast a spell this way, mana of any type can be spent to cast it.
SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STLookPlay | Duration$ UntilTheEndOfYourNextTurn | ForgetOnMoved$ Exile | SubAbility$ DBCleanup

View File

@@ -2,7 +2,7 @@ Name:Hatching Plans
ManaCost:1 U
Types:Enchantment
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME is put into a graveyard from the battlefield, draw three cards.
SVar:TrigDraw:DB$ Draw | Defined$ TriggeredCardController | NumCards$ 3
SVar:TrigDraw:DB$ Draw | NumCards$ 3
SVar:SacMe:5
AI:RemoveDeck:Random
Oracle:When Hatching Plans is put into a graveyard from the battlefield, draw three cards.

View File

@@ -3,6 +3,6 @@ ManaCost:2 R
Types:Sorcery
A:SP$ GainControl | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SubAbility$ DBScry | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. If you control a Wolf or Werewolf, scry 2.
SVar:DBScry:DB$ Scry | ScryNum$ 2 | ConditionPresent$ Wolf.YouCtrl,Werewolf.YouCtrl | StackDescription$ If you control a Wolf or Werewolf, scry 2. (Look at the top two cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)
SVar:PlayMain1:OPPONENTCREATURES
SVar:PlayMain1:ALWAYS
DeckHints:Type$Wolf|Werewolf
Oracle:Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. If you control a Wolf or Werewolf, scry 2. (Look at the top two cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)

View File

@@ -3,6 +3,6 @@ ManaCost:3 R
Types:Sorcery
A:SP$ GainControl | ValidTgts$ Creature,Artifact | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SubAbility$ DBToken | SpellDescription$ Gain control of target artifact or creature until end of turn. Untap it. It gains haste until end of turn. Create a tapped Powerstone token. (It's an artifact with "{T}: Add {C}. This mana can't be spent to cast a nonartifact spell.")
SVar:DBToken:DB$ Token | TokenTapped$ True | TokenScript$ c_a_powerstone
SVar:PlayMain1:OPPONENTCREATURES
SVar:PlayMain1:ALWAYS
DeckHas:Ability$Token & Type$Artifact
Oracle:Gain control of target artifact or creature until end of turn. Untap it. It gains haste until end of turn. Create a tapped Powerstone token. (It's an artifact with "{T}: Add {C}. This mana can't be spent to cast a nonartifact spell.")

View File

@@ -3,5 +3,5 @@ ManaCost:2 R
Types:Sorcery
S:Mode$ Continuous | CharacteristicDefining$ True | AddKeyword$ Flash | CheckSVar$ Count$CommittedCrimeThisTurn.1.0 | Description$ CARDNAME has flash as long as you've committed a crime this turn. (Targeting opponents, anything they control, and/or cards in their graveyards is a crime.)
A:SP$ GainControl | ValidTgts$ Creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.
SVar:PlayMain1:OPPONENTCREATURES
SVar:PlayMain1:ALWAYS
Oracle:Take for a Ride has flash as long as you've committed a crime this turn. (Targeting opponents, anything they control, and/or cards in their graveyards is a crime.)\nGain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.

View File

@@ -3,5 +3,5 @@ ManaCost:2 R
Types:Sorcery
A:SP$ GainControl | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SubAbility$ DBDraw | SpellDescription$ Gain control of target creature until end of turn. Untap it. It gains haste until end of turn. You may discard a card. If you do, draw a card.
SVar:DBDraw:DB$ Draw | UnlessCost$ Discard<1/Card> | UnlessSwitched$ True | UnlessPayer$ You
SVar:PlayMain1:OPPONENTCREATURES
SVar:PlayMain1:ALWAYS
Oracle:Gain control of target creature until end of turn. Untap it. It gains haste until end of turn. You may discard a card. If you do, draw a card.