mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge branch 'blocking' into 'master'
Slight improvement to mustBlockAnAttacker See merge request core-developers/forge!6460
This commit is contained in:
@@ -1064,6 +1064,7 @@ public class AiBlockController {
|
|||||||
reinforceBlockersToKill(combat);
|
reinforceBlockersToKill(combat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO could be made more accurate if this would be inside each blocker choosing loop instead
|
||||||
lifeInDanger |= removeUnpayableBlocks(combat);
|
lifeInDanger |= removeUnpayableBlocks(combat);
|
||||||
|
|
||||||
// == 2. If the AI life would still be in danger make a safer approach ==
|
// == 2. If the AI life would still be in danger make a safer approach ==
|
||||||
|
|||||||
@@ -780,6 +780,7 @@ public class CombatUtil {
|
|||||||
if (getBlockCost(blocker.getGame(), blocker, cardToBeBlocked) != null) {
|
if (getBlockCost(blocker.getGame(), blocker, cardToBeBlocked) != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int additionalBlockers = getMinNumBlockersForAttacker(cardToBeBlocked, defending) -1;
|
int additionalBlockers = getMinNumBlockersForAttacker(cardToBeBlocked, defending) -1;
|
||||||
int potentialBlockers = 0;
|
int potentialBlockers = 0;
|
||||||
// if the attacker can only be blocked with multiple creatures check if that's possible
|
// 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) {
|
if (getBlockCost(blocker.getGame(), blocker, attacker) != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canBlock(attacker, blocker, combat)) {
|
if (canBlock(attacker, blocker, combat)) {
|
||||||
boolean must = true;
|
boolean must = true;
|
||||||
if (getMinNumBlockersForAttacker(attacker, defending) > 1) {
|
if (getMinNumBlockersForAttacker(attacker, defending) > 1) {
|
||||||
@@ -877,48 +879,18 @@ public class CombatUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final CardCollectionView attackers = combat.getAttackers();
|
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) {
|
for (final Card attacker : attackers) {
|
||||||
if (getBlockCost(blocker.getGame(), blocker, attacker) != null) {
|
if (getBlockCost(blocker.getGame(), blocker, attacker) != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attacker.hasStartOfKeyword("All creatures able to block CARDNAME do so.")
|
if (attackerLureSatisfied(attacker, blocker, combat.getBlockers(attacker))) {
|
||||||
|| (attacker.hasStartOfKeyword("CARDNAME must be blocked if able.")
|
continue;
|
||||||
&& 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardCollection requirementCards = new CardCollection();
|
|
||||||
final Player defender = blocker.getController();
|
|
||||||
for (final Card attacker : attackersWithLure) {
|
|
||||||
if (canBeBlocked(attacker, combat, defender) && canBlock(attacker, blocker)) {
|
if (canBeBlocked(attacker, combat, defender) && canBlock(attacker, blocker)) {
|
||||||
boolean canBe = true;
|
boolean canBe = true;
|
||||||
Player defendingPlayer = combat.getDefenderPlayerByAttacker(attacker);
|
Player defendingPlayer = combat.getDefenderPlayerByAttacker(attacker);
|
||||||
@@ -963,12 +935,60 @@ public class CombatUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean mustBlock = !combat.getAttackersBlockedBy(blocker).containsAll(requirementCards);
|
if (combat.getAttackersBlockedBy(blocker).containsAll(requirementCards)) {
|
||||||
if (mustBlock && !canBlock(blocker, combat)) {
|
return false;
|
||||||
// the blocker can't block more but is he even part of another requirement?
|
|
||||||
mustBlock = Collections.disjoint(combat.getAttackersBlockedBy(blocker), requirementCards);
|
|
||||||
}
|
}
|
||||||
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?
|
// can a player block with one or more creatures at the moment?
|
||||||
|
|||||||
@@ -578,7 +578,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Card attacker : combat.getAttackers()) {
|
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);
|
final boolean canAttack = CombatUtil.checkPropagandaEffects(game, attacker, combat);
|
||||||
|
|
||||||
if (!canAttack) {
|
if (!canAttack) {
|
||||||
|
|||||||
@@ -642,4 +642,4 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
public List<Object> getTriggerRemembered() {
|
public List<Object> getTriggerRemembered() {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
} // end class StaticAbility
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user