diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/CardDownloadData.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/CardDownloadData.java index 6755f978ea..aacde1c663 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/CardDownloadData.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/CardDownloadData.java @@ -76,7 +76,11 @@ public class CardDownloadData { this.tokenDescriptor = card.tokenDescriptor; this.tokenClassName = card.tokenClassName; this.fileName = card.fileName; - + this.splitCard = card.splitCard; + this.flipCard = card.flipCard; + this.flippedSide = card.flippedSide; + this.downloadName = card.downloadName; + this.isType2 = card.isType2; } @Override diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java b/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java index efa5ee8b89..6abc2577f6 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java +++ b/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java @@ -31,6 +31,7 @@ public class MomirGame extends GameImpl { public MomirGame(final MomirGame game) { super(game); + this.numPlayers = game.numPlayers; } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index 770d3b0aae..b05e7d1f8a 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -68,7 +68,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { protected Set actionCache; private static final List optimizers = new ArrayList<>(); - protected int lastLoggedTurn = 0; + protected int lastLoggedTurn; protected static final String BLANKS = "..............................................."; static { @@ -80,11 +80,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { public ComputerPlayer6(String name, RangeOfInfluence range, int skill) { super(name, range); - if (skill < 4) { - maxDepth = 4; - } else { - maxDepth = skill; - } + maxDepth = Math.max(skill, 4); maxThink = skill * 3; maxNodes = Config2.maxNodes; getSuggestedActions(); @@ -94,7 +90,11 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { public ComputerPlayer6(final ComputerPlayer6 player) { super(player); this.maxDepth = player.maxDepth; + this.maxNodes = player.maxNodes; + this.maxThink = player.maxThink; + this.lastLoggedTurn = player.lastLoggedTurn; this.currentScore = player.currentScore; + this.root = player.root; if (player.combat != null) { this.combat = player.combat.copy(); } @@ -102,6 +102,8 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { this.targets.addAll(player.targets); this.choices.addAll(player.choices); this.actionCache = player.actionCache; + this.attackersList.addAll(player.attackersList); + this.attackersToCheck.addAll(player.attackersToCheck); } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index ee208d00d6..4f5a053af6 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -58,6 +58,7 @@ public class SimulatedPlayer2 extends ComputerPlayer { this.suggested = new ArrayList<>(player.suggested); // this.allActions = player.allActions; // dynamic, no need to copy this.originalPlayer = player.originalPlayer.copy(); + this.forced = player.forced; } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 8393ebf864..94f38ddb64 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -67,7 +67,7 @@ import java.util.Map.Entry; public class ComputerPlayer extends PlayerImpl implements Player { private static final Logger log = Logger.getLogger(ComputerPlayer.class); - private long lastThinkTime = 0; // msecs for last AI actions calc + private long lastThinkTime; // msecs for last AI actions calc protected int PASSIVITY_PENALTY = 5; // Penalty value for doing nothing if some actions are available @@ -105,6 +105,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { public ComputerPlayer(final ComputerPlayer player) { super(player); + this.lastThinkTime = player.lastThinkTime; + this.PASSIVITY_PENALTY = player.PASSIVITY_PENALTY; + this.COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS = player.COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS; } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java index b9eba1c9e1..b9ae6f783d 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java @@ -31,10 +31,13 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { private static final int THINK_MAX_RATIO = 100; private static final double THINK_TIME_MULTIPLIER = 2.0; private static final boolean USE_MULTIPLE_THREADS = true; + private static final Logger logger = Logger.getLogger(ComputerPlayerMCTS.class); protected transient MCTSNode root; + protected String lastPhase = ""; + protected long totalThinkTime; + protected long totalSimulations; protected int maxThinkTime; - private static final Logger logger = Logger.getLogger(ComputerPlayerMCTS.class); private int poolSize; public ComputerPlayerMCTS(String name, RangeOfInfluence range, int skill) { @@ -50,6 +53,11 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { public ComputerPlayerMCTS(final ComputerPlayerMCTS player) { super(player); + this.maxThinkTime = player.maxThinkTime; + this.poolSize = player.poolSize; + this.lastPhase = player.lastPhase; + this.totalSimulations = player.totalSimulations; + this.totalThinkTime = player.totalThinkTime; } @Override @@ -57,8 +65,6 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { return new ComputerPlayerMCTS(this); } - protected String lastPhase = ""; - @Override public boolean priority(Game game) { if (game.getStep().getType() == PhaseStep.UPKEEP) { @@ -152,9 +158,6 @@ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { MCTSNode.logHitMiss(); } - protected long totalThinkTime = 0; - protected long totalSimulations = 0; - protected void applyMCTS(final Game game, final NextAction action) { int thinkTime = calculateThinkTime(game, action); diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 4eb502940c..0a113e3d13 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -30,8 +30,8 @@ import java.util.*; */ public class SimulatedPlayerMCTS extends MCTSPlayer { - private boolean isSimulatedPlayer; - private int actionCount = 0; + private final boolean isSimulatedPlayer; + private int actionCount; private static final Logger logger = Logger.getLogger(SimulatedPlayerMCTS.class); public SimulatedPlayerMCTS(Player originalPlayer, boolean isSimulatedPlayer) { @@ -43,6 +43,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { public SimulatedPlayerMCTS(final SimulatedPlayerMCTS player) { super(player); this.isSimulatedPlayer = player.isSimulatedPlayer; + this.actionCount = player.actionCount; } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java index 18c518cbd2..a287d26edc 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java @@ -40,8 +40,8 @@ public class ComputerPlayer2 extends ComputerPlayer implements Player { protected int maxDepth; protected int maxNodes; protected int maxThink; - protected int nodeCount = 0; - protected long thinkTime = 0; + protected int nodeCount; + protected long thinkTime; protected transient LinkedList actions = new LinkedList<>(); protected transient List targets = new ArrayList<>(); protected transient List choices = new ArrayList<>(); @@ -59,6 +59,10 @@ public class ComputerPlayer2 extends ComputerPlayer implements Player { public ComputerPlayer2(final ComputerPlayer2 player) { super(player); this.maxDepth = player.maxDepth; + this.maxNodes = player.maxNodes; + this.maxThink = player.maxThink; + this.nodeCount = player.nodeCount; + this.thinkTime = player.thinkTime; this.currentScore = player.currentScore; if (player.combat != null) { this.combat = player.combat.copy(); diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java index 46fc0c4e6e..44d07e484c 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -28,9 +28,9 @@ import java.util.concurrent.ConcurrentLinkedQueue; public class SimulatedPlayer extends ComputerPlayer { private static final Logger logger = Logger.getLogger(SimulatedPlayer.class); - private boolean isSimulatedPlayer; + private final boolean isSimulatedPlayer; private transient ConcurrentLinkedQueue allActions; - private static PassAbility pass = new PassAbility(); + private static final PassAbility pass = new PassAbility(); protected int maxDepth; public SimulatedPlayer(Player originalPlayer, boolean isSimulatedPlayer, int maxDepth) { @@ -44,6 +44,7 @@ public class SimulatedPlayer extends ComputerPlayer { public SimulatedPlayer(final SimulatedPlayer player) { super(player); this.isSimulatedPlayer = player.isSimulatedPlayer; + this.maxDepth = player.maxDepth; } @Override diff --git a/Mage.Sets/src/mage/cards/c/Carom.java b/Mage.Sets/src/mage/cards/c/Carom.java index 71b8629453..4f5717640e 100644 --- a/Mage.Sets/src/mage/cards/c/Carom.java +++ b/Mage.Sets/src/mage/cards/c/Carom.java @@ -61,8 +61,9 @@ class CaromEffect extends RedirectionEffect { staticText = "The next " + amount + " damage that would be dealt to target creature this turn is dealt to another target creature instead"; } - public CaromEffect(final CaromEffect effect) { + private CaromEffect(final CaromEffect effect) { super(effect); + this.redirectToObject = effect.redirectToObject; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DesecratorHag.java b/Mage.Sets/src/mage/cards/d/DesecratorHag.java index d10c71c298..e79cdf9a3e 100644 --- a/Mage.Sets/src/mage/cards/d/DesecratorHag.java +++ b/Mage.Sets/src/mage/cards/d/DesecratorHag.java @@ -30,7 +30,8 @@ public final class DesecratorHag extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // When Desecrator Hag enters the battlefield, return to your hand the creature card in your graveyard with the greatest power. If two or more cards are tied for greatest power, you choose one of them. + // When Desecrator Hag enters the battlefield, return to your hand the creature card in your graveyard with the greatest power. + // If two or more cards are tied for greatest power, you choose one of them. this.addAbility(new EntersBattlefieldTriggeredAbility(new DesecratorHagEffect(), false)); } @@ -47,17 +48,21 @@ public final class DesecratorHag extends CardImpl { class DesecratorHagEffect extends OneShotEffect { - int creatureGreatestPower = 0; + int creatureGreatestPower; Cards cards = new CardsImpl(); TargetCard target = new TargetCard(Zone.GRAVEYARD, new FilterCard()); public DesecratorHagEffect() { super(Outcome.DrawCard); - this.staticText = "return to your hand the creature card in your graveyard with the greatest power. If two or more cards are tied for greatest power, you choose one of them"; + this.staticText = "return to your hand the creature card in your graveyard with the greatest power. " + + "If two or more cards are tied for greatest power, you choose one of them"; } public DesecratorHagEffect(final DesecratorHagEffect effect) { super(effect); + this.creatureGreatestPower = effect.creatureGreatestPower; + this.cards = effect.cards.copy(); + this.target = effect.target.copy(); } @Override @@ -68,35 +73,40 @@ class DesecratorHagEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - for (Card card : you.getGraveyard().getCards(game)) { - if (card.isCreature(game)) { - if (card.getPower().getValue() > creatureGreatestPower) { - creatureGreatestPower = card.getPower().getValue(); - cards.clear(); + if (you == null) { + return false; + } + + for (Card card : you.getGraveyard().getCards(game)) { + if (card.isCreature(game)) { + if (card.getPower().getValue() > creatureGreatestPower) { + creatureGreatestPower = card.getPower().getValue(); + cards.clear(); + cards.add(card); + } else { + if (card.getPower().getValue() == creatureGreatestPower) { cards.add(card); - } else { - if (card.getPower().getValue() == creatureGreatestPower) { - cards.add(card); - } } } } - if (cards.isEmpty()) { - return true; - } - if (cards.size() > 1 - && you.choose(Outcome.DrawCard, cards, target, game)) { - if (target != null) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - return you.moveCards(card, Zone.HAND, source, game); - } - } - } else { - return you.moveCards(cards, Zone.HAND, source, game); - } } - return false; + + if (cards.isEmpty()) { + return false; + } else if (cards.size() == 1) { + return you.moveCards(cards, Zone.HAND, source, game); + } else { + if (you.choose(Outcome.DrawCard, cards, target, game)) { + if (target == null) { + return false; + } + + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + return you.moveCards(card, Zone.HAND, source, game); + } + } + return false; + } } } diff --git a/Mage.Sets/src/mage/cards/d/DesperateGambit.java b/Mage.Sets/src/mage/cards/d/DesperateGambit.java index d516d09dfd..e0d26b90a0 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateGambit.java +++ b/Mage.Sets/src/mage/cards/d/DesperateGambit.java @@ -33,7 +33,9 @@ public final class DesperateGambit extends CardImpl { public DesperateGambit(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); - // Choose a source you control and flip a coin. If you win the flip, the next time that source would deal damage this turn, it deals double that damage instead. If you lose the flip, the next time it would deal damage this turn, prevent that damage. + // Choose a source you control and flip a coin. + // If you win the flip, the next time that source would deal damage this turn, it deals double that damage instead. + // If you lose the flip, the next time it would deal damage this turn, prevent that damage. this.getSpellAbility().addEffect(new DesperateGambitEffect()); } @@ -54,13 +56,16 @@ class DesperateGambitEffect extends PreventionEffectImpl { public DesperateGambitEffect() { super(Duration.EndOfTurn, Integer.MAX_VALUE, false); - staticText = "Choose a source you control and flip a coin. If you win the flip, the next time that source would deal damage this turn, it deals double that damage instead. If you lose the flip, the next time it would deal damage this turn, prevent that damage"; + staticText = "Choose a source you control and flip a coin. " + + "If you win the flip, the next time that source would deal damage this turn, it deals double that damage instead. " + + "If you lose the flip, the next time it would deal damage this turn, prevent that damage"; this.target = new TargetControlledSource(); } public DesperateGambitEffect(final DesperateGambitEffect effect) { super(effect); this.target = effect.target.copy(); + this.wonFlip = effect.wonFlip; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DoublingSeason.java b/Mage.Sets/src/mage/cards/d/DoublingSeason.java index 0a174d172f..86eecd59dc 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingSeason.java +++ b/Mage.Sets/src/mage/cards/d/DoublingSeason.java @@ -80,7 +80,7 @@ class DoublingSeasonTokenEffect extends ReplacementEffectImpl { class DoublingSeasonCounterEffect extends ReplacementEffectImpl { - private boolean landPlayed = false; // a played land is not an effect + private boolean landPlayed; // a played land is not an effect DoublingSeasonCounterEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); @@ -89,6 +89,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl { private DoublingSeasonCounterEffect(final DoublingSeasonCounterEffect effect) { super(effect); + this.landPlayed = effect.landPlayed; } @Override @@ -110,8 +111,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl { } if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); - landPlayed = (permanent != null - && permanent.isLand(game)); // a played land is not an effect + landPlayed = (permanent != null && permanent.isLand(game)); // a played land is not an effect } return permanent != null && permanent.isControlledBy(source.getControllerId()) diff --git a/Mage.Sets/src/mage/cards/d/DreadWight.java b/Mage.Sets/src/mage/cards/d/DreadWight.java index 9c2d509f31..953bd0dd0f 100644 --- a/Mage.Sets/src/mage/cards/d/DreadWight.java +++ b/Mage.Sets/src/mage/cards/d/DreadWight.java @@ -8,6 +8,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; import mage.abilities.effects.ContinuousEffect; @@ -101,7 +102,7 @@ class DreadWightTriggeredAbility extends TriggeredAbilityImpl { class DreadWightEffect extends OneShotEffect { - String rule = "doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it."; + static final String rule = "doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it."; public DreadWightEffect() { super(Outcome.Detriment); @@ -122,44 +123,42 @@ class DreadWightEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent != null) { - // add paralyzation counter - Effect effect = new AddCountersTargetEffect(CounterType.PARALYZATION.createInstance()); - effect.setTargetPointer(new FixedTarget(permanent, game)); - effect.apply(game, source); - // tap permanent - permanent.tap(source, game); - // does not untap while paralyzation counter is on it - ContinuousRuleModifyingEffect effect2 = new DreadWightDoNotUntapEffect( - Duration.WhileOnBattlefield, - permanent.getId()); - effect2.setText("This creature doesn't untap during its controller's untap step for as long as it has a paralyzation counter on it"); - Condition condition = new DreadWightCounterCondition(permanent.getId()); - ConditionalContinuousRuleModifyingEffect conditionalEffect = new ConditionalContinuousRuleModifyingEffect( - effect2, - condition); - Ability ability = new SimpleStaticAbility( - Zone.BATTLEFIELD, - conditionalEffect); - ContinuousEffect effect3 = new GainAbilityTargetEffect( - ability, - Duration.WhileOnBattlefield); - ability.setRuleVisible(true); - effect3.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect3, source); - // each gains 4: remove paralyzation counter - Ability activatedAbility = new SimpleActivatedAbility( - Zone.BATTLEFIELD, - new RemoveCounterSourceEffect(CounterType.PARALYZATION.createInstance()), - new ManaCostsImpl("{4}")); - ContinuousEffect effect4 = new GainAbilityTargetEffect( - activatedAbility, - Duration.WhileOnBattlefield); - effect4.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect4, source); - return true; + if (permanent == null) { + return false; } - return false; + // add paralyzation counter + Effect effect = new AddCountersTargetEffect(CounterType.PARALYZATION.createInstance()); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + + // tap permanent + permanent.tap(source, game); + + // does not untap while paralyzation counter is on it + ContinuousRuleModifyingEffect effect2 = new DreadWightDoNotUntapEffect(Duration.WhileOnBattlefield, permanent.getId()); + effect2.setText(rule); + + Condition condition = new DreadWightCounterCondition(permanent.getId()); + ConditionalContinuousRuleModifyingEffect conditionalEffect = new ConditionalContinuousRuleModifyingEffect( + effect2, + condition); + + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, conditionalEffect); + ContinuousEffect effect3 = new GainAbilityTargetEffect(ability, Duration.WhileOnBattlefield); + ability.setRuleVisible(true); + effect3.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect3, source); + + // each gains 4: remove paralyzation counter + Ability activatedAbility = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new RemoveCounterSourceEffect(CounterType.PARALYZATION.createInstance()), + new GenericManaCost(4)); + ContinuousEffect effect4 = new GainAbilityTargetEffect(activatedAbility, Duration.WhileOnBattlefield); + effect4.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect4, source); + + return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DubiousChallenge.java b/Mage.Sets/src/mage/cards/d/DubiousChallenge.java index 2ab0539038..da593c9b8b 100644 --- a/Mage.Sets/src/mage/cards/d/DubiousChallenge.java +++ b/Mage.Sets/src/mage/cards/d/DubiousChallenge.java @@ -97,12 +97,21 @@ class DubiousChallengeEffect extends OneShotEffect { class DubiousChallengeMoveToBattlefieldEffect extends OneShotEffect { + private Cards cards; + private Player player; + public DubiousChallengeMoveToBattlefieldEffect() { super(Outcome.Benefit); } public DubiousChallengeMoveToBattlefieldEffect(final DubiousChallengeMoveToBattlefieldEffect effect) { super(effect); + if (effect.cards != null) { + this.cards = effect.cards.copy(); + } + if (effect.player != null) { + this.player = effect.player.copy(); + } } @Override @@ -117,12 +126,10 @@ class DubiousChallengeMoveToBattlefieldEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (cards != null && player != null) { - return player.moveCards(cards, Zone.BATTLEFIELD, source, game); + if (cards == null || player == null) { + return false; } - return false; - } - private Cards cards; - private Player player; + return player.moveCards(cards, Zone.BATTLEFIELD, source, game); + } } diff --git a/Mage.Sets/src/mage/cards/e/ElkinBottle.java b/Mage.Sets/src/mage/cards/e/ElkinBottle.java index 0fb1583933..b12022a4ae 100644 --- a/Mage.Sets/src/mage/cards/e/ElkinBottle.java +++ b/Mage.Sets/src/mage/cards/e/ElkinBottle.java @@ -61,17 +61,21 @@ class ElkinBottleExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), CardUtil.createObjectRealtedWindowTitle(source, game, null)); - ContinuousEffect effect = new ElkinBottleCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), game)); - game.addEffect(effect, source); - } - return true; + if (controller == null) { + return false; } - return false; + + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + + controller.moveCardsToExile(card, source, game, true, source.getSourceId(), CardUtil.createObjectRealtedWindowTitle(source, game, null)); + ContinuousEffect effect = new ElkinBottleCastFromExileEffect(); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); + game.addEffect(effect, source); + + return true; } } @@ -86,6 +90,7 @@ class ElkinBottleCastFromExileEffect extends AsThoughEffectImpl { public ElkinBottleCastFromExileEffect(final ElkinBottleCastFromExileEffect effect) { super(effect); + this.sameStep = effect.sameStep; } @Override @@ -96,9 +101,9 @@ class ElkinBottleCastFromExileEffect extends AsThoughEffectImpl { @Override public boolean isInactive(Ability source, Game game) { if (game.getPhase().getStep().getType() == PhaseStep.UPKEEP) { - if (!sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - return true; - } + return !sameStep + && game.isActivePlayer(source.getControllerId()) + || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving(); } else { sameStep = false; } diff --git a/Mage.Sets/src/mage/cards/f/FoodChain.java b/Mage.Sets/src/mage/cards/f/FoodChain.java index 0baaf25c51..81e6a69c9d 100644 --- a/Mage.Sets/src/mage/cards/f/FoodChain.java +++ b/Mage.Sets/src/mage/cards/f/FoodChain.java @@ -34,7 +34,9 @@ public final class FoodChain extends CardImpl { public FoodChain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); - // Exile a creature you control: Add X mana of any one color, where X is the exiled creature's converted mana cost plus one. Spend this mana only to cast creature spells. + // Exile a creature you control: Add X mana of any one color, + // where X is the exiled creature's converted mana cost plus one. + // Spend this mana only to cast creature spells. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new FoodChainManaEffect(), new ExileTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_A_CREATURE, true))); this.addAbility(ability); @@ -68,11 +70,13 @@ class FoodChainManaEffect extends ManaEffect { ConditionalManaBuilder manaBuilder = new FoodChainManaBuilder(); FoodChainManaEffect() { - this.staticText = "Add X mana of any one color, where X is 1 plus the exiled creature's mana value. Spend this mana only to cast creature spells"; + this.staticText = "Add X mana of any one color, where X is 1 plus the exiled creature's mana value. " + + "Spend this mana only to cast creature spells"; } FoodChainManaEffect(final FoodChainManaEffect effect) { super(effect); + this.manaBuilder = effect.manaBuilder; } @Override @@ -83,21 +87,25 @@ class FoodChainManaEffect extends ManaEffect { @Override public List getNetMana(Game game, Ability source) { List netMana = new ArrayList<>(); - if (game != null) { - int cmc = -1; - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { - if (permanent.isCreature(game)) { - cmc = Math.max(cmc, permanent.getManaCost().manaValue()); - } - } - if (cmc != -1) { - netMana.add(manaBuilder.setMana(Mana.BlackMana(cmc + 1), source, game).build()); - netMana.add(manaBuilder.setMana(Mana.BlueMana(cmc + 1), source, game).build()); - netMana.add(manaBuilder.setMana(Mana.RedMana(cmc + 1), source, game).build()); - netMana.add(manaBuilder.setMana(Mana.GreenMana(cmc + 1), source, game).build()); - netMana.add(manaBuilder.setMana(Mana.WhiteMana(cmc + 1), source, game).build()); + if (game == null) { + return netMana; + } + + int cmc = -1; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + if (permanent.isCreature(game)) { + cmc = Math.max(cmc, permanent.getManaCost().manaValue()); } } + + if (cmc != -1) { + netMana.add(manaBuilder.setMana(Mana.BlackMana(cmc + 1), source, game).build()); + netMana.add(manaBuilder.setMana(Mana.BlueMana( cmc + 1), source, game).build()); + netMana.add(manaBuilder.setMana(Mana.RedMana( cmc + 1), source, game).build()); + netMana.add(manaBuilder.setMana(Mana.GreenMana(cmc + 1), source, game).build()); + netMana.add(manaBuilder.setMana(Mana.WhiteMana(cmc + 1), source, game).build()); + } + return netMana; } @@ -107,24 +115,27 @@ class FoodChainManaEffect extends ManaEffect { if (game == null) { return mana; } + Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int manaCostExiled = 0; - for (Cost cost : source.getCosts()) { - if (cost.isPaid() && cost instanceof ExileTargetCost) { - for (Card card : ((ExileTargetCost) cost).getPermanents()) { - manaCostExiled += card.getManaValue(); - } + if (controller == null) { + return mana; + } + + int manaCostExiled = 0; + for (Cost cost : source.getCosts()) { + if (cost.isPaid() && cost instanceof ExileTargetCost) { + for (Card card : ((ExileTargetCost) cost).getPermanents()) { + manaCostExiled += card.getManaValue(); } } - ChoiceColor choice = new ChoiceColor(); - if (!controller.choose(Outcome.PutManaInPool, choice, game)) { - return mana; - } - Mana chosen = choice.getMana(manaCostExiled + 1); - return manaBuilder.setMana(chosen, source, game).build(); } - return mana; - } + ChoiceColor choice = new ChoiceColor(); + if (!controller.choose(Outcome.PutManaInPool, choice, game)) { + return mana; + } + + Mana chosen = choice.getMana(manaCostExiled + 1); + return manaBuilder.setMana(chosen, source, game).build(); + } } diff --git a/Mage.Sets/src/mage/cards/f/FrayingSanity.java b/Mage.Sets/src/mage/cards/f/FrayingSanity.java index 13c3805d8c..54a492726e 100644 --- a/Mage.Sets/src/mage/cards/f/FrayingSanity.java +++ b/Mage.Sets/src/mage/cards/f/FrayingSanity.java @@ -86,7 +86,7 @@ class FrayingSanityTriggeredAbility extends TriggeredAbilityImpl { class FrayingSanityEffect extends OneShotEffect { - int xAmount = 0; + int xAmount; public FrayingSanityEffect() { super(Outcome.Detriment); @@ -95,6 +95,7 @@ class FrayingSanityEffect extends OneShotEffect { public FrayingSanityEffect(final FrayingSanityEffect effect) { super(effect); + this.xAmount = effect.xAmount; } @Override diff --git a/Mage.Sets/src/mage/cards/g/GabrielAngelfire.java b/Mage.Sets/src/mage/cards/g/GabrielAngelfire.java index 655a17127e..44466a51f0 100644 --- a/Mage.Sets/src/mage/cards/g/GabrielAngelfire.java +++ b/Mage.Sets/src/mage/cards/g/GabrielAngelfire.java @@ -33,7 +33,8 @@ public final class GabrielAngelfire extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // At the beginning of your upkeep, choose flying, first strike, trample, or rampage 3. Gabriel Angelfire gains that ability until your next upkeep. + // At the beginning of your upkeep, choose flying, first strike, trample, or rampage 3. + // Gabriel Angelfire gains that ability until your next upkeep. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new GabrielAngelfireGainAbilityEffect(), TargetController.YOU, false)); } @@ -64,9 +65,10 @@ class GabrielAngelfireGainAbilityEffect extends GainAbilitySourceEffect { staticText = "choose flying, first strike, trample, or rampage 3. {this} gains that ability until your next upkeep"; } - public GabrielAngelfireGainAbilityEffect(final GabrielAngelfireGainAbilityEffect effect) { + private GabrielAngelfireGainAbilityEffect(final GabrielAngelfireGainAbilityEffect effect) { super(effect); ability.newId(); + this.sameStep = effect.sameStep; } @Override @@ -77,9 +79,7 @@ class GabrielAngelfireGainAbilityEffect extends GainAbilitySourceEffect { @Override public boolean isInactive(Ability source, Game game) { if (game.getPhase().getStep().getType() == PhaseStep.UPKEEP) { - if (!sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - return true; - } + return !sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving(); } else { sameStep = false; } @@ -89,29 +89,32 @@ class GabrielAngelfireGainAbilityEffect extends GainAbilitySourceEffect { @Override public void init(Ability source, Game game) { super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Choice choice = new ChoiceImpl(true); - choice.setMessage("Choose one"); - choice.setChoices(choices); - if (controller.choose(outcome, choice, game)) { - switch (choice.getChoice()) { - case "First strike": - ability = FirstStrikeAbility.getInstance(); - break; - case "Trample": - ability = TrampleAbility.getInstance(); - break; - case "Rampage 3": - ability = new RampageAbility(3); - break; - default: - ability = FlyingAbility.getInstance(); - break; - } - } else { - discard(); + if (controller == null) { + return; + } + + Choice choice = new ChoiceImpl(true); + choice.setMessage("Choose one"); + choice.setChoices(choices); + if (controller.choose(outcome, choice, game)) { + switch (choice.getChoice()) { + case "First strike": + ability = FirstStrikeAbility.getInstance(); + break; + case "Trample": + ability = TrampleAbility.getInstance(); + break; + case "Rampage 3": + ability = new RampageAbility(3); + break; + default: + ability = FlyingAbility.getInstance(); + break; } + } else { + discard(); } } diff --git a/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java b/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java index fbdffde000..229026c186 100644 --- a/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java +++ b/Mage.Sets/src/mage/cards/g/GloomwidowsFeast.java @@ -50,11 +50,10 @@ public final class GloomwidowsFeast extends CardImpl { class GloomwidowsFeastEffect extends OneShotEffect { - boolean applied = false; - public GloomwidowsFeastEffect() { super(Outcome.DestroyPermanent); - this.staticText = "Destroy target creature with flying. If that creature was blue or black, create a 1/2 green Spider creature token with reach"; + this.staticText = "Destroy target creature with flying. " + + "If that creature was blue or black, create a 1/2 green Spider creature token with reach"; } public GloomwidowsFeastEffect(final GloomwidowsFeastEffect effect) { @@ -69,15 +68,20 @@ class GloomwidowsFeastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - targetCreature.destroy(source, game, false); - Permanent destroyedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - if (destroyedCreature.getColor(game).isBlue() - || destroyedCreature.getColor(game).isBlack()) { - SpiderToken token = new SpiderToken(); - token.putOntoBattlefield(1, game, source, source.getControllerId()); - return true; - } + if (targetCreature == null) { + return false; + } + + targetCreature.destroy(source, game, false); + Permanent destroyedCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (destroyedCreature == null) { + return false; + } + + if (destroyedCreature.getColor(game).isBlue() || destroyedCreature.getColor(game).isBlack()) { + SpiderToken token = new SpiderToken(); + token.putOntoBattlefield(1, game, source, source.getControllerId()); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java b/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java index 6deeeeca43..b237fa08ad 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java +++ b/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java @@ -1,5 +1,6 @@ package mage.cards.g; +import javafx.event.EventType; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -55,6 +56,7 @@ class GoblinPsychopathEffect extends ReplacementEffectImpl { public GoblinPsychopathEffect(final GoblinPsychopathEffect effect) { super(effect); + this.wonFlip = effect.wonFlip; } @Override @@ -88,10 +90,14 @@ class GoblinPsychopathEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); - if (controller == null || object == null - || !(this.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0)) { + if (object == null || controller == null) { return false; } + + if (!(this.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0)) { + return false; + } + DamageEvent damageEvent = (DamageEvent) event; if (!damageEvent.isCombatDamage() || wonFlip) { return false; diff --git a/Mage.Sets/src/mage/cards/g/Godsend.java b/Mage.Sets/src/mage/cards/g/Godsend.java index fb7a68cc1c..b1beab7bca 100644 --- a/Mage.Sets/src/mage/cards/g/Godsend.java +++ b/Mage.Sets/src/mage/cards/g/Godsend.java @@ -61,7 +61,7 @@ public final class Godsend extends CardImpl { class GodsendTriggeredAbility extends TriggeredAbilityImpl { - private Set possibleTargets = new HashSet<>(); + private final Set possibleTargets = new HashSet<>(); GodsendTriggeredAbility() { super(Zone.BATTLEFIELD, new GodsendExileEffect(), true); @@ -69,6 +69,7 @@ class GodsendTriggeredAbility extends TriggeredAbilityImpl { GodsendTriggeredAbility(final GodsendTriggeredAbility ability) { super(ability); + this.possibleTargets.addAll(ability.possibleTargets); } @Override @@ -153,13 +154,19 @@ class GodsendExileEffect extends OneShotEffect { Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (creature != null && controller != null && sourcePermanent != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - controller.moveCardToExileWithInfo(creature, exileId, - sourcePermanent.getIdName() + " (" + sourcePermanent.getZoneChangeCounter(game) + ')', source, game, Zone.BATTLEFIELD, true); - + if (creature == null || controller == null || sourcePermanent == null) { + return false; } - return false; + + UUID exileId = CardUtil.getCardExileZoneId(game, source); + return controller.moveCardToExileWithInfo( + creature, + exileId, + sourcePermanent.getIdName() + " (" + sourcePermanent.getZoneChangeCounter(game) + ')', + source, + game, + Zone.BATTLEFIELD, + true); } } @@ -190,19 +197,30 @@ class GodsendRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.CAST_SPELL && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - MageObject object = game.getObject(event.getSourceId()); - if (object != null) { - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if ((exileZone != null)) { - for (Card card : exileZone.getCards(game)) { - if ((card.getName().equals(object.getName()))) { - return true; - } - } - } + if (event.getType() != GameEvent.EventType.CAST_SPELL) { + return false; + } + + if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + return false; + } + + MageObject object = game.getObject(event.getSourceId()); + if (object == null) { + return false; + } + + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + if (exileZone == null) { + return false; + } + + for (Card card : exileZone.getCards(game)) { + if ((card.getName().equals(object.getName()))) { + return true; } } + return false; } } diff --git a/Mage.Sets/src/mage/cards/g/GrinningTotem.java b/Mage.Sets/src/mage/cards/g/GrinningTotem.java index 65c4191f30..a39d233235 100644 --- a/Mage.Sets/src/mage/cards/g/GrinningTotem.java +++ b/Mage.Sets/src/mage/cards/g/GrinningTotem.java @@ -102,6 +102,7 @@ class GrinningTotemMayPlayEffect extends AsThoughEffectImpl { public GrinningTotemMayPlayEffect(final GrinningTotemMayPlayEffect effect) { super(effect); + this.sameStep = effect.sameStep; } @Override @@ -112,9 +113,7 @@ class GrinningTotemMayPlayEffect extends AsThoughEffectImpl { @Override public boolean isInactive(Ability source, Game game) { if (game.getPhase().getStep().getType() == PhaseStep.UPKEEP) { - if (!sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - return true; - } + return !sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving(); } else { sameStep = false; } @@ -198,10 +197,10 @@ class GrinningTotemPutIntoGraveyardEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); ExileZone zone = game.getExile().getExileZone(exileZoneId); - if (controller != null && zone != null) { - return controller.moveCards(zone, Zone.GRAVEYARD, source, game); + if (controller == null || zone == null) { + return false; } - return false; - } + return controller.moveCards(zone, Zone.GRAVEYARD, source, game); + } } diff --git a/Mage.Sets/src/mage/cards/g/GuardDogs.java b/Mage.Sets/src/mage/cards/g/GuardDogs.java index 3659e5dc4a..e0b427cd6a 100644 --- a/Mage.Sets/src/mage/cards/g/GuardDogs.java +++ b/Mage.Sets/src/mage/cards/g/GuardDogs.java @@ -34,8 +34,9 @@ public final class GuardDogs extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // {2}{W}, {T}: Choose a permanent you control. Prevent all combat damage target creature would deal this turn if it shares a color with that permanent. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GuardDogsEffect(), new ManaCostsImpl("{2}{W}")); + // {2}{W}, {T}: Choose a permanent you control. + // Prevent all combat damage target creature would deal this turn if it shares a color with that permanent. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GuardDogsEffect(), new ManaCostsImpl<>("{2}{W}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); @@ -57,13 +58,14 @@ class GuardDogsEffect extends PreventionEffectImpl { public GuardDogsEffect() { super(Duration.EndOfTurn, Integer.MAX_VALUE, true); - this.staticText = "Choose a permanent you control. Prevent all combat damage target creature would deal this turn if it shares a color with that permanent"; + this.staticText = "Choose a permanent you control. " + + "Prevent all combat damage target creature would deal this turn if it shares a color with that permanent"; } - public GuardDogsEffect(final GuardDogsEffect effect) { + private GuardDogsEffect(final GuardDogsEffect effect) { super(effect); + this.controlledTarget = effect.controlledTarget.copy(); } - @Override public void init(Ability source, Game game) { @@ -72,7 +74,6 @@ class GuardDogsEffect extends PreventionEffectImpl { this.controlledTarget.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); } - @Override public GuardDogsEffect copy() { @@ -81,20 +82,21 @@ class GuardDogsEffect extends PreventionEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - MageObject mageObject = game.getObject(event.getSourceId()); - if (mageObject != null - && controlledTarget.getFirstTarget() != null) { - Permanent permanent = game.getPermanentOrLKIBattlefield(controlledTarget.getFirstTarget()); - Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - if (permanent != null - && targetPermanent != null - && this.getTargetPointer().getTargets(game, source).contains(event.getSourceId()) - && permanent.getColor(game).shares(targetPermanent.getColor(game))) { - return true; - } - } + if (this.used || !super.applies(event, source, game)) { + return false; } - return false; + MageObject mageObject = game.getObject(event.getSourceId()); + if (mageObject == null || controlledTarget.getFirstTarget() == null) { + return false; + } + + Permanent permanent = game.getPermanentOrLKIBattlefield(controlledTarget.getFirstTarget()); + Permanent targetPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent == null || targetPermanent == null) { + return false; + } + + return this.getTargetPointer().getTargets(game, source).contains(event.getSourceId()) + && permanent.getColor(game).shares(targetPermanent.getColor(game)); } } diff --git a/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java b/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java index 84d856df42..87b4317688 100644 --- a/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java +++ b/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java @@ -40,7 +40,7 @@ public final class HisokaMinamoSensei extends CardImpl { this.toughness = new MageInt(3); // {2}{U}, Discard a card: Counter target spell if it has the same converted mana cost as the discarded card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HisokaMinamoSenseiCounterEffect(), new ManaCostsImpl("{2}{U}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HisokaMinamoSenseiCounterEffect(), new ManaCostsImpl<>("{2}{U}")); ability.addTarget(new TargetSpell()); TargetCardInHand targetCard = new TargetCardInHand(new FilterCard("a card")); ability.addCost(new HisokaMinamoSenseiDiscardTargetCost(targetCard)); @@ -60,7 +60,7 @@ public final class HisokaMinamoSensei extends CardImpl { class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { - protected Card card = null; + protected Card card; public HisokaMinamoSenseiDiscardTargetCost(TargetCardInHand target) { this.addTarget(target); @@ -69,23 +69,27 @@ class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { public HisokaMinamoSenseiDiscardTargetCost(HisokaMinamoSenseiDiscardTargetCost cost) { super(cost); + this.card = cost.card.copy(); } @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - if (targets.choose(Outcome.Discard, controllerId, source.getSourceId(), source, game)) { - Player player = game.getPlayer(controllerId); - if(player != null) { - for (UUID targetId : targets.get(0).getTargets()) { - card = player.getHand().get(targetId, game); - if (card == null) { - return false; - } - paid |= player.discard(card, true, source, game); + if (!targets.choose(Outcome.Discard, controllerId, source.getSourceId(), source, game)) { + return paid; + } - } + Player player = game.getPlayer(controllerId); + if (player == null) { + return paid; + } + + for (UUID targetId : targets.get(0).getTargets()) { + card = player.getHand().get(targetId, game); + if (card != null) { + paid |= player.discard(card, true, source, game); } } + return paid; } @@ -106,24 +110,32 @@ class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { } class HisokaMinamoSenseiCounterEffect extends OneShotEffect { + HisokaMinamoSenseiCounterEffect() { super(Outcome.Detriment); staticText = "Counter target spell if it has the same mana value as the discarded card"; } - HisokaMinamoSenseiCounterEffect(final HisokaMinamoSenseiCounterEffect effect) { + private HisokaMinamoSenseiCounterEffect(final HisokaMinamoSenseiCounterEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null) { - HisokaMinamoSenseiDiscardTargetCost cost = (HisokaMinamoSenseiDiscardTargetCost) source.getCosts().get(0); - if (cost != null && cost.getDiscardedCard().getManaValue() == spell.getManaValue()) { - return game.getStack().counter(targetPointer.getFirst(game, source), source, game); - } + if (spell == null) { + return false; } + + HisokaMinamoSenseiDiscardTargetCost cost = (HisokaMinamoSenseiDiscardTargetCost) source.getCosts().get(0); + if (cost == null) { + return false; + } + + if (cost.getDiscardedCard().getManaValue() == spell.getManaValue()) { + return game.getStack().counter(targetPointer.getFirst(game, source), source, game); + } + return false; } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java b/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java index 6f47376227..2acbdc2808 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryPresence.java @@ -39,9 +39,10 @@ public final class IllusionaryPresence extends CardImpl { this.toughness = new MageInt(2); // Cumulative upkeep {U} - this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{U}"))); + this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl<>("{U}"))); - // At the beginning of your upkeep, choose a land type. Illusionary Presence gains landwalk of the chosen type until end of turn. + // At the beginning of your upkeep, choose a land type. + // Illusionary Presence gains landwalk of the chosen type until end of turn. Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ChooseBasicLandTypeEffect(Outcome.Neutral), TargetController.YOU, false); ability.addEffect(new IllusionaryPresenceEffect()); this.addAbility(ability); @@ -60,8 +61,6 @@ public final class IllusionaryPresence extends CardImpl { class IllusionaryPresenceEffect extends OneShotEffect { - Ability gainedAbility; - public IllusionaryPresenceEffect() { super(Outcome.Benefit); this.staticText = "{this} gains landwalk of the chosen type until end of turn"; @@ -79,33 +78,38 @@ class IllusionaryPresenceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { MageObject mageObject = game.getObject(source); - if (mageObject != null) { - SubType landTypeChoice = SubType.byDescription((String) game.getState().getValue(mageObject.getId().toString() + "BasicLandType")); - if (landTypeChoice != null) { - switch (landTypeChoice) { - case PLAINS: - gainedAbility = new PlainswalkAbility(); - break; - case FOREST: - gainedAbility = new ForestwalkAbility(); - break; - case SWAMP: - gainedAbility = new SwampwalkAbility(); - break; - case ISLAND: - gainedAbility = new IslandwalkAbility(); - break; - case MOUNTAIN: - gainedAbility = new MountainwalkAbility(); - break; - } - if (gainedAbility != null) { - GainAbilitySourceEffect effect = new GainAbilitySourceEffect(gainedAbility, Duration.EndOfTurn); - game.addEffect(effect, source); - return true; - } - } + if (mageObject == null) { + return false; } - return false; + + SubType landTypeChoice = SubType.byDescription((String) game.getState().getValue(mageObject.getId().toString() + "BasicLandType")); + if (landTypeChoice == null) { + return false; + } + + Ability gainedAbility; + switch (landTypeChoice) { + case PLAINS: + gainedAbility = new PlainswalkAbility(); + break; + case FOREST: + gainedAbility = new ForestwalkAbility(); + break; + case SWAMP: + gainedAbility = new SwampwalkAbility(); + break; + case ISLAND: + gainedAbility = new IslandwalkAbility(); + break; + case MOUNTAIN: + gainedAbility = new MountainwalkAbility(); + break; + default: + return false; + } + + GainAbilitySourceEffect effect = new GainAbilitySourceEffect(gainedAbility, Duration.EndOfTurn); + game.addEffect(effect, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java index 88cd2c1d18..d583d82870 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java @@ -141,8 +141,10 @@ class ChooseTwoBasicLandTypesEffect extends OneShotEffect { this.staticText = "choose two basic land types"; } - public ChooseTwoBasicLandTypesEffect(final ChooseTwoBasicLandTypesEffect effect) { + private ChooseTwoBasicLandTypesEffect(final ChooseTwoBasicLandTypesEffect effect) { super(effect); + this.choiceOne = effect.choiceOne; + this.choiceTwo = effect.choiceTwo; } @Override diff --git a/Mage.Sets/src/mage/cards/i/ImminentDoom.java b/Mage.Sets/src/mage/cards/i/ImminentDoom.java index f12f45763c..cd7d447aca 100644 --- a/Mage.Sets/src/mage/cards/i/ImminentDoom.java +++ b/Mage.Sets/src/mage/cards/i/ImminentDoom.java @@ -33,7 +33,9 @@ public final class ImminentDoom extends CardImpl { // Imminent Doom enters the battlefield with a doom counter on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.DOOM.createInstance(1)), "with a doom counter on it")); - // Whenever you cast a spell with converted mana cost equal to the number of doom counters on Imminent Doom, Imminent Doom deals that much damage to any target. Then put a doom counter on Imminent Doom. + // Whenever you cast a spell with converted mana cost equal to the number of doom counters on Imminent Doom, + // Imminent Doom deals that much damage to any target. + // Then put a doom counter on Imminent Doom. Ability ability = new ImminentDoomTriggeredAbility(); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); @@ -52,7 +54,8 @@ public final class ImminentDoom extends CardImpl { class ImminentDoomTriggeredAbility extends TriggeredAbilityImpl { - private String rule = "Whenever you cast a spell with mana value equal to the number of doom counters on {this}, {this} deals that much damage to any target. Then put a doom counter on {this}."; + private static final String rule = "Whenever you cast a spell with mana value equal to the number of doom counters on {this}, " + + "{this} deals that much damage to any target. Then put a doom counter on {this}."; public ImminentDoomTriggeredAbility() { super(Zone.BATTLEFIELD, new ImminentDoomEffect()); @@ -74,16 +77,24 @@ class ImminentDoomTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.getControllerId())) { - Permanent imminentDoom = game.getPermanent(getSourceId()); - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null - && imminentDoom != null - && spell.getManaValue() == imminentDoom.getCounters(game).getCount(CounterType.DOOM)) { - game.getState().setValue("ImminentDoomCount" + getSourceId().toString(), imminentDoom.getCounters(game).getCount(CounterType.DOOM)); // store its current value - return true; - } - } + if (!event.getPlayerId().equals(this.getControllerId())) { + return false; + } + + Permanent imminentDoom = game.getPermanent(getSourceId()); + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (imminentDoom == null || spell == null) { + return false; + } + + if (spell.getManaValue() == imminentDoom.getCounters(game).getCount(CounterType.DOOM)) { + // store its current value + game.getState().setValue( + "ImminentDoomCount" + getSourceId().toString(), + imminentDoom.getCounters(game).getCount(CounterType.DOOM) + ); + return true; + } return false; } @@ -111,13 +122,13 @@ class ImminentDoomEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent imminentDoom = game.getPermanent(source.getSourceId()); - if (imminentDoom != null) { - Effect effect = new DamageTargetEffect((int) game.getState().getValue("ImminentDoomCount" + source.getSourceId().toString())); - effect.apply(game, source); - imminentDoom.addCounters(CounterType.DOOM.createInstance(), source.getControllerId(), source, game); - return true; + if (imminentDoom == null) { + return false; } - return false; - } + Effect effect = new DamageTargetEffect((int) game.getState().getValue("ImminentDoomCount" + source.getSourceId().toString())); + effect.apply(game, source); + imminentDoom.addCounters(CounterType.DOOM.createInstance(), source.getControllerId(), source, game); + return true; + } } diff --git a/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java b/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java index 125626947f..edafa6fdae 100644 --- a/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java +++ b/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java @@ -48,11 +48,14 @@ class ImpulsiveManeuversEffect extends PreventionEffectImpl { public ImpulsiveManeuversEffect() { super(Duration.EndOfTurn, Integer.MAX_VALUE, false); - staticText = "flip a coin. If you win the flip, the next time that creature would deal combat damage this turn, it deals double that damage instead. If you lose the flip, the next time that creature would deal combat damage this turn, prevent that damage"; + staticText = "flip a coin. " + + "If you win the flip, the next time that creature would deal combat damage this turn, it deals double that damage instead. " + + "If you lose the flip, the next time that creature would deal combat damage this turn, prevent that damage"; } public ImpulsiveManeuversEffect(final ImpulsiveManeuversEffect effect) { super(effect); + this.wonFlip = effect.wonFlip; } @Override @@ -86,21 +89,25 @@ class ImpulsiveManeuversEffect extends PreventionEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { MageObject object = game.getObject(event.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && object != null) { - if (super.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0) { - DamageEvent damageEvent = (DamageEvent) event; - if (damageEvent.isCombatDamage()) { - if (wonFlip) { - event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); - this.discard(); - } else { - preventDamageAction(event, source, game); - this.discard(); - return true; - } - } - } + if (controller == null || object == null) { + return false; } - return false; + + if (!super.applies(event, source, game)) { + return false; + } + + if (!(event instanceof DamageEvent) || !((DamageEvent) event).isCombatDamage() || event.getAmount() <= 0) { + return false; + } + + if (wonFlip) { + event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); + } else { + preventDamageAction(event, source, game); + } + + this.discard(); + return true; } } diff --git a/Mage.Sets/src/mage/cards/i/IncubationDruid.java b/Mage.Sets/src/mage/cards/i/IncubationDruid.java index 0bc69a192d..ac82a1398b 100644 --- a/Mage.Sets/src/mage/cards/i/IncubationDruid.java +++ b/Mage.Sets/src/mage/cards/i/IncubationDruid.java @@ -60,7 +60,7 @@ public final class IncubationDruid extends CardImpl { class AnyColorLandsProduceManaEffect extends ManaEffect { - private boolean inManaTypeCalculation = false; + private boolean inManaTypeCalculation; AnyColorLandsProduceManaEffect() { super(); @@ -70,6 +70,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { private AnyColorLandsProduceManaEffect(final AnyColorLandsProduceManaEffect effect) { super(effect); + this.inManaTypeCalculation = effect.inManaTypeCalculation; } @Override diff --git a/Mage.Sets/src/mage/cards/i/InventorsFair.java b/Mage.Sets/src/mage/cards/i/InventorsFair.java index 66df24f538..8abfa661d6 100644 --- a/Mage.Sets/src/mage/cards/i/InventorsFair.java +++ b/Mage.Sets/src/mage/cards/i/InventorsFair.java @@ -17,7 +17,6 @@ import mage.constants.CardType; import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.common.TargetCardInLibrary; @@ -39,8 +38,9 @@ public final class InventorsFair extends CardImpl { // {t}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {4}, {T}, Sacrifice Inventors' Fair: Search your library for an artifact card, reveal it, put it into your hand, then shuffle your library. - // Activate this ability only if you control threeor more artifacts. + // {4}, {T}, Sacrifice Inventors' Fair: Search your library for an artifact card, reveal it, + // put it into your hand, then shuffle your library. + // Activate this ability only if you control three or more artifacts. Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_ARTIFACT), true), new GenericManaCost(4), MetalcraftCondition.instance); ability.addCost(new TapSourceCost()); @@ -61,8 +61,6 @@ public final class InventorsFair extends CardImpl { class InventorsFairAbility extends TriggeredAbilityImpl { - private FilterArtifactPermanent filter = new FilterArtifactPermanent(); - public InventorsFairAbility() { super(Zone.BATTLEFIELD, new GainLifeEffect(1)); } @@ -88,7 +86,7 @@ class InventorsFairAbility extends TriggeredAbilityImpl { @Override public boolean checkInterveningIfClause(Game game) { - return game.getBattlefield().countAll(filter, this.controllerId, game) >= 3; + return game.getBattlefield().countAll(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, this.controllerId, game) >= 3; } @Override diff --git a/Mage.Sets/src/mage/cards/j/JodahsAvenger.java b/Mage.Sets/src/mage/cards/j/JodahsAvenger.java index dbb32877b0..54765c97c1 100644 --- a/Mage.Sets/src/mage/cards/j/JodahsAvenger.java +++ b/Mage.Sets/src/mage/cards/j/JodahsAvenger.java @@ -8,7 +8,7 @@ import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; @@ -38,7 +38,7 @@ public final class JodahsAvenger extends CardImpl { this.toughness = new MageInt(4); // {0}: Until end of turn, Jodah's Avenger gets -1/-1 and gains your choice of double strike, protection from red, vigilance, or shadow. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new JodahsAvengerEffect(), new ManaCostsImpl("{0}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new JodahsAvengerEffect(), new GenericManaCost(0))); } private JodahsAvenger(final JodahsAvenger card) { @@ -70,6 +70,7 @@ class JodahsAvengerEffect extends ContinuousEffectImpl { public JodahsAvengerEffect(final JodahsAvengerEffect effect) { super(effect); + this.gainedAbility = effect.gainedAbility.copy(); } @Override @@ -80,41 +81,46 @@ class JodahsAvengerEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Choice choice = new ChoiceImpl(true); - choice.setMessage("Choose one"); - choice.setChoices(choices); - if (controller.choose(outcome, choice, game)) { - switch (choice.getChoice()) { - case "Double strike": - gainedAbility = DoubleStrikeAbility.getInstance(); - break; - case "Vigilance": - gainedAbility = VigilanceAbility.getInstance(); - break; - case "Shadow": - gainedAbility = ShadowAbility.getInstance(); - break; - default: - gainedAbility = ProtectionAbility.from(ObjectColor.RED); - break; - } - } else { - discard(); + if (controller == null) { + return; + } + + Choice choice = new ChoiceImpl(true); + choice.setMessage("Choose one"); + choice.setChoices(choices); + if (controller.choose(outcome, choice, game)) { + switch (choice.getChoice()) { + case "Double strike": + gainedAbility = DoubleStrikeAbility.getInstance(); + break; + case "Vigilance": + gainedAbility = VigilanceAbility.getInstance(); + break; + case "Shadow": + gainedAbility = ShadowAbility.getInstance(); + break; + default: + gainedAbility = ProtectionAbility.from(ObjectColor.RED); + break; } + } else { + discard(); } } @Override public boolean apply(Game game, Ability source) { Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (sourceObject != null) { - sourceObject.addPower(-1); - sourceObject.addToughness(-1); - game.addEffect(new GainAbilitySourceEffect(gainedAbility, Duration.EndOfTurn), source); - return true; + if (sourceObject == null) { + return false; } - return false; + + sourceObject.addPower(-1); + sourceObject.addToughness(-1); + game.addEffect(new GainAbilitySourceEffect(gainedAbility, Duration.EndOfTurn), source); + + return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JointAssault.java b/Mage.Sets/src/mage/cards/j/JointAssault.java index 9a687b5c29..785de56d10 100644 --- a/Mage.Sets/src/mage/cards/j/JointAssault.java +++ b/Mage.Sets/src/mage/cards/j/JointAssault.java @@ -56,6 +56,7 @@ class JointAssaultBoostTargetEffect extends ContinuousEffectImpl { super(effect); this.power = effect.power; this.toughness = effect.toughness; + this.paired = effect.paired; } @Override @@ -66,12 +67,11 @@ class JointAssaultBoostTargetEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); + UUID permanentId = targetPointer.getFirst(game, source); Permanent target = game.getPermanent(permanentId); - if (target != null) { - if (target.getPairedCard() != null) { - this.paired = target.getPairedCard(); - } + if (target != null && target.getPairedCard() != null) { + this.paired = target.getPairedCard(); } } diff --git a/Mage.Sets/src/mage/cards/k/KarnLiberated.java b/Mage.Sets/src/mage/cards/k/KarnLiberated.java index d83d596df4..b6a06efdf4 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLiberated.java +++ b/Mage.Sets/src/mage/cards/k/KarnLiberated.java @@ -46,7 +46,8 @@ public final class KarnLiberated extends CardImpl { ability2.addTarget(new TargetPermanent()); this.addAbility(ability2); - // -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. + // -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. this.addAbility(new LoyaltyAbility(new KarnLiberatedEffect(), -14)); } @@ -62,11 +63,10 @@ public final class KarnLiberated extends CardImpl { class KarnLiberatedEffect extends OneShotEffect { - private UUID exileId; - public KarnLiberatedEffect() { super(Outcome.ExtraTurn); - this.staticText = "Restart the game, leaving in exile all non-Aura permanent cards exiled with {this}. Then put those cards onto the battlefield under your control"; + this.staticText = "Restart the game, leaving in exile all non-Aura permanent cards exiled with {this}. " + + "Then put those cards onto the battlefield under your control"; } public KarnLiberatedEffect(final KarnLiberatedEffect effect) { @@ -75,6 +75,7 @@ class KarnLiberatedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + UUID exileId = null; MageObject sourceObject = source.getSourceObject(game); if (sourceObject == null) { return false; @@ -84,8 +85,7 @@ class KarnLiberatedEffect extends OneShotEffect { exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (zone.getId().equals(exileId)) { for (Card card : zone.getCards(game)) { - if (!card.hasSubtype(SubType.AURA, game) - && card.isPermanent(game)) { + if (!card.hasSubtype(SubType.AURA, game) && card.isPermanent(game)) { cards.add(card); } } diff --git a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java index ad8746a4fc..b017834063 100644 --- a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java +++ b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java @@ -59,8 +59,6 @@ public final class KrovikanVampire extends CardImpl { class KrovikanVampireEffect extends OneShotEffect { - Set creaturesAffected = new HashSet<>(); - KrovikanVampireEffect() { super(Outcome.Neutral); staticText = "put that card onto the battlefield under your control. Sacrifice it when you lose control of {this}"; @@ -74,25 +72,24 @@ class KrovikanVampireEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent krovikanVampire = game.getPermanent(source.getSourceId()); - creaturesAffected = (Set) game.getState().getValue(source.getSourceId() + "creatureToGainControl"); - if (creaturesAffected != null - && controller != null - && krovikanVampire != null) { - creaturesAffected.stream().map((creatureId) -> { - controller.moveCards(game.getCard(creatureId), Zone.BATTLEFIELD, source, game, false, false, false, null); - return creatureId; - }).map((creatureId) -> { - OneShotEffect effect = new SacrificeTargetEffect(); - effect.setText("Sacrifice this if Krovikan Vampire leaves the battlefield or its current controller loses control of it."); - effect.setTargetPointer(new FixedTarget(creatureId, game)); - return effect; - }).map((effect) -> new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId())).forEachOrdered((dTA) -> { - game.addDelayedTriggeredAbility(dTA, source); - }); - creaturesAffected.clear(); - return true; + Set creaturesAffected = (Set) game.getState().getValue(source.getSourceId() + "creatureToGainControl"); + if (creaturesAffected == null || controller == null || krovikanVampire == null) { + return false; } - return false; + + creaturesAffected.stream().map((creatureId) -> { + controller.moveCards(game.getCard(creatureId), Zone.BATTLEFIELD, source, game, false, false, false, null); + return creatureId; + }).map((creatureId) -> { + OneShotEffect effect = new SacrificeTargetEffect(); + effect.setText("Sacrifice this if Krovikan Vampire leaves the battlefield or its current controller loses control of it."); + effect.setTargetPointer(new FixedTarget(creatureId, game)); + return effect; + }).map((effect) -> new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId())).forEachOrdered((dTA) -> { + game.addDelayedTriggeredAbility(dTA, source); + }); + creaturesAffected.clear(); + return true; } @Override @@ -109,21 +106,24 @@ class KrovikanVampireInterveningIfCondition implements Condition { public boolean apply(Game game, Ability source) { KrovikanVampireCreaturesDiedWatcher watcherDied = game.getState().getWatcher(KrovikanVampireCreaturesDiedWatcher.class); KrovikanVampireCreaturesDamagedWatcher watcherDamaged = game.getState().getWatcher(KrovikanVampireCreaturesDamagedWatcher.class); - if (watcherDied != null) { - Set creaturesThatDiedThisTurn = watcherDied.getDiedThisTurn(); - creaturesThatDiedThisTurn.stream().filter((mor) -> (watcherDamaged != null)).forEachOrdered((mor) -> { - watcherDamaged.getDamagedBySource().stream().filter((mor2) -> (mor2 != null - && mor == mor2)).forEachOrdered((_item) -> { - creaturesAffected.add(mor); - }); - }); - if (creaturesAffected != null - && creaturesAffected.size() > 0) { - game.getState().setValue(source.getSourceId() + "creatureToGainControl", creaturesAffected); - return true; - } + if (watcherDied == null) { + return false; } - return false; + + Set creaturesThatDiedThisTurn = watcherDied.getDiedThisTurn(); + creaturesThatDiedThisTurn.stream().filter((mor) -> (watcherDamaged != null)).forEachOrdered((mor) -> { + watcherDamaged.getDamagedBySource().stream().filter((mor2) -> (mor2 != null + && mor == mor2)).forEachOrdered((_item) -> { + creaturesAffected.add(mor); + }); + }); + + if (creaturesAffected == null || creaturesAffected.isEmpty()) { + return false; + } + + game.getState().setValue(source.getSourceId() + "creatureToGainControl", creaturesAffected); + return true; } @Override @@ -142,14 +142,15 @@ class KrovikanVampireCreaturesDamagedWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT - || !sourceId.equals(event.getSourceId())) { + if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT || !sourceId.equals(event.getSourceId())) { return; } + Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent == null || !permanent.isCreature(game)) { return; } + damagedBySource.add(event.getTargetId()); } @@ -215,14 +216,10 @@ class KrovikanVampireDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getTargetId().equals(krovikanVampire)) { - return true; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && event.getTargetId().equals(krovikanVampire)) { - return true; + if (event.getType() == GameEvent.EventType.LOST_CONTROL || event.getType() == GameEvent.EventType.ZONE_CHANGE) { + return event.getTargetId().equals(krovikanVampire); } + return false; } diff --git a/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java b/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java index 31cc5e63f9..5d411f78c9 100644 --- a/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java +++ b/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java @@ -60,6 +60,7 @@ class LivingBreakthroughEffect extends ContinuousRuleModifyingEffectImpl { private LivingBreakthroughEffect(final LivingBreakthroughEffect effect) { super(effect); + this.manaValue = effect.manaValue; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java b/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java index cb196f280b..29801ea61c 100644 --- a/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java +++ b/Mage.Sets/src/mage/cards/m/MarshlandBloodcaster.java @@ -70,6 +70,7 @@ class MarshlandBloodcasterEffect extends ContinuousEffectImpl { public MarshlandBloodcasterEffect(final MarshlandBloodcasterEffect effect) { super(effect); + this.spellsCast = effect.spellsCast; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MyrIncubator.java b/Mage.Sets/src/mage/cards/m/MyrIncubator.java index 1e6f2b81ef..63441027b4 100644 --- a/Mage.Sets/src/mage/cards/m/MyrIncubator.java +++ b/Mage.Sets/src/mage/cards/m/MyrIncubator.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.SearchEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -30,8 +31,10 @@ public final class MyrIncubator extends CardImpl { public MyrIncubator(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); - // {6}, {tap}, Sacrifice Myr Incubator: Search your library for any number of artifact cards, exile them, then put that many 1/1 colorless Myr artifact creature tokens onto the battlefield. Then shuffle your library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MyrIncubatorEffect(), new ManaCostsImpl("{6}")); + // {6}, {tap}, Sacrifice Myr Incubator: Search your library for any number of artifact cards, exile them, + // then put that many 1/1 colorless Myr artifact creature tokens onto the battlefield. + // Then shuffle your library. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MyrIncubatorEffect(), new GenericManaCost(6)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); @@ -56,32 +59,39 @@ class MyrIncubatorEffect extends SearchEffect { filter.add(CardType.ARTIFACT.getPredicate()); } - int tokensToCreate = 0; + int tokensToCreate; MyrIncubatorEffect() { super(new TargetCardInLibrary(0, Integer.MAX_VALUE, filter), Outcome.Neutral); - staticText = "Search your library for any number of artifact cards, exile them, then create that many 1/1 colorless Myr artifact creature tokens. Then shuffle"; + staticText = "Search your library for any number of artifact cards, exile them, " + + "then create that many 1/1 colorless Myr artifact creature tokens. " + + "Then shuffle"; } MyrIncubatorEffect(final MyrIncubatorEffect effect) { super(effect); + this.tokensToCreate = effect.tokensToCreate; } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null - && controller.searchLibrary(target, source, game)) { - if (!target.getTargets().isEmpty()) { - tokensToCreate = target.getTargets().size(); - controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); - } + if (controller == null) { + return false; + } + + if (!controller.searchLibrary(target, source, game)) { + return false; + } + + if (!target.getTargets().isEmpty()) { + tokensToCreate = target.getTargets().size(); + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); CreateTokenEffect effect = new CreateTokenEffect(new MyrToken(), tokensToCreate); effect.apply(game, source); - controller.shuffleLibrary(source, game); - return true; } - return false; + controller.shuffleLibrary(source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/n/NaturesBlessing.java b/Mage.Sets/src/mage/cards/n/NaturesBlessing.java index 75ac616fd7..6094cdf0e3 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesBlessing.java +++ b/Mage.Sets/src/mage/cards/n/NaturesBlessing.java @@ -55,7 +55,6 @@ public final class NaturesBlessing extends CardImpl { class NaturesBlessingEffect extends OneShotEffect { private static final Set choices = new HashSet<>(); - private Ability gainedAbility; static { choices.add("+1/+1 counter"); @@ -82,30 +81,33 @@ class NaturesBlessingEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent targetPermanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (controller != null && targetPermanent != null) { - Choice choice = new ChoiceImpl(true); - choice.setMessage("Choose one"); - choice.setChoices(choices); - if (controller.choose(outcome, choice, game)) { - switch (choice.getChoice()) { - case "Banding": - gainedAbility = BandingAbility.getInstance(); - break; - case "First strike": - gainedAbility = FirstStrikeAbility.getInstance(); - break; - case "Trample": - gainedAbility = TrampleAbility.getInstance(); - } - } - if (gainedAbility != null) { - game.addEffect(new GainAbilityTargetEffect(gainedAbility, Duration.Custom), source); - } else { - targetPermanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - game.informPlayers(controller.getLogName() + " puts a +1/+1 counter on " + targetPermanent.getLogName()); - } - return true; + if (controller == null || targetPermanent == null) { + return false; } - return false; + + Ability gainedAbility = null; + Choice choice = new ChoiceImpl(true); + choice.setMessage("Choose one"); + choice.setChoices(choices); + if (controller.choose(outcome, choice, game)) { + switch (choice.getChoice()) { + case "Banding": + gainedAbility = BandingAbility.getInstance(); + break; + case "First strike": + gainedAbility = FirstStrikeAbility.getInstance(); + break; + case "Trample": + gainedAbility = TrampleAbility.getInstance(); + } + } + + if (gainedAbility != null) { + game.addEffect(new GainAbilityTargetEffect(gainedAbility, Duration.Custom), source); + } else { + targetPermanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); + game.informPlayers(controller.getLogName() + " puts a +1/+1 counter on " + targetPermanent.getLogName()); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java b/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java index 0dc9e8bc0e..60fb729d75 100644 --- a/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java +++ b/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java @@ -60,6 +60,7 @@ class NecromancersStockpileDiscardTargetCost extends CostImpl { public NecromancersStockpileDiscardTargetCost(NecromancersStockpileDiscardTargetCost cost) { super(cost); + this.isZombieCard = cost.isZombieCard; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PredatoryFocus.java b/Mage.Sets/src/mage/cards/p/PredatoryFocus.java index af588c39aa..48517be38d 100644 --- a/Mage.Sets/src/mage/cards/p/PredatoryFocus.java +++ b/Mage.Sets/src/mage/cards/p/PredatoryFocus.java @@ -46,6 +46,7 @@ class PredatoryFocusEffect extends AsThoughEffectImpl { public PredatoryFocusEffect(PredatoryFocusEffect effect) { super(effect); + this.choseUse = effect.choseUse; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PrimitiveEtchings.java b/Mage.Sets/src/mage/cards/p/PrimitiveEtchings.java index fefca12943..e6c3c08e39 100644 --- a/Mage.Sets/src/mage/cards/p/PrimitiveEtchings.java +++ b/Mage.Sets/src/mage/cards/p/PrimitiveEtchings.java @@ -49,6 +49,7 @@ class PrimitiveEtchingsAbility extends TriggeredAbilityImpl { PrimitiveEtchingsAbility(final PrimitiveEtchingsAbility ability) { super(ability); + this.lastTriggeredTurn = ability.lastTriggeredTurn; } @Override diff --git a/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java b/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java index a1a15feb12..600c0f1782 100644 --- a/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java +++ b/Mage.Sets/src/mage/cards/r/RaziaBorosArchangel.java @@ -87,6 +87,7 @@ class RaziaBorosArchangelEffect extends RedirectionEffect { public RaziaBorosArchangelEffect(final RaziaBorosArchangelEffect effect) { super(effect); + this.redirectToObject = effect.redirectToObject; } @Override diff --git a/Mage.Sets/src/mage/cards/r/Rowen.java b/Mage.Sets/src/mage/cards/r/Rowen.java index 5b0a7a0863..8fe3235ee5 100644 --- a/Mage.Sets/src/mage/cards/r/Rowen.java +++ b/Mage.Sets/src/mage/cards/r/Rowen.java @@ -47,8 +47,9 @@ class RowenAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new InfoEffect(""), false); } - RowenAbility(final RowenAbility ability) { + private RowenAbility(final RowenAbility ability) { super(ability); + this.lastTriggeredTurn = ability.lastTriggeredTurn; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java index 633ea117ca..02ec36702b 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java +++ b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java @@ -54,7 +54,7 @@ class ChooseNumberEffect extends OneShotEffect { staticText = "choose a number"; } - public ChooseNumberEffect(final ChooseNumberEffect effect) { + private ChooseNumberEffect(final ChooseNumberEffect effect) { super(effect); } @@ -83,14 +83,12 @@ class ChooseNumberEffect extends OneShotEffect { class SanctumPrelateReplacementEffect extends ContinuousRuleModifyingEffectImpl { - Integer choiceValue; - public SanctumPrelateReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); staticText = "Noncreature spells with mana value equal to the chosen number can't be cast"; } - public SanctumPrelateReplacementEffect(final SanctumPrelateReplacementEffect effect) { + private SanctumPrelateReplacementEffect(final SanctumPrelateReplacementEffect effect) { super(effect); } @@ -120,7 +118,7 @@ class SanctumPrelateReplacementEffect extends ContinuousRuleModifyingEffectImpl @Override public boolean applies(GameEvent event, Ability source, Game game) { - choiceValue = (Integer) game.getState().getValue(source.getSourceId().toString()); + Integer choiceValue = (Integer) game.getState().getValue(source.getSourceId().toString()); Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && !spell.isCreature(game)) { diff --git a/Mage.Sets/src/mage/cards/s/ScuteMob.java b/Mage.Sets/src/mage/cards/s/ScuteMob.java index 33f7bbcd2e..60bdfea9dc 100644 --- a/Mage.Sets/src/mage/cards/s/ScuteMob.java +++ b/Mage.Sets/src/mage/cards/s/ScuteMob.java @@ -12,6 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -45,8 +46,6 @@ public final class ScuteMob extends CardImpl { class ScuteMobAbility extends TriggeredAbilityImpl { - private FilterLandPermanent filter = new FilterLandPermanent(); - public ScuteMobAbility() { super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(4))); } @@ -72,7 +71,7 @@ class ScuteMobAbility extends TriggeredAbilityImpl { @Override public boolean checkInterveningIfClause(Game game) { - return game.getBattlefield().countAll(filter, this.controllerId, game) >= 5; + return game.getBattlefield().countAll(StaticFilters.FILTER_LAND, this.controllerId, game) >= 5; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SlumberingTora.java b/Mage.Sets/src/mage/cards/s/SlumberingTora.java index 545c74fcd9..883ac95d83 100644 --- a/Mage.Sets/src/mage/cards/s/SlumberingTora.java +++ b/Mage.Sets/src/mage/cards/s/SlumberingTora.java @@ -31,6 +31,7 @@ public final class SlumberingTora extends CardImpl { public SlumberingTora(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + // {2}, Discard a Spirit or Arcane card: Slumbering Tora becomes an X/X Cat artifact creature until end of turn, // where X is the discarded card's converted mana cost. Ability ability = new SimpleActivatedAbility(new SlumberingToraEffect(), new ManaCostsImpl("{2}")); @@ -49,7 +50,7 @@ public final class SlumberingTora extends CardImpl { private static class SlumberingToraEffect extends ContinuousEffectImpl { - private int convManaCosts = 0; + private int convManaCosts; private SlumberingToraEffect() { super(Duration.EndOfTurn, Outcome.BecomeCreature); @@ -59,6 +60,7 @@ public final class SlumberingTora extends CardImpl { private SlumberingToraEffect(final SlumberingToraEffect effect) { super(effect); + this.convManaCosts = effect.convManaCosts; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SoulEcho.java b/Mage.Sets/src/mage/cards/s/SoulEcho.java index eb227a537d..eef64e8136 100644 --- a/Mage.Sets/src/mage/cards/s/SoulEcho.java +++ b/Mage.Sets/src/mage/cards/s/SoulEcho.java @@ -80,13 +80,24 @@ class SoulEchoOpponentsChoiceEffect extends OneShotEffect { Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); - if (controller != null && opponent != null && permanent != null) { - if (opponent.chooseUse(outcome, "Have all damage dealt to " + controller.getLogName() + " be decremented from echo counters on " + permanent.getLogName() + " until " + controller.getLogName() + "'s next upkeep instead?", source, game)) { - game.informPlayers("Until " + controller.getLogName() + "'s next upkeep, for each 1 damage that would be dealt to " + controller.getLogName() + ", an echo counter from " + permanent.getLogName() + " is removed instead"); - game.addEffect(new SoulEchoReplacementEffect(), source); - } + if (controller == null || opponent == null || permanent == null) { + return false; + } + + String msg1 = "Have all damage dealt to " + controller.getLogName() + + " be decremented from echo counters on " + permanent.getLogName() + + " until " + controller.getLogName() + "'s next upkeep instead?"; + + String msg2 = "Until " + controller.getLogName() + + "'s next upkeep, for each 1 damage that would be dealt to " + controller.getLogName() + + ", an echo counter from " + permanent.getLogName() + " is removed instead"; + + if (opponent.chooseUse(outcome, msg1, source, game)) { + game.informPlayers(msg2); + game.addEffect(new SoulEchoReplacementEffect(), source); return true; } + return false; } } @@ -99,16 +110,15 @@ class SoulEchoReplacementEffect extends ReplacementEffectImpl { super(Duration.Custom, Outcome.PreventDamage); } - SoulEchoReplacementEffect(final SoulEchoReplacementEffect effect) { + private SoulEchoReplacementEffect(final SoulEchoReplacementEffect effect) { super(effect); + this.sameStep = effect.sameStep; } @Override public boolean isInactive(Ability source, Game game) { if (game.getPhase().getStep().getType() == PhaseStep.UPKEEP) { - if (!sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - return true; - } + return !sameStep && game.isActivePlayer(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving(); } else { sameStep = false; } @@ -117,14 +127,18 @@ class SoulEchoReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - int damage = damageEvent.getAmount(); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); - if (permanent != null && controller != null) { - permanent.removeCounters(CounterType.ECHO.createInstance(damage), source, game); - game.informPlayers(controller.getLogName() + ": " + damage + " damage replaced with " + permanent.getLogName()); + if (permanent == null && controller == null) { + return false; } + DamageEvent damageEvent = (DamageEvent) event; + int damage = damageEvent.getAmount(); + + permanent.removeCounters(CounterType.ECHO.createInstance(damage), source, game); + game.informPlayers(controller.getLogName() + ": " + damage + " damage replaced with " + permanent.getLogName()); + return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java b/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java index c996d6cabd..19218b0c58 100644 --- a/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java +++ b/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java @@ -45,7 +45,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - private boolean wonAlready = false; + private boolean wonAlready; static { filter.add(new NamePredicate("The Cheese Stands Alone")); @@ -56,8 +56,9 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp staticText = "When you control no permanents other than {this} and have no cards in hand, you win the game"; } - public CheeseStandsAloneContinuousEffect(final CheeseStandsAloneContinuousEffect effect) { + private CheeseStandsAloneContinuousEffect(final CheeseStandsAloneContinuousEffect effect) { super(effect); + this.wonAlready = effect.wonAlready; } @Override diff --git a/Mage.Sets/src/mage/cards/u/UrzasAvenger.java b/Mage.Sets/src/mage/cards/u/UrzasAvenger.java index 5dd53fa018..ac6ced46fd 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasAvenger.java +++ b/Mage.Sets/src/mage/cards/u/UrzasAvenger.java @@ -67,8 +67,9 @@ class UrzasAvengerEffect extends ContinuousEffectImpl { this.staticText = "{this} gets -1/-1 and gains your choice of banding, flying, first strike, or trample until end of turn"; } - public UrzasAvengerEffect(final UrzasAvengerEffect effect) { + private UrzasAvengerEffect(final UrzasAvengerEffect effect) { super(effect); + this.gainedAbility = effect.gainedAbility; } @Override diff --git a/Mage.Sets/src/mage/cards/v/VerdantSuccession.java b/Mage.Sets/src/mage/cards/v/VerdantSuccession.java index b9b251ccc3..e977259bd6 100644 --- a/Mage.Sets/src/mage/cards/v/VerdantSuccession.java +++ b/Mage.Sets/src/mage/cards/v/VerdantSuccession.java @@ -100,8 +100,6 @@ class VerdantSuccessionTriggeredAbility extends TriggeredAbilityImpl { class VerdantSuccessionEffect extends OneShotEffect { - private Permanent permanent; - VerdantSuccessionEffect() { super(Outcome.PutCardInPlay); } @@ -118,26 +116,36 @@ class VerdantSuccessionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { MageObject mageObject = game.getObject(source); - if(mageObject != null) { - permanent = (Permanent) game.getState().getValue("verdantSuccession" + mageObject); - if (permanent != null) { - Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null) { - FilterCard filterCard = new FilterCard("Card named " + permanent.getName()); - filterCard.add(new NamePredicate(permanent.getName())); - TargetCardInLibrary target = new TargetCardInLibrary(filterCard); - controller.searchLibrary(target, source, game); - if (!target.getTargets().isEmpty()) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null - && controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - controller.shuffleLibrary(source, game); - } - return true; - } - } + if (mageObject == null) { + return false; + } + + Permanent permanent = (Permanent) game.getState().getValue("verdantSuccession" + mageObject); + if (permanent == null) { + return false; + } + + Player controller = game.getPlayer(permanent.getControllerId()); + if (controller == null) { + return false; + } + + FilterCard filterCard = new FilterCard("Card named " + permanent.getName()); + filterCard.add(new NamePredicate(permanent.getName())); + + TargetCardInLibrary target = new TargetCardInLibrary(filterCard); + if (!controller.searchLibrary(target, source, game)) { + return false; + } + + if (!target.getTargets().isEmpty()) { + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } - return false; + + controller.shuffleLibrary(source, game); // Shuffle even if no card was found since it revealed the order of cards. + return true; } } diff --git a/Mage.Sets/src/mage/cards/w/WarCadence.java b/Mage.Sets/src/mage/cards/w/WarCadence.java index ba1df721d9..8c6ed1e3d7 100644 --- a/Mage.Sets/src/mage/cards/w/WarCadence.java +++ b/Mage.Sets/src/mage/cards/w/WarCadence.java @@ -2,8 +2,11 @@ package mage.cards.w; import java.util.UUID; + +import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManacostVariableValue; @@ -28,7 +31,7 @@ public final class WarCadence extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // {X}{R}: This turn, creatures can't block unless their controller pays {X} for each blocking creature they control. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new WarCadenceReplacementEffect(), new ManaCostsImpl("{X}{R}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new WarCadenceReplacementEffect(), new ManaCostsImpl<>("{X}{R}"))); } @@ -53,26 +56,28 @@ class WarCadenceReplacementEffect extends ReplacementEffectImpl { WarCadenceReplacementEffect(WarCadenceReplacementEffect effect) { super(effect); + this.xCosts = effect.xCosts.copy(); } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); - if (player != null) { - int amount = xCosts.calculate(game, source, this); - if (amount > 0) { - String mana = "{" + amount + '}'; - ManaCostsImpl cost = new ManaCostsImpl(mana); - if (cost.canPay(source, source, event.getPlayerId(), game) - && player.chooseUse(Outcome.Benefit, "Pay " + mana + " to declare blocker?", source, game)) { - if (cost.payOrRollback(source, game, source, event.getPlayerId())) { - return false; - } - } - return true; - } + if (player == null) { + return false; } - return false; + + int amount = xCosts.calculate(game, source, this); + if (amount <= 0) { + return false; + } + + String mana = "{" + amount + '}'; + ManaCostsImpl cost = new ManaCostsImpl<>(mana); + if (cost.canPay(source, source, event.getPlayerId(), game) + && player.chooseUse(Outcome.Benefit, "Pay " + mana + " to declare blocker?", source, game)) { + return !cost.payOrRollback(source, game, source, event.getPlayerId()); + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/w/WarTax.java b/Mage.Sets/src/mage/cards/w/WarTax.java index f99701148c..85ef63046e 100644 --- a/Mage.Sets/src/mage/cards/w/WarTax.java +++ b/Mage.Sets/src/mage/cards/w/WarTax.java @@ -54,6 +54,7 @@ class WarTaxCantAttackUnlessPaysEffect extends PayCostToAttackBlockEffectImpl { WarTaxCantAttackUnlessPaysEffect(WarTaxCantAttackUnlessPaysEffect effect) { super(effect); + this.xCosts = effect.xCosts.copy(); } @Override diff --git a/Mage.Sets/src/mage/cards/w/Woeleecher.java b/Mage.Sets/src/mage/cards/w/Woeleecher.java index 03b3db9cb0..7ccd20f27f 100644 --- a/Mage.Sets/src/mage/cards/w/Woeleecher.java +++ b/Mage.Sets/src/mage/cards/w/Woeleecher.java @@ -54,9 +54,6 @@ public final class Woeleecher extends CardImpl { class WoeleecherEffect extends OneShotEffect { - private int numberCountersOriginal = 0; - private int numberCountersAfter = 0; - public WoeleecherEffect() { super(Outcome.ReturnToHand); this.staticText = "Remove a -1/-1 counter from target creature. If you do, you gain 2 life"; @@ -75,15 +72,19 @@ class WoeleecherEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(source.getFirstTarget()); Player you = game.getPlayer(source.getControllerId()); - if (target != null && you != null) { - numberCountersOriginal = target.getCounters(game).getCount(CounterType.M1M1); - target.removeCounters(CounterType.M1M1.createInstance(), source, game); - numberCountersAfter = target.getCounters(game).getCount(CounterType.M1M1); - if (numberCountersAfter < numberCountersOriginal && you != null) { - you.gainLife(2, game, source); - return true; - } + if (target == null || you == null) { + return false; + } + + int numberCountersOriginal = target.getCounters(game).getCount(CounterType.M1M1); + target.removeCounters(CounterType.M1M1.createInstance(), source, game); + int numberCountersAfter = target.getCounters(game).getCount(CounterType.M1M1); + + if (numberCountersAfter < numberCountersOriginal) { + you.gainLife(2, game, source); + return true; + } else { + return false; } - return false; } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 91cbaa1b52..c0111416c2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -31,7 +31,7 @@ import java.util.*; public class RandomPlayer extends ComputerPlayer { private final boolean isSimulatedPlayer; - private int actionCount = 0; + private int actionCount; protected PassAbility pass = new PassAbility(); @@ -43,6 +43,8 @@ public class RandomPlayer extends ComputerPlayer { public RandomPlayer(final RandomPlayer player) { super(player); this.isSimulatedPlayer = player.isSimulatedPlayer; + this.actionCount = player.actionCount; + this.pass = player.pass.copy(); } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 0b91513700..3e133682d4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -83,7 +83,7 @@ public class TestPlayer implements Player { public static final String DIE_ROLL = "[die_roll]: "; private int maxCallsWithoutAction = 400; - private int foundNoAction = 0; + private int foundNoAction; // full playable AI, TODO: can be deleted? private boolean AIPlayer; @@ -106,9 +106,9 @@ public class TestPlayer implements Player { // - enable checks for wrong or missing choice commands (you must set up all choices by unit test) // - enable inner choice dialogs accessable by set up choices // (example: card call TestPlayer's choice, but it uses another choices, see docs in TestComputerPlayer) - private boolean strictChooseMode = false; + private boolean strictChooseMode; - private String[] groupsForTargetHandling = null; + private String[] groupsForTargetHandling; // Tracks the initial turns (turn 0s) both players are given at the start of the game. // Before actual turns start. Needed for checking attacker/blocker legality in the tests @@ -146,6 +146,7 @@ public class TestPlayer implements Player { this.groupsForTargetHandling = testPlayer.groupsForTargetHandling.clone(); } this.strictChooseMode = testPlayer.strictChooseMode; + this.maxCallsWithoutAction = testPlayer.maxCallsWithoutAction; } public void addChoice(String choice) { diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 13bb83085f..9e80805eea 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -23,10 +23,10 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge protected boolean optional; protected boolean leavesTheBattlefieldTrigger; - private boolean triggersOnce = false; - private boolean doOnlyOnce = false; - private GameEvent triggerEvent = null; - private String triggerPhrase = null; + private boolean triggersOnce; + private boolean doOnlyOnce; + private GameEvent triggerEvent; + private String triggerPhrase; public TriggeredAbilityImpl(Zone zone, Effect effect) { this(zone, effect, false); @@ -55,6 +55,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge this.triggersOnce = ability.triggersOnce; this.doOnlyOnce = ability.doOnlyOnce; this.triggerPhrase = ability.triggerPhrase; + this.triggerEvent = ability.triggerEvent; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/BlocksAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksAttachedTriggeredAbility.java index ee1a7a5883..03314d7f9c 100644 --- a/Mage/src/main/java/mage/abilities/common/BlocksAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BlocksAttachedTriggeredAbility.java @@ -15,8 +15,8 @@ import mage.target.targetpointer.FixedTarget; */ public class BlocksAttachedTriggeredAbility extends TriggeredAbilityImpl { - private boolean setFixedTargetPointer; - private String attachedDescription; + private final boolean setFixedTargetPointer; + private final String attachedDescription; private boolean setFixedTargetPointerToBlocked; public BlocksAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional) { @@ -40,6 +40,7 @@ public class BlocksAttachedTriggeredAbility extends TriggeredAbilityImpl { super(ability); this.setFixedTargetPointer = ability.setFixedTargetPointer; this.attachedDescription = ability.attachedDescription; + this.setFixedTargetPointerToBlocked = ability.setFixedTargetPointerToBlocked; } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java b/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java index 65d621a71f..81d02ecbec 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/CyclingDiscardCost.java @@ -16,13 +16,14 @@ import java.util.UUID; */ public class CyclingDiscardCost extends CostImpl { - private MageObjectReference cycledCard = null; + private MageObjectReference cycledCard; public CyclingDiscardCost() { } private CyclingDiscardCost(CyclingDiscardCost cost) { super(cost); + this.cycledCard = cost.cycledCard; } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index 59229470ab..afb03bc817 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -49,6 +49,7 @@ public class ManaCostsImpl extends ArrayList implements M this.add(cost.copy()); } this.phyrexian = costs.phyrexian; + this.phyrexianPaid = costs.phyrexianPaid; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java index e27858f214..59d7bd360a 100644 --- a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java @@ -42,9 +42,7 @@ public abstract class EffectImpl implements Effect { if (effect.values != null) { values = new HashMap<>(); Map map = effect.values; - for (Map.Entry entry : map.entrySet()) { - values.put(entry.getKey(), entry.getValue()); - } + values.putAll(map); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java index 60289092c2..d9e855f0e1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java @@ -21,6 +21,7 @@ public class DontUntapInControllersNextUntapStepSourceEffect extends ContinuousR public DontUntapInControllersNextUntapStepSourceEffect(final DontUntapInControllersNextUntapStepSourceEffect effect) { super(effect); + this.validForTurnNum = effect.validForTurnNum; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java index 358d96171c..218d990d13 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryTargetEffect.java @@ -17,7 +17,6 @@ import mage.util.CardUtil; public class ExileCardsFromTopOfLibraryTargetEffect extends OneShotEffect { int amount; - String targetName; public ExileCardsFromTopOfLibraryTargetEffect(int amount) { this(amount, null); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java index 76594aa066..adfc194ec1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreaturesFromExileEffect.java @@ -10,9 +10,10 @@ import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; +// TODO: Remove this class? Nothing uses it. public class ReturnCreaturesFromExileEffect extends OneShotEffect { - private UUID exileId; + private final UUID exileId; private boolean byOwner; public ReturnCreaturesFromExileEffect(UUID exileId, boolean byOwner, String description) { @@ -23,9 +24,10 @@ public class ReturnCreaturesFromExileEffect extends OneShotEffect { } - public ReturnCreaturesFromExileEffect(final ReturnCreaturesFromExileEffect effect) { + private ReturnCreaturesFromExileEffect(final ReturnCreaturesFromExileEffect effect) { super(effect); this.exileId = effect.exileId; + this.byOwner = effect.byOwner; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/MustBeBlockedByAllAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/MustBeBlockedByAllAttachedEffect.java index 38fccf97aa..3d15d8836e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/MustBeBlockedByAllAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/MustBeBlockedByAllAttachedEffect.java @@ -29,6 +29,7 @@ public class MustBeBlockedByAllAttachedEffect extends RequirementEffect { public MustBeBlockedByAllAttachedEffect(final MustBeBlockedByAllAttachedEffect effect) { super(effect); + this.attachmentType = effect.attachmentType; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureIfVehicleEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureIfVehicleEffect.java index b90851be6d..e3f053631c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureIfVehicleEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureIfVehicleEffect.java @@ -11,7 +11,7 @@ import mage.game.permanent.Permanent; */ public class BecomesCreatureIfVehicleEffect extends ContinuousEffectImpl { - private CardType addedType = CardType.CREATURE; + private static final CardType addedType = CardType.CREATURE; public BecomesCreatureIfVehicleEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java index 61249801e7..82bbbb7b4a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java @@ -22,7 +22,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { protected boolean addStillALandText; protected boolean loseName; protected boolean keepAbilities; - protected boolean removeSubtypes = false; + protected boolean removeSubtypes; public BecomesCreatureTargetEffect(Token token, boolean loseAllAbilities, boolean stillALand, Duration duration) { @@ -58,6 +58,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { this.addStillALandText = effect.addStillALandText; this.loseName = effect.loseName; this.keepAbilities = effect.keepAbilities; + this.removeSubtypes = effect.removeSubtypes; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java index ad66fc7eee..74679bb7d7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlTargetEffect.java @@ -57,6 +57,7 @@ public class GainControlTargetEffect extends ContinuousEffectImpl { super(effect); this.controllingPlayerId = effect.controllingPlayerId; this.fixedControl = effect.fixedControl; + this.firstControlChange = effect.firstControlChange; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/AmassEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/AmassEffect.java index 10f4296947..ec75295da0 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/AmassEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/AmassEffect.java @@ -32,7 +32,7 @@ public class AmassEffect extends OneShotEffect { } private final DynamicValue amassNumber; - private UUID amassedCreatureId = null; + private UUID amassedCreatureId; public AmassEffect(int amassNumber) { this(StaticValue.get(amassNumber)); @@ -53,6 +53,7 @@ public class AmassEffect extends OneShotEffect { private AmassEffect(final AmassEffect effect) { super(effect); this.amassNumber = effect.amassNumber; + this.amassedCreatureId = effect.amassedCreatureId; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ProtectionChosenColorAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ProtectionChosenColorAttachedEffect.java index 01bb7f25ab..47febba291 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/ProtectionChosenColorAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/ProtectionChosenColorAttachedEffect.java @@ -39,6 +39,7 @@ public class ProtectionChosenColorAttachedEffect extends ContinuousEffectImpl { this.protectionAbility = effect.protectionAbility.copy(); } this.notRemoveItself = effect.notRemoveItself; + this.notRemoveControlled = effect.notRemoveControlled; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java index 9032e9f509..dbd0994e35 100644 --- a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java @@ -135,7 +135,7 @@ class ReturnAttackerToHandTargetCost extends CostImpl { filter.add(UnblockedPredicate.instance); } - private UUID defendingPlayerId = null; + private UUID defendingPlayerId; public ReturnAttackerToHandTargetCost() { this.addTarget(new TargetControlledPermanent(filter)); @@ -144,6 +144,7 @@ class ReturnAttackerToHandTargetCost extends CostImpl { public ReturnAttackerToHandTargetCost(ReturnAttackerToHandTargetCost cost) { super(cost); + this.defendingPlayerId = cost.defendingPlayerId; } @Override @@ -152,8 +153,7 @@ class ReturnAttackerToHandTargetCost extends CostImpl { for (UUID targetId : targets.get(0).getTargets()) { Permanent permanent = game.getPermanent(targetId); Player controller = game.getPlayer(controllerId); - if (permanent == null - || controller == null) { + if (permanent == null || controller == null) { return false; } defendingPlayerId = game.getCombat().getDefenderId(permanent.getId()); diff --git a/Mage/src/main/java/mage/abilities/keyword/VanishingUpkeepAbility.java b/Mage/src/main/java/mage/abilities/keyword/VanishingUpkeepAbility.java index 5a0c14bf37..bdcacc7556 100644 --- a/Mage/src/main/java/mage/abilities/keyword/VanishingUpkeepAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/VanishingUpkeepAbility.java @@ -12,8 +12,8 @@ import mage.util.CardUtil; public class VanishingUpkeepAbility extends BeginningOfUpkeepTriggeredAbility { - private int vanishingAmount; - private String permanentType; + private final int vanishingAmount; + private final String permanentType; public VanishingUpkeepAbility(int vanishingEffect) { super(new VanishingEffect(), TargetController.YOU, false); @@ -27,9 +27,10 @@ public class VanishingUpkeepAbility extends BeginningOfUpkeepTriggeredAbility { this.permanentType = permanentType; } - public VanishingUpkeepAbility(final VanishingUpkeepAbility ability) { + private VanishingUpkeepAbility(final VanishingUpkeepAbility ability) { super(ability); this.vanishingAmount = ability.vanishingAmount; + this.permanentType = ability.permanentType; } @Override @@ -63,15 +64,16 @@ class VanishingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent p = game.getPermanent(source.getSourceId()); - if (p != null) { - int amount = p.getCounters(game).getCount(CounterType.TIME); - if (amount > 0) { - p.removeCounters(CounterType.TIME.createInstance(), source, game); - game.informPlayers("Removed a time counter from " + p.getLogName() + " (" + amount + " left)"); - } - return true; + if (p == null) { + return false; } - return false; + + int amount = p.getCounters(game).getCount(CounterType.TIME); + if (amount > 0) { + p.removeCounters(CounterType.TIME.createInstance(), source, game); + game.informPlayers("Removed a time counter from " + p.getLogName() + " (" + amount + " left)"); + } + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java index 76ab737115..4d84a04140 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java @@ -73,7 +73,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { private final FilterPermanent filter; private final boolean onlyColors; // false if mana types can be produced (also Colorless mana), if true only colors can be produced (no Colorless mana). - private boolean inManaTypeCalculation = false; + private boolean inManaTypeCalculation; AnyColorLandsProduceManaEffect(TargetController targetController, boolean onlyColors, FilterPermanent filter) { super(); @@ -93,6 +93,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { super(effect); this.filter = effect.filter.copy(); this.onlyColors = effect.onlyColors; + this.inManaTypeCalculation = effect.inManaTypeCalculation; } @Override diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java index c72bc21b2e..19336d2b65 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java @@ -57,7 +57,7 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect { private final FilterPermanent filter; private final boolean onlyColors; // false if mana types can be produced (also Colorless mana), if true only colors can be produced (no Colorless mana). - private boolean inManaTypeCalculation = false; + private boolean inManaTypeCalculation; public AnyColorPermanentTypesManaEffect(TargetController targetController, boolean onlyColors, FilterPermanent permanentTypes) { super(); @@ -72,6 +72,7 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect { super(effect); this.filter = effect.filter.copy(); this.onlyColors = effect.onlyColors; + this.inManaTypeCalculation = effect.inManaTypeCalculation; } @Override diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 4da2e7772e..e963ffb255 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -85,9 +85,9 @@ public class GameState implements Serializable, Copyable { private Exile exile; private Battlefield battlefield; private int turnNum = 1; - private int stepNum = 0; - private UUID turnId = null; - private boolean extraTurn = false; + private int stepNum; + private UUID turnId; + private boolean extraTurn; private boolean legendaryRuleActive = true; private boolean gameOver; private boolean paused; @@ -105,8 +105,8 @@ public class GameState implements Serializable, Copyable { private int permanentOrderNumber; private final Map usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>(); private Set commandersToStay = new HashSet<>(); // commanders that do not go back to command zone - private boolean manaBurn = false; - private boolean hasDayNight = false; + private boolean manaBurn; + private boolean hasDayNight; private boolean isDaytime = true; private int applyEffectsCounter; // Upcounting number of each applyEffects execution @@ -157,6 +157,7 @@ public class GameState implements Serializable, Copyable { this.battlefield = state.battlefield.copy(); this.turnNum = state.turnNum; this.stepNum = state.stepNum; + this.turnId = state.turnId; this.extraTurn = state.extraTurn; this.legendaryRuleActive = state.legendaryRuleActive; this.effects = state.effects.copy(); @@ -199,6 +200,7 @@ public class GameState implements Serializable, Copyable { this.commandersToStay.addAll(state.commandersToStay); this.hasDayNight = state.hasDayNight; this.isDaytime = state.isDaytime; + this.manaBurn = state.manaBurn; } public void clearOnGameRestart() { diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java index 39aca940b8..6eccd681c5 100644 --- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java @@ -39,6 +39,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl { public GameTinyLeadersImpl(final GameTinyLeadersImpl game) { super(game); this.alsoHand = game.alsoHand; + this.alsoLibrary = game.alsoLibrary; this.startingPlayerSkipsDraw = game.startingPlayerSkipsDraw; } @@ -50,35 +51,37 @@ public abstract class GameTinyLeadersImpl extends GameImpl { // move tiny leader to command zone for (UUID playerId : state.getPlayerList(startingPlayerId)) { Player player = getPlayer(playerId); - if (player != null) { - String commanderName = player.getMatchPlayer().getDeck().getName(); - Card commander = findCommander(this, player, commanderName); + if (player == null) { + continue; + } + + String commanderName = player.getMatchPlayer().getDeck().getName(); + Card commander = findCommander(this, player, commanderName); + if (commander != null) { + // already exists - just move to zone (example: game restart by Karn Liberated) + commander.moveToZone(Zone.COMMAND, null, this, true); + } else { + // create new commander + commander = getCommanderCard(commanderName, player.getId()); if (commander != null) { - // already exists - just move to zone (example: game restart by Karn Liberated) + Set cards = new HashSet<>(); + cards.add(commander); + this.loadCards(cards, playerId); + player.addCommanderId(commander.getId()); commander.moveToZone(Zone.COMMAND, null, this, true); + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); + ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary, false, "Commander")); + ability.addEffect(new CommanderCostModification(commander)); + // Commander rule #4 was removed Jan. 18, 2016 + // ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); + CommanderInfoWatcher watcher = new CommanderInfoWatcher("Commander", commander.getId(), false); + getState().addWatcher(watcher); + watcher.addCardInfoToCommander(this); + this.getState().addAbility(ability, null); } else { - // create new commander - commander = getCommanderCard(commanderName, player.getId()); - if (commander != null) { - Set cards = new HashSet<>(); - cards.add(commander); - this.loadCards(cards, playerId); - player.addCommanderId(commander.getId()); - commander.moveToZone(Zone.COMMAND, null, this, true); - Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); - ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary, false, "Commander")); - ability.addEffect(new CommanderCostModification(commander)); - // Commander rule #4 was removed Jan. 18, 2016 - // ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); - CommanderInfoWatcher watcher = new CommanderInfoWatcher("Commander", commander.getId(), false); - getState().addWatcher(watcher); - watcher.addCardInfoToCommander(this); - this.getState().addAbility(ability, null); - } else { - // GameWorker.call processing errors and write it in magediag.log by defalt - // Test use case: create tiny game with random generated deck - game freezes with empty battlefield - throw new IllegalStateException("Commander card could not be created. Name: [" + player.getMatchPlayer().getDeck().getName() + ']'); - } + // GameWorker.call processing errors and write it in magediag.log by defalt + // Test use case: create tiny game with random generated deck - game freezes with empty battlefield + throw new IllegalStateException("Commander card could not be created. Name: [" + player.getMatchPlayer().getDeck().getName() + ']'); } } } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index e0f809d4f8..1d55e613a6 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -73,7 +73,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected boolean morphed = false; protected int classLevel = 1; protected final Set goadingPlayers = new HashSet<>(); + // The UUID of the controller under who the permanent first entered the battelfield under. protected UUID originalControllerId; + // The UUID of the current controller. protected UUID controllerId; protected UUID beforeResetControllerId; protected int damage; @@ -129,6 +131,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.flipped = permanent.flipped; this.originalControllerId = permanent.originalControllerId; this.controllerId = permanent.controllerId; + this.beforeResetControllerId = permanent.controllerId; this.damage = permanent.damage; this.controlledFromStartOfControllerTurn = permanent.controlledFromStartOfControllerTurn; this.turnsOnBattlefield = permanent.turnsOnBattlefield; @@ -141,11 +144,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.deathtouched = permanent.deathtouched; this.markedLifelink = permanent.markedLifelink; - for (Map.Entry> entry : permanent.connectedCards.entrySet()) { - this.connectedCards.put(entry.getKey(), entry.getValue()); - } + this.connectedCards.putAll(permanent.connectedCards); if (permanent.dealtDamageByThisTurn != null) { - dealtDamageByThisTurn = new HashSet<>(permanent.dealtDamageByThisTurn); + this.dealtDamageByThisTurn = new HashSet<>(permanent.dealtDamageByThisTurn); } if (permanent.markedDamage != null) { markedDamage = new ArrayList<>(); @@ -171,6 +172,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.bandedCards.addAll(permanent.bandedCards); this.timesLoyaltyUsed = permanent.timesLoyaltyUsed; this.transformCount = permanent.transformCount; + this.removedFromCombat = permanent.removedFromCombat; this.morphed = permanent.morphed; this.manifested = permanent.manifested; @@ -193,6 +195,14 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { abilities.setControllerId(controllerId); } + /** + * Used to override the original controller of a permanent to be different than the player who cast the spell + * or activated the ability. + * + * E.g. Xantcha, Sleeper Agent. Who enters the battlefield directly under someone else's control. + * + * @param originalControllerId The UUID of the original controller of the permanent + */ @Override public void setOriginalControllerId(UUID originalControllerId) { this.originalControllerId = originalControllerId; @@ -764,6 +774,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean checkControlChanged(Game game) { + // TODO Put a break point and see what's going on with controllerID, beforeResetControllerID, and originalControllerID if (!controllerId.equals(beforeResetControllerId)) { this.removeFromCombat(game); this.controlledFromStartOfControllerTurn = false; @@ -904,7 +915,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { * @return */ private int doDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List appliedEffects) { - int damageDone = 0; + int damageDone; if (damageAmount < 1 || !canDamage(game.getObject(attackerId), game)) { return 0; } @@ -1758,20 +1769,22 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List appliedEffects) { Zone fromZone = game.getState().getZone(objectId); Player controller = game.getPlayer(controllerId); - if (controller != null) { - ZoneChangeEvent event = new ZoneChangeEvent(this, source, controllerId, fromZone, toZone, appliedEffects); - ZoneChangeInfo zoneChangeInfo; - if (toZone == Zone.LIBRARY) { - zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */); - } else { - zoneChangeInfo = new ZoneChangeInfo(event); - } - boolean successfullyMoved = ZonesHandler.moveCard(zoneChangeInfo, game, source); - //20180810 - 701.3d - detachAllAttachments(game); - return successfullyMoved; + if (controller == null) { + return false; } - return false; + + ZoneChangeEvent event = new ZoneChangeEvent(this, source, controllerId, fromZone, toZone, appliedEffects); + ZoneChangeInfo zoneChangeInfo; + if (toZone == Zone.LIBRARY) { + zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */); + } else { + zoneChangeInfo = new ZoneChangeInfo(event); + } + boolean successfullyMoved = ZonesHandler.moveCard(zoneChangeInfo, game, source); + //20180810 - 701.3d + detachAllAttachments(game); + + return successfullyMoved; } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java index ca2655b9cc..900bca5353 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -81,6 +81,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { this.expansionSetCodeChecked = token.expansionSetCodeChecked; this.copySourceCard = token.copySourceCard; // will never be changed this.availableImageSetCodes = token.availableImageSetCodes; + this.tokenDescriptor = token.tokenDescriptor; } @Override diff --git a/Mage/src/main/java/mage/game/stack/SpellStack.java b/Mage/src/main/java/mage/game/stack/SpellStack.java index 473bb29b3e..8acac812bd 100644 --- a/Mage/src/main/java/mage/game/stack/SpellStack.java +++ b/Mage/src/main/java/mage/game/stack/SpellStack.java @@ -30,6 +30,7 @@ public class SpellStack extends ArrayDeque { for (StackObject spell : stack) { this.addLast(spell.copy()); } + this.dateLastAdded = stack.dateLastAdded; } //resolve top StackObject