CardFactoryUtil: Miracle is a Trigger now, and make PlayAi a bit better to check if it can play it

This commit is contained in:
Hanmac
2016-11-18 19:55:05 +00:00
parent 959fd5471e
commit 3c5eb2248b
9 changed files with 119 additions and 172 deletions

View File

@@ -18,7 +18,6 @@
package forge.ai;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -974,40 +973,6 @@ public class AiController {
return Boolean.parseBoolean(prop);
}
/** Returns the spell ability which has already been played - use it for reference only */
public SpellAbility chooseAndPlaySa(boolean mandatory, boolean withoutPayingManaCost, final SpellAbility... list) {
return chooseAndPlaySa(Arrays.asList(list), mandatory, withoutPayingManaCost);
}
/** Returns the spell ability which has already been played - use it for reference only */
public SpellAbility chooseAndPlaySa(final List<SpellAbility> choices, boolean mandatory, boolean withoutPayingManaCost) {
for (final SpellAbility sa : choices) {
sa.setActivatingPlayer(player);
//Spells
if (sa instanceof Spell) {
if (AiPlayDecision.WillPlay != canPlayFromEffectAI((Spell) sa, mandatory, withoutPayingManaCost)) {
continue;
}
}
else {
if (AiPlayDecision.WillPlay == canPlaySa(sa)) {
continue;
}
}
if (withoutPayingManaCost) {
ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, sa, game);
}
else if (!ComputerUtilCost.canPayCost(sa, player)) {
continue;
}
else {
ComputerUtil.playStack(sa, player, game);
}
return sa;
}
return null;
}
public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) {
final Card card = spell.getHostCard();

View File

@@ -1,5 +1,12 @@
package forge.ai;
import java.security.InvalidParameterException;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@@ -44,17 +51,10 @@ import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.ITriggerEvent;
import forge.util.MyRandom;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.security.InvalidParameterException;
import java.util.*;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
/**
@@ -79,6 +79,7 @@ public class PlayerControllerAi extends PlayerController {
brains.setUseSimulation(value);
}
@Override
public SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
if (abilities.size() == 0) {
return null;
@@ -314,11 +315,6 @@ public class PlayerControllerAi extends PlayerController {
ComputerUtil.playNoStack(player, effectSA, game);
}
@Override
public void playMiracle(SpellAbility miracle, Card card) {
getAi().chooseAndPlaySa(false, false, miracle);
}
@Override
public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) {
return getAi().chooseCardsToDelve(genericAmount, grave);
@@ -436,6 +432,7 @@ public class PlayerControllerAi extends PlayerController {
return brains.chooseNumber(sa, title, min, max);
}
@Override
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
return brains.chooseNumber(sa, title, options, relatedPlayer);
}

View File

@@ -1,14 +1,18 @@
package forge.ai.ability;
import java.util.List;
import com.google.common.base.Predicate;
import forge.ai.AiPlayDecision;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.PlayerControllerAi;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.player.Player;
@@ -17,64 +21,38 @@ import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class PlayAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Cost abCost = sa.getPayCosts();
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
final Game game = ai.getGame();
final Card source = sa.getHostCard();
final Random r = MyRandom.getRandom();
if (abCost != null) {
// AI currently disabled for these costs
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
return false;
}
}
// don't use this as a response
if (!ai.getGame().getStack().isEmpty()) {
if (!game.getStack().isEmpty()) {
return false;
}
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getRestrictions().getNumberTurnActivations());
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false; // prevent infinite loop
}
List<Card> cards;
CardCollection cards;
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
ZoneType zone = tgt.getZone().get(0);
cards = CardLists.getValidCards(ai.getGame().getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
if (cards.isEmpty()) {
return false;
}
sa.getTargets().add(ComputerUtilCard.getBestAI(cards));
} else if (!sa.hasParam("Valid")) {
cards = new ArrayList<Card>(AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa));
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
if (cards.isEmpty()) {
return false;
}
}
return chance;
return true;
}
/**
@@ -90,9 +68,7 @@ public class PlayAi extends SpellAbilityAi {
*/
@Override
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
return false;
}
@@ -112,7 +88,8 @@ public class PlayAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -122,6 +99,16 @@ public class PlayAi extends SpellAbilityAi {
// timing restrictions still apply
if (!s.getRestrictions().checkTimingRestrictions(c, s))
continue;
if (sa.hasParam("PlayCost")) {
Cost abCost;
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
abCost = new Cost(c.getManaCost(), false);
} else {
abCost = new Cost(sa.getParam("PlayCost"), false);
}
spell = (Spell) spell.copyWithDefinedCost(abCost);
}
if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, false, true)) {
return true;
}

View File

@@ -213,15 +213,17 @@ public class PlayEffect extends SpellAbilityEffect {
final boolean noManaCost = sa.hasParam("WithoutManaCost");
if (noManaCost) {
tgtSA = tgtSA.copyWithNoManaCost();
} else if (sa.hasParam("PlayMadness")) {
} else if (sa.hasParam("PlayCost")) {
Cost abCost;
if ("ManaCost".equals(sa.getParam("PlayMadness"))) {
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
abCost = new Cost(source.getManaCost(), false);
} else {
abCost = new Cost(sa.getParam("PlayMadness"), false);
abCost = new Cost(sa.getParam("PlayCost"), false);
}
tgtSA = tgtSA.copyWithDefinedCost(abCost);
}
if (sa.hasParam("Madness")) {
tgtSA.getHostCard().setMadness(true);
}

View File

@@ -211,7 +211,6 @@ public class Card extends GameEntity implements Comparable<Card> {
private NavigableMap<Long, Player> tempControllers = Maps.newTreeMap();
private String originalText = "", text = "";
private Cost miracleCost = null;
private String chosenType = "";
private List<String> chosenColors;
private String namedCard = "";
@@ -3471,13 +3470,6 @@ public class Card extends GameEntity implements Comparable<Card> {
unearthed = b;
}
public final Cost getMiracleCost() {
return miracleCost;
}
public final void setMiracleCost(final Cost cost) {
miracleCost = cost;
}
public final boolean hasSuspend() {
return suspend;
}

View File

@@ -2282,6 +2282,9 @@ public class CardFactoryUtil {
else if (keyword.equals("Ingest")) {
addTriggerAbility(keyword, card, null);
}
else if (keyword.startsWith("Miracle")) {
addTriggerAbility(keyword, card, null);
}
else if (keyword.equals("Persist")) {
addTriggerAbility(keyword, card, null);
}
@@ -2981,9 +2984,9 @@ public class CardFactoryUtil {
"Execute$ " + trigPlay + " | Secondary$ True | TriggerDescription$ " +
"Play Madness " + ManaCostParser.parse(manacost) + " - " + card.getName();
final String playMadness = "AB$ Play | Cost$ 0 | Defined$ Self | PlayMadness$ " + manacost +
final String playMadness = "AB$ Play | Cost$ 0 | Defined$ Self | PlayCost$ " + manacost +
" | ConditionDefined$ Self | ConditionPresent$ Card.StrictlySelf+inZoneExile" +
" | Optional$ True | SubAbility$ DBWasNotPlayMadness | RememberPlayed$ True";
" | Optional$ True | SubAbility$ DBWasNotPlayMadness | RememberPlayed$ True | Madness$ True";
final String moveToYard = "DB$ ChangeZone | Defined$ Self.StrictlySelf | Origin$ Exile | " +
"Destination$ Graveyard | ConditionDefined$ Remembered | ConditionPresent$" +
" Card | ConditionCompare$ EQ0 | SubAbility$ DBMadnessCleanup";
@@ -3006,6 +3009,22 @@ public class CardFactoryUtil {
card.setSVar("MeleePump", effect);
card.setSVar("MeleeX", "TriggeredPlayersDefenders$Amount");
final Trigger trigger = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
final Trigger cardTrigger = card.addTrigger(trigger);
if (!intrinsic) {
kws.addTrigger(cardTrigger);
}
} else if (keyword.startsWith("Miracle")) {
final String[] k = keyword.split(":");
final String manacost = k[1];
final String abStr = "DB$ Play | Defined$ Self | PlayCost$ " + manacost;
final String trigStr = "Mode$ Drawn | ValidCard$ Card.Self | Miracle$ True | Secondary$ True "
+ "| Static$ True | TriggerDescription$ CARDNAME - Miracle";
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
trigger.setOverridingAbility(AbilityFactory.getAbility(abStr, card));
final Trigger cardTrigger = card.addTrigger(trigger);
if (!intrinsic) {
kws.addTrigger(cardTrigger);
@@ -4082,17 +4101,6 @@ public class CardFactoryUtil {
addTriggerAbility(parse, card, null);
} // madness
if (hasKeyword(card, "Miracle") != -1) {
final int n = hasKeyword(card, "Miracle");
if (n != -1) {
final String parse = card.getKeywords().get(n).toString();
// card.removeIntrinsicKeyword(parse);
final String[] k = parse.split(":");
card.setMiracleCost(new Cost(k[1], false));
}
} // miracle
if (hasKeyword(card, "Devour") != -1) {
final int n = hasKeyword(card, "Devour");
addReplacementEffect(card.getKeywords().get(n), card, null);

View File

@@ -17,6 +17,8 @@
*/
package forge.game.trigger;
import forge.game.Game;
import forge.game.GameStage;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
@@ -64,6 +66,13 @@ public class TriggerDrawn extends Trigger {
return false;
}
}
if (this.mapParams.containsKey("Miracle")) {
final Game game = this.getHostCard().getGame();
if (number != 1 || game.getAge() == GameStage.Mulligan) {
return false;
}
}
return true;
}

View File

@@ -268,11 +268,6 @@ public class PlayerControllerForTests extends PlayerController {
return chooseItems(validCards, min);
}
@Override
public void playMiracle(SpellAbility miracle, Card card) {
throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!");
}
@Override
public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) {
return CardCollection.EMPTY;

View File

@@ -694,14 +694,6 @@ public class PlayerControllerHuman
return new CardCollection(inp.getSelected());
}
@Override
public void playMiracle(final SpellAbility miracle, final Card card) {
final CardView view = CardView.get(card);
if (getGui().confirm(view, view + " - Drawn. Play for Miracle Cost?")) {
HumanPlay.playSpellAbility(this, player, miracle);
}
}
@Override
public CardCollectionView chooseCardsToDelve(final int genericAmount, final CardCollection grave) {
final int cardsInGrave = Math.min(genericAmount, grave.size());