Merge branch 'blocking' into 'master'

Slight improvement to mustBlockAnAttacker

See merge request core-developers/forge!6460
This commit is contained in:
Bug Hunter
2022-03-29 17:40:37 +00:00
4 changed files with 63 additions and 42 deletions

View File

@@ -1064,6 +1064,7 @@ public class AiBlockController {
reinforceBlockersToKill(combat);
}
// TODO could be made more accurate if this would be inside each blocker choosing loop instead
lifeInDanger |= removeUnpayableBlocks(combat);
// == 2. If the AI life would still be in danger make a safer approach ==

View File

@@ -780,6 +780,7 @@ public class CombatUtil {
if (getBlockCost(blocker.getGame(), blocker, cardToBeBlocked) != null) {
continue;
}
int additionalBlockers = getMinNumBlockersForAttacker(cardToBeBlocked, defending) -1;
int potentialBlockers = 0;
// if the attacker can only be blocked with multiple creatures check if that's possible
@@ -809,6 +810,7 @@ public class CombatUtil {
if (getBlockCost(blocker.getGame(), blocker, attacker) != null) {
continue;
}
if (canBlock(attacker, blocker, combat)) {
boolean must = true;
if (getMinNumBlockersForAttacker(attacker, defending) > 1) {
@@ -877,48 +879,18 @@ public class CombatUtil {
}
final CardCollectionView attackers = combat.getAttackers();
final CardCollection attackersWithLure = new CardCollection();
final CardCollection requirementCards = new CardCollection();
final Player defender = blocker.getController();
for (final Card attacker : attackers) {
if (getBlockCost(blocker.getGame(), blocker, attacker) != null) {
continue;
}
if (attacker.hasStartOfKeyword("All creatures able to block CARDNAME do so.")
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked if able.")
&& combat.getBlockers(attacker).isEmpty())
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked by exactly one creature if able.")
&& combat.getBlockers(attacker).size() != 1)
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked by two or more creatures if able.")
&& combat.getBlockers(attacker).size() < 2)) {
attackersWithLure.add(attacker);
} else {
// TODO replace with Hidden Keyword or Static Ability
for (KeywordInterface inst : attacker.getKeywords()) {
String keyword = inst.getOriginal();
// MustBeBlockedBy <valid>
if (keyword.startsWith("MustBeBlockedBy ")) {
final String valid = keyword.substring("MustBeBlockedBy ".length());
if (blocker.isValid(valid, null, null, null) &&
CardLists.getValidCardCount(combat.getBlockers(attacker), valid, null, null, null) == 0) {
attackersWithLure.add(attacker);
break;
}
}
// MustBeBlockedByAll:<valid>
if (keyword.startsWith("MustBeBlockedByAll")) {
final String valid = keyword.split(":")[1];
if (blocker.isValid(valid, null, null, null)) {
attackersWithLure.add(attacker);
break;
}
}
}
if (attackerLureSatisfied(attacker, blocker, combat.getBlockers(attacker))) {
continue;
}
}
final CardCollection requirementCards = new CardCollection();
final Player defender = blocker.getController();
for (final Card attacker : attackersWithLure) {
if (canBeBlocked(attacker, combat, defender) && canBlock(attacker, blocker)) {
boolean canBe = true;
Player defendingPlayer = combat.getDefenderPlayerByAttacker(attacker);
@@ -963,12 +935,60 @@ public class CombatUtil {
return false;
}
boolean mustBlock = !combat.getAttackersBlockedBy(blocker).containsAll(requirementCards);
if (mustBlock && !canBlock(blocker, combat)) {
// the blocker can't block more but is he even part of another requirement?
mustBlock = Collections.disjoint(combat.getAttackersBlockedBy(blocker), requirementCards);
if (combat.getAttackersBlockedBy(blocker).containsAll(requirementCards)) {
return false;
}
return mustBlock;
if (!canBlock(blocker, combat)) {
// the blocker can't block more but is he even part of another requirement?
for (final Card attacker : attackers) {
final boolean requirementSatisfied = attackerLureSatisfied(attacker, blocker, combat.getBlockers(attacker));
final CardCollection reducedBlockers = combat.getBlockers(attacker);
if (requirementSatisfied && reducedBlockers.contains(blocker)) {
reducedBlockers.remove(blocker);
if (!attackerLureSatisfied(attacker, blocker, reducedBlockers)) {
return false;
}
}
}
}
return Collections.disjoint(combat.getAttackersBlockedBy(blocker), requirementCards);
}
private static boolean attackerLureSatisfied(final Card attacker, final Card blocker, final CardCollection blockers) {
if (attacker.hasStartOfKeyword("All creatures able to block CARDNAME do so.")
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked if able.")
&& blockers.isEmpty())
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked by exactly one creature if able.")
&& blockers.size() != 1)
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked by two or more creatures if able.")
&& blockers.size() < 2)) {
return false;
}
// TODO replace with Hidden Keyword or Static Ability
for (KeywordInterface inst : attacker.getKeywords()) {
String keyword = inst.getOriginal();
// MustBeBlockedBy <valid>
if (keyword.startsWith("MustBeBlockedBy ")) {
final String valid = keyword.substring("MustBeBlockedBy ".length());
if (blocker.isValid(valid, null, null, null) &&
CardLists.getValidCardCount(blockers, valid, null, null, null) == 0) {
return false;
}
}
// MustBeBlockedByAll:<valid>
if (keyword.startsWith("MustBeBlockedByAll")) {
final String valid = keyword.split(":")[1];
if (blocker.isValid(valid, null, null, null)) {
return false;
}
}
}
return true;
}
// can a player block with one or more creatures at the moment?

View File

@@ -578,7 +578,7 @@ public class PhaseHandler implements java.io.Serializable {
}
for (final Card attacker : combat.getAttackers()) {
// TODO currently doesn't refund (can really only happen if you cancel paying for a creature with an attacking requirement that could be satisfied without a tax)
// TODO currently doesn't refund previous attackers (can really only happen if you cancel paying for a creature with an attack requirement that could be satisfied without a tax)
final boolean canAttack = CombatUtil.checkPropagandaEffects(game, attacker, combat);
if (!canAttack) {

View File

@@ -642,4 +642,4 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
public List<Object> getTriggerRemembered() {
return ImmutableList.of();
}
} // end class StaticAbility
}