Merge pull request #2100 from tool4ever/somefixes

Improve damage check logic for losing + performance
This commit is contained in:
Anthony Calosa
2022-12-15 07:38:42 +08:00
committed by GitHub
7 changed files with 44 additions and 53 deletions

View File

@@ -814,9 +814,6 @@ public class AiController {
} }
public AiPlayDecision canPlaySa(SpellAbility sa) { public AiPlayDecision canPlaySa(SpellAbility sa) {
final Card card = sa.getHostCard();
final boolean isRightTiming = sa.canCastTiming(player);
if (!checkAiSpecificRestrictions(sa)) { if (!checkAiSpecificRestrictions(sa)) {
return AiPlayDecision.CantPlayAi; return AiPlayDecision.CantPlayAi;
} }
@@ -824,6 +821,12 @@ public class AiController {
return canPlaySa(((WrappedAbility) sa).getWrappedAbility()); return canPlaySa(((WrappedAbility) sa).getWrappedAbility());
} }
if (!sa.canCastTiming(player)) {
return AiPlayDecision.AnotherTime;
}
final Card card = sa.getHostCard();
// Trying to play a card that has Buyback without a Buyback cost, look for possible additional considerations // Trying to play a card that has Buyback without a Buyback cost, look for possible additional considerations
if (getBooleanProperty(AiProps.TRY_TO_PRESERVE_BUYBACK_SPELLS)) { if (getBooleanProperty(AiProps.TRY_TO_PRESERVE_BUYBACK_SPELLS)) {
if (card.hasKeyword(Keyword.BUYBACK) && !sa.isBuyBackAbility() && !canPlaySpellWithoutBuyback(card, sa)) { if (card.hasKeyword(Keyword.BUYBACK) && !sa.isBuyBackAbility() && !canPlaySpellWithoutBuyback(card, sa)) {
@@ -898,9 +901,6 @@ public class AiController {
return AiPlayDecision.AnotherTime; return AiPlayDecision.AnotherTime;
} }
if (sa instanceof SpellPermanent) { if (sa instanceof SpellPermanent) {
if (!isRightTiming) {
return AiPlayDecision.AnotherTime;
}
return canPlayFromEffectAI((SpellPermanent)sa, false, true); return canPlayFromEffectAI((SpellPermanent)sa, false, true);
} }
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
@@ -912,18 +912,13 @@ public class AiController {
} }
} }
if (sa instanceof Spell) { if (sa instanceof Spell) {
if (ComputerUtil.getDamageForPlaying(player, sa) >= player.getLife() if (!player.cantLoseForZeroOrLessLife() && player.canLoseLife() &&
&& !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) { ComputerUtil.getDamageForPlaying(player, sa) >= player.getLife()) {
return AiPlayDecision.CurseEffects; return AiPlayDecision.CurseEffects;
} }
if (!isRightTiming) {
return AiPlayDecision.AnotherTime;
}
return canPlaySpellBasic(card, sa); return canPlaySpellBasic(card, sa);
} }
if (!isRightTiming) {
return AiPlayDecision.AnotherTime;
}
return AiPlayDecision.WillPlay; return AiPlayDecision.WillPlay;
} }
@@ -1411,8 +1406,8 @@ public class AiController {
if (!checkETBEffects(card, spell, null)) { if (!checkETBEffects(card, spell, null)) {
return AiPlayDecision.BadEtbEffects; return AiPlayDecision.BadEtbEffects;
} }
if (damage + ComputerUtil.getDamageFromETB(player, card) >= player.getLife() if (!player.cantLoseForZeroOrLessLife() && player.canLoseLife()
&& !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) { && damage + ComputerUtil.getDamageFromETB(player, card) >= player.getLife()) {
return AiPlayDecision.BadEtbEffects; return AiPlayDecision.BadEtbEffects;
} }
} }
@@ -1496,35 +1491,33 @@ public class AiController {
if (landsWannaPlay != null && !landsWannaPlay.isEmpty()) { if (landsWannaPlay != null && !landsWannaPlay.isEmpty()) {
// TODO search for other land it might want to play? // TODO search for other land it might want to play?
Card land = chooseBestLandToPlay(landsWannaPlay); Card land = chooseBestLandToPlay(landsWannaPlay);
if (ComputerUtil.getDamageFromETB(player, land) < player.getLife() || !player.canLoseLife() if ((!player.canLoseLife() || player.cantLoseForZeroOrLessLife() || ComputerUtil.getDamageFromETB(player, land) < player.getLife())
|| player.cantLoseForZeroOrLessLife() ) { && (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land))) {
if (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land)) { final List<SpellAbility> abilities = Lists.newArrayList();
final List<SpellAbility> abilities = Lists.newArrayList();
// TODO extend this logic to evaluate MDFC with both sides land // TODO extend this logic to evaluate MDFC with both sides land
// this can only happen if its a MDFC land // this can only happen if its a MDFC land
if (!land.isLand()) { if (!land.isLand()) {
land.setState(CardStateName.Modal, true); land.setState(CardStateName.Modal, true);
land.setBackSide(true); land.setBackSide(true);
} }
LandAbility la = new LandAbility(land, player, null); LandAbility la = new LandAbility(land, player, null);
la.setCardState(land.getCurrentState());
if (la.canPlay()) {
abilities.add(la);
}
// add mayPlay option
for (CardPlayOption o : land.mayPlay(player)) {
la = new LandAbility(land, player, o.getAbility());
la.setCardState(land.getCurrentState()); la.setCardState(land.getCurrentState());
if (la.canPlay()) { if (la.canPlay()) {
abilities.add(la); abilities.add(la);
} }
}
// add mayPlay option if (!abilities.isEmpty()) {
for (CardPlayOption o : land.mayPlay(player)) { return abilities;
la = new LandAbility(land, player, o.getAbility());
la.setCardState(land.getCurrentState());
if (la.canPlay()) {
abilities.add(la);
}
}
if (!abilities.isEmpty()) {
return abilities;
}
} }
} }
} }

