mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Merge branch 'manifestChoices' into 'master'
Manifest: add Choices See merge request core-developers/forge!2020
This commit is contained in:
@@ -1,12 +1,17 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
@@ -83,13 +88,67 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean shouldManyfest(final Card card, final Player ai, final SpellAbility sa) {
|
||||||
|
final Game game = ai.getGame();
|
||||||
|
// check to ensure that there are no replacement effects that prevent creatures ETBing from library
|
||||||
|
// (e.g. Grafdigger's Cage)
|
||||||
|
Card topCopy = CardUtil.getLKICopy(card);
|
||||||
|
topCopy.turnFaceDownNoUpdate();
|
||||||
|
topCopy.setManifested(true);
|
||||||
|
|
||||||
|
final Map<String, Object> repParams = Maps.newHashMap();
|
||||||
|
repParams.put("Event", "Moved");
|
||||||
|
repParams.put("Affected", topCopy);
|
||||||
|
repParams.put("Origin", card.getZone().getZoneType());
|
||||||
|
repParams.put("Destination", ZoneType.Battlefield);
|
||||||
|
repParams.put("Source", sa.getHostCard());
|
||||||
|
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams, ReplacementLayer.Other);
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.mayPlayerLook(ai)) {
|
||||||
|
// try to avoid manifest a non Permanent
|
||||||
|
if (!card.isPermanent())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// do not manifest a card with X in its cost
|
||||||
|
if (card.getManaCost().countX() > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// try to avoid manifesting a creature with zero or less thoughness
|
||||||
|
if (card.isCreature() && card.getNetToughness() <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// card has ETBTrigger or ETBReplacement
|
||||||
|
if (card.hasETBTrigger(false) || card.hasETBReplacement()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("Choices") || sa.hasParam("ChoiceZone")) {
|
||||||
|
ZoneType choiceZone = ZoneType.Hand;
|
||||||
|
if (sa.hasParam("ChoiceZone")) {
|
||||||
|
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||||
|
}
|
||||||
|
CardCollection choices = new CardCollection(game.getCardsIn(choiceZone));
|
||||||
|
if (sa.hasParam("Choices")) {
|
||||||
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), ai, host);
|
||||||
|
}
|
||||||
|
if (choices.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Library is empty, no Manifest
|
// Library is empty, no Manifest
|
||||||
final CardCollectionView library = ai.getCardsIn(ZoneType.Library);
|
final CardCollectionView library = ai.getCardsIn(ZoneType.Library);
|
||||||
if (library.isEmpty())
|
if (library.isEmpty())
|
||||||
@@ -100,44 +159,10 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to ensure that there are no replacement effects that prevent creatures ETBing from library
|
if (!shouldManyfest(library.getFirst(), ai, sa)) {
|
||||||
// (e.g. Grafdigger's Cage)
|
|
||||||
Card topCopy = CardUtil.getLKICopy(library.getFirst());
|
|
||||||
topCopy.turnFaceDownNoUpdate();
|
|
||||||
topCopy.setManifested(true);
|
|
||||||
|
|
||||||
final Map<String, Object> repParams = Maps.newHashMap();
|
|
||||||
repParams.put("Event", "Moved");
|
|
||||||
repParams.put("Affected", topCopy);
|
|
||||||
repParams.put("Origin", ZoneType.Library);
|
|
||||||
repParams.put("Destination", ZoneType.Battlefield);
|
|
||||||
repParams.put("Source", sa.getHostCard());
|
|
||||||
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams, ReplacementLayer.Other);
|
|
||||||
if (!list.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the AI can see the top card of the library, check it
|
|
||||||
final Card topCard = library.getFirst();
|
|
||||||
if (topCard.mayPlayerLook(ai)) {
|
|
||||||
// try to avoid manifest a non Permanent
|
|
||||||
if (!topCard.isPermanent())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// do not manifest a card with X in its cost
|
|
||||||
if (topCard.getManaCost().countX() > 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// try to avoid manifesting a creature with zero or less thoughness
|
|
||||||
if (topCard.isCreature() && topCard.getNetToughness() <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// card has ETBTrigger or ETBReplacement
|
|
||||||
if (topCard.hasETBTrigger(false) || topCard.hasETBReplacement()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Probably should be a little more discerning on playing during OPPs turn
|
// Probably should be a little more discerning on playing during OPPs turn
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -152,4 +177,26 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
return MyRandom.getRandom().nextFloat() < .8;
|
return MyRandom.getRandom().nextFloat() < .8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
|
if (Iterables.size(options) <= 1) {
|
||||||
|
return Iterables.getFirst(options, null);
|
||||||
|
}
|
||||||
|
CardCollection filtered = CardLists.filter(options, new Predicate<Card>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Card input) {
|
||||||
|
if (shouldManyfest(input, ai, sa)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!filtered.isEmpty()) {
|
||||||
|
return ComputerUtilCard.getBestAI(filtered);
|
||||||
|
}
|
||||||
|
if (isOptional) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Iterables.getFirst(options, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class ManifestEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
final Player activator = sa.getActivatingPlayer();
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
// Usually a number leaving possibility for X, Sacrifice X land: Manifest X creatures.
|
// Usually a number leaving possibility for X, Sacrifice X land: Manifest X creatures.
|
||||||
final int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(source,
|
final int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(source,
|
||||||
@@ -28,7 +29,22 @@ public class ManifestEffect extends SpellAbilityEffect {
|
|||||||
for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) {
|
for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) {
|
||||||
if (sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
if (sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
||||||
CardCollection tgtCards;
|
CardCollection tgtCards;
|
||||||
if ("TopOfLibrary".equals(defined)) {
|
if (sa.hasParam("Choices") || sa.hasParam("ChoiceZone")) {
|
||||||
|
ZoneType choiceZone = ZoneType.Hand;
|
||||||
|
if (sa.hasParam("ChoiceZone")) {
|
||||||
|
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||||
|
}
|
||||||
|
CardCollection choices = new CardCollection(game.getCardsIn(choiceZone));
|
||||||
|
if (sa.hasParam("Choices")) {
|
||||||
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, source);
|
||||||
|
}
|
||||||
|
if (choices.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose cards to manifest ";
|
||||||
|
tgtCards = new CardCollection(activator.getController().chooseEntitiesForEffect(choices, amount, amount, null, sa, title, p));
|
||||||
|
} else if ("TopOfLibrary".equals(defined)) {
|
||||||
tgtCards = p.getTopXCardsFromLibrary(amount);
|
tgtCards = p.getTopXCardsFromLibrary(amount);
|
||||||
} else {
|
} else {
|
||||||
tgtCards = getTargetCards(sa);
|
tgtCards = getTargetCards(sa);
|
||||||
|
|||||||
5
forge-gui/res/cardsfolder/upcoming/scroll_of_fate.txt
Normal file
5
forge-gui/res/cardsfolder/upcoming/scroll_of_fate.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Name:Scroll of Fate
|
||||||
|
ManaCost:3
|
||||||
|
Types:Artifact
|
||||||
|
A:AB$ Manifest | Cost$ T | Choices$ Card.YouCtrl | ChoiceZone$ Hand | SpellDescription$ Manifest a card from your hand. (Put that card onto the battlefield face down as a 2/2 creature. Turn it face up at any time for its mana cost if it's a creature card.)
|
||||||
|
Oracle:{T}: Manifest a card from your hand. (Put that card onto the battlefield face down as a 2/2 creature. Turn it face up any time for its mana cost if it's a creature card.)
|
||||||
Reference in New Issue
Block a user