diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 4582e497d63..627f0930642 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2007,6 +2007,11 @@ public class TestPlayer implements Player { computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game); } + @Override + public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game, boolean allowUndo) { + computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game, allowUndo); + } + @Override public boolean searchLibrary(TargetCardInLibrary target, Game game) { return computerPlayer.searchLibrary(target, game); diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index e6cdc6f7bcc..79da1b98fa8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -993,6 +993,11 @@ public class PlayerStub implements Player { } + @Override + public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game, boolean allowUndo) { + + } + @Override public List getAvailableAttackers(Game game) { return null; diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 8abc77f1c81..de1bf60ec63 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -412,19 +412,19 @@ public class Combat implements Serializable, Copyable { creaturesForcedToAttack.put(creature.getId(), defendersForcedToAttack); // No need to attack a special defender if (defendersForcedToAttack.isEmpty()) { - if (defendersForcedToAttack.isEmpty()) { - if (defendersCostlessAttackable.size() >= 1) { - if (defenders.size() == 1) { - player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false); - } else { - TargetDefender target = new TargetDefender(defenders, creature.getId()); - target.setRequired(true); - target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack"); - if (player.chooseTarget(Outcome.Damage, target, null, game)) { - player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); - } - } + if (defenders.size() == 1) { + player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false); + } else { + TargetDefender target = new TargetDefender(defenders, creature.getId()); + target.setRequired(true); + target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack"); + if (player.chooseTarget(Outcome.Damage, target, null, game)) { + player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); } + } + } else { + if (defenders.size() == 1) { + player.declareAttacker(creature.getId(), defendersForcedToAttack.iterator().next(), game, false); } else { TargetDefender target = new TargetDefender(defendersCostlessAttackable, creature.getId()); target.setRequired(true); @@ -432,8 +432,6 @@ public class Combat implements Serializable, Copyable { player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); } } - } else { - player.declareAttacker(creature.getId(), defendersForcedToAttack.iterator().next(), game, false); } } @@ -684,7 +682,7 @@ public class Combat implements Serializable, Copyable { forcingAttackers.add(attackingCreatureId); creatureMustBlockAttackers.put(possibleBlocker.getId(), forcingAttackers); // assign block to the first forcing attacker automatically - defender.declareBlocker(defender.getId(), possibleBlocker.getId(), attackingCreatureId, game); + defender.declareBlocker(defender.getId(), possibleBlocker.getId(), attackingCreatureId, game, false); } } } @@ -972,7 +970,7 @@ public class Combat implements Serializable, Copyable { // 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> entry : creatureMustBlockAttackers.entrySet()) { - boolean blockIsValid; + boolean blockIsValid = true; Permanent creatureForcedToBlock = game.getPermanent(entry.getKey()); if (creatureForcedToBlock == null) { break; @@ -982,29 +980,6 @@ public class Combat implements Serializable, Copyable { continue; } - // Check if blocker is really able to block one or more attackers (maybe not if the attacker has menace) - if not continue with the next forced blocker - // TODO: Probably there is some potential to abuse the check if forced blockers are assigned to differnt attackers with e.g. menace. - // While if assigned all to one the block is possible - if (creatureForcedToBlock.getBlocking() == 0) { - boolean validBlockPossible = false; - for (UUID possibleAttackerId : entry.getValue()) { - CombatGroup attackersGroup = findGroup(possibleAttackerId); - if (attackersGroup.getBlockers().contains(creatureForcedToBlock.getId())) { - // forcedBlocker blocks a valid blocker, so no problem break check if valid block option exists - validBlockPossible = true; - break; - } - Permanent attackingCreature = game.getPermanent(possibleAttackerId); - if (attackingCreature.getMinBlockedBy() > 1) { // e.g. Menace - if (attackersGroup.getBlockers().size() + 1 >= attackingCreature.getMinBlockedBy()) { - validBlockPossible = true; - } - } - } - if (!validBlockPossible) { - continue; - } - } // // check if creature has to pay a cost to block so it's not mandatory to block // boolean removedAttacker = false; @@ -1021,8 +996,24 @@ public class Combat implements Serializable, Copyable { // continue; // } // creature does not block -> not allowed + + // Check if blocker is really able to block one or more attackers (maybe not if the attacker has menace) - if not continue with the next forced blocker + // TODO: Probably there is some potential to abuse the check if forced blockers are assigned to differnt attackers with e.g. menace. + // While if assigned all to one the block is possible if (creatureForcedToBlock.getBlocking() == 0) { - blockIsValid = false; + blockIsValid = entry.getValue().isEmpty(); + for (UUID possibleAttackerId : entry.getValue()) { + CombatGroup attackersGroup = game.getCombat().findGroup(possibleAttackerId); + Permanent attackingCreature = game.getPermanent(possibleAttackerId); + if (attackersGroup == null || attackingCreature == null) { + continue; + } + if (attackingCreature.getMinBlockedBy() > 1) { // e.g. Menace + if (attackersGroup.getBlockers().size() + 1 < attackingCreature.getMinBlockedBy()) { + blockIsValid = true; + } + } + } } else { blockIsValid = false; // which attacker is he blocking @@ -1242,19 +1233,21 @@ public class Combat implements Serializable, Copyable { } } } - + @SuppressWarnings("deprecation") public boolean declareAttacker(UUID creatureId, UUID defenderId, UUID playerId, Game game) { Permanent attacker = game.getPermanent(creatureId); if (attacker != null) { - if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId()) && !attacker.getAbilities().containsKey(JohanVigilanceAbility.getInstance().getId())) { - if (!attacker.isTapped()) { - attacker.setTapped(true); - attackersTappedByAttack.add(attacker.getId()); - } - } if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) { - return addAttackerToCombat(creatureId, defenderId, game); + if (addAttackerToCombat(creatureId, defenderId, game)) { + if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId()) && !attacker.getAbilities().containsKey(JohanVigilanceAbility.getInstance().getId())) { + if (!attacker.isTapped()) { + attacker.setTapped(true); + attackersTappedByAttack.add(attacker.getId()); + } + } + return true; + } } } return false; diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index efb73fff230..b2510b262fb 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -627,6 +627,8 @@ public interface Player extends MageItem, Copyable { void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game); + void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game, boolean allowUndo); + List getAvailableAttackers(Game game); List getAvailableAttackers(UUID defenderId, Game game); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a2be32c19e4..bdb8a60a3e1 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2353,7 +2353,12 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) { - if (isHuman()) { + declareBlocker(defenderId, blockerId, attackerId, game, true); + } + + @Override + public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game, boolean allowUndo) { + if (isHuman() && allowUndo) { setStoredBookmark(game.bookmarkState()); } Permanent blocker = game.getPermanent(blockerId);