mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18: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 com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.*;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.game.Game;
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
|
import forge.game.combat.Combat;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -23,14 +24,45 @@ public class MustBlockAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
// disabled for the AI until he/she can make decisions about who to make
|
final Card source = sa.getHostCard();
|
||||||
// block
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
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
|
@Override
|
||||||
@@ -62,27 +94,7 @@ public class MustBlockAi extends SpellAbilityAi {
|
|||||||
boolean chance = false;
|
boolean chance = false;
|
||||||
|
|
||||||
if (abTgt != null) {
|
if (abTgt != null) {
|
||||||
List<Card> list = CardLists.filter(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
final List<Card> list = determineGoodBlockers(definedAttacker, ai, ComputerUtil.getOpponentFor(ai), sa, true,true);
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -98,4 +110,39 @@ public class MustBlockAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
return chance;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ Types:Creature Cat Beast
|
|||||||
PT:3/5
|
PT:3/5
|
||||||
A:AB$ MustBlock | Cost$ 2 G | ValidTgts$ Creature | CheckSVar$ FormidableTest | SVarCompare$ GE8 | References$ FormidableTest | PrecostDesc$ Formidable — | TgtPrompt$ Select target creature that must block this creature this turn | SpellDescription$ Target creature blocks CARDNAME this turn if able. Activate this ability only if creatures you control have total power 8 or greater.
|
A:AB$ MustBlock | Cost$ 2 G | ValidTgts$ Creature | CheckSVar$ FormidableTest | SVarCompare$ GE8 | References$ FormidableTest | PrecostDesc$ Formidable — | TgtPrompt$ Select target creature that must block this creature this turn | SpellDescription$ Target creature blocks CARDNAME this turn if able. Activate this ability only if creatures you control have total power 8 or greater.
|
||||||
SVar:FormidableTest:Count$SumPower_Creature.YouCtrl
|
SVar:FormidableTest:Count$SumPower_Creature.YouCtrl
|
||||||
AI:RemoveDeck:All
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/lurking_arynx.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/lurking_arynx.jpg
|
||||||
Oracle:Formidable — {2}{G}: Target creature blocks Lurking Arynx this turn if able. Activate this ability only if creatures you control have total power 8 or greater.
|
Oracle:Formidable — {2}{G}: Target creature blocks Lurking Arynx this turn if able. Activate this ability only if creatures you control have total power 8 or greater.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Matsu-Tribe Decoy
|
|||||||
ManaCost:2 G
|
ManaCost:2 G
|
||||||
Types:Creature Snake Warrior
|
Types:Creature Snake Warrior
|
||||||
PT:1/3
|
PT:1/3
|
||||||
A:AB$ MustBlock | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
A:AB$ MustBlock | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ AllowNonLethal | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
||||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Creature | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigTap | TriggerDescription$ Whenever CARDNAME deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step.
|
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Creature | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigTap | TriggerDescription$ Whenever CARDNAME deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step.
|
||||||
SVar:TrigTap:DB$ Tap | Defined$ TriggeredTarget | SubAbility$ DBPump
|
SVar:TrigTap:DB$ Tap | Defined$ TriggeredTarget | SubAbility$ DBPump
|
||||||
SVar:DBPump:DB$ Pump | Defined$ TriggeredTarget | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True | IsCurse$ True
|
SVar:DBPump:DB$ Pump | Defined$ TriggeredTarget | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True | IsCurse$ True
|
||||||
|
|||||||
@@ -7,14 +7,13 @@ AlternateMode:DoubleFaced
|
|||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/profane_procession.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/profane_procession.jpg
|
||||||
Oracle:{3}{W}{B}: Exile target creature. Then if there are three or more cards exiled with Profane Procession, transform it.
|
Oracle:{3}{W}{B}: Exile target creature. Then if there are three or more cards exiled with Profane Procession, transform it.
|
||||||
|
|
||||||
//Not sure if ExiledWithSource and IsRemembered persist through transformation, but for this card, it's absolutely vital that they do.
|
|
||||||
ALTERNATE
|
ALTERNATE
|
||||||
|
|
||||||
Name:Tomb of the Dusk Rose
|
Name:Tomb of the Dusk Rose
|
||||||
ManaCost:no cost
|
ManaCost:no cost
|
||||||
Types:Legendary Land
|
Types:Legendary Land
|
||||||
A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color.
|
A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color.
|
||||||
A:AB$ ChooseCard | Cost$ 2 W B T | Choices$ Creature.IsRemembered+ExiledWithSource | ChoiceZone$ Exile | SubAbility$ DBChangeZone | SpellDescription$ Put a creature card exiled with this permanent onto the battlefield under your control.
|
A:AB$ ChooseCard | Cost$ 2 W B T | Choices$ Creature.IsRemembered+ExiledWithSource | ChoiceZone$ Exile | SubAbility$ DBChangeZone | AILogic$ AtLeast1 | Mandatory$ True | SpellDescription$ Put a creature card exiled with this permanent onto the battlefield under your control.
|
||||||
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.IsRemembered+ExiledWithSource | ChangeNum$ 1 | GainControl$ True
|
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.IsRemembered+ExiledWithSource | ChangeNum$ 1 | GainControl$ True
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/tomb_of_the_dusk_rose.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/tomb_of_the_dusk_rose.jpg
|
||||||
Oracle:(Transforms from Profane Procession.)\n{T}: Add one mana of any color.\n{2}{W}{B},{T}: Put a creature card exiled with this permanent onto the battlefield under your control.
|
Oracle:(Transforms from Profane Procession.)\n{T}: Add one mana of any color.\n{2}{W}{B},{T}: Put a creature card exiled with this permanent onto the battlefield under your control.
|
||||||
@@ -3,7 +3,5 @@ ManaCost:3 W
|
|||||||
Types:Creature Elephant
|
Types:Creature Elephant
|
||||||
PT:2/2
|
PT:2/2
|
||||||
A:AB$ MustBlock | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
A:AB$ MustBlock | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
||||||
AI:RemoveDeck:All
|
|
||||||
AI:RemoveDeck:Random
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/rampant_elephant.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/rampant_elephant.jpg
|
||||||
Oracle:{G}: Target creature blocks Rampant Elephant this turn if able.
|
Oracle:{G}: Target creature blocks Rampant Elephant this turn if able.
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ Types:Legendary Creature Gorgon
|
|||||||
PT:7/5
|
PT:7/5
|
||||||
A:AB$ MustBlock | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
A:AB$ MustBlock | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
||||||
A:AB$ ChangeZone | Cost$ B G | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.blockingSource,Creature.blockedBySource | TgtPrompt$ Select target creature blocking Sisters of Stone Death | RememberTargets$ True | SpellDescription$ Exile target creature blocking or blocked by CARDNAME.
|
A:AB$ ChangeZone | Cost$ B G | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.blockingSource,Creature.blockedBySource | TgtPrompt$ Select target creature blocking Sisters of Stone Death | RememberTargets$ True | SpellDescription$ Exile target creature blocking or blocked by CARDNAME.
|
||||||
A:AB$ ChangeZone | Cost$ 2 B | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.IsRemembered+ExiledWithSource | Hidden$ True | GainControl$ True | SpellDescription$ Put a creature card exiled with CARDNAME onto the battlefield under your control.
|
A:AB$ ChooseCard | Cost$ 2 B | Choices$ Creature.IsRemembered+ExiledWithSource | ChoiceZone$ Exile | AILogic$ AtLeast1 | Mandatory$ True | SubAbility$ DBChangeZone | SpellDescription$ Put a creature card exiled with CARDNAME onto the battlefield under your control.
|
||||||
|
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.IsRemembered+ExiledWithSource | ChangeNum$ 1 | GainControl$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
|
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ DBCleanup | Static$ True | Secondary$ True | TriggerDescription$ Forget all remembered cards.
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ DBCleanup | Static$ True | Secondary$ True | TriggerDescription$ Forget all remembered cards.
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
AI:RemoveDeck:All
|
|
||||||
AI:RemoveDeck:Random
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/sisters_of_stone_death.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/sisters_of_stone_death.jpg
|
||||||
Oracle:{G}: Target creature blocks Sisters of Stone Death this turn if able.\n{B}{G}: Exile target creature blocking or blocked by Sisters of Stone Death.\n{2}{B}: Put a creature card exiled with Sisters of Stone Death onto the battlefield under your control.
|
Oracle:{G}: Target creature blocks Sisters of Stone Death this turn if able.\n{B}{G}: Exile target creature blocking or blocked by Sisters of Stone Death.\n{2}{B}: Put a creature card exiled with Sisters of Stone Death onto the battlefield under your control.
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ ManaCost:3 G
|
|||||||
Types:Creature Horror
|
Types:Creature Horror
|
||||||
PT:1/5
|
PT:1/5
|
||||||
K:Infect
|
K:Infect
|
||||||
A:AB$ MustBlock | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
A:AB$ MustBlock | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ AllowNonLethal | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
||||||
AI:RemoveDeck:All
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/tangle_angler.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/tangle_angler.jpg
|
||||||
Oracle:Infect (This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.)\n{G}: Target creature blocks Tangle Angler this turn if able.
|
Oracle:Infect (This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.)\n{G}: Target creature blocks Tangle Angler this turn if able.
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ ManaCost:3 G
|
|||||||
Types:Creature Elephant
|
Types:Creature Elephant
|
||||||
PT:3/3
|
PT:3/3
|
||||||
A:AB$ MustBlock | Cost$ 1 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
A:AB$ MustBlock | Cost$ 1 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Target creature blocks CARDNAME this turn if able.
|
||||||
AI:RemoveDeck:All
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/trumpeting_armodon.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/trumpeting_armodon.jpg
|
||||||
Oracle:{1}{G}: Target creature blocks Trumpeting Armodon this turn if able.
|
Oracle:{1}{G}: Target creature blocks Trumpeting Armodon this turn if able.
|
||||||
|
|||||||
Reference in New Issue
Block a user