From 87a6d3a1eed4cf68b3a2827a626fa523c223e0d4 Mon Sep 17 00:00:00 2001 From: Dilnu Date: Sun, 18 Sep 2016 16:28:01 -0400 Subject: [PATCH] Fix the combat code to support Slayer's Cleaver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the checks for unfulfilled conditions to confirm the blocking creatures actually are fulfilled. Update the check for whether a blocker is already being used correctly to confirm that there isn’t a stricter condition that is unfulfilled. This change also updates the Slayer’s Cleaver test to make it more robust. --- .../sets/eldritchmoon/SlayersCleaver.java | 1 - .../requirement/BlockRequirementTest.java | 12 +-- .../main/java/mage/game/combat/Combat.java | 83 ++++++++++++------- 3 files changed, 58 insertions(+), 38 deletions(-) diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/SlayersCleaver.java b/Mage.Sets/src/mage/sets/eldritchmoon/SlayersCleaver.java index b98dab2ec3a..179f3418598 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/SlayersCleaver.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/SlayersCleaver.java @@ -56,7 +56,6 @@ public class SlayersCleaver extends CardImpl { // Equipped creature gets +3/+1 and must be blocked by an Eldrazi if able. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(3, 1)); - // TODO: The effect does not work correctly yet, we need to do some chnanges to combat handling to fix this ability.addEffect(new SlayersCleaverEffect()); this.addAbility(ability); // Equip {4} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java index 35a0edb3c9f..f3f8d72281b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java @@ -231,12 +231,12 @@ public class BlockRequirementTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); addCard(Zone.BATTLEFIELD, playerA, "Memnite"); // {1} 1/1 - addCard(Zone.BATTLEFIELD, playerB, "Wretched Gryff"); // {7} Flying 3/4 - Eldrazi Hippogriff - addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); // {3}{R} 3/3 + addCard(Zone.BATTLEFIELD, playerB, "Dimensional Infiltrator"); // 2/1 - Eldrazi + addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves"); // 1/1 activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Memnite"); // pumps to 4/2 - attack(1, playerA, "Memnite"); // must be blocked by Wretched Gryff - block(1, playerB, "Hill Giant", "Memnite"); // should not be allowed as only blocker + attack(1, playerA, "Memnite"); // must be blocked by Dimensional Infiltrator + block(1, playerB, "Llanowar Elves", "Memnite"); // should not be allowed as only blocker setStopAt(1, PhaseStep.END_COMBAT); execute(); @@ -244,7 +244,7 @@ public class BlockRequirementTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Slayer's Cleaver", 1); assertLife(playerB, 20); assertGraveyardCount(playerA, "Memnite", 1); - assertGraveyardCount(playerB, "Wretched Gryff", 1); - assertPermanentCount(playerB, "Hill Giant", 1); + assertGraveyardCount(playerB, "Dimensional Infiltrator", 1); + assertGraveyardCount(playerB, "Llanowar Elves", 1); } } diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index a14d641932c..0420af99270 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -749,38 +749,50 @@ public class Combat implements Serializable, Copyable { // check if for attacking creatures with mustBeBlockedByAtLeastOne requirements are fulfilled for (UUID toBeBlockedCreatureId : mustBeBlockedByAtLeastOne.keySet()) { for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() && combatGroup.getAttackers().contains(toBeBlockedCreatureId)) { - // creature is not blocked but has possible blockers - if (controller.isHuman()) { - Permanent toBeBlockedCreature = game.getPermanent(toBeBlockedCreatureId); - if (toBeBlockedCreature != null) { - // check if all possible blocker block other creatures they are forced to block - // read through all possible blockers - for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) { - String blockRequiredMessage = isCreatureDoingARequiredBlock(possibleBlockerId, mustBeBlockedByAtLeastOne, game); - if (blockRequiredMessage != null) { // message means not required - removeBlocker(possibleBlockerId, game); - game.informPlayer(controller, blockRequiredMessage + " Existing block removed. It's a requirement to block " + toBeBlockedCreature.getIdName() + "."); - return false; + if (combatGroup.getAttackers().contains(toBeBlockedCreatureId)) { + boolean requirementFulfilled = false; + // Check whether an applicable creature is blocking. + for (UUID blockerId : combatGroup.getBlockers()) { + if (mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId).contains(blockerId)) { + requirementFulfilled = true; + break; + } + } + if (!requirementFulfilled) { + // creature is not blocked but has possible blockers + if (controller.isHuman()) { + Permanent toBeBlockedCreature = game.getPermanent(toBeBlockedCreatureId); + if (toBeBlockedCreature != null) { + // check if all possible blocker block other creatures they are forced to block + // read through all possible blockers + for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) { + String blockRequiredMessage = isCreatureDoingARequiredBlock( + possibleBlockerId, toBeBlockedCreatureId, mustBeBlockedByAtLeastOne, game); + if (blockRequiredMessage != null) { // message means not required + removeBlocker(possibleBlockerId, game); + game.informPlayer(controller, blockRequiredMessage + " Existing block removed. It's a requirement to block " + toBeBlockedCreature.getIdName() + "."); + return false; + } } } - } - } else { - // take the first potential blocker from the set to block for the AI - for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) { - String blockRequiredMessage = isCreatureDoingARequiredBlock(possibleBlockerId, mustBeBlockedByAtLeastOne, game); - if (blockRequiredMessage != null) { - // set the block - Permanent possibleBlocker = game.getPermanent(possibleBlockerId); - Player defender = game.getPlayer(possibleBlocker.getControllerId()); - if (defender != null) { - if (possibleBlocker.getBlocking() > 0) { - removeBlocker(possibleBlockerId, game); + } else { + // take the first potential blocker from the set to block for the AI + for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) { + String blockRequiredMessage = isCreatureDoingARequiredBlock( + possibleBlockerId, toBeBlockedCreatureId, mustBeBlockedByAtLeastOne, game); + if (blockRequiredMessage != null) { + // set the block + Permanent possibleBlocker = game.getPermanent(possibleBlockerId); + Player defender = game.getPlayer(possibleBlocker.getControllerId()); + if (defender != null) { + if (possibleBlocker.getBlocking() > 0) { + removeBlocker(possibleBlockerId, game); + } + defender.declareBlocker(defender.getId(), possibleBlockerId, toBeBlockedCreatureId, game); } - defender.declareBlocker(defender.getId(), possibleBlockerId, toBeBlockedCreatureId, game); + break; } - break; } } } @@ -887,11 +899,12 @@ public class Combat implements Serializable, Copyable { * required block * * @param possibleBlockerId + * @param toBeBlockedCreatureId * @param mustBeBlockedByAtLeastOne * @param game * @return null block is required otherwise message with reason why not */ - protected String isCreatureDoingARequiredBlock(UUID possibleBlockerId, Map> mustBeBlockedByAtLeastOne, Game game) { + protected String isCreatureDoingARequiredBlock(UUID possibleBlockerId, UUID toBeBlockedCreatureId, Map> mustBeBlockedByAtLeastOne, Game game) { Permanent possibleBlocker = game.getPermanent(possibleBlockerId); if (possibleBlocker != null) { if (possibleBlocker.getBlocking() == 0) { @@ -908,9 +921,17 @@ public class Combat implements Serializable, Copyable { if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) { // blocks a creature that has to be blocked by at least one if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) { - // the creature blocks alone already a creature that has to be blocked by at least one, - // so this is ok - return null; + Set blockedSet = mustBeBlockedByAtLeastOne.get(blockedAttackerId); + Set toBlockSet = mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId); + if (toBlockSet == null) { + // This should never happen.
 + return null; + } else if (toBlockSet.containsAll(blockedSet)) { + // the creature already blocks alone a creature that has to be blocked by at least one
 + // and has more possible blockers, so this is ok
 + return null; + } + } // TODO: Check if the attacker is already blocked by another creature // and despite there is need that this attacker blocks this attacker also