diff --git a/Mage.Common/src/main/java/mage/view/CombatGroupView.java b/Mage.Common/src/main/java/mage/view/CombatGroupView.java index e8fc7449d8d..c757ad6ff0d 100644 --- a/Mage.Common/src/main/java/mage/view/CombatGroupView.java +++ b/Mage.Common/src/main/java/mage/view/CombatGroupView.java @@ -40,7 +40,7 @@ public class CombatGroupView implements Serializable { attackers.put(id, new PermanentView(attacker, game.getCard(attacker.getId()),null, game)); } } - for (UUID id: combatGroup.getBlockerOrder()) { + for (UUID id: combatGroup.getBlockers()) { Permanent blocker = game.getPermanent(id); if (blocker != null) { blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game)); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java index dd63da4bdf0..e03ed1d623f 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayerControllableProxy.java @@ -13,10 +13,8 @@ import mage.constants.MultiAmountType; import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.match.Match; -import mage.game.permanent.Permanent; import mage.game.tournament.Tournament; import mage.players.Player; import mage.target.Target; @@ -287,24 +285,6 @@ public class ComputerPlayerControllableProxy extends ComputerPlayer7 { } } - @Override - public UUID chooseAttackerOrder(java.util.List attackers, Game game) { - if (isUnderMe(game)) { - return super.chooseAttackerOrder(attackers, game); - } else { - return getControllingPlayer(game).chooseAttackerOrder(attackers, game); - } - } - - @Override - public UUID chooseBlockerOrder(java.util.List blockers, CombatGroup combatGroup, java.util.List blockerOrder, Game game) { - if (isUnderMe(game)) { - return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } else { - return getControllingPlayer(game).chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } - } - @Override public int getAmount(int min, int max, String message, Game game) { if (isUnderMe(game)) { diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index a938ef24827..d21ecf58811 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -2177,9 +2177,6 @@ public class ComputerPlayer extends PlayerImpl { // TODO: add AI support with outcome and replace random with min/max public int getAmount(int min, int max, String message, Game game) { log.debug("getAmount"); - if (message.startsWith("Assign damage to ")) { - return min; - } if (min < max && min == 0) { return RandomUtil.nextInt(CardUtil.overflowInc(max, 1)); } @@ -2192,7 +2189,7 @@ public class ComputerPlayer extends PlayerImpl { log.debug("getMultiAmount"); int needCount = messages.size(); - List defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax); + List defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax); if (needCount == 0) { return defaultList; } @@ -2210,18 +2207,6 @@ public class ComputerPlayer extends PlayerImpl { return MultiAmountType.prepareMaxValues(messages, totalMin, totalMax); } - @Override - public UUID chooseAttackerOrder(List attackers, Game game) { - //TODO: improve this - return attackers.iterator().next().getId(); - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - //TODO: improve this - return blockers.iterator().next().getId(); - } - @Override public List getAvailableManaProducers(Game game) { return super.getAvailableManaProducers(game); diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 3bb1a34df10..5fd1b4ea3b2 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -374,22 +374,6 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer { return super.chooseMode(modes, source, game); } - @Override - public UUID chooseAttackerOrder(List attackers, Game game) { - if (this.isHuman()) { - return attackers.get(RandomUtil.nextInt(attackers.size())).getId(); - } - return super.chooseAttackerOrder(attackers, game); - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - if (this.isHuman()) { - return blockers.get(RandomUtil.nextInt(blockers.size())).getId(); - } - return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } - @Override public int getAmount(int min, int max, String message, Game game) { if (this.isHuman()) { 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 f35221b49eb..f035a45fe6b 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 @@ -2122,56 +2122,6 @@ public class HumanPlayer extends PlayerImpl { } } - @Override - public UUID chooseAttackerOrder(java.util.List attackers, Game game) { - if (gameInCheckPlayableState(game)) { - return null; - } - - while (canRespond()) { - prepareForResponse(game); - if (!isExecutingMacro()) { - game.fireSelectTargetEvent(playerId, "Pick attacker", attackers, true); - } - waitForResponse(game); - - UUID responseId = getFixedResponseUUID(game); - if (responseId != null) { - for (Permanent perm : attackers) { - if (perm.getId().equals(responseId)) { - return perm.getId(); - } - } - } - } - return null; - } - - @Override - public UUID chooseBlockerOrder(java.util.List blockers, CombatGroup combatGroup, java.util.List blockerOrder, Game game) { - if (gameInCheckPlayableState(game)) { - return null; - } - - while (canRespond()) { - prepareForResponse(game); - if (!isExecutingMacro()) { - game.fireSelectTargetEvent(playerId, "Pick blocker", blockers, true); - } - waitForResponse(game); - - UUID responseId = getFixedResponseUUID(game); - if (responseId != null) { - for (Permanent perm : blockers) { - if (perm.getId().equals(responseId)) { - return perm.getId(); - } - } - } - } - return null; - } - protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) { if (gameInCheckPlayableState(game)) { return; @@ -2260,7 +2210,7 @@ public class HumanPlayer extends PlayerImpl { Game game ) { int needCount = messages.size(); - List defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax); + List defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax); if (needCount == 0 || (needCount == 1 && totalMin == totalMax) || messages.stream().map(m -> m.min == m.max).reduce(true, Boolean::logicalAnd)) { // nothing to choose diff --git a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java index 4b922159db5..55a1fcc34b2 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianWarlord.java @@ -153,10 +153,6 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, permanent.getId(), source, null)); } - CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again - if (blockGroup != null) { - blockGroup.pickAttackerOrder(permanent.getControllerId(), game); - } } } return true; @@ -164,15 +160,4 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect { } return false; } - - private CombatGroup findBlockingGroup(Permanent blocker, Game game) { - if (game.getCombat().blockingGroupsContains(blocker.getId())) { // if (blocker.getBlocking() > 1) { - for (CombatGroup group : game.getCombat().getBlockingGroups()) { - if (group.getBlockers().contains(blocker.getId())) { - return group; - } - } - } - return null; - } } diff --git a/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java b/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java index 2e78b4dd69a..cdec812d778 100644 --- a/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java +++ b/Mage.Sets/src/mage/cards/b/BrimazKingOfOreskos.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -22,6 +21,8 @@ import mage.game.permanent.token.CatSoldierCreatureToken; import mage.game.permanent.token.Token; import mage.players.Player; +import java.util.UUID; + /** * * @author LevelX2 @@ -99,7 +100,6 @@ class BrimazKingOfOreskosEffect extends OneShotEffect { combatGroup.addBlocker(tokenId, source.getControllerId(), game); game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game); } - combatGroup.pickBlockerOrder(attackingCreature.getControllerId(), game); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FalseOrders.java b/Mage.Sets/src/mage/cards/f/FalseOrders.java index cd722afb7dc..4842979f81c 100644 --- a/Mage.Sets/src/mage/cards/f/FalseOrders.java +++ b/Mage.Sets/src/mage/cards/f/FalseOrders.java @@ -169,21 +169,6 @@ class FalseOrdersUnblockEffect extends OneShotEffect { game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, permanent.getId(), source, null)); } - CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again - if (blockGroup != null) { - blockGroup.pickAttackerOrder(permanent.getControllerId(), game); - } return true; } - - private CombatGroup findBlockingGroup(Permanent blocker, Game game) { - if (game.getCombat().blockingGroupsContains(blocker.getId())) { // if (blocker.getBlocking() > 1) { - for (CombatGroup group : game.getCombat().getBlockingGroups()) { - if (group.getBlockers().contains(blocker.getId())) { - return group; - } - } - } - return null; - } } diff --git a/Mage.Sets/src/mage/cards/f/FlashFoliage.java b/Mage.Sets/src/mage/cards/f/FlashFoliage.java index d85dcb7795c..6730fc8a77c 100644 --- a/Mage.Sets/src/mage/cards/f/FlashFoliage.java +++ b/Mage.Sets/src/mage/cards/f/FlashFoliage.java @@ -90,7 +90,6 @@ class FlashFoliageEffect extends OneShotEffect { game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game); } } - combatGroup.pickBlockerOrder(attackingCreature.getControllerId(), game); } } return true; diff --git a/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java b/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java index e9eee846fe8..f05d68a0bf5 100644 --- a/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java +++ b/Mage.Sets/src/mage/cards/g/GeneralJarkeld.java @@ -1,10 +1,6 @@ package mage.cards.g; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.condition.common.IsStepCondition; @@ -13,18 +9,18 @@ import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.PhaseStep; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAttackingCreature; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + /** * * @author L_J @@ -138,11 +134,9 @@ class GeneralJarkeldSwitchBlockersEffect extends OneShotEffect { // the ability doesn't unblock a group that loses all blockers, however it will newly block a previously unblocked group if it gains a blocker this way if (!(chosenGroup1.getBlockers().isEmpty())) { chosenGroup1.setBlocked(true, game); - chosenGroup1.pickBlockerOrder(attacker1.getControllerId(), game); } if (!(chosenGroup2.getBlockers().isEmpty())) { chosenGroup2.setBlocked(true, game); - chosenGroup2.pickBlockerOrder(attacker2.getControllerId(), game); } return true; } @@ -197,7 +191,6 @@ class GeneralJarkeldSwitchBlockersEffect extends OneShotEffect { // 10/4/2004 The new blocker does not trigger any abilities which trigger on creatures becoming blockers, because the creatures were already blockers and the simple change of who is blocking does not trigger such abilities. game.getCombat().addBlockingGroup(blocker.getId(), attacker, controller.getId(), game); } - blockGroup.pickAttackerOrder(blocker.getControllerId(), game); } } } diff --git a/Mage.Sets/src/mage/cards/m/MirrorMatch.java b/Mage.Sets/src/mage/cards/m/MirrorMatch.java index c6d0ade8f98..6efa0a8fd1e 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMatch.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMatch.java @@ -1,13 +1,12 @@ package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -20,6 +19,8 @@ import mage.players.Player; import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTargets; +import java.util.UUID; + /** * * @author LevelX2 @@ -75,20 +76,15 @@ class MirrorMatchEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(attacker, game)); effect.apply(game, source); CombatGroup group = game.getCombat().findGroup(attacker.getId()); - boolean isCreature = false; if (group != null) { for (Permanent addedToken : effect.getAddedPermanents()) { if (addedToken.isCreature(game)) { group.addBlockerToGroup(addedToken.getId(), attackerId, game); - isCreature = true; } } ExileTargetEffect exileEffect = new ExileTargetEffect("Exile those tokens at end of combat"); exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)); game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); - if (isCreature) { - group.pickBlockerOrder(attacker.getControllerId(), game); - } } } } diff --git a/Mage.Sets/src/mage/cards/s/SorrowsPath.java b/Mage.Sets/src/mage/cards/s/SorrowsPath.java index 82c220c5c8b..19836342a1f 100644 --- a/Mage.Sets/src/mage/cards/s/SorrowsPath.java +++ b/Mage.Sets/src/mage/cards/s/SorrowsPath.java @@ -176,14 +176,8 @@ class SorrowsPathSwitchBlockersEffect extends OneShotEffect { group.addBlockerToGroup(blocker.getId(), blocker.getControllerId(), game); game.getCombat().addBlockingGroup(blocker.getId(), attacker.getId(), blocker.getControllerId(), game); game.fireEvent(new BlockerDeclaredEvent(attacker.getId(), blocker.getId(), blocker.getControllerId())); - group.pickBlockerOrder(attacker.getControllerId(), game); } } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, blocker.getId(), source, null)); - CombatGroup blockGroup = findBlockingGroup(blocker, game); // a new blockingGroup is formed, so it's necessary to find it again - if (blockGroup != null) { - blockGroup.pickAttackerOrder(blocker.getControllerId(), game); - } } - } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java index 25cd66fa768..0ed11cc3005 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AfflictTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class AfflictTest extends CardTestPlayerBase { private final String khenra = "Khenra Eternal"; @@ -35,7 +37,7 @@ public class AfflictTest extends CardTestPlayerBase { attack(1, playerA, khenra); block(1, playerB, elves + ":0", khenra); block(1, playerB, elves + ":1", khenra); - setChoice(playerA, "X=1"); // assign damage + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStrictChooseMode(true); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java new file mode 100644 index 00000000000..ecd2da9036a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BandingTest.java @@ -0,0 +1,104 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author notgreat + */ +public class BandingTest extends CardTestPlayerBase { + + @Test + public void BandingAttackSimple() { + addCard(Zone.BATTLEFIELD, playerA, "Squire"); // 1/2 + addCard(Zone.BATTLEFIELD, playerA, "Benalish Infantry"); // Banding 1/3 + addCard(Zone.BATTLEFIELD, playerA, "Eager Cadet"); // 1/1 + + addCard(Zone.BATTLEFIELD, playerB, "Naga Eternal"); // 3/2 + + attack(1, playerA, "Squire"); + attack(1, playerA, "Benalish Infantry"); + attack(1, playerA, "Eager Cadet"); + setChoice(playerA, true); + setChoice(playerA, "Squire"); + block(1, playerB, "Naga Eternal", "Squire"); + setChoiceAmount(playerA, 1, 2); //1 to Squire, 2 to Infantry, attacking player chooses + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 0); + assertDamageReceived(playerA, "Squire", 1); + assertDamageReceived(playerA, "Benalish Infantry", 2); + assertGraveyardCount(playerB, 1); + assertLife(playerB, 19); // Only Eager Cadet gets through + } + + @Test + public void BandingBlockSimple() { + addCard(Zone.BATTLEFIELD, playerA, "Alpine Grizzly"); // 4/2 + addCard(Zone.BATTLEFIELD, playerB, "Squire"); // 1/2 + addCard(Zone.BATTLEFIELD, playerB, "Sanctuary Cat"); // 1/2 + addCard(Zone.BATTLEFIELD, playerB, "Benalish Infantry"); // Banding 1/3 + + attack(1, playerA, "Alpine Grizzly"); + block(1, playerB, "Squire", "Alpine Grizzly"); + block(1, playerB, "Sanctuary Cat", "Alpine Grizzly"); + block(1, playerB, "Benalish Infantry", "Alpine Grizzly"); + setChoiceAmount(playerB, 1, 1, 2); //1 to Squire, 1 to Cat, 2 to Infantry, defending player chooses + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 1); + assertGraveyardCount(playerB, 0); + assertDamageReceived(playerB, "Squire", 1); + assertDamageReceived(playerB, "Sanctuary Cat", 1); + assertDamageReceived(playerB, "Benalish Infantry", 2); + assertLife(playerB, 20); + + } + + @Test + public void DoubleBanding() { + addCard(Zone.BATTLEFIELD, playerA, "Benalish Infantry"); // Banding 1/3 + addCard(Zone.BATTLEFIELD, playerA, "Fortress Crab"); // 1/6 + addCard(Zone.BATTLEFIELD, playerA, "Eager Cadet"); // 1/1 + + addCard(Zone.BATTLEFIELD, playerB, "Catacomb Slug"); // 2/6 + addCard(Zone.BATTLEFIELD, playerB, "War Elephant"); // Banding 2/2 Trample + + attack(1, playerA, "Benalish Infantry"); + attack(1, playerA, "Fortress Crab"); + attack(1, playerA, "Eager Cadet"); + setChoice(playerA, true); + setChoice(playerA, "Fortress Crab"); + + block(1, playerB, "Catacomb Slug", "Benalish Infantry"); + block(1, playerB, "War Elephant", "Benalish Infantry"); + + setChoiceAmount(playerB, 0, 1); // Damage from Benalish Infantry + setChoiceAmount(playerB, 0, 1); // Damage from Fortress Crab + + setChoiceAmount(playerA, 1, 1); // Damage from War Elephant + setChoiceAmount(playerA, 0, 2); // Damage from Catacomb Slug + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 0); + assertDamageReceived(playerA, "Benalish Infantry", 1); + assertDamageReceived(playerA, "Fortress Crab", 3); + + assertGraveyardCount(playerB, "War Elephant", 1); + assertDamageReceived(playerB, "Catacomb Slug", 0); + + assertLife(playerB, 19); // Only Eager Cadet gets through + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java index d3c9cbd45a4..6d097cd3152 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BushidoTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author noxx */ @@ -21,6 +23,7 @@ public class BushidoTest extends CardTestPlayerBase { attack(2, playerB, "Isao, Enlightened Bushi"); block(2, playerA, "Elite Vanguard", "Isao, Enlightened Bushi"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); @@ -39,6 +42,7 @@ public class BushidoTest extends CardTestPlayerBase { attack(2, playerB, "Elite Vanguard"); block(2, playerA, "Isao, Enlightened Bushi", "Elite Vanguard"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); @@ -58,9 +62,9 @@ public class BushidoTest extends CardTestPlayerBase { attack(2, playerB, "Isao, Enlightened Bushi"); block(2, playerA, "Llanowar Elves", "Isao, Enlightened Bushi"); block(2, playerA, "Elvish Mystic", "Isao, Enlightened Bushi"); - setChoice(playerB, "X=1"); // assign damage - setChoice(playerB, "X=1"); // assign damage + setChoice(playerB, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java index 831b6f77dd8..47e5417a493 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author TheElk801 */ @@ -158,15 +160,7 @@ public class ExcessDamageTest extends CardTestPlayerBase { block(2, playerA, bear, myrSuperion); block(2, playerA, envoy, myrSuperion); block(2, playerA, bondedConstruct, myrSuperion); - - //Assign this much damage to the first blocking creature - setChoice(playerB, "X=2"); - - //Assign this much damage to the second blocking creature - setChoice(playerB, "X=1"); - - //Assign this much damage to the third blocking creature - setChoice(playerB, "X=1"); + setChoice(playerB, CHOICE_SKIP); // Assign default damage setStrictChooseMode(true); setStopAt(2, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java index 4c4afe0424e..e9356c93be4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/emblems/TheRingEmblemTest.java @@ -15,6 +15,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author JayDi85 */ @@ -247,9 +249,10 @@ public class TheRingEmblemTest extends CardTestPlayerBase { attack(3, playerA, "Ashiok's Skulker"); block(3, playerB, "Alabaster Kirin", "Ashiok's Skulker"); block(3, playerB, "Alaborn Trooper", "Ashiok's Skulker"); - setChoice(playerA, "Whenever your Ring-bearer becomes blocked"); // 2x triggers from two blockers - setChoice(playerA, "At end of combat, that permanent"); // 2x triggers from two blockers setChoice(playerA, "Mountain"); // draw/discard on attack trigger + setChoice(playerA, "Whenever your Ring-bearer becomes blocked"); // 2x triggers from two blockers + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setChoice(playerA, "At end of combat, that permanent"); // 2x triggers from two blockers checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerA, "Ashiok's Skulker", 1); checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerB, "Academy Manufactor", 0); checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerB, "Alabaster Kirin", 0); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java index 24c35a7b2be..c8918a46f58 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/prevention/PreventDamageRemoveCountersTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { @Test @@ -67,7 +69,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { attack(3, playerA, "Magma Pummeler", playerB); block(3, playerB, "Memnite", "Magma Pummeler"); block(3, playerB, "Goblin Piker", "Magma Pummeler"); - setChoice(playerA, "X=5"); // damage for Pummeler, does not really matter for this test. + setChoice(playerA, CHOICE_SKIP); // Assign default damage addTarget(playerA, playerB); // For the one trigger setStopAt(3, PhaseStep.END_TURN); @@ -117,7 +119,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { attack(3, playerA, "Magma Pummeler", playerB); block(3, playerB, "Centaur Courser", "Magma Pummeler"); block(3, playerB, "Air Elemental", "Magma Pummeler"); - setChoice(playerA, "X=5"); // damage for Pummeler, does not really matter for this test. + setChoice(playerA, CHOICE_SKIP); // Assign default damage addTarget(playerA, playerB); // For the one trigger setStopAt(3, PhaseStep.END_TURN); @@ -148,7 +150,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase { attack(1, playerA, "Undergrowth Champion", playerB); block(1, playerB, "Grizzly Bears", "Undergrowth Champion"); block(1, playerB, "Elite Vanguard", "Undergrowth Champion"); - setChoice(playerA, "X=2"); // damage attribution + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java index 6f1035cc025..a2c4f36613f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java @@ -6,6 +6,8 @@ import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class RiteOfPassageTest extends CardTestPlayerBase { @@ -38,12 +40,7 @@ public class RiteOfPassageTest extends CardTestPlayerBase { attack(1, playerA, "Watchwolf", playerB); block(1, playerB, "Memnite", "Watchwolf"); block(1, playerB, "Agent of Stromgald", "Watchwolf"); - - // Assign this much damage to Memnite - setChoice(playerA, "X=1"); - - // Assign this much damage to Agent of Stromgald - setChoice(playerA, "X=1"); + setChoice(playerA, CHOICE_SKIP); setStopAt(1, PhaseStep.END_TURN); setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java index 9360089029e..70bd6aca5dd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BindingAgonyTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author Susucr */ @@ -33,7 +35,9 @@ public class BindingAgonyTest extends CardTestPlayerBase { attack(1, playerA, "Grizzly Bears"); block(1, playerB, "Centaur Courser", "Grizzly Bears"); block(1, playerB, "Memnite", "Grizzly Bears"); + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -53,6 +57,7 @@ public class BindingAgonyTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, agony, "Grizzly Bears", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java index 18669190a78..e34500b79c8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/BloatflySwarmTest.java @@ -6,6 +6,8 @@ import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author Susucr */ @@ -32,6 +34,7 @@ public class BloatflySwarmTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swarm, true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", swarm); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -55,7 +58,9 @@ public class BloatflySwarmTest extends CardTestPlayerBase { attack(1, playerA, swarm); block(1, playerB, "Brimstone Dragon", swarm); block(1, playerB, "Giant Spider", swarm); + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -79,8 +84,9 @@ public class BloatflySwarmTest extends CardTestPlayerBase { attack(1, playerA, swarm); block(1, playerB, "Wind Drake", swarm); block(1, playerB, "Giant Spider", swarm); - setChoice(playerA, "X=5"); // damage attribution + setChoice(playerA, CHOICE_SKIP); // Assign default damage + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java index 2048a9ea9f7..b2d10bbb30c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/sth/WallOfEssenceTest.java @@ -48,7 +48,7 @@ public class WallOfEssenceTest extends CardTestPlayerBase { block(1, playerB, "Memnite", "Grizzly Bears"); block(1, playerB, wall, "Grizzly Bears"); - setChoice(playerA, "X=2"); // 2 damage on Memnite, no damage to Wall + setChoiceAmount(playerA, 2, 0); // 2 damage on Memnite, no damage to Wall setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java index 0e64acdd57b..4467b893c77 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tsp/PhantomWurmTest.java @@ -6,6 +6,8 @@ import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * @author Susucr */ @@ -31,9 +33,7 @@ public class PhantomWurmTest extends CardTestPlayerBase { attack(1, playerA, wurm, playerB); block(1, playerB, "Memnite", wurm); block(1, playerB, "Eager Cadet", wurm); - - setChoice(playerA, "X=1"); // damage assignment - setChoice(playerA, "X=3"); // damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_TURN); execute(); @@ -115,9 +115,7 @@ public class PhantomWurmTest extends CardTestPlayerBase { attack(1, playerA, wurm, playerB); block(1, playerB, "Memnite", wurm); block(1, playerB, "Goblin Striker", wurm); - - setChoice(playerA, "X=1"); // damage assignment - setChoice(playerA, "X=3"); // damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_TURN); execute(); @@ -139,9 +137,7 @@ public class PhantomWurmTest extends CardTestPlayerBase { attack(1, playerA, wurm, playerB); block(1, playerB, "Boros Recruit", wurm); block(1, playerB, "Goblin Striker", wurm); - - setChoice(playerA, "X=1"); // damage assignment - setChoice(playerA, "X=3"); // damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java index 06b8129f704..12ab9202cfe 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetMultiAmountTest.java @@ -57,7 +57,7 @@ public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps { private void assertDefaultValuesUnconstrained(String need, int count, int min, int max) { List constraints = getUnconstrainedConstraints(count); - List defaultValues = MultiAmountType.prepareDefaltValues(constraints, min, max); + List defaultValues = MultiAmountType.prepareDefaultValues(constraints, min, max); String current = defaultValues .stream() .map(String::valueOf) @@ -122,7 +122,7 @@ public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps { getUnconstrainedConstraints(4)); // good values are checking in test_DefaultValues, it's an additional - List list = MultiAmountType.prepareDefaltValues(constraints.get(3), 0, 0); + List list = MultiAmountType.prepareDefaultValues(constraints.get(3), 0, 0); // count (0, 0, 0) Assert.assertFalse("count", MultiAmountType.isGoodValues(list, constraints.get(0), 0, 0)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java index 826afcc4757..01c72e5f8ef 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommander4Players; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * * @author jimga150 @@ -32,12 +34,7 @@ public class DonnaNobleTests extends CardTestCommander4Players { attack(5, playerA, "Impervious Greatwurm", playerB); block(5, playerB, "Memnite", "Impervious Greatwurm"); block(5, playerB, "Expedition Envoy", "Impervious Greatwurm"); - - //Assign this much damage to the first blocking creature - setChoice(playerA, "X=1"); - - //Assign this much damage to the second blocking creature - setChoice(playerA, "X=1"); + setChoice(playerA, CHOICE_SKIP); // Assign default damage //Target this player with Donna Noble addTarget(playerA, playerB); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java index e4c30fd1535..2d7f9422924 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java @@ -6,6 +6,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommander4Players; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + /** * * @author jimga150 @@ -25,9 +27,7 @@ public class DamagedBatchTests extends CardTestCommander4Players { attack(1, playerA, "Donna Noble", playerB); block(1, playerB, "Memnite", "Donna Noble"); block(1, playerB, "Expedition Envoy", "Donna Noble"); - - //Assign this much damage to the first blocking creature - setChoice(playerA, "X=1"); + setChoice(playerA, CHOICE_SKIP); // Assign default damage //Target this player with Donna Noble addTarget(playerA, playerB); diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java index a43818f1c90..f64f6bf89a5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/AttackBlockRestrictionsTest.java @@ -11,6 +11,7 @@ import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; /** * Test restrictions for choosing attackers and blockers. @@ -753,9 +754,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { 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"); + setChoice(playerA, CHOICE_SKIP); setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -783,7 +784,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { // ai must choose all blockers anyway attack(1, playerA, "Sonorous Howlbonder"); aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites + setChoiceAmount(playerA, 1, 1, 0); // assign damage to blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite"); @@ -813,9 +814,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { 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"); + setChoice(playerA, CHOICE_SKIP); setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -845,7 +846,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { // ai must choose all blockers attack(1, playerA, "Sonorous Howlbonder"); aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); - setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites + setChoiceAmount(playerA, 1, 1, 0, 0, 0); // assign damage to blocking memnites checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder"); checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite"); @@ -879,7 +880,6 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { 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"); @@ -998,8 +998,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps { addTarget(playerA, "Alley Strangler"); // boost target setChoice(playerA, true); // boost target attack(1, playerA, "Alley Strangler"); - setChoiceAmount(playerA, 1); // assign damage to 1 of 2 blocking memnites - setChoiceAmount(playerA, 1); // assign damage to 2 of 2 blocking memnites + setChoiceAmount(playerA, 1, 1); // assign damage to blocking memnites aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB); checkAttackers("x1 attacker", 1, playerA, "Alley Strangler"); checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite"); diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java index c4f4c2218b2..6484c53cf75 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java @@ -197,4 +197,35 @@ public class DamageDistributionTest extends CardTestPlayerBase { assertLife(playerB, 20 - 5); } + + @Test + public void test2x2Block() { + addCard(Zone.BATTLEFIELD, playerA, "Catacomb Slug"); // 2/6 + addCard(Zone.BATTLEFIELD, playerA, "Catacomb Crocodile"); // 3/7 + + addCard(Zone.BATTLEFIELD, playerB, "Brave the Sands"); //can block 2 + addCard(Zone.BATTLEFIELD, playerB, "Marsh Hulk"); // 4/6 + addCard(Zone.BATTLEFIELD, playerB, "Fortress Crab"); // 1/6 + + attack(1, playerA, "Catacomb Slug"); + attack(1, playerA, "Catacomb Crocodile"); + block(1, playerB, "Fortress Crab", "Catacomb Slug"); + block(1, playerB, "Fortress Crab", "Catacomb Crocodile"); + block(1, playerB, "Marsh Hulk", "Catacomb Slug"); + block(1, playerB, "Marsh Hulk", "Catacomb Crocodile"); + + setChoiceAmount(playerA, 1, 1); // Catacomb Slug + setChoiceAmount(playerA, 1, 2); // Catacomb Crocodile + setChoiceAmount(playerB, 1, 0); // Fortress Crab + setChoiceAmount(playerB, 2, 2); // Marsh Hulk + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertDamageReceived(playerA, "Catacomb Slug", 3); + assertDamageReceived(playerA, "Catacomb Crocodile", 2); + assertDamageReceived(playerB, "Fortress Crab", 2); + assertDamageReceived(playerB, "Marsh Hulk", 3); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java index 2881b195b43..aaeb74c2ec2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java @@ -5,6 +5,8 @@ import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.mage.test.player.TestPlayer.CHOICE_SKIP; + public class LifelinkInCombatTest extends CardTestPlayerBase { @Test public void testOneBlockerTrample() { @@ -43,8 +45,7 @@ public class LifelinkInCombatTest extends CardTestPlayerBase { attack(1, playerA, "Brion Stoutarm"); block(1, playerB, "Boros Recruit", "Brion Stoutarm"); block(1, playerB, "Suntail Hawk", "Brion Stoutarm"); - setChoice(playerA, "X=1"); // Damage assignment - setChoice(playerA, "X=1"); // Damage assignment + setChoice(playerA, CHOICE_SKIP); // Assign default damage addTarget(playerA, "Brion Stoutarm"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); 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 15dfcd42146..e33796e3a0f 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 @@ -2975,11 +2975,12 @@ public class TestPlayer implements Player { assertAliasSupportInChoices(false); int needCount = messages.size(); - List defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax); + List defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax); if (needCount == 0) { return defaultList; } + List answer = new ArrayList<>(defaultList); if (!choices.isEmpty()) { // must fill all possible choices or skip it @@ -4477,19 +4478,6 @@ public class TestPlayer implements Player { return computerPlayer.playMana(ability, unpaid, promptText, game); } - @Override - public UUID chooseAttackerOrder(List attacker, Game game - ) { - return computerPlayer.chooseAttackerOrder(attacker, game); - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, - List blockerOrder, Game game - ) { - return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); - } - @Override public void sideboard(Match match, Deck deck ) { diff --git a/Mage/src/main/java/mage/constants/MultiAmountType.java b/Mage/src/main/java/mage/constants/MultiAmountType.java index ed5b1d5b34b..398ca7a0fd8 100644 --- a/Mage/src/main/java/mage/constants/MultiAmountType.java +++ b/Mage/src/main/java/mage/constants/MultiAmountType.java @@ -8,23 +8,24 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -public enum MultiAmountType { +public class MultiAmountType { - MANA("Add mana", "Distribute mana among colors"), - DAMAGE("Assign damage", "Assign damage among targets"), - P1P1("Add +1/+1 counters", "Distribute +1/+1 counters among creatures"), - COUNTERS("Choose counters", "Move counters"), - CHEAT_LANDS("Choose lands", "Add lands to your battlefield", true); + public static final MultiAmountType MANA = new MultiAmountType("Add mana", "Distribute mana among colors"); + public static final MultiAmountType DAMAGE = new MultiAmountType("Assign damage", "Assign damage among targets"); + + public static final MultiAmountType P1P1 = new MultiAmountType("Add +1/+1 counters", "Distribute +1/+1 counters among creatures"); + public static final MultiAmountType COUNTERS = new MultiAmountType("Choose counters", "Move counters"); + public static final MultiAmountType CHEAT_LANDS = new MultiAmountType("Choose lands", "Add lands to your battlefield", true); private final String title; private final String header; private final boolean canCancel; // choice dialog will return null instead default values - MultiAmountType(String title, String header) { + public MultiAmountType(String title, String header) { this(title, header, false); } - MultiAmountType(String title, String header, boolean canCancel) { + public MultiAmountType(String title, String header, boolean canCancel) { this.title = title; this.header = header; this.canCancel = canCancel; @@ -42,7 +43,7 @@ public enum MultiAmountType { return canCancel; } - public static List prepareDefaltValues(List constraints, int min, int max) { + public static List prepareDefaultValues(List constraints, int min, int max) { // default values must be assigned from first to last by minimum values List res = constraints.stream().map(m -> m.defaultValue > Integer.MIN_VALUE ? m.defaultValue : Math.min(0, max)) .collect(Collectors.toList()); @@ -50,7 +51,7 @@ public enum MultiAmountType { return res; } - int total = res.stream().mapToInt(x -> x).sum();; + int total = res.stream().mapToInt(x -> x).sum(); // Fill values until we reach the overall minimum. Do this by filling values up until either their max or however much is leftover, starting with the first option. if (min > 0 && total < min) { @@ -174,7 +175,7 @@ public enum MultiAmountType { // data check if (returnDefaultOnError && !isGoodValues(res, constraints, min, max)) { // on broken data - return default - return prepareDefaltValues(constraints, min, max); + return prepareDefaultValues(constraints, min, max); } return res; diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 91637785533..333e786b95a 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -60,7 +60,7 @@ public class Combat implements Serializable, Copyable { protected List groups = new ArrayList<>(); protected List formerGroups = new ArrayList<>(); - protected Map blockingGroups = new HashMap<>(); + protected Map blockingGroups = new LinkedHashMap<>(); // all possible defenders (players, planeswalkers or battle) protected Set defenders = new HashSet<>(); // how many creatures attack defending player @@ -200,7 +200,7 @@ public class Combat implements Serializable, Copyable { StringBuilder sb = new StringBuilder(); sb.append(attackingPlayerId).append(defenders); for (CombatGroup group : groups) { - sb.append(group.defenderId).append(group.attackers).append(group.attackerOrder).append(group.blockers).append(group.blockerOrder); + sb.append(group.defenderId).append(group.attackers).append(group.blockers); } return sb.toString(); } @@ -785,7 +785,7 @@ public class Combat implements Serializable, Copyable { if (attackerExists) { if (!group.getBlockers().isEmpty()) { sb.append("blocked by "); - for (UUID blockingCreatureId : group.getBlockerOrder()) { + for (UUID blockingCreatureId : group.getBlockers()) { Permanent blockingCreature = game.getPermanent(blockingCreatureId); if (blockingCreature != null) { sb.append(blockingCreature.getLogName()).append(" ("); @@ -1799,24 +1799,11 @@ public class Combat implements Serializable, Copyable { return playerDefenders; } - public void damageAssignmentOrder(Game game) { - for (CombatGroup group : groups) { - group.pickBlockerOrder(attackingPlayerId, game); - } - for (Map.Entry blockingGroup : blockingGroups.entrySet()) { - Permanent blocker = game.getPermanent(blockingGroup.getKey()); - if (blocker != null) { - blockingGroup.getValue().pickAttackerOrder(blocker.getControllerId(), game); - } - } - } - @SuppressWarnings("deprecation") public void removeAttacker(UUID attackerId, Game game) { for (CombatGroup group : groups) { if (group.attackers.contains(attackerId)) { group.attackers.remove(attackerId); - group.attackerOrder.remove(attackerId); for (Set attackingCreatures : numberCreaturesDefenderAttackedBy.values()) { attackingCreatures.remove(attackerId); } @@ -1869,7 +1856,6 @@ public class Combat implements Serializable, Copyable { } for (CombatGroup group : groupsToCheck) { group.blockers.remove(blockerId); - group.blockerOrder.remove(blockerId); if (group.blockers.isEmpty()) { group.blocked = false; } @@ -1885,11 +1871,9 @@ public class Combat implements Serializable, Copyable { if (blockGroup.blockers.contains(blockerId)) { for (UUID attackerId : group.getAttackers()) { blockGroup.attackers.remove(attackerId); - blockGroup.attackerOrder.remove(attackerId); } if (creature.getBlocking() == 0) { blockGroup.blockers.remove(blockerId); - blockGroup.attackerOrder.clear(); } } if (blockGroup.blockers.isEmpty()) { @@ -1914,7 +1898,6 @@ public class Combat implements Serializable, Copyable { for (CombatGroup group : groups) { if (group.blockers.contains(blockerId)) { group.blockers.remove(blockerId); - group.blockerOrder.remove(blockerId); if (group.blockers.isEmpty()) { group.blocked = false; } @@ -1924,7 +1907,6 @@ public class Combat implements Serializable, Copyable { for (CombatGroup group : getBlockingGroups()) { if (group.blockers.contains(blockerId)) { group.blockers.remove(blockerId); - group.attackerOrder.clear(); } if (group.blockers.isEmpty()) { canRemove = true; diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index c7caca995fa..03a492dd05f 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -6,6 +6,7 @@ import mage.abilities.common.ControllerDivideCombatDamageAbility; import mage.abilities.common.DamageAsThoughNotBlockedAbility; import mage.abilities.keyword.*; import mage.constants.AsThoughEffectType; +import mage.constants.MultiAmountType; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; @@ -15,6 +16,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.Copyable; +import mage.util.MultiAmountMessage; import mage.watchers.common.FirstStrikeWatcher; import java.io.Serializable; @@ -29,8 +31,6 @@ public class CombatGroup implements Serializable, Copyable { protected List attackers = new ArrayList<>(); protected List formerAttackers = new ArrayList<>(); protected List blockers = new ArrayList<>(); - protected List blockerOrder = new ArrayList<>(); - protected List attackerOrder = new ArrayList<>(); protected Map players = new HashMap<>(); protected boolean blocked; protected UUID defenderId; // planeswalker, player, or battle id, can be null after remove from combat (e.g. due damage) @@ -52,8 +52,6 @@ public class CombatGroup implements Serializable, Copyable { this.attackers.addAll(group.attackers); this.formerAttackers.addAll(group.formerAttackers); this.blockers.addAll(group.blockers); - this.blockerOrder.addAll(group.blockerOrder); - this.attackerOrder.addAll(group.attackerOrder); this.players.putAll(group.players); this.blocked = group.blocked; this.defenderId = group.defenderId; @@ -91,10 +89,6 @@ public class CombatGroup implements Serializable, Copyable { return blockers; } - public List getBlockerOrder() { - return blockerOrder; - } - private static boolean hasFirstOrDoubleStrike(Permanent perm) { return hasFirstStrike(perm) || hasDoubleStrike(perm); } @@ -175,7 +169,6 @@ public class CombatGroup implements Serializable, Copyable { if (attacker != null && !assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(attacker, attacker.getControllerId(), first, game, true)) { if (blockers.isEmpty()) { unblockedDamage(first, game); - return; } else { Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId()); if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) && @@ -186,11 +179,7 @@ public class CombatGroup implements Serializable, Copyable { blocked = false; unblockedDamage(first, game); } - if (blockers.size() == 1) { - singleBlockerDamage(player, first, game); - } else { - multiBlockerDamage(player, first, game); - } + blockerDamage(player, first, game); } } } @@ -206,9 +195,7 @@ public class CombatGroup implements Serializable, Copyable { } } if (attackers.size() != 1) { - multiAttackerDamage(first, game); - // } else { - // singleAttackerDamage(first, game); + attackerDamage(first, game); } } } @@ -269,51 +256,16 @@ public class CombatGroup implements Serializable, Copyable { } } } - - private void singleBlockerDamage(Player player, boolean first, Game game) { - Permanent blocker = game.getPermanent(blockers.get(0)); - Permanent attacker = game.getPermanent(attackers.get(0)); - if (blocker != null && attacker != null) { - int blockerDamage = getDamageValueFromPermanent(blocker, game); // must be set before attacker damage marking because of effects like Test of Faith - if (blocked && dealsDamageThisStep(attacker, first, game)) { - int damage = getDamageValueFromPermanent(attacker, game); - if (hasTrample(attacker)) { - int lethalDamage = getLethalDamage(blocker, attacker, game); - if (lethalDamage >= damage) { - blocker.markDamage(damage, attacker.getId(), null, game, true, true); - } else { - int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); - blocker.markDamage(damageAssigned, attacker.getId(), null, game, true, true); - damage -= damageAssigned; - if (damage > 0) { - defenderDamage(attacker, damage, game, false); - } - } - } else { - blocker.markDamage(damage, attacker.getId(), null, game, true, true); - } - } - if (dealsDamageThisStep(blocker, first, game)) { - if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately - if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { - attacker.markDamage(blockerDamage, blocker.getId(), null, game, true, true); - } - } - } - } - } - - private void multiBlockerDamage(Player player, boolean first, Game game) { + private void blockerDamage(Player player, boolean first, Game game) { Permanent attacker = game.getPermanent(attackers.get(0)); if (attacker == null) { return; } - boolean oldRuleDamage = (Objects.equals(player.getId(), defendingPlayerId)); int damage = getDamageValueFromPermanent(attacker, game); if (dealsDamageThisStep(attacker, first, game)) { // must be set before attacker damage marking because of effects like Test of Faith Map blockerPower = new HashMap<>(); - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately @@ -322,42 +274,62 @@ public class CombatGroup implements Serializable, Copyable { } } Map assigned = new HashMap<>(); + List damageDivision = new ArrayList<>(); + List blockersCopy = new ArrayList<>(blockers); if (blocked) { - boolean excessDamageToDefender = true; - for (UUID blockerId : new ArrayList<>(blockerOrder)) { // prevent ConcurrentModificationException + int remainingDamage = damage; + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { - int lethalDamage = getLethalDamage(blocker, attacker, game); - if (lethalDamage >= damage) { - if (!oldRuleDamage) { - assigned.put(blockerId, damage); - damage = 0; - break; - } else if (damage == 0) { - break; - } - } - int damageAssigned = 0; - if (!oldRuleDamage) { - damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); - } else { - damageAssigned = player.getAmount(0, damage, "Assign damage to " + blocker.getName(), game); - if (damageAssigned < lethalDamage) { - excessDamageToDefender = false; // all blockers need to have lethal damage assigned before it can trample over to the defender - } - } - assigned.put(blockerId, damageAssigned); - damage -= damageAssigned; + int defaultDamage = Math.min(remainingDamage, blocker.getLethalDamage(attacker.getId(), game)); + remainingDamage -= defaultDamage; + String message = String.format("%s, P/T: %d/%d", + blocker.getLogName(), + blocker.getPower().getValue(), + blocker.getToughness().getValue()); + damageDivision.add(new MultiAmountMessage(message, 0, damage, defaultDamage)); } } - if (damage > 0 && hasTrample(attacker) && excessDamageToDefender) { - defenderDamage(attacker, damage, game, false); - } else if (!blockerOrder.isEmpty()) { - // Assign the damage left to first blocker - assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) == null ? 0 : assigned.get(blockerOrder.get(0)) + damage); + List amounts; + if (hasTrample(attacker)){ + if (remainingDamage > 0 || damageDivision.size() > 1) { + MultiAmountType dialogue = new MultiAmountType("Assign combat damage (with trample)", + String.format("Assign combat damage among creatures blocking %s, P/T: %d/%d (Unassigned damage tramples through)", + attacker.getLogName(), attacker.getPower().getValue(), attacker.getToughness().getValue())); + amounts = player.getMultiAmountWithIndividualConstraints(Outcome.Damage, damageDivision, damage - remainingDamage, damage, dialogue, game); + } else { + amounts = new ArrayList<>(); + if (damageDivision.size() == 1) { // Assign all damage to one blocker + amounts.add(damage); + } + } + int trampleDamage = damage - (amounts.stream().mapToInt(x -> x).sum()); + if (trampleDamage > 0) { + defenderDamage(attacker, trampleDamage, game, false); + } + } else { + if (remainingDamage > 0){ + damageDivision.get(0).defaultValue += remainingDamage; + } + if (damageDivision.size() > 1) { + MultiAmountType dialogue = new MultiAmountType("Assign combat damage", + String.format("Assign combat damage among creatures blocking %s, P/T: %d/%d", + attacker.getLogName(), attacker.getPower().getValue(), attacker.getToughness().getValue())); + amounts = player.getMultiAmountWithIndividualConstraints(Outcome.Damage, damageDivision, damage, damage, dialogue, game); + } else { + amounts = new LinkedList<>(); + if (damageDivision.size() == 1) { // Assign all damage to one blocker + amounts.add(damage); + } + } + } + if (!damageDivision.isEmpty()){ + for (int i=0; i { } } } else { - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { @@ -395,7 +367,7 @@ public class CombatGroup implements Serializable, Copyable { if (dealsDamageThisStep(attacker, first, game)) { // must be set before attacker damage marking because of effects like Test of Faith Map blockerPower = new HashMap<>(); - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately @@ -422,7 +394,7 @@ public class CombatGroup implements Serializable, Copyable { } } if (isAttacking) { - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Integer power = blockerPower.get(blockerId); if (power != null) { // might be missing canDamage condition? @@ -439,7 +411,7 @@ public class CombatGroup implements Serializable, Copyable { } } else { if (isAttacking) { - for (UUID blockerId : blockerOrder) { + for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (dealsDamageThisStep(blocker, first, game)) { if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) { @@ -473,81 +445,62 @@ public class CombatGroup implements Serializable, Copyable { /** * Damages attacking creatures by a creature that blocked several ones * Damages only attackers as blocker was damage in - * {@link #singleBlockerDamage}. + * {@link #blockerDamage}. *

- * Handles abilities like "{this} an block any number of creatures.". - *

- * Blocker damage for blockers blocking single creatures is handled in the - * single/multi blocker methods, so this shouldn't be used anymore. - * - * @param first - * @param game - * @deprecated - */ - @Deprecated - private void singleAttackerDamage(boolean first, Game game) { - Permanent blocker = game.getPermanent(blockers.get(0)); - Permanent attacker = game.getPermanent(attackers.get(0)); - if (blocker != null && attacker != null) { - if (dealsDamageThisStep(blocker, first, game)) { - int damage = getDamageValueFromPermanent(blocker, game); - attacker.markDamage(damage, blocker.getId(), null, game, true, true); - } - } - } - - /** - * Damages attacking creatures by a creature that blocked several ones - * Damages only attackers as blocker was damage in either - * {@link #singleBlockerDamage} or {@link #multiBlockerDamage}. - *

- * Handles abilities like "{this} an block any number of creatures.". + * Handles abilities like "{this} can block any number of creatures.". * * @param first * @param game */ - private void multiAttackerDamage(boolean first, Game game) { + private void attackerDamage(boolean first, Game game) { Permanent blocker = game.getPermanent(blockers.get(0)); if (blocker == null) { return; } - boolean oldRuleDamage = attackerAssignsCombatDamage(game); // handles banding - Player player = game.getPlayer(oldRuleDamage ? game.getCombat().getAttackingPlayerId() : blocker.getControllerId()); + //Handle Banding + Player player = game.getPlayer(attackerAssignsCombatDamage(game) ? game.getCombat().getAttackingPlayerId() : blocker.getControllerId()); int damage = getDamageValueFromPermanent(blocker, game); if (dealsDamageThisStep(blocker, first, game)) { Map assigned = new HashMap<>(); - for (UUID attackerId : attackerOrder) { + List damageDivision = new ArrayList<>(); + List attackersCopy = new ArrayList<>(attackers); + int remainingDamage = damage; + for (UUID attackerId : attackers) { Permanent attacker = game.getPermanent(attackerId); if (attacker != null) { - int lethalDamage = getLethalDamage(attacker, blocker, game); - if (lethalDamage >= damage) { - if (!oldRuleDamage) { - assigned.put(attackerId, damage); - damage = 0; - break; - } else if (damage == 0) { - break; - } - } - int damageAssigned = 0; - if (!oldRuleDamage) { - damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + attacker.getName(), game); - } else { - damageAssigned = player.getAmount(0, damage, "Assign damage to " + attacker.getName(), game); - } - assigned.put(attackerId, damageAssigned); - damage -= damageAssigned; + int defaultDamage = Math.min(remainingDamage, attacker.getLethalDamage(blocker.getId(), game)); + remainingDamage -= defaultDamage; + String message = String.format("%s, P/T: %d/%d", + attacker.getLogName(), + attacker.getPower().getValue(), + attacker.getToughness().getValue()); + damageDivision.add(new MultiAmountMessage(message, 0, damage, defaultDamage)); } } - if (damage > 0) { - // Assign the damage left to first attacker - assigned.put(attackerOrder.get(0), assigned.get(attackerOrder.get(0)) + damage); + List amounts; + if (remainingDamage > 0){ + damageDivision.get(0).defaultValue += remainingDamage; + } + if (damageDivision.size() > 1) { + MultiAmountType dialogue = new MultiAmountType("Assign blocker combat damage", + String.format("Assign combat damage among creatures blocked by %s, P/T: %d/%d", + blocker.getLogName(), blocker.getPower().getValue(), blocker.getToughness().getValue())); + amounts = player.getMultiAmountWithIndividualConstraints(Outcome.Damage, damageDivision, damage, damage, dialogue, game); + } else { + amounts = new LinkedList<>(); + amounts.add(damage); + } + if (!damageDivision.isEmpty()){ + for (int i=0; i entry : assigned.entrySet()) { Permanent attacker = game.getPermanent(entry.getKey()); - attacker.markDamage(entry.getValue(), blocker.getId(), null, game, true, true); + if (attacker != null) { + attacker.markDamage(entry.getValue(), blocker.getId(), null, game, true, true); + } } } } @@ -632,74 +585,11 @@ public class CombatGroup implements Serializable, Copyable { if (blockerId != null && blocker != null) { blocker.setBlocking(blocker.getBlocking() + 1); blockers.add(blockerId); - blockerOrder.add(blockerId); this.blocked = true; this.players.put(blockerId, playerId); } } - public void pickBlockerOrder(UUID playerId, Game game) { - if (blockers.isEmpty()) { - return; - } - Player player = game.getPlayer(playerId); // game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : playerId); // this was incorrect because defenderAssignsCombatDamage might be false by the time damage is dealt - List blockerList = new ArrayList<>(blockers); - blockerOrder.clear(); - while (player.canRespond()) { - if (blockerList.size() == 1) { - blockerOrder.add(blockerList.get(0)); - break; - } else { - List blockerPerms = new ArrayList<>(); - for (UUID blockerId : blockerList) { - blockerPerms.add(game.getPermanent(blockerId)); - } - UUID blockerId = player.chooseBlockerOrder(blockerPerms, this, blockerOrder, game); - blockerOrder.add(blockerId); - blockerList.remove(blockerId); - } - } - if (!game.isSimulation() && blockerOrder.size() > 1) { - logDamageAssignmentOrder("Creatures blocking ", attackers, blockerOrder, game); - } - } - - public void pickAttackerOrder(UUID playerId, Game game) { - Player player = game.getPlayer(playerId); - if (attackers.isEmpty() || player == null) { - return; - } - List attackerList = new ArrayList<>(attackers); - List newAttackerOrder = new ArrayList<>(); - while (true) { - if (attackerList.size() == 1) { - newAttackerOrder.add(attackerList.get(0)); - break; - } else { - List attackerPerms = new ArrayList<>(); - for (UUID attackerId : attackerList) { - attackerPerms.add(game.getPermanent(attackerId)); - } - UUID attackerId = player.chooseAttackerOrder(attackerPerms, game); - if (attackerId == null) { - break; - } - newAttackerOrder.add(attackerId); - attackerList.remove(attackerId); - } - } - if (attackerOrder.isEmpty() || newAttackerOrder.size() == attackerOrder.size()) { - attackerOrder.clear(); - attackerOrder.addAll(newAttackerOrder); - - if (!game.isSimulation() && attackerOrder.size() > 1) { - logDamageAssignmentOrder("Creatures blocked by ", blockers, attackerOrder, game); - } - } else { - game.informPlayers(player.getLogName() + " try to skip choose attacker order"); - } - } - private void logDamageAssignmentOrder(String prefix, List assignedFor, List assignedOrder, Game game) { StringBuilder sb = new StringBuilder(prefix); boolean first = true; @@ -746,12 +636,9 @@ public class CombatGroup implements Serializable, Copyable { formerAttackers.add(creatureId); attackers.remove(creatureId); result = true; - attackerOrder.remove(creatureId); } else if (blockers.contains(creatureId)) { blockers.remove(creatureId); result = true; - //20100423 - 509.2a - blockerOrder.remove(creatureId); } return result; } @@ -825,7 +712,6 @@ public class CombatGroup implements Serializable, Copyable { game.getCombat().removeBlocker(blockerId, game); } blockers.clear(); - blockerOrder.clear(); if (!game.isSimulation()) { game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded."); } @@ -844,7 +730,6 @@ public class CombatGroup implements Serializable, Copyable { game.getCombat().removeBlocker(blockerId, game); } blockers.clear(); - blockerOrder.clear(); if (!game.isSimulation()) { game.informPlayers(new StringBuilder(attacker.getLogName()) .append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy()) diff --git a/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java b/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java index 0e33c98afcb..063670ed7b4 100644 --- a/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java +++ b/Mage/src/main/java/mage/game/turn/DeclareBlockersStep.java @@ -1,12 +1,12 @@ package mage.game.turn; -import java.util.UUID; - import mage.constants.PhaseStep; import mage.game.Game; import mage.game.events.GameEvent.EventType; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -37,7 +37,6 @@ public class DeclareBlockersStep extends Step { game.getCombat().selectBlockers(game); if (!game.isPaused() && !game.executingRollback()) { game.getCombat().acceptBlockers(game); - game.getCombat().damageAssignmentOrder(game); } } @@ -46,7 +45,6 @@ public class DeclareBlockersStep extends Step { super.resumeBeginStep(game, activePlayerId); game.getCombat().resumeSelectBlockers(game); game.getCombat().acceptBlockers(game); - game.getCombat().damageAssignmentOrder(game); } @Override diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 5e8286c5b6e..c6c7d14039c 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -23,7 +23,6 @@ import mage.filter.FilterCard; import mage.filter.FilterMana; import mage.filter.FilterPermanent; import mage.game.*; -import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.events.GameEvent; import mage.game.match.Match; @@ -761,19 +760,6 @@ public interface Player extends MageItem, Copyable { void selectBlockers(Ability source, Game game, UUID defendingPlayerId); - UUID chooseAttackerOrder(List attacker, Game game); - - /** - * Choose the order in which blockers get damage assigned to - * - * @param blockers list of blockers where to choose the next one from - * @param combatGroup the concerning combat group - * @param blockerOrder the already set order of blockers - * @param game - * @return blocker next to add to the blocker order - */ - UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game); - int getAmount(int min, int max, String message, Game game); /** diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java index d1fa0f7d57b..da4d4167d2d 100644 --- a/Mage/src/main/java/mage/players/StubPlayer.java +++ b/Mage/src/main/java/mage/players/StubPlayer.java @@ -18,10 +18,8 @@ import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.filter.FilterMana; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.match.Match; -import mage.game.permanent.Permanent; import mage.game.tournament.Tournament; import mage.target.Target; import mage.target.TargetAmount; @@ -190,16 +188,6 @@ public class StubPlayer extends PlayerImpl { } - @Override - public UUID chooseAttackerOrder(List attacker, Game game) { - return null; - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - return null; - } - @Override public int getAmount(int min, int max, String message, Game game) { return 0;