mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +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)) {
|
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(possibleCounters, player)) {
|
||||||
SpellAbility currentSA = sa;
|
SpellAbility currentSA = sa;
|
||||||
|
|
||||||
|
Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
// check everything necessary
|
// check everything necessary
|
||||||
|
|
||||||
|
|
||||||
AiPlayDecision opinion = canPlayAndPayFor(currentSA);
|
AiPlayDecision opinion = canPlayAndPayFor(currentSA);
|
||||||
//PhaseHandler ph = game.getPhaseHandler();
|
//PhaseHandler ph = game.getPhaseHandler();
|
||||||
// System.out.printf("Ai thinks '%s' of %s @ %s %s >>> \n", opinion, sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase());
|
// System.out.printf("Ai thinks '%s' of %s @ %s %s >>> \n", opinion, sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase());
|
||||||
@@ -566,6 +567,11 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// TODO - "Look" at Targeted SA and "calculate" the threshold
|
||||||
@@ -592,11 +598,17 @@ public class AiController {
|
|||||||
if (sa.getApi() == ApiType.Counter || sa.getApi() == exceptSA) {
|
if (sa.getApi() == ApiType.Counter || sa.getApi() == exceptSA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
// TODO: this currently only works as a limited prediction of permanent spells.
|
// 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,
|
// 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.
|
// 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 (!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) {
|
if (sa instanceof SpellPermanent) {
|
||||||
return sa;
|
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.setActivatingPlayer(player);
|
||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||||
@@ -1126,8 +1142,13 @@ public class AiController {
|
|||||||
// PhaseHandler ph = game.getPhaseHandler();
|
// 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());
|
// 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;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ public class ComputerUtilAbility {
|
|||||||
public static List<SpellAbility> getOriginalAndAltCostAbilities(final List<SpellAbility> originList, final Player player) {
|
public static List<SpellAbility> getOriginalAndAltCostAbilities(final List<SpellAbility> originList, final Player player) {
|
||||||
final List<SpellAbility> newAbilities = new ArrayList<SpellAbility>();
|
final List<SpellAbility> newAbilities = new ArrayList<SpellAbility>();
|
||||||
for (SpellAbility sa : originList) {
|
for (SpellAbility sa : originList) {
|
||||||
sa.setActivatingPlayer(player);
|
|
||||||
//add alternative costs as additional spell abilities
|
//add alternative costs as additional spell abilities
|
||||||
newAbilities.add(sa);
|
newAbilities.add(sa);
|
||||||
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||||
@@ -103,7 +102,6 @@ public class ComputerUtilAbility {
|
|||||||
|
|
||||||
final List<SpellAbility> result = new ArrayList<SpellAbility>();
|
final List<SpellAbility> result = new ArrayList<SpellAbility>();
|
||||||
for (SpellAbility sa : newAbilities) {
|
for (SpellAbility sa : newAbilities) {
|
||||||
sa.setActivatingPlayer(player);
|
|
||||||
result.addAll(GameActionUtil.getOptionalCosts(sa));
|
result.addAll(GameActionUtil.getOptionalCosts(sa));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -471,11 +471,19 @@ public class SpecialCardAi {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player originalActivator = testSa.getActivatingPlayer(); // needed for delayed triggers
|
||||||
testSa.setActivatingPlayer(ai);
|
testSa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
|
boolean willPlay = false;
|
||||||
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSa) == AiPlayDecision.WillPlay) {
|
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSa) == AiPlayDecision.WillPlay) {
|
||||||
// the AI is willing to play the spell
|
willPlay = true;
|
||||||
return 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
|
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();
|
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 (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) {
|
||||||
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste")
|
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste")
|
||||||
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ public class SpellAbilityPicker {
|
|||||||
if (sa.isManaAbility()) {
|
if (sa.isManaAbility()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
|
|
||||||
AiPlayDecision opinion = canPlayAndPayForSim(sa);
|
AiPlayDecision opinion = canPlayAndPayForSim(sa);
|
||||||
@@ -88,8 +90,14 @@ public class SpellAbilityPicker {
|
|||||||
// PhaseHandler ph = game.getPhaseHandler();
|
// 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());
|
// 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;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
candidateSAs.set(writeIndex, sa);
|
candidateSAs.set(writeIndex, sa);
|
||||||
writeIndex++;
|
writeIndex++;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user