- Added a full advanced Flash Attach Ai infrastructure and the basic logic and toggles for it.

This commit is contained in:
Agetian
2018-11-22 09:03:15 +03:00
parent 874c545a08
commit 0b0c08631a
7 changed files with 86 additions and 19 deletions

View File

@@ -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 -->

View File

@@ -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.
*

View File

@@ -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;
}
}