mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
- DelayedTriggerAi: port over AILogic NarsetRebound and SpellCopy (the latter doesn't quite work yet and the spell somehow magically fizzles with no trace).
This commit is contained in:
committed by
Michael Kamensky
parent
d5639f5395
commit
9b040b063b
@@ -970,8 +970,18 @@ public class PlayerControllerAi extends PlayerController {
|
||||
@Override
|
||||
public void orderAndPlaySimultaneousSa(List<SpellAbility> activePlayerSAs) {
|
||||
for (final SpellAbility sa : getAi().orderPlaySa(activePlayerSAs)) {
|
||||
if (prepareSingleSa(sa.getHostCard(),sa,true)) {
|
||||
if (sa.isTrigger() && prepareSingleSa(sa.getHostCard(), sa, true)) {
|
||||
ComputerUtil.playStack(sa, player, game);
|
||||
} else if (sa.isCopied()) {
|
||||
player.getGame().getStackZone().add(sa.getHostCard());
|
||||
// TODO check if static abilities needs to be run for things affecting the copy?
|
||||
if (sa.isMayChooseNewTargets() && !sa.setupTargets()) {
|
||||
// if targets can't be done, remove copy from existence
|
||||
sa.getHostCard().ceaseToExist();
|
||||
continue;
|
||||
}
|
||||
// need finally add the new spell to the stack
|
||||
player.getGame().getStack().add(sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import forge.ai.AiController;
|
||||
import forge.ai.AiPlayDecision;
|
||||
import forge.ai.PlayerControllerAi;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.ai.SpellApiToAi;
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.ai.*;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class DelayedTriggerAi extends SpellAbilityAi {
|
||||
|
||||
@@ -53,6 +57,92 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
// Card-specific logic
|
||||
String logic = sa.getParamOrDefault("AILogic", "");
|
||||
if (logic.equals("SpellCopy")) {
|
||||
// fetch Instant or Sorcery and AI has reason to play this turn
|
||||
// does not try to get itself
|
||||
final ManaCost costSa = sa.getPayCosts().getTotalMana();
|
||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!(c.isInstant() || c.isSorcery()) || c.equals(sa.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|
||||
|| ab.hasParam("AINoRecursiveCheck")) {
|
||||
// prevent infinitely recursing mana ritual and other abilities with reentry
|
||||
continue;
|
||||
} else if ("SpellCopy".equals(ab.getParam("AILogic")) && ab.getApi() == ApiType.DelayedTrigger) {
|
||||
// don't copy another copy spell, too complex for the AI
|
||||
continue;
|
||||
}
|
||||
if (!ab.canPlay()) {
|
||||
continue;
|
||||
}
|
||||
AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab);
|
||||
// see if we can pay both for this spell and for the Effect spell we're considering
|
||||
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
|
||||
ManaCost costAb = ab.getPayCosts().getTotalMana();
|
||||
ManaCost total = ManaCost.combine(costSa, costAb);
|
||||
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
|
||||
// can we pay both costs?
|
||||
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if(count == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (logic.equals("NarsetRebound")) {
|
||||
// should be done in Main2, but it might broke for other cards
|
||||
//if (phase.getPhase().isBefore(PhaseType.MAIN2)) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// fetch Instant or Sorcery without Rebound and AI has reason to play this turn
|
||||
// only need count, not the list
|
||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!(c.isInstant() || c.isSorcery()) || c.hasKeyword(Keyword.REBOUND)) {
|
||||
return false;
|
||||
}
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|
||||
|| ab.hasParam("AINoRecursiveCheck")) {
|
||||
// prevent infinitely recursing mana ritual and other abilities with reentry
|
||||
continue;
|
||||
}
|
||||
if (!ab.canPlay()) {
|
||||
continue;
|
||||
}
|
||||
AiPlayDecision decision = ((PlayerControllerAi) ai.getController()).getAi().canPlaySa(ab);
|
||||
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
|
||||
if (ComputerUtilMana.canPayManaCost(ab, ai, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generic logic
|
||||
SpellAbility trigsa = null;
|
||||
if (sa.hasAdditionalAbility("Execute")) {
|
||||
trigsa = sa.getAdditionalAbility("Execute");
|
||||
|
||||
@@ -7,14 +7,11 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.ai.*;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -109,89 +106,6 @@ public class EffectAi extends SpellAbilityAi {
|
||||
|| CardLists.getType(ai.getCardsIn(ZoneType.Battlefield), "Planeswalker").isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
randomReturn = true;
|
||||
} else if (logic.equals("SpellCopy")) {
|
||||
// fetch Instant or Sorcery and AI has reason to play this turn
|
||||
// does not try to get itself
|
||||
final ManaCost costSa = sa.getPayCosts().getTotalMana();
|
||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!(c.isInstant() || c.isSorcery()) || c.equals(sa.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|
||||
|| ab.hasParam("AINoRecursiveCheck")) {
|
||||
// prevent infinitely recursing mana ritual and other abilities with reentry
|
||||
continue;
|
||||
} else if ("SpellCopy".equals(ab.getParam("AILogic")) && ab.getApi() == ApiType.Effect) {
|
||||
// don't copy another copy spell, too complex for the AI
|
||||
continue;
|
||||
}
|
||||
if (!ab.canPlay()) {
|
||||
continue;
|
||||
}
|
||||
AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab);
|
||||
// see if we can pay both for this spell and for the Effect spell we're considering
|
||||
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
|
||||
ManaCost costAb = ab.getPayCosts().getTotalMana();
|
||||
ManaCost total = ManaCost.combine(costSa, costAb);
|
||||
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
|
||||
// can we pay both costs?
|
||||
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if(count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
randomReturn = true;
|
||||
} else if (logic.equals("NarsetRebound")) {
|
||||
// should be done in Main2, but it might broke for other cards
|
||||
//if (phase.getPhase().isBefore(PhaseType.MAIN2)) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// fetch Instant or Sorcery without Rebound and AI has reason to play this turn
|
||||
// only need count, not the list
|
||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!(c.isInstant() || c.isSorcery()) || c.hasKeyword(Keyword.REBOUND)) {
|
||||
return false;
|
||||
}
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|
||||
|| ab.hasParam("AINoRecursiveCheck")) {
|
||||
// prevent infinitely recursing mana ritual and other abilities with reentry
|
||||
continue;
|
||||
}
|
||||
if (!ab.canPlay()) {
|
||||
continue;
|
||||
}
|
||||
AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab);
|
||||
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
|
||||
if (ComputerUtilMana.canPayManaCost(ab, ai, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if(count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
randomReturn = true;
|
||||
} else if (logic.equals("Always")) {
|
||||
randomReturn = true;
|
||||
|
||||
Reference in New Issue
Block a user