mirror of
https://github.com/correl/mage.git
synced 2025-04-08 01:01:04 -09:00
Merge from origin/master
This commit is contained in:
commit
cef3b9488f
13 changed files with 251 additions and 162 deletions
Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human
Mage.Sets/src/mage/cards
a
d
h
k
m
s
w
Mage.Tests/src/test/java/org/mage/test
Mage/src/main/java/mage
abilities/keyword
game
watchers/common
|
@ -635,7 +635,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose one or multiple cards
|
// choose one or multiple cards
|
||||||
if (cards == null) {
|
if (cards == null || cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,6 +666,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
options.put("choosable", (Serializable) choosable);
|
options.put("choosable", (Serializable) choosable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if nothing to choose then show window (user must see non selectable items and click on any of them)
|
||||||
|
if (required && choosable.isEmpty()) {
|
||||||
|
required = false;
|
||||||
|
}
|
||||||
|
|
||||||
updateGameStatePriority("choose(4)", game);
|
updateGameStatePriority("choose(4)", game);
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
|
@ -704,7 +709,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cards == null) {
|
if (cards == null || cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,6 +740,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
options.put("choosable", (Serializable) choosable);
|
options.put("choosable", (Serializable) choosable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if nothing to choose then show window (user must see non selectable items and click on any of them)
|
||||||
|
if (required && choosable.isEmpty()) {
|
||||||
|
required = false;
|
||||||
|
}
|
||||||
|
|
||||||
updateGameStatePriority("chooseTarget(5)", game);
|
updateGameStatePriority("chooseTarget(5)", game);
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
|
|
|
@ -26,7 +26,8 @@ public final class AgonizingRemorse extends CardImpl {
|
||||||
public AgonizingRemorse(UUID ownerId, CardSetInfo setInfo) {
|
public AgonizingRemorse(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
|
||||||
|
|
||||||
// Target opponent reveals their hand. You choose a nonland card from it or a card from their graveyard. Exile that card. You lose 1 life.
|
// Target opponent reveals their hand. You choose a nonland card from
|
||||||
|
// it or a card from their graveyard. Exile that card. You lose 1 life.
|
||||||
this.getSpellAbility().addEffect(new AgonizingRemorseEffect());
|
this.getSpellAbility().addEffect(new AgonizingRemorseEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetOpponent());
|
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||||
}
|
}
|
||||||
|
@ -45,8 +46,8 @@ class AgonizingRemorseEffect extends OneShotEffect {
|
||||||
|
|
||||||
AgonizingRemorseEffect() {
|
AgonizingRemorseEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
staticText = "Target opponent reveals their hand. You choose a nonland card from it " +
|
staticText = "Target opponent reveals their hand. You choose a nonland card from it "
|
||||||
"or a card from their graveyard. Exile that card. You lose 1 life.";
|
+ "or a card from their graveyard. Exile that card. You lose 1 life.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private AgonizingRemorseEffect(final AgonizingRemorseEffect effect) {
|
private AgonizingRemorseEffect(final AgonizingRemorseEffect effect) {
|
||||||
|
@ -68,12 +69,15 @@ class AgonizingRemorseEffect extends OneShotEffect {
|
||||||
opponent.revealCards(source, opponent.getHand(), game);
|
opponent.revealCards(source, opponent.getHand(), game);
|
||||||
TargetCard target;
|
TargetCard target;
|
||||||
Cards cards;
|
Cards cards;
|
||||||
if (controller.chooseUse(outcome, "Exile a card from hand or graveyard?", null, "Hand", "Graveyard", source, game)) {
|
if (controller.chooseUse(outcome, "Exile a card from hand or graveyard?",
|
||||||
target = new TargetCard(Zone.HAND, new FilterNonlandCard("nonland card in " + opponent.getName() + "'s hand"));
|
null, "Hand", "Graveyard", source, game)) {
|
||||||
|
target = new TargetCard(Zone.HAND, new FilterNonlandCard("nonland card in "
|
||||||
|
+ opponent.getName() + "'s hand"));
|
||||||
target.setNotTarget(true);
|
target.setNotTarget(true);
|
||||||
cards = opponent.getHand();
|
cards = opponent.getHand();
|
||||||
} else {
|
} else {
|
||||||
target = new TargetCard(Zone.GRAVEYARD, new FilterCard("card in " + opponent.getName() + "'s graveyard"));
|
target = new TargetCard(Zone.GRAVEYARD, new FilterCard("card in "
|
||||||
|
+ opponent.getName() + "'s graveyard"));
|
||||||
target.setNotTarget(true);
|
target.setNotTarget(true);
|
||||||
cards = opponent.getGraveyard();
|
cards = opponent.getGraveyard();
|
||||||
}
|
}
|
||||||
|
@ -85,6 +89,7 @@ class AgonizingRemorseEffect extends OneShotEffect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
controller.moveCards(card, Zone.EXILED, source, game);
|
controller.moveCards(card, Zone.EXILED, source, game);
|
||||||
|
controller.loseLife(1, game, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,20 +28,26 @@ public final class DrivenDespair extends SplitCard {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, new CardType[]{CardType.SORCERY}, "{1}{G}", "{1}{B}", SpellAbilityType.SPLIT_AFTERMATH);
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, new CardType[]{CardType.SORCERY}, "{1}{G}", "{1}{B}", SpellAbilityType.SPLIT_AFTERMATH);
|
||||||
|
|
||||||
// Until end of turn, creatures you control gain trample and "Whenever this creature deals combat damage to a player, draw a card."
|
// Until end of turn, creatures you control gain trample and "Whenever this creature deals combat damage to a player, draw a card."
|
||||||
getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn));
|
getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(
|
||||||
|
TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES)
|
||||||
|
.setText("Until end of turn, creatures you control gain trample"));
|
||||||
TriggeredAbility ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1), false);
|
TriggeredAbility ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1), false);
|
||||||
getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn)
|
getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn)
|
||||||
.setText("and \"Whenever this creature deals combat damage to a player, draw a card.\""));
|
.setText("\"Whenever this creature deals combat damage to a player, draw a card.\"")
|
||||||
|
.concatBy("and"));
|
||||||
|
|
||||||
// Despair {1}{B}
|
// Despair {1}{B}
|
||||||
// Sorcery
|
// Sorcery
|
||||||
// Aftermath
|
// Aftermath
|
||||||
getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true));
|
getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true));
|
||||||
// Until end of turn, creatures you control gain menace and "Whenever this creature deals combat damage to a player, that player discards a card."
|
// Until end of turn, creatures you control gain menace and "Whenever this creature deals combat damage to a player, that player discards a card."
|
||||||
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES));
|
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(
|
||||||
|
new MenaceAbility(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES)
|
||||||
|
.setText("Until end of turn, creatures you control gain menace"));
|
||||||
ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DiscardTargetEffect(1), false, true);
|
ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DiscardTargetEffect(1), false, true);
|
||||||
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES)
|
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES)
|
||||||
.setText("and \"Whenever this creature deals combat damage to a player, that player discards a card.\""));
|
.setText("\"Whenever this creature deals combat damage to a player, that player discards a card.\"")
|
||||||
|
.concatBy("and"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,18 @@ package mage.cards.h;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.TriggeredAbilityImpl;
|
import mage.abilities.TriggeredAbilityImpl;
|
||||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
|
||||||
import mage.abilities.keyword.LifelinkAbility;
|
import mage.abilities.keyword.LifelinkAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterPermanent;
|
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
|
||||||
import mage.filter.predicate.permanent.EnchantedPredicate;
|
|
||||||
import mage.game.Controllable;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.ZoneChangeEvent;
|
import mage.game.events.ZoneChangeEvent;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* @author TheElk801
|
||||||
|
@ -35,7 +30,8 @@ public final class HatefulEidolon extends CardImpl {
|
||||||
// Lifelink
|
// Lifelink
|
||||||
this.addAbility(LifelinkAbility.getInstance());
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
// Whenever an enchanted creature dies, draw a card for each Aura you controlled that was attached to it.
|
// Whenever an enchanted creature dies, draw a card for each
|
||||||
|
// Aura you controlled that was attached to it.
|
||||||
this.addAbility(new HatefulEidolonTriggeredAbility());
|
this.addAbility(new HatefulEidolonTriggeredAbility());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,12 +47,6 @@ public final class HatefulEidolon extends CardImpl {
|
||||||
|
|
||||||
class HatefulEidolonTriggeredAbility extends TriggeredAbilityImpl {
|
class HatefulEidolonTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
private static final FilterPermanent filter = new FilterCreaturePermanent();
|
|
||||||
|
|
||||||
static {
|
|
||||||
filter.add(EnchantedPredicate.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
HatefulEidolonTriggeredAbility() {
|
HatefulEidolonTriggeredAbility() {
|
||||||
super(Zone.BATTLEFIELD, null, false);
|
super(Zone.BATTLEFIELD, null, false);
|
||||||
}
|
}
|
||||||
|
@ -77,21 +67,21 @@ class HatefulEidolonTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
|
int auraCount = 0;
|
||||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||||
if (!zEvent.isDiesEvent() || !filter.match(zEvent.getTarget(), game)) {
|
if (!zEvent.isDiesEvent()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int auraCount = zEvent
|
Permanent deadCreature = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||||
.getTarget()
|
if (deadCreature.getAttachments().isEmpty()) {
|
||||||
.getAttachments()
|
return false;
|
||||||
.stream()
|
}
|
||||||
.map(game::getPermanentOrLKIBattlefield)
|
for (UUID auraId : deadCreature.getAttachments()) {
|
||||||
.filter(Objects::nonNull)
|
Permanent aura = game.getPermanentOrLKIBattlefield(auraId);
|
||||||
.filter(permanent -> permanent.hasSubtype(SubType.AURA, game))
|
if (aura.getControllerId().equals(controllerId)) {
|
||||||
.map(Controllable::getControllerId)
|
auraCount += 1;
|
||||||
.filter(this.getControllerId()::equals)
|
}
|
||||||
.mapToInt(x -> 1)
|
}
|
||||||
.sum();
|
|
||||||
this.getEffects().clear();
|
this.getEffects().clear();
|
||||||
this.addEffect(new DrawCardSourceControllerEffect(auraCount));
|
this.addEffect(new DrawCardSourceControllerEffect(auraCount));
|
||||||
return true;
|
return true;
|
||||||
|
@ -99,6 +89,7 @@ class HatefulEidolonTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return "Whenever an enchanted creature dies, draw a card for each Aura you controlled that was attached to it.";
|
return "Whenever an enchanted creature dies, draw a card for each "
|
||||||
|
+ "Aura you controlled that was attached to it.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package mage.cards.k;
|
package mage.cards.k;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.DelayedTriggeredAbility;
|
import mage.abilities.DelayedTriggeredAbility;
|
||||||
|
@ -23,10 +26,6 @@ import mage.target.TargetPlayer;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author bunchOfDevs
|
* @author bunchOfDevs
|
||||||
*/
|
*/
|
||||||
|
@ -111,7 +110,7 @@ class KarnLiberatedEffect extends OneShotEffect {
|
||||||
&& !cards.contains(card)) { // not the exiled cards
|
&& !cards.contains(card)) { // not the exiled cards
|
||||||
if (game.getCommandersIds(player).contains(card.getId())) {
|
if (game.getCommandersIds(player).contains(card.getId())) {
|
||||||
game.addCommander(new Commander(card)); // TODO: check restart and init
|
game.addCommander(new Commander(card)); // TODO: check restart and init
|
||||||
// no needs in initCommander call -- it's uses on game startup (init)
|
// no needs in initCommander call -- it's used on game startup (init)
|
||||||
game.setZone(card.getId(), Zone.COMMAND);
|
game.setZone(card.getId(), Zone.COMMAND);
|
||||||
} else {
|
} else {
|
||||||
player.getLibrary().putOnTop(card, game);
|
player.getLibrary().putOnTop(card, game);
|
||||||
|
@ -124,10 +123,7 @@ class KarnLiberatedEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
for (Card card : cards) {
|
for (Card card : cards) {
|
||||||
game.getState().setZone(card.getId(), Zone.EXILED);
|
game.getState().setZone(card.getId(), Zone.EXILED);
|
||||||
if (card.isPermanent()
|
game.getExile().add(exileId, sourceObject.getIdName(), card);
|
||||||
&& !card.hasSubtype(SubType.AURA, game)) {
|
|
||||||
game.getExile().add(exileId, sourceObject.getIdName(), card);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
game.addDelayedTriggeredAbility(new KarnLiberatedDelayedTriggeredAbility(exileId), source);
|
game.addDelayedTriggeredAbility(new KarnLiberatedDelayedTriggeredAbility(exileId), source);
|
||||||
game.setStartingPlayerId(source.getControllerId());
|
game.setStartingPlayerId(source.getControllerId());
|
||||||
|
|
|
@ -79,8 +79,7 @@ public final class MagusOfTheMoon extends CardImpl {
|
||||||
land.removeAllAbilities(source.getSourceId(), game);
|
land.removeAllAbilities(source.getSourceId(), game);
|
||||||
land.getSubtype(game).removeAll(SubType.getLandTypes());
|
land.getSubtype(game).removeAll(SubType.getLandTypes());
|
||||||
land.getSubtype(game).add(SubType.MOUNTAIN);
|
land.getSubtype(game).add(SubType.MOUNTAIN);
|
||||||
break;
|
// Mountains have the red mana ability intrinsically so the ability must be added in this layer
|
||||||
case AbilityAddingRemovingEffects_6:
|
|
||||||
land.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
land.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +89,7 @@ public final class MagusOfTheMoon extends CardImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLayer(Layer layer) {
|
public boolean hasLayer(Layer layer) {
|
||||||
return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4;
|
return layer == Layer.TypeChangingEffects_4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,11 @@ import mage.filter.FilterCard;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetCard;
|
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
import mage.target.common.TargetOpponent;
|
import mage.target.common.TargetOpponent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import mage.filter.common.FilterNonlandCard;
|
||||||
|
import mage.target.TargetCard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* @author TheElk801
|
||||||
|
@ -26,7 +26,9 @@ public final class SpectersShriek extends CardImpl {
|
||||||
public SpectersShriek(UUID ownerId, CardSetInfo setInfo) {
|
public SpectersShriek(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}");
|
||||||
|
|
||||||
// Target opponent reveals their hand. You may choose a nonland card from it. If you do, that player exiles that card. If a nonblack card is exiled this way, exile a card from your hand.
|
// Target opponent reveals their hand. You may choose a nonland card
|
||||||
|
// from it. If you do, that player exiles that card. If a nonblack
|
||||||
|
// card is exiled this way, exile a card from your hand.
|
||||||
this.getSpellAbility().addEffect(new SpectersShriekEffect());
|
this.getSpellAbility().addEffect(new SpectersShriekEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetOpponent());
|
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||||
}
|
}
|
||||||
|
@ -47,8 +49,8 @@ class SpectersShriekEffect extends OneShotEffect {
|
||||||
|
|
||||||
SpectersShriekEffect() {
|
SpectersShriekEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
staticText = "Target opponent reveals their hand. You may choose a nonland card from it. If you do, " +
|
staticText = "Target opponent reveals their hand. You may choose a nonland card from it. If you do, "
|
||||||
"that player exiles that card. If a nonblack card is exiled this way, exile a card from your hand.";
|
+ "that player exiles that card. If a nonblack card is exiled this way, exile a card from your hand.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpectersShriekEffect(final SpectersShriekEffect effect) {
|
private SpectersShriekEffect(final SpectersShriekEffect effect) {
|
||||||
|
@ -64,17 +66,20 @@ class SpectersShriekEffect extends OneShotEffect {
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
Player player = game.getPlayer(source.getFirstTarget());
|
Player player = game.getPlayer(source.getFirstTarget());
|
||||||
if (controller == null || player == null) {
|
if (controller == null
|
||||||
|
|| player == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
player.revealCards(source, player.getHand(), game);
|
player.revealCards(source, player.getHand(), game);
|
||||||
if (player.getHand().count(StaticFilters.FILTER_CARD_NON_LAND, game) == 0
|
if (player.getHand().count(StaticFilters.FILTER_CARD_NON_LAND, game) == 0
|
||||||
|| !controller.chooseUse(outcome, "Exile a card from " + player.getName() + "'s hand?", source, game)) {
|
|| !controller.chooseUse(Outcome.Benefit, "Exile a card from "
|
||||||
return true;
|
+ player.getName() + "'s hand?", source, game)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
TargetCard target = new TargetCardInHand(StaticFilters.FILTER_CARD_NON_LAND);
|
TargetCard target = new TargetCard(Zone.HAND, new FilterNonlandCard());
|
||||||
target.setNotTarget(true);
|
target.setNotTarget(true);
|
||||||
if (!controller.choose(outcome, player.getHand(), target, game)) {
|
target.setRequired(false);
|
||||||
|
if (!controller.chooseTarget(Outcome.Benefit, player.getHand(), target, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Card card = game.getCard(target.getFirstTarget());
|
Card card = game.getCard(target.getFirstTarget());
|
||||||
|
@ -83,12 +88,13 @@ class SpectersShriekEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
boolean isBlack = card.getColor(game).isBlack();
|
boolean isBlack = card.getColor(game).isBlack();
|
||||||
player.moveCards(card, Zone.EXILED, source, game);
|
player.moveCards(card, Zone.EXILED, source, game);
|
||||||
if (isBlack || controller.getHand().isEmpty()) {
|
if (isBlack
|
||||||
|
|| controller.getHand().isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
target = new TargetCardInHand(filter);
|
target = new TargetCardInHand(filter);
|
||||||
target.setNotTarget(true);
|
target.setNotTarget(true);
|
||||||
return controller.choose(outcome, controller.getHand(), target, game)
|
return controller.choose(Outcome.Detriment, controller.getHand(), target, game)
|
||||||
&& controller.moveCards(game.getCard(target.getFirstTarget()), Zone.EXILED, source, game);
|
&& controller.moveCards(game.getCard(target.getFirstTarget()), Zone.EXILED, source, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -40,8 +39,8 @@ class WhirlwindDenialEffect extends OneShotEffect {
|
||||||
|
|
||||||
WhirlwindDenialEffect() {
|
WhirlwindDenialEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
staticText = "For each spell and ability your opponents control, " +
|
staticText = "For each spell and ability your opponents control, "
|
||||||
"counter it unless its controller pays {4}.";
|
+ "counter it unless its controller pays {4}.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private WhirlwindDenialEffect(final WhirlwindDenialEffect effect) {
|
private WhirlwindDenialEffect(final WhirlwindDenialEffect effect) {
|
||||||
|
@ -59,7 +58,7 @@ class WhirlwindDenialEffect extends OneShotEffect {
|
||||||
.stream()
|
.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEachOrdered(stackObject -> {
|
.forEachOrdered(stackObject -> {
|
||||||
if (!game.getOpponents(stackObject.getControllerId()).contains(source.getControllerId())) {
|
if (!game.getOpponents(source.getControllerId()).contains(stackObject.getControllerId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Player player = game.getPlayer(stackObject.getControllerId());
|
Player player = game.getPlayer(stackObject.getControllerId());
|
||||||
|
@ -67,13 +66,14 @@ class WhirlwindDenialEffect extends OneShotEffect {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Cost cost = new GenericManaCost(4);
|
Cost cost = new GenericManaCost(4);
|
||||||
if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game)
|
if (cost.canPay(source, source.getSourceId(), stackObject.getControllerId(), game)
|
||||||
&& player.chooseUse(outcome, "Pay {4} to prevent " + stackObject.getIdName() + " from being countered?", source, game)
|
&& player.chooseUse(outcome, "Pay {4} to prevent "
|
||||||
&& cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) {
|
+ stackObject.getIdName() + " from being countered?", source, game)
|
||||||
|
&& cost.pay(source, game, source.getSourceId(), stackObject.getControllerId(), false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stackObject.counter(source.getSourceId(), game);
|
stackObject.counter(source.getSourceId(), game);
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package org.mage.test.commander.duel;
|
package org.mage.test.commander.duel;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -6,6 +5,8 @@ import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
|
import mage.watchers.common.CommanderInfoWatcher;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
|
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
// When you cast Prossh, Skyraider of Kher, put X 0/1 red Kobold creature tokens named Kobolds of Kher Keep onto the battlefield, where X is the amount of mana spent to cast Prossh.
|
// When you cast Prossh, Skyraider of Kher, put X 0/1 red Kobold creature tokens named Kobolds of Kher Keep onto the battlefield, where X is the amount of mana spent to cast Prossh.
|
||||||
// Sacrifice another creature: Prossh gets +1/+0 until end of turn.
|
// Sacrifice another creature: Prossh gets +1/+0 until end of turn.
|
||||||
setDecknamePlayerA("Power Hungry.dck"); // Commander = Prosssh, Skyrider of Kher {3}{B}{R}{G}
|
setDecknamePlayerA("Power Hungry.dck"); // Commander = Prosssh, Skyrider of Kher {3}{B}{R}{G}
|
||||||
setDecknamePlayerB("CommanderDuel_UW.dck"); // Daxos of Meletis {1}{W}{U}
|
setDecknamePlayerB("CommanderDuel_UW.dck"); // Commander = Daxos of Meletis {1}{W}{U}
|
||||||
return super.createNewGameAndPlayers();
|
return super.createNewGameAndPlayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +39,10 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Savage Summoning");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Savage Summoning");
|
||||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Prossh, Skyraider of Kher"); // 5/5
|
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Prossh, Skyraider of Kher"); // 5/5
|
||||||
setStopAt(1, PhaseStep.END_COMBAT);
|
setStopAt(1, PhaseStep.END_COMBAT);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Savage Summoning", 1);
|
assertGraveyardCount(playerA, "Savage Summoning", 1);
|
||||||
assertPermanentCount(playerA, "Prossh, Skyraider of Kher", 1);
|
assertPermanentCount(playerA, "Prossh, Skyraider of Kher", 1);
|
||||||
|
@ -66,7 +70,10 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-14: Restart");
|
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-14: Restart");
|
||||||
|
|
||||||
setStopAt(5, PhaseStep.BEGIN_COMBAT);
|
setStopAt(5, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Karn Liberated", 0);
|
assertGraveyardCount(playerA, "Karn Liberated", 0);
|
||||||
assertPermanentCount(playerA, "Silvercoat Lion", 2);
|
assertPermanentCount(playerA, "Silvercoat Lion", 2);
|
||||||
|
@ -75,6 +82,55 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the commander is exiled by Karn (and not returned to the command
|
||||||
|
* zone), it needs to restart the game in play and not the command zone.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCommanderRestoredToBattlefieldAfterKarnUltimate() {
|
||||||
|
// +4: Target player exiles a card from their hand.
|
||||||
|
// -3: Exile target permanent.
|
||||||
|
// -14: Restart the game, leaving in exile all non-Aura permanent cards exiled with Karn Liberated. Then put those cards onto the battlefield under your control.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Karn Liberated", 1); // Planeswalker (6)
|
||||||
|
addCard(Zone.HAND, playerA, "Silvercoat Lion", 3);
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
|
||||||
|
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
||||||
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Daxos of Meletis");
|
||||||
|
|
||||||
|
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
||||||
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
attack(4, playerB, "Daxos of Meletis");
|
||||||
|
|
||||||
|
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-3: Exile target permanent", "Daxos of Meletis");
|
||||||
|
setChoice(playerB, "No"); // Move commander NOT to command zone
|
||||||
|
|
||||||
|
activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
||||||
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
activateAbility(9, PhaseStep.PRECOMBAT_MAIN, playerA, "-14: Restart");
|
||||||
|
|
||||||
|
setStopAt(9, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Karn Liberated", 0);
|
||||||
|
assertPermanentCount(playerA, "Silvercoat Lion", 3);
|
||||||
|
assertCommandZoneCount(playerA, "Prossh, Skyraider of Kher", 1);
|
||||||
|
assertCommandZoneCount(playerB, "Daxos of Meletis", 0);
|
||||||
|
assertPermanentCount(playerA, "Daxos of Meletis", 1); // Karn brings back the cards under the control of Karn's controller
|
||||||
|
|
||||||
|
CommanderInfoWatcher watcher = currentGame.getState().getWatcher(CommanderInfoWatcher.class, playerB.getCommandersIds().iterator().next());
|
||||||
|
Assert.assertEquals("Watcher is reset to 0 commander damage", 0, (int) watcher.getDamageToPlayer().size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mogg infestation creates tokens "for each creature that died this way".
|
* Mogg infestation creates tokens "for each creature that died this way".
|
||||||
* When a commander is moved to a command zone, it doesn't "die", and thus
|
* When a commander is moved to a command zone, it doesn't "die", and thus
|
||||||
|
@ -92,9 +148,14 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
|
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Daxos of Meletis");
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Daxos of Meletis");
|
||||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Mogg Infestation");
|
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Mogg Infestation");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
setChoice(playerB, "Yes"); // Move commander to command zone
|
||||||
|
|
||||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Mogg Infestation", 1);
|
assertGraveyardCount(playerA, "Mogg Infestation", 1);
|
||||||
assertCommandZoneCount(playerB, "Daxos of Meletis", 1);
|
assertCommandZoneCount(playerB, "Daxos of Meletis", 1);
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package org.mage.test.player;
|
package org.mage.test.player;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import mage.MageItem;
|
import mage.MageItem;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
|
@ -56,13 +61,6 @@ import mage.util.CardUtil;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
|
import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,7 +78,7 @@ public class TestPlayer implements Player {
|
||||||
public static final String ATTACK_SKIP = "[attack_skip]";
|
public static final String ATTACK_SKIP = "[attack_skip]";
|
||||||
public static final String NO_TARGET = "NO_TARGET"; // cast spell or activate ability without target defines
|
public static final String NO_TARGET = "NO_TARGET"; // cast spell or activate ability without target defines
|
||||||
|
|
||||||
private int maxCallsWithoutAction = 100;
|
private int maxCallsWithoutAction = 400;
|
||||||
private int foundNoAction = 0;
|
private int foundNoAction = 0;
|
||||||
private boolean AIPlayer;
|
private boolean AIPlayer;
|
||||||
private final List<PlayerAction> actions = new ArrayList<>();
|
private final List<PlayerAction> actions = new ArrayList<>();
|
||||||
|
@ -179,7 +177,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param maxCallsWithoutAction max number of priority passes a player may
|
* @param maxCallsWithoutAction max number of priority passes a player may
|
||||||
* have for this test (default = 100)
|
* have for this test (default = 100)
|
||||||
*/
|
*/
|
||||||
public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
|
public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
|
||||||
this.maxCallsWithoutAction = maxCallsWithoutAction;
|
this.maxCallsWithoutAction = maxCallsWithoutAction;
|
||||||
|
@ -518,6 +516,7 @@ public class TestPlayer implements Player {
|
||||||
if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) {
|
if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) {
|
||||||
actions.remove(action);
|
actions.remove(action);
|
||||||
groupsForTargetHandling = null;
|
groupsForTargetHandling = null;
|
||||||
|
foundNoAction = 0; // Reset enless loop check because of no action
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
game.restoreState(bookmark, ability.getRule());
|
game.restoreState(bookmark, ability.getRule());
|
||||||
|
@ -854,7 +853,8 @@ public class TestPlayer implements Player {
|
||||||
if (numberOfActions == actions.size()) {
|
if (numberOfActions == actions.size()) {
|
||||||
foundNoAction++;
|
foundNoAction++;
|
||||||
if (foundNoAction > maxCallsWithoutAction) {
|
if (foundNoAction > maxCallsWithoutAction) {
|
||||||
throw new AssertionError("More priority calls to " + getName() + " and doing no action than allowed (" + maxCallsWithoutAction + ')');
|
throw new AssertionError("More priority calls to " + getName()
|
||||||
|
+ " without taking any action than allowed (" + maxCallsWithoutAction + ") on turn " + game.getTurnNum());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundNoAction = 0;
|
foundNoAction = 0;
|
||||||
|
@ -903,12 +903,12 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
List<String> data = cards.stream()
|
List<String> data = cards.stream()
|
||||||
.map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ")
|
.map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ")
|
||||||
+ c.getIdName()
|
+ c.getIdName()
|
||||||
+ (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
|
+ (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
|
||||||
+ " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
|
+ " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
|
||||||
+ (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
|
+ (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
|
||||||
+ ", " + (c.isTapped() ? "Tapped" : "Untapped")
|
+ ", " + (c.isTapped() ? "Tapped" : "Untapped")
|
||||||
+ (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
|
+ (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
@ -932,11 +932,11 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
List<String> data = abilities.stream()
|
List<String> data = abilities.stream()
|
||||||
.map(a -> (a.getZone() + " -> "
|
.map(a -> (a.getZone() + " -> "
|
||||||
+ a.getSourceObject(game).getIdName() + " -> "
|
+ a.getSourceObject(game).getIdName() + " -> "
|
||||||
+ (a.toString().length() > 0
|
+ (a.toString().length() > 0
|
||||||
? a.toString().substring(0, Math.min(20, a.toString().length()) - 1)
|
? a.toString().substring(0, Math.min(20, a.toString().length()) - 1)
|
||||||
: a.getClass().getSimpleName())
|
: a.getClass().getSimpleName())
|
||||||
+ "..."))
|
+ "..."))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
@ -1290,7 +1290,7 @@ public class TestPlayer implements Player {
|
||||||
UUID defenderId = null;
|
UUID defenderId = null;
|
||||||
boolean mustAttackByAction = false;
|
boolean mustAttackByAction = false;
|
||||||
boolean madeAttackByAction = false;
|
boolean madeAttackByAction = false;
|
||||||
for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext(); ) {
|
for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext();) {
|
||||||
PlayerAction action = it.next();
|
PlayerAction action = it.next();
|
||||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
|
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
|
||||||
mustAttackByAction = true;
|
mustAttackByAction = true;
|
||||||
|
@ -1779,7 +1779,7 @@ public class TestPlayer implements Player {
|
||||||
// skip targets
|
// skip targets
|
||||||
if (targets.get(0).equals(TARGET_SKIP)) {
|
if (targets.get(0).equals(TARGET_SKIP)) {
|
||||||
Assert.assertTrue("found skip target, but it require more targets, needs "
|
Assert.assertTrue("found skip target, but it require more targets, needs "
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
targets.remove(0);
|
targets.remove(0);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2082,7 +2082,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
this.chooseStrictModeFailed("choice", game,
|
this.chooseStrictModeFailed("choice", game,
|
||||||
"Triggered list (total " + abilities.size() + "):\n"
|
"Triggered list (total " + abilities.size() + "):\n"
|
||||||
+ abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n")));
|
+ abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n")));
|
||||||
return computerPlayer.chooseTriggeredAbility(abilities, game);
|
return computerPlayer.chooseTriggeredAbility(abilities, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3258,7 +3258,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Target target,
|
public boolean choose(Outcome outcome, Target target,
|
||||||
UUID sourceId, Game game
|
UUID sourceId, Game game
|
||||||
) {
|
) {
|
||||||
// needed to call here the TestPlayer because it's overwitten
|
// needed to call here the TestPlayer because it's overwitten
|
||||||
return choose(outcome, target, sourceId, game, null);
|
return choose(outcome, target, sourceId, game, null);
|
||||||
|
@ -3266,7 +3266,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Cards cards,
|
public boolean choose(Outcome outcome, Cards cards,
|
||||||
TargetCard target, Game game
|
TargetCard target, Game game
|
||||||
) {
|
) {
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
for (String choose2 : choices) {
|
for (String choose2 : choices) {
|
||||||
|
@ -3302,7 +3302,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
|
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
|
||||||
Ability source, Game game
|
Ability source, Game game
|
||||||
) {
|
) {
|
||||||
// chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
|
// chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
|
||||||
// if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
|
// if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
|
||||||
|
@ -3314,7 +3314,7 @@ public class TestPlayer implements Player {
|
||||||
// skip targets
|
// skip targets
|
||||||
if (targets.get(0).equals(TARGET_SKIP)) {
|
if (targets.get(0).equals(TARGET_SKIP)) {
|
||||||
Assert.assertTrue("found skip target, but it require more targets, needs "
|
Assert.assertTrue("found skip target, but it require more targets, needs "
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
targets.remove(0);
|
targets.remove(0);
|
||||||
return false; // false in chooseTargetAmount = stop to choose
|
return false; // false in chooseTargetAmount = stop to choose
|
||||||
|
@ -3367,15 +3367,15 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choosePile(Outcome outcome, String message,
|
public boolean choosePile(Outcome outcome, String message,
|
||||||
List<? extends Card> pile1, List<? extends Card> pile2,
|
List<? extends Card> pile1, List<? extends Card> pile2,
|
||||||
Game game
|
Game game
|
||||||
) {
|
) {
|
||||||
return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
|
return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean playMana(Ability ability, ManaCost unpaid,
|
public boolean playMana(Ability ability, ManaCost unpaid,
|
||||||
String promptText, Game game
|
String promptText, Game game
|
||||||
) {
|
) {
|
||||||
groupsForTargetHandling = null;
|
groupsForTargetHandling = null;
|
||||||
return computerPlayer.playMana(ability, unpaid, promptText, game);
|
return computerPlayer.playMana(ability, unpaid, promptText, game);
|
||||||
|
@ -3389,15 +3389,15 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
|
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
|
||||||
List<UUID> blockerOrder, Game game
|
List<UUID> blockerOrder, Game game
|
||||||
) {
|
) {
|
||||||
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void assignDamage(int damage, List<UUID> targets,
|
public void assignDamage(int damage, List<UUID> targets,
|
||||||
String singleTargetName, UUID sourceId,
|
String singleTargetName, UUID sourceId,
|
||||||
Game game
|
Game game
|
||||||
) {
|
) {
|
||||||
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
|
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
|
||||||
}
|
}
|
||||||
|
@ -3416,14 +3416,14 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pickCard(List<Card> cards, Deck deck,
|
public void pickCard(List<Card> cards, Deck deck,
|
||||||
Draft draft
|
Draft draft
|
||||||
) {
|
) {
|
||||||
computerPlayer.pickCard(cards, deck, draft);
|
computerPlayer.pickCard(cards, deck, draft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean scry(int value, Ability source,
|
public boolean scry(int value, Ability source,
|
||||||
Game game
|
Game game
|
||||||
) {
|
) {
|
||||||
// Don't scry at the start of the game.
|
// Don't scry at the start of the game.
|
||||||
if (game.getTurnNum() == 1 && game.getStep() == null) {
|
if (game.getTurnNum() == 1 && game.getStep() == null) {
|
||||||
|
@ -3434,44 +3434,44 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean surveil(int value, Ability source,
|
public boolean surveil(int value, Ability source,
|
||||||
Game game
|
Game game
|
||||||
) {
|
) {
|
||||||
return computerPlayer.surveil(value, source, game);
|
return computerPlayer.surveil(value, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Card card, Zone toZone,
|
public boolean moveCards(Card card, Zone toZone,
|
||||||
Ability source, Game game
|
Ability source, Game game
|
||||||
) {
|
) {
|
||||||
return computerPlayer.moveCards(card, toZone, source, game);
|
return computerPlayer.moveCards(card, toZone, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Card card, Zone toZone,
|
public boolean moveCards(Card card, Zone toZone,
|
||||||
Ability source, Game game,
|
Ability source, Game game,
|
||||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||||
) {
|
) {
|
||||||
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
|
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Cards cards, Zone toZone,
|
public boolean moveCards(Cards cards, Zone toZone,
|
||||||
Ability source, Game game
|
Ability source, Game game
|
||||||
) {
|
) {
|
||||||
return computerPlayer.moveCards(cards, toZone, source, game);
|
return computerPlayer.moveCards(cards, toZone, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||||
Ability source, Game game
|
Ability source, Game game
|
||||||
) {
|
) {
|
||||||
return computerPlayer.moveCards(cards, toZone, source, game);
|
return computerPlayer.moveCards(cards, toZone, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||||
Ability source, Game game,
|
Ability source, Game game,
|
||||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||||
) {
|
) {
|
||||||
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
|
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
|
@ -21,27 +19,29 @@ import mage.target.targetpointer.FixedTarget;
|
||||||
/**
|
/**
|
||||||
* 702.53. Haunt
|
* 702.53. Haunt
|
||||||
*
|
*
|
||||||
* 702.53a Haunt is a triggered ability. "Haunt" on a permanent means "When this permanent is put
|
* 702.53a Haunt is a triggered ability. "Haunt" on a permanent means "When this
|
||||||
* into a graveyard from the battlefield, exile it haunting target creature."
|
* permanent is put into a graveyard from the battlefield, exile it haunting
|
||||||
* "Haunt" on an instant or sorcery spell means "When this spell is put into a graveyard during
|
* target creature." "Haunt" on an instant or sorcery spell means "When this
|
||||||
* its resolution, exile it haunting target creature."
|
* spell is put into a graveyard during its resolution, exile it haunting target
|
||||||
|
* creature."
|
||||||
*
|
*
|
||||||
* 702.53b Cards that are in the exile zone as the result of a haunt ability "haunt" the creature
|
* 702.53b Cards that are in the exile zone as the result of a haunt ability
|
||||||
* targeted by that ability. The phrase "creature it haunts" refers to the object targeted by the
|
* "haunt" the creature targeted by that ability. The phrase "creature it
|
||||||
* haunt ability, regardless of whether or not that object is still a creature.
|
* haunts" refers to the object targeted by the haunt ability, regardless of
|
||||||
|
* whether or not that object is still a creature.
|
||||||
*
|
*
|
||||||
* 702.53c Triggered abilities of cards with haunt that refer to the haunted creature can trigger in the exile zone.
|
* 702.53c Triggered abilities of cards with haunt that refer to the haunted
|
||||||
|
* creature can trigger in the exile zone.
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class HauntAbility extends TriggeredAbilityImpl {
|
public class HauntAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
private boolean usedFromExile = false;
|
private boolean usedFromExile = false;
|
||||||
private boolean creatureHaunt;
|
private boolean creatureHaunt;
|
||||||
|
|
||||||
public HauntAbility(Card card, Effect effect) {
|
public HauntAbility(Card card, Effect effect) {
|
||||||
super(Zone.ALL, effect , false);
|
super(Zone.ALL, effect, false);
|
||||||
creatureHaunt = card.isCreature();
|
creatureHaunt = card.isCreature();
|
||||||
addSubAbility(new HauntExileAbility(creatureHaunt));
|
addSubAbility(new HauntExileAbility(creatureHaunt));
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ public class HauntAbility extends TriggeredAbilityImpl {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
switch (event.getType()) {
|
switch (event.getType()) {
|
||||||
|
@ -76,16 +76,24 @@ public class HauntAbility extends TriggeredAbilityImpl {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZONE_CHANGE:
|
case ZONE_CHANGE:
|
||||||
if (!usedFromExile && game.getState().getZone(getSourceId()) == Zone.EXILED) {
|
if (!usedFromExile
|
||||||
|
&& game.getState().getZone(getSourceId()) == Zone.EXILED) {
|
||||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||||
if (zEvent.isDiesEvent()) {
|
if (zEvent.isDiesEvent()) {
|
||||||
Card card = game.getCard(getSourceId());
|
Card card = game.getCard(getSourceId());
|
||||||
if (card != null) {
|
if (card != null
|
||||||
String key = new StringBuilder("Haunting_").append(getSourceId().toString()).append('_').append(card.getZoneChangeCounter(game)).toString();
|
&& game.getCard(event.getTargetId()) != null) {
|
||||||
|
String key = new StringBuilder("Haunting_")
|
||||||
|
.append(getSourceId().toString())
|
||||||
|
.append(card.getZoneChangeCounter(game)
|
||||||
|
+ (game.getPermanentOrLKIBattlefield(event.getTargetId()))
|
||||||
|
.getZoneChangeCounter(game)).toString();
|
||||||
Object object = game.getState().getValue(key);
|
Object object = game.getState().getValue(key);
|
||||||
if (object instanceof FixedTarget) {
|
if (object instanceof FixedTarget) {
|
||||||
FixedTarget target = (FixedTarget) object;
|
FixedTarget target = (FixedTarget) object;
|
||||||
if (target.getTarget() != null && target.getTarget().equals(event.getTargetId())) {
|
if (target.getTarget() != null
|
||||||
|
&& target.getTarget()
|
||||||
|
.equals(event.getTargetId())) {
|
||||||
usedFromExile = true;
|
usedFromExile = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -102,23 +110,24 @@ public class HauntAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return (creatureHaunt ? "When {this} enters the battlefield or the creature it haunts dies, " :
|
return (creatureHaunt ? "When {this} enters the battlefield or the creature it haunts dies, "
|
||||||
"When the creature {this} haunts dies, ")
|
: "When the creature {this} haunts dies, ")
|
||||||
+ super.getRule();
|
+ super.getRule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HauntExileAbility extends ZoneChangeTriggeredAbility {
|
class HauntExileAbility extends ZoneChangeTriggeredAbility {
|
||||||
|
|
||||||
private static final String RULE_TEXT_CREATURE = "Haunt <i>(When this creature dies, exile it haunting target creature.)</i>";
|
private static final String RULE_TEXT_CREATURE = "Haunt <i>(When this creature dies, "
|
||||||
private static final String RULE_TEXT_SPELL = "Haunt <i>(When this spell card is put into a graveyard after resolving, exile it haunting target creature.)</i>";
|
+ "exile it haunting target creature.)</i>";
|
||||||
|
private static final String RULE_TEXT_SPELL = "Haunt <i>(When this spell card is put "
|
||||||
|
+ "into a graveyard after resolving, exile it haunting target creature.)</i>";
|
||||||
|
|
||||||
private boolean creatureHaunt;
|
private boolean creatureHaunt;
|
||||||
|
|
||||||
// TODO: It's not checked yet, if the Haunt spell has resolved (and was not countered or removed from stack).
|
// TODO: It's not checked yet, if the Haunt spell has resolved (and was not countered or removed from stack).
|
||||||
|
|
||||||
public HauntExileAbility(boolean creatureHaunt) {
|
public HauntExileAbility(boolean creatureHaunt) {
|
||||||
super(creatureHaunt ? Zone.BATTLEFIELD: Zone.STACK, Zone.GRAVEYARD, new HauntEffect(), null, false);
|
super(creatureHaunt ? Zone.BATTLEFIELD : Zone.STACK, Zone.GRAVEYARD, new HauntEffect(), null, false);
|
||||||
this.creatureHaunt = creatureHaunt;
|
this.creatureHaunt = creatureHaunt;
|
||||||
this.setRuleAtTheTop(creatureHaunt);
|
this.setRuleAtTheTop(creatureHaunt);
|
||||||
this.addTarget(new TargetCreaturePermanent());
|
this.addTarget(new TargetCreaturePermanent());
|
||||||
|
@ -131,19 +140,22 @@ class HauntExileAbility extends ZoneChangeTriggeredAbility {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||||
boolean fromOK = true;
|
boolean fromOK = true;
|
||||||
Permanent sourcePermanent = (Permanent) game.getLastKnownInformation(sourceId, Zone.BATTLEFIELD);
|
Permanent sourcePermanent = (Permanent) game.getLastKnownInformation(sourceId, Zone.BATTLEFIELD);
|
||||||
if (creatureHaunt && sourcePermanent == null) {
|
if (creatureHaunt
|
||||||
|
&& sourcePermanent == null) {
|
||||||
// check it was previously on battlefield
|
// check it was previously on battlefield
|
||||||
fromOK = false;
|
fromOK = false;
|
||||||
}
|
}
|
||||||
if (!this.hasSourceObjectAbility(game, sourcePermanent, event)) {
|
if (!this.hasSourceObjectAbility(game, sourcePermanent, event)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// check now it is in graveyard
|
// check now it is in graveyard
|
||||||
Zone after = game.getState().getZone(sourceId);
|
Zone after = game.getState().getZone(sourceId);
|
||||||
return fromOK && after != null && Zone.GRAVEYARD.match(after);
|
return fromOK
|
||||||
|
&& after != null
|
||||||
|
&& Zone.GRAVEYARD.match(after);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -182,7 +194,10 @@ class HauntEffect extends OneShotEffect {
|
||||||
if (hauntedCreature != null) {
|
if (hauntedCreature != null) {
|
||||||
if (card.moveToExile(source.getSourceId(), "Haunting", source.getSourceId(), game)) {
|
if (card.moveToExile(source.getSourceId(), "Haunting", source.getSourceId(), game)) {
|
||||||
// remember the haunted creature
|
// remember the haunted creature
|
||||||
String key = new StringBuilder("Haunting_").append(source.getSourceId().toString()).append('_').append(card.getZoneChangeCounter(game)).toString();
|
String key = new StringBuilder("Haunting_")
|
||||||
|
.append(source.getSourceId().toString())
|
||||||
|
.append(card.getZoneChangeCounter(game)
|
||||||
|
+ hauntedCreature.getZoneChangeCounter(game)).toString(); // in case it is blinked
|
||||||
game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source)));
|
game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source)));
|
||||||
card.addInfo("hauntinfo", new StringBuilder("Haunting ").append(hauntedCreature.getLogName()).toString(), game);
|
card.addInfo("hauntinfo", new StringBuilder("Haunting ").append(hauntedCreature.getLogName()).toString(), game);
|
||||||
hauntedCreature.addInfo("hauntinfo", new StringBuilder("Haunted by ").append(card.getLogName()).toString(), game);
|
hauntedCreature.addInfo("hauntinfo", new StringBuilder("Haunted by ").append(card.getLogName()).toString(), game);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.InfoEffect;
|
import mage.abilities.effects.common.InfoEffect;
|
||||||
|
@ -16,9 +18,6 @@ import mage.players.Player;
|
||||||
import mage.watchers.common.CommanderInfoWatcher;
|
import mage.watchers.common.CommanderInfoWatcher;
|
||||||
import mage.watchers.common.CommanderPlaysCountWatcher;
|
import mage.watchers.common.CommanderPlaysCountWatcher;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public abstract class GameCommanderImpl extends GameImpl {
|
public abstract class GameCommanderImpl extends GameImpl {
|
||||||
|
|
||||||
// private final Map<UUID, Cards> mulliganedCards = new HashMap<>();
|
// private final Map<UUID, Cards> mulliganedCards = new HashMap<>();
|
||||||
|
@ -78,7 +77,9 @@ public abstract class GameCommanderImpl extends GameImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initCommander(Card commander, Player player) {
|
public void initCommander(Card commander, Player player) {
|
||||||
commander.moveToZone(Zone.COMMAND, null, this, true);
|
if (!Zone.EXILED.equals(getState().getZone(commander.getId()))) { // Exile check needed for Karn Liberated restart
|
||||||
|
commander.moveToZone(Zone.COMMAND, null, this, true);
|
||||||
|
}
|
||||||
commander.getAbilities().setControllerId(player.getId());
|
commander.getAbilities().setControllerId(player.getId());
|
||||||
|
|
||||||
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
|
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package mage.watchers.common;
|
package mage.watchers.common;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.WatcherScope;
|
import mage.constants.WatcherScope;
|
||||||
|
@ -11,10 +14,6 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/* 20130711
|
/* 20130711
|
||||||
*903.14a A player that's been dealt 21 or more combat damage by the same commander
|
*903.14a A player that's been dealt 21 or more combat damage by the same commander
|
||||||
* over the course of the game loses the game. (This is a state-based action. See rule 704.)
|
* over the course of the game loses the game. (This is a state-based action. See rule 704.)
|
||||||
|
@ -69,7 +68,7 @@ public class CommanderInfoWatcher extends Watcher {
|
||||||
}
|
}
|
||||||
if (object != null) {
|
if (object != null) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("<b>" + commanderTypeName + "</b>");
|
sb.append("<b>").append(commanderTypeName).append("</b>");
|
||||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
int playsCount = watcher.getPlaysCount(sourceId);
|
int playsCount = watcher.getPlaysCount(sourceId);
|
||||||
if (playsCount > 0) {
|
if (playsCount > 0) {
|
||||||
|
@ -80,7 +79,7 @@ public class CommanderInfoWatcher extends Watcher {
|
||||||
if (checkCommanderDamage) {
|
if (checkCommanderDamage) {
|
||||||
for (Map.Entry<UUID, Integer> entry : damageToPlayer.entrySet()) {
|
for (Map.Entry<UUID, Integer> entry : damageToPlayer.entrySet()) {
|
||||||
Player damagedPlayer = game.getPlayer(entry.getKey());
|
Player damagedPlayer = game.getPlayer(entry.getKey());
|
||||||
sb.append("<b>" + commanderTypeName + "</b> did ").append(entry.getValue()).append(" combat damage to player ").append(damagedPlayer.getLogName()).append('.');
|
sb.append("<b>").append(commanderTypeName).append("</b> did ").append(entry.getValue()).append(" combat damage to player ").append(damagedPlayer.getLogName()).append('.');
|
||||||
this.addInfo(object, "Commander" + entry.getKey(),
|
this.addInfo(object, "Commander" + entry.getKey(),
|
||||||
"<b>" + commanderTypeName + "</b> did " + entry.getValue() + " combat damage to player " + damagedPlayer.getLogName() + '.', game);
|
"<b>" + commanderTypeName + "</b> did " + entry.getValue() + " combat damage to player " + damagedPlayer.getLogName() + '.', game);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue