mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
- Improved the AI logic for MustBlock a little.
- Enabled several cards for the AI. - Implemented Sisters of Stone Death's 3rd ability similar to Tomb of the Dusk Rose which the AI can properly work with (seems functionally identical). - Fixed the AI logic spec for Tomb of the Dusk Rose.
This commit is contained in:
@@ -2,15 +2,16 @@ package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.ComputerUtilCombat;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.ai.*;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -23,14 +24,45 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||
// disabled for the AI until he/she can make decisions about who to make
|
||||
// block
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = aiPlayer.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
final boolean onlyLethal = !"AllowNonLethal".equals(sa.getParam("AILogic"));
|
||||
|
||||
if (combat == null || !combat.isAttacking(source)) {
|
||||
return false;
|
||||
} else if (AiCardMemory.isRememberedCard(aiPlayer, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) {
|
||||
// The AI can meaningfully do it only to one creature per card yet, trying to do it to multiple cards
|
||||
// may result in overextending and losing the attacker
|
||||
return false;
|
||||
}
|
||||
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
final List<Card> list = determineGoodBlockers(source, aiPlayer, combat.getDefenderPlayerByAttacker(source), sa, onlyLethal,false);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
final Card blocker = ComputerUtilCard.getBestCreatureAI(list);
|
||||
if (blocker == null) {
|
||||
return false;
|
||||
}
|
||||
sa.getTargets().add(blocker);
|
||||
AiCardMemory.rememberCard(aiPlayer, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||
return false;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
// The AI can't handle "target creature blocks another target creature" abilities yet
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise it's a standard targeted "target creature blocks CARDNAME" ability, so use the main canPlayAI code path
|
||||
return canPlayAI(aiPlayer, sa);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,27 +94,7 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
boolean chance = false;
|
||||
|
||||
if (abTgt != null) {
|
||||
List<Card> list = CardLists.filter(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
boolean tapped = c.isTapped();
|
||||
c.setTapped(false);
|
||||
if (!CombatUtil.canBlock(definedAttacker, c)) {
|
||||
return false;
|
||||
}
|
||||
if (ComputerUtilCombat.canDestroyAttacker(ai, definedAttacker, c, null, false)) {
|
||||
return false;
|
||||
}
|
||||
if (!ComputerUtilCombat.canDestroyBlocker(ai, c, definedAttacker, null, false)) {
|
||||
return false;
|
||||
}
|
||||
c.setTapped(tapped);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
final List<Card> list = determineGoodBlockers(definedAttacker, ai, ComputerUtil.getOpponentFor(ai), sa, true,true);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -98,4 +110,39 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
return chance;
|
||||
}
|
||||
|
||||
private List<Card> determineGoodBlockers(Card attacker, Player ai, Player defender, SpellAbility sa, boolean onlyLethal, boolean testTapped) {
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
|
||||
List<Card> list = Lists.newArrayList();
|
||||
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
boolean tapped = c.isTapped();
|
||||
if (testTapped) {
|
||||
c.setTapped(false);
|
||||
}
|
||||
if (!CombatUtil.canBlock(attacker, c)) {
|
||||
return false;
|
||||
}
|
||||
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, c, null, false)) {
|
||||
return false;
|
||||
}
|
||||
if (onlyLethal && !ComputerUtilCombat.canDestroyBlocker(ai, c, attacker, null, false)) {
|
||||
return false;
|
||||
}
|
||||
if (testTapped) {
|
||||
c.setTapped(tapped);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user