View File

@@ -100,20 +100,19 @@ public abstract class DamageAiBase extends SpellAbilityAi {
return false; return false;
} }
if (!enemy.canLoseLife()) {
return false;
}
if (!noPrevention) { if (!noPrevention) {
restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, hostcard, false); restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, hostcard, false);
} else { } else {
restDamage = enemy.staticReplaceDamage(restDamage, hostcard, false); restDamage = enemy.staticReplaceDamage(restDamage, hostcard, false);
} }
if (restDamage == 0) { if (restDamage == 0) {
return false; return false;
} }
if (!enemy.canLoseLife()) {
return false;
}
final CardCollectionView hand = comp.getCardsIn(ZoneType.Hand); final CardCollectionView hand = comp.getCardsIn(ZoneType.Hand);
if ((enemy.getLife() - restDamage) < 5) { if ((enemy.getLife() - restDamage) < 5) {

View File

@@ -1420,10 +1420,8 @@ public class GameAction {
checkAgain = true; checkAgain = true;
} }
} }
if (!spaceSculptors.isEmpty()) { for (Player p : spaceSculptors) {
for (Player p : spaceSculptors) { checkAgain |= stateBasedAction704_5u(p);
checkAgain |= stateBasedAction704_5u(p);
}
} }
// 704.5m World rule // 704.5m World rule
checkAgain |= handleWorldRule(noRegCreats); checkAgain |= handleWorldRule(noRegCreats);

View File

@@ -80,8 +80,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
} }
// special handling for Karn to filter out non-cards // special handling for Karn to filter out non-cards
CardCollection cmdCards = new CardCollection(p.getCardsIn(ZoneType.Command)); for (Card c : p.getCardsIn(ZoneType.Command)) {
for (Card c : cmdCards) {
if (c.isCommander()) { if (c.isCommander()) {
newLibrary.add(c); newLibrary.add(c);
} }

View File

@@ -124,13 +124,15 @@ public class RollDiceEffect extends SpellAbilityEffect {
total += modifier; total += modifier;
// Run triggers // Run triggers
int rollNum = 1;
for (Integer roll : rolls) { for (Integer roll : rolls) {
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player); final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player);
runParams.put(AbilityKey.Sides, sides); runParams.put(AbilityKey.Sides, sides);
runParams.put(AbilityKey.Modifier, modifier); runParams.put(AbilityKey.Modifier, modifier);
runParams.put(AbilityKey.Result, roll); runParams.put(AbilityKey.Result, roll);
runParams.put(AbilityKey.Number, player.getNumRollsThisTurn() + 1 - amount + rolls.indexOf(roll)); runParams.put(AbilityKey.Number, player.getNumRollsThisTurn() - amount + rollNum);
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false); player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDie, runParams, false);
rollNum++;
} }
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player); final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player);
runParams.put(AbilityKey.Sides, sides); runParams.put(AbilityKey.Sides, sides);

View File

@@ -1921,7 +1921,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
public final boolean cantLoseForZeroOrLessLife() { public final boolean cantLoseForZeroOrLessLife() {
return hasKeyword("You don't lose the game for having 0 or less life."); return cantLose() || hasKeyword("You don't lose the game for having 0 or less life.");
} }
public final boolean cantWin() { public final boolean cantWin() {

View File

@@ -585,7 +585,7 @@ public class TargetRestrictions {
final Card srcCard = sa.getHostCard(); // should there be OrginalHost at any moment? final Card srcCard = sa.getHostCard(); // should there be OrginalHost at any moment?
for (final Card c : game.getCardsIn(this.tgtZone)) { for (final Card c : game.getCardsIn(this.tgtZone)) {
if (c.isValid(this.validTgts, srcCard.getController(), srcCard, sa) if (c.isValid(this.validTgts, sa.getActivatingPlayer(), srcCard, sa)
&& (!isTargeted || sa.canTarget(c)) && (!isTargeted || sa.canTarget(c))
&& !sa.getTargets().contains(c)) { && !sa.getTargets().contains(c)) {
candidates.add(c); candidates.add(c);