mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Merge branch 'blockfix' into 'master'
AiBlockController: fix for menace + trample See merge request core-developers/forge!5614
This commit is contained in:
@@ -185,7 +185,7 @@ public class AiBlockController {
|
||||
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
|
||||
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) {
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ public class AiBlockController {
|
||||
|
||||
// 6. Blockers that don't survive until the next turn anyway
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) {
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -537,7 +537,7 @@ public class AiBlockController {
|
||||
|
||||
// Try to block a Menace attacker with two blockers, neither of which will die
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() <= 1) {
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ public class AiBlockController {
|
||||
List<Card> killingBlockers;
|
||||
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) {
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > 1) {
|
||||
continue;
|
||||
}
|
||||
if (ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||
@@ -642,7 +642,7 @@ public class AiBlockController {
|
||||
|
||||
Card attacker = attackers.get(0);
|
||||
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > 1
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
|| ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||
attackers.remove(0);
|
||||
@@ -691,7 +691,7 @@ public class AiBlockController {
|
||||
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
|
||||
|
||||
for (final Card attacker : currentAttackers) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() <= 1) {
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) <= 1) {
|
||||
continue;
|
||||
}
|
||||
List<Card> possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
@@ -729,8 +729,7 @@ public class AiBlockController {
|
||||
// "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
|
||||
|
||||
for (final Card attacker : tramplingAttackers) {
|
||||
|
||||
if (((StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) && !combat.isBlocked(attacker))
|
||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > combat.getBlockers(attacker).size()
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
continue;
|
||||
@@ -829,7 +828,7 @@ public class AiBlockController {
|
||||
|
||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||
final int evalThresholdToken = aic.getIntProperty(AiProps.THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER);
|
||||
final int evalThresholdNonToken = aic.getIntProperty(AiProps.THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER);
|
||||
final int evalThresholdNonToken = aic.getIntProperty(AiProps.THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER);
|
||||
final boolean onlyIfLethal = aic.getBooleanProperty(AiProps.CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL);
|
||||
|
||||
if (evalThresholdToken > 0 || evalThresholdNonToken > 0) {
|
||||
|
||||
@@ -1776,8 +1776,7 @@ public class ComputerUtilCombat {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attacker may kill the blocker before he can deal normal
|
||||
// (secondary) damage
|
||||
// Attacker may kill the blocker before he can deal normal (secondary) damage
|
||||
if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
|
||||
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
if (attackerDamage >= defenderLife) {
|
||||
@@ -1828,7 +1827,7 @@ public class ComputerUtilCombat {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean blockerWouldBeDestroyed(Player ai, final Card blocker, Combat combat) {
|
||||
// TODO THis function only checks if a single attacker at a time would destroy a blocker
|
||||
// TODO This function only checks if a single attacker at a time would destroy a blocker
|
||||
// This needs to expand to tally up damage
|
||||
final List<Card> attackers = combat.getAttackersBlockedBy(blocker);
|
||||
|
||||
@@ -2010,8 +2009,7 @@ public class ComputerUtilCombat {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attacker may kill the blocker before he can deal normal
|
||||
// (secondary) damage
|
||||
// Attacker may kill the blocker before he can deal normal (secondary) damage
|
||||
if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
|
||||
&& !attacker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
if (defenderDamage >= attackerLife) {
|
||||
|
||||
@@ -413,7 +413,7 @@ public class SpecialCardAi {
|
||||
if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) {
|
||||
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterEnumType.LOYALTY);
|
||||
int totalDamageToPW = 0;
|
||||
for (Card atk : (combat.getAttackersOf(combat.getDefenderByAttacker(source)))) {
|
||||
for (Card atk :combat.getAttackersOf(combat.getDefenderByAttacker(source))) {
|
||||
if (combat.isUnblocked(atk)) {
|
||||
totalDamageToPW += atk.getNetCombatDamage();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class PlayAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||
final String logic = sa.hasParam("AILogic") ? sa.getParam("AILogic") : "";
|
||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||
|
||||
final Game game = ai.getGame();
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
@@ -121,7 +121,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
// Only cast it if Human has the full amount of valid
|
||||
// Only cast it if AI doesn't have the full amount of Valid
|
||||
// TODO: Cast if the type is favorable: my "worst" valid is worse than his "worst" valid
|
||||
final String num = sa.hasParam("Amount") ? sa.getParam("Amount") : "1";
|
||||
final String num = sa.getParamOrDefault("Amount", "1");
|
||||
int amount = AbilityUtils.calculateAmount(source, num, sa);
|
||||
|
||||
if (num.equals("X") && sa.getSVar(num).equals("Count$xPaid")) {
|
||||
|
||||
@@ -1482,7 +1482,7 @@ public class AbilityUtils {
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
// The player who has the chance to cancel the ability
|
||||
final String pays = sa.hasParam("UnlessPayer") ? sa.getParam("UnlessPayer") : "TargetedController";
|
||||
final String pays = sa.getParamOrDefault("UnlessPayer", "TargetedController");
|
||||
final FCollectionView<Player> allPayers = getDefinedPlayers(sa.getHostCard(), pays, sa);
|
||||
final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always'
|
||||
final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
|
||||
@@ -1565,7 +1565,7 @@ public class AbilityUtils {
|
||||
" ", ""), sa);
|
||||
//Check for XColor
|
||||
ManaCostBeingPaid toPay = new ManaCostBeingPaid(ManaCost.ZERO);
|
||||
byte xColor = ManaAtom.fromName(sa.hasParam("UnlessXColor") ? sa.getParam("UnlessXColor") : "1");
|
||||
byte xColor = ManaAtom.fromName(sa.getParamOrDefault("UnlessXColor", "1"));
|
||||
toPay.increaseShard(ManaCostShard.valueOf(xColor), xCost);
|
||||
cost = new Cost(toPay.toManaCost(), true);
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ public abstract class SpellAbilityEffect {
|
||||
boolean your = location.startsWith("Your");
|
||||
boolean combat = location.endsWith("Combat");
|
||||
|
||||
String desc = sa.hasParam("AtEOTDesc") ? sa.getParam("AtEOTDesc") : "";
|
||||
String desc = sa.getParamOrDefault("AtEOTDesc", "");
|
||||
|
||||
if (your) {
|
||||
location = location.substring("Your".length());
|
||||
|
||||
@@ -33,7 +33,7 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
Player activator = sa.getActivatingPlayer();
|
||||
Card source = sa.getHostCard();
|
||||
Game game = activator.getGame();
|
||||
String valid = sa.hasParam("Valid") ? sa.getParam("Valid") : "Card";
|
||||
String valid = sa.getParamOrDefault("Valid", "Card");
|
||||
ZoneType zone = sa.hasParam("Zone") ? ZoneType.smartValueOf(sa.getParam("Zone")) : ZoneType.Battlefield;
|
||||
|
||||
int min = Integer.MAX_VALUE;
|
||||
|
||||
@@ -182,7 +182,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
boolean runDiscard = !sa.hasParam("Optional") || p.getController().confirmAction(sa, PlayerActionConfirmMode.Random, message);
|
||||
|
||||
if (runDiscard) {
|
||||
final String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card";
|
||||
final String valid = sa.getParamOrDefault("DiscardValid", "Card");
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), valid, source.getController(), source, sa);
|
||||
list = CardLists.filter(list, Presets.NON_TOKEN);
|
||||
CardCollection toDiscard = new CardCollection();
|
||||
@@ -250,7 +250,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
dPHand = p.getController().chooseCardsToRevealFromHand(amount, amount, dPHand);
|
||||
}
|
||||
|
||||
final String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card";
|
||||
final String valid = sa.getParamOrDefault("DiscardValid", "Card");
|
||||
String[] dValid = valid.split(",");
|
||||
CardCollection validCards = CardLists.getValidCards(dPHand, dValid, source.getController(), source, sa);
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean flipCoinCall(final Player caller, final SpellAbility sa, final int multiplier) {
|
||||
String varName = sa.hasParam("SaveNumFlipsToSVar") ? sa.getParam("SaveNumFlipsToSVar") : "X";
|
||||
String varName = sa.getParamOrDefault("SaveNumFlipsToSVar", "X");
|
||||
return flipCoinCall(caller, sa, multiplier, varName);
|
||||
}
|
||||
public static boolean flipCoinCall(final Player caller, final SpellAbility sa, final int multiplier, final String varName) {
|
||||
|
||||
@@ -15,7 +15,7 @@ public class MakeCardEffect extends SpellAbilityEffect {
|
||||
final Player player = sa.getActivatingPlayer();
|
||||
final Game game = player.getGame();
|
||||
|
||||
String name = sa.hasParam("Name") ? sa.getParam("Name") : sa.getHostCard().getName();
|
||||
String name = sa.getParamOrDefault("Name", sa.getHostCard().getName());
|
||||
if (name.equals("ChosenName")) {
|
||||
name = sa.getHostCard().getChosenName();
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("RandomKeyword")) {
|
||||
final String num = sa.hasParam("RandomKWNum") ? sa.getParam("RandomKWNum") : "1";
|
||||
final String num = sa.getParamOrDefault("RandomKWNum", "1");
|
||||
final int numkw = AbilityUtils.calculateAmount(host, num, sa);
|
||||
List<String> choice = Lists.newArrayList();
|
||||
List<String> total = Lists.newArrayList(keywords);
|
||||
|
||||
@@ -32,7 +32,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
|
||||
|
||||
ZoneType leaveZone = ZoneType.smartValueOf(sa.hasParam("RestrictFromZone") ? sa.getParam("RestrictFromZone") : null);
|
||||
restartZones.remove(leaveZone);
|
||||
String leaveRestriction = sa.hasParam("RestrictFromValid") ? sa.getParam("RestrictFromValid") : "Card";
|
||||
String leaveRestriction = sa.getParamOrDefault("RestrictFromValid", "Card");
|
||||
|
||||
//Card.resetUniqueNumber();
|
||||
// need this code here, otherwise observables fail
|
||||
|
||||
@@ -26,7 +26,6 @@ public class AttackingBand {
|
||||
public void addAttacker(Card card) { attackers.add(card); }
|
||||
public void removeAttacker(Card card) { attackers.remove(card); }
|
||||
|
||||
|
||||
public static boolean isValidBand(List<Card> band, boolean shareDamage) {
|
||||
if (band.isEmpty()) {
|
||||
// An empty band is not a valid band
|
||||
@@ -73,7 +72,6 @@ public class AttackingBand {
|
||||
return isValidBand(newBand, false);
|
||||
}
|
||||
|
||||
|
||||
public boolean contains(Card c) {
|
||||
return attackers.contains(c);
|
||||
}
|
||||
|
||||
@@ -345,13 +345,6 @@ public class Combat {
|
||||
return result;
|
||||
}
|
||||
|
||||
public final CardCollection getBlockers(final Card card) {
|
||||
// If requesting the ordered blocking list pass true, directly.
|
||||
AttackingBand band = getBandOfAttacker(card);
|
||||
Collection<Card> blockers = blockedBands.get(band);
|
||||
return blockers == null ? new CardCollection() : new CardCollection(blockers);
|
||||
}
|
||||
|
||||
public final boolean isBlocked(final Card attacker) {
|
||||
AttackingBand band = getBandOfAttacker(attacker);
|
||||
return band != null && Boolean.TRUE.equals(band.isBlocked());
|
||||
@@ -408,6 +401,10 @@ public class Combat {
|
||||
return result;
|
||||
}
|
||||
|
||||
public final CardCollection getBlockers(final Card card) {
|
||||
// If requesting the ordered blocking list pass true, directly.
|
||||
return getBlockers(getBandOfAttacker(card));
|
||||
}
|
||||
public final CardCollection getBlockers(final AttackingBand band) {
|
||||
Collection<Card> blockers = blockedBands.get(band);
|
||||
return blockers == null ? new CardCollection() : new CardCollection(blockers);
|
||||
@@ -512,8 +509,7 @@ public class Combat {
|
||||
final CardCollection oldBlockers = blockersOrderedForDamageAssignment.get(attacker);
|
||||
if (oldBlockers == null || oldBlockers.isEmpty()) {
|
||||
blockersOrderedForDamageAssignment.put(attacker, new CardCollection(blocker));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlocker(attacker, blocker, oldBlockers);
|
||||
blockersOrderedForDamageAssignment.put(attacker, orderedBlockers);
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ public class CombatUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( combat != null ) {
|
||||
if (combat != null) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, defendingPlayer).getRight() == combat.getBlockers(attacker).size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -763,7 +763,7 @@ public class CombatUtil {
|
||||
for (final Card attacker : attackers) {
|
||||
if (canBlock(attacker, blocker, combat)) {
|
||||
boolean must = true;
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, defending).getLeft() > 1) {
|
||||
if (getMinNumBlockersForAttacker(attacker, defending) > 1) {
|
||||
final List<Card> possibleBlockers = Lists.newArrayList(defendersArmy);
|
||||
possibleBlockers.remove(blocker);
|
||||
if (!canBeBlocked(attacker, possibleBlockers, combat)) {
|
||||
@@ -875,7 +875,7 @@ public class CombatUtil {
|
||||
|
||||
Player defendingPlayer = combat.getDefenderPlayerByAttacker(attacker);
|
||||
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, defendingPlayer).getLeft() > 1) {
|
||||
if (getMinNumBlockersForAttacker(attacker, defendingPlayer) > 1) {
|
||||
final List<Card> blockers = defendingPlayer.getCreaturesInPlay();
|
||||
blockers.remove(blocker);
|
||||
if (!canBeBlocked(attacker, blockers, combat)) {
|
||||
@@ -894,7 +894,7 @@ public class CombatUtil {
|
||||
&& combat.isAttacking(attacker)) {
|
||||
boolean canBe = true;
|
||||
Player defendingPlayer = combat.getDefenderPlayerByAttacker(attacker);
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, defendingPlayer).getLeft() > 1) {
|
||||
if (getMinNumBlockersForAttacker(attacker, defendingPlayer) > 1) {
|
||||
final List<Card> blockers = freeBlockers != null ? new CardCollection(freeBlockers) : defendingPlayer.getCreaturesInPlay();
|
||||
blockers.remove(blocker);
|
||||
if (!canBeBlocked(attacker, blockers, combat)) {
|
||||
@@ -1099,4 +1099,4 @@ public class CombatUtil {
|
||||
public static int getMinNumBlockersForAttacker(Card attacker, Player defender) {
|
||||
return StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, defender).getLeft();
|
||||
}
|
||||
} // end class CombatUtil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user