tests: added many use cases for must be blocked, must blocking and menace effects (related to #13182)

This commit is contained in:
Oleg Agafonov 2024-12-31 22:07:07 +04:00
parent aef220a19b
commit b2279a8e9c
2 changed files with 423 additions and 44 deletions

View file

@ -5,15 +5,17 @@ import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Test restrictions for choosing attackers and blockers.
*
* @author noxx
* @author noxx, JayDi85
*/
public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
@ -337,10 +339,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
/*
* Mogg Flunkies cannot attack alone. Cards like Goblin Assault force all goblins to attack each turn.
* Mogg Flunkies should not be able to attack.
*/
*/
@Test
public void testMustAttackButCannotAttackAlone()
{
public void testMustAttackButCannotAttackAlone() {
/* Mogg Flunkies {1}{R} 3/3
Creature Goblin
Mogg Flunkies can't attack or block alone.
@ -434,11 +435,11 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
@Test
public void underworldCerberusBlockedByOneTest() {
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); // 1/1
@ -450,18 +451,18 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
try {
execute();
fail("Expected exception not thrown");
} catch(UnsupportedOperationException e) {
} catch (UnsupportedOperationException e) {
assertEquals("Underworld Cerberus is blocked by 1 creature(s). It has to be blocked by 3 or more.", e.getMessage());
}
}
@Test
public void underworldCerberusBlockedByTwoTest() {
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 2); // 1/1
@ -474,7 +475,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
try {
execute();
fail("Expected exception not thrown");
} catch(UnsupportedOperationException e) {
} catch (UnsupportedOperationException e) {
assertEquals("Underworld Cerberus is blocked by 2 creature(s). It has to be blocked by 3 or more.", e.getMessage());
}
}
@ -482,11 +483,11 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
@Test
public void underworldCerberusBlockedByThreeTest() {
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
@ -511,17 +512,17 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
@Test
public void underworldCerberusBlockedByTenTest() {
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
/* Underworld Cerberus {3}{B}{3} 6/6
* Underworld Cerberus can't be blocked except by three or more creatures.
* Cards in graveyards can't be the targets of spells or abilities.
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
*/
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
// Blocked by 10 creatures - this is acceptable as it's >3
attack(3, playerA, "Underworld Cerberus");
for(int i = 0; i < 10; i++) {
for (int i = 0; i < 10; i++) {
block(3, playerB, "Memnite:" + i, "Underworld Cerberus");
}
@ -541,24 +542,24 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
assertLife(playerA, 20);
assertLife(playerB, 20);
}
@Test
public void irresistiblePreyMustBeBlockedTest() {
addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves");
addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr");
addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.HAND, playerA, "Irresistible Prey");
addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irresistible Prey", "Llanowar Elves"); // must be blocked
attack(1, playerA, "Llanowar Elves");
attack(1, playerA, "Alpha Myr");
// attempt to block the creature that doesn't have "must be blocked"
block(1, playerB, "Bronze Sable", "Alpha Myr");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
@ -568,4 +569,383 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
assertTapped("Alpha Myr", true);
assertLife(playerB, 18);
}
}
@Test
public void test_MustBeBlocked_nothing() {
// Fear of Being Hunted must be blocked if able.
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
attack(1, playerA, "Fear of Being Hunted");
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
checkBlockers("no blocker", 1, playerB, "");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 4);
}
@Test
public void test_MustBeBlocked_1_blocker() {
// Fear of Being Hunted must be blocked if able.
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
//
addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr", 1); // 2/1
attack(1, playerA, "Fear of Being Hunted");
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
checkBlockers("forced x1 blocker", 1, playerB, "Alpha Myr");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Fear of Being Hunted", 1);
}
@Test
public void test_MustBeBlocked_many_blockers_good() {
// Fear of Being Hunted must be blocked if able.
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
//
addCard(Zone.BATTLEFIELD, playerB, "Spectral Bears", 10); // 3/3
// TODO: human logic can't be tested (until isHuman replaced by ~isComputer), so current use case will
// take first available blocker
attack(1, playerA, "Fear of Being Hunted");
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
checkBlockers("x1 optimal blocker", 1, playerB, "Spectral Bears");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Fear of Being Hunted", 1);
}
@Test
public void test_MustBeBlocked_many_blockers_bad() {
// Fear of Being Hunted must be blocked if able.
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
// TODO: human logic can't be tested (until isHuman replaced by ~isComputer), so current use case will
// take first available blocker
attack(1, playerA, "Fear of Being Hunted");
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
checkBlockers("x1 optimal blocker", 1, playerB, "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertPermanentCount(playerA, "Fear of Being Hunted", 1);
}
@Test
@Ignore
// TODO: enable and duplicate for AI -- after implement choose blocker logic and isHuman replace by ~isComputer
public void test_MustBeBlocked_many_blockers_optimal() {
// Fear of Being Hunted must be blocked if able.
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); // 2/2
addCard(Zone.BATTLEFIELD, playerB, "Spectral Bears", 1); // 3/3
addCard(Zone.BATTLEFIELD, playerB, "Deadbridge Goliath", 1); // 5/5
attack(1, playerA, "Fear of Being Hunted");
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
checkBlockers("x1 optimal blocker", 1, playerB, "Deadbridge Goliath");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Fear of Being Hunted", 1);
}
@Test
public void test_MustBlocking_zero_blockers() {
// All creatures able to block target creature this turn do so.
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
//
// Menace
// Each creature you control with menace can't be blocked except by three or more creatures.
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
// prepare
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
attack(1, playerA, "Sonorous Howlbonder");
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
checkBlockers("no blocker", 1, playerB, "");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 2);
assertPermanentCount(playerA, "Sonorous Howlbonder", 1);
}
@Test
public void test_MustBlocking_full_blockers() {
// All creatures able to block target creature this turn do so.
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
//
// Menace
// Each creature you control with menace can't be blocked except by three or more creatures.
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
// prepare
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
attack(1, playerA, "Sonorous Howlbonder");
setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Sonorous Howlbonder", 1);
}
@Test
public void test_MustBlocking_many_blockers() {
// possible bug: AI's blockers auto-fix assign too many blockers (e.g. x10 instead x3 by required effect)
// All creatures able to block target creature this turn do so.
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
//
// Menace
// Each creature you control with menace can't be blocked except by three or more creatures.
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 5); // 1/1
// prepare
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
attack(1, playerA, "Sonorous Howlbonder");
setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Sonorous Howlbonder", 1);
}
@Test
@Ignore
// TODO: need exception fix - java.lang.UnsupportedOperationException: Sonorous Howlbonder is blocked by 1 creature(s). It has to be blocked by 3 or more.
// It's auto-fix in block configuration, so exception must be fixed cause AI works with it
public void test_MustBlocking_low_blockers() {
// possible bug: exception on wrong block configuration
// if effect require x3 blockers, but opponent has only 1 then it must use 1 blocker anyway
// All creatures able to block target creature this turn do so.
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
//
// Menace
// Each creature you control with menace can't be blocked except by three or more creatures.
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
// prepare
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
attack(1, playerA, "Sonorous Howlbonder");
setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
checkBlockers("one possible blocker", 1, playerB, "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Sonorous Howlbonder", 1);
}
@Test
public void test_MustBeBlockedWithMenace_0_blockers() {
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creatures
// power until end of turn. That creature must be blocked this combat if able.
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
//
// Menace
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
addTarget(playerA, "Alley Strangler"); // boost target
setChoice(playerA, true); // boost target
attack(1, playerA, "Alley Strangler");
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
checkBlockers("no blocker", 1, playerB, "");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 4);
assertGraveyardCount(playerA, "Alley Strangler", 0);
}
@Test
@Ignore // TODO: need improve of block configuration auto-fix (block by x2 instead x1)
public void test_MustBeBlockedWithMenace_all_blockers() {
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creatures
// power until end of turn. That creature must be blocked this combat if able.
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
//
// Menace
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 2); // 1/1
// If the target creature has menace, two creatures must block it if able.
// (2020-06-23)
addTarget(playerA, "Alley Strangler"); // boost target
setChoice(playerA, true); // boost target
attack(1, playerA, "Alley Strangler");
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Alley Strangler", 0);
}
@Test
@Ignore // TODO: need improve of block configuration auto-fix (block by x2 instead x1)
public void test_MustBeBlockedWithMenace_many_blockers() {
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creatures
// power until end of turn. That creature must be blocked this combat if able.
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
//
// Menace
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
// If the target creature has menace, two creatures must block it if able.
// (2020-06-23)
addTarget(playerA, "Alley Strangler"); // boost target
setChoice(playerA, true); // boost target
attack(1, playerA, "Alley Strangler");
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Alley Strangler", 0);
}
@Test
@Ignore
// TODO: need auto-fix cause AI use it (it must ignore bad blocker configuration and allow to pass without blockers at all)
public void test_MustBeBlockedWithMenace_low_blockers_auto() {
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creatures
// power until end of turn. That creature must be blocked this combat if able.
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
//
// Menace
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
// If the target creature has menace, two creatures must block it if able.
// (2020-06-23)
//
// If a creature is required to block a creature with menace, another creature must also block that creature
// if able. If none can, the creature thats required to block can block another creature or not block at all.
// (2020-04-17)
// auto-fix block config inside
addTarget(playerA, "Alley Strangler"); // boost target
setChoice(playerA, true); // boost target
attack(1, playerA, "Alley Strangler");
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
checkBlockers("no blockers", 1, playerB, "");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 4);
assertGraveyardCount(playerA, "Alley Strangler", 0);
}
@Test
@Ignore
// TODO: need exception fix java.lang.UnsupportedOperationException: Alley Strangler is blocked by 1 creature(s). It has to be blocked by 2 or more.
// It's ok to have such exception in unit tests from manual setup
// If it's impossible to auto-fix, then keep that error and ignore the test
public void test_MustBeBlockedWithMenace_low_blockers_manual() {
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creatures
// power until end of turn. That creature must be blocked this combat if able.
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
//
// Menace
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
//
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
// If the target creature has menace, two creatures must block it if able.
// (2020-06-23)
//
// If a creature is required to block a creature with menace, another creature must also block that creature
// if able. If none can, the creature thats required to block can block another creature or not block at all.
// (2020-04-17)
// define blocker manual
addTarget(playerA, "Alley Strangler"); // boost target
setChoice(playerA, true); // boost target
attack(1, playerA, "Alley Strangler");
block(1, playerB, "Memnite", "Alley Strangler");
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
checkBlockers("no blockers", 1, playerB, "");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 4);
assertGraveyardCount(playerA, "Alley Strangler", 0);
}
}

View file

@ -893,7 +893,7 @@ public class Combat implements Serializable, Copyable<Combat> {
Map<UUID, Integer> minNumberOfBlockersMap = new HashMap<>();
Map<UUID, Integer> minPossibleBlockersMap = new HashMap<>();
// check mustBlock requirements of creatures from opponents of attacking player
// FIND attackers and potential blockers for "must be blocked" effects
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED, player.getId(), game)) {
// creature is controlled by an opponent of the attacker
if (opponents.contains(creature.getControllerId())) {
@ -1012,7 +1012,7 @@ public class Combat implements Serializable, Copyable<Combat> {
if (toBeBlockedCreature != null) {
CombatGroup toBeBlockedGroup = findGroup(toBeBlockedCreature);
if (toBeBlockedGroup != null && toBeBlockedGroup.getDefendingPlayerId().equals(creature.getControllerId())) {
minNumberOfBlockersMap.put(toBeBlockedCreature, effect.getMinNumberOfBlockers());
minNumberOfBlockersMap.put(toBeBlockedCreature, effect.getMinNumberOfBlockers()); // TODO: fail on multiple effects 1 + 2 min blockers?
Permanent toBeBlockedCreaturePermanent = game.getPermanent(toBeBlockedCreature);
if (toBeBlockedCreaturePermanent != null) {
minPossibleBlockersMap.put(toBeBlockedCreature, toBeBlockedCreaturePermanent.getMinBlockedBy());
@ -1096,12 +1096,10 @@ public class Combat implements Serializable, Copyable<Combat> {
}
}
}
}
// check if for attacking creatures with mustBeBlockedByAtLeastX requirements are fulfilled
// APPLY potential blockers to attackers with "must be blocked" effects
for (UUID toBeBlockedCreatureId : mustBeBlockedByAtLeastX.keySet()) {
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
if (combatGroup.getAttackers().contains(toBeBlockedCreatureId)) {
@ -1124,6 +1122,8 @@ public class Combat implements Serializable, Copyable<Combat> {
if (!requirementFulfilled) {
// creature is not blocked but has possible blockers
if (controller.isHuman()) {
// HUMAN logic - send warning about wrong blocker config and repeat declare
// TODO: replace isHuman by !isComputer for working unit tests
Permanent toBeBlockedCreature = game.getPermanent(toBeBlockedCreatureId);
if (toBeBlockedCreature != null) {
// check if all possible blocker block other creatures they are forced to block
@ -1142,9 +1142,8 @@ public class Combat implements Serializable, Copyable<Combat> {
}
}
}
} else {
// take the first potential blocker from the set to block for the AI
// AI logic - auto-fix wrong blocker config (take the first potential blocker)
for (UUID possibleBlockerId : mustBeBlockedByAtLeastX.get(toBeBlockedCreatureId)) {
String blockRequiredMessage = isCreatureDoingARequiredBlock(
possibleBlockerId, toBeBlockedCreatureId, mustBeBlockedByAtLeastX, game);
@ -1167,8 +1166,8 @@ public class Combat implements Serializable, Copyable<Combat> {
}
}
}
}
// check if creatures are forced to block but do not block at all or block creatures they are not forced to block
StringBuilder sb = new StringBuilder();
for (Map.Entry<UUID, Set<UUID>> entry : creatureMustBlockAttackers.entrySet()) {