mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
FDN New Combat Rules (#13279)
* Remove all combat ordering code * Use MultiAmount division for damage * Remove damage selection division skipping in tests * Fix Banding, Multi-block, add tests * Fix test * Fix random iteration order, fix new tests * Add more info to choose dialog, make MultiAmountType class instead of enum * Don't prompt for trample damage assignment if none possible * Mark "Assign default damage" on tests, minor other test improvements
This commit is contained in:
parent
f53e43fd46
commit
969ffa1c98
36 changed files with 323 additions and 503 deletions
|
|
@ -40,7 +40,7 @@ public class CombatGroupView implements Serializable {
|
||||||
attackers.put(id, new PermanentView(attacker, game.getCard(attacker.getId()),null, game));
|
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);
|
Permanent blocker = game.getPermanent(id);
|
||||||
if (blocker != null) {
|
if (blocker != null) {
|
||||||
blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game));
|
blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game));
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,8 @@ import mage.constants.MultiAmountType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.combat.CombatGroup;
|
|
||||||
import mage.game.draft.Draft;
|
import mage.game.draft.Draft;
|
||||||
import mage.game.match.Match;
|
import mage.game.match.Match;
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.game.tournament.Tournament;
|
import mage.game.tournament.Tournament;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
|
|
@ -287,24 +285,6 @@ public class ComputerPlayerControllableProxy extends ComputerPlayer7 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseAttackerOrder(java.util.List<Permanent> 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<Permanent> blockers, CombatGroup combatGroup, java.util.List<UUID> blockerOrder, Game game) {
|
|
||||||
if (isUnderMe(game)) {
|
|
||||||
return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
|
||||||
} else {
|
|
||||||
return getControllingPlayer(game).chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Game game) {
|
||||||
if (isUnderMe(game)) {
|
if (isUnderMe(game)) {
|
||||||
|
|
|
||||||
|
|
@ -2177,9 +2177,6 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
// TODO: add AI support with outcome and replace random with min/max
|
// TODO: add AI support with outcome and replace random with min/max
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Game game) {
|
||||||
log.debug("getAmount");
|
log.debug("getAmount");
|
||||||
if (message.startsWith("Assign damage to ")) {
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
if (min < max && min == 0) {
|
if (min < max && min == 0) {
|
||||||
return RandomUtil.nextInt(CardUtil.overflowInc(max, 1));
|
return RandomUtil.nextInt(CardUtil.overflowInc(max, 1));
|
||||||
}
|
}
|
||||||
|
|
@ -2192,7 +2189,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
log.debug("getMultiAmount");
|
log.debug("getMultiAmount");
|
||||||
|
|
||||||
int needCount = messages.size();
|
int needCount = messages.size();
|
||||||
List<Integer> defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax);
|
List<Integer> defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax);
|
||||||
if (needCount == 0) {
|
if (needCount == 0) {
|
||||||
return defaultList;
|
return defaultList;
|
||||||
}
|
}
|
||||||
|
|
@ -2210,18 +2207,6 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return MultiAmountType.prepareMaxValues(messages, totalMin, totalMax);
|
return MultiAmountType.prepareMaxValues(messages, totalMin, totalMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseAttackerOrder(List<Permanent> attackers, Game game) {
|
|
||||||
//TODO: improve this
|
|
||||||
return attackers.iterator().next().getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game) {
|
|
||||||
//TODO: improve this
|
|
||||||
return blockers.iterator().next().getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MageObject> getAvailableManaProducers(Game game) {
|
public List<MageObject> getAvailableManaProducers(Game game) {
|
||||||
return super.getAvailableManaProducers(game);
|
return super.getAvailableManaProducers(game);
|
||||||
|
|
|
||||||
|
|
@ -374,22 +374,6 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
return super.chooseMode(modes, source, game);
|
return super.chooseMode(modes, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseAttackerOrder(List<Permanent> attackers, Game game) {
|
|
||||||
if (this.isHuman()) {
|
|
||||||
return attackers.get(RandomUtil.nextInt(attackers.size())).getId();
|
|
||||||
}
|
|
||||||
return super.chooseAttackerOrder(attackers, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game) {
|
|
||||||
if (this.isHuman()) {
|
|
||||||
return blockers.get(RandomUtil.nextInt(blockers.size())).getId();
|
|
||||||
}
|
|
||||||
return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Game game) {
|
||||||
if (this.isHuman()) {
|
if (this.isHuman()) {
|
||||||
|
|
|
||||||
|
|
@ -2122,56 +2122,6 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseAttackerOrder(java.util.List<Permanent> 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<Permanent> blockers, CombatGroup combatGroup, java.util.List<UUID> 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) {
|
protected void selectCombatGroup(UUID defenderId, UUID blockerId, Game game) {
|
||||||
if (gameInCheckPlayableState(game)) {
|
if (gameInCheckPlayableState(game)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2260,7 +2210,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
Game game
|
Game game
|
||||||
) {
|
) {
|
||||||
int needCount = messages.size();
|
int needCount = messages.size();
|
||||||
List<Integer> defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax);
|
List<Integer> defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax);
|
||||||
if (needCount == 0 || (needCount == 1 && totalMin == totalMax)
|
if (needCount == 0 || (needCount == 1 && totalMin == totalMax)
|
||||||
|| messages.stream().map(m -> m.min == m.max).reduce(true, Boolean::logicalAnd)) {
|
|| messages.stream().map(m -> m.min == m.max).reduce(true, Boolean::logicalAnd)) {
|
||||||
// nothing to choose
|
// nothing to choose
|
||||||
|
|
|
||||||
|
|
@ -153,10 +153,6 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
|
||||||
game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
||||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, permanent.getId(), source, null));
|
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;
|
return true;
|
||||||
|
|
@ -164,15 +160,4 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
package mage.cards.b;
|
package mage.cards.b;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.AttacksTriggeredAbility;
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
|
@ -22,6 +21,8 @@ import mage.game.permanent.token.CatSoldierCreatureToken;
|
||||||
import mage.game.permanent.token.Token;
|
import mage.game.permanent.token.Token;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
|
|
@ -99,7 +100,6 @@ class BrimazKingOfOreskosEffect extends OneShotEffect {
|
||||||
combatGroup.addBlocker(tokenId, source.getControllerId(), game);
|
combatGroup.addBlocker(tokenId, source.getControllerId(), game);
|
||||||
game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game);
|
game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game);
|
||||||
}
|
}
|
||||||
combatGroup.pickBlockerOrder(attackingCreature.getControllerId(), game);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,21 +169,6 @@ class FalseOrdersUnblockEffect extends OneShotEffect {
|
||||||
game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
game.fireEvent(new BlockerDeclaredEvent(chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
||||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKS, permanent.getId(), source, null));
|
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;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ class FlashFoliageEffect extends OneShotEffect {
|
||||||
game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game);
|
game.getCombat().addBlockingGroup(tokenId, attackingCreature.getId(), controller.getId(), game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
combatGroup.pickBlockerOrder(attackingCreature.getControllerId(), game);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
|
|
||||||
package mage.cards.g;
|
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.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.condition.common.IsStepCondition;
|
import mage.abilities.condition.common.IsStepCondition;
|
||||||
|
|
@ -13,18 +9,18 @@ import mage.abilities.decorator.ConditionalActivatedAbility;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.*;
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.SuperType;
|
|
||||||
import mage.constants.PhaseStep;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.combat.CombatGroup;
|
import mage.game.combat.CombatGroup;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetAttackingCreature;
|
import mage.target.common.TargetAttackingCreature;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author L_J
|
* @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
|
// 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())) {
|
if (!(chosenGroup1.getBlockers().isEmpty())) {
|
||||||
chosenGroup1.setBlocked(true, game);
|
chosenGroup1.setBlocked(true, game);
|
||||||
chosenGroup1.pickBlockerOrder(attacker1.getControllerId(), game);
|
|
||||||
}
|
}
|
||||||
if (!(chosenGroup2.getBlockers().isEmpty())) {
|
if (!(chosenGroup2.getBlockers().isEmpty())) {
|
||||||
chosenGroup2.setBlocked(true, game);
|
chosenGroup2.setBlocked(true, game);
|
||||||
chosenGroup2.pickBlockerOrder(attacker2.getControllerId(), game);
|
|
||||||
}
|
}
|
||||||
return true;
|
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.
|
// 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);
|
game.getCombat().addBlockingGroup(blocker.getId(), attacker, controller.getId(), game);
|
||||||
}
|
}
|
||||||
blockGroup.pickAttackerOrder(blocker.getControllerId(), game);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
|
|
||||||
package mage.cards.m;
|
package mage.cards.m;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
|
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
|
||||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.ExileTargetEffect;
|
|
||||||
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||||
|
import mage.abilities.effects.common.ExileTargetEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
|
@ -20,6 +19,8 @@ import mage.players.Player;
|
||||||
import mage.target.targetpointer.FixedTarget;
|
import mage.target.targetpointer.FixedTarget;
|
||||||
import mage.target.targetpointer.FixedTargets;
|
import mage.target.targetpointer.FixedTargets;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
|
|
@ -75,20 +76,15 @@ class MirrorMatchEffect extends OneShotEffect {
|
||||||
effect.setTargetPointer(new FixedTarget(attacker, game));
|
effect.setTargetPointer(new FixedTarget(attacker, game));
|
||||||
effect.apply(game, source);
|
effect.apply(game, source);
|
||||||
CombatGroup group = game.getCombat().findGroup(attacker.getId());
|
CombatGroup group = game.getCombat().findGroup(attacker.getId());
|
||||||
boolean isCreature = false;
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
for (Permanent addedToken : effect.getAddedPermanents()) {
|
for (Permanent addedToken : effect.getAddedPermanents()) {
|
||||||
if (addedToken.isCreature(game)) {
|
if (addedToken.isCreature(game)) {
|
||||||
group.addBlockerToGroup(addedToken.getId(), attackerId, game);
|
group.addBlockerToGroup(addedToken.getId(), attackerId, game);
|
||||||
isCreature = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExileTargetEffect exileEffect = new ExileTargetEffect("Exile those tokens at end of combat");
|
ExileTargetEffect exileEffect = new ExileTargetEffect("Exile those tokens at end of combat");
|
||||||
exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game));
|
exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game));
|
||||||
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source);
|
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source);
|
||||||
if (isCreature) {
|
|
||||||
group.pickBlockerOrder(attacker.getControllerId(), game);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,14 +176,8 @@ class SorrowsPathSwitchBlockersEffect extends OneShotEffect {
|
||||||
group.addBlockerToGroup(blocker.getId(), blocker.getControllerId(), game);
|
group.addBlockerToGroup(blocker.getId(), blocker.getControllerId(), game);
|
||||||
game.getCombat().addBlockingGroup(blocker.getId(), attacker.getId(), blocker.getControllerId(), game);
|
game.getCombat().addBlockingGroup(blocker.getId(), attacker.getId(), blocker.getControllerId(), game);
|
||||||
game.fireEvent(new BlockerDeclaredEvent(attacker.getId(), blocker.getId(), blocker.getControllerId()));
|
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));
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
public class AfflictTest extends CardTestPlayerBase {
|
public class AfflictTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
private final String khenra = "Khenra Eternal";
|
private final String khenra = "Khenra Eternal";
|
||||||
|
|
@ -35,7 +37,7 @@ public class AfflictTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, khenra);
|
attack(1, playerA, khenra);
|
||||||
block(1, playerB, elves + ":0", khenra);
|
block(1, playerB, elves + ":0", khenra);
|
||||||
block(1, playerB, elves + ":1", khenra);
|
block(1, playerB, elves + ":1", khenra);
|
||||||
setChoice(playerA, "X=1"); // assign damage
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author noxx
|
* @author noxx
|
||||||
*/
|
*/
|
||||||
|
|
@ -21,6 +23,7 @@ public class BushidoTest extends CardTestPlayerBase {
|
||||||
attack(2, playerB, "Isao, Enlightened Bushi");
|
attack(2, playerB, "Isao, Enlightened Bushi");
|
||||||
block(2, playerA, "Elite Vanguard", "Isao, Enlightened Bushi");
|
block(2, playerA, "Elite Vanguard", "Isao, Enlightened Bushi");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.END_COMBAT);
|
setStopAt(2, PhaseStep.END_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -39,6 +42,7 @@ public class BushidoTest extends CardTestPlayerBase {
|
||||||
attack(2, playerB, "Elite Vanguard");
|
attack(2, playerB, "Elite Vanguard");
|
||||||
block(2, playerA, "Isao, Enlightened Bushi", "Elite Vanguard");
|
block(2, playerA, "Isao, Enlightened Bushi", "Elite Vanguard");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.END_COMBAT);
|
setStopAt(2, PhaseStep.END_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -58,9 +62,9 @@ public class BushidoTest extends CardTestPlayerBase {
|
||||||
attack(2, playerB, "Isao, Enlightened Bushi");
|
attack(2, playerB, "Isao, Enlightened Bushi");
|
||||||
block(2, playerA, "Llanowar Elves", "Isao, Enlightened Bushi");
|
block(2, playerA, "Llanowar Elves", "Isao, Enlightened Bushi");
|
||||||
block(2, playerA, "Elvish Mystic", "Isao, Enlightened Bushi");
|
block(2, playerA, "Elvish Mystic", "Isao, Enlightened Bushi");
|
||||||
setChoice(playerB, "X=1"); // assign damage
|
setChoice(playerB, CHOICE_SKIP); // Assign default damage
|
||||||
setChoice(playerB, "X=1"); // assign damage
|
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.END_COMBAT);
|
setStopAt(2, PhaseStep.END_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* @author TheElk801
|
||||||
*/
|
*/
|
||||||
|
|
@ -158,15 +160,7 @@ public class ExcessDamageTest extends CardTestPlayerBase {
|
||||||
block(2, playerA, bear, myrSuperion);
|
block(2, playerA, bear, myrSuperion);
|
||||||
block(2, playerA, envoy, myrSuperion);
|
block(2, playerA, envoy, myrSuperion);
|
||||||
block(2, playerA, bondedConstruct, myrSuperion);
|
block(2, playerA, bondedConstruct, myrSuperion);
|
||||||
|
setChoice(playerB, CHOICE_SKIP); // Assign default damage
|
||||||
//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");
|
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.END_TURN);
|
setStopAt(2, PhaseStep.END_TURN);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author JayDi85
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
|
|
@ -247,9 +249,10 @@ public class TheRingEmblemTest extends CardTestPlayerBase {
|
||||||
attack(3, playerA, "Ashiok's Skulker");
|
attack(3, playerA, "Ashiok's Skulker");
|
||||||
block(3, playerB, "Alabaster Kirin", "Ashiok's Skulker");
|
block(3, playerB, "Alabaster Kirin", "Ashiok's Skulker");
|
||||||
block(3, playerB, "Alaborn Trooper", "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, "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, 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, "Academy Manufactor", 0);
|
||||||
checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerB, "Alabaster Kirin", 0);
|
checkPermanentCount("after attack on 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, playerB, "Alabaster Kirin", 0);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
public class PreventDamageRemoveCountersTest extends CardTestPlayerBase {
|
public class PreventDamageRemoveCountersTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -67,7 +69,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase {
|
||||||
attack(3, playerA, "Magma Pummeler", playerB);
|
attack(3, playerA, "Magma Pummeler", playerB);
|
||||||
block(3, playerB, "Memnite", "Magma Pummeler");
|
block(3, playerB, "Memnite", "Magma Pummeler");
|
||||||
block(3, playerB, "Goblin Piker", "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
|
addTarget(playerA, playerB); // For the one trigger
|
||||||
|
|
||||||
setStopAt(3, PhaseStep.END_TURN);
|
setStopAt(3, PhaseStep.END_TURN);
|
||||||
|
|
@ -117,7 +119,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase {
|
||||||
attack(3, playerA, "Magma Pummeler", playerB);
|
attack(3, playerA, "Magma Pummeler", playerB);
|
||||||
block(3, playerB, "Centaur Courser", "Magma Pummeler");
|
block(3, playerB, "Centaur Courser", "Magma Pummeler");
|
||||||
block(3, playerB, "Air Elemental", "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
|
addTarget(playerA, playerB); // For the one trigger
|
||||||
|
|
||||||
setStopAt(3, PhaseStep.END_TURN);
|
setStopAt(3, PhaseStep.END_TURN);
|
||||||
|
|
@ -148,7 +150,7 @@ public class PreventDamageRemoveCountersTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, "Undergrowth Champion", playerB);
|
attack(1, playerA, "Undergrowth Champion", playerB);
|
||||||
block(1, playerB, "Grizzly Bears", "Undergrowth Champion");
|
block(1, playerB, "Grizzly Bears", "Undergrowth Champion");
|
||||||
block(1, playerB, "Elite Vanguard", "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);
|
setStopAt(1, PhaseStep.END_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import mage.counters.CounterType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
public class RiteOfPassageTest extends CardTestPlayerBase {
|
public class RiteOfPassageTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,12 +40,7 @@ public class RiteOfPassageTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, "Watchwolf", playerB);
|
attack(1, playerA, "Watchwolf", playerB);
|
||||||
block(1, playerB, "Memnite", "Watchwolf");
|
block(1, playerB, "Memnite", "Watchwolf");
|
||||||
block(1, playerB, "Agent of Stromgald", "Watchwolf");
|
block(1, playerB, "Agent of Stromgald", "Watchwolf");
|
||||||
|
setChoice(playerA, CHOICE_SKIP);
|
||||||
// Assign this much damage to Memnite
|
|
||||||
setChoice(playerA, "X=1");
|
|
||||||
|
|
||||||
// Assign this much damage to Agent of Stromgald
|
|
||||||
setChoice(playerA, "X=1");
|
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Susucr
|
* @author Susucr
|
||||||
*/
|
*/
|
||||||
|
|
@ -33,7 +35,9 @@ public class BindingAgonyTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, "Grizzly Bears");
|
attack(1, playerA, "Grizzly Bears");
|
||||||
block(1, playerB, "Centaur Courser", "Grizzly Bears");
|
block(1, playerB, "Centaur Courser", "Grizzly Bears");
|
||||||
block(1, playerB, "Memnite", "Grizzly Bears");
|
block(1, playerB, "Memnite", "Grizzly Bears");
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
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, agony, "Grizzly Bears", true);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import mage.counters.CounterType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Susucr
|
* @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, swarm, true);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", swarm);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", swarm);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -55,7 +58,9 @@ public class BloatflySwarmTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, swarm);
|
attack(1, playerA, swarm);
|
||||||
block(1, playerB, "Brimstone Dragon", swarm);
|
block(1, playerB, "Brimstone Dragon", swarm);
|
||||||
block(1, playerB, "Giant Spider", swarm);
|
block(1, playerB, "Giant Spider", swarm);
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -79,8 +84,9 @@ public class BloatflySwarmTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, swarm);
|
attack(1, playerA, swarm);
|
||||||
block(1, playerB, "Wind Drake", swarm);
|
block(1, playerB, "Wind Drake", swarm);
|
||||||
block(1, playerB, "Giant Spider", 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);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public class WallOfEssenceTest extends CardTestPlayerBase {
|
||||||
block(1, playerB, "Memnite", "Grizzly Bears");
|
block(1, playerB, "Memnite", "Grizzly Bears");
|
||||||
block(1, playerB, wall, "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);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import mage.counters.CounterType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Susucr
|
* @author Susucr
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,9 +33,7 @@ public class PhantomWurmTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, wurm, playerB);
|
attack(1, playerA, wurm, playerB);
|
||||||
block(1, playerB, "Memnite", wurm);
|
block(1, playerB, "Memnite", wurm);
|
||||||
block(1, playerB, "Eager Cadet", wurm);
|
block(1, playerB, "Eager Cadet", wurm);
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
setChoice(playerA, "X=1"); // damage assignment
|
|
||||||
setChoice(playerA, "X=3"); // damage assignment
|
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
@ -115,9 +115,7 @@ public class PhantomWurmTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, wurm, playerB);
|
attack(1, playerA, wurm, playerB);
|
||||||
block(1, playerB, "Memnite", wurm);
|
block(1, playerB, "Memnite", wurm);
|
||||||
block(1, playerB, "Goblin Striker", wurm);
|
block(1, playerB, "Goblin Striker", wurm);
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
setChoice(playerA, "X=1"); // damage assignment
|
|
||||||
setChoice(playerA, "X=3"); // damage assignment
|
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
@ -139,9 +137,7 @@ public class PhantomWurmTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, wurm, playerB);
|
attack(1, playerA, wurm, playerB);
|
||||||
block(1, playerB, "Boros Recruit", wurm);
|
block(1, playerB, "Boros Recruit", wurm);
|
||||||
block(1, playerB, "Goblin Striker", wurm);
|
block(1, playerB, "Goblin Striker", wurm);
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
setChoice(playerA, "X=1"); // damage assignment
|
|
||||||
setChoice(playerA, "X=3"); // damage assignment
|
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
|
|
||||||
private void assertDefaultValuesUnconstrained(String need, int count, int min, int max) {
|
private void assertDefaultValuesUnconstrained(String need, int count, int min, int max) {
|
||||||
List<MultiAmountMessage> constraints = getUnconstrainedConstraints(count);
|
List<MultiAmountMessage> constraints = getUnconstrainedConstraints(count);
|
||||||
List<Integer> defaultValues = MultiAmountType.prepareDefaltValues(constraints, min, max);
|
List<Integer> defaultValues = MultiAmountType.prepareDefaultValues(constraints, min, max);
|
||||||
String current = defaultValues
|
String current = defaultValues
|
||||||
.stream()
|
.stream()
|
||||||
.map(String::valueOf)
|
.map(String::valueOf)
|
||||||
|
|
@ -122,7 +122,7 @@ public class TargetMultiAmountTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
getUnconstrainedConstraints(4));
|
getUnconstrainedConstraints(4));
|
||||||
|
|
||||||
// good values are checking in test_DefaultValues, it's an additional
|
// good values are checking in test_DefaultValues, it's an additional
|
||||||
List<Integer> list = MultiAmountType.prepareDefaltValues(constraints.get(3), 0, 0);
|
List<Integer> list = MultiAmountType.prepareDefaultValues(constraints.get(3), 0, 0);
|
||||||
|
|
||||||
// count (0, 0, 0)
|
// count (0, 0, 0)
|
||||||
Assert.assertFalse("count", MultiAmountType.isGoodValues(list, constraints.get(0), 0, 0));
|
Assert.assertFalse("count", MultiAmountType.isGoodValues(list, constraints.get(0), 0, 0));
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestCommander4Players;
|
import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author jimga150
|
* @author jimga150
|
||||||
|
|
@ -32,12 +34,7 @@ public class DonnaNobleTests extends CardTestCommander4Players {
|
||||||
attack(5, playerA, "Impervious Greatwurm", playerB);
|
attack(5, playerA, "Impervious Greatwurm", playerB);
|
||||||
block(5, playerB, "Memnite", "Impervious Greatwurm");
|
block(5, playerB, "Memnite", "Impervious Greatwurm");
|
||||||
block(5, playerB, "Expedition Envoy", "Impervious Greatwurm");
|
block(5, playerB, "Expedition Envoy", "Impervious Greatwurm");
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
//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");
|
|
||||||
|
|
||||||
//Target this player with Donna Noble
|
//Target this player with Donna Noble
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestCommander4Players;
|
import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author jimga150
|
* @author jimga150
|
||||||
|
|
@ -25,9 +27,7 @@ public class DamagedBatchTests extends CardTestCommander4Players {
|
||||||
attack(1, playerA, "Donna Noble", playerB);
|
attack(1, playerA, "Donna Noble", playerB);
|
||||||
block(1, playerB, "Memnite", "Donna Noble");
|
block(1, playerB, "Memnite", "Donna Noble");
|
||||||
block(1, playerB, "Expedition Envoy", "Donna Noble");
|
block(1, playerB, "Expedition Envoy", "Donna Noble");
|
||||||
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
//Assign this much damage to the first blocking creature
|
|
||||||
setChoice(playerA, "X=1");
|
|
||||||
|
|
||||||
//Target this player with Donna Noble
|
//Target this player with Donna Noble
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test restrictions for choosing attackers and blockers.
|
* 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");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||||
|
|
||||||
attack(1, playerA, "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");
|
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||||
checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite");
|
checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite");
|
||||||
|
setChoice(playerA, CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
@ -783,7 +784,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
// ai must choose all blockers anyway
|
// ai must choose all blockers anyway
|
||||||
attack(1, playerA, "Sonorous Howlbonder");
|
attack(1, playerA, "Sonorous Howlbonder");
|
||||||
aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB);
|
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");
|
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||||
checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite");
|
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");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||||
|
|
||||||
attack(1, playerA, "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");
|
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||||
checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite");
|
checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite");
|
||||||
|
setChoice(playerA, CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
@ -845,7 +846,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
// ai must choose all blockers
|
// ai must choose all blockers
|
||||||
attack(1, playerA, "Sonorous Howlbonder");
|
attack(1, playerA, "Sonorous Howlbonder");
|
||||||
aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB);
|
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");
|
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||||
checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite");
|
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");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||||
|
|
||||||
attack(1, playerA, "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");
|
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||||
checkBlockers("one possible blocker", 1, playerB, "Memnite");
|
checkBlockers("one possible blocker", 1, playerB, "Memnite");
|
||||||
|
|
||||||
|
|
@ -998,8 +998,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
addTarget(playerA, "Alley Strangler"); // boost target
|
addTarget(playerA, "Alley Strangler"); // boost target
|
||||||
setChoice(playerA, true); // boost target
|
setChoice(playerA, true); // boost target
|
||||||
attack(1, playerA, "Alley Strangler");
|
attack(1, playerA, "Alley Strangler");
|
||||||
setChoiceAmount(playerA, 1); // assign damage to 1 of 2 blocking memnites
|
setChoiceAmount(playerA, 1, 1); // assign damage to blocking memnites
|
||||||
setChoiceAmount(playerA, 1); // assign damage to 2 of 2 blocking memnites
|
|
||||||
aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB);
|
aiPlayStep(1, PhaseStep.DECLARE_BLOCKERS, playerB);
|
||||||
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
||||||
checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite");
|
checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite");
|
||||||
|
|
|
||||||
|
|
@ -197,4 +197,35 @@ public class DamageDistributionTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
assertLife(playerB, 20 - 5);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import static org.mage.test.player.TestPlayer.CHOICE_SKIP;
|
||||||
|
|
||||||
public class LifelinkInCombatTest extends CardTestPlayerBase {
|
public class LifelinkInCombatTest extends CardTestPlayerBase {
|
||||||
@Test
|
@Test
|
||||||
public void testOneBlockerTrample() {
|
public void testOneBlockerTrample() {
|
||||||
|
|
@ -43,8 +45,7 @@ public class LifelinkInCombatTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, "Brion Stoutarm");
|
attack(1, playerA, "Brion Stoutarm");
|
||||||
block(1, playerB, "Boros Recruit", "Brion Stoutarm");
|
block(1, playerB, "Boros Recruit", "Brion Stoutarm");
|
||||||
block(1, playerB, "Suntail Hawk", "Brion Stoutarm");
|
block(1, playerB, "Suntail Hawk", "Brion Stoutarm");
|
||||||
setChoice(playerA, "X=1"); // Damage assignment
|
setChoice(playerA, CHOICE_SKIP); // Assign default damage
|
||||||
setChoice(playerA, "X=1"); // Damage assignment
|
|
||||||
addTarget(playerA, "Brion Stoutarm");
|
addTarget(playerA, "Brion Stoutarm");
|
||||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2975,11 +2975,12 @@ public class TestPlayer implements Player {
|
||||||
assertAliasSupportInChoices(false);
|
assertAliasSupportInChoices(false);
|
||||||
|
|
||||||
int needCount = messages.size();
|
int needCount = messages.size();
|
||||||
List<Integer> defaultList = MultiAmountType.prepareDefaltValues(messages, totalMin, totalMax);
|
List<Integer> defaultList = MultiAmountType.prepareDefaultValues(messages, totalMin, totalMax);
|
||||||
if (needCount == 0) {
|
if (needCount == 0) {
|
||||||
return defaultList;
|
return defaultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<Integer> answer = new ArrayList<>(defaultList);
|
List<Integer> answer = new ArrayList<>(defaultList);
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
// must fill all possible choices or skip it
|
// must fill all possible choices or skip it
|
||||||
|
|
@ -4477,19 +4478,6 @@ public class TestPlayer implements Player {
|
||||||
return computerPlayer.playMana(ability, unpaid, promptText, game);
|
return computerPlayer.playMana(ability, unpaid, promptText, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseAttackerOrder(List<Permanent> attacker, Game game
|
|
||||||
) {
|
|
||||||
return computerPlayer.chooseAttackerOrder(attacker, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
|
|
||||||
List<UUID> blockerOrder, Game game
|
|
||||||
) {
|
|
||||||
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sideboard(Match match, Deck deck
|
public void sideboard(Match match, Deck deck
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -8,23 +8,24 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public enum MultiAmountType {
|
public class MultiAmountType {
|
||||||
|
|
||||||
MANA("Add mana", "Distribute mana among colors"),
|
public static final MultiAmountType MANA = new MultiAmountType("Add mana", "Distribute mana among colors");
|
||||||
DAMAGE("Assign damage", "Assign damage among targets"),
|
public static final MultiAmountType DAMAGE = new MultiAmountType("Assign damage", "Assign damage among targets");
|
||||||
P1P1("Add +1/+1 counters", "Distribute +1/+1 counters among creatures"),
|
|
||||||
COUNTERS("Choose counters", "Move counters"),
|
public static final MultiAmountType P1P1 = new MultiAmountType("Add +1/+1 counters", "Distribute +1/+1 counters among creatures");
|
||||||
CHEAT_LANDS("Choose lands", "Add lands to your battlefield", true);
|
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 title;
|
||||||
private final String header;
|
private final String header;
|
||||||
private final boolean canCancel; // choice dialog will return null instead default values
|
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);
|
this(title, header, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiAmountType(String title, String header, boolean canCancel) {
|
public MultiAmountType(String title, String header, boolean canCancel) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.canCancel = canCancel;
|
this.canCancel = canCancel;
|
||||||
|
|
@ -42,7 +43,7 @@ public enum MultiAmountType {
|
||||||
return canCancel;
|
return canCancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> prepareDefaltValues(List<MultiAmountMessage> constraints, int min, int max) {
|
public static List<Integer> prepareDefaultValues(List<MultiAmountMessage> constraints, int min, int max) {
|
||||||
// default values must be assigned from first to last by minimum values
|
// default values must be assigned from first to last by minimum values
|
||||||
List<Integer> res = constraints.stream().map(m -> m.defaultValue > Integer.MIN_VALUE ? m.defaultValue : Math.min(0, max))
|
List<Integer> res = constraints.stream().map(m -> m.defaultValue > Integer.MIN_VALUE ? m.defaultValue : Math.min(0, max))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
@ -50,7 +51,7 @@ public enum MultiAmountType {
|
||||||
return res;
|
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.
|
// 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) {
|
if (min > 0 && total < min) {
|
||||||
|
|
@ -174,7 +175,7 @@ public enum MultiAmountType {
|
||||||
// data check
|
// data check
|
||||||
if (returnDefaultOnError && !isGoodValues(res, constraints, min, max)) {
|
if (returnDefaultOnError && !isGoodValues(res, constraints, min, max)) {
|
||||||
// on broken data - return default
|
// on broken data - return default
|
||||||
return prepareDefaltValues(constraints, min, max);
|
return prepareDefaultValues(constraints, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
|
|
||||||
protected List<CombatGroup> groups = new ArrayList<>();
|
protected List<CombatGroup> groups = new ArrayList<>();
|
||||||
protected List<CombatGroup> formerGroups = new ArrayList<>();
|
protected List<CombatGroup> formerGroups = new ArrayList<>();
|
||||||
protected Map<UUID, CombatGroup> blockingGroups = new HashMap<>();
|
protected Map<UUID, CombatGroup> blockingGroups = new LinkedHashMap<>();
|
||||||
// all possible defenders (players, planeswalkers or battle)
|
// all possible defenders (players, planeswalkers or battle)
|
||||||
protected Set<UUID> defenders = new HashSet<>();
|
protected Set<UUID> defenders = new HashSet<>();
|
||||||
// how many creatures attack defending player
|
// how many creatures attack defending player
|
||||||
|
|
@ -200,7 +200,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(attackingPlayerId).append(defenders);
|
sb.append(attackingPlayerId).append(defenders);
|
||||||
for (CombatGroup group : groups) {
|
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();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -785,7 +785,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
if (attackerExists) {
|
if (attackerExists) {
|
||||||
if (!group.getBlockers().isEmpty()) {
|
if (!group.getBlockers().isEmpty()) {
|
||||||
sb.append("blocked by ");
|
sb.append("blocked by ");
|
||||||
for (UUID blockingCreatureId : group.getBlockerOrder()) {
|
for (UUID blockingCreatureId : group.getBlockers()) {
|
||||||
Permanent blockingCreature = game.getPermanent(blockingCreatureId);
|
Permanent blockingCreature = game.getPermanent(blockingCreatureId);
|
||||||
if (blockingCreature != null) {
|
if (blockingCreature != null) {
|
||||||
sb.append(blockingCreature.getLogName()).append(" (");
|
sb.append(blockingCreature.getLogName()).append(" (");
|
||||||
|
|
@ -1799,24 +1799,11 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
return playerDefenders;
|
return playerDefenders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void damageAssignmentOrder(Game game) {
|
|
||||||
for (CombatGroup group : groups) {
|
|
||||||
group.pickBlockerOrder(attackingPlayerId, game);
|
|
||||||
}
|
|
||||||
for (Map.Entry<UUID, CombatGroup> blockingGroup : blockingGroups.entrySet()) {
|
|
||||||
Permanent blocker = game.getPermanent(blockingGroup.getKey());
|
|
||||||
if (blocker != null) {
|
|
||||||
blockingGroup.getValue().pickAttackerOrder(blocker.getControllerId(), game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public void removeAttacker(UUID attackerId, Game game) {
|
public void removeAttacker(UUID attackerId, Game game) {
|
||||||
for (CombatGroup group : groups) {
|
for (CombatGroup group : groups) {
|
||||||
if (group.attackers.contains(attackerId)) {
|
if (group.attackers.contains(attackerId)) {
|
||||||
group.attackers.remove(attackerId);
|
group.attackers.remove(attackerId);
|
||||||
group.attackerOrder.remove(attackerId);
|
|
||||||
for (Set<UUID> attackingCreatures : numberCreaturesDefenderAttackedBy.values()) {
|
for (Set<UUID> attackingCreatures : numberCreaturesDefenderAttackedBy.values()) {
|
||||||
attackingCreatures.remove(attackerId);
|
attackingCreatures.remove(attackerId);
|
||||||
}
|
}
|
||||||
|
|
@ -1869,7 +1856,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
}
|
}
|
||||||
for (CombatGroup group : groupsToCheck) {
|
for (CombatGroup group : groupsToCheck) {
|
||||||
group.blockers.remove(blockerId);
|
group.blockers.remove(blockerId);
|
||||||
group.blockerOrder.remove(blockerId);
|
|
||||||
if (group.blockers.isEmpty()) {
|
if (group.blockers.isEmpty()) {
|
||||||
group.blocked = false;
|
group.blocked = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1885,11 +1871,9 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
if (blockGroup.blockers.contains(blockerId)) {
|
if (blockGroup.blockers.contains(blockerId)) {
|
||||||
for (UUID attackerId : group.getAttackers()) {
|
for (UUID attackerId : group.getAttackers()) {
|
||||||
blockGroup.attackers.remove(attackerId);
|
blockGroup.attackers.remove(attackerId);
|
||||||
blockGroup.attackerOrder.remove(attackerId);
|
|
||||||
}
|
}
|
||||||
if (creature.getBlocking() == 0) {
|
if (creature.getBlocking() == 0) {
|
||||||
blockGroup.blockers.remove(blockerId);
|
blockGroup.blockers.remove(blockerId);
|
||||||
blockGroup.attackerOrder.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blockGroup.blockers.isEmpty()) {
|
if (blockGroup.blockers.isEmpty()) {
|
||||||
|
|
@ -1914,7 +1898,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
for (CombatGroup group : groups) {
|
for (CombatGroup group : groups) {
|
||||||
if (group.blockers.contains(blockerId)) {
|
if (group.blockers.contains(blockerId)) {
|
||||||
group.blockers.remove(blockerId);
|
group.blockers.remove(blockerId);
|
||||||
group.blockerOrder.remove(blockerId);
|
|
||||||
if (group.blockers.isEmpty()) {
|
if (group.blockers.isEmpty()) {
|
||||||
group.blocked = false;
|
group.blocked = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1924,7 +1907,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
for (CombatGroup group : getBlockingGroups()) {
|
for (CombatGroup group : getBlockingGroups()) {
|
||||||
if (group.blockers.contains(blockerId)) {
|
if (group.blockers.contains(blockerId)) {
|
||||||
group.blockers.remove(blockerId);
|
group.blockers.remove(blockerId);
|
||||||
group.attackerOrder.clear();
|
|
||||||
}
|
}
|
||||||
if (group.blockers.isEmpty()) {
|
if (group.blockers.isEmpty()) {
|
||||||
canRemove = true;
|
canRemove = true;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import mage.abilities.common.ControllerDivideCombatDamageAbility;
|
||||||
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
|
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
|
||||||
import mage.abilities.keyword.*;
|
import mage.abilities.keyword.*;
|
||||||
import mage.constants.AsThoughEffectType;
|
import mage.constants.AsThoughEffectType;
|
||||||
|
import mage.constants.MultiAmountType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
@ -15,6 +16,7 @@ import mage.game.events.GameEvent;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.Copyable;
|
import mage.util.Copyable;
|
||||||
|
import mage.util.MultiAmountMessage;
|
||||||
import mage.watchers.common.FirstStrikeWatcher;
|
import mage.watchers.common.FirstStrikeWatcher;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
@ -29,8 +31,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
protected List<UUID> attackers = new ArrayList<>();
|
protected List<UUID> attackers = new ArrayList<>();
|
||||||
protected List<UUID> formerAttackers = new ArrayList<>();
|
protected List<UUID> formerAttackers = new ArrayList<>();
|
||||||
protected List<UUID> blockers = new ArrayList<>();
|
protected List<UUID> blockers = new ArrayList<>();
|
||||||
protected List<UUID> blockerOrder = new ArrayList<>();
|
|
||||||
protected List<UUID> attackerOrder = new ArrayList<>();
|
|
||||||
protected Map<UUID, UUID> players = new HashMap<>();
|
protected Map<UUID, UUID> players = new HashMap<>();
|
||||||
protected boolean blocked;
|
protected boolean blocked;
|
||||||
protected UUID defenderId; // planeswalker, player, or battle id, can be null after remove from combat (e.g. due damage)
|
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<CombatGroup> {
|
||||||
this.attackers.addAll(group.attackers);
|
this.attackers.addAll(group.attackers);
|
||||||
this.formerAttackers.addAll(group.formerAttackers);
|
this.formerAttackers.addAll(group.formerAttackers);
|
||||||
this.blockers.addAll(group.blockers);
|
this.blockers.addAll(group.blockers);
|
||||||
this.blockerOrder.addAll(group.blockerOrder);
|
|
||||||
this.attackerOrder.addAll(group.attackerOrder);
|
|
||||||
this.players.putAll(group.players);
|
this.players.putAll(group.players);
|
||||||
this.blocked = group.blocked;
|
this.blocked = group.blocked;
|
||||||
this.defenderId = group.defenderId;
|
this.defenderId = group.defenderId;
|
||||||
|
|
@ -91,10 +89,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
return blockers;
|
return blockers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UUID> getBlockerOrder() {
|
|
||||||
return blockerOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hasFirstOrDoubleStrike(Permanent perm) {
|
private static boolean hasFirstOrDoubleStrike(Permanent perm) {
|
||||||
return hasFirstStrike(perm) || hasDoubleStrike(perm);
|
return hasFirstStrike(perm) || hasDoubleStrike(perm);
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +169,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
if (attacker != null && !assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(attacker, attacker.getControllerId(), first, game, true)) {
|
if (attacker != null && !assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(attacker, attacker.getControllerId(), first, game, true)) {
|
||||||
if (blockers.isEmpty()) {
|
if (blockers.isEmpty()) {
|
||||||
unblockedDamage(first, game);
|
unblockedDamage(first, game);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId());
|
Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId());
|
||||||
if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) &&
|
if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) &&
|
||||||
|
|
@ -186,11 +179,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
blocked = false;
|
blocked = false;
|
||||||
unblockedDamage(first, game);
|
unblockedDamage(first, game);
|
||||||
}
|
}
|
||||||
if (blockers.size() == 1) {
|
blockerDamage(player, first, game);
|
||||||
singleBlockerDamage(player, first, game);
|
|
||||||
} else {
|
|
||||||
multiBlockerDamage(player, first, game);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -206,9 +195,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attackers.size() != 1) {
|
if (attackers.size() != 1) {
|
||||||
multiAttackerDamage(first, game);
|
attackerDamage(first, game);
|
||||||
// } else {
|
|
||||||
// singleAttackerDamage(first, game);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -269,51 +256,16 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void blockerDamage(Player player, boolean first, Game game) {
|
||||||
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) {
|
|
||||||
Permanent attacker = game.getPermanent(attackers.get(0));
|
Permanent attacker = game.getPermanent(attackers.get(0));
|
||||||
if (attacker == null) {
|
if (attacker == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean oldRuleDamage = (Objects.equals(player.getId(), defendingPlayerId));
|
|
||||||
int damage = getDamageValueFromPermanent(attacker, game);
|
int damage = getDamageValueFromPermanent(attacker, game);
|
||||||
if (dealsDamageThisStep(attacker, first, game)) {
|
if (dealsDamageThisStep(attacker, first, game)) {
|
||||||
// must be set before attacker damage marking because of effects like Test of Faith
|
// must be set before attacker damage marking because of effects like Test of Faith
|
||||||
Map<UUID, Integer> blockerPower = new HashMap<>();
|
Map<UUID, Integer> blockerPower = new HashMap<>();
|
||||||
for (UUID blockerId : blockerOrder) {
|
for (UUID blockerId : blockers) {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (dealsDamageThisStep(blocker, first, game)) {
|
if (dealsDamageThisStep(blocker, first, game)) {
|
||||||
if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately
|
if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately
|
||||||
|
|
@ -322,42 +274,62 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<UUID, Integer> assigned = new HashMap<>();
|
Map<UUID, Integer> assigned = new HashMap<>();
|
||||||
|
List<MultiAmountMessage> damageDivision = new ArrayList<>();
|
||||||
|
List<UUID> blockersCopy = new ArrayList<>(blockers);
|
||||||
if (blocked) {
|
if (blocked) {
|
||||||
boolean excessDamageToDefender = true;
|
int remainingDamage = damage;
|
||||||
for (UUID blockerId : new ArrayList<>(blockerOrder)) { // prevent ConcurrentModificationException
|
for (UUID blockerId : blockers) {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (blocker != null) {
|
if (blocker != null) {
|
||||||
int lethalDamage = getLethalDamage(blocker, attacker, game);
|
int defaultDamage = Math.min(remainingDamage, blocker.getLethalDamage(attacker.getId(), game));
|
||||||
if (lethalDamage >= damage) {
|
remainingDamage -= defaultDamage;
|
||||||
if (!oldRuleDamage) {
|
String message = String.format("%s, P/T: %d/%d",
|
||||||
assigned.put(blockerId, damage);
|
blocker.getLogName(),
|
||||||
damage = 0;
|
blocker.getPower().getValue(),
|
||||||
break;
|
blocker.getToughness().getValue());
|
||||||
} else if (damage == 0) {
|
damageDivision.add(new MultiAmountMessage(message, 0, damage, defaultDamage));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (damage > 0 && hasTrample(attacker) && excessDamageToDefender) {
|
List<Integer> amounts;
|
||||||
defenderDamage(attacker, damage, game, false);
|
if (hasTrample(attacker)){
|
||||||
} else if (!blockerOrder.isEmpty()) {
|
if (remainingDamage > 0 || damageDivision.size() > 1) {
|
||||||
// Assign the damage left to first blocker
|
MultiAmountType dialogue = new MultiAmountType("Assign combat damage (with trample)",
|
||||||
assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) == null ? 0 : assigned.get(blockerOrder.get(0)) + damage);
|
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<blockersCopy.size(); i++) {
|
||||||
|
assigned.put(blockersCopy.get(i), amounts.get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (UUID blockerId : blockerOrder) {
|
for (UUID blockerId : blockers) {
|
||||||
Integer power = blockerPower.get(blockerId);
|
Integer power = blockerPower.get(blockerId);
|
||||||
if (power != null) {
|
if (power != null) {
|
||||||
// might be missing canDamage condition?
|
// might be missing canDamage condition?
|
||||||
|
|
@ -374,7 +346,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (UUID blockerId : blockerOrder) {
|
for (UUID blockerId : blockers) {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (dealsDamageThisStep(blocker, first, game)) {
|
if (dealsDamageThisStep(blocker, first, game)) {
|
||||||
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
|
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
|
||||||
|
|
@ -395,7 +367,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
if (dealsDamageThisStep(attacker, first, game)) {
|
if (dealsDamageThisStep(attacker, first, game)) {
|
||||||
// must be set before attacker damage marking because of effects like Test of Faith
|
// must be set before attacker damage marking because of effects like Test of Faith
|
||||||
Map<UUID, Integer> blockerPower = new HashMap<>();
|
Map<UUID, Integer> blockerPower = new HashMap<>();
|
||||||
for (UUID blockerId : blockerOrder) {
|
for (UUID blockerId : blockers) {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (dealsDamageThisStep(blocker, first, game)) {
|
if (dealsDamageThisStep(blocker, first, game)) {
|
||||||
if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately
|
if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately
|
||||||
|
|
@ -422,7 +394,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isAttacking) {
|
if (isAttacking) {
|
||||||
for (UUID blockerId : blockerOrder) {
|
for (UUID blockerId : blockers) {
|
||||||
Integer power = blockerPower.get(blockerId);
|
Integer power = blockerPower.get(blockerId);
|
||||||
if (power != null) {
|
if (power != null) {
|
||||||
// might be missing canDamage condition?
|
// might be missing canDamage condition?
|
||||||
|
|
@ -439,7 +411,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isAttacking) {
|
if (isAttacking) {
|
||||||
for (UUID blockerId : blockerOrder) {
|
for (UUID blockerId : blockers) {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (dealsDamageThisStep(blocker, first, game)) {
|
if (dealsDamageThisStep(blocker, first, game)) {
|
||||||
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
|
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
|
||||||
|
|
@ -473,81 +445,62 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
/**
|
/**
|
||||||
* Damages attacking creatures by a creature that blocked several ones
|
* Damages attacking creatures by a creature that blocked several ones
|
||||||
* Damages only attackers as blocker was damage in
|
* Damages only attackers as blocker was damage in
|
||||||
* {@link #singleBlockerDamage}.
|
* {@link #blockerDamage}.
|
||||||
* <p>
|
* <p>
|
||||||
* Handles abilities like "{this} an block any number of creatures.".
|
* Handles abilities like "{this} can block any number of creatures.".
|
||||||
* <p>
|
|
||||||
* 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}.
|
|
||||||
* <p>
|
|
||||||
* Handles abilities like "{this} an block any number of creatures.".
|
|
||||||
*
|
*
|
||||||
* @param first
|
* @param first
|
||||||
* @param game
|
* @param game
|
||||||
*/
|
*/
|
||||||
private void multiAttackerDamage(boolean first, Game game) {
|
private void attackerDamage(boolean first, Game game) {
|
||||||
Permanent blocker = game.getPermanent(blockers.get(0));
|
Permanent blocker = game.getPermanent(blockers.get(0));
|
||||||
if (blocker == null) {
|
if (blocker == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean oldRuleDamage = attackerAssignsCombatDamage(game); // handles banding
|
//Handle Banding
|
||||||
Player player = game.getPlayer(oldRuleDamage ? game.getCombat().getAttackingPlayerId() : blocker.getControllerId());
|
Player player = game.getPlayer(attackerAssignsCombatDamage(game) ? game.getCombat().getAttackingPlayerId() : blocker.getControllerId());
|
||||||
int damage = getDamageValueFromPermanent(blocker, game);
|
int damage = getDamageValueFromPermanent(blocker, game);
|
||||||
|
|
||||||
if (dealsDamageThisStep(blocker, first, game)) {
|
if (dealsDamageThisStep(blocker, first, game)) {
|
||||||
Map<UUID, Integer> assigned = new HashMap<>();
|
Map<UUID, Integer> assigned = new HashMap<>();
|
||||||
for (UUID attackerId : attackerOrder) {
|
List<MultiAmountMessage> damageDivision = new ArrayList<>();
|
||||||
|
List<UUID> attackersCopy = new ArrayList<>(attackers);
|
||||||
|
int remainingDamage = damage;
|
||||||
|
for (UUID attackerId : attackers) {
|
||||||
Permanent attacker = game.getPermanent(attackerId);
|
Permanent attacker = game.getPermanent(attackerId);
|
||||||
if (attacker != null) {
|
if (attacker != null) {
|
||||||
int lethalDamage = getLethalDamage(attacker, blocker, game);
|
int defaultDamage = Math.min(remainingDamage, attacker.getLethalDamage(blocker.getId(), game));
|
||||||
if (lethalDamage >= damage) {
|
remainingDamage -= defaultDamage;
|
||||||
if (!oldRuleDamage) {
|
String message = String.format("%s, P/T: %d/%d",
|
||||||
assigned.put(attackerId, damage);
|
attacker.getLogName(),
|
||||||
damage = 0;
|
attacker.getPower().getValue(),
|
||||||
break;
|
attacker.getToughness().getValue());
|
||||||
} else if (damage == 0) {
|
damageDivision.add(new MultiAmountMessage(message, 0, damage, defaultDamage));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (damage > 0) {
|
List<Integer> amounts;
|
||||||
// Assign the damage left to first attacker
|
if (remainingDamage > 0){
|
||||||
assigned.put(attackerOrder.get(0), assigned.get(attackerOrder.get(0)) + damage);
|
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<attackersCopy.size(); i++) {
|
||||||
|
assigned.put(attackersCopy.get(i), amounts.get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) {
|
for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) {
|
||||||
Permanent attacker = game.getPermanent(entry.getKey());
|
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<CombatGroup> {
|
||||||
if (blockerId != null && blocker != null) {
|
if (blockerId != null && blocker != null) {
|
||||||
blocker.setBlocking(blocker.getBlocking() + 1);
|
blocker.setBlocking(blocker.getBlocking() + 1);
|
||||||
blockers.add(blockerId);
|
blockers.add(blockerId);
|
||||||
blockerOrder.add(blockerId);
|
|
||||||
this.blocked = true;
|
this.blocked = true;
|
||||||
this.players.put(blockerId, playerId);
|
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<UUID> blockerList = new ArrayList<>(blockers);
|
|
||||||
blockerOrder.clear();
|
|
||||||
while (player.canRespond()) {
|
|
||||||
if (blockerList.size() == 1) {
|
|
||||||
blockerOrder.add(blockerList.get(0));
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
List<Permanent> 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<UUID> attackerList = new ArrayList<>(attackers);
|
|
||||||
List<UUID> newAttackerOrder = new ArrayList<>();
|
|
||||||
while (true) {
|
|
||||||
if (attackerList.size() == 1) {
|
|
||||||
newAttackerOrder.add(attackerList.get(0));
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
List<Permanent> 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<UUID> assignedFor, List<UUID> assignedOrder, Game game) {
|
private void logDamageAssignmentOrder(String prefix, List<UUID> assignedFor, List<UUID> assignedOrder, Game game) {
|
||||||
StringBuilder sb = new StringBuilder(prefix);
|
StringBuilder sb = new StringBuilder(prefix);
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
|
|
@ -746,12 +636,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
formerAttackers.add(creatureId);
|
formerAttackers.add(creatureId);
|
||||||
attackers.remove(creatureId);
|
attackers.remove(creatureId);
|
||||||
result = true;
|
result = true;
|
||||||
attackerOrder.remove(creatureId);
|
|
||||||
} else if (blockers.contains(creatureId)) {
|
} else if (blockers.contains(creatureId)) {
|
||||||
blockers.remove(creatureId);
|
blockers.remove(creatureId);
|
||||||
result = true;
|
result = true;
|
||||||
//20100423 - 509.2a
|
|
||||||
blockerOrder.remove(creatureId);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -825,7 +712,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
game.getCombat().removeBlocker(blockerId, game);
|
game.getCombat().removeBlocker(blockerId, game);
|
||||||
}
|
}
|
||||||
blockers.clear();
|
blockers.clear();
|
||||||
blockerOrder.clear();
|
|
||||||
if (!game.isSimulation()) {
|
if (!game.isSimulation()) {
|
||||||
game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded.");
|
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<CombatGroup> {
|
||||||
game.getCombat().removeBlocker(blockerId, game);
|
game.getCombat().removeBlocker(blockerId, game);
|
||||||
}
|
}
|
||||||
blockers.clear();
|
blockers.clear();
|
||||||
blockerOrder.clear();
|
|
||||||
if (!game.isSimulation()) {
|
if (!game.isSimulation()) {
|
||||||
game.informPlayers(new StringBuilder(attacker.getLogName())
|
game.informPlayers(new StringBuilder(attacker.getLogName())
|
||||||
.append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy())
|
.append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy())
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
|
|
||||||
package mage.game.turn;
|
package mage.game.turn;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent.EventType;
|
import mage.game.events.GameEvent.EventType;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
|
@ -37,7 +37,6 @@ public class DeclareBlockersStep extends Step {
|
||||||
game.getCombat().selectBlockers(game);
|
game.getCombat().selectBlockers(game);
|
||||||
if (!game.isPaused() && !game.executingRollback()) {
|
if (!game.isPaused() && !game.executingRollback()) {
|
||||||
game.getCombat().acceptBlockers(game);
|
game.getCombat().acceptBlockers(game);
|
||||||
game.getCombat().damageAssignmentOrder(game);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +45,6 @@ public class DeclareBlockersStep extends Step {
|
||||||
super.resumeBeginStep(game, activePlayerId);
|
super.resumeBeginStep(game, activePlayerId);
|
||||||
game.getCombat().resumeSelectBlockers(game);
|
game.getCombat().resumeSelectBlockers(game);
|
||||||
game.getCombat().acceptBlockers(game);
|
game.getCombat().acceptBlockers(game);
|
||||||
game.getCombat().damageAssignmentOrder(game);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import mage.filter.FilterCard;
|
||||||
import mage.filter.FilterMana;
|
import mage.filter.FilterMana;
|
||||||
import mage.filter.FilterPermanent;
|
import mage.filter.FilterPermanent;
|
||||||
import mage.game.*;
|
import mage.game.*;
|
||||||
import mage.game.combat.CombatGroup;
|
|
||||||
import mage.game.draft.Draft;
|
import mage.game.draft.Draft;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.match.Match;
|
import mage.game.match.Match;
|
||||||
|
|
@ -761,19 +760,6 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
|
|
||||||
void selectBlockers(Ability source, Game game, UUID defendingPlayerId);
|
void selectBlockers(Ability source, Game game, UUID defendingPlayerId);
|
||||||
|
|
||||||
UUID chooseAttackerOrder(List<Permanent> 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<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game);
|
|
||||||
|
|
||||||
int getAmount(int min, int max, String message, Game game);
|
int getAmount(int min, int max, String message, Game game);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,8 @@ import mage.constants.Outcome;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
import mage.filter.FilterMana;
|
import mage.filter.FilterMana;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.combat.CombatGroup;
|
|
||||||
import mage.game.draft.Draft;
|
import mage.game.draft.Draft;
|
||||||
import mage.game.match.Match;
|
import mage.game.match.Match;
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.game.tournament.Tournament;
|
import mage.game.tournament.Tournament;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.TargetAmount;
|
import mage.target.TargetAmount;
|
||||||
|
|
@ -190,16 +188,6 @@ public class StubPlayer extends PlayerImpl {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseAttackerOrder(List<Permanent> attacker, Game game) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Game game) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue