mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
- Update for ProtectAI. AI can now use Protect effects to 1) counter certain targeted spells 2) protect against lethal damage 3) create an unblockable attacker depending on expected damage
This commit is contained in:
@@ -23,6 +23,7 @@ import com.google.common.collect.Lists;
|
|||||||
import forge.ai.ability.AnimateAi;
|
import forge.ai.ability.AnimateAi;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
|
import forge.game.ability.effects.ProtectEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardFactory;
|
import forge.game.card.CardFactory;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
@@ -82,7 +83,7 @@ public class AiAttackController {
|
|||||||
public AiAttackController(final Player ai) {
|
public AiAttackController(final Player ai) {
|
||||||
this.ai = ai;
|
this.ai = ai;
|
||||||
this.defendingOpponent = choosePreferredDefenderPlayer();
|
this.defendingOpponent = choosePreferredDefenderPlayer();
|
||||||
getOpponentCreatures();
|
this.oppList = getOpponentCreatures(this.defendingOpponent);
|
||||||
this.myList = ai.getCreaturesInPlay();
|
this.myList = ai.getCreaturesInPlay();
|
||||||
this.attackers = new ArrayList<Card>();
|
this.attackers = new ArrayList<Card>();
|
||||||
for (Card c : myList) {
|
for (Card c : myList) {
|
||||||
@@ -96,7 +97,7 @@ public class AiAttackController {
|
|||||||
public AiAttackController(final Player ai, Card attacker) {
|
public AiAttackController(final Player ai, Card attacker) {
|
||||||
this.ai = ai;
|
this.ai = ai;
|
||||||
this.defendingOpponent = choosePreferredDefenderPlayer();
|
this.defendingOpponent = choosePreferredDefenderPlayer();
|
||||||
getOpponentCreatures();
|
this.oppList = getOpponentCreatures(this.defendingOpponent);
|
||||||
this.myList = ai.getCreaturesInPlay();
|
this.myList = ai.getCreaturesInPlay();
|
||||||
this.attackers = new ArrayList<Card>();
|
this.attackers = new ArrayList<Card>();
|
||||||
if (CombatUtil.canAttack(attacker, this.defendingOpponent)) {
|
if (CombatUtil.canAttack(attacker, this.defendingOpponent)) {
|
||||||
@@ -105,30 +106,31 @@ public class AiAttackController {
|
|||||||
this.blockers = this.getPossibleBlockers(oppList, this.attackers);
|
this.blockers = this.getPossibleBlockers(oppList, this.attackers);
|
||||||
} // overloaded constructor to evaluate single specified attacker
|
} // overloaded constructor to evaluate single specified attacker
|
||||||
|
|
||||||
private void getOpponentCreatures() {
|
public static List<Card> getOpponentCreatures(final Player defender) {
|
||||||
this.oppList = Lists.newArrayList();
|
List<Card> defenders = Lists.newArrayList();
|
||||||
this.oppList.addAll(this.defendingOpponent.getCreaturesInPlay());
|
defenders.addAll(defender.getCreaturesInPlay());
|
||||||
Predicate<Card> canAnimate = new Predicate<Card>() {
|
Predicate<Card> canAnimate = new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card c) {
|
public boolean apply(Card c) {
|
||||||
return !c.isCreature() && !c.isPlaneswalker();
|
return !c.isCreature() && !c.isPlaneswalker();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (Card c : CardLists.filter(this.defendingOpponent.getCardsIn(ZoneType.Battlefield), canAnimate)) {
|
for (Card c : CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), canAnimate)) {
|
||||||
if (c.isToken() && !c.isCopiedToken()) {
|
if (c.isToken() && !c.isCopiedToken()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||||
if (sa.getApi() == ApiType.Animate) {
|
if (sa.getApi() == ApiType.Animate) {
|
||||||
if (ComputerUtilCost.canPayCost(sa, this.defendingOpponent)
|
if (ComputerUtilCost.canPayCost(sa, defender)
|
||||||
&& sa.getRestrictions().checkOtherRestrictions(c, sa, this.defendingOpponent)) {
|
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
||||||
Card animatedCopy = CardFactory.getCard(c.getPaperCard(), this.defendingOpponent);
|
Card animatedCopy = CardFactory.getCard(c.getPaperCard(), defender);
|
||||||
AnimateAi.becomeAnimated(animatedCopy, sa);
|
AnimateAi.becomeAnimated(animatedCopy, sa);
|
||||||
this.oppList.add(animatedCopy);
|
defenders.add(animatedCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return defenders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Choose opponent for AI to attack here. Expand as necessary. */
|
/** Choose opponent for AI to attack here. Expand as necessary. */
|
||||||
@@ -1074,6 +1076,62 @@ public class AiAttackController {
|
|||||||
return shouldAttack(ai, attacker, oppList, combat);
|
return shouldAttack(ai, attacker, oppList, combat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toProtectAttacker(SpellAbility sa) {
|
||||||
|
if (sa.getApi() != ApiType.Protection || oppList.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final List<String> choices = ProtectEffect.getProtectionList(sa);
|
||||||
|
String color = ComputerUtilCard.getMostProminentColor(oppList), artifact = null;
|
||||||
|
if (choices.contains("artifacts")) {
|
||||||
|
artifact = "artifacts";
|
||||||
|
}
|
||||||
|
if (!choices.contains(color)) {
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
for (Card c : oppList) {
|
||||||
|
if (!c.isArtifact()) {
|
||||||
|
artifact = null;
|
||||||
|
}
|
||||||
|
switch (color) {
|
||||||
|
case "black":
|
||||||
|
if (!c.isBlack()) {
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "blue":
|
||||||
|
if (!c.isBlue()) {
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "green":
|
||||||
|
if (!c.isGreen()) {
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "red":
|
||||||
|
if (!c.isRed()) {
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "white":
|
||||||
|
if (!c.isWhite()) {
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (color == null && artifact == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (color != null) {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
if (artifact != null) {
|
||||||
|
return artifact;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean shouldThisAttack(final Player ai, Card attacker) {
|
public static boolean shouldThisAttack(final Player ai, Card attacker) {
|
||||||
AiAttackController aiAtk = new AiAttackController(ai, attacker);
|
AiAttackController aiAtk = new AiAttackController(ai, attacker);
|
||||||
Combat combat = ai.getGame().getCombat();
|
Combat combat = ai.getGame().getCombat();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package forge.ai;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.ai.ability.ProtectAi;
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.card.CardType.Constant;
|
import forge.card.CardType.Constant;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
@@ -1358,6 +1359,11 @@ public class ComputerUtil {
|
|||||||
|| saviour.getParam("KW").endsWith("Hexproof"))) {
|
|| saviour.getParam("KW").endsWith("Hexproof"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cannot protect against source
|
||||||
|
if (saviourApi == ApiType.Protection && (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// don't bounce or blink a permanent that the human
|
// don't bounce or blink a permanent that the human
|
||||||
// player owns or is a token
|
// player owns or is a token
|
||||||
@@ -1385,7 +1391,7 @@ public class ComputerUtil {
|
|||||||
else if ((threatApi == ApiType.Destroy || threatApi == ApiType.DestroyAll)
|
else if ((threatApi == ApiType.Destroy || threatApi == ApiType.DestroyAll)
|
||||||
&& (((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll)
|
&& (((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll)
|
||||||
&& !topStack.hasParam("NoRegen")) || saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump
|
&& !topStack.hasParam("NoRegen")) || saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump
|
||||||
|| saviourApi == null)) {
|
|| saviourApi == ApiType.Protection || saviourApi == null)) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
final Card c = (Card) o;
|
final Card c = (Card) o;
|
||||||
@@ -1405,6 +1411,11 @@ public class ComputerUtil {
|
|||||||
|| saviour.getParam("KW").endsWith("Hexproof"))) {
|
|| saviour.getParam("KW").endsWith("Hexproof"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (saviourApi == ApiType.Protection) {
|
||||||
|
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// don't bounce or blink a permanent that the human
|
// don't bounce or blink a permanent that the human
|
||||||
// player owns or is a token
|
// player owns or is a token
|
||||||
@@ -1422,7 +1433,8 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
// Exiling => bounce/shroud
|
// Exiling => bounce/shroud
|
||||||
else if ((threatApi == ApiType.ChangeZone || threatApi == ApiType.ChangeZoneAll)
|
else if ((threatApi == ApiType.ChangeZone || threatApi == ApiType.ChangeZoneAll)
|
||||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == null)
|
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump
|
||||||
|
|| saviourApi == ApiType.Protection || saviourApi == null)
|
||||||
&& topStack.hasParam("Destination")
|
&& topStack.hasParam("Destination")
|
||||||
&& topStack.getParam("Destination").equals("Exile")) {
|
&& topStack.getParam("Destination").equals("Exile")) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
@@ -1433,6 +1445,11 @@ public class ComputerUtil {
|
|||||||
&& (saviour.getParam("KW").endsWith("Shroud") || saviour.getParam("KW").endsWith("Hexproof"))) {
|
&& (saviour.getParam("KW").endsWith("Shroud") || saviour.getParam("KW").endsWith("Hexproof"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (saviourApi == ApiType.Protection) {
|
||||||
|
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// don't bounce or blink a permanent that the human
|
// don't bounce or blink a permanent that the human
|
||||||
// player owns or is a token
|
// player owns or is a token
|
||||||
@@ -1446,7 +1463,8 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
//GainControl
|
//GainControl
|
||||||
else if (threatApi == ApiType.GainControl
|
else if (threatApi == ApiType.GainControl
|
||||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == null)) {
|
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.Protection
|
||||||
|
|| saviourApi == null)) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
final Card c = (Card) o;
|
final Card c = (Card) o;
|
||||||
@@ -1455,6 +1473,11 @@ public class ComputerUtil {
|
|||||||
&& (saviour.getParam("KW").endsWith("Shroud") || saviour.getParam("KW").endsWith("Hexproof"))) {
|
&& (saviour.getParam("KW").endsWith("Shroud") || saviour.getParam("KW").endsWith("Hexproof"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (saviourApi == ApiType.Protection) {
|
||||||
|
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
threatened.add(c);
|
threatened.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.google.common.collect.Multimap;
|
|||||||
|
|
||||||
import forge.ai.ability.ChangeZoneAi;
|
import forge.ai.ability.ChangeZoneAi;
|
||||||
import forge.ai.ability.CharmAi;
|
import forge.ai.ability.CharmAi;
|
||||||
|
import forge.ai.ability.ProtectAi;
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
@@ -28,6 +29,8 @@ import forge.game.cost.CostPart;
|
|||||||
import forge.game.cost.CostPartMana;
|
import forge.game.cost.CostPartMana;
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
|
import forge.game.phase.PhaseHandler;
|
||||||
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.LobbyPlayer;
|
import forge.game.player.LobbyPlayer;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
@@ -600,6 +603,41 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
@Override
|
@Override
|
||||||
public String chooseProtectionType(String string, SpellAbility sa, List<String> choices) {
|
public String chooseProtectionType(String string, SpellAbility sa, List<String> choices) {
|
||||||
String choice = choices.get(0);
|
String choice = choices.get(0);
|
||||||
|
if (game.stack.size() > 1) {
|
||||||
|
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||||
|
SpellAbility spell = si.getSpellAbility();
|
||||||
|
if (sa != spell) {
|
||||||
|
String s = ProtectAi.toProtectFrom(spell.getHostCard(), sa);
|
||||||
|
if (s != null) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Combat combat = game.getCombat();
|
||||||
|
if (combat != null ) {
|
||||||
|
Card toSave = sa.getTargetCard();
|
||||||
|
List<Card> threats = null;
|
||||||
|
if (combat.isBlocked(toSave)) {
|
||||||
|
threats = combat.getBlockers(toSave);
|
||||||
|
}
|
||||||
|
if (combat.isBlocking(toSave)) {
|
||||||
|
threats = combat.getAttackersBlockedBy(toSave);
|
||||||
|
}
|
||||||
|
if (threats != null) {
|
||||||
|
ComputerUtilCard.sortByEvaluateCreature(threats);
|
||||||
|
String s = ProtectAi.toProtectFrom(threats.get(0), sa);
|
||||||
|
if (s != null) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
|
if (ph.getPlayerTurn() == sa.getActivatingPlayer() && ph.getPhase() == PhaseType.MAIN1) {
|
||||||
|
AiAttackController aiAtk = new AiAttackController(sa.getActivatingPlayer(), sa.getTargetCard());
|
||||||
|
return aiAtk.toProtectAttacker(sa);
|
||||||
|
}
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
if (logic == null || logic.equals("MostProminentHumanCreatures")) {
|
if (logic == null || logic.equals("MostProminentHumanCreatures")) {
|
||||||
List<Card> list = new ArrayList<Card>();
|
List<Card> list = new ArrayList<Card>();
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.ProtectEffect;
|
import forge.game.ability.effects.ProtectEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
|
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;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
public class ProtectAi extends SpellAbilityAi {
|
public class ProtectAi extends SpellAbilityAi {
|
||||||
private static boolean hasProtectionFrom(final Card card, final String color) {
|
private static boolean hasProtectionFrom(final Card card, final String color) {
|
||||||
@@ -50,6 +55,38 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
return protect && !isEmpty;
|
return protect && !isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Find a choice for a Protect SpellAbility that protects from a specific threat card.
|
||||||
|
* @param threat Card to protect against
|
||||||
|
* @param sa Protect SpellAbility
|
||||||
|
* @return choice that can protect against the given threat, null if no such choice exists
|
||||||
|
*/
|
||||||
|
public static String toProtectFrom(final Card threat, SpellAbility sa) {
|
||||||
|
if (sa.getApi() != ApiType.Protection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final List<String> choices = ProtectEffect.getProtectionList(sa);
|
||||||
|
if (threat.isArtifact() && choices.contains("artifacts")) {
|
||||||
|
return "artifacts";
|
||||||
|
}
|
||||||
|
if (threat.isBlack() && choices.contains("black")) {
|
||||||
|
return "black";
|
||||||
|
}
|
||||||
|
if (threat.isBlue() && choices.contains("blue")) {
|
||||||
|
return "blue";
|
||||||
|
}
|
||||||
|
if (threat.isGreen() && choices.contains("green")) {
|
||||||
|
return "green";
|
||||||
|
}
|
||||||
|
if (threat.isRed() && choices.contains("red")) {
|
||||||
|
return "red";
|
||||||
|
}
|
||||||
|
if (threat.isWhite() && choices.contains("white")) {
|
||||||
|
return "white";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -64,6 +101,7 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
final List<String> gains = ProtectEffect.getProtectionList(sa);
|
final List<String> gains = ProtectEffect.getProtectionList(sa);
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
|
|
||||||
List<Card> list = ai.getCreaturesInPlay();
|
List<Card> list = ai.getCreaturesInPlay();
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@@ -85,22 +123,51 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( combat != null ) {
|
if (!game.stack.isEmpty()) {
|
||||||
// is the creature blocking and unable to destroy the attacker
|
//counter bad effect on stack
|
||||||
// or would be destroyed itself?
|
if (ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(c)) {
|
||||||
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
Card threat = game.getStack().peekAbility().getHostCard();
|
||||||
return true;
|
//check to see if threat has already been countered by resolved protect
|
||||||
}
|
if (!c.hasProtectionFrom(threat) && (ProtectAi.toProtectFrom(threat, sa) != null)) {
|
||||||
|
return true;
|
||||||
// is the creature in blocked and the blocker would survive
|
}
|
||||||
// TODO Potential NPE here if no blockers are actually left
|
}
|
||||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
}
|
||||||
&& combat.isAttacking(c) && combat.isBlocked(c)
|
|
||||||
&& ComputerUtilCombat.blockerWouldBeDestroyed(ai, combat.getBlockers(c).get(0), combat)) {
|
if (combat != null) {
|
||||||
return true;
|
//creature is blocking and would be destroyed itself
|
||||||
|
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
||||||
|
List<Card> threats = combat.getAttackersBlockedBy(c);
|
||||||
|
return (true && (ProtectAi.toProtectFrom(threats.get(0), sa) != null));
|
||||||
|
}
|
||||||
|
|
||||||
|
//creature is attacking and would be destroyed itself
|
||||||
|
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
|
&& combat.isAttacking(c) && combat.isBlocked(c) ) {
|
||||||
|
List<Card> blockers = combat.getBlockers(c);
|
||||||
|
if (!blockers.isEmpty() && ComputerUtilCombat.blockerWouldBeDestroyed(ai, blockers.get(0), combat)) {
|
||||||
|
List<Card> threats = combat.getBlockers(c);
|
||||||
|
ComputerUtilCard.sortByEvaluateCreature(threats);
|
||||||
|
return (true && (ProtectAi.toProtectFrom(threats.get(0), sa) != null));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//make unblockable
|
||||||
|
if (ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1) {
|
||||||
|
AiAttackController aiAtk = new AiAttackController(ai, c);
|
||||||
|
String s = aiAtk.toProtectAttacker(sa);
|
||||||
|
if (s==null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
Combat combat = ai.getGame().getCombat();
|
||||||
|
int dmg = ComputerUtilCombat.damageIfUnblocked(c, ai.getOpponent(), combat);
|
||||||
|
float ratio = 1.0f * dmg / ai.getOpponent().getLife();
|
||||||
|
Random r = MyRandom.getRandom();
|
||||||
|
return r.nextFloat() < ratio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -111,6 +178,7 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
// if there is no target and host card isn't in play, don't activate
|
// if there is no target and host card isn't in play, don't activate
|
||||||
if ((sa.getTargetRestrictions() == null) && !hostCard.isInPlay()) {
|
if ((sa.getTargetRestrictions() == null) && !hostCard.isInPlay()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -136,16 +204,27 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Phase Restrictions
|
// Phase Restrictions
|
||||||
if (game.getStack().isEmpty() && game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE)) {
|
boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1);
|
||||||
// Instant-speed protections should not be cast outside of combat
|
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
// when the stack is empty
|
//only non-instants are Floating Shield, Midvast Protector, Sejiri Steppe
|
||||||
if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
//sorceries can only give protection in order to create an unblockable attacker
|
||||||
|
if (notAiMain1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!game.getStack().isEmpty()) {
|
} else {
|
||||||
// TODO protection something only if the top thing on the stack will
|
if (game.getStack().isEmpty()) {
|
||||||
// kill it via damage or destroy
|
//try to save attacker or blocker
|
||||||
return false;
|
if (ph.getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
|
||||||
|
if (notAiMain1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//prevent repeated protects
|
||||||
|
if (game.getStack().peekAbility().getApi() == ApiType.Protection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||||
|
|||||||
@@ -2,7 +2,5 @@ Name:Apostle's Blessing
|
|||||||
ManaCost:1 PW
|
ManaCost:1 PW
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Protection | Cost$ 1 PW | ValidTgts$ Creature.YouCtrl,Artifact.YouCtrl | TgtPrompt$ Select target artifact or creature you control | Gains$ Choice | Choices$ AnyColor,artifacts | SpellDescription$ Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn.
|
A:SP$ Protection | Cost$ 1 PW | ValidTgts$ Creature.YouCtrl,Artifact.YouCtrl | TgtPrompt$ Select target artifact or creature you control | Gains$ Choice | Choices$ AnyColor,artifacts | SpellDescription$ Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn.
|
||||||
#Computer isn't very good at picking a color to get protection from yet
|
|
||||||
SVar:RemAIDeck:True
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/apostles_blessing.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/apostles_blessing.jpg
|
||||||
Oracle:({W/P} can be paid with either {W} or 2 life.)\nTarget artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn.
|
Oracle:({W/P} can be paid with either {W} or 2 life.)\nTarget artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn.
|
||||||
@@ -3,6 +3,5 @@ ManaCost:W
|
|||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Protection | Cost$ W | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | Gains$ Choice | Choices$ AnyColor | SubAbility$ DBScry | SpellDescription$ Target creature you control gains protection from the color of your choice until end of turn. Scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.)
|
A:SP$ Protection | Cost$ W | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | Gains$ Choice | Choices$ AnyColor | SubAbility$ DBScry | SpellDescription$ Target creature you control gains protection from the color of your choice until end of turn. Scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.)
|
||||||
SVar:DBScry:DB$ Scry | ScryNum$ 1
|
SVar:DBScry:DB$ Scry | ScryNum$ 1
|
||||||
SVar:RemAIDeck:True
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/gods_willing.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/gods_willing.jpg
|
||||||
Oracle:Target creature you control gains protection from the color of your choice until end of turn. Scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.)
|
Oracle:Target creature you control gains protection from the color of your choice until end of turn. Scry 1. (Look at the top card of your library. You may put that card on the bottom of your library.)
|
||||||
Reference in New Issue
Block a user