mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 02:08:00 +00:00
- Attempting to fix a long-standing bug with the delayed triggers getting the wrong activator set at their resolution time (the AI was aggressively overwriting the activator via its SpellAbility simulation routines).
- Ensured that all callers of getOriginalAndAltCostAbilities both call setActivatingPlayer and then reset it to its original value if there was one after the simulation completes. Thus, an aggressive setActivatingPlayer inside getOriginalAndAltCostAbilities should not be necessary.
This commit is contained in:
@@ -545,10 +545,11 @@ public class AiController {
|
||||
|
||||
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(possibleCounters, player)) {
|
||||
SpellAbility currentSA = sa;
|
||||
|
||||
Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers
|
||||
sa.setActivatingPlayer(player);
|
||||
// check everything necessary
|
||||
|
||||
|
||||
|
||||
AiPlayDecision opinion = canPlayAndPayFor(currentSA);
|
||||
//PhaseHandler ph = game.getPhaseHandler();
|
||||
// System.out.printf("Ai thinks '%s' of %s @ %s %s >>> \n", opinion, sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase());
|
||||
@@ -559,13 +560,18 @@ public class AiController {
|
||||
} else {
|
||||
// Compare bestSA with this SA
|
||||
final int restrictionLevel = ComputerUtil.counterSpellRestriction(player, currentSA);
|
||||
|
||||
|
||||
if (restrictionLevel > bestRestriction) {
|
||||
bestRestriction = restrictionLevel;
|
||||
bestSA = currentSA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This was only a simulation, so set the original activator back if there was one
|
||||
if (originalActivator != null) {
|
||||
sa.setActivatingPlayer(originalActivator);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - "Look" at Targeted SA and "calculate" the threshold
|
||||
@@ -592,11 +598,17 @@ public class AiController {
|
||||
if (sa.getApi() == ApiType.Counter || sa.getApi() == exceptSA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers
|
||||
sa.setActivatingPlayer(player);
|
||||
// TODO: this currently only works as a limited prediction of permanent spells.
|
||||
// Ideally this should cast canPlaySa to determine that the AI is truly able/willing to cast a spell,
|
||||
// but that is currently difficult to implement due to various side effects leading to stack overflow.
|
||||
if (!ComputerUtil.castPermanentInMain1(player, sa) && sa.getHostCard() != null && !sa.getHostCard().isLand() && ComputerUtilCost.canPayCost(sa, player)) {
|
||||
if (originalActivator != null) {
|
||||
sa.setActivatingPlayer(originalActivator); // we are only simulating, so set the original activator back
|
||||
}
|
||||
|
||||
if (sa instanceof SpellPermanent) {
|
||||
return sa;
|
||||
}
|
||||
@@ -1118,6 +1130,10 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
// We need the original activating player in case the SA is on a delayed trigger waiting to be
|
||||
// activated by another player (e.g. Rainbow Vale EOT trigger).
|
||||
Player origActivatingPlayer = sa.getActivatingPlayer();
|
||||
|
||||
sa.setActivatingPlayer(player);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
@@ -1125,9 +1141,14 @@ public class AiController {
|
||||
AiPlayDecision opinion = canPlayAndPayFor(sa);
|
||||
// PhaseHandler ph = game.getPhaseHandler();
|
||||
// System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase());
|
||||
|
||||
if (opinion != AiPlayDecision.WillPlay)
|
||||
|
||||
if (origActivatingPlayer != null) {
|
||||
sa.setActivatingPlayer(origActivatingPlayer); // since this was only a simulation, restore the original activator
|
||||
}
|
||||
|
||||
if (opinion != AiPlayDecision.WillPlay) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return sa;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ public class ComputerUtilAbility {
|
||||
public static List<SpellAbility> getOriginalAndAltCostAbilities(final List<SpellAbility> originList, final Player player) {
|
||||
final List<SpellAbility> newAbilities = new ArrayList<SpellAbility>();
|
||||
for (SpellAbility sa : originList) {
|
||||
sa.setActivatingPlayer(player);
|
||||
//add alternative costs as additional spell abilities
|
||||
newAbilities.add(sa);
|
||||
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||
@@ -103,7 +102,6 @@ public class ComputerUtilAbility {
|
||||
|
||||
final List<SpellAbility> result = new ArrayList<SpellAbility>();
|
||||
for (SpellAbility sa : newAbilities) {
|
||||
sa.setActivatingPlayer(player);
|
||||
result.addAll(GameActionUtil.getOptionalCosts(sa));
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -471,11 +471,19 @@ public class SpecialCardAi {
|
||||
continue;
|
||||
}
|
||||
|
||||
Player originalActivator = testSa.getActivatingPlayer(); // needed for delayed triggers
|
||||
testSa.setActivatingPlayer(ai);
|
||||
|
||||
boolean willPlay = false;
|
||||
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSa) == AiPlayDecision.WillPlay) {
|
||||
// the AI is willing to play the spell
|
||||
return true;
|
||||
willPlay = true;
|
||||
}
|
||||
|
||||
if (originalActivator != null) {
|
||||
testSa.setActivatingPlayer(originalActivator); // we are only simulating, so set the original activator back
|
||||
}
|
||||
|
||||
if (willPlay) { return true; } // The AI seems willing to play the spell
|
||||
}
|
||||
|
||||
return false; // haven't found anything to play with the excess generated mana
|
||||
|
||||
@@ -156,7 +156,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
SpellAbility testSaNoCost = testSa.copyWithNoManaCost();
|
||||
testSaNoCost.setActivatingPlayer(ai);
|
||||
testSaNoCost.setActivatingPlayer(ai); // should be safe since we're operating on a copy
|
||||
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) {
|
||||
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste")
|
||||
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
||||
|
||||
@@ -81,15 +81,23 @@ public class SpellAbilityPicker {
|
||||
if (sa.isManaAbility()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers
|
||||
sa.setActivatingPlayer(player);
|
||||
|
||||
AiPlayDecision opinion = canPlayAndPayForSim(sa);
|
||||
// print(" " + opinion + ": " + sa);
|
||||
// PhaseHandler ph = game.getPhaseHandler();
|
||||
// System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase());
|
||||
|
||||
if (opinion != AiPlayDecision.WillPlay)
|
||||
|
||||
if (originalActivator != null) {
|
||||
sa.setActivatingPlayer(originalActivator); // we are only simulating, so set the original activator back
|
||||
}
|
||||
|
||||
if (opinion != AiPlayDecision.WillPlay) {
|
||||
continue;
|
||||
}
|
||||
|
||||
candidateSAs.set(writeIndex, sa);
|
||||
writeIndex++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user