mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
CardFactoryUtil: Miracle is a Trigger now, and make PlayAi a bit better to check if it can play it
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user