mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
- Refactored AI logic for cards like Goblin Dark-Dwellers and Snapcaster Mage.
- Rewired AI logic for Goblin Dark-Dwellers to work with the new card implementation.
This commit is contained in:
@@ -2636,4 +2636,34 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
|
||||||
|
// determine and target a card with a SA that the AI can afford and will play
|
||||||
|
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
|
Card targetSpellCard = null;
|
||||||
|
for (Card c : options) {
|
||||||
|
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||||
|
if (ab.getApi() == null) {
|
||||||
|
// only API-based SAs are supported, other things may lead to a NPE (e.g. Ancestral Vision Suspend SA)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SpellAbility abTest = withoutPayingManaCost ? ab.copyWithNoManaCost() : ab.copy();
|
||||||
|
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
|
||||||
|
abTest.setActivatingPlayer(ai);
|
||||||
|
abTest.getRestrictions().setZone(c.getZone().getZoneType());
|
||||||
|
final boolean play = AiPlayDecision.WillPlay == aic.canPlaySa(abTest);
|
||||||
|
final boolean pay = ComputerUtilCost.canPayCost(abTest, ai);
|
||||||
|
if (play && pay) {
|
||||||
|
targetSpellCard = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetSpellCard == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
sa.getTargets().add(targetSpellCard);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package forge.ai.ability;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
import forge.ai.AiController;
|
||||||
|
|
||||||
import forge.ai.AiPlayDecision;
|
import forge.ai.AiPlayDecision;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.PlayerControllerAi;
|
import forge.ai.PlayerControllerAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
@@ -68,7 +70,34 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
|
final Card source = sa.getHostCard();
|
||||||
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
|
// general logic (no AILogic specified)
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
|
if (!sa.hasParam("AILogic")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardCollection cards = null;
|
||||||
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
if (tgt != null) {
|
||||||
|
ZoneType zone = tgt.getZone().get(0);
|
||||||
|
cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!sa.hasParam("Valid")) {
|
||||||
|
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("PlayWithNoManaCost".equals(sa.getParam("AILogic"))) {
|
||||||
|
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, true);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -478,11 +478,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("Snapcaster".equals(sa.getParam("AILogic"))) {
|
if ("Snapcaster".equals(sa.getParam("AILogic"))) {
|
||||||
if (!doTargetSpellToPlayLogic(ai, list, sa, false)) {
|
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if ("PlaySpellForFree".equals(sa.getParam("AILogic"))) {
|
|
||||||
if (!doTargetSpellToPlayLogic(ai, list, sa, true)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,36 +516,6 @@ public class PumpAi extends PumpAiBase {
|
|||||||
return true;
|
return true;
|
||||||
} // pumpTgtAI()
|
} // pumpTgtAI()
|
||||||
|
|
||||||
private boolean doTargetSpellToPlayLogic(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
|
|
||||||
// determine and target a card with a SA that the AI can afford and will play
|
|
||||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
|
||||||
Card targetSpellCard = null;
|
|
||||||
for (Card c : options) {
|
|
||||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
|
||||||
if (ab.getApi() == null) {
|
|
||||||
// only API-based SAs are supported, other things may lead to a NPE (e.g. Ancestral Vision Suspend SA)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SpellAbility abTest = withoutPayingManaCost ? ab.copyWithNoManaCost() : ab.copy();
|
|
||||||
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
|
|
||||||
abTest.setActivatingPlayer(ai);
|
|
||||||
abTest.getRestrictions().setZone(c.getZone().getZoneType());
|
|
||||||
final boolean play = AiPlayDecision.WillPlay == aic.canPlaySa(abTest);
|
|
||||||
final boolean pay = ComputerUtilCost.canPayCost(abTest, ai);
|
|
||||||
if (play && pay) {
|
|
||||||
targetSpellCard = c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (targetSpellCard == null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
sa.getTargets().add(targetSpellCard);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean pumpMandatoryTarget(final Player ai, final SpellAbility sa) {
|
private boolean pumpMandatoryTarget(final Player ai, final SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Creature Goblin
|
|||||||
PT:4/4
|
PT:4/4
|
||||||
K:Menace
|
K:Menace
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPlay | TriggerDescription$ When CARDNAME enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPlay | TriggerDescription$ When CARDNAME enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead.
|
||||||
SVar:TrigPlay:DB$ Play | TgtZone$ Graveyard | ValidTgts$ Instant.YouCtrl+cmcLE3,Sorcery.YouCtrl+cmcLE3 | TgtPrompt$ Choose target instant or sorcery card with converted mana cost 3 or less from your graveyard | WithoutManaCost$ True | Optional$ True | ReplaceGraveyard$ Exile
|
SVar:TrigPlay:DB$ Play | TgtZone$ Graveyard | ValidTgts$ Instant.YouCtrl+cmcLE3,Sorcery.YouCtrl+cmcLE3 | TgtPrompt$ Choose target instant or sorcery card with converted mana cost 3 or less from your graveyard | WithoutManaCost$ True | Optional$ True | ReplaceGraveyard$ Exile | AILogic$ PlayWithNoManaCost
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/goblin_dark_dwellers.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/goblin_dark_dwellers.jpg
|
||||||
Oracle:Menace\nWhen Goblin Dark-Dwellers enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead.
|
Oracle:Menace\nWhen Goblin Dark-Dwellers enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead.
|
||||||
|
|||||||
Reference in New Issue
Block a user