diff --git a/forge-ai/src/main/java/forge/ai/AiProps.java b/forge-ai/src/main/java/forge/ai/AiProps.java index 77ba9a66bfd..5594de7300e 100644 --- a/forge-ai/src/main/java/forge/ai/AiProps.java +++ b/forge-ai/src/main/java/forge/ai/AiProps.java @@ -120,7 +120,10 @@ public enum AiProps { /** */ FLASH_CHANCE_TO_CAST_FOR_ETB_BEFORE_MAIN1("10"), /** */ FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB("0"), /** */ FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER("100"), - FLASH_CHANCE_TO_USE_AURAS_AS_COMBAT_TRICKS("80"); /** */ + FLASH_USE_AURAS_AS_COMBAT_TRICKS("true"), + FLASH_AURA_CHANCE_TO_CAST_EARLY("0"), + FLASH_AURA_CHANCE_CAST_AT_EOT("10"), + FLASH_AURA_CHANCE_TO_RESPOND_TO_STACK("0"); /** */ // Experimental features, must be promoted or removed after extensive testing and, ideally, defaulting // <-- There are no experimental options here --> 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 1454d0ff650..7b74dd97518 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -26,6 +26,7 @@ import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; +import forge.util.MyRandom; import java.util.ArrayList; import java.util.Iterator; @@ -53,15 +54,8 @@ public class AttachAi extends SpellAbilityAi { if (ai.getController().isAI()) { advancedFlash = ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.FLASH_ENABLE_ADVANCED_LOGIC); } - if (source.withFlash(ai) && source.isAura()) { - if (advancedFlash) { - Game game = ai.getGame(); - Combat combat = game.getCombat(); - - if (combat == null || game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) { - return false; - } - } + if (source.withFlash(ai) && source.isAura() && advancedFlash && !doAdvancedFlashAuraLogic(ai)) { + return false; } if (abCost != null) { @@ -134,6 +128,44 @@ public class AttachAi extends SpellAbilityAi { return true; } + private boolean doAdvancedFlashAuraLogic(Player ai) { + Game game = ai.getGame(); + Combat combat = game.getCombat(); + AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); + boolean canRespondToStack = false; + if (!game.getStack().isEmpty()) { + SpellAbility peekSa = game.getStack().peekAbility(); + Player activator = peekSa.getActivatingPlayer(); + if (activator != null && activator.isOpponentOf(ai) && peekSa.getApi() != ApiType.DestroyAll && + peekSa.getApi() != ApiType.Destroy) { + // TODO: improve this so that the AI predicts how much damage will be dealt to the creature + // so that it can try to save it (and won't bother targeting it if it can't be saved) + canRespondToStack = true; + } + } + + boolean useAurasAsTricks = aic.getBooleanProperty(AiProps.FLASH_USE_AURAS_AS_COMBAT_TRICKS); + int chanceToCastAtEOT = aic.getIntProperty(AiProps.FLASH_AURA_CHANCE_CAST_AT_EOT); + int chanceToCastEarly = aic.getIntProperty(AiProps.FLASH_AURA_CHANCE_TO_CAST_EARLY); + int chanceToRespondToStack = aic.getIntProperty(AiProps.FLASH_AURA_CHANCE_TO_RESPOND_TO_STACK); + + boolean hasFloatMana = ai.getManaPool().totalMana() > 0; + boolean willDiscardNow = game.getPhaseHandler().is(PhaseType.END_OF_TURN, ai) + && ai.getCardsIn(ZoneType.Hand).size() > ai.getMaxHandSize(); + boolean willRespondToStack = canRespondToStack && MyRandom.percentTrue(chanceToRespondToStack); + boolean willCastEarly = MyRandom.percentTrue(chanceToCastEarly); + boolean willCastAtEOT = game.getPhaseHandler().is(PhaseType.END_OF_TURN) + && game.getPhaseHandler().getNextTurn().equals(ai) && MyRandom.percentTrue(chanceToCastAtEOT) || !useAurasAsTricks; + + boolean alternativeConsiderations = hasFloatMana || willDiscardNow || willRespondToStack || willCastAtEOT || willCastEarly; + + if (!alternativeConsiderations && (combat == null || game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) { + return false; + } + + return true; + } + /** * Acceptable choice. * diff --git a/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java b/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java index 451627d834c..25fd2b80f12 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java @@ -118,14 +118,12 @@ public class PermanentCreatureAi extends PermanentAi { boolean isOwnEOT = ph.is(PhaseType.END_OF_TURN, ai); boolean isEOTBeforeMyTurn = ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai); boolean isOppDeclareAttackers = ph.is(PhaseType.COMBAT_DECLARE_ATTACKERS) && isOppTurn && ai.getGame().getCombat() != null; - boolean isMyDeclareAttackers = ph.is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai) && ai.getGame().getCombat() != null; boolean isMyMain1OrLater = ph.is(PhaseType.MAIN1, ai) || (ph.getPhase().isAfter(PhaseType.MAIN1) && ph.getPlayerTurn().equals(ai)); boolean canRespondToStack = false; if (!game.getStack().isEmpty()) { SpellAbility peekSa = game.getStack().peekAbility(); Player activator = peekSa.getActivatingPlayer(); - if (activator != null && activator.isOpponentOf(ai) - && (peekSa.getApi() != ApiType.DestroyAll || peekSa.getApi() == ApiType.Counter)) { + if (activator != null && activator.isOpponentOf(ai) && peekSa.getApi() != ApiType.DestroyAll) { canRespondToStack = true; } } diff --git a/forge-gui/res/ai/Cautious.ai b/forge-gui/res/ai/Cautious.ai index 1cd38cea295..9c286a738cb 100644 --- a/forge-gui/res/ai/Cautious.ai +++ b/forge-gui/res/ai/Cautious.ai @@ -169,8 +169,16 @@ FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB=0 # The chance the AI will attempt to add a new blocker in combat where it's low on creature count compared to the # number of attacking creatures. FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER=100 -# The chance that the AI will try to hold off playing auras with Flash until the declare blockers step in combat. -FLASH_CHANCE_TO_USE_AURAS_AS_COMBAT_TRICKS=90 +# If enabled, the AI will try to hold off playing auras with Flash until the declare blockers step in combat. +# If disabled, but advanced logic is enabled, will generally try to play these auras before its own turn. +FLASH_USE_AURAS_AS_COMBAT_TRICKS=true +# The chance that the AI will cast a flash aura enchantment at the earliest opportunity +FLASH_AURA_CHANCE_TO_CAST_EARLY=0 +# The chance that the AI will cast a flash aura at the end of turn before its own turn +FLASH_AURA_CHANCE_CAST_AT_EOT=5 +# The chance that the AI will respond to stack with a flash aura (may do silly things sometimes, so better left +# disabled or at a low chance until improved) +FLASH_AURA_CHANCE_TO_RESPOND_TO_STACK=0 # Scry AI toggles # The total number of mana-producing lands at which the AI will still consider scrying away non-lands diff --git a/forge-gui/res/ai/Default.ai b/forge-gui/res/ai/Default.ai index 5bbf462354d..fd0f751a028 100644 --- a/forge-gui/res/ai/Default.ai +++ b/forge-gui/res/ai/Default.ai @@ -170,8 +170,16 @@ FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB=0 # The chance the AI will attempt to add a new blocker in combat where it's low on creature count compared to the # number of attacking creatures. FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER=100 -# The chance that the AI will try to hold off playing auras with Flash until the declare blockers step in combat. -FLASH_CHANCE_TO_USE_AURAS_AS_COMBAT_TRICKS=80 +# If enabled, the AI will try to hold off playing auras with Flash until the declare blockers step in combat. +# If disabled, but advanced logic is enabled, will generally try to play these auras before its own turn. +FLASH_USE_AURAS_AS_COMBAT_TRICKS=true +# The chance that the AI will cast a flash aura enchantment at the earliest opportunity +FLASH_AURA_CHANCE_TO_CAST_EARLY=5 +# The chance that the AI will cast a flash aura at the end of turn before its own turn +FLASH_AURA_CHANCE_CAST_AT_EOT=10 +# The chance that the AI will respond to stack with a flash aura (may do silly things sometimes, so better left +# disabled or at a low chance until improved) +FLASH_AURA_CHANCE_TO_RESPOND_TO_STACK=0 # Scry AI toggles # The total number of mana-producing lands at which the AI will still consider scrying away non-lands diff --git a/forge-gui/res/ai/Experimental.ai b/forge-gui/res/ai/Experimental.ai index 7e7b5337ee9..addc695a53e 100644 --- a/forge-gui/res/ai/Experimental.ai +++ b/forge-gui/res/ai/Experimental.ai @@ -172,6 +172,16 @@ FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB=15 FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER=100 # The chance that the AI will try to hold off playing auras with Flash until the declare blockers step in combat. FLASH_CHANCE_TO_USE_AURAS_AS_COMBAT_TRICKS=100 +# If enabled, the AI will try to hold off playing auras with Flash until the declare blockers step in combat. +# If disabled, but advanced logic is enabled, will generally try to play these auras before its own turn. +FLASH_USE_AURAS_AS_COMBAT_TRICKS=true +# The chance that the AI will cast a flash aura enchantment at the earliest opportunity +FLASH_AURA_CHANCE_TO_CAST_EARLY=0 +# The chance that the AI will cast a flash aura at the end of turn before its own turn +FLASH_AURA_CHANCE_CAST_AT_EOT=10 +# The chance that the AI will respond to stack with a flash aura (may do silly things sometimes, so better left +# disabled or at a low chance until improved) +FLASH_AURA_CHANCE_TO_RESPOND_TO_STACK=100 # Scry AI toggles # The total number of mana-producing lands at which the AI will still consider scrying away non-lands diff --git a/forge-gui/res/ai/Reckless.ai b/forge-gui/res/ai/Reckless.ai index d7bdc96c3fb..173b62fd4b4 100644 --- a/forge-gui/res/ai/Reckless.ai +++ b/forge-gui/res/ai/Reckless.ai @@ -170,8 +170,16 @@ FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB=10 # The chance the AI will attempt to add a new blocker in combat where it's low on creature count compared to the # number of attacking creatures. FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER=100 -# The chance that the AI will try to hold off playing auras with Flash until the declare blockers step in combat. -FLASH_CHANCE_TO_USE_AURAS_AS_COMBAT_TRICKS=30 +# If enabled, the AI will try to hold off playing auras with Flash until the declare blockers step in combat. +# If disabled, but advanced logic is enabled, will generally try to play these auras before its own turn. +FLASH_USE_AURAS_AS_COMBAT_TRICKS=true +# The chance that the AI will cast a flash aura enchantment at the earliest opportunity +FLASH_AURA_CHANCE_TO_CAST_EARLY=10 +# The chance that the AI will cast a flash aura at the end of turn before its own turn +FLASH_AURA_CHANCE_CAST_AT_EOT=30 +# The chance that the AI will respond to stack with a flash aura (may do silly things sometimes, so better left +# disabled or at a low chance until improved) +FLASH_AURA_CHANCE_TO_RESPOND_TO_STACK=10 # Scry AI toggles # The total number of mana-producing lands at which the AI will still consider scrying away non-lands