From 89eed178391c386e5053aa933a21dae85708c4b6 Mon Sep 17 00:00:00 2001 From: Seravy Date: Sat, 17 Feb 2018 12:01:39 +0100 Subject: [PATCH 01/15] Reserve mana for fog effects --- forge-ai/src/main/java/forge/ai/ability/FogAi.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/ability/FogAi.java b/forge-ai/src/main/java/forge/ai/ability/FogAi.java index 66f34652f0b..46b0d1f1023 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FogAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FogAi.java @@ -3,6 +3,7 @@ package forge.ai.ability; import forge.ai.ComputerUtil; import forge.ai.ComputerUtilCombat; +import forge.ai.PlayerControllerAi; import forge.ai.SpellAbilityAi; import forge.game.Game; import forge.game.card.Card; @@ -20,6 +21,15 @@ public class FogAi extends SpellAbilityAi { @Override protected boolean canPlayAI(Player ai, SpellAbility sa) { final Game game = ai.getGame(); + + // Reserve mana to cast this card if it will be likely needed + if ((ComputerUtil.aiLifeInDanger(ai, false, 0)) + && ((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) || + (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) + ) { + ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS); + } + // AI should only activate this during Human's Declare Blockers phase if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) { return false; From 769f6ea976fe970654582bb26574fd8bddc38c73 Mon Sep 17 00:00:00 2001 From: Seravy Date: Sat, 17 Feb 2018 12:38:52 +0100 Subject: [PATCH 02/15] Reserve mana for fog effects if in danger --- forge-ai/src/main/java/forge/ai/AiCardMemory.java | 6 ++++++ forge-ai/src/main/java/forge/ai/ComputerUtilMana.java | 1 + forge-ai/src/main/java/forge/ai/ability/FogAi.java | 9 +++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index 510c6834c48..d648ba743a1 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -48,6 +48,7 @@ public class AiCardMemory { private final Set memAnimatedThisTurn; private final Set memBouncedThisTurn; private final Set memActivatedThisTurn; + private final Set memChosenFogEffect; public AiCardMemory() { this.memMandatoryAttackers = new HashSet<>(); @@ -58,6 +59,7 @@ public class AiCardMemory { this.memBouncedThisTurn = new HashSet<>(); this.memActivatedThisTurn = new HashSet<>(); this.memTrickAttackers = new HashSet<>(); + this.memChosenFogEffect = new HashSet<>(); } /** @@ -74,6 +76,7 @@ public class AiCardMemory { ANIMATED_THIS_TURN, BOUNCED_THIS_TURN, ACTIVATED_THIS_TURN, + CHOSEN_FOG_EFFECT, //REVEALED_CARDS // stub, not linked to AI code yet } @@ -95,6 +98,8 @@ public class AiCardMemory { return memBouncedThisTurn; case ACTIVATED_THIS_TURN: return memActivatedThisTurn; + case CHOSEN_FOG_EFFECT: + return memChosenFogEffect; //case REVEALED_CARDS: // return memRevealedCards; default: @@ -271,6 +276,7 @@ public class AiCardMemory { clearMemorySet(MemorySet.ANIMATED_THIS_TURN); clearMemorySet(MemorySet.BOUNCED_THIS_TURN); clearMemorySet(MemorySet.ACTIVATED_THIS_TURN); + clearMemorySet(MemorySet.CHOSEN_FOG_EFFECT); } // Static functions to simplify access to AI card memory of a given AI player. diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index d30d7006045..1e8565d60d9 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -853,6 +853,7 @@ public class ComputerUtilMana { // For combat tricks, always obey mana reservation if (curPhase == PhaseType.COMBAT_DECLARE_BLOCKERS || curPhase == PhaseType.CLEANUP) { AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK); + AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); } else { if (AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/FogAi.java b/forge-ai/src/main/java/forge/ai/ability/FogAi.java index 46b0d1f1023..e09fe972b2f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FogAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FogAi.java @@ -1,10 +1,7 @@ package forge.ai.ability; -import forge.ai.ComputerUtil; -import forge.ai.ComputerUtilCombat; -import forge.ai.PlayerControllerAi; -import forge.ai.SpellAbilityAi; +import forge.ai.*; import forge.game.Game; import forge.game.card.Card; import forge.game.card.CardPredicates; @@ -26,8 +23,12 @@ public class FogAi extends SpellAbilityAi { if ((ComputerUtil.aiLifeInDanger(ai, false, 0)) && ((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) || (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) + && (AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) ) { ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS); + + AiCardMemory.rememberCard(ai, sa.getHostCard(), AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); + } // AI should only activate this during Human's Declare Blockers phase From 606f52e4933d50d287b0e574ca7bd041acd10647 Mon Sep 17 00:00:00 2001 From: Seravy Date: Sat, 17 Feb 2018 13:18:24 +0100 Subject: [PATCH 03/15] Improved feature --- forge-ai/src/main/java/forge/ai/AiCardMemory.java | 6 ++++++ forge-ai/src/main/java/forge/ai/AiController.java | 7 +++++-- forge-ai/src/main/java/forge/ai/ComputerUtilCard.java | 2 +- forge-ai/src/main/java/forge/ai/ComputerUtilMana.java | 8 +++++--- forge-ai/src/main/java/forge/ai/ability/FogAi.java | 6 +++--- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index d648ba743a1..be8593fdefe 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -44,6 +44,7 @@ public class AiCardMemory { private final Set memTrickAttackers; private final Set memHeldManaSources; private final Set memHeldManaSourcesForCombat; + private final Set memHeldManaSourcesForEnemyCombat; private final Set memAttachedThisTurn; private final Set memAnimatedThisTurn; private final Set memBouncedThisTurn; @@ -54,6 +55,7 @@ public class AiCardMemory { this.memMandatoryAttackers = new HashSet<>(); this.memHeldManaSources = new HashSet<>(); this.memHeldManaSourcesForCombat = new HashSet<>(); + this.memHeldManaSourcesForEnemyCombat = new HashSet<>(); this.memAttachedThisTurn = new HashSet<>(); this.memAnimatedThisTurn = new HashSet<>(); this.memBouncedThisTurn = new HashSet<>(); @@ -72,6 +74,7 @@ public class AiCardMemory { TRICK_ATTACKERS, HELD_MANA_SOURCES_FOR_MAIN2, HELD_MANA_SOURCES_FOR_DECLBLK, + HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK, ATTACHED_THIS_TURN, ANIMATED_THIS_TURN, BOUNCED_THIS_TURN, @@ -90,6 +93,8 @@ public class AiCardMemory { return memHeldManaSources; case HELD_MANA_SOURCES_FOR_DECLBLK: return memHeldManaSourcesForCombat; + case HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK: + return memHeldManaSourcesForEnemyCombat; case ATTACHED_THIS_TURN: return memAttachedThisTurn; case ANIMATED_THIS_TURN: @@ -272,6 +277,7 @@ public class AiCardMemory { clearMemorySet(MemorySet.TRICK_ATTACKERS); clearMemorySet(MemorySet.HELD_MANA_SOURCES_FOR_MAIN2); clearMemorySet(MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK); + clearMemorySet(MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK); clearMemorySet(MemorySet.ATTACHED_THIS_TURN); clearMemorySet(MemorySet.ANIMATED_THIS_TURN); clearMemorySet(MemorySet.BOUNCED_THIS_TURN); diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 21e10ee577e..2ff37ca8e86 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -581,10 +581,10 @@ public class AiController { } public void reserveManaSources(SpellAbility sa) { - reserveManaSources(sa, PhaseType.MAIN2); + reserveManaSources(sa, PhaseType.MAIN2, false); } - public void reserveManaSources(SpellAbility sa, PhaseType phaseType) { + public void reserveManaSources(SpellAbility sa, PhaseType phaseType, boolean enemy) { ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, true, 0); CardCollection manaSources = ComputerUtilMana.getManaSourcesToPayCost(cost, sa, player); @@ -596,6 +596,9 @@ public class AiController { break; case COMBAT_DECLARE_BLOCKERS: memSet = AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK; + if (enemy) { + memSet = AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK; + } break; default: System.out.println("Warning: unsupported mana reservation phase specified for reserveManaSources: " diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index ccd53683d25..11f3f00782a 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1498,7 +1498,7 @@ public class ComputerUtilCard { // Reserve the mana until Declare Blockers such that the AI doesn't tap out before having a chance to use // the combat trick if (ai.getController().isAI()) { - ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS); + ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS, false); } return false; } else { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 1e8565d60d9..731e2de4522 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -853,10 +853,12 @@ public class ComputerUtilMana { // For combat tricks, always obey mana reservation if (curPhase == PhaseType.COMBAT_DECLARE_BLOCKERS || curPhase == PhaseType.CLEANUP) { AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK); + } else if (!(ai.getGame().getPhaseHandler().isPlayerTurn(ai)) && (curPhase == PhaseType.COMBAT_DECLARE_BLOCKERS || curPhase == PhaseType.CLEANUP)) { + AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK); AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); - } - else { - if (AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK)) { + } else { + if ((AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK)) || + (AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK))) { // This mana source is held elsewhere for a combat trick. return true; } diff --git a/forge-ai/src/main/java/forge/ai/ability/FogAi.java b/forge-ai/src/main/java/forge/ai/ability/FogAi.java index e09fe972b2f..1d73d9dfa08 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FogAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FogAi.java @@ -20,12 +20,12 @@ public class FogAi extends SpellAbilityAi { final Game game = ai.getGame(); // Reserve mana to cast this card if it will be likely needed - if ((ComputerUtil.aiLifeInDanger(ai, false, 0)) - && ((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) || + if (((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) || (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) && (AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) + && (ComputerUtil.aiLifeInDanger(ai, false, 0)) ) { - ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS); + ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS, true); AiCardMemory.rememberCard(ai, sa.getHostCard(), AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); From 9e58ec0f80152a744ab68a76927f00296a29a4e8 Mon Sep 17 00:00:00 2001 From: Seravy Date: Sat, 17 Feb 2018 14:04:31 +0100 Subject: [PATCH 04/15] React to removal of Spike Weaver by using the effect if it's already planning to do later Don't leave blockers if planning to use fog effect, except the card that will do so. (might need to tap to use it, or might get killed if attacking) --- .../java/forge/ai/AiAttackController.java | 10 +++++++ .../src/main/java/forge/ai/ability/FogAi.java | 29 +++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 8d9c231e8d7..f346e48bccd 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -45,6 +45,8 @@ import forge.util.Expressions; import forge.util.MyRandom; import forge.util.collect.FCollectionView; +import static forge.ai.AiCardMemory.isMemorySetEmpty; + //doesHumanAttackAndWin() uses the global variable AllZone.getComputerPlayer() /** @@ -268,6 +270,14 @@ public class AiAttackController { if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) { return attackers; } + // no need to block is already holding mana to cast fog next turn + if (!isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) { + // Don't send the card that'll do the fog effect to attack, it's unsafe! + if (attackers.contains(AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) { + attackers.remove(AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); + } + return attackers; + } List opponentsAttackers = new ArrayList(oppList); opponentsAttackers = CardLists.filter(opponentsAttackers, new Predicate() { @Override diff --git a/forge-ai/src/main/java/forge/ai/ability/FogAi.java b/forge-ai/src/main/java/forge/ai/ability/FogAi.java index 1d73d9dfa08..8c5cfeff097 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FogAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FogAi.java @@ -3,6 +3,7 @@ package forge.ai.ability; import forge.ai.*; import forge.game.Game; +import forge.game.GameObject; import forge.game.card.Card; import forge.game.card.CardPredicates; import forge.game.phase.PhaseType; @@ -10,6 +11,8 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.util.Aggregates; +import java.util.List; + public class FogAi extends SpellAbilityAi { /* (non-Javadoc) @@ -18,6 +21,25 @@ public class FogAi extends SpellAbilityAi { @Override protected boolean canPlayAI(Player ai, SpellAbility sa) { final Game game = ai.getGame(); + final Card hostCard = sa.getHostCard(); + + // Don't cast it, if the effect is already in place + if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) { + return false; + } + + // if card would be destroyed, react and use immediately if it's not own turn + if ((AiCardMemory.isRememberedCard(ai, hostCard, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) + && (!game.getStack().isEmpty()) && + (!game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) + ) { + final List objects = ComputerUtil + .predictThreatenedObjects(ai, sa); + if (objects.contains(hostCard)) { + AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK); + return true; + } + } // Reserve mana to cast this card if it will be likely needed if (((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) || @@ -27,7 +49,7 @@ public class FogAi extends SpellAbilityAi { ) { ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS, true); - AiCardMemory.rememberCard(ai, sa.getHostCard(), AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); + AiCardMemory.rememberCard(ai, hostCard, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); } @@ -44,11 +66,6 @@ public class FogAi extends SpellAbilityAi { return false; } - // Don't cast it, if the effect is already in place - if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) { - return false; - } - if ("SeriousDamage".equals(sa.getParam("AILogic")) && game.getCombat() != null) { int dmg = 0; for (Card atk : game.getCombat().getAttackersOf(ai)) { From 300e3fd4d342eed3744c9ddc224b788cc74c354d Mon Sep 17 00:00:00 2001 From: Seravy Date: Sat, 17 Feb 2018 21:14:59 +0100 Subject: [PATCH 05/15] Fixed using the fog effect on "about to die" Spike Weaver --- forge-ai/src/main/java/forge/ai/ability/FogAi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/FogAi.java b/forge-ai/src/main/java/forge/ai/ability/FogAi.java index 8c5cfeff097..32357aad658 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FogAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FogAi.java @@ -34,7 +34,7 @@ public class FogAi extends SpellAbilityAi { (!game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) ) { final List objects = ComputerUtil - .predictThreatenedObjects(ai, sa); + .predictThreatenedObjects(ai, null); if (objects.contains(hostCard)) { AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK); return true; From 91616396b73726615e183d38dac6e183469335db Mon Sep 17 00:00:00 2001 From: Seravy Date: Sat, 17 Feb 2018 21:21:06 +0100 Subject: [PATCH 06/15] Also no need to block if Awakening is in play. Adding here to prevent future merge conflict with Fog having the same effect. --- forge-ai/src/main/java/forge/ai/AiAttackController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index f346e48bccd..ee12f37379f 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -278,6 +278,13 @@ public class AiAttackController { } return attackers; } + // no need to block if awakening in play + for (Card card : ai.getGame().getCardsIn(ZoneType.Battlefield)) { + if ("Awakening".equals(card.getName())) { + return attackers; + } + } + List opponentsAttackers = new ArrayList(oppList); opponentsAttackers = CardLists.filter(opponentsAttackers, new Predicate() { @Override From 3931903018a02aa9eb687cb1030647e807891165 Mon Sep 17 00:00:00 2001 From: Seravy Date: Mon, 19 Feb 2018 10:41:55 +0100 Subject: [PATCH 07/15] New SVar "ChosenProtection" Marks Auras that would grant protection from the chosen color, to prevent attaching to a creature that already would have protection from that color. While picking a different color would be another solution, there is a chance it's wasteful to protect from a secondary color, or the player plays only one color so it's better to not target that creature at all. This attempt didn't work, next commit did. --- .../src/main/java/forge/ai/PlayerControllerAi.java | 7 +++++++ .../src/main/java/forge/ai/ability/AttachAi.java | 13 +++++++++++++ forge-gui/res/cardsfolder/f/flickering_ward.txt | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 71cd9581976..beb020694d5 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -935,6 +935,11 @@ public class PlayerControllerAi extends PlayerController { if (sa.hasParam("AILogic")) { CardCollectionView aiLibrary = player.getCardsIn(ZoneType.Library); CardCollectionView oppLibrary = ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Library); + CardCollectionView oppAllCards = CardCollection.combine + (ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Hand), + ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Library), + ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Battlefield)); + final Card source = sa.getHostCard(); final String logic = sa.getParam("AILogic"); @@ -955,6 +960,8 @@ public class PlayerControllerAi extends PlayerController { return ComputerUtilCard.getMostProminentCardName(aiLibrary); } else if (logic.equals("MostProminentInHumanDeck")) { return ComputerUtilCard.getMostProminentCardName(oppLibrary); + } else if (logic.equals("MostProminentColorInHumanDeck")) { + return ComputerUtilCard.getMostProminentColor(oppAllCards); } else if (logic.equals("MostProminentCreatureInComputerDeck")) { CardCollectionView cards = CardLists.getValidCards(aiLibrary, "Creature", player, sa.getHostCard()); return ComputerUtilCard.getMostProminentCardName(cards); diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index d4592c9c9f2..df5aa341b30 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1311,6 +1311,19 @@ public class AttachAi extends SpellAbilityAi { if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { return false; } + + // Don't play if would choose a color the target is already protected from + if (card.hasSVar("ChosenProtection")) { + CardCollectionView oppAllCards = CardCollection.combine + (ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand), + ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Library), + ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield)); + String cc = ComputerUtilCard.getMostProminentColor(oppAllCards); + if (card.hasKeyword("Protection from " + cc.toLowerCase())) { + return false; + } + } + final boolean evasive = (keyword.equals("Unblockable") || keyword.equals("Fear") || keyword.equals("Intimidate") || keyword.equals("Shadow") || keyword.equals("Flying") || keyword.equals("Horsemanship") diff --git a/forge-gui/res/cardsfolder/f/flickering_ward.txt b/forge-gui/res/cardsfolder/f/flickering_ward.txt index 78040fe4b23..f7e2d9f1363 100644 --- a/forge-gui/res/cardsfolder/f/flickering_ward.txt +++ b/forge-gui/res/cardsfolder/f/flickering_ward.txt @@ -3,10 +3,11 @@ ManaCost:W Types:Enchantment Aura K:Enchant creature K:ETBReplacement:Other:ChooseColor -SVar:ChooseColor:DB$ ChooseColor | Defined$ You | SpellDescription$ As CARDNAME enters the battlefield, choose a color. | AILogic$ MostProminentInHumanDeck +SVar:ChooseColor:DB$ ChooseColor | Defined$ You | SpellDescription$ As CARDNAME enters the battlefield, choose a color. | AILogic$ MostProminentColorInHumanDeck A:SP$ Attach | Cost$ W | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Protection:Card.ChosenColor:Protection from ChosenColor:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the chosen color. This effect doesn't remove CARDNAME. A:AB$ ChangeZone | Cost$ W | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return CARDNAME to its owner's hand. SVar:RemAIDeck:True +SVar:ChosenProtection:True SVar:Picture:http://www.wizards.com/global/images/magic/general/flickering_ward.jpg Oracle:Enchant creature\nAs Flickering Ward enters the battlefield, choose a color.\nEnchanted creature has protection from the chosen color. This effect doesn't remove Flickering Ward.\n{W}: Return Flickering Ward to its owner's hand. From 23e312f2bac52142223ee1d2256b88768c0c4abd Mon Sep 17 00:00:00 2001 From: Seravy Date: Mon, 19 Feb 2018 11:18:20 +0100 Subject: [PATCH 08/15] Now it works --- forge-ai/src/main/java/forge/ai/PlayerControllerAi.java | 7 ------- forge-ai/src/main/java/forge/ai/ability/AttachAi.java | 6 ++---- forge-gui/res/cardsfolder/f/flickering_ward.txt | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index beb020694d5..71cd9581976 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -935,11 +935,6 @@ public class PlayerControllerAi extends PlayerController { if (sa.hasParam("AILogic")) { CardCollectionView aiLibrary = player.getCardsIn(ZoneType.Library); CardCollectionView oppLibrary = ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Library); - CardCollectionView oppAllCards = CardCollection.combine - (ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Hand), - ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Library), - ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Battlefield)); - final Card source = sa.getHostCard(); final String logic = sa.getParam("AILogic"); @@ -960,8 +955,6 @@ public class PlayerControllerAi extends PlayerController { return ComputerUtilCard.getMostProminentCardName(aiLibrary); } else if (logic.equals("MostProminentInHumanDeck")) { return ComputerUtilCard.getMostProminentCardName(oppLibrary); - } else if (logic.equals("MostProminentColorInHumanDeck")) { - return ComputerUtilCard.getMostProminentColor(oppAllCards); } else if (logic.equals("MostProminentCreatureInComputerDeck")) { CardCollectionView cards = CardLists.getValidCards(aiLibrary, "Creature", player, sa.getHostCard()); return ComputerUtilCard.getMostProminentCardName(cards); diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index df5aa341b30..9ee4c629968 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1313,11 +1313,9 @@ public class AttachAi extends SpellAbilityAi { } // Don't play if would choose a color the target is already protected from - if (card.hasSVar("ChosenProtection")) { + if (sa.getHostCard().hasSVar("ChosenProtection")) { CardCollectionView oppAllCards = CardCollection.combine - (ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand), - ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Library), - ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield)); + (ComputerUtil.getOpponentFor(ai).getAllCards()); String cc = ComputerUtilCard.getMostProminentColor(oppAllCards); if (card.hasKeyword("Protection from " + cc.toLowerCase())) { return false; diff --git a/forge-gui/res/cardsfolder/f/flickering_ward.txt b/forge-gui/res/cardsfolder/f/flickering_ward.txt index f7e2d9f1363..f2a01a3fd2e 100644 --- a/forge-gui/res/cardsfolder/f/flickering_ward.txt +++ b/forge-gui/res/cardsfolder/f/flickering_ward.txt @@ -3,7 +3,7 @@ ManaCost:W Types:Enchantment Aura K:Enchant creature K:ETBReplacement:Other:ChooseColor -SVar:ChooseColor:DB$ ChooseColor | Defined$ You | SpellDescription$ As CARDNAME enters the battlefield, choose a color. | AILogic$ MostProminentColorInHumanDeck +SVar:ChooseColor:DB$ ChooseColor | Defined$ You | SpellDescription$ As CARDNAME enters the battlefield, choose a color. | AILogic$ MostProminentInHumanDeck A:SP$ Attach | Cost$ W | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Protection:Card.ChosenColor:Protection from ChosenColor:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the chosen color. This effect doesn't remove CARDNAME. A:AB$ ChangeZone | Cost$ W | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return CARDNAME to its owner's hand. From acd68f86d1b0265ab5fc126c9050733e4aa7eb01 Mon Sep 17 00:00:00 2001 From: Seravy Date: Mon, 19 Feb 2018 11:22:52 +0100 Subject: [PATCH 09/15] Add Svar to all cards where applicable --- forge-gui/res/cardsfolder/c/cho_mannos_blessing.txt | 1 + forge-gui/res/cardsfolder/f/floating_shield.txt | 1 + forge-gui/res/cardsfolder/p/pentarch_ward.txt | 1 + forge-gui/res/cardsfolder/w/ward_of_lights.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/forge-gui/res/cardsfolder/c/cho_mannos_blessing.txt b/forge-gui/res/cardsfolder/c/cho_mannos_blessing.txt index 6a5fe54791c..870d2ff486c 100644 --- a/forge-gui/res/cardsfolder/c/cho_mannos_blessing.txt +++ b/forge-gui/res/cardsfolder/c/cho_mannos_blessing.txt @@ -8,4 +8,5 @@ SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInHumanD A:SP$ Attach | Cost$ W W | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Protection:Card.ChosenColor:Protection from ChosenColor:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the chosen color. This effect doesn't remove CARDNAME. SVar:Picture:http://www.wizards.com/global/images/magic/general/cho_mannos_blessing.jpg +SVar:ChosenProtection:True Oracle:Flash\nEnchant creature\nAs Cho-Manno's Blessing enters the battlefield, choose a color.\nEnchanted creature has protection from the chosen color. This effect doesn't remove Cho-Manno's Blessing. diff --git a/forge-gui/res/cardsfolder/f/floating_shield.txt b/forge-gui/res/cardsfolder/f/floating_shield.txt index a14669460bb..a4dc1de9f3c 100644 --- a/forge-gui/res/cardsfolder/f/floating_shield.txt +++ b/forge-gui/res/cardsfolder/f/floating_shield.txt @@ -8,5 +8,6 @@ A:SP$ Attach | Cost$ 2 W | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Protection:Card.ChosenColor:Protection from ChosenColor:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the chosen color. This effect doesn't remove CARDNAME. A:AB$ Protection | Cost$ Sac<1/CARDNAME> | ValidTgts$ Creature | TgtPrompt$ Select target creature | Gains$ ChosenColor | SpellDescription$ Target creature gains protection from the chosen color until end of turn. SVar:RemAIDeck:True +SVar:ChosenProtection:True SVar:Picture:http://www.wizards.com/global/images/magic/general/floating_shield.jpg Oracle:Enchant creature\nAs Floating Shield enters the battlefield, choose a color.\nEnchanted creature has protection from the chosen color. This effect doesn't remove Floating Shield.\nSacrifice Floating Shield: Target creature gains protection from the chosen color until end of turn. diff --git a/forge-gui/res/cardsfolder/p/pentarch_ward.txt b/forge-gui/res/cardsfolder/p/pentarch_ward.txt index b573ae3a13f..a5d0a3d3d89 100644 --- a/forge-gui/res/cardsfolder/p/pentarch_ward.txt +++ b/forge-gui/res/cardsfolder/p/pentarch_ward.txt @@ -9,4 +9,5 @@ SVar:TrigDraw:DB$Draw | Defined$ You | NumCards$ 1 A:SP$ Attach | Cost$ 2 W | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Protection:Card.ChosenColor:Protection from ChosenColor:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the chosen color. This effect doesn't remove CARDNAME. SVar:Picture:http://www.wizards.com/global/images/magic/general/pentarch_ward.jpg +SVar:ChosenProtection:True Oracle:Enchant creature\nAs Pentarch Ward enters the battlefield, choose a color.\nWhen Pentarch Ward enters the battlefield, draw a card.\nEnchanted creature has protection from the chosen color. This effect doesn't remove Pentarch Ward. diff --git a/forge-gui/res/cardsfolder/w/ward_of_lights.txt b/forge-gui/res/cardsfolder/w/ward_of_lights.txt index b93c9b1c9e4..364d5fecc0b 100644 --- a/forge-gui/res/cardsfolder/w/ward_of_lights.txt +++ b/forge-gui/res/cardsfolder/w/ward_of_lights.txt @@ -11,5 +11,6 @@ SVar:ChooseColor:DB$ ChooseColor | Defined$ You | SpellDescription$ As CARDNAME A:SP$ Attach | Cost$ W W | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Protection:Card.ChosenColor:Protection from ChosenColor:Card.CardUID_HostCardUID | Description$ Enchanted creature has protection from the chosen color. This effect doesn't remove CARDNAME. SVar:RemAIDeck:True +SVar:ChosenProtection:True SVar:Picture:http://www.wizards.com/global/images/magic/general/ward_of_lights.jpg Oracle:You may cast Ward of Lights as though it had flash. If you cast it any time a sorcery couldn't have been cast, the controller of the permanent it becomes sacrifices it at the beginning of the next cleanup step.\nEnchant creature\nAs Ward of Lights enters the battlefield, choose a color.\nEnchanted creature has protection from the chosen color. This effect doesn't remove Ward of Lights. From 3c03199ea30780977a9e9f78aa6866d1090a2b6d Mon Sep 17 00:00:00 2001 From: Seravy Date: Mon, 19 Feb 2018 11:30:58 +0100 Subject: [PATCH 10/15] Do not play protection if enchanted by aura of the color that would be chosen --- forge-ai/src/main/java/forge/ai/ability/AttachAi.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index 9ee4c629968..3ac221b6b5a 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1320,6 +1320,14 @@ public class AttachAi extends SpellAbilityAi { if (card.hasKeyword("Protection from " + cc.toLowerCase())) { return false; } + // Also don't play if it would destroy own Aura + for (Card c : card.getEnchantedBy(false)) { + if ((c.getController().equals(ai)) + && (c.isOfColor(cc)) + ) { + return false; + } + } } final boolean evasive = (keyword.equals("Unblockable") || keyword.equals("Fear") From 9a206ff33850921f00a08edf0ef210d62fde8638 Mon Sep 17 00:00:00 2001 From: Agetian Date: Mon, 30 Apr 2018 08:49:20 +0300 Subject: [PATCH 11/15] - Some style and logic fixes. --- forge-ai/src/main/java/forge/ai/ability/AttachAi.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index dd3443e9918..4074512d7b0 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1314,17 +1314,14 @@ public class AttachAi extends SpellAbilityAi { // Don't play if would choose a color the target is already protected from if (sa.getHostCard().hasSVar("ChosenProtection")) { - CardCollectionView oppAllCards = CardCollection.combine - (ComputerUtil.getOpponentFor(ai).getAllCards()); + CardCollectionView oppAllCards = ai.getOpponents().getCardsIn(ZoneType.Battlefield); String cc = ComputerUtilCard.getMostProminentColor(oppAllCards); if (card.hasKeyword("Protection from " + cc.toLowerCase())) { return false; } // Also don't play if it would destroy own Aura for (Card c : card.getEnchantedBy(false)) { - if ((c.getController().equals(ai)) - && (c.isOfColor(cc)) - ) { + if ((c.getController().equals(ai)) && (c.isOfColor(cc))) { return false; } } From 9aec30e0dcc4a623ecbb0f740d088cfd8ca475b6 Mon Sep 17 00:00:00 2001 From: Agetian Date: Mon, 30 Apr 2018 09:36:31 +0300 Subject: [PATCH 12/15] - FogAi update from Seravy (updated for style consistency). --- .../main/java/forge/ai/AiAttackController.java | 6 ++---- .../src/main/java/forge/ai/ability/FogAi.java | 17 ++++++----------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 0028ed584f7..7a6ca1f3f0b 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -40,8 +40,6 @@ import forge.util.Expressions; import forge.util.MyRandom; import forge.util.collect.FCollectionView; -import static forge.ai.AiCardMemory.isMemorySetEmpty; - import java.util.ArrayList; import java.util.List; @@ -265,8 +263,8 @@ public class AiAttackController { if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) { return attackers; } - // no need to block is already holding mana to cast fog next turn - if (!isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) { + // no need to block (already holding mana to cast fog next turn) + if (!AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) { // Don't send the card that'll do the fog effect to attack, it's unsafe! if (attackers.contains(AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) { attackers.remove(AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); diff --git a/forge-ai/src/main/java/forge/ai/ability/FogAi.java b/forge-ai/src/main/java/forge/ai/ability/FogAi.java index 32357aad658..00a7386ea7d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FogAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FogAi.java @@ -30,11 +30,9 @@ public class FogAi extends SpellAbilityAi { // if card would be destroyed, react and use immediately if it's not own turn if ((AiCardMemory.isRememberedCard(ai, hostCard, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) - && (!game.getStack().isEmpty()) && - (!game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) - ) { - final List objects = ComputerUtil - .predictThreatenedObjects(ai, null); + && (!game.getStack().isEmpty()) + && (!game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer()))) { + final List objects = ComputerUtil.predictThreatenedObjects(ai, null); if (objects.contains(hostCard)) { AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK); return true; @@ -42,15 +40,12 @@ public class FogAi extends SpellAbilityAi { } // Reserve mana to cast this card if it will be likely needed - if (((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) || - (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) + if (((game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) + || (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) && (AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) - && (ComputerUtil.aiLifeInDanger(ai, false, 0)) - ) { + && (ComputerUtil.aiLifeInDanger(ai, false, 0))) { ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS, true); - AiCardMemory.rememberCard(ai, hostCard, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); - } // AI should only activate this during Human's Declare Blockers phase From 8c325d82644beb1423f5fce6e45fb422b13f4f04 Mon Sep 17 00:00:00 2001 From: Agetian Date: Mon, 30 Apr 2018 09:40:53 +0300 Subject: [PATCH 13/15] - Style update. --- forge-ai/src/main/java/forge/ai/AiController.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 4c7a6b1bb06..a645ec1a81c 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -595,10 +595,8 @@ public class AiController { memSet = AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2; break; case COMBAT_DECLARE_BLOCKERS: - memSet = AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK; - if (enemy) { - memSet = AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK; - } + memSet = enemy ? AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK + : AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK; break; default: System.out.println("Warning: unsupported mana reservation phase specified for reserveManaSources: " From 4afeaeb17051f11ed873ec81fb9e7b48e46f63f7 Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 2 May 2018 08:04:31 +0300 Subject: [PATCH 14/15] - Updating AiAttackController to use an AI hint SVar for Awakening and Prophet of Kruphix. --- .../main/java/forge/ai/AiAttackController.java | 18 +++++++++++++++--- forge-gui/res/cardsfolder/a/awakening.txt | 1 + .../res/cardsfolder/p/prophet_of_kruphix.txt | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 7a6ca1f3f0b..3b44e040cbf 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -38,6 +38,7 @@ import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.util.Expressions; import forge.util.MyRandom; +import forge.util.TextUtil; import forge.util.collect.FCollectionView; import java.util.ArrayList; @@ -271,10 +272,21 @@ public class AiAttackController { } return attackers; } - // no need to block if awakening in play + + // no need to block if an effect is in play which untaps all creatures (pseudo-Vigilance akin to + // Awakening or Prophet of Kruphix) for (Card card : ai.getGame().getCardsIn(ZoneType.Battlefield)) { - if ("Awakening".equals(card.getName())) { - return attackers; + boolean untapsEachTurn = card.hasSVar("UntapsEachTurn"); + boolean untapsEachOtherTurn = card.hasSVar("UntapsEachOtherPlayerTurn"); + + if (untapsEachTurn || untapsEachOtherTurn) { + String affected = untapsEachTurn ? card.getSVar("UntapsEachTurn") + : card.getSVar("UntapsEachOtherPlayerTurn"); + for (String aff : TextUtil.split(affected, ',')) { + if (aff.equals("Creature") && (untapsEachTurn || (untapsEachOtherTurn && ai.equals(card.getController())))) { + return attackers; + } + } } } diff --git a/forge-gui/res/cardsfolder/a/awakening.txt b/forge-gui/res/cardsfolder/a/awakening.txt index 0c52bdff23e..89ff19f084a 100644 --- a/forge-gui/res/cardsfolder/a/awakening.txt +++ b/forge-gui/res/cardsfolder/a/awakening.txt @@ -3,6 +3,7 @@ ManaCost:2 G G Types:Enchantment T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ TrigUntapAll | TriggerDescription$ At the beginning of each upkeep, untap all creatures and lands. SVar:TrigUntapAll:DB$UntapAll | ValidCards$ Creature,Land | SpellDescription$ untap all creatures and lands. +SVar:UntapsEachTurn:Creature,Land SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/awakening.jpg Oracle:At the beginning of each upkeep, untap all creatures and lands. diff --git a/forge-gui/res/cardsfolder/p/prophet_of_kruphix.txt b/forge-gui/res/cardsfolder/p/prophet_of_kruphix.txt index d5ce14ce9ef..e3e983edeb4 100644 --- a/forge-gui/res/cardsfolder/p/prophet_of_kruphix.txt +++ b/forge-gui/res/cardsfolder/p/prophet_of_kruphix.txt @@ -4,5 +4,6 @@ Types:Creature Human Wizard PT:2/3 S:Mode$ Continuous | Affected$ Creature.YouCtrl,Land.YouCtrl | AddHiddenKeyword$ CARDNAME untaps during each other player's untap step. | Description$ Untap all creatures and lands you control during each other player's untap step. S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddHiddenKeyword$ Flash | AffectedZone$ Exile,Graveyard,Hand,Library,Command | Description$ You may cast creature spells as though they had flash. +SVar:UntapsEachOtherPlayerTurn:Creature,Land SVar:Picture:http://www.wizards.com/global/images/magic/general/prophet_of_kruphix.jpg Oracle:Untap all creatures and lands you control during each other player's untap step.\nYou may cast creature spells as though they had flash. From b6f55309aeb896ff76affcea862291fc62468897 Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 2 May 2018 08:19:01 +0300 Subject: [PATCH 15/15] - Some formatting. --- forge-ai/src/main/java/forge/ai/AiAttackController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 3b44e040cbf..dffb3344d36 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -282,8 +282,10 @@ public class AiAttackController { if (untapsEachTurn || untapsEachOtherTurn) { String affected = untapsEachTurn ? card.getSVar("UntapsEachTurn") : card.getSVar("UntapsEachOtherPlayerTurn"); + for (String aff : TextUtil.split(affected, ',')) { - if (aff.equals("Creature") && (untapsEachTurn || (untapsEachOtherTurn && ai.equals(card.getController())))) { + if (aff.equals("Creature") + && (untapsEachTurn || (untapsEachOtherTurn && ai.equals(card.getController())))) { return attackers; } }