mirror of
https://github.com/correl/mage.git
synced 2024-11-28 11:09:54 +00:00
* Fixed that creatures that have to pay costs to block were wrongly forced to block causing UI locks.
This commit is contained in:
parent
3868066e00
commit
0a0cb40783
7 changed files with 112 additions and 6 deletions
|
@ -76,7 +76,10 @@ public class HelperPanel extends JPanel {
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
||||||
JPanel container = new JPanel();
|
JPanel container = new JPanel();
|
||||||
|
|
||||||
container.setPreferredSize(new Dimension(100, 30));
|
container.setPreferredSize(new Dimension(100, 30));
|
||||||
|
container.setMinimumSize(new Dimension(20, 20));
|
||||||
|
container.setMaximumSize(new Dimension(2000, 100));
|
||||||
container.setLayout(new GridBagLayout());
|
container.setLayout(new GridBagLayout());
|
||||||
container.setOpaque(false);
|
container.setOpaque(false);
|
||||||
|
|
||||||
|
|
|
@ -964,7 +964,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) {
|
protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) {
|
||||||
updateGameStatePriority("selectCombatGroup", game);
|
updateGameStatePriority("selectCombatGroup", game);
|
||||||
TargetAttackingCreature target = new TargetAttackingCreature();
|
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);
|
waitForResponse(game);
|
||||||
if (response.getBoolean() != null) {
|
if (response.getBoolean() != null) {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class OppressiveRays extends CardImpl {
|
||||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||||
this.addAbility(ability);
|
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)));
|
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockUnlessPaysAttachedEffect(new ManaCostsImpl<>("{3}"), AttachmentType.AURA)));
|
||||||
|
|
||||||
// Activated abilities of enchanted creature cost {3} more to activate.
|
// Activated abilities of enchanted creature cost {3} more to activate.
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,9 +47,9 @@ public class CantAttackBlockUnlessPaysAttachedEffect extends PayCostToAttackBloc
|
||||||
|
|
||||||
public CantAttackBlockUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) {
|
public CantAttackBlockUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) {
|
||||||
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts);
|
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 "
|
+ "creature can't attack or block unless its controller pays "
|
||||||
+ manaCosts == null ? "" : manaCosts.getText();
|
+ (manaCosts == null ? "" : manaCosts.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
public CantAttackBlockUnlessPaysAttachedEffect(CantAttackBlockUnlessPaysAttachedEffect effect) {
|
public CantAttackBlockUnlessPaysAttachedEffect(CantAttackBlockUnlessPaysAttachedEffect effect) {
|
||||||
|
|
|
@ -263,7 +263,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mustAttack) {
|
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<UUID> defendersCostlessAttackable = new HashSet<>();
|
HashSet<UUID> defendersCostlessAttackable = new HashSet<>();
|
||||||
defendersCostlessAttackable.addAll(defenders);
|
defendersCostlessAttackable.addAll(defenders);
|
||||||
for (UUID defenderId : defenders) {
|
for (UUID defenderId : defenders) {
|
||||||
|
@ -501,6 +501,12 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
UUID attackingCreatureId = requirementEntry.getKey().mustBlockAttacker(ability, game);
|
UUID attackingCreatureId = requirementEntry.getKey().mustBlockAttacker(ability, game);
|
||||||
Player defender = game.getPlayer(possibleBlocker.getControllerId());
|
Player defender = game.getPlayer(possibleBlocker.getControllerId());
|
||||||
if (attackingCreatureId != null && defender != null && possibleBlocker.canBlock(attackingCreatureId, game)) {
|
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())) {
|
if (creatureMustBlockAttackers.containsKey(possibleBlocker.getId())) {
|
||||||
creatureMustBlockAttackers.get(possibleBlocker.getId()).add(attackingCreatureId);
|
creatureMustBlockAttackers.get(possibleBlocker.getId()).add(attackingCreatureId);
|
||||||
} else {
|
} else {
|
||||||
|
@ -734,6 +740,21 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
if (creatureForcedToBlock == null) {
|
if (creatureForcedToBlock == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // check if creature has to pay a cost to block so it's not mandatory to block
|
||||||
|
// boolean removedAttacker = false;
|
||||||
|
// for (Iterator<UUID> 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
|
// creature does not block -> not allowed
|
||||||
if (creatureForcedToBlock.getBlocking() == 0) {
|
if (creatureForcedToBlock.getBlocking() == 0) {
|
||||||
blockIsValid = false;
|
blockIsValid = false;
|
||||||
|
@ -765,7 +786,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!blockIsValid) {
|
if (!blockIsValid) {
|
||||||
sb.append(" ").append(creatureForcedToBlock.getLogName());
|
sb.append(" ").append(creatureForcedToBlock.getIdName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sb.length() > 0) {
|
if (sb.length() > 0) {
|
||||||
|
|
|
@ -2043,6 +2043,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) {
|
public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) {
|
||||||
|
if (isHuman()) {
|
||||||
|
setStoredBookmark(game.bookmarkState());
|
||||||
|
}
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
CombatGroup group = game.getCombat().findGroup(attackerId);
|
CombatGroup group = game.getCombat().findGroup(attackerId);
|
||||||
if (blocker != null && group != null && group.canBlock(blocker, game)) {
|
if (blocker != null && group != null && group.canBlock(blocker, game)) {
|
||||||
|
|
Loading…
Reference in a new issue