From 0a0cb40783ee0c088d9b97ac5b70ab5c02bae947 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 1 Jul 2015 23:00:22 +0200 Subject: [PATCH] * Fixed that creatures that have to pay costs to block were wrongly forced to block causing UI locks. --- .../java/mage/client/game/HelperPanel.java | 3 + .../src/mage/player/human/HumanPlayer.java | 2 +- .../sets/journeyintonyx/OppressiveRays.java | 2 +- .../requirement/BlockRequirementTest.java | 79 +++++++++++++++++++ ...ntAttackBlockUnlessPaysAttachedEffect.java | 4 +- Mage/src/mage/game/combat/Combat.java | 25 +++++- Mage/src/mage/players/PlayerImpl.java | 3 + 7 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index f4f716f92b7..6cba5bd8f09 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -76,7 +76,10 @@ public class HelperPanel extends JPanel { setOpaque(false); JPanel container = new JPanel(); + container.setPreferredSize(new Dimension(100, 30)); + container.setMinimumSize(new Dimension(20, 20)); + container.setMaximumSize(new Dimension(2000, 100)); container.setLayout(new GridBagLayout()); container.setOpaque(false); diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index d6ebfc67492..9a7fd691cc3 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -964,7 +964,7 @@ public class HumanPlayer extends PlayerImpl { protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) { updateGameStatePriority("selectCombatGroup", game); TargetAttackingCreature target = new TargetAttackingCreature(); - game.fireSelectTargetEvent(playerId, "Select attacker to block", target.possibleTargets(null, playerId, game), false, getOptions(target, null)); + game.fireSelectTargetEvent(playerId, addSecondLineWithObjectName("Select attacker to block", blockerId, game), target.possibleTargets(null, playerId, game), false, getOptions(target, null)); waitForResponse(game); if (response.getBoolean() != null) { // do nothing diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java b/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java index 5b4dd456448..9db872e0d35 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java @@ -69,7 +69,7 @@ public class OppressiveRays extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - // Enchanted creature can't attack or block unless its controller pays 3. + // Enchanted creature can't attack or block unless its controller pays {3}. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockUnlessPaysAttachedEffect(new ManaCostsImpl<>("{3}"), AttachmentType.AURA))); // Activated abilities of enchanted creature cost {3} more to activate. 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 new file mode 100644 index 00000000000..cbd8ba96d6c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.requirement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BlockRequirementTest extends CardTestPlayerBase { + + @Test + public void testPrizedUnicorn() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2 + + // All creatures able to block Prized Unicorn do so. + addCard(Zone.BATTLEFIELD, playerB, "Prized Unicorn"); // 2/2 + + // Silvercoat Lion should be forced to block + attack(2, playerB, "Prized Unicorn"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Prized Unicorn", 1); + } + + @Test + public void testPrizedUnicornAndOppressiveRays() { + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + addCard(Zone.HAND, playerA, "Oppressive Rays"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2 + + // All creatures able to block Prized Unicorn do so. + addCard(Zone.BATTLEFIELD, playerB, "Prized Unicorn"); // 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oppressive Rays", "Silvercoat Lion"); + + // Silvercoat Lion has not to block because it has to pay {3} to block + attack(2, playerB, "Prized Unicorn"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Oppressive Rays", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Prized Unicorn", 1); + } +} diff --git a/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java index 3a0a9b6f100..dd60faa3303 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java @@ -47,9 +47,9 @@ public class CantAttackBlockUnlessPaysAttachedEffect extends PayCostToAttackBloc public CantAttackBlockUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) { super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts); - staticText = attachmentType.equals(AttachmentType.AURA) ? "Enchanted " : "Equipped " + staticText = (attachmentType.equals(AttachmentType.AURA) ? "Enchanted " : "Equipped ") + "creature can't attack or block unless its controller pays " - + manaCosts == null ? "" : manaCosts.getText(); + + (manaCosts == null ? "" : manaCosts.getText()); } public CantAttackBlockUnlessPaysAttachedEffect(CantAttackBlockUnlessPaysAttachedEffect effect) { diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index 59317b4a64c..5c8a5154da7 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -263,7 +263,7 @@ public class Combat implements Serializable, Copyable { } } if (mustAttack) { - // check which defenders the forced to attck creature can attack without paying a cost + // check which defenders the forced to attack creature can attack without paying a cost HashSet defendersCostlessAttackable = new HashSet<>(); defendersCostlessAttackable.addAll(defenders); for (UUID defenderId : defenders) { @@ -501,6 +501,12 @@ public class Combat implements Serializable, Copyable { UUID attackingCreatureId = requirementEntry.getKey().mustBlockAttacker(ability, game); Player defender = game.getPlayer(possibleBlocker.getControllerId()); if (attackingCreatureId != null && defender != null && possibleBlocker.canBlock(attackingCreatureId, game)) { + // check if the possible blocker has to pay cost to block, if so don't force + if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects( + GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, attackingCreatureId, possibleBlocker.getId(), possibleBlocker.getControllerId()), game)) { + // has cost to block to pay so remove this attacker + continue; + } if (creatureMustBlockAttackers.containsKey(possibleBlocker.getId())) { creatureMustBlockAttackers.get(possibleBlocker.getId()).add(attackingCreatureId); } else { @@ -734,6 +740,21 @@ public class Combat implements Serializable, Copyable { if (creatureForcedToBlock == null) { break; } + +// // check if creature has to pay a cost to block so it's not mandatory to block +// boolean removedAttacker = false; +// for (Iterator iterator = entry.getValue().iterator(); iterator.hasNext();) { +// UUID possibleAttackerId = iterator.next(); +// if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects( +// GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, possibleAttackerId, creatureForcedToBlock.getId(), creatureForcedToBlock.getControllerId()), game)) { +// // has cost to block to pay so remove this attacker +// iterator.remove(); +// removedAttacker = true; +// } +// } +// if (removedAttacker && entry.getValue().isEmpty()) { +// continue; +// } // creature does not block -> not allowed if (creatureForcedToBlock.getBlocking() == 0) { blockIsValid = false; @@ -765,7 +786,7 @@ public class Combat implements Serializable, Copyable { } if (!blockIsValid) { - sb.append(" ").append(creatureForcedToBlock.getLogName()); + sb.append(" ").append(creatureForcedToBlock.getIdName()); } } if (sb.length() > 0) { diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 6b8185d257f..dff7eac4c1b 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2043,6 +2043,9 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) { + if (isHuman()) { + setStoredBookmark(game.bookmarkState()); + } Permanent blocker = game.getPermanent(blockerId); CombatGroup group = game.getCombat().findGroup(attackerId); if (blocker != null && group != null && group.canBlock(blocker, game)) {