Better fix for Bow of Nylea / avoid view update if AI only

This commit is contained in:
tool4EvEr
2022-11-24 09:34:36 +01:00
parent bc1d4f3dce
commit 363631990a
26 changed files with 53 additions and 56 deletions

View File

@@ -1424,7 +1424,7 @@ public class AiAttackController {
continue;
}
if (sa.usesTargeting()) {
sa.setActivatingPlayer(c.getController());
sa.setActivatingPlayer(c.getController(), true);
List<Card> validTargets = CardUtil.getValidCardsToTarget(sa.getTargetRestrictions(), sa);
if (validTargets.isEmpty()) {
missTarget = true;

View File

@@ -165,7 +165,7 @@ public class AiController {
for (final Card c : all) {
for (final SpellAbility sa : c.getNonManaAbilities()) {
if (sa instanceof SpellPermanent) {
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
if (checkETBEffects(c, sa, ApiType.Counter)) {
spellAbilities.add(sa);
}
@@ -499,7 +499,7 @@ public class AiController {
if (reSA == null || !ApiType.Tap.equals(reSA.getApi())) {
continue;
}
reSA.setActivatingPlayer(reSA.getHostCard().getController());
reSA.setActivatingPlayer(reSA.getHostCard().getController(), true);
if (reSA.metConditions()) {
foundTapped = true;
break;
@@ -583,7 +583,7 @@ public class AiController {
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(possibleCounters, player)) {
SpellAbility currentSA = sa;
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
// check everything necessary
AiPlayDecision opinion = canPlayAndPayFor(currentSA);
@@ -638,7 +638,7 @@ public class AiController {
if (saApi == ApiType.Counter || saApi == exceptSA) {
continue;
}
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
// 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.
@@ -1734,7 +1734,7 @@ public class AiController {
}
}
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
SpellAbility root = sa.getRootAbility();
if (root.isSpell() || root.isTrigger() || root.isReplacementAbility()) {

View File

@@ -244,7 +244,7 @@ public class ComputerUtil {
// this is used for AI's counterspells
public static final boolean playStack(SpellAbility sa, final Player ai, final Game game) {
sa.setActivatingPlayer(ai);
sa.setActivatingPlayer(ai, true);
if (!ComputerUtilCost.canPayCost(sa, ai, false))
return false;
@@ -284,7 +284,7 @@ public class ComputerUtil {
public static final void playSpellAbilityForFree(final Player ai, final SpellAbility sa) {
final Game game = ai.getGame();
sa.setActivatingPlayer(ai);
sa.setActivatingPlayer(ai, true);
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
@@ -296,7 +296,7 @@ public class ComputerUtil {
public static final boolean playSpellAbilityWithoutPayingManaCost(final Player ai, final SpellAbility sa, final Game game) {
SpellAbility newSA = sa.copyWithNoManaCost();
newSA.setActivatingPlayer(ai);
newSA.setActivatingPlayer(ai, true);
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA) || !ComputerUtilMana.canPayManaCost(newSA, ai, 0, false)) {
return false;
@@ -336,7 +336,7 @@ public class ComputerUtil {
}
public static final void playNoStack(final Player ai, SpellAbility sa, final Game game, final boolean effect) {
sa.setActivatingPlayer(ai);
sa.setActivatingPlayer(ai, true);
// TODO: We should really restrict what doesn't use the Stack
if (ComputerUtilCost.canPayCost(sa, ai, effect)) {
final Card source = sa.getHostCard();
@@ -955,7 +955,7 @@ public class ComputerUtil {
if (!sa.isActivatedAbility() || sa.getApi() != ApiType.Regenerate) {
continue; // Not a Regenerate ability
}
sa.setActivatingPlayer(controller);
sa.setActivatingPlayer(controller, true);
if (!(sa.canPlay() && ComputerUtilCost.canPayCost(sa, controller, false))) {
continue; // Can't play ability
}
@@ -1530,7 +1530,7 @@ public class ComputerUtil {
if (sa.getApi() != ApiType.DealDamage) {
continue;
}
sa.setActivatingPlayer(ai);
sa.setActivatingPlayer(ai, true);
final String numDam = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), numDam, sa);
if (dmg <= damage) {
@@ -2984,7 +2984,7 @@ public class ComputerUtil {
}
SpellAbility abTest = withoutPayingManaCost ? ab.copyWithNoManaCost() : ab.copy();
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
abTest.setActivatingPlayer(ai);
abTest.setActivatingPlayer(ai, true);
abTest.getRestrictions().setZone(c.getZone().getZoneType());
if (AiPlayDecision.WillPlay == aic.canPlaySa(abTest) && ComputerUtilCost.canPayCost(abTest, ai, false)) {
targets.add(c);

View File

@@ -91,7 +91,7 @@ public class ComputerUtilAbility {
List<SpellAbility> originListWithAddCosts = Lists.newArrayList();
for (SpellAbility sa : originList) {
// If this spell has alternative additional costs, add them instead of the unmodified SA itself
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
originListWithAddCosts.addAll(GameActionUtil.getAdditionalCostSpell(sa));
}
@@ -118,7 +118,7 @@ public class ComputerUtilAbility {
final List<SpellAbility> result = Lists.newArrayList();
for (SpellAbility sa : newAbilities) {
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
// Optional cost selection through the AI controller
boolean choseOptCost = false;

View File

@@ -687,7 +687,7 @@ public class ComputerUtilCard {
if (!ComputerUtilCost.canPayCost(sa, opp, sa.isTrigger())) {
continue;
}
sa.setActivatingPlayer(opp);
sa.setActivatingPlayer(opp, true);
if (sa.canTarget(card)) {
continue;
}

View File

@@ -1253,7 +1253,7 @@ public class ComputerUtilCombat {
continue;
}
sa.setActivatingPlayer(source.getController());
sa.setActivatingPlayer(source.getController(), true);
if (sa.hasParam("Cost")) {
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
@@ -1433,7 +1433,7 @@ public class ComputerUtilCombat {
if (sa == null) {
continue;
}
sa.setActivatingPlayer(source.getController());
sa.setActivatingPlayer(source.getController(), true);
if (sa.usesTargeting()) {
continue; // targeted pumping not supported

View File

@@ -526,7 +526,7 @@ public class ComputerUtilCost {
*/
public static boolean canPayCost(final SpellAbility sa, final Player player, final boolean effect) {
if (sa.getActivatingPlayer() == null) {
sa.setActivatingPlayer(player); // complaints on NPE had came before this line was added.
sa.setActivatingPlayer(player, true); // complaints on NPE had came before this line was added.
}
final boolean cannotBeCountered = !CardFactoryUtil.isCounterable(sa.getHostCard());

View File

@@ -82,7 +82,7 @@ public class ComputerUtilMana {
public static boolean hasEnoughManaSourcesToCast(final SpellAbility sa, final Player ai) {
if (ai == null || sa == null)
return false;
sa.setActivatingPlayer(ai);
sa.setActivatingPlayer(ai, true);
return payManaCost(sa, ai, true, 0, false, false);
}
@@ -90,7 +90,7 @@ public class ComputerUtilMana {
int score = 0;
for (SpellAbility ability : card.getSpellAbilities()) {
ability.setActivatingPlayer(card.getController());
ability.setActivatingPlayer(card.getController(), true);
if (ability.isManaAbility()) {
score += ability.calculateScoreForManaAbility();
}
@@ -1012,7 +1012,7 @@ public class ComputerUtilMana {
if (checkCosts) {
// Check if AI can still play this mana ability
ma.setActivatingPlayer(ai);
ma.setActivatingPlayer(ai, true);
// if the AI can't pay the additional costs skip the mana ability
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
return false;
@@ -1420,7 +1420,7 @@ public class ComputerUtilMana {
maxProduced = 0;
for (SpellAbility ma : src.getManaAbilities()) {
ma.setActivatingPlayer(p);
ma.setActivatingPlayer(p, true);
if (!checkPlayable || ma.canPlay()) {
int costsToActivate = ma.getPayCosts().getCostMana() != null ? ma.getPayCosts().getCostMana().convertAmount() : 0;
int producedMana = ma.getParamOrDefault("Produced", "").split(" ").length;
@@ -1459,7 +1459,7 @@ public class ComputerUtilMana {
@Override
public boolean apply(final Card c) {
for (final SpellAbility am : getAIPlayableMana(c)) {
am.setActivatingPlayer(ai);
am.setActivatingPlayer(ai, true);
if (!checkPlayable || (am.canPlay() && am.checkRestrictions(ai))) {
return true;
}
@@ -1516,7 +1516,7 @@ public class ComputerUtilMana {
needsLimitedResources |= !cost.isReusuableResource();
// if the AI can't pay the additional costs skip the mana ability
m.setActivatingPlayer(ai);
m.setActivatingPlayer(ai, true);
if (!CostPayment.canPayAdditionalCosts(m.getPayCosts(), m)) {
continue;
}
@@ -1585,7 +1585,7 @@ public class ComputerUtilMana {
if (DEBUG_MANA_PAYMENT) {
System.out.println("DEBUG_MANA_PAYMENT: groupSourcesByManaColor m = " + m);
}
m.setActivatingPlayer(ai);
m.setActivatingPlayer(ai, true);
if (checkPlayable && !m.canPlay()) {
continue;
}

View File

@@ -907,7 +907,6 @@ public abstract class GameState {
for (SpellAbility ab : saList) {
if (ab.getDescription().startsWith("Awaken")) {
ab.setActivatingPlayer(c.getController());
ab.getSubAbility().setActivatingPlayer(c.getController());
// target for Awaken is set in its first subability
handleScriptedTargetingForSA(game, ab.getSubAbility(), tgtID);
sa = kwName.equals("AwakenOnly") ? ab.getSubAbility() : ab;

View File

@@ -695,7 +695,7 @@ public class PlayerControllerAi extends PlayerController {
public boolean payManaOptional(Card c, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose) {
// TODO replace with EmptySa
final Ability ability = new AbilityStatic(c, cost, null) { @Override public void resolve() {} };
ability.setActivatingPlayer(c.getController());
ability.setActivatingPlayer(c.getController(), true);
ability.setCardState(sa.getCardState());
// FIXME: This is a hack to check if the AI can play the "exile from library" pay costs (Cumulative Upkeep,
@@ -1086,7 +1086,7 @@ public class PlayerControllerAi extends PlayerController {
final Card source = sa.getHostCard();
// TODO replace with EmptySa
final Ability emptyAbility = new AbilityStatic(source, cost, sa.getTargetRestrictions()) { @Override public void resolve() { } };
emptyAbility.setActivatingPlayer(player);
emptyAbility.setActivatingPlayer(player, true);
emptyAbility.setTriggeringObjects(sa.getTriggeringObjects());
emptyAbility.setSVars(sa.getSVars());
emptyAbility.setCardState(sa.getCardState());

View File

@@ -118,7 +118,7 @@ public abstract class SpellAbilityAi {
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
if (aiLogic.equals("CheckCondition")) {
SpellAbility saCopy = sa.copy();
saCopy.setActivatingPlayer(ai);
saCopy.setActivatingPlayer(ai, true);
return saCopy.metConditions();
}

View File

@@ -121,7 +121,7 @@ public class AttachAi extends SpellAbilityAi {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Chained to the Rocks")) {
final SpellAbility effectExile = AbilityFactory.getAbility(source.getSVar("TrigExile"), source);
effectExile.setActivatingPlayer(ai);
effectExile.setActivatingPlayer(ai, true);
final TargetRestrictions exile_tgt = effectExile.getTargetRestrictions();
final List<Card> targets = CardUtil.getValidCardsToTarget(exile_tgt, effectExile);
return !targets.isEmpty();

View File

@@ -88,7 +88,7 @@ public class CharmAi extends SpellAbilityAi {
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
// First pass using standard canPlayAi() for good choices
for (AbilitySub sub : choices) {
sub.setActivatingPlayer(ai);
sub.setActivatingPlayer(ai, true);
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
chosenList.add(sub);
if (chosenList.size() == num) {
@@ -219,13 +219,13 @@ public class CharmAi extends SpellAbilityAi {
List<AbilitySub> chosenList = Lists.newArrayList();
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
for (AbilitySub sub : choices) {
sub.setActivatingPlayer(ai);
sub.setActivatingPlayer(ai, true);
// Assign generic good choice to fill up choices if necessary
if ("Good".equals(sub.getParam("AILogic")) && aic.doTrigger(sub, false)) {
goodChoice = sub;
} else {
// Standard canPlayAi()
sub.setActivatingPlayer(ai);
sub.setActivatingPlayer(ai, true);
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
chosenList.add(sub);
if (chosenList.size() == min) {

View File

@@ -102,7 +102,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
} else if ("PayUnlessCost".equals(logic)) {
for (final SpellAbility sp : spells) {
String unlessCost = sp.getParam("UnlessCost");
sp.setActivatingPlayer(sa.getActivatingPlayer());
sp.setActivatingPlayer(sa.getActivatingPlayer(), true);
Cost unless = new Cost(unlessCost, false);
SpellAbility paycost = new SpellAbility.EmptySa(sa.getHostCard(), player);
paycost.setPayCosts(unless);

View File

@@ -427,7 +427,7 @@ public class CountersPutAi extends CountersAi {
}
// need to set Activating player
oa.setActivatingPlayer(ai);
oa.setActivatingPlayer(ai, true);
CardCollection targets = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), oa);
if (!targets.isEmpty()) {

View File

@@ -26,7 +26,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
if (trigsa == null) {
return false;
}
trigsa.setActivatingPlayer(ai);
trigsa.setActivatingPlayer(ai, true);
if (trigsa instanceof AbilitySub) {
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
@@ -43,7 +43,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
}
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
trigsa.setActivatingPlayer(ai);
trigsa.setActivatingPlayer(ai, true);
if (!sa.hasParam("OptionalDecider")) {
return aic.doTrigger(trigsa, true);
@@ -164,7 +164,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
if (trigsa == null) {
return false;
}
trigsa.setActivatingPlayer(ai);
trigsa.setActivatingPlayer(ai, true);
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
}

View File

@@ -20,7 +20,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
return false;
}
trigsa.setActivatingPlayer(ai);
trigsa.setActivatingPlayer(ai, true);
if (trigsa instanceof AbilitySub) {
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
@@ -48,7 +48,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
}
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
trigsa.setActivatingPlayer(ai);
trigsa.setActivatingPlayer(ai, true);
return aic.doTrigger(trigsa, !"You".equals(sa.getParamOrDefault("OptionalDecider", "You")));
}
@@ -65,7 +65,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
return false;
}
trigsa.setActivatingPlayer(ai);
trigsa.setActivatingPlayer(ai, true);
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
}

View File

@@ -226,7 +226,7 @@ public class ManaEffectAi extends SpellAbilityAi {
if (testSaNoCost == null) {
continue;
}
testSaNoCost.setActivatingPlayer(ai);
testSaNoCost.setActivatingPlayer(ai, true);
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) {
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword(Keyword.HASTE)
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {

View File

@@ -176,7 +176,7 @@ public class PermanentAi extends SpellAbilityAi {
emptyAbility.setPayCosts(new Cost(costs, true));
emptyAbility.setTargetRestrictions(sa.getTargetRestrictions());
emptyAbility.setCardState(sa.getCardState());
emptyAbility.setActivatingPlayer(ai);
emptyAbility.setActivatingPlayer(ai, true);
if (!ComputerUtilCost.canPayCost(emptyAbility, ai, true)) {
// AiPlayDecision.AnotherTime

View File

@@ -38,7 +38,7 @@ public class PermanentNoncreatureAi extends PermanentAi {
if (host.hasSVar("OblivionRing")) {
SpellAbility effectExile = AbilityFactory.getAbility(host.getSVar("TrigExile"), host);
final ZoneType origin = ZoneType.listValueOf(effectExile.getParam("Origin")).get(0);
effectExile.setActivatingPlayer(ai);
effectExile.setActivatingPlayer(ai, true);
CardCollection targets = CardLists.getTargetableCards(game.getCardsIn(origin), effectExile);
if (sourceName.equals("Suspension Field")
|| sourceName.equals("Detention Sphere")) {

View File

@@ -148,7 +148,7 @@ public class PlayAi extends SpellAbilityAi {
// of which spell was the reason for the choice can be used there
for (SpellAbility s : c.getBasicSpells(c.getState(CardStateName.Original))) {
Spell spell = (Spell) s;
s.setActivatingPlayer(ai);
s.setActivatingPlayer(ai, true);
// timing restrictions still apply
if (!s.getRestrictions().checkTimingRestrictions(c, s))
continue;

View File

@@ -39,7 +39,7 @@ public class RevealAi extends RevealAiBase {
final Card c = sa.getHostCard();
for (SpellAbility s : c.getBasicSpells()) {
Spell spell = (Spell) s;
s.setActivatingPlayer(ai);
s.setActivatingPlayer(ai, true);
// timing restrictions still apply
if (!s.getRestrictions().checkTimingRestrictions(c, s))
continue;

View File

@@ -47,7 +47,7 @@ public class VentureAi extends SpellAbilityAi {
for (SpellAbility room : spells) {
if (player.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player?
room.setActivatingPlayer(player);
room.setActivatingPlayer(player, true);
if (((PlayerControllerAi)player.getController()).getAi().canPlaySa(room) == AiPlayDecision.WillPlay) {
viableRooms.add(room);
}

View File

@@ -65,7 +65,7 @@ public class PossibleTargetSelector {
if (targetingSa == null) {
return;
}
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
targetingSa.resetTargets();
tgt = targetingSa.getTargetRestrictions();
maxTargets = tgt.getMaxTargets(sa.getHostCard(), targetingSa);

View File

@@ -84,7 +84,7 @@ public class SpellAbilityPicker {
if (sa.isManaAbility()) {
continue;
}
sa.setActivatingPlayer(player);
sa.setActivatingPlayer(player, true);
AiPlayDecision opinion = canPlayAndPayForSim(sa);
// print(" " + opinion + ": " + sa);

View File

@@ -65,12 +65,10 @@ public class CharmEffect extends SpellAbilityEffect {
// using getCardForUi game is not set, so can't guess max charm
num = Integer.MAX_VALUE;
} else {
// fallback needed while ability building @TRT please check why it broke CharmEffect without using try-catch
try {
if (sa.getActivatingPlayer() == null) {
sa.setActivatingPlayer(source.getController());
}
} catch (Exception e) {}
// fallback needed while ability building
if (sa.getActivatingPlayer() == null) {
sa.setActivatingPlayer(source.getController(), true);
}
num = Math.min(AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa), list.size());
}
final int min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParam("MinCharmNum"), sa) : num;