From 1852de2f47c6d5c816b48f3db0cfa652d36ba428 Mon Sep 17 00:00:00 2001 From: Ingmar Goudt Date: Sun, 7 Apr 2019 20:22:14 +0200 Subject: [PATCH 01/76] refactor the copy functionality for no-args watchers --- Mage/src/main/java/mage/game/GameImpl.java | 10 ++- Mage/src/main/java/mage/watchers/Watcher.java | 71 +++++++++++++++---- ...fDamageAPlayerReceivedThisTurnWatcher.java | 12 ---- .../common/AttackedLastTurnWatcher.java | 17 ----- .../AttackedOrBlockedThisCombatWatcher.java | 11 --- .../common/AttackedThisTurnWatcher.java | 11 --- .../common/BlockedAttackerWatcher.java | 14 ---- ...kedByOnlyOneCreatureThisCombatWatcher.java | 9 --- .../common/BlockedThisTurnWatcher.java | 13 +--- .../watchers/common/BloodthirstWatcher.java | 6 +- .../CardsAmountDrawnThisTurnWatcher.java | 9 --- .../CardsDrawnDuringDrawStepWatcher.java | 11 --- .../common/CardsPutIntoGraveyardWatcher.java | 12 ---- .../common/CastFromGraveyardWatcher.java | 9 --- .../watchers/common/CastFromHandWatcher.java | 7 -- .../common/CastSpellLastTurnWatcher.java | 17 ----- .../common/CastSpellYourLastTurnWatcher.java | 10 --- .../ChooseBlockersRedundancyWatcher.java | 11 --- .../CreatureAttackedWhichPlayerWatcher.java | 12 ---- .../common/CreatureWasCastWatcher.java | 9 --- .../watchers/common/CreaturesDiedWatcher.java | 11 --- .../watchers/common/DamageDoneWatcher.java | 6 -- ...heBattlefieldWhileSpellWasCastWatcher.java | 10 --- .../common/FirstSpellCastThisTurnWatcher.java | 11 --- .../watchers/common/GravestormWatcher.java | 10 --- .../mage/watchers/common/LandfallWatcher.java | 11 --- .../LifeLossOtherFromCombatWatcher.java | 10 --- .../common/ManaSpentToCastWatcher.java | 10 --- .../mage/watchers/common/MiracleWatcher.java | 12 ---- .../mage/watchers/common/MorbidWatcher.java | 10 --- ...rOfTimesPermanentTargetedATurnWatcher.java | 10 --- .../PermanentsEnteredBattlefieldWatcher.java | 11 --- ...EnteredBattlefieldYourLastTurnWatcher.java | 11 --- .../common/PermanentsSacrificedWatcher.java | 9 --- .../watchers/common/PlanarRollWatcher.java | 7 -- .../mage/watchers/common/PlayLandWatcher.java | 11 --- .../common/PlayerAttackedStepWatcher.java | 12 ---- .../common/PlayerAttackedWatcher.java | 13 ---- .../common/PlayerCastCreatureWatcher.java | 10 --- .../common/PlayerDamagedBySourceWatcher.java | 9 +-- .../common/PlayerGainedLifeWatcher.java | 11 --- .../PlayerLostLifeNonCombatWatcher.java | 12 ---- .../common/PlayerLostLifeWatcher.java | 12 ---- .../PlayersAttackedLastTurnWatcher.java | 12 ---- .../PlayersAttackedThisTurnWatcher.java | 13 ---- .../mage/watchers/common/ProwlWatcher.java | 12 ---- .../mage/watchers/common/RevoltWatcher.java | 10 --- .../common/SourceDidDamageWatcher.java | 10 --- .../watchers/common/SpellsCastWatcher.java | 10 --- .../common/WasBlockedThisTurnWatcher.java | 10 --- .../watchers/common/ZuberasDiedWatcher.java | 10 --- Mage/src/test/java/mage/WatcherTest.java | 4 ++ 52 files changed, 73 insertions(+), 538 deletions(-) create mode 100644 Mage/src/test/java/mage/WatcherTest.java diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 8d2589f68f4..58b50ee951b 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1025,8 +1025,14 @@ public abstract class GameImpl implements Game, Serializable { } public void initPlayerDefaultWatchers(UUID playerId) { - getState().addWatcher(new PlayerDamagedBySourceWatcher(playerId)); - getState().addWatcher(new BloodthirstWatcher(playerId)); + PlayerDamagedBySourceWatcher playerDamagedBySourceWatcher = new PlayerDamagedBySourceWatcher(); + playerDamagedBySourceWatcher.setControllerId(playerId); + + getState().addWatcher(playerDamagedBySourceWatcher); + + BloodthirstWatcher bloodthirstWatcher = new BloodthirstWatcher(); + bloodthirstWatcher.setControllerId(playerId); + getState().addWatcher(bloodthirstWatcher); } protected void sendStartMessage(Player choosingPlayer, Player startingPlayer) { diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java index af3ceddd727..f954166119e 100644 --- a/Mage/src/main/java/mage/watchers/Watcher.java +++ b/Mage/src/main/java/mage/watchers/Watcher.java @@ -1,17 +1,20 @@ package mage.watchers; -import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.UUID; +import mage.Mana; import mage.constants.WatcherScope; import mage.game.Game; +import mage.game.turn.Step; import mage.game.events.GameEvent; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + /** - * * watches for certain game events to occur and flags condition * * @author BetaSteward_at_googlemail.com @@ -26,7 +29,6 @@ public abstract class Watcher implements Serializable { protected final WatcherScope scope; - public Watcher(WatcherScope scope) { this.scope = scope; } @@ -62,8 +64,8 @@ public abstract class Watcher implements Serializable { return controllerId + getBasicKey(); case CARD: return sourceId + getBasicKey(); - default: - return getBasicKey(); + default: + return getBasicKey(); } } @@ -75,18 +77,61 @@ public abstract class Watcher implements Serializable { condition = false; } - protected String getBasicKey(){ + protected String getBasicKey() { return getClass().getSimpleName(); } public abstract void watch(GameEvent event, Game game); - public T copy(){ + public T copy() { try { - Constructor constructor = this.getClass().getDeclaredConstructor(getClass()); + List constructors = Arrays.asList(this.getClass().getConstructors()); + if (constructors.size() > 1) { + logger.error(getClass().getSimpleName() + " has multiple constructors"); + return null; + } + + Constructor constructor = (Constructor) constructors.get(0); + if (constructor.getParameterCount() > 0) { + logger.error(getClass().getSimpleName() + " constructor has arguments, should be 0 and inject the parameters by setters"); + return null; + } constructor.setAccessible(true); - return (T) constructor.newInstance(this); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + T watcher = (T) constructor.newInstance(); + List allFields = new ArrayList<>(); + allFields.addAll(Arrays.asList(getClass().getDeclaredFields())); + allFields.addAll(Arrays.asList(getClass().getSuperclass().getDeclaredFields())); + for (Field field : allFields) { + field.setAccessible(true); + if (field.getType().isPrimitive()) { + field.set(watcher, field.get(this)); + } + else if(field.getType() == Step.class){ + field.set(watcher, field.get(this)); + } + else if (field.getType() == Mana.class) { + field.set(watcher, field.get(this)); + } else if (field.getType() == UUID.class) { + field.set(watcher, field.get(this)); + } else if (field.getType().isEnum()) { + field.set(watcher, field.get(this)); + } else if (field.getType() == Set.class) { + ((Set) field.get(watcher)).clear(); + ((Set) field.get(watcher)).addAll((Set) field.get(this)); + } else if (field.getType() == Map.class) { + ((Map) field.get(watcher)).clear(); + ((Map) field.get(watcher)).putAll((Map) field.get(this)); + } else if (field.getType() == List.class) { + ((List) field.get(watcher)).clear(); + ((List) field.get(watcher)).addAll((List) field.get(this)); + } else { + if (field.getType() != Logger.class) { + logger.error(field.getType() + " can not be copied"); + } + } + } + return watcher; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { logger.error("Can't copy watcher: " + e.getMessage(), e); } return null; diff --git a/Mage/src/main/java/mage/watchers/common/AmountOfDamageAPlayerReceivedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AmountOfDamageAPlayerReceivedThisTurnWatcher.java index c7e63fc85fe..26dda05ed90 100644 --- a/Mage/src/main/java/mage/watchers/common/AmountOfDamageAPlayerReceivedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AmountOfDamageAPlayerReceivedThisTurnWatcher.java @@ -24,13 +24,6 @@ public class AmountOfDamageAPlayerReceivedThisTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public AmountOfDamageAPlayerReceivedThisTurnWatcher(final AmountOfDamageAPlayerReceivedThisTurnWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfDamageReceivedThisTurn.entrySet()) { - amountOfDamageReceivedThisTurn.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { @@ -50,9 +43,4 @@ public class AmountOfDamageAPlayerReceivedThisTurnWatcher extends Watcher { public void reset() { amountOfDamageReceivedThisTurn.clear(); } - - @Override - public AmountOfDamageAPlayerReceivedThisTurnWatcher copy() { - return new AmountOfDamageAPlayerReceivedThisTurnWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java index c6af696380d..93a26edd102 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedLastTurnWatcher.java @@ -27,18 +27,6 @@ public class AttackedLastTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public AttackedLastTurnWatcher(final AttackedLastTurnWatcher watcher) { - super(watcher); - for (Entry> entry : watcher.attackedLastTurnCreatures.entrySet()) { - Set allAttackersCopy = new HashSet<>(entry.getValue()); - attackedLastTurnCreatures.put(entry.getKey(), allAttackersCopy); - } - for (Entry> entry : watcher.attackedThisTurnCreatures.entrySet()) { - Set allAttackersCopy = new HashSet<>(entry.getValue()); - attackedThisTurnCreatures.put(entry.getKey(), allAttackersCopy); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { @@ -79,9 +67,4 @@ public class AttackedLastTurnWatcher extends Watcher { return new HashSet<>(); } - @Override - public AttackedLastTurnWatcher copy() { - return new AttackedLastTurnWatcher(this); - } - } diff --git a/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java index 0af8f15aa7a..7a9bf240a90 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedOrBlockedThisCombatWatcher.java @@ -26,12 +26,6 @@ public class AttackedOrBlockedThisCombatWatcher extends Watcher { super(WatcherScope.GAME); } - public AttackedOrBlockedThisCombatWatcher(final AttackedOrBlockedThisCombatWatcher watcher) { - super(watcher); - this.getAttackedThisTurnCreatures().addAll(watcher.getAttackedThisTurnCreatures()); - this.getBlockedThisTurnCreatures().addAll(watcher.getBlockedThisTurnCreatures()); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BEGIN_COMBAT_STEP_PRE) { @@ -53,9 +47,4 @@ public class AttackedOrBlockedThisCombatWatcher extends Watcher { return this.blockedThisTurnCreatures; } - @Override - public AttackedOrBlockedThisCombatWatcher copy() { - return new AttackedOrBlockedThisCombatWatcher(this); - } - } diff --git a/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java index adedcff2cc5..72ea6cfae37 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java @@ -25,12 +25,6 @@ public class AttackedThisTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public AttackedThisTurnWatcher(final AttackedThisTurnWatcher watcher) { - super(watcher); - this.attackedThisTurnCreatures.addAll(watcher.attackedThisTurnCreatures); - this.attackedThisTurnCreaturesCounts.putAll(watcher.attackedThisTurnCreaturesCounts); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { @@ -58,11 +52,6 @@ public class AttackedThisTurnWatcher extends Watcher { return false; } - @Override - public AttackedThisTurnWatcher copy() { - return new AttackedThisTurnWatcher(this); - } - @Override public void reset() { super.reset(); diff --git a/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java b/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java index 57506bf6832..7d70b8f10ed 100644 --- a/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/BlockedAttackerWatcher.java @@ -25,20 +25,6 @@ public class BlockedAttackerWatcher extends Watcher { super(WatcherScope.GAME); } - public BlockedAttackerWatcher(final BlockedAttackerWatcher watcher) { - super(watcher); - for (MageObjectReference mageObjectReference : watcher.blockData.keySet()) { - Set blockedAttackers = new HashSet<>(); - blockedAttackers.addAll(watcher.blockData.get(mageObjectReference)); - blockData.put(mageObjectReference, blockedAttackers); - } - } - -// @Override -// public BlockedAttackerWatcher copy() { -// return new BlockedAttackerWatcher(this); -// } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == EventType.BLOCKER_DECLARED) { diff --git a/Mage/src/main/java/mage/watchers/common/BlockedByOnlyOneCreatureThisCombatWatcher.java b/Mage/src/main/java/mage/watchers/common/BlockedByOnlyOneCreatureThisCombatWatcher.java index b058d6136ea..901ef40009e 100644 --- a/Mage/src/main/java/mage/watchers/common/BlockedByOnlyOneCreatureThisCombatWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/BlockedByOnlyOneCreatureThisCombatWatcher.java @@ -21,11 +21,6 @@ public class BlockedByOnlyOneCreatureThisCombatWatcher extends Watcher { super(WatcherScope.GAME); } - public BlockedByOnlyOneCreatureThisCombatWatcher(final BlockedByOnlyOneCreatureThisCombatWatcher watcher) { - super(watcher); - this.blockedByOneCreature.putAll(watcher.blockedByOneCreature); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BEGIN_COMBAT_STEP_PRE) { @@ -64,8 +59,4 @@ public class BlockedByOnlyOneCreatureThisCombatWatcher extends Watcher { return null; } - @Override - public BlockedByOnlyOneCreatureThisCombatWatcher copy() { - return new BlockedByOnlyOneCreatureThisCombatWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java index 6d8b27ad052..70b08e29c3d 100644 --- a/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/BlockedThisTurnWatcher.java @@ -16,21 +16,10 @@ import java.util.Set; */ public class BlockedThisTurnWatcher extends Watcher { - private final Set blockedThisTurnCreatures; + private final Set blockedThisTurnCreatures = new HashSet<>(); public BlockedThisTurnWatcher() { super(WatcherScope.GAME); - blockedThisTurnCreatures = new HashSet<>(); - } - - public BlockedThisTurnWatcher(final BlockedThisTurnWatcher watcher) { - super(watcher); - blockedThisTurnCreatures = new HashSet<>(watcher.blockedThisTurnCreatures); - } - - @Override - public Watcher copy() { - return new BlockedThisTurnWatcher(this); } @Override diff --git a/Mage/src/main/java/mage/watchers/common/BloodthirstWatcher.java b/Mage/src/main/java/mage/watchers/common/BloodthirstWatcher.java index b27a6a62473..b70f17779b0 100644 --- a/Mage/src/main/java/mage/watchers/common/BloodthirstWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/BloodthirstWatcher.java @@ -15,14 +15,10 @@ import java.util.UUID; * @author Loki */ public class BloodthirstWatcher extends Watcher { - public BloodthirstWatcher(UUID controllerId) { + public BloodthirstWatcher() { super(WatcherScope.PLAYER); - this.controllerId = controllerId; } - public BloodthirstWatcher(final BloodthirstWatcher watcher) { - super(watcher); - } @Override public void watch(GameEvent event, Game game) { diff --git a/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java index 71c1a703060..60be9cbbbe7 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java @@ -25,10 +25,6 @@ public class CardsAmountDrawnThisTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public CardsAmountDrawnThisTurnWatcher(final CardsAmountDrawnThisTurnWatcher watcher) { - super(watcher); - amountOfCardsDrawnThisTurn.putAll(watcher.amountOfCardsDrawnThisTurn); - } @Override public void watch(GameEvent event, Game game) { @@ -61,9 +57,4 @@ public class CardsAmountDrawnThisTurnWatcher extends Watcher { public int getAmountCardsDrawn(UUID playerId) { return amountOfCardsDrawnThisTurn.getOrDefault(playerId, 0); } - - @Override - public CardsAmountDrawnThisTurnWatcher copy() { - return new CardsAmountDrawnThisTurnWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CardsDrawnDuringDrawStepWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsDrawnDuringDrawStepWatcher.java index 60cc296e91f..c416860cc8e 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsDrawnDuringDrawStepWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsDrawnDuringDrawStepWatcher.java @@ -27,13 +27,6 @@ public class CardsDrawnDuringDrawStepWatcher extends Watcher { super(WatcherScope.GAME); } - public CardsDrawnDuringDrawStepWatcher(final CardsDrawnDuringDrawStepWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfCardsDrawnThisTurn.entrySet()) { - amountOfCardsDrawnThisTurn.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DREW_CARD @@ -57,8 +50,4 @@ public class CardsDrawnDuringDrawStepWatcher extends Watcher { amountOfCardsDrawnThisTurn.clear(); } - @Override - public CardsDrawnDuringDrawStepWatcher copy() { - return new CardsDrawnDuringDrawStepWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java index 004019037d5..6ab8c8a9528 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java @@ -32,14 +32,6 @@ public class CardsPutIntoGraveyardWatcher extends Watcher { super(WatcherScope.GAME); } - public CardsPutIntoGraveyardWatcher(final CardsPutIntoGraveyardWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfCardsThisTurn.entrySet()) { - amountOfCardsThisTurn.put(entry.getKey(), entry.getValue()); - } - this.cardsPutToGraveyardFromBattlefield.addAll(watcher.cardsPutToGraveyardFromBattlefield); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.UNTAP_STEP_PRE) { @@ -73,8 +65,4 @@ public class CardsPutIntoGraveyardWatcher extends Watcher { cardsPutToGraveyardFromBattlefield.clear(); } - @Override - public CardsPutIntoGraveyardWatcher copy() { - return new CardsPutIntoGraveyardWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CastFromGraveyardWatcher.java b/Mage/src/main/java/mage/watchers/common/CastFromGraveyardWatcher.java index 2950ef15b03..3391c0bf9fb 100644 --- a/Mage/src/main/java/mage/watchers/common/CastFromGraveyardWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CastFromGraveyardWatcher.java @@ -23,10 +23,6 @@ public class CastFromGraveyardWatcher extends Watcher { super(WatcherScope.GAME); } - public CastFromGraveyardWatcher(final CastFromGraveyardWatcher watcher) { - super(watcher); - } - @Override public void watch(GameEvent event, Game game) { /** @@ -55,9 +51,4 @@ public class CastFromGraveyardWatcher extends Watcher { super.reset(); spellsCastFromGraveyard.clear(); } - - @Override - public CastFromGraveyardWatcher copy() { - return new CastFromGraveyardWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CastFromHandWatcher.java b/Mage/src/main/java/mage/watchers/common/CastFromHandWatcher.java index 58983d93bf7..5fbaebb330d 100644 --- a/Mage/src/main/java/mage/watchers/common/CastFromHandWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CastFromHandWatcher.java @@ -21,9 +21,6 @@ public class CastFromHandWatcher extends Watcher { super(WatcherScope.GAME); } - public CastFromHandWatcher(final CastFromHandWatcher watcher) { - super(watcher); - } @Override public void watch(GameEvent event, Game game) { @@ -58,8 +55,4 @@ public class CastFromHandWatcher extends Watcher { spellsCastFromHand.clear(); } - @Override - public CastFromHandWatcher copy() { - return new CastFromHandWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java index 7f30b3b5ca4..718f7bc56a3 100644 --- a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java @@ -23,17 +23,6 @@ public class CastSpellLastTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public CastSpellLastTurnWatcher(final CastSpellLastTurnWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfSpellsCastOnCurrentTurn.entrySet()) { - amountOfSpellsCastOnCurrentTurn.put(entry.getKey(), entry.getValue()); - } - for (Entry entry : watcher.amountOfSpellsCastOnPrevTurn.entrySet()) { - amountOfSpellsCastOnPrevTurn.put(entry.getKey(), entry.getValue()); - } - this.spellsCastThisTurnInOrder.addAll(watcher.spellsCastThisTurnInOrder); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { @@ -81,10 +70,4 @@ public class CastSpellLastTurnWatcher extends Watcher { } return 0; } -// -// @Override -// public CastSpellLastTurnWatcher copy() { -// return new CastSpellLastTurnWatcher(this); -// } - } diff --git a/Mage/src/main/java/mage/watchers/common/CastSpellYourLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CastSpellYourLastTurnWatcher.java index f99ca683b4d..b13b6858382 100644 --- a/Mage/src/main/java/mage/watchers/common/CastSpellYourLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CastSpellYourLastTurnWatcher.java @@ -22,16 +22,6 @@ public class CastSpellYourLastTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public CastSpellYourLastTurnWatcher(final CastSpellYourLastTurnWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfSpellsCastOnCurrentTurn.entrySet()) { - amountOfSpellsCastOnCurrentTurn.put(entry.getKey(), entry.getValue()); - } - for (Entry entry : watcher.amountOfSpellsCastOnPrevTurn.entrySet()) { - amountOfSpellsCastOnPrevTurn.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { lastActivePlayer = game.getActivePlayerId(); diff --git a/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java b/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java index 4d8fceaef50..c93ed3b0a67 100644 --- a/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java @@ -20,23 +20,12 @@ public class ChooseBlockersRedundancyWatcher extends Watcher { // workaround for super(WatcherScope.GAME); } - public ChooseBlockersRedundancyWatcher(final ChooseBlockersRedundancyWatcher watcher) { - super(watcher); - this.copyCount = watcher.copyCount; - this.copyCountApply = watcher.copyCountApply; - } - @Override public void reset() { copyCount = 0; copyCountApply = 0; } - @Override - public ChooseBlockersRedundancyWatcher copy() { - return new ChooseBlockersRedundancyWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { } diff --git a/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java b/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java index 6ebaee8ee60..f9d647a932e 100644 --- a/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CreatureAttackedWhichPlayerWatcher.java @@ -23,13 +23,6 @@ public class CreatureAttackedWhichPlayerWatcher extends Watcher { super(WatcherScope.GAME); } - public CreatureAttackedWhichPlayerWatcher(final CreatureAttackedWhichPlayerWatcher watcher) { - super(watcher); - for (Entry entry : watcher.getPlayerAttackedThisTurnByCreature.entrySet()) { - getPlayerAttackedThisTurnByCreature.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { @@ -50,9 +43,4 @@ public class CreatureAttackedWhichPlayerWatcher extends Watcher { public void reset() { getPlayerAttackedThisTurnByCreature.clear(); } - - @Override - public CreatureAttackedWhichPlayerWatcher copy() { - return new CreatureAttackedWhichPlayerWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CreatureWasCastWatcher.java b/Mage/src/main/java/mage/watchers/common/CreatureWasCastWatcher.java index 11f1fbf5b9f..79da22b221e 100644 --- a/Mage/src/main/java/mage/watchers/common/CreatureWasCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CreatureWasCastWatcher.java @@ -25,10 +25,6 @@ public class CreatureWasCastWatcher extends Watcher { super(WatcherScope.GAME); } - public CreatureWasCastWatcher(final CreatureWasCastWatcher watcher) { - super(watcher); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { @@ -58,9 +54,4 @@ public class CreatureWasCastWatcher extends Watcher { super.reset(); creaturesCasted.clear(); } - - @Override - public CreatureWasCastWatcher copy() { - return new CreatureWasCastWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java b/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java index cf30c774900..f48166180e4 100644 --- a/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java @@ -22,12 +22,6 @@ public class CreaturesDiedWatcher extends Watcher { super(WatcherScope.GAME); } - public CreaturesDiedWatcher(final CreaturesDiedWatcher watcher) { - super(watcher); - this.amountOfCreaturesThatDiedByController.putAll(watcher.amountOfCreaturesThatDiedByController); - this.amountOfCreaturesThatDiedByOwner.putAll(watcher.amountOfCreaturesThatDiedByOwner); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { @@ -57,11 +51,6 @@ public class CreaturesDiedWatcher extends Watcher { return amountOfCreaturesThatDiedByOwner.getOrDefault(playerId, 0); } - @Override - public CreaturesDiedWatcher copy() { - return new CreaturesDiedWatcher(this); - } - public int getAmountOfCreaturesDiedThisTurn() { return amountOfCreaturesThatDiedByController.values().stream().mapToInt(x -> x).sum(); } diff --git a/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java b/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java index 0f28c0a3966..6e4008ab16a 100644 --- a/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DamageDoneWatcher.java @@ -40,12 +40,6 @@ public class DamageDoneWatcher extends Watcher { this.damagedObjects = new HashMap<>(); } - private DamageDoneWatcher(final DamageDoneWatcher watcher) { - super(watcher); - this.damagingObjects = new HashMap<>(watcher.damagingObjects); - this.damagedObjects = new HashMap<>(watcher.damagedObjects); - } - @Override public void watch(GameEvent event, Game game) { switch (event.getType()) { diff --git a/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java b/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java index 46266a82184..66a0b24ce0c 100644 --- a/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java @@ -29,11 +29,6 @@ public class DragonOnTheBattlefieldWhileSpellWasCastWatcher extends Watcher { super(WatcherScope.GAME); } - public DragonOnTheBattlefieldWhileSpellWasCastWatcher(final DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher) { - super(watcher); - this.castWithDragonOnTheBattlefield.addAll(watcher.castWithDragonOnTheBattlefield); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { @@ -67,9 +62,4 @@ public class DragonOnTheBattlefieldWhileSpellWasCastWatcher extends Watcher { public boolean castWithConditionTrue(UUID spellId) { return castWithDragonOnTheBattlefield.contains(spellId); } - - @Override - public DragonOnTheBattlefieldWhileSpellWasCastWatcher copy() { - return new DragonOnTheBattlefieldWhileSpellWasCastWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/FirstSpellCastThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/FirstSpellCastThisTurnWatcher.java index c60052f27dd..80a1a329ee2 100644 --- a/Mage/src/main/java/mage/watchers/common/FirstSpellCastThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/FirstSpellCastThisTurnWatcher.java @@ -22,12 +22,6 @@ public class FirstSpellCastThisTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public FirstSpellCastThisTurnWatcher(final FirstSpellCastThisTurnWatcher watcher) { - super(watcher); - playerFirstSpellCast.putAll(watcher.playerFirstSpellCast); - playerFirstCastSpell.putAll(watcher.playerFirstCastSpell); - } - @Override public void watch(GameEvent event, Game game) { switch (event.getType()) { @@ -44,11 +38,6 @@ public class FirstSpellCastThisTurnWatcher extends Watcher { } } - @Override - public FirstSpellCastThisTurnWatcher copy() { - return new FirstSpellCastThisTurnWatcher(this); - } - @Override public void reset() { super.reset(); diff --git a/Mage/src/main/java/mage/watchers/common/GravestormWatcher.java b/Mage/src/main/java/mage/watchers/common/GravestormWatcher.java index 3b9afad51f9..86f25b35f89 100644 --- a/Mage/src/main/java/mage/watchers/common/GravestormWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/GravestormWatcher.java @@ -22,11 +22,6 @@ public class GravestormWatcher extends Watcher { super(WatcherScope.GAME); } - public GravestormWatcher(final GravestormWatcher watcher) { - super(watcher); - this.gravestormCount = watcher.gravestormCount; - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE) { @@ -46,9 +41,4 @@ public class GravestormWatcher extends Watcher { public int getGravestormCount() { return this.gravestormCount; } - - @Override - public GravestormWatcher copy() { - return new GravestormWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/LandfallWatcher.java b/Mage/src/main/java/mage/watchers/common/LandfallWatcher.java index ce1827b8c38..36e1293c5ef 100644 --- a/Mage/src/main/java/mage/watchers/common/LandfallWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/LandfallWatcher.java @@ -22,17 +22,6 @@ public class LandfallWatcher extends Watcher { super(WatcherScope.GAME); } - private LandfallWatcher(final LandfallWatcher watcher) { - super(watcher); - playerPlayedLand.addAll(watcher.playerPlayedLand); - landEnteredBattlefield.addAll(watcher.landEnteredBattlefield); - } - - @Override - public LandfallWatcher copy() { - return new LandfallWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { diff --git a/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java b/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java index d35a740342f..71790be2359 100644 --- a/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/LifeLossOtherFromCombatWatcher.java @@ -22,11 +22,6 @@ public class LifeLossOtherFromCombatWatcher extends Watcher { super(WatcherScope.GAME); } - public LifeLossOtherFromCombatWatcher(final LifeLossOtherFromCombatWatcher watcher) { - super(watcher); - this.players.addAll(watcher.players); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.LOST_LIFE && !event.getFlag()) { @@ -52,9 +47,4 @@ public class LifeLossOtherFromCombatWatcher extends Watcher { super.reset(); players.clear(); } - - @Override - public LifeLossOtherFromCombatWatcher copy() { - return new LifeLossOtherFromCombatWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java b/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java index 127574ca081..797640be338 100644 --- a/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ManaSpentToCastWatcher.java @@ -23,11 +23,6 @@ public class ManaSpentToCastWatcher extends Watcher { super(WatcherScope.CARD); } - public ManaSpentToCastWatcher(final ManaSpentToCastWatcher watcher) { - super(watcher); - this.payment = watcher.payment; - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST && event.getZone() == Zone.HAND) { @@ -43,11 +38,6 @@ public class ManaSpentToCastWatcher extends Watcher { } } - @Override - public ManaSpentToCastWatcher copy() { - return new ManaSpentToCastWatcher(this); - } - public Mana getAndResetLastPayment() { Mana returnPayment = null; if (payment != null) { diff --git a/Mage/src/main/java/mage/watchers/common/MiracleWatcher.java b/Mage/src/main/java/mage/watchers/common/MiracleWatcher.java index 325b958d594..555afc47f44 100644 --- a/Mage/src/main/java/mage/watchers/common/MiracleWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/MiracleWatcher.java @@ -32,13 +32,6 @@ public class MiracleWatcher extends Watcher { super(WatcherScope.GAME); } - public MiracleWatcher(final MiracleWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfCardsDrawnThisTurn.entrySet()) { - amountOfCardsDrawnThisTurn.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.UNTAP_STEP_PRE) { @@ -82,9 +75,4 @@ public class MiracleWatcher extends Watcher { public void reset() { amountOfCardsDrawnThisTurn.clear(); } - - @Override - public MiracleWatcher copy() { - return new MiracleWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/MorbidWatcher.java b/Mage/src/main/java/mage/watchers/common/MorbidWatcher.java index 349c6ce5332..0b7fb9ac7a5 100644 --- a/Mage/src/main/java/mage/watchers/common/MorbidWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/MorbidWatcher.java @@ -17,10 +17,6 @@ public class MorbidWatcher extends Watcher { super(WatcherScope.GAME); } - public MorbidWatcher(final MorbidWatcher watcher) { - super(watcher); - } - @Override public void watch(GameEvent event, Game game) { if (condition) { @@ -32,10 +28,4 @@ public class MorbidWatcher extends Watcher { condition = true; } } - -// @Override -// public MorbidWatcher copy() { -// return new MorbidWatcher(this); -// } - } diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index fa14361fd55..e71a844b28d 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -23,11 +23,6 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { super(WatcherScope.GAME); } - public NumberOfTimesPermanentTargetedATurnWatcher(final NumberOfTimesPermanentTargetedATurnWatcher watcher) { - super(watcher); - this.permanentsTargeted.putAll(watcher.permanentsTargeted); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.TARGETED) { @@ -55,9 +50,4 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { super.reset(); permanentsTargeted.clear(); } - - @Override - public NumberOfTimesPermanentTargetedATurnWatcher copy() { - return new NumberOfTimesPermanentTargetedATurnWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java index b4bc3df198e..9e74b0bae7b 100644 --- a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldWatcher.java @@ -26,17 +26,6 @@ public class PermanentsEnteredBattlefieldWatcher extends Watcher { super(WatcherScope.GAME); } - public PermanentsEnteredBattlefieldWatcher(final PermanentsEnteredBattlefieldWatcher watcher) { - super(watcher); - this.enteringBattlefield.putAll(watcher.enteringBattlefield); - this.enteringBattlefieldLastTurn.putAll(watcher.enteringBattlefieldLastTurn); - } - - @Override - public PermanentsEnteredBattlefieldWatcher copy() { - return new PermanentsEnteredBattlefieldWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java index 2c52ef3ed81..5e71320ed1c 100644 --- a/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PermanentsEnteredBattlefieldYourLastTurnWatcher.java @@ -27,17 +27,6 @@ public class PermanentsEnteredBattlefieldYourLastTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public PermanentsEnteredBattlefieldYourLastTurnWatcher(final PermanentsEnteredBattlefieldYourLastTurnWatcher watcher) { - super(watcher); - this.enteringBattlefield.putAll(watcher.enteringBattlefield); - this.enteringBattlefieldLastTurn.putAll(watcher.enteringBattlefieldLastTurn); - } - - @Override - public PermanentsEnteredBattlefieldYourLastTurnWatcher copy() { - return new PermanentsEnteredBattlefieldYourLastTurnWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { lastActivePlayer = game.getActivePlayerId(); diff --git a/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java b/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java index 9db7329017a..c157be5f23c 100644 --- a/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PermanentsSacrificedWatcher.java @@ -25,15 +25,6 @@ public class PermanentsSacrificedWatcher extends Watcher { super(WatcherScope.GAME); } - public PermanentsSacrificedWatcher(final PermanentsSacrificedWatcher watcher) { - super(watcher); - } - - @Override - public PermanentsSacrificedWatcher copy() { - return new PermanentsSacrificedWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SACRIFICED_PERMANENT) { diff --git a/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java b/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java index 21ab1cc6a1f..b373d3c73c8 100644 --- a/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java @@ -24,13 +24,6 @@ public class PlanarRollWatcher extends Watcher { super(WatcherScope.GAME); } - public PlanarRollWatcher(final PlanarRollWatcher watcher) { - super(watcher); - for (Entry entry : watcher.numberTimesPlanarDieRolled.entrySet()) { - numberTimesPlanarDieRolled.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.PLANAR_DIE_ROLLED) { diff --git a/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java index bc6769e219e..f8b1ba53173 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayLandWatcher.java @@ -21,17 +21,6 @@ public class PlayLandWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayLandWatcher(final PlayLandWatcher watcher) { - super(watcher); - playerPlayedLand.addAll(watcher.playerPlayedLand); - landPlayed.addAll(watcher.landPlayed); - } - - @Override - public PlayLandWatcher copy() { - return new PlayLandWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.LAND_PLAYED) { diff --git a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java index 57e37025870..a710a145fa5 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java @@ -21,18 +21,6 @@ public class PlayerAttackedStepWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayerAttackedStepWatcher(final PlayerAttackedStepWatcher watcher) { - super(watcher); - for (Map.Entry entry : watcher.playerAttacked.entrySet()) { - this.playerAttacked.put(entry.getKey(), entry.getValue()); - } - } - - @Override - public PlayerAttackedStepWatcher copy() { - return new PlayerAttackedStepWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST) { diff --git a/Mage/src/main/java/mage/watchers/common/PlayerAttackedWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerAttackedWatcher.java index be67da9b4f2..c806e4d68cb 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerAttackedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerAttackedWatcher.java @@ -24,19 +24,6 @@ public class PlayerAttackedWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayerAttackedWatcher(final PlayerAttackedWatcher watcher) { - super(watcher); - for (Map.Entry entry : watcher.playerAttacked.entrySet()) { - this.playerAttacked.put(entry.getKey(), entry.getValue()); - - } - } - - @Override - public PlayerAttackedWatcher copy() { - return new PlayerAttackedWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { diff --git a/Mage/src/main/java/mage/watchers/common/PlayerCastCreatureWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerCastCreatureWatcher.java index 420a019923a..591731ca84c 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerCastCreatureWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerCastCreatureWatcher.java @@ -22,11 +22,6 @@ public class PlayerCastCreatureWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayerCastCreatureWatcher(final PlayerCastCreatureWatcher watcher) { - super(watcher); - this.playerIds.addAll(watcher.playerIds); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { @@ -37,11 +32,6 @@ public class PlayerCastCreatureWatcher extends Watcher { } } - @Override - public PlayerCastCreatureWatcher copy() { - return new PlayerCastCreatureWatcher(this); - } - @Override public void reset() { super.reset(); diff --git a/Mage/src/main/java/mage/watchers/common/PlayerDamagedBySourceWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerDamagedBySourceWatcher.java index d3e26cfa744..c30c552ec41 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerDamagedBySourceWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerDamagedBySourceWatcher.java @@ -20,17 +20,10 @@ public class PlayerDamagedBySourceWatcher extends Watcher { private final Set damageSourceIds = new HashSet<>(); - public PlayerDamagedBySourceWatcher(UUID playerId) { + public PlayerDamagedBySourceWatcher() { super(WatcherScope.PLAYER); - setControllerId(playerId); } - public PlayerDamagedBySourceWatcher(final PlayerDamagedBySourceWatcher watcher) { - super(watcher); - this.damageSourceIds.addAll(watcher.damageSourceIds); - } - - @Override public void watch(GameEvent event, Game game) { if (event.getType() == EventType.DAMAGED_PLAYER) { diff --git a/Mage/src/main/java/mage/watchers/common/PlayerGainedLifeWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerGainedLifeWatcher.java index e067a5fb905..4c24cc1d226 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerGainedLifeWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerGainedLifeWatcher.java @@ -24,12 +24,6 @@ public class PlayerGainedLifeWatcher extends Watcher { super(WatcherScope.GAME); } - private PlayerGainedLifeWatcher(final PlayerGainedLifeWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfLifeGainedThisTurn.entrySet()) { - amountOfLifeGainedThisTurn.put(entry.getKey(), entry.getValue()); - } - } @Override public void watch(GameEvent event, Game game) { @@ -54,9 +48,4 @@ public class PlayerGainedLifeWatcher extends Watcher { super.reset(); amountOfLifeGainedThisTurn.clear(); } - - @Override - public PlayerGainedLifeWatcher copy() { - return new PlayerGainedLifeWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/PlayerLostLifeNonCombatWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerLostLifeNonCombatWatcher.java index d6a28c4d396..785cb528af3 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerLostLifeNonCombatWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerLostLifeNonCombatWatcher.java @@ -28,13 +28,6 @@ public class PlayerLostLifeNonCombatWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayerLostLifeNonCombatWatcher(final PlayerLostLifeNonCombatWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfLifeLostThisTurn.entrySet()) { - amountOfLifeLostThisTurn.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.LOST_LIFE && !event.getFlag()) { @@ -76,9 +69,4 @@ public class PlayerLostLifeNonCombatWatcher extends Watcher { amountOfLifeLostLastTurn.putAll(amountOfLifeLostThisTurn); amountOfLifeLostThisTurn.clear(); } - -// @Override -// public PlayerLostLifeNonCombatWatcher copy() { -// return new PlayerLostLifeNonCombatWatcher(this); -// } } diff --git a/Mage/src/main/java/mage/watchers/common/PlayerLostLifeWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerLostLifeWatcher.java index b90a4e44578..40a233421be 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerLostLifeWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerLostLifeWatcher.java @@ -26,13 +26,6 @@ public class PlayerLostLifeWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayerLostLifeWatcher(final PlayerLostLifeWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfLifeLostThisTurn.entrySet()) { - amountOfLifeLostThisTurn.put(entry.getKey(), entry.getValue()); - } - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.LOST_LIFE) { @@ -74,9 +67,4 @@ public class PlayerLostLifeWatcher extends Watcher { amountOfLifeLostLastTurn.putAll(amountOfLifeLostThisTurn); amountOfLifeLostThisTurn.clear(); } - -// @Override -// public PlayerLostLifeWatcher copy() { -// return new PlayerLostLifeWatcher(this); -// } } diff --git a/Mage/src/main/java/mage/watchers/common/PlayersAttackedLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayersAttackedLastTurnWatcher.java index 2416759fb63..2b5f73c08a4 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayersAttackedLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayersAttackedLastTurnWatcher.java @@ -23,18 +23,6 @@ public class PlayersAttackedLastTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayersAttackedLastTurnWatcher(final PlayersAttackedLastTurnWatcher watcher) { - super(watcher); - for (Map.Entry entry : watcher.playersAttackedInLastTurn.entrySet()) { - this.playersAttackedInLastTurn.put(entry.getKey(), entry.getValue()); - } - } - - @Override - public PlayersAttackedLastTurnWatcher copy() { - return new PlayersAttackedLastTurnWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { diff --git a/Mage/src/main/java/mage/watchers/common/PlayersAttackedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayersAttackedThisTurnWatcher.java index b10eb74ab6b..fe39ea98e88 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayersAttackedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayersAttackedThisTurnWatcher.java @@ -23,19 +23,6 @@ public class PlayersAttackedThisTurnWatcher extends Watcher { super(WatcherScope.GAME); } - public PlayersAttackedThisTurnWatcher(final PlayersAttackedThisTurnWatcher watcher) { - super(watcher); - - for (Map.Entry entry : watcher.playersAttackedThisTurn.entrySet()) { - this.playersAttackedThisTurn.putIfAbsent(entry.getKey(), entry.getValue()); - } - - for (Map.Entry entry : watcher.opponentsAttackedThisTurn.entrySet()) { - this.opponentsAttackedThisTurn.putIfAbsent(entry.getKey(), entry.getValue()); - } - } - - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { diff --git a/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java b/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java index 33573832651..f7c13409049 100644 --- a/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ProwlWatcher.java @@ -29,18 +29,6 @@ public class ProwlWatcher extends Watcher { super(WatcherScope.GAME); } - private ProwlWatcher(final ProwlWatcher watcher) { - super(watcher); - for (Entry> entry : watcher.damagingSubtypes.entrySet()) { - damagingSubtypes.put(entry.getKey(), entry.getValue()); - } - } - - @Override - public ProwlWatcher copy() { - return new ProwlWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == EventType.DAMAGED_PLAYER) { diff --git a/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java b/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java index 65084d84cd8..8007ef86fee 100644 --- a/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/RevoltWatcher.java @@ -25,11 +25,6 @@ public class RevoltWatcher extends Watcher { super(WatcherScope.GAME); } - public RevoltWatcher(final RevoltWatcher watcher) { - super(watcher); - this.revoltActivePlayerIds.addAll(watcher.revoltActivePlayerIds); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && event instanceof ZoneChangeEvent) { @@ -51,9 +46,4 @@ public class RevoltWatcher extends Watcher { public void reset() { revoltActivePlayerIds.clear(); } - - @Override - public RevoltWatcher copy() { - return new RevoltWatcher(this); - } } diff --git a/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java b/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java index e790aee96b8..f338ea1cb02 100644 --- a/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SourceDidDamageWatcher.java @@ -22,16 +22,6 @@ public class SourceDidDamageWatcher extends Watcher { super(WatcherScope.GAME); } - public SourceDidDamageWatcher(final SourceDidDamageWatcher watcher) { - super(watcher); - this.damageSources.addAll(watcher.damageSources); - } - - @Override - public SourceDidDamageWatcher copy() { - return new SourceDidDamageWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == EventType.DAMAGED_CREATURE diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java index 64fe3db2d6b..883ab6f9ef6 100644 --- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -30,16 +30,6 @@ public class SpellsCastWatcher extends Watcher { super(WatcherScope.GAME); } - public SpellsCastWatcher(final SpellsCastWatcher watcher) { - super(watcher); - this.spellsCast.putAll(watcher.spellsCast); - } - - @Override - public SpellsCastWatcher copy() { - return new SpellsCastWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (EventType.SPELL_CAST == event.getType()) { diff --git a/Mage/src/main/java/mage/watchers/common/WasBlockedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/WasBlockedThisTurnWatcher.java index c142833210f..89ad6edb0dd 100644 --- a/Mage/src/main/java/mage/watchers/common/WasBlockedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/WasBlockedThisTurnWatcher.java @@ -23,16 +23,6 @@ public class WasBlockedThisTurnWatcher extends Watcher { wasBlockedThisTurnCreatures = new HashSet<>(); } - private WasBlockedThisTurnWatcher(final WasBlockedThisTurnWatcher watcher) { - super(watcher); - wasBlockedThisTurnCreatures = new HashSet<>(watcher.wasBlockedThisTurnCreatures); - } - - @Override - public Watcher copy() { - return new WasBlockedThisTurnWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { diff --git a/Mage/src/main/java/mage/watchers/common/ZuberasDiedWatcher.java b/Mage/src/main/java/mage/watchers/common/ZuberasDiedWatcher.java index 61c555cffe0..840b428da39 100644 --- a/Mage/src/main/java/mage/watchers/common/ZuberasDiedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/ZuberasDiedWatcher.java @@ -24,16 +24,6 @@ public class ZuberasDiedWatcher extends Watcher { super(WatcherScope.GAME); } - public ZuberasDiedWatcher(final ZuberasDiedWatcher watcher) { - super(watcher); - this.zuberasDiedThisTurn = watcher.zuberasDiedThisTurn; - } - - @Override - public ZuberasDiedWatcher copy() { - return new ZuberasDiedWatcher(this); - } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) { diff --git a/Mage/src/test/java/mage/WatcherTest.java b/Mage/src/test/java/mage/WatcherTest.java new file mode 100644 index 00000000000..8b8c44b160e --- /dev/null +++ b/Mage/src/test/java/mage/WatcherTest.java @@ -0,0 +1,4 @@ +package mage; + +public class WatcherTest { +} From 7a35a762483d9a0489b5bf3137829d417b5c4820 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 29 Apr 2019 14:18:44 -0500 Subject: [PATCH 02/76] - The Adapt effect now works correctly when the permanent is blinked. --- .../effects/keyword/AdaptEffect.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/AdaptEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/AdaptEffect.java index ed29b59aea6..43a81809f3c 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/AdaptEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/AdaptEffect.java @@ -1,8 +1,10 @@ package mage.abilities.effects.keyword; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; @@ -19,10 +21,10 @@ public class AdaptEffect extends OneShotEffect { public AdaptEffect(int adaptNumber) { super(Outcome.BoostCreature); this.adaptNumber = adaptNumber; - staticText = "Adapt " + adaptNumber + - " (If this creature has no +1/+1 counters on it, put " + - CardUtil.numberToText(adaptNumber) + " +1/+1 counter" + - (adaptNumber > 1 ? "s" : "") + " on it.)"; + staticText = "Adapt " + adaptNumber + + " (If this creature has no +1/+1 counters on it, put " + + CardUtil.numberToText(adaptNumber) + " +1/+1 counter" + + (adaptNumber > 1 ? "s" : "") + " on it.)"; } private AdaptEffect(final AdaptEffect effect) { @@ -37,7 +39,15 @@ public class AdaptEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); + // Verify source object did not change zone and is on the battlefield + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (sourceObject == null) { + if (game.getState().getZone(source.getSourceId()).equals(Zone.BATTLEFIELD) + && source.getSourceObjectZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { + sourceObject = game.getPermanent(source.getSourceId()); + } + } + Permanent permanent = ((Permanent) sourceObject); if (permanent == null) { return false; } @@ -48,7 +58,8 @@ public class AdaptEffect extends OneShotEffect { if (game.replaceEvent(event)) { return false; } - if (permanent.getCounters(game).getCount(CounterType.P1P1) == 0 || event.getFlag()) { + if (permanent.getCounters(game).getCount(CounterType.P1P1) == 0 + || event.getFlag()) { permanent.addCounters(CounterType.P1P1.createInstance(event.getAmount()), source, game); } return true; From 9cab9bc4299b4d44d71aba2cf76941c46e16ce55 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 29 Apr 2019 15:22:21 -0500 Subject: [PATCH 03/76] - Fixed #5758 --- Mage.Sets/src/mage/cards/d/DovinHandOfControl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java index 734bef43bdc..34eaf023bcc 100644 --- a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java +++ b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java @@ -18,6 +18,8 @@ import mage.target.TargetPermanent; import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.mana.ManaAbility; +import mage.game.stack.Spell; /** * @author TheElk801 @@ -76,7 +78,12 @@ class DovinHandOfControlEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { Card card = game.getCard(abilityToModify.getSourceId()); - return card != null && (card.isInstantOrSorcery() || card.isArtifact()) + if (!(abilityToModify instanceof SpellAbility)) { + return false; + } + return card != null + && (card.isInstantOrSorcery() + || card.isArtifact()) && game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId()); } From 6f150b4fe0e4ed4c5c4b8b6b32c6ad542febbfad Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 29 Apr 2019 16:42:36 -0500 Subject: [PATCH 04/76] - Fixed #5770 --- .../src/mage/cards/g/GodEternalRhonas.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GodEternalRhonas.java b/Mage.Sets/src/mage/cards/g/GodEternalRhonas.java index 6195751ad21..4ba9c292ad8 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalRhonas.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalRhonas.java @@ -7,20 +7,18 @@ import mage.abilities.common.GodEternalDiesTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import java.util.UUID; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.filter.common.FilterControlledCreaturePermanent; /** * @author TheElk801 @@ -57,17 +55,13 @@ public final class GodEternalRhonas extends CardImpl { } class GodEternalRhonasEffect extends OneShotEffect { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(AnotherPredicate.instance); - } + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); GodEternalRhonasEffect() { super(Outcome.Benefit); - staticText = "double the power of each other creature you control until end of turn. " + - "Those creatures gain vigilance until end of turn."; + staticText = "double the power of each other creature you control until end of turn. " + + "Those creatures gain vigilance until end of turn."; } private GodEternalRhonasEffect(final GodEternalRhonasEffect effect) { @@ -81,8 +75,11 @@ class GodEternalRhonasEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (permanent == null) { + Permanent godEternalRhonas = game.getPermanent(source.getSourceId()); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { + if (permanent == null + || godEternalRhonas != null + && permanent == godEternalRhonas) { continue; } ContinuousEffect effect = new BoostTargetEffect( @@ -91,11 +88,14 @@ class GodEternalRhonasEffect extends OneShotEffect { ); effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); + + ContinuousEffect effect2 = new GainAbilityTargetEffect( + VigilanceAbility.getInstance(), + Duration.EndOfTurn + ); + effect2.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect2, source); } - game.addEffect(new GainAbilityControlledEffect( - VigilanceAbility.getInstance(), - Duration.EndOfTurn, filter - ), source); return true; } -} \ No newline at end of file +} From 67f02ec5abd4d983ff26c79328f2a7eca30dda88 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 12:53:41 +0400 Subject: [PATCH 05/76] * AI: fixed error on cards with target player or planeswalker; --- .../java/mage/player/ai/ComputerPlayer.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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 879718fdc10..3e6603d7b93 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 @@ -669,12 +669,26 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target.getOriginalTarget() instanceof TargetPlayerOrPlaneswalker) { List targets; TargetPlayerOrPlaneswalker origTarget = ((TargetPlayerOrPlaneswalker) target); + + // TODO: if effect is bad and no opponent's targets available then AI can't target yourself but must by rules + /* + battlefield:Computer:Mountain:5 + hand:Computer:Viashino Pyromancer:3 + battlefield:Human:Shalai, Voice of Plenty:1 + */ + // TODO: in multiplayer game there many opponents - if random opponents don't have targets then AI must use next opponent, but it skips + // (e.g. you randomOpponentId must be replaced by List randomOpponents) + + // normal cycle (good for you, bad for opponents) + + // possible good/bad permanents if (outcome.isGood()) { targets = threats(abilityControllerId, source.getSourceId(), ((FilterPermanentOrPlayer) target.getFilter()).getPermanentFilter(), game, target.getTargets()); } else { targets = threats(randomOpponentId, source.getSourceId(), ((FilterPermanentOrPlayer) target.getFilter()).getPermanentFilter(), game, target.getTargets()); } + // possible good/bad players if (targets.isEmpty()) { if (outcome.isGood()) { if (target.canTarget(getId(), abilityControllerId, source, game)) { @@ -685,9 +699,12 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } + // can't find targets (e.g. effect is bad, but you need take targets from yourself) if (targets.isEmpty() && target.isRequired(source)) { - targets = game.getBattlefield().getActivePermanents(((TargetPlayerOrPlaneswalker) origTarget.getFilter()).getFilterPermanent(), playerId, game); + targets = game.getBattlefield().getActivePermanents(origTarget.getFilterPermanent(), playerId, game); } + + // try target permanent for (Permanent permanent : targets) { List alreadyTargeted = target.getTargets(); if (target.canTarget(abilityControllerId, permanent.getId(), source, game)) { @@ -697,6 +714,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } + // try target player as normal if (outcome.isGood()) { if (target.canTarget(getId(), abilityControllerId, source, game)) { return tryAddTarget(target, abilityControllerId, source, game); From f7622d3c4a2d366d90c7459a9bf914105f5a1d0f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 14:40:19 +0400 Subject: [PATCH 06/76] * Awakening of Vitu-Ghazi - fixed that it creates non legendary tokens --- .../continuous/BecomesCreatureAllEffect.java | 59 +++++++++++++------ .../BecomesCreatureTargetEffect.java | 27 +++++---- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java index 54d96c75d15..07892612d65 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java @@ -1,19 +1,13 @@ - package mage.abilities.effects.common.continuous; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.TokenImpl; import mage.game.permanent.token.Token; import java.util.HashSet; @@ -21,7 +15,6 @@ import java.util.Set; /** * @author LevelX2 - * */ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { @@ -29,13 +22,19 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { protected String theyAreStillType; private final FilterPermanent filter; private boolean loseColor = true; + protected boolean loseName = false; public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor) { + this(token, theyAreStillType, filter, duration, loseColor, false); + } + + public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) { super(duration, Outcome.BecomeCreature); this.token = token; this.theyAreStillType = theyAreStillType; this.filter = filter; this.loseColor = loseColor; + this.loseName = loseName; } public BecomesCreatureAllEffect(final BecomesCreatureAllEffect effect) { @@ -44,6 +43,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { this.theyAreStillType = effect.theyAreStillType; this.filter = effect.filter.copy(); this.loseColor = effect.loseColor; + this.loseName = effect.loseName; } @Override @@ -65,30 +65,47 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Set affectedPermanents = new HashSet<>(); if (this.affectedObjectsSet) { - for(MageObjectReference ref : affectedObjectList) { + for (MageObjectReference ref : affectedObjectList) { affectedPermanents.add(ref.getPermanent(game)); } } else { affectedPermanents = new HashSet<>(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)); } - for(Permanent permanent : affectedPermanents) { + for (Permanent permanent : affectedPermanents) { if (permanent != null) { switch (layer) { + case TextChangingEffects_3: + if (sublayer == SubLayer.NA) { + if (loseName) { + permanent.setName(token.getName()); + } + } + break; + case TypeChangingEffects_4: if (sublayer == SubLayer.NA) { - if (!token.getCardType().isEmpty()) { - for (CardType t : token.getCardType()) { - if (!permanent.getCardType().contains(t)) { - permanent.addCardType(t); + if (theyAreStillType != null) { + permanent.getSubtype(game).retainAll(SubType.getLandTypes()); + permanent.getSubtype(game).addAll(token.getSubtype(game)); + } else { + for (SubType t : token.getSubtype(game)) { + if (!permanent.hasSubtype(t, game)) { + permanent.getSubtype(game).add(t); } } } - if (theyAreStillType == null) { - permanent.getSubtype(game).clear(); + + for (SuperType t : token.getSuperType()) { + if (!permanent.getSuperType().contains(t)) { + permanent.addSuperType(t); + } } - if (!token.getSubtype(game).isEmpty()) { - permanent.getSubtype(game).addAll(token.getSubtype(game)); + + for (CardType t : token.getCardType()) { + if (!permanent.getCardType().contains(t)) { + permanent.addCardType(t); + } } } break; @@ -141,7 +158,11 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.PTChangingEffects_7 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.TypeChangingEffects_4 + || layer == Layer.TextChangingEffects_3; } @Override 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 97d55d1aa0b..ae2f629c6c0 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 @@ -69,30 +69,34 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { } } break; + case TypeChangingEffects_4: if (sublayer == SubLayer.NA) { if (loseAllAbilities) { permanent.getSubtype(game).retainAll(SubType.getLandTypes()); permanent.getSubtype(game).addAll(token.getSubtype(game)); } else { - if (!token.getSubtype(game).isEmpty()) { - for (SubType subtype : token.getSubtype(game)) { - if (!permanent.hasSubtype(subtype, game)) { - permanent.getSubtype(game).add(subtype); - } + for (SubType t : token.getSubtype(game)) { + if (!permanent.hasSubtype(t, game)) { + permanent.getSubtype(game).add(t); } - } } - if (!token.getCardType().isEmpty()) { - for (CardType t : token.getCardType()) { - if (!permanent.getCardType().contains(t)) { - permanent.addCardType(t); - } + + for (SuperType t : token.getSuperType()) { + if (!permanent.getSuperType().contains(t)) { + permanent.addSuperType(t); + } + } + + for (CardType t : token.getCardType()) { + if (!permanent.getCardType().contains(t)) { + permanent.addCardType(t); } } } break; + case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { if (loseAllAbilities) { @@ -107,6 +111,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { } } break; + case AbilityAddingRemovingEffects_6: if (loseAllAbilities) { permanent.removeAllAbilities(source.getSourceId(), game); From fc1fa70e0dff97d1513d468eff621c8210ef31af Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 14:42:23 +0400 Subject: [PATCH 07/76] Prepare hotfix and fix empty hands on startup (versions compatibility problem) --- Mage.Common/src/main/java/mage/utils/MageVersion.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index cbf339b7796..f1741344447 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -13,9 +13,9 @@ public class MageVersion implements Serializable, Comparable { public static final int MAGE_VERSION_MINOR = 4; public static final int MAGE_VERSION_PATCH = 35; public static final String MAGE_EDITION_INFO = ""; // set "-beta" for 1.4.32-betaV0 - public static final String MAGE_VERSION_MINOR_PATCH = "V2"; // default + public static final String MAGE_VERSION_MINOR_PATCH = "V3"; // default // strict mode - private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = false; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes) + private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes) public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true; private final int major; From da7bd9c0d7017701e3dabf637eb1ce027185e45d Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 16:33:41 +0400 Subject: [PATCH 08/76] * Added new game type: Freeform Commander Two Player Duel --- .../src/main/java/mage/client/dialog/NewTableDialog.java | 3 ++- Mage.Server.Plugins/pom.xml | 5 +++-- Mage.Server/config/config.xml | 1 + Mage.Server/pom.xml | 6 ++++++ Mage.Server/release/config/config.xml | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index c488ff96217..1fece571293 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -683,8 +683,9 @@ public class NewTableDialog extends MageDialog { return false; } break; + case "Freeform Commander Two Player Duel": case "Freeform Commander Free For All": - if (!options.getDeckType().equals("Variant Magic - Freeform Commander")){ + if (!options.getDeckType().equals("Variant Magic - Freeform Commander")) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Deck type Freeform Commander needs also a Freeform Commander game type", "Error", JOptionPane.ERROR_MESSAGE); return false; } diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index cf0611b51e3..248976e2124 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -25,8 +25,9 @@ Mage.Game.TinyLeadersDuel Mage.Game.CanadianHighlanderDuel Mage.Game.PennyDreadfulCommanderFreeForAll - Mage.Game.FreeformCommanderFreeForAll - Mage.Game.BrawlDuel + Mage.Game.FreeformCommanderDuel + Mage.Game.FreeformCommanderFreeForAll + Mage.Game.BrawlDuel Mage.Game.BrawlFreeForAll Mage.Game.TwoPlayerDuel Mage.Player.AI diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 0475c3cbe67..aa2223ceab4 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -79,6 +79,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 9386469d6c0..72d63a77d60 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -184,6 +184,12 @@ ${project.version} runtime + + ${project.groupId} + mage-game-freeformcommanderduel + ${project.version} + runtime + ${project.groupId} diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 2a294b3093d..bea0cd3d653 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -73,6 +73,7 @@ + From 060ebe655e5446999c6c60a6acf3e399681b5cf7 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 16:33:41 +0400 Subject: [PATCH 09/76] * Added new game type: Freeform Commander Two Player Duel (#5771) --- .../mage/client/dialog/NewTableDialog.java | 3 +- .../Mage.Game.FreeformCommanderDuel/pom.xml | 50 +++++++++++++++++++ .../src/mage/game/FreeformCommanderDuel.java | 36 +++++++++++++ .../mage/game/FreeformCommanderDuelMatch.java | 31 ++++++++++++ .../mage/game/FreeformCommanderDuelType.java | 29 +++++++++++ Mage.Server.Plugins/pom.xml | 5 +- Mage.Server/config/config.xml | 1 + Mage.Server/pom.xml | 6 +++ Mage.Server/release/config/config.xml | 1 + 9 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelMatch.java create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelType.java diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index c488ff96217..1fece571293 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -683,8 +683,9 @@ public class NewTableDialog extends MageDialog { return false; } break; + case "Freeform Commander Two Player Duel": case "Freeform Commander Free For All": - if (!options.getDeckType().equals("Variant Magic - Freeform Commander")){ + if (!options.getDeckType().equals("Variant Magic - Freeform Commander")) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Deck type Freeform Commander needs also a Freeform Commander game type", "Error", JOptionPane.ERROR_MESSAGE); return false; } diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml new file mode 100644 index 00000000000..96f60f12a15 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.mage + mage-server-plugins + 1.4.35 + + + mage-game-freeformcommanderduel + jar + Mage Game Freeform Commander Two Player + + + + ${project.groupId} + mage + ${project.version} + + + + + src + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + maven-resources-plugin + + UTF-8 + + + + + + mage-game-freeformcommanderduel + + + + + diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java new file mode 100644 index 00000000000..d57f520f079 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuel.java @@ -0,0 +1,36 @@ +package mage.game; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.match.MatchType; +import mage.game.mulligan.Mulligan; + +/** + * @author JayDi85 + */ +public class FreeformCommanderDuel extends GameCommanderImpl { + + public FreeformCommanderDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { + super(attackOption, range, mulligan, startLife); + } + + public FreeformCommanderDuel(final FreeformCommanderDuel game) { + super(game); + } + + @Override + public MatchType getGameType() { + return new FreeformCommanderDuelType(); + } + + @Override + public int getNumPlayers() { + return 2; + } + + @Override + public FreeformCommanderDuel copy() { + return new FreeformCommanderDuel(this); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelMatch.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelMatch.java new file mode 100644 index 00000000000..1f3e141929d --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelMatch.java @@ -0,0 +1,31 @@ +package mage.game; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; +import mage.game.mulligan.Mulligan; + +/** + * @author JayDi85 + */ +public class FreeformCommanderDuelMatch extends MatchImpl { + + public FreeformCommanderDuelMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + int startLife = 20; + boolean alsoHand = true; + boolean checkCommanderDamage = true; + + Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans()); + FreeformCommanderDuel game = new FreeformCommanderDuel(options.getAttackOption(), options.getRange(), mulligan, startLife); + game.setCheckCommanderDamage(checkCommanderDamage); + game.setStartMessage(this.createGameStartMessage()); + game.setAlsoHand(alsoHand); + game.setAlsoLibrary(true); + initGame(game); + games.add(game); + } +} diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelType.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelType.java new file mode 100644 index 00000000000..586617ee518 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/src/mage/game/FreeformCommanderDuelType.java @@ -0,0 +1,29 @@ +package mage.game; + +import mage.game.match.MatchType; + +/** + * @author JayDi85 + */ +public class FreeformCommanderDuelType extends MatchType { + + public FreeformCommanderDuelType() { + this.name = "Freeform Commander Two Player Duel"; + this.maxPlayers = 2; + this.minPlayers = 2; + this.numTeams = 0; + this.useAttackOption = false; + this.useRange = false; + this.sideboardingAllowed = false; + } + + protected FreeformCommanderDuelType(final FreeformCommanderDuelType matchType) { + super(matchType); + } + + @Override + public FreeformCommanderDuelType copy() { + return new FreeformCommanderDuelType(this); + } + +} diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index cf0611b51e3..248976e2124 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -25,8 +25,9 @@ Mage.Game.TinyLeadersDuel Mage.Game.CanadianHighlanderDuel Mage.Game.PennyDreadfulCommanderFreeForAll - Mage.Game.FreeformCommanderFreeForAll - Mage.Game.BrawlDuel + Mage.Game.FreeformCommanderDuel + Mage.Game.FreeformCommanderFreeForAll + Mage.Game.BrawlDuel Mage.Game.BrawlFreeForAll Mage.Game.TwoPlayerDuel Mage.Player.AI diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 0475c3cbe67..aa2223ceab4 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -79,6 +79,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 9386469d6c0..72d63a77d60 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -184,6 +184,12 @@ ${project.version} runtime + + ${project.groupId} + mage-game-freeformcommanderduel + ${project.version} + runtime + ${project.groupId} diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 2a294b3093d..bea0cd3d653 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -73,6 +73,7 @@ + From ec5134a6631f699ccef8a065b32a13f8ec7267f9 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 18:26:37 +0400 Subject: [PATCH 10/76] * Chandra, Fire Artisan - fixed that it doesn't triggers on own damage; --- Mage/src/main/java/mage/game/permanent/PermanentImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index a76df41f434..725e077df00 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -909,7 +909,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { if (countersToRemove > getCounters(game).getCount(CounterType.LOYALTY)) { countersToRemove = getCounters(game).getCount(CounterType.LOYALTY); } - getCounters(game).removeCounter(CounterType.LOYALTY, countersToRemove); + removeCounters(CounterType.LOYALTY.getName(), countersToRemove, game); game.fireEvent(new DamagedPlaneswalkerEvent(objectId, sourceId, controllerId, actualDamage, combat)); return actualDamage; } From a8708e61d74b8c6815e0efbc0a7f15ed497cfa19 Mon Sep 17 00:00:00 2001 From: Ingmar Goudt Date: Tue, 30 Apr 2019 16:41:06 +0200 Subject: [PATCH 11/76] simplify some of the field setters --- Mage/src/main/java/mage/watchers/Watcher.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java index f954166119e..05efdc76f39 100644 --- a/Mage/src/main/java/mage/watchers/Watcher.java +++ b/Mage/src/main/java/mage/watchers/Watcher.java @@ -103,19 +103,7 @@ public abstract class Watcher implements Serializable { allFields.addAll(Arrays.asList(getClass().getSuperclass().getDeclaredFields())); for (Field field : allFields) { field.setAccessible(true); - if (field.getType().isPrimitive()) { - field.set(watcher, field.get(this)); - } - else if(field.getType() == Step.class){ - field.set(watcher, field.get(this)); - } - else if (field.getType() == Mana.class) { - field.set(watcher, field.get(this)); - } else if (field.getType() == UUID.class) { - field.set(watcher, field.get(this)); - } else if (field.getType().isEnum()) { - field.set(watcher, field.get(this)); - } else if (field.getType() == Set.class) { + if (field.getType() == Set.class) { ((Set) field.get(watcher)).clear(); ((Set) field.get(watcher)).addAll((Set) field.get(this)); } else if (field.getType() == Map.class) { @@ -126,7 +114,7 @@ public abstract class Watcher implements Serializable { ((List) field.get(watcher)).addAll((List) field.get(this)); } else { if (field.getType() != Logger.class) { - logger.error(field.getType() + " can not be copied"); + field.set(watcher, field.get(this)); } } } From 2a2cfccad3c67d96ce247d18435d90ee2482dc44 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 30 Apr 2019 09:46:37 -0500 Subject: [PATCH 12/76] - little fix Dreadhorde Invasion. --- Mage.Sets/src/mage/cards/d/DreadhordeInvasion.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/d/DreadhordeInvasion.java b/Mage.Sets/src/mage/cards/d/DreadhordeInvasion.java index 5deecbca9d2..98092d6deb4 100644 --- a/Mage.Sets/src/mage/cards/d/DreadhordeInvasion.java +++ b/Mage.Sets/src/mage/cards/d/DreadhordeInvasion.java @@ -10,11 +10,12 @@ import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.filter.predicate.permanent.TokenPredicate; import java.util.UUID; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerPredicate; /** * @author TheElk801 @@ -27,6 +28,7 @@ public final class DreadhordeInvasion extends CardImpl { static { filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 5)); filter.add(TokenPredicate.instance); + filter.add(new ControllerPredicate(TargetController.YOU)); } public DreadhordeInvasion(UUID ownerId, CardSetInfo setInfo) { From 07faf872ea4a916083eb6196e9a5c67c75daa37f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 18:50:03 +0400 Subject: [PATCH 13/76] Fixed that some cards doesn't trigger counter remove events; --- Mage.Sets/src/mage/cards/a/AllHallowsEve.java | 11 ++++++----- Mage.Sets/src/mage/cards/a/Aurification.java | 14 ++++++++------ Mage.Sets/src/mage/cards/i/Inhumaniac.java | 10 ++++++---- Mage.Sets/src/mage/cards/m/MineLayer.java | 12 +++++++----- .../abilities/costs/common/RemoveCounterCost.java | 13 ++++--------- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AllHallowsEve.java b/Mage.Sets/src/mage/cards/a/AllHallowsEve.java index e033af793ad..d61463522c9 100644 --- a/Mage.Sets/src/mage/cards/a/AllHallowsEve.java +++ b/Mage.Sets/src/mage/cards/a/AllHallowsEve.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.dynamicvalue.common.StaticValue; @@ -9,7 +7,9 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; @@ -19,8 +19,9 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class AllHallowsEve extends CardImpl { @@ -72,7 +73,7 @@ class AllHallowsEveEffect extends OneShotEffect { if (allHallowsEve != null && controller != null && game.getExile().getCard(allHallowsEve.getId(), game) != null) { - allHallowsEve.getCounters(game).removeCounter(CounterType.SCREAM, 1); + allHallowsEve.removeCounters(CounterType.SCREAM.getName(), 1, game); if (allHallowsEve.getCounters(game).getCount(CounterType.SCREAM) == 0) { allHallowsEve.moveToZone(Zone.GRAVEYARD, source.getId(), game, false); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { diff --git a/Mage.Sets/src/mage/cards/a/Aurification.java b/Mage.Sets/src/mage/cards/a/Aurification.java index ef96fbf8ceb..3d85d0f83e7 100644 --- a/Mage.Sets/src/mage/cards/a/Aurification.java +++ b/Mage.Sets/src/mage/cards/a/Aurification.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; @@ -24,8 +22,9 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import mage.util.SubTypeList; +import java.util.UUID; + /** - * * @author andyfries */ @@ -40,7 +39,7 @@ public final class Aurification extends CardImpl { static final String rule = "Each creature with a gold counter on it is a Wall in addition to its other creature types and has defender."; public Aurification(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // Whenever a creature deals damage to you, put a gold counter on it. this.addAbility(new AddGoldCountersAbility()); @@ -127,8 +126,11 @@ public final class Aurification extends CardImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { - if (permanent != null){ - permanent.getCounters(game).removeAllCounters(CounterType.GOLD); + if (permanent != null) { + int numToRemove = permanent.getCounters(game).getCount(CounterType.GOLD); + if (numToRemove > 0) { + permanent.removeCounters(CounterType.GOLD.getName(), numToRemove, game); + } } } return true; diff --git a/Mage.Sets/src/mage/cards/i/Inhumaniac.java b/Mage.Sets/src/mage/cards/i/Inhumaniac.java index 8525bb3b53d..e7ae91e038c 100644 --- a/Mage.Sets/src/mage/cards/i/Inhumaniac.java +++ b/Mage.Sets/src/mage/cards/i/Inhumaniac.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -17,8 +15,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author spjspj */ public final class Inhumaniac extends CardImpl { @@ -71,7 +70,10 @@ class InhumaniacEffect extends OneShotEffect { } else if (amount >= 5) { permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); } else if (amount == 1) { - permanent.getCounters(game).removeAllCounters(CounterType.P1P1); + int numToRemove = permanent.getCounters(game).getCount(CounterType.P1P1); + if (numToRemove > 0) { + permanent.removeCounters(CounterType.P1P1.getName(), numToRemove, game); + } } return true; } diff --git a/Mage.Sets/src/mage/cards/m/MineLayer.java b/Mage.Sets/src/mage/cards/m/MineLayer.java index 113cb6f6917..1932c318edd 100644 --- a/Mage.Sets/src/mage/cards/m/MineLayer.java +++ b/Mage.Sets/src/mage/cards/m/MineLayer.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BecomesTappedTriggeredAbility; @@ -12,11 +10,11 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterLandPermanent; @@ -25,8 +23,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetLandPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class MineLayer extends CardImpl { @@ -87,7 +86,10 @@ class RemoveAllMineCountersEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.LAND)) { if (permanent != null) { - permanent.getCounters(game).removeAllCounters(CounterType.MINE); + int numToRemove = permanent.getCounters(game).getCount(CounterType.MINE); + if (numToRemove > 0) { + permanent.removeCounters(CounterType.MINE.getName(), numToRemove, game); + } } } return true; diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java index 90c7b07cd92..ab1196345f3 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java @@ -1,9 +1,5 @@ - package mage.abilities.costs.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; @@ -18,8 +14,11 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX */ public class RemoveCounterCost extends CostImpl { @@ -102,10 +101,6 @@ public class RemoveCounterCost extends CostImpl { new StringBuilder("Remove how many counters from ").append(permanent.getIdName()).toString(), game); } permanent.removeCounters(counterName, numberOfCountersSelected, game); - if (permanent.getCounters(game).getCount(counterName) == 0) { - // this removes only the item with number = 0 from the collection - permanent.getCounters(game).removeCounter(counterName); - } countersRemoved += numberOfCountersSelected; if (!game.isSimulation()) { game.informPlayers(new StringBuilder(controller.getLogName()) From 9273dd1ea033788605d032dc8238ea9598385f70 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 19:21:43 +0400 Subject: [PATCH 14/76] Fixed that some cards doesn't trigger counter add events; --- Mage.Sets/src/mage/cards/c/CrovaxTheCursed.java | 9 ++++----- Mage.Sets/src/mage/cards/e/EntrailsFeaster.java | 15 +++++---------- Mage.Sets/src/mage/cards/n/NaturesBlessing.java | 13 ++++++------- Mage.Sets/src/mage/cards/o/Oubliette.java | 11 +++++------ Mage.Sets/src/mage/cards/t/TawnossCoffin.java | 10 +++++----- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CrovaxTheCursed.java b/Mage.Sets/src/mage/cards/c/CrovaxTheCursed.java index a1f7a602a2f..a7598d50587 100644 --- a/Mage.Sets/src/mage/cards/c/CrovaxTheCursed.java +++ b/Mage.Sets/src/mage/cards/c/CrovaxTheCursed.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -22,14 +20,15 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CrovaxTheCursed extends CardImpl { public CrovaxTheCursed(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VAMPIRE); this.power = new MageInt(0); @@ -82,7 +81,7 @@ class CrovaxTheCursedEffect extends OneShotEffect { if (creatures > 0 && controller.chooseUse(outcome, "Sacrifice a creature?", source, game)) { if (new SacrificeControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURES, 1, "").apply(game, source)) { if (sourceObject != null) { - sourceObject.getCounters(game).addCounter(CounterType.P1P1.createInstance()); + sourceObject.addCounters(CounterType.P1P1.createInstance(), source, game); game.informPlayers(controller.getLogName() + " puts a +1/+1 counter on " + sourceObject.getName()); } } diff --git a/Mage.Sets/src/mage/cards/e/EntrailsFeaster.java b/Mage.Sets/src/mage/cards/e/EntrailsFeaster.java index b9715cbc129..90b8131918f 100644 --- a/Mage.Sets/src/mage/cards/e/EntrailsFeaster.java +++ b/Mage.Sets/src/mage/cards/e/EntrailsFeaster.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -9,11 +7,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; import mage.game.Game; @@ -21,14 +15,15 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; +import java.util.UUID; + /** - * * @author L_J */ public final class EntrailsFeaster extends CardImpl { public EntrailsFeaster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.CAT); this.power = new MageInt(1); @@ -81,7 +76,7 @@ class EntrailsFeasterEffect extends OneShotEffect { if (cardChosen != null) { controller.moveCardsToExile(cardChosen, source, game, true, null, ""); if (sourceObject != null) { - sourceObject.getCounters(game).addCounter(CounterType.P1P1.createInstance()); + sourceObject.addCounters(CounterType.P1P1.createInstance(), source, game); game.informPlayers(controller.getLogName() + " puts a +1/+1 counter on " + sourceObject.getLogName()); } } diff --git a/Mage.Sets/src/mage/cards/n/NaturesBlessing.java b/Mage.Sets/src/mage/cards/n/NaturesBlessing.java index 0b24af291c0..4a400381679 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesBlessing.java +++ b/Mage.Sets/src/mage/cards/n/NaturesBlessing.java @@ -1,9 +1,5 @@ - package mage.cards.n; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; @@ -27,14 +23,17 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author L_J */ public final class NaturesBlessing extends CardImpl { public NaturesBlessing(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{W}"); // {G}{W}, Discard a card: Put a +1/+1 counter on target creature or that creature gains banding, first strike, or trample. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new NaturesBlessingEffect(), new ManaCostsImpl("{G}{W}")); @@ -102,7 +101,7 @@ class NaturesBlessingEffect extends OneShotEffect { if (gainedAbility != null) { game.addEffect(new GainAbilityTargetEffect(gainedAbility, Duration.Custom), source); } else { - targetPermanent.getCounters(game).addCounter(CounterType.P1P1.createInstance()); + targetPermanent.addCounters(CounterType.P1P1.createInstance(), source, game); game.informPlayers(controller.getLogName() + " puts a +1/+1 counter on " + targetPermanent.getLogName()); } return true; diff --git a/Mage.Sets/src/mage/cards/o/Oubliette.java b/Mage.Sets/src/mage/cards/o/Oubliette.java index 57514db4384..9e933be1310 100644 --- a/Mage.Sets/src/mage/cards/o/Oubliette.java +++ b/Mage.Sets/src/mage/cards/o/Oubliette.java @@ -1,9 +1,5 @@ - package mage.cards.o; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -27,8 +23,11 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author MarcoMarin */ public final class Oubliette extends CardImpl { @@ -137,7 +136,7 @@ class OublietteReturnEffect extends OneShotEffect { if (counters != null) { for (Counter counter : counters.values()) { if (counter != null) { - newPermanent.getCounters(game).addCounter(counter); + newPermanent.getCounters(game).addCounter(counter); // it's restore counters, not add (e.g. without add events) } } } diff --git a/Mage.Sets/src/mage/cards/t/TawnossCoffin.java b/Mage.Sets/src/mage/cards/t/TawnossCoffin.java index af280935ff3..45fef01174d 100644 --- a/Mage.Sets/src/mage/cards/t/TawnossCoffin.java +++ b/Mage.Sets/src/mage/cards/t/TawnossCoffin.java @@ -1,8 +1,5 @@ package mage.cards.t; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -32,8 +29,11 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author MarcoMarin */ public final class TawnossCoffin extends CardImpl { @@ -194,7 +194,7 @@ class TawnossCoffinReturnEffect extends OneShotEffect { if (notedCounters != null) { for (Counter c : notedCounters.values()) { //would be nice if could just use that copy function to set the whole field if (c != null) { - newPermanent.getCounters(game).addCounter(c); + newPermanent.getCounters(game).addCounter(c); // it's restore counters, not add (e.g. without add events) } } } From f25f7a0f68005c08df27c5335b2338ea899632f0 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 30 Apr 2019 19:49:55 +0400 Subject: [PATCH 15/76] * Gideon Blackblade - fixed that it's can't prevent damage (#5738) --- Mage.Sets/src/mage/cards/g/GideonBlackblade.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java index c45c68e9be3..d1f44563d36 100644 --- a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java +++ b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java @@ -96,7 +96,7 @@ class GideonBlackbladeToken extends TokenImpl { subtype.add(SubType.SOLDIER); power = new MageInt(4); toughness = new MageInt(4); - addAbility(IndestructibleAbility.getInstance()); + addAbility(new SimpleStaticAbility(new PreventAllDamageToSourceEffect(Duration.WhileOnBattlefield))); } private GideonBlackbladeToken(final GideonBlackbladeToken token) { From c5249d6ef0b2bbe8172fae14ed9de9d8c1747108 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 30 Apr 2019 16:13:02 -0500 Subject: [PATCH 16/76] - #5772. Now the exiled tokens of Ugin the Ineffable reference the exiled card. Controller can view the exiled card at will. --- .../src/mage/cards/u/UginTheIneffable.java | 70 +++++++++++++++---- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java index c8f079335d7..9a589358775 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java +++ b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java @@ -23,15 +23,17 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.token.Token; import mage.game.permanent.token.UginTheIneffableToken; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; - import java.util.HashSet; import java.util.Set; import java.util.UUID; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import static mage.constants.Outcome.Benefit; @@ -83,9 +85,9 @@ class UginTheIneffableEffect extends OneShotEffect { UginTheIneffableEffect() { super(Benefit); - staticText = "Exile the top card of your library face down and look at it. " + - "Create a 2/2 colorless Spirit creature token. When that token leaves the battlefield, " + - "put the exiled card into your hand."; + staticText = "Exile the top card of your library face down and look at it. " + + "Create a 2/2 colorless Spirit creature token. When that token leaves the battlefield, " + + "put the exiled card into your hand."; } private UginTheIneffableEffect(final UginTheIneffableEffect effect) { @@ -108,15 +110,27 @@ class UginTheIneffableEffect extends OneShotEffect { player.lookAtCards(sourcePermanent.getIdName(), card, game); player.moveCards(card, Zone.EXILED, source, game); card.turnFaceDown(game, source.getControllerId()); - Token token = new UginTheIneffableToken(); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); Set tokenObjs = new HashSet<>(); - for (UUID tokenId : token.getLastAddedTokenIds()) { - tokenObjs.add(new MageObjectReference(tokenId, game)); + CreateTokenEffect effect = new CreateTokenEffect(new UginTheIneffableToken()); + effect.apply(game, source); + for (UUID addedTokenId : effect.getLastAddedTokenIds()) { + + // display referenced exiled face-down card on token + SimpleStaticAbility sa = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("Referenced object: " + card.getIdName())); + GainAbilityTargetEffect gainAbilityEffect = new GainAbilityTargetEffect(sa, Duration.WhileOnBattlefield); + gainAbilityEffect.setTargetPointer(new FixedTarget(addedTokenId)); + game.addEffect(gainAbilityEffect, source); + + // look at face-down card in exile + UginTheIneffableLookAtFaceDownEffect lookAtEffect = new UginTheIneffableLookAtFaceDownEffect(); + lookAtEffect.setTargetPointer(new FixedTarget(card.getId())); + game.addEffect(lookAtEffect, source); + + tokenObjs.add(new MageObjectReference(addedTokenId, game)); + game.addDelayedTriggeredAbility(new UginTheIneffableDelayedTriggeredAbility( + tokenObjs, new MageObjectReference(card, game) + ), source); } - game.addDelayedTriggeredAbility(new UginTheIneffableDelayedTriggeredAbility( - tokenObjs, new MageObjectReference(card, game) - ), source); return true; } } @@ -167,3 +181,35 @@ class UginTheIneffableDelayedTriggeredAbility extends DelayedTriggeredAbility { return "When this token leaves the battlefield, put the exiled card into your hand."; } } + +class UginTheIneffableLookAtFaceDownEffect extends AsThoughEffectImpl { + + UginTheIneffableLookAtFaceDownEffect() { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + } + + private UginTheIneffableLookAtFaceDownEffect(final UginTheIneffableLookAtFaceDownEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public UginTheIneffableLookAtFaceDownEffect copy() { + return new UginTheIneffableLookAtFaceDownEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + if (cardId == null) { + this.discard(); + } + return affectedControllerId.equals(source.getControllerId()) + && objectId.equals(cardId) + && game.getState().getExile().containsId(cardId, game); + } +} From 2352e9d765c26b31d8ecb230d56401cb068f6321 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 30 Apr 2019 17:21:03 -0500 Subject: [PATCH 17/76] - Bolas's Citadel now handles additional costs correctly. --- Mage.Sets/src/mage/cards/b/BolassCitadel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java index f7f617045d1..dce1e6898d0 100644 --- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java +++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java @@ -23,6 +23,7 @@ import mage.players.Player; import mage.target.common.TargetControlledPermanent; import java.util.UUID; +import mage.abilities.costs.Cost; /** * @author jeffwadsworth @@ -98,9 +99,14 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { Player controller = game.getPlayer(cardOnTop.getOwnerId()); if (controller != null && cardOnTop.equals(controller.getLibrary().getFromTop(game))) { + // add the life cost first PayLifeCost cost = new PayLifeCost(cardOnTop.getManaCost().convertedManaCost()); Costs costs = new CostsImpl(); costs.add(cost); + // check for additional costs that must be paid + for (Cost additionalCost : cardOnTop.getSpellAbility().getCosts()) { + costs.add(additionalCost); + } controller.setCastSourceIdWithAlternateMana(cardOnTop.getId(), null, costs); return true; } From 367a1fd189c5128892ce79e6e30c49e887a14a3e Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 12:49:19 +0400 Subject: [PATCH 18/76] Added ConditionalPreventionEffect to support prevention effects with conditions (#5738) --- .../src/mage/cards/g/GideonBlackblade.java | 5 +- .../continuous/ConditionalPreventionTest.java | 137 +++++++++++++++++ .../continuous/GideonBlackbladeTest.java | 39 +++++ .../ConditionalContinuousEffect.java | 27 +++- .../ConditionalPreventionEffect.java | 140 ++++++++++++++++++ .../effects/ContinuousEffectImpl.java | 8 - .../abilities/effects/ContinuousEffects.java | 2 + 7 files changed, 340 insertions(+), 18 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/GideonBlackbladeTest.java create mode 100644 Mage/src/main/java/mage/abilities/decorator/ConditionalPreventionEffect.java diff --git a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java index d1f44563d36..f57cd82e970 100644 --- a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java +++ b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java @@ -7,6 +7,7 @@ import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalPreventionEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.PreventAllDamageToSourceEffect; @@ -61,7 +62,7 @@ public final class GideonBlackblade extends CardImpl { ))); // Prevent all damage that would be dealt to Gideon Blackblade during your turn. - this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + this.addAbility(new SimpleStaticAbility(new ConditionalPreventionEffect( new PreventAllDamageToSourceEffect(Duration.WhileOnBattlefield), MyTurnCondition.instance, "Prevent all damage that would be dealt to {this} during your turn." ))); @@ -96,7 +97,7 @@ class GideonBlackbladeToken extends TokenImpl { subtype.add(SubType.SOLDIER); power = new MageInt(4); toughness = new MageInt(4); - addAbility(new SimpleStaticAbility(new PreventAllDamageToSourceEffect(Duration.WhileOnBattlefield))); + this.addAbility(IndestructibleAbility.getInstance()); } private GideonBlackbladeToken(final GideonBlackbladeToken token) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java new file mode 100644 index 00000000000..42c8b0bb6f7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java @@ -0,0 +1,137 @@ +package org.mage.test.cards.continuous; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.condition.common.NotMyTurnCondition; +import mage.abilities.decorator.ConditionalPreventionEffect; +import mage.abilities.effects.common.PreventAllDamageToAllEffect; +import mage.abilities.effects.common.PreventAllDamageToPlayersEffect; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ConditionalPreventionTest extends CardTestPlayerBase { + + // conditional effects go to layered, but there are prevention effects list too + + @Test + public void test_NotPreventDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 0); + assertHandCount(playerA, "Lightning Bolt", 0); + } + + @Test + public void test_PreventDamageNormal() { + addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility(new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT))); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 1); + assertHandCount(playerA, "Lightning Bolt", 0); + } + + @Test + public void test_PreventDamageConditionalActive() { + addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility( + new ConditionalPreventionEffect( + new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT), + MyTurnCondition.instance, + "" + ) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 1); + assertHandCount(playerA, "Lightning Bolt", 0); + } + + @Test + public void test_PreventDamageConditionalNotActive() { + addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility( + new ConditionalPreventionEffect( + new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT), + NotMyTurnCondition.instance, + "" + ) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 0); + assertHandCount(playerA, "Lightning Bolt", 0); + } + + @Test + public void test_PreventDamageConditionalNotActiveWithOtherEffect() { + addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility( + new ConditionalPreventionEffect( + new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT), + new PreventAllDamageToPlayersEffect(Duration.WhileOnBattlefield, false), + NotMyTurnCondition.instance, + "" + ) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); // will prevent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); // will not prevent + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 0); // not prevented, dies + assertLife(playerA, 20); // prevented, no damage + assertHandCount(playerA, "Lightning Bolt", 0); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GideonBlackbladeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GideonBlackbladeTest.java new file mode 100644 index 00000000000..9f1baf27a7f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/GideonBlackbladeTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class GideonBlackbladeTest extends CardTestPlayerBase { + + // Gideon Blackblade L4 + // As long as it's your turn, Gideon Blackblade is a 4/4 Human Soldier creature with indestructible that's still a planeswalker. + // Prevent all damage that would be dealt to Gideon Blackblade during your turn. + + @Test + public void test_PreventDamageToGideonOnYourTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Gideon Blackblade"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + + checkPT("turn 1 before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gideon Blackblade", 4, 4); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", "Gideon Blackblade"); + checkPT("turn 1 after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", 4, 4); + checkPermanentCounters("turn 1 after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", CounterType.LOYALTY, 4); + + checkPT("turn 2 before", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Gideon Blackblade", 0, 0); + castSpell(2, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", "Gideon Blackblade"); + checkPT("turn 2 after", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", 0, 0); + checkPermanentCounters("turn 2 after", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", CounterType.LOYALTY, 4 - 3); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java index 1ce5392da33..254f20dd03e 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java @@ -1,9 +1,5 @@ package mage.abilities.decorator; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.condition.Condition; @@ -11,11 +7,14 @@ import mage.abilities.condition.FixedCondition; import mage.abilities.condition.LockedInCondition; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.SubLayer; +import mage.constants.*; import mage.game.Game; +import org.junit.Assert; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; /** * Adds condition to {@link ContinuousEffect}. Acts as decorator. @@ -48,6 +47,17 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { this.otherwiseEffect = otherwiseEffect; this.baseCondition = condition; this.staticText = text; + + // checks for compatibility + if (effect != null && !effect.getEffectType().equals(EffectType.CONTINUOUS)) { + Assert.fail("ConditionalContinuousEffect supports only " + EffectType.CONTINUOUS.toString() + " but found " + effect.getEffectType().toString()); + } + if (otherwiseEffect != null && !otherwiseEffect.getEffectType().equals(EffectType.CONTINUOUS)) { + Assert.fail("ConditionalContinuousEffect supports only " + EffectType.CONTINUOUS.toString() + " but found " + effect.getEffectType().toString()); + } + if (effect != null && otherwiseEffect != null && !effect.getEffectType().equals(otherwiseEffect.getEffectType())) { + Assert.fail("ConditionalContinuousEffect must be same but found " + effect.getEffectType().toString() + " and " + otherwiseEffect.getEffectType().toString()); + } } public ConditionalContinuousEffect(final ConditionalContinuousEffect effect) { @@ -68,6 +78,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); if (baseCondition instanceof LockedInCondition) { condition = new FixedCondition(((LockedInCondition) baseCondition).getBaseCondition().apply(game, source)); } else { diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalPreventionEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalPreventionEffect.java new file mode 100644 index 00000000000..ec256f06dc0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalPreventionEffect.java @@ -0,0 +1,140 @@ +package mage.abilities.decorator; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.condition.Condition; +import mage.abilities.condition.FixedCondition; +import mage.abilities.condition.LockedInCondition; +import mage.abilities.effects.PreventionEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author JayDi85 + */ +public class ConditionalPreventionEffect extends PreventionEffectImpl { + + protected PreventionEffect effect; + protected PreventionEffect otherwiseEffect; + protected Condition baseCondition; + protected Condition condition; + protected boolean conditionState; + protected boolean initDone = false; + + public ConditionalPreventionEffect(PreventionEffect effect, Condition condition, String text) { + this(effect, null, condition, text); + } + + /** + * Only use this if both effects have the same layers + * + * @param effect + * @param otherwiseEffect + * @param condition + * @param text + */ + public ConditionalPreventionEffect(PreventionEffect effect, PreventionEffect otherwiseEffect, Condition condition, String text) { + super(effect.getDuration()); + this.effect = effect; + this.otherwiseEffect = otherwiseEffect; + this.baseCondition = condition; + this.staticText = text; + } + + public ConditionalPreventionEffect(final ConditionalPreventionEffect effect) { + super(effect); + this.effect = (PreventionEffect) effect.effect.copy(); + if (effect.otherwiseEffect != null) { + this.otherwiseEffect = (PreventionEffect) effect.otherwiseEffect.copy(); + } + this.condition = effect.condition; // TODO: checks conditional copy -- it's can be usefull for memory leaks fix? + this.conditionState = effect.conditionState; + this.baseCondition = effect.baseCondition; + this.initDone = effect.initDone; + } + + @Override + public boolean isDiscarded() { + return this.discarded || effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded()); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (baseCondition instanceof LockedInCondition) { + condition = new FixedCondition(((LockedInCondition) baseCondition).getBaseCondition().apply(game, source)); + } else { + condition = baseCondition; + } + effect.setTargetPointer(this.targetPointer); + effect.init(source, game); + if (otherwiseEffect != null) { + otherwiseEffect.setTargetPointer(this.targetPointer); + otherwiseEffect.init(source, game); + } + initDone = true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if (conditionState) { + effect.setTargetPointer(this.targetPointer); + return effect.replaceEvent(event, source, game); + } else if (otherwiseEffect != null) { + otherwiseEffect.setTargetPointer(this.targetPointer); + return otherwiseEffect.replaceEvent(event, source, game); + } + + if (!conditionState && effect.getDuration() == Duration.OneUse) { + used = true; + } + if (!conditionState && effect.getDuration() == Duration.Custom) { + this.discard(); + } + + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return effect.checksEventType(event, game) + || (otherwiseEffect != null && otherwiseEffect.checksEventType(event, game)); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!initDone) { // if simpleStaticAbility, init won't be called + init(source, game); + } + conditionState = condition.apply(game, source); + if (conditionState) { + effect.setTargetPointer(this.targetPointer); + return effect.applies(event, source, game); + } else if (otherwiseEffect != null) { + otherwiseEffect.setTargetPointer(this.targetPointer); + return otherwiseEffect.applies(event, source, game); + } + return false; + } + + @Override + public String getText(Mode mode) { + if ((staticText == null || staticText.isEmpty()) && this.effect != null) { // usefull for conditional night/day card abilities + return effect.getText(mode); + } + return staticText; + } + + @Override + public ConditionalPreventionEffect copy() { + return new ConditionalPreventionEffect(this); + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java index 2970bbe3531..7cc9ed6c867 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java @@ -311,14 +311,6 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu } } return dependentToEffects; - /* - return allEffectsInLayer.stream() - .filter(effect -> effect.getDependencyTypes().contains(dependendToTypes)) - .map(Effect::getId) - .collect(Collectors.toSet()); - - } - return new HashSet<>();*/ } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index e6bb1a53da2..6cb9d0e7703 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -369,6 +369,7 @@ public class ContinuousEffects implements Serializable { replaceEffects.put(effect, applicableAbilities); } } + for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext(); ) { PreventionEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { @@ -394,6 +395,7 @@ public class ContinuousEffects implements Serializable { replaceEffects.put(effect, applicableAbilities); } } + return replaceEffects; } From 53341c551932231b437e44e54e7ace0810fa1e63 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 1 May 2019 17:12:24 +0200 Subject: [PATCH 19/76] [ICE] Added Fylgja --- Mage.Sets/src/mage/cards/f/Fylgja.java | 66 ++ Mage.Sets/src/mage/sets/IceAge.java | 759 +++++++++--------- .../common/PreventDamageToAttachedEffect.java | 5 +- 3 files changed, 448 insertions(+), 382 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/f/Fylgja.java diff --git a/Mage.Sets/src/mage/cards/f/Fylgja.java b/Mage.Sets/src/mage/cards/f/Fylgja.java new file mode 100644 index 00000000000..49df5677476 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Fylgja.java @@ -0,0 +1,66 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.PreventDamageToAttachedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public final class Fylgja extends CardImpl { + + public Fylgja(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Fylgja enters the battlefield with four healing counters on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.HEALING.createInstance(4)) + .setText("with four healing counters on it."))); + + // Remove a healing counter from Fylgja: Prevent the next 1 damage that would be dealt to enchanted creature this turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToAttachedEffect(Duration.EndOfTurn, AttachmentType.AURA, 1, false) + .setText("Prevent the next 1 damage that would be dealt to enchanted creature this turn"), + new RemoveCountersSourceCost(CounterType.HEALING.createInstance(1)))); + + // {2}{W}: Put a healing counter on Fylgja. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.HEALING.createInstance(1)), + new ManaCostsImpl("{2}{W}"))); + } + + private Fylgja(final Fylgja card) { + super(card); + } + + @Override + public Fylgja copy() { + return new Fylgja(this); + } +} diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index 863417b6bec..5c0ab4bc858 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -1,379 +1,380 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author North - */ -public final class IceAge extends ExpansionSet { - - private static final IceAge instance = new IceAge(); - - public static IceAge getInstance() { - return instance; - } - - private IceAge() { - super("Ice Age", "ICE", ExpansionSet.buildDate(1995, 5, 1), SetType.EXPANSION); - this.blockName = "Ice Age"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - - cards.add(new SetCardInfo("Abyssal Specter", 113, Rarity.UNCOMMON, mage.cards.a.AbyssalSpecter.class)); - cards.add(new SetCardInfo("Adarkar Sentinel", 306, Rarity.UNCOMMON, mage.cards.a.AdarkarSentinel.class)); - cards.add(new SetCardInfo("Adarkar Wastes", 351, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); - cards.add(new SetCardInfo("Aegis of the Meek", 307, Rarity.RARE, mage.cards.a.AegisOfTheMeek.class)); - cards.add(new SetCardInfo("Aggression", 169, Rarity.UNCOMMON, mage.cards.a.Aggression.class)); - cards.add(new SetCardInfo("Altar of Bone", 281, Rarity.RARE, mage.cards.a.AltarOfBone.class)); - cards.add(new SetCardInfo("Anarchy", 170, Rarity.UNCOMMON, mage.cards.a.Anarchy.class)); - cards.add(new SetCardInfo("Arenson's Aura", 3, Rarity.COMMON, mage.cards.a.ArensonsAura.class)); - cards.add(new SetCardInfo("Armor of Faith", 4, Rarity.COMMON, mage.cards.a.ArmorOfFaith.class)); - cards.add(new SetCardInfo("Arnjlot's Ascent", 57, Rarity.COMMON, mage.cards.a.ArnjlotsAscent.class)); - cards.add(new SetCardInfo("Ashen Ghoul", 114, Rarity.UNCOMMON, mage.cards.a.AshenGhoul.class)); - cards.add(new SetCardInfo("Aurochs", 225, Rarity.COMMON, mage.cards.a.Aurochs.class)); - cards.add(new SetCardInfo("Avalanche", 171, Rarity.UNCOMMON, mage.cards.a.Avalanche.class)); - cards.add(new SetCardInfo("Balduvian Barbarians", 172, Rarity.COMMON, mage.cards.b.BalduvianBarbarians.class)); - cards.add(new SetCardInfo("Balduvian Bears", 226, Rarity.COMMON, mage.cards.b.BalduvianBears.class)); - cards.add(new SetCardInfo("Balduvian Conjurer", 58, Rarity.UNCOMMON, mage.cards.b.BalduvianConjurer.class)); - cards.add(new SetCardInfo("Balduvian Hydra", 173, Rarity.RARE, mage.cards.b.BalduvianHydra.class)); - cards.add(new SetCardInfo("Barbed Sextant", 312, Rarity.COMMON, mage.cards.b.BarbedSextant.class)); - cards.add(new SetCardInfo("Baton of Morale", 313, Rarity.UNCOMMON, mage.cards.b.BatonOfMorale.class)); - cards.add(new SetCardInfo("Battle Cry", 5, Rarity.UNCOMMON, mage.cards.b.BattleCry.class)); - cards.add(new SetCardInfo("Battle Frenzy", 175, Rarity.COMMON, mage.cards.b.BattleFrenzy.class)); - cards.add(new SetCardInfo("Binding Grasp", 60, Rarity.UNCOMMON, mage.cards.b.BindingGrasp.class)); - cards.add(new SetCardInfo("Black Scarab", 6, Rarity.UNCOMMON, mage.cards.b.BlackScarab.class)); - cards.add(new SetCardInfo("Blessed Wine", 7, Rarity.COMMON, mage.cards.b.BlessedWine.class)); - cards.add(new SetCardInfo("Blinking Spirit", 8, Rarity.RARE, mage.cards.b.BlinkingSpirit.class)); - cards.add(new SetCardInfo("Blizzard", 227, Rarity.RARE, mage.cards.b.Blizzard.class)); - cards.add(new SetCardInfo("Blue Scarab", 9, Rarity.UNCOMMON, mage.cards.b.BlueScarab.class)); - cards.add(new SetCardInfo("Brainstorm", 61, Rarity.COMMON, mage.cards.b.Brainstorm.class)); - cards.add(new SetCardInfo("Brand of Ill Omen", 177, Rarity.RARE, mage.cards.b.BrandOfIllOmen.class)); - cards.add(new SetCardInfo("Breath of Dreams", 62, Rarity.UNCOMMON, mage.cards.b.BreathOfDreams.class)); - cards.add(new SetCardInfo("Brine Shaman", 115, Rarity.COMMON, mage.cards.b.BrineShaman.class)); - cards.add(new SetCardInfo("Brown Ouphe", 228, Rarity.COMMON, mage.cards.b.BrownOuphe.class)); - cards.add(new SetCardInfo("Brushland", 352, Rarity.RARE, mage.cards.b.Brushland.class)); - cards.add(new SetCardInfo("Burnt Offering", 116, Rarity.COMMON, mage.cards.b.BurntOffering.class)); - cards.add(new SetCardInfo("Call to Arms", 10, Rarity.RARE, mage.cards.c.CallToArms.class)); - cards.add(new SetCardInfo("Caribou Range", 11, Rarity.RARE, mage.cards.c.CaribouRange.class)); - cards.add(new SetCardInfo("Celestial Sword", 314, Rarity.RARE, mage.cards.c.CelestialSword.class)); - cards.add(new SetCardInfo("Centaur Archer", 282, Rarity.UNCOMMON, mage.cards.c.CentaurArcher.class)); - cards.add(new SetCardInfo("Chaos Lord", 178, Rarity.RARE, mage.cards.c.ChaosLord.class)); - cards.add(new SetCardInfo("Chaos Moon", 179, Rarity.RARE, mage.cards.c.ChaosMoon.class)); - cards.add(new SetCardInfo("Chub Toad", 229, Rarity.COMMON, mage.cards.c.ChubToad.class)); - cards.add(new SetCardInfo("Circle of Protection: Black", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); - cards.add(new SetCardInfo("Circle of Protection: Blue", 13, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); - cards.add(new SetCardInfo("Circle of Protection: Green", 14, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); - cards.add(new SetCardInfo("Circle of Protection: Red", 15, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); - cards.add(new SetCardInfo("Circle of Protection: White", 16, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); - cards.add(new SetCardInfo("Clairvoyance", 63, Rarity.COMMON, mage.cards.c.Clairvoyance.class)); - cards.add(new SetCardInfo("Cloak of Confusion", 117, Rarity.COMMON, mage.cards.c.CloakOfConfusion.class)); - cards.add(new SetCardInfo("Cold Snap", 17, Rarity.UNCOMMON, mage.cards.c.ColdSnap.class)); - cards.add(new SetCardInfo("Conquer", 180, Rarity.UNCOMMON, mage.cards.c.Conquer.class)); - cards.add(new SetCardInfo("Cooperation", 18, Rarity.COMMON, mage.cards.c.Cooperation.class)); - cards.add(new SetCardInfo("Counterspell", 64, Rarity.COMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Crown of the Ages", 315, Rarity.RARE, mage.cards.c.CrownOfTheAges.class)); - cards.add(new SetCardInfo("Curse of Marit Lage", 181, Rarity.RARE, mage.cards.c.CurseOfMaritLage.class)); - cards.add(new SetCardInfo("Dance of the Dead", 118, Rarity.UNCOMMON, mage.cards.d.DanceOfTheDead.class)); - cards.add(new SetCardInfo("Dark Banishing", 119, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); - cards.add(new SetCardInfo("Dark Ritual", 120, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Death Ward", 19, Rarity.COMMON, mage.cards.d.DeathWard.class)); - cards.add(new SetCardInfo("Deflection", 65, Rarity.RARE, mage.cards.d.Deflection.class)); - cards.add(new SetCardInfo("Demonic Consultation", 121, Rarity.UNCOMMON, mage.cards.d.DemonicConsultation.class)); - cards.add(new SetCardInfo("Despotic Scepter", 316, Rarity.RARE, mage.cards.d.DespoticScepter.class)); - cards.add(new SetCardInfo("Diabolic Vision", 284, Rarity.UNCOMMON, mage.cards.d.DiabolicVision.class)); - cards.add(new SetCardInfo("Dire Wolves", 230, Rarity.COMMON, mage.cards.d.DireWolves.class)); - cards.add(new SetCardInfo("Disenchant", 20, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Dread Wight", 122, Rarity.RARE, mage.cards.d.DreadWight.class)); - cards.add(new SetCardInfo("Dreams of the Dead", 66, Rarity.UNCOMMON, mage.cards.d.DreamsOfTheDead.class)); - cards.add(new SetCardInfo("Drift of the Dead", 123, Rarity.UNCOMMON, mage.cards.d.DriftOfTheDead.class)); - cards.add(new SetCardInfo("Drought", 21, Rarity.UNCOMMON, mage.cards.d.Drought.class)); - cards.add(new SetCardInfo("Dwarven Armory", 182, Rarity.RARE, mage.cards.d.DwarvenArmory.class)); - cards.add(new SetCardInfo("Earthlink", 285, Rarity.RARE, mage.cards.e.Earthlink.class)); - cards.add(new SetCardInfo("Earthlore", 231, Rarity.COMMON, mage.cards.e.Earthlore.class)); - cards.add(new SetCardInfo("Elder Druid", 232, Rarity.RARE, mage.cards.e.ElderDruid.class)); - cards.add(new SetCardInfo("Elemental Augury", 286, Rarity.RARE, mage.cards.e.ElementalAugury.class)); - cards.add(new SetCardInfo("Elkin Bottle", 317, Rarity.RARE, mage.cards.e.ElkinBottle.class)); - cards.add(new SetCardInfo("Enduring Renewal", 23, Rarity.RARE, mage.cards.e.EnduringRenewal.class)); - cards.add(new SetCardInfo("Energy Storm", 24, Rarity.RARE, mage.cards.e.EnergyStorm.class)); - cards.add(new SetCardInfo("Enervate", 67, Rarity.COMMON, mage.cards.e.Enervate.class)); - cards.add(new SetCardInfo("Errant Minion", 68, Rarity.COMMON, mage.cards.e.ErrantMinion.class)); - cards.add(new SetCardInfo("Errantry", 183, Rarity.COMMON, mage.cards.e.Errantry.class)); - cards.add(new SetCardInfo("Essence Filter", 233, Rarity.COMMON, mage.cards.e.EssenceFilter.class)); - cards.add(new SetCardInfo("Essence Flare", 69, Rarity.COMMON, mage.cards.e.EssenceFlare.class)); - cards.add(new SetCardInfo("Fanatical Fever", 234, Rarity.UNCOMMON, mage.cards.f.FanaticalFever.class)); - cards.add(new SetCardInfo("Fear", 124, Rarity.COMMON, mage.cards.f.Fear.class)); - cards.add(new SetCardInfo("Fiery Justice", 288, Rarity.RARE, mage.cards.f.FieryJustice.class)); - cards.add(new SetCardInfo("Fire Covenant", 289, Rarity.UNCOMMON, mage.cards.f.FireCovenant.class)); - cards.add(new SetCardInfo("Flame Spirit", 184, Rarity.UNCOMMON, mage.cards.f.FlameSpirit.class)); - cards.add(new SetCardInfo("Flare", 185, Rarity.COMMON, mage.cards.f.Flare.class)); - cards.add(new SetCardInfo("Flooded Woodlands", 290, Rarity.RARE, mage.cards.f.FloodedWoodlands.class)); - cards.add(new SetCardInfo("Flow of Maggots", 125, Rarity.RARE, mage.cards.f.FlowOfMaggots.class)); - cards.add(new SetCardInfo("Folk of the Pines", 235, Rarity.COMMON, mage.cards.f.FolkOfThePines.class)); - cards.add(new SetCardInfo("Forbidden Lore", 236, Rarity.RARE, mage.cards.f.ForbiddenLore.class)); - cards.add(new SetCardInfo("Force Void", 70, Rarity.UNCOMMON, mage.cards.f.ForceVoid.class)); - cards.add(new SetCardInfo("Forest", 380, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 381, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 382, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forgotten Lore", 237, Rarity.UNCOMMON, mage.cards.f.ForgottenLore.class)); - cards.add(new SetCardInfo("Formation", 25, Rarity.RARE, mage.cards.f.Formation.class)); - cards.add(new SetCardInfo("Foul Familiar", 126, Rarity.COMMON, mage.cards.f.FoulFamiliar.class)); - cards.add(new SetCardInfo("Foxfire", 238, Rarity.COMMON, mage.cards.f.Foxfire.class)); - cards.add(new SetCardInfo("Freyalise Supplicant", 239, Rarity.UNCOMMON, mage.cards.f.FreyaliseSupplicant.class)); - cards.add(new SetCardInfo("Freyalise's Charm", 240, Rarity.UNCOMMON, mage.cards.f.FreyalisesCharm.class)); - cards.add(new SetCardInfo("Freyalise's Winds", 241, Rarity.RARE, mage.cards.f.FreyalisesWinds.class)); - cards.add(new SetCardInfo("Fumarole", 291, Rarity.UNCOMMON, mage.cards.f.Fumarole.class)); - cards.add(new SetCardInfo("Fyndhorn Bow", 318, Rarity.UNCOMMON, mage.cards.f.FyndhornBow.class)); - cards.add(new SetCardInfo("Fyndhorn Brownie", 242, Rarity.COMMON, mage.cards.f.FyndhornBrownie.class)); - cards.add(new SetCardInfo("Fyndhorn Elder", 243, Rarity.UNCOMMON, mage.cards.f.FyndhornElder.class)); - cards.add(new SetCardInfo("Fyndhorn Elves", 244, Rarity.COMMON, mage.cards.f.FyndhornElves.class)); - cards.add(new SetCardInfo("Fyndhorn Pollen", 245, Rarity.RARE, mage.cards.f.FyndhornPollen.class)); - cards.add(new SetCardInfo("Game of Chaos", 186, Rarity.RARE, mage.cards.g.GameOfChaos.class)); - cards.add(new SetCardInfo("Gangrenous Zombies", 127, Rarity.COMMON, mage.cards.g.GangrenousZombies.class)); - cards.add(new SetCardInfo("Gaze of Pain", 128, Rarity.COMMON, mage.cards.g.GazeOfPain.class)); - cards.add(new SetCardInfo("General Jarkeld", 27, Rarity.RARE, mage.cards.g.GeneralJarkeld.class)); - cards.add(new SetCardInfo("Giant Growth", 246, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); - cards.add(new SetCardInfo("Giant Trap Door Spider", 293, Rarity.UNCOMMON, mage.cards.g.GiantTrapDoorSpider.class)); - cards.add(new SetCardInfo("Glacial Chasm", 353, Rarity.UNCOMMON, mage.cards.g.GlacialChasm.class)); - cards.add(new SetCardInfo("Glacial Crevasses", 187, Rarity.RARE, mage.cards.g.GlacialCrevasses.class)); - cards.add(new SetCardInfo("Glacial Wall", 71, Rarity.UNCOMMON, mage.cards.g.GlacialWall.class)); - cards.add(new SetCardInfo("Glaciers", 294, Rarity.RARE, mage.cards.g.Glaciers.class)); - cards.add(new SetCardInfo("Goblin Lyre", 319, Rarity.RARE, mage.cards.g.GoblinLyre.class)); - cards.add(new SetCardInfo("Goblin Mutant", 188, Rarity.UNCOMMON, mage.cards.g.GoblinMutant.class)); - cards.add(new SetCardInfo("Goblin Snowman", 191, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); - cards.add(new SetCardInfo("Gorilla Pack", 247, Rarity.COMMON, mage.cards.g.GorillaPack.class)); - cards.add(new SetCardInfo("Gravebind", 129, Rarity.RARE, mage.cards.g.Gravebind.class)); - cards.add(new SetCardInfo("Green Scarab", 28, Rarity.UNCOMMON, mage.cards.g.GreenScarab.class)); - cards.add(new SetCardInfo("Hallowed Ground", 29, Rarity.UNCOMMON, mage.cards.h.HallowedGround.class)); - cards.add(new SetCardInfo("Halls of Mist", 354, Rarity.RARE, mage.cards.h.HallsOfMist.class)); - cards.add(new SetCardInfo("Heal", 30, Rarity.COMMON, mage.cards.h.Heal.class)); - cards.add(new SetCardInfo("Hecatomb", 130, Rarity.RARE, mage.cards.h.Hecatomb.class)); - cards.add(new SetCardInfo("Hematite Talisman", 320, Rarity.UNCOMMON, mage.cards.h.HematiteTalisman.class)); - cards.add(new SetCardInfo("Hoar Shade", 131, Rarity.COMMON, mage.cards.h.HoarShade.class)); - cards.add(new SetCardInfo("Hot Springs", 248, Rarity.RARE, mage.cards.h.HotSprings.class)); - cards.add(new SetCardInfo("Howl from Beyond", 132, Rarity.COMMON, mage.cards.h.HowlFromBeyond.class)); - cards.add(new SetCardInfo("Hurricane", 249, Rarity.UNCOMMON, mage.cards.h.Hurricane.class)); - cards.add(new SetCardInfo("Hyalopterous Lemure", 133, Rarity.UNCOMMON, mage.cards.h.HyalopterousLemure.class)); - cards.add(new SetCardInfo("Hydroblast", 72, Rarity.COMMON, mage.cards.h.Hydroblast.class)); - cards.add(new SetCardInfo("Hymn of Rebirth", 295, Rarity.UNCOMMON, mage.cards.h.HymnOfRebirth.class)); - cards.add(new SetCardInfo("Ice Cauldron", 321, Rarity.RARE, mage.cards.i.IceCauldron.class)); - cards.add(new SetCardInfo("Ice Floe", 355, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); - cards.add(new SetCardInfo("Iceberg", 73, Rarity.UNCOMMON, mage.cards.i.Iceberg.class)); - cards.add(new SetCardInfo("Icequake", 134, Rarity.UNCOMMON, mage.cards.i.Icequake.class)); - cards.add(new SetCardInfo("Icy Manipulator", 322, Rarity.UNCOMMON, mage.cards.i.IcyManipulator.class)); - cards.add(new SetCardInfo("Icy Prison", 74, Rarity.RARE, mage.cards.i.IcyPrison.class)); - cards.add(new SetCardInfo("Illusionary Forces", 75, Rarity.COMMON, mage.cards.i.IllusionaryForces.class)); - cards.add(new SetCardInfo("Illusionary Presence", 76, Rarity.RARE, mage.cards.i.IllusionaryPresence.class)); - cards.add(new SetCardInfo("Illusionary Terrain", 77, Rarity.UNCOMMON, mage.cards.i.IllusionaryTerrain.class)); - cards.add(new SetCardInfo("Illusionary Wall", 78, Rarity.COMMON, mage.cards.i.IllusionaryWall.class)); - cards.add(new SetCardInfo("Illusions of Grandeur", 79, Rarity.RARE, mage.cards.i.IllusionsOfGrandeur.class)); - cards.add(new SetCardInfo("Imposing Visage", 193, Rarity.COMMON, mage.cards.i.ImposingVisage.class)); - cards.add(new SetCardInfo("Incinerate", 194, Rarity.COMMON, mage.cards.i.Incinerate.class)); - cards.add(new SetCardInfo("Infernal Darkness", 135, Rarity.RARE, mage.cards.i.InfernalDarkness.class)); - cards.add(new SetCardInfo("Infernal Denizen", 136, Rarity.RARE, mage.cards.i.InfernalDenizen.class)); - cards.add(new SetCardInfo("Infinite Hourglass", 323, Rarity.RARE, mage.cards.i.InfiniteHourglass.class)); - cards.add(new SetCardInfo("Infuse", 80, Rarity.COMMON, mage.cards.i.Infuse.class)); - cards.add(new SetCardInfo("Island", 368, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 369, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 370, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jester's Cap", 324, Rarity.RARE, mage.cards.j.JestersCap.class)); - cards.add(new SetCardInfo("Jester's Mask", 325, Rarity.RARE, mage.cards.j.JestersMask.class)); - cards.add(new SetCardInfo("Jeweled Amulet", 326, Rarity.UNCOMMON, mage.cards.j.JeweledAmulet.class)); - cards.add(new SetCardInfo("Johtull Wurm", 250, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class)); - cards.add(new SetCardInfo("Jokulhaups", 195, Rarity.RARE, mage.cards.j.Jokulhaups.class)); - cards.add(new SetCardInfo("Juniper Order Druid", 251, Rarity.COMMON, mage.cards.j.JuniperOrderDruid.class)); - cards.add(new SetCardInfo("Justice", 32, Rarity.UNCOMMON, mage.cards.j.Justice.class)); - cards.add(new SetCardInfo("Karplusan Forest", 356, Rarity.RARE, mage.cards.k.KarplusanForest.class)); - cards.add(new SetCardInfo("Karplusan Giant", 196, Rarity.UNCOMMON, mage.cards.k.KarplusanGiant.class)); - cards.add(new SetCardInfo("Karplusan Yeti", 197, Rarity.RARE, mage.cards.k.KarplusanYeti.class)); - cards.add(new SetCardInfo("Kelsinko Ranger", 33, Rarity.COMMON, mage.cards.k.KelsinkoRanger.class)); - cards.add(new SetCardInfo("Kjeldoran Dead", 137, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); - cards.add(new SetCardInfo("Kjeldoran Frostbeast", 296, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class)); - cards.add(new SetCardInfo("Kjeldoran Knight", 36, Rarity.RARE, mage.cards.k.KjeldoranKnight.class)); - cards.add(new SetCardInfo("Kjeldoran Phalanx", 37, Rarity.RARE, mage.cards.k.KjeldoranPhalanx.class)); - cards.add(new SetCardInfo("Kjeldoran Royal Guard", 38, Rarity.RARE, mage.cards.k.KjeldoranRoyalGuard.class)); - cards.add(new SetCardInfo("Kjeldoran Skycaptain", 39, Rarity.UNCOMMON, mage.cards.k.KjeldoranSkycaptain.class)); - cards.add(new SetCardInfo("Kjeldoran Skyknight", 40, Rarity.COMMON, mage.cards.k.KjeldoranSkyknight.class)); - cards.add(new SetCardInfo("Kjeldoran Warrior", 41, Rarity.COMMON, mage.cards.k.KjeldoranWarrior.class)); - cards.add(new SetCardInfo("Knight of Stromgald", 138, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); - cards.add(new SetCardInfo("Krovikan Elementalist", 139, Rarity.UNCOMMON, mage.cards.k.KrovikanElementalist.class)); - cards.add(new SetCardInfo("Krovikan Fetish", 140, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); - cards.add(new SetCardInfo("Krovikan Sorcerer", 81, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); - cards.add(new SetCardInfo("Krovikan Vampire", 141, Rarity.UNCOMMON, mage.cards.k.KrovikanVampire.class)); - cards.add(new SetCardInfo("Land Cap", 357, Rarity.RARE, mage.cards.l.LandCap.class)); - cards.add(new SetCardInfo("Lapis Lazuli Talisman", 327, Rarity.UNCOMMON, mage.cards.l.LapisLazuliTalisman.class)); - cards.add(new SetCardInfo("Lava Tubes", 358, Rarity.RARE, mage.cards.l.LavaTubes.class)); - cards.add(new SetCardInfo("Legions of Lim-Dul", 142, Rarity.COMMON, mage.cards.l.LegionsOfLimDul.class)); - cards.add(new SetCardInfo("Leshrac's Rite", 143, Rarity.UNCOMMON, mage.cards.l.LeshracsRite.class)); - cards.add(new SetCardInfo("Leshrac's Sigil", 144, Rarity.UNCOMMON, mage.cards.l.LeshracsSigil.class)); - cards.add(new SetCardInfo("Lhurgoyf", 252, Rarity.RARE, mage.cards.l.Lhurgoyf.class)); - cards.add(new SetCardInfo("Lightning Blow", 42, Rarity.RARE, mage.cards.l.LightningBlow.class)); - cards.add(new SetCardInfo("Lim-Dul's Cohort", 145, Rarity.COMMON, mage.cards.l.LimDulsCohort.class)); - cards.add(new SetCardInfo("Lim-Dul's Hex", 146, Rarity.UNCOMMON, mage.cards.l.LimDulsHex.class)); - cards.add(new SetCardInfo("Lost Order of Jarkeld", 43, Rarity.RARE, mage.cards.l.LostOrderOfJarkeld.class)); - cards.add(new SetCardInfo("Lure", 253, Rarity.UNCOMMON, mage.cards.l.Lure.class)); - cards.add(new SetCardInfo("Magus of the Unseen", 82, Rarity.RARE, mage.cards.m.MagusOfTheUnseen.class)); - cards.add(new SetCardInfo("Malachite Talisman", 328, Rarity.UNCOMMON, mage.cards.m.MalachiteTalisman.class)); - cards.add(new SetCardInfo("Marton Stromgald", 199, Rarity.RARE, mage.cards.m.MartonStromgald.class)); - cards.add(new SetCardInfo("Melee", 200, Rarity.UNCOMMON, mage.cards.m.Melee.class)); - cards.add(new SetCardInfo("Melting", 201, Rarity.UNCOMMON, mage.cards.m.Melting.class)); - cards.add(new SetCardInfo("Merieke Ri Berit", 297, Rarity.RARE, mage.cards.m.MeriekeRiBerit.class)); - cards.add(new SetCardInfo("Mesmeric Trance", 83, Rarity.RARE, mage.cards.m.MesmericTrance.class)); - cards.add(new SetCardInfo("Meteor Shower", 202, Rarity.COMMON, mage.cards.m.MeteorShower.class)); - cards.add(new SetCardInfo("Mind Ravel", 147, Rarity.COMMON, mage.cards.m.MindRavel.class)); - cards.add(new SetCardInfo("Mind Warp", 148, Rarity.UNCOMMON, mage.cards.m.MindWarp.class)); - cards.add(new SetCardInfo("Mind Whip", 149, Rarity.RARE, mage.cards.m.MindWhip.class)); - cards.add(new SetCardInfo("Minion of Leshrac", 150, Rarity.RARE, mage.cards.m.MinionOfLeshrac.class)); - cards.add(new SetCardInfo("Minion of Tevesh Szat", 151, Rarity.RARE, mage.cards.m.MinionOfTeveshSzat.class)); - cards.add(new SetCardInfo("Mistfolk", 84, Rarity.COMMON, mage.cards.m.Mistfolk.class)); - cards.add(new SetCardInfo("Mole Worms", 152, Rarity.UNCOMMON, mage.cards.m.MoleWorms.class)); - cards.add(new SetCardInfo("Monsoon", 298, Rarity.RARE, mage.cards.m.Monsoon.class)); - cards.add(new SetCardInfo("Moor Fiend", 153, Rarity.COMMON, mage.cards.m.MoorFiend.class)); - cards.add(new SetCardInfo("Mountain Goat", 203, Rarity.COMMON, mage.cards.m.MountainGoat.class)); - cards.add(new SetCardInfo("Mountain Titan", 299, Rarity.RARE, mage.cards.m.MountainTitan.class)); - cards.add(new SetCardInfo("Mountain", 376, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 377, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 378, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mudslide", 204, Rarity.RARE, mage.cards.m.Mudslide.class)); - cards.add(new SetCardInfo("Musician", 85, Rarity.RARE, mage.cards.m.Musician.class)); - cards.add(new SetCardInfo("Mystic Might", 86, Rarity.RARE, mage.cards.m.MysticMight.class)); - cards.add(new SetCardInfo("Mystic Remora", 87, Rarity.COMMON, mage.cards.m.MysticRemora.class)); - cards.add(new SetCardInfo("Nacre Talisman", 329, Rarity.UNCOMMON, mage.cards.n.NacreTalisman.class)); - cards.add(new SetCardInfo("Naked Singularity", 330, Rarity.RARE, mage.cards.n.NakedSingularity.class)); - cards.add(new SetCardInfo("Nature's Lore", 255, Rarity.UNCOMMON, mage.cards.n.NaturesLore.class)); - cards.add(new SetCardInfo("Necropotence", 154, Rarity.RARE, mage.cards.n.Necropotence.class)); - cards.add(new SetCardInfo("Norritt", 155, Rarity.COMMON, mage.cards.n.Norritt.class)); - cards.add(new SetCardInfo("Oath of Lim-Dul", 156, Rarity.RARE, mage.cards.o.OathOfLimDul.class)); - cards.add(new SetCardInfo("Onyx Talisman", 331, Rarity.UNCOMMON, mage.cards.o.OnyxTalisman.class)); - cards.add(new SetCardInfo("Orcish Cannoneers", 205, Rarity.UNCOMMON, mage.cards.o.OrcishCannoneers.class)); - cards.add(new SetCardInfo("Orcish Healer", 208, Rarity.UNCOMMON, mage.cards.o.OrcishHealer.class)); - cards.add(new SetCardInfo("Orcish Librarian", 209, Rarity.RARE, mage.cards.o.OrcishLibrarian.class)); - cards.add(new SetCardInfo("Orcish Lumberjack", 210, Rarity.COMMON, mage.cards.o.OrcishLumberjack.class)); - cards.add(new SetCardInfo("Orcish Squatters", 211, Rarity.RARE, mage.cards.o.OrcishSquatters.class)); - cards.add(new SetCardInfo("Order of the Sacred Torch", 45, Rarity.RARE, mage.cards.o.OrderOfTheSacredTorch.class)); - cards.add(new SetCardInfo("Order of the White Shield", 46, Rarity.UNCOMMON, mage.cards.o.OrderOfTheWhiteShield.class)); - cards.add(new SetCardInfo("Pale Bears", 256, Rarity.RARE, mage.cards.p.PaleBears.class)); - cards.add(new SetCardInfo("Panic", 212, Rarity.COMMON, mage.cards.p.Panic.class)); - cards.add(new SetCardInfo("Pentagram of the Ages", 332, Rarity.RARE, mage.cards.p.PentagramOfTheAges.class)); - cards.add(new SetCardInfo("Pestilence Rats", 157, Rarity.COMMON, mage.cards.p.PestilenceRats.class)); - cards.add(new SetCardInfo("Phantasmal Mount", 88, Rarity.UNCOMMON, mage.cards.p.PhantasmalMount.class)); - cards.add(new SetCardInfo("Pit Trap", 333, Rarity.UNCOMMON, mage.cards.p.PitTrap.class)); - cards.add(new SetCardInfo("Plains", 364, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 365, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 366, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Polar Kraken", 89, Rarity.RARE, mage.cards.p.PolarKraken.class)); - cards.add(new SetCardInfo("Portent", 90, Rarity.COMMON, mage.cards.p.Portent.class)); - cards.add(new SetCardInfo("Power Sink", 91, Rarity.COMMON, mage.cards.p.PowerSink.class)); - cards.add(new SetCardInfo("Pox", 158, Rarity.RARE, mage.cards.p.Pox.class)); - cards.add(new SetCardInfo("Prismatic Ward", 47, Rarity.COMMON, mage.cards.p.PrismaticWard.class)); - cards.add(new SetCardInfo("Pygmy Allosaurus", 257, Rarity.RARE, mage.cards.p.PygmyAllosaurus.class)); - cards.add(new SetCardInfo("Pyknite", 258, Rarity.COMMON, mage.cards.p.Pyknite.class)); - cards.add(new SetCardInfo("Pyroblast", 213, Rarity.COMMON, mage.cards.p.Pyroblast.class)); - cards.add(new SetCardInfo("Pyroclasm", 214, Rarity.UNCOMMON, mage.cards.p.Pyroclasm.class)); - cards.add(new SetCardInfo("Rally", 48, Rarity.COMMON, mage.cards.r.Rally.class)); - cards.add(new SetCardInfo("Ray of Command", 92, Rarity.COMMON, mage.cards.r.RayOfCommand.class)); - cards.add(new SetCardInfo("Ray of Erasure", 93, Rarity.COMMON, mage.cards.r.RayOfErasure.class)); - cards.add(new SetCardInfo("Reality Twist", 94, Rarity.RARE, mage.cards.r.RealityTwist.class)); - cards.add(new SetCardInfo("Reclamation", 300, Rarity.RARE, mage.cards.r.Reclamation.class)); - cards.add(new SetCardInfo("Red Scarab", 49, Rarity.UNCOMMON, mage.cards.r.RedScarab.class)); - cards.add(new SetCardInfo("Regeneration", 259, Rarity.COMMON, mage.cards.r.Regeneration.class)); - cards.add(new SetCardInfo("Rime Dryad", 260, Rarity.COMMON, mage.cards.r.RimeDryad.class)); - cards.add(new SetCardInfo("Ritual of Subdual", 261, Rarity.RARE, mage.cards.r.RitualOfSubdual.class)); - cards.add(new SetCardInfo("River Delta", 359, Rarity.RARE, mage.cards.r.RiverDelta.class)); - cards.add(new SetCardInfo("Runed Arch", 334, Rarity.RARE, mage.cards.r.RunedArch.class)); - cards.add(new SetCardInfo("Sabretooth Tiger", 215, Rarity.COMMON, mage.cards.s.SabretoothTiger.class)); - cards.add(new SetCardInfo("Scaled Wurm", 262, Rarity.COMMON, mage.cards.s.ScaledWurm.class)); - cards.add(new SetCardInfo("Sea Spirit", 95, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); - cards.add(new SetCardInfo("Seizures", 159, Rarity.COMMON, mage.cards.s.Seizures.class)); - cards.add(new SetCardInfo("Seraph", 51, Rarity.RARE, mage.cards.s.Seraph.class)); - cards.add(new SetCardInfo("Shambling Strider", 263, Rarity.COMMON, mage.cards.s.ShamblingStrider.class)); - cards.add(new SetCardInfo("Shatter", 216, Rarity.COMMON, mage.cards.s.Shatter.class)); - cards.add(new SetCardInfo("Shield Bearer", 52, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); - cards.add(new SetCardInfo("Shield of the Ages", 335, Rarity.UNCOMMON, mage.cards.s.ShieldOfTheAges.class)); - cards.add(new SetCardInfo("Shyft", 96, Rarity.RARE, mage.cards.s.Shyft.class)); - cards.add(new SetCardInfo("Sibilant Spirit", 97, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); - cards.add(new SetCardInfo("Silver Erne", 98, Rarity.UNCOMMON, mage.cards.s.SilverErne.class)); - cards.add(new SetCardInfo("Skeleton Ship", 301, Rarity.RARE, mage.cards.s.SkeletonShip.class)); - cards.add(new SetCardInfo("Skull Catapult", 336, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); - cards.add(new SetCardInfo("Snow Devil", 100, Rarity.COMMON, mage.cards.s.SnowDevil.class)); - cards.add(new SetCardInfo("Snow Fortress", 337, Rarity.RARE, mage.cards.s.SnowFortress.class)); - cards.add(new SetCardInfo("Snow Hound", 53, Rarity.UNCOMMON, mage.cards.s.SnowHound.class)); - cards.add(new SetCardInfo("Snow-Covered Forest", 383, Rarity.LAND, mage.cards.s.SnowCoveredForest.class)); - cards.add(new SetCardInfo("Snow-Covered Island", 371, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class)); - cards.add(new SetCardInfo("Snow-Covered Mountain", 379, Rarity.LAND, mage.cards.s.SnowCoveredMountain.class)); - cards.add(new SetCardInfo("Snow-Covered Plains", 367, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class)); - cards.add(new SetCardInfo("Snow-Covered Swamp", 372, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class)); - cards.add(new SetCardInfo("Soldevi Golem", 338, Rarity.RARE, mage.cards.s.SoldeviGolem.class)); - cards.add(new SetCardInfo("Soldevi Machinist", 102, Rarity.UNCOMMON, mage.cards.s.SoldeviMachinist.class)); - cards.add(new SetCardInfo("Soldevi Simulacrum", 339, Rarity.UNCOMMON, mage.cards.s.SoldeviSimulacrum.class)); - cards.add(new SetCardInfo("Songs of the Damned", 160, Rarity.COMMON, mage.cards.s.SongsOfTheDamned.class)); - cards.add(new SetCardInfo("Soul Barrier", 103, Rarity.UNCOMMON, mage.cards.s.SoulBarrier.class)); - cards.add(new SetCardInfo("Soul Burn", 161, Rarity.COMMON, mage.cards.s.SoulBurn.class)); - cards.add(new SetCardInfo("Soul Kiss", 162, Rarity.COMMON, mage.cards.s.SoulKiss.class)); - cards.add(new SetCardInfo("Spoils of Evil", 163, Rarity.RARE, mage.cards.s.SpoilsOfEvil.class)); - cards.add(new SetCardInfo("Staff of the Ages", 340, Rarity.RARE, mage.cards.s.StaffOfTheAges.class)); - cards.add(new SetCardInfo("Stampede", 265, Rarity.RARE, mage.cards.s.Stampede.class)); - cards.add(new SetCardInfo("Stench of Evil", 165, Rarity.UNCOMMON, mage.cards.s.StenchOfEvil.class)); - cards.add(new SetCardInfo("Stone Rain", 217, Rarity.COMMON, mage.cards.s.StoneRain.class)); - cards.add(new SetCardInfo("Stone Spirit", 218, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); - cards.add(new SetCardInfo("Stonehands", 219, Rarity.COMMON, mage.cards.s.Stonehands.class)); - cards.add(new SetCardInfo("Storm Spirit", 303, Rarity.RARE, mage.cards.s.StormSpirit.class)); - cards.add(new SetCardInfo("Stormbind", 304, Rarity.RARE, mage.cards.s.Stormbind.class)); - cards.add(new SetCardInfo("Stromgald Cabal", 166, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); - cards.add(new SetCardInfo("Stunted Growth", 266, Rarity.RARE, mage.cards.s.StuntedGrowth.class)); - cards.add(new SetCardInfo("Sulfurous Springs", 360, Rarity.RARE, mage.cards.s.SulfurousSprings.class)); - cards.add(new SetCardInfo("Sunstone", 341, Rarity.UNCOMMON, mage.cards.s.Sunstone.class)); - cards.add(new SetCardInfo("Swamp", 373, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 374, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 375, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swords to Plowshares", 54, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); - cards.add(new SetCardInfo("Tarpan", 267, Rarity.COMMON, mage.cards.t.Tarpan.class)); - cards.add(new SetCardInfo("Thermokarst", 268, Rarity.UNCOMMON, mage.cards.t.Thermokarst.class)); - cards.add(new SetCardInfo("Thoughtleech", 269, Rarity.UNCOMMON, mage.cards.t.Thoughtleech.class)); - cards.add(new SetCardInfo("Thunder Wall", 104, Rarity.UNCOMMON, mage.cards.t.ThunderWall.class)); - cards.add(new SetCardInfo("Timberline Ridge", 361, Rarity.RARE, mage.cards.t.TimberlineRidge.class)); - cards.add(new SetCardInfo("Time Bomb", 342, Rarity.RARE, mage.cards.t.TimeBomb.class)); - cards.add(new SetCardInfo("Tinder Wall", 270, Rarity.COMMON, mage.cards.t.TinderWall.class)); - cards.add(new SetCardInfo("Tor Giant", 220, Rarity.COMMON, mage.cards.t.TorGiant.class)); - cards.add(new SetCardInfo("Total War", 221, Rarity.RARE, mage.cards.t.TotalWar.class)); - cards.add(new SetCardInfo("Touch of Death", 167, Rarity.COMMON, mage.cards.t.TouchOfDeath.class)); - cards.add(new SetCardInfo("Trailblazer", 272, Rarity.RARE, mage.cards.t.Trailblazer.class)); - cards.add(new SetCardInfo("Underground River", 362, Rarity.RARE, mage.cards.u.UndergroundRiver.class)); - cards.add(new SetCardInfo("Updraft", 105, Rarity.UNCOMMON, mage.cards.u.Updraft.class)); - cards.add(new SetCardInfo("Urza's Bauble", 343, Rarity.UNCOMMON, mage.cards.u.UrzasBauble.class)); - cards.add(new SetCardInfo("Veldt", 363, Rarity.RARE, mage.cards.v.Veldt.class)); - cards.add(new SetCardInfo("Venomous Breath", 273, Rarity.UNCOMMON, mage.cards.v.VenomousBreath.class)); - cards.add(new SetCardInfo("Vertigo", 222, Rarity.UNCOMMON, mage.cards.v.Vertigo.class)); - cards.add(new SetCardInfo("Vexing Arcanix", 344, Rarity.RARE, mage.cards.v.VexingArcanix.class)); - cards.add(new SetCardInfo("Vibrating Sphere", 345, Rarity.RARE, mage.cards.v.VibratingSphere.class)); - cards.add(new SetCardInfo("Walking Wall", 346, Rarity.UNCOMMON, mage.cards.w.WalkingWall.class)); - cards.add(new SetCardInfo("Wall of Lava", 223, Rarity.UNCOMMON, mage.cards.w.WallOfLava.class)); - cards.add(new SetCardInfo("Wall of Pine Needles", 274, Rarity.UNCOMMON, mage.cards.w.WallOfPineNeedles.class)); - cards.add(new SetCardInfo("Wall of Shields", 347, Rarity.UNCOMMON, mage.cards.w.WallOfShields.class)); - cards.add(new SetCardInfo("War Chariot", 348, Rarity.UNCOMMON, mage.cards.w.WarChariot.class)); - cards.add(new SetCardInfo("Warning", 55, Rarity.COMMON, mage.cards.w.Warning.class)); - cards.add(new SetCardInfo("Whalebone Glider", 349, Rarity.UNCOMMON, mage.cards.w.WhaleboneGlider.class)); - cards.add(new SetCardInfo("White Scarab", 56, Rarity.UNCOMMON, mage.cards.w.WhiteScarab.class)); - cards.add(new SetCardInfo("Whiteout", 275, Rarity.UNCOMMON, mage.cards.w.Whiteout.class)); - cards.add(new SetCardInfo("Wiitigo", 276, Rarity.RARE, mage.cards.w.Wiitigo.class)); - cards.add(new SetCardInfo("Wild Growth", 277, Rarity.COMMON, mage.cards.w.WildGrowth.class)); - cards.add(new SetCardInfo("Wind Spirit", 106, Rarity.UNCOMMON, mage.cards.w.WindSpirit.class)); - cards.add(new SetCardInfo("Wings of Aesthir", 305, Rarity.UNCOMMON, mage.cards.w.WingsOfAesthir.class)); - cards.add(new SetCardInfo("Withering Wisps", 168, Rarity.UNCOMMON, mage.cards.w.WitheringWisps.class)); - cards.add(new SetCardInfo("Woolly Mammoths", 278, Rarity.COMMON, mage.cards.w.WoollyMammoths.class)); - cards.add(new SetCardInfo("Woolly Spider", 279, Rarity.COMMON, mage.cards.w.WoollySpider.class)); - cards.add(new SetCardInfo("Word of Blasting", 224, Rarity.UNCOMMON, mage.cards.w.WordOfBlasting.class)); - cards.add(new SetCardInfo("Word of Undoing", 108, Rarity.COMMON, mage.cards.w.WordOfUndoing.class)); - cards.add(new SetCardInfo("Wrath of Marit Lage", 109, Rarity.RARE, mage.cards.w.WrathOfMaritLage.class)); - cards.add(new SetCardInfo("Yavimaya Gnats", 280, Rarity.UNCOMMON, mage.cards.y.YavimayaGnats.class)); - cards.add(new SetCardInfo("Zur's Weirding", 110, Rarity.RARE, mage.cards.z.ZursWeirding.class)); - cards.add(new SetCardInfo("Zuran Enchanter", 111, Rarity.COMMON, mage.cards.z.ZuranEnchanter.class)); - cards.add(new SetCardInfo("Zuran Orb", 350, Rarity.UNCOMMON, mage.cards.z.ZuranOrb.class)); - cards.add(new SetCardInfo("Zuran Spellcaster", 112, Rarity.COMMON, mage.cards.z.ZuranSpellcaster.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author North + */ +public final class IceAge extends ExpansionSet { + + private static final IceAge instance = new IceAge(); + + public static IceAge getInstance() { + return instance; + } + + private IceAge() { + super("Ice Age", "ICE", ExpansionSet.buildDate(1995, 5, 1), SetType.EXPANSION); + this.blockName = "Ice Age"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + + cards.add(new SetCardInfo("Abyssal Specter", 113, Rarity.UNCOMMON, mage.cards.a.AbyssalSpecter.class)); + cards.add(new SetCardInfo("Adarkar Sentinel", 306, Rarity.UNCOMMON, mage.cards.a.AdarkarSentinel.class)); + cards.add(new SetCardInfo("Adarkar Wastes", 351, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); + cards.add(new SetCardInfo("Aegis of the Meek", 307, Rarity.RARE, mage.cards.a.AegisOfTheMeek.class)); + cards.add(new SetCardInfo("Aggression", 169, Rarity.UNCOMMON, mage.cards.a.Aggression.class)); + cards.add(new SetCardInfo("Altar of Bone", 281, Rarity.RARE, mage.cards.a.AltarOfBone.class)); + cards.add(new SetCardInfo("Anarchy", 170, Rarity.UNCOMMON, mage.cards.a.Anarchy.class)); + cards.add(new SetCardInfo("Arenson's Aura", 3, Rarity.COMMON, mage.cards.a.ArensonsAura.class)); + cards.add(new SetCardInfo("Armor of Faith", 4, Rarity.COMMON, mage.cards.a.ArmorOfFaith.class)); + cards.add(new SetCardInfo("Arnjlot's Ascent", 57, Rarity.COMMON, mage.cards.a.ArnjlotsAscent.class)); + cards.add(new SetCardInfo("Ashen Ghoul", 114, Rarity.UNCOMMON, mage.cards.a.AshenGhoul.class)); + cards.add(new SetCardInfo("Aurochs", 225, Rarity.COMMON, mage.cards.a.Aurochs.class)); + cards.add(new SetCardInfo("Avalanche", 171, Rarity.UNCOMMON, mage.cards.a.Avalanche.class)); + cards.add(new SetCardInfo("Balduvian Barbarians", 172, Rarity.COMMON, mage.cards.b.BalduvianBarbarians.class)); + cards.add(new SetCardInfo("Balduvian Bears", 226, Rarity.COMMON, mage.cards.b.BalduvianBears.class)); + cards.add(new SetCardInfo("Balduvian Conjurer", 58, Rarity.UNCOMMON, mage.cards.b.BalduvianConjurer.class)); + cards.add(new SetCardInfo("Balduvian Hydra", 173, Rarity.RARE, mage.cards.b.BalduvianHydra.class)); + cards.add(new SetCardInfo("Barbed Sextant", 312, Rarity.COMMON, mage.cards.b.BarbedSextant.class)); + cards.add(new SetCardInfo("Baton of Morale", 313, Rarity.UNCOMMON, mage.cards.b.BatonOfMorale.class)); + cards.add(new SetCardInfo("Battle Cry", 5, Rarity.UNCOMMON, mage.cards.b.BattleCry.class)); + cards.add(new SetCardInfo("Battle Frenzy", 175, Rarity.COMMON, mage.cards.b.BattleFrenzy.class)); + cards.add(new SetCardInfo("Binding Grasp", 60, Rarity.UNCOMMON, mage.cards.b.BindingGrasp.class)); + cards.add(new SetCardInfo("Black Scarab", 6, Rarity.UNCOMMON, mage.cards.b.BlackScarab.class)); + cards.add(new SetCardInfo("Blessed Wine", 7, Rarity.COMMON, mage.cards.b.BlessedWine.class)); + cards.add(new SetCardInfo("Blinking Spirit", 8, Rarity.RARE, mage.cards.b.BlinkingSpirit.class)); + cards.add(new SetCardInfo("Blizzard", 227, Rarity.RARE, mage.cards.b.Blizzard.class)); + cards.add(new SetCardInfo("Blue Scarab", 9, Rarity.UNCOMMON, mage.cards.b.BlueScarab.class)); + cards.add(new SetCardInfo("Brainstorm", 61, Rarity.COMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brand of Ill Omen", 177, Rarity.RARE, mage.cards.b.BrandOfIllOmen.class)); + cards.add(new SetCardInfo("Breath of Dreams", 62, Rarity.UNCOMMON, mage.cards.b.BreathOfDreams.class)); + cards.add(new SetCardInfo("Brine Shaman", 115, Rarity.COMMON, mage.cards.b.BrineShaman.class)); + cards.add(new SetCardInfo("Brown Ouphe", 228, Rarity.COMMON, mage.cards.b.BrownOuphe.class)); + cards.add(new SetCardInfo("Brushland", 352, Rarity.RARE, mage.cards.b.Brushland.class)); + cards.add(new SetCardInfo("Burnt Offering", 116, Rarity.COMMON, mage.cards.b.BurntOffering.class)); + cards.add(new SetCardInfo("Call to Arms", 10, Rarity.RARE, mage.cards.c.CallToArms.class)); + cards.add(new SetCardInfo("Caribou Range", 11, Rarity.RARE, mage.cards.c.CaribouRange.class)); + cards.add(new SetCardInfo("Celestial Sword", 314, Rarity.RARE, mage.cards.c.CelestialSword.class)); + cards.add(new SetCardInfo("Centaur Archer", 282, Rarity.UNCOMMON, mage.cards.c.CentaurArcher.class)); + cards.add(new SetCardInfo("Chaos Lord", 178, Rarity.RARE, mage.cards.c.ChaosLord.class)); + cards.add(new SetCardInfo("Chaos Moon", 179, Rarity.RARE, mage.cards.c.ChaosMoon.class)); + cards.add(new SetCardInfo("Chub Toad", 229, Rarity.COMMON, mage.cards.c.ChubToad.class)); + cards.add(new SetCardInfo("Circle of Protection: Black", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); + cards.add(new SetCardInfo("Circle of Protection: Blue", 13, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); + cards.add(new SetCardInfo("Circle of Protection: Green", 14, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); + cards.add(new SetCardInfo("Circle of Protection: Red", 15, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); + cards.add(new SetCardInfo("Circle of Protection: White", 16, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); + cards.add(new SetCardInfo("Clairvoyance", 63, Rarity.COMMON, mage.cards.c.Clairvoyance.class)); + cards.add(new SetCardInfo("Cloak of Confusion", 117, Rarity.COMMON, mage.cards.c.CloakOfConfusion.class)); + cards.add(new SetCardInfo("Cold Snap", 17, Rarity.UNCOMMON, mage.cards.c.ColdSnap.class)); + cards.add(new SetCardInfo("Conquer", 180, Rarity.UNCOMMON, mage.cards.c.Conquer.class)); + cards.add(new SetCardInfo("Cooperation", 18, Rarity.COMMON, mage.cards.c.Cooperation.class)); + cards.add(new SetCardInfo("Counterspell", 64, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Crown of the Ages", 315, Rarity.RARE, mage.cards.c.CrownOfTheAges.class)); + cards.add(new SetCardInfo("Curse of Marit Lage", 181, Rarity.RARE, mage.cards.c.CurseOfMaritLage.class)); + cards.add(new SetCardInfo("Dance of the Dead", 118, Rarity.UNCOMMON, mage.cards.d.DanceOfTheDead.class)); + cards.add(new SetCardInfo("Dark Banishing", 119, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); + cards.add(new SetCardInfo("Dark Ritual", 120, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Death Ward", 19, Rarity.COMMON, mage.cards.d.DeathWard.class)); + cards.add(new SetCardInfo("Deflection", 65, Rarity.RARE, mage.cards.d.Deflection.class)); + cards.add(new SetCardInfo("Demonic Consultation", 121, Rarity.UNCOMMON, mage.cards.d.DemonicConsultation.class)); + cards.add(new SetCardInfo("Despotic Scepter", 316, Rarity.RARE, mage.cards.d.DespoticScepter.class)); + cards.add(new SetCardInfo("Diabolic Vision", 284, Rarity.UNCOMMON, mage.cards.d.DiabolicVision.class)); + cards.add(new SetCardInfo("Dire Wolves", 230, Rarity.COMMON, mage.cards.d.DireWolves.class)); + cards.add(new SetCardInfo("Disenchant", 20, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Dread Wight", 122, Rarity.RARE, mage.cards.d.DreadWight.class)); + cards.add(new SetCardInfo("Dreams of the Dead", 66, Rarity.UNCOMMON, mage.cards.d.DreamsOfTheDead.class)); + cards.add(new SetCardInfo("Drift of the Dead", 123, Rarity.UNCOMMON, mage.cards.d.DriftOfTheDead.class)); + cards.add(new SetCardInfo("Drought", 21, Rarity.UNCOMMON, mage.cards.d.Drought.class)); + cards.add(new SetCardInfo("Dwarven Armory", 182, Rarity.RARE, mage.cards.d.DwarvenArmory.class)); + cards.add(new SetCardInfo("Earthlink", 285, Rarity.RARE, mage.cards.e.Earthlink.class)); + cards.add(new SetCardInfo("Earthlore", 231, Rarity.COMMON, mage.cards.e.Earthlore.class)); + cards.add(new SetCardInfo("Elder Druid", 232, Rarity.RARE, mage.cards.e.ElderDruid.class)); + cards.add(new SetCardInfo("Elemental Augury", 286, Rarity.RARE, mage.cards.e.ElementalAugury.class)); + cards.add(new SetCardInfo("Elkin Bottle", 317, Rarity.RARE, mage.cards.e.ElkinBottle.class)); + cards.add(new SetCardInfo("Enduring Renewal", 23, Rarity.RARE, mage.cards.e.EnduringRenewal.class)); + cards.add(new SetCardInfo("Energy Storm", 24, Rarity.RARE, mage.cards.e.EnergyStorm.class)); + cards.add(new SetCardInfo("Enervate", 67, Rarity.COMMON, mage.cards.e.Enervate.class)); + cards.add(new SetCardInfo("Errant Minion", 68, Rarity.COMMON, mage.cards.e.ErrantMinion.class)); + cards.add(new SetCardInfo("Errantry", 183, Rarity.COMMON, mage.cards.e.Errantry.class)); + cards.add(new SetCardInfo("Essence Filter", 233, Rarity.COMMON, mage.cards.e.EssenceFilter.class)); + cards.add(new SetCardInfo("Essence Flare", 69, Rarity.COMMON, mage.cards.e.EssenceFlare.class)); + cards.add(new SetCardInfo("Fanatical Fever", 234, Rarity.UNCOMMON, mage.cards.f.FanaticalFever.class)); + cards.add(new SetCardInfo("Fear", 124, Rarity.COMMON, mage.cards.f.Fear.class)); + cards.add(new SetCardInfo("Fiery Justice", 288, Rarity.RARE, mage.cards.f.FieryJustice.class)); + cards.add(new SetCardInfo("Fire Covenant", 289, Rarity.UNCOMMON, mage.cards.f.FireCovenant.class)); + cards.add(new SetCardInfo("Flame Spirit", 184, Rarity.UNCOMMON, mage.cards.f.FlameSpirit.class)); + cards.add(new SetCardInfo("Flare", 185, Rarity.COMMON, mage.cards.f.Flare.class)); + cards.add(new SetCardInfo("Flooded Woodlands", 290, Rarity.RARE, mage.cards.f.FloodedWoodlands.class)); + cards.add(new SetCardInfo("Flow of Maggots", 125, Rarity.RARE, mage.cards.f.FlowOfMaggots.class)); + cards.add(new SetCardInfo("Folk of the Pines", 235, Rarity.COMMON, mage.cards.f.FolkOfThePines.class)); + cards.add(new SetCardInfo("Forbidden Lore", 236, Rarity.RARE, mage.cards.f.ForbiddenLore.class)); + cards.add(new SetCardInfo("Force Void", 70, Rarity.UNCOMMON, mage.cards.f.ForceVoid.class)); + cards.add(new SetCardInfo("Forest", 380, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 381, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 382, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forgotten Lore", 237, Rarity.UNCOMMON, mage.cards.f.ForgottenLore.class)); + cards.add(new SetCardInfo("Formation", 25, Rarity.RARE, mage.cards.f.Formation.class)); + cards.add(new SetCardInfo("Foul Familiar", 126, Rarity.COMMON, mage.cards.f.FoulFamiliar.class)); + cards.add(new SetCardInfo("Foxfire", 238, Rarity.COMMON, mage.cards.f.Foxfire.class)); + cards.add(new SetCardInfo("Freyalise Supplicant", 239, Rarity.UNCOMMON, mage.cards.f.FreyaliseSupplicant.class)); + cards.add(new SetCardInfo("Freyalise's Charm", 240, Rarity.UNCOMMON, mage.cards.f.FreyalisesCharm.class)); + cards.add(new SetCardInfo("Freyalise's Winds", 241, Rarity.RARE, mage.cards.f.FreyalisesWinds.class)); + cards.add(new SetCardInfo("Fumarole", 291, Rarity.UNCOMMON, mage.cards.f.Fumarole.class)); + cards.add(new SetCardInfo("Fylgja", 26, Rarity.COMMON, mage.cards.f.Fylgja.class)); + cards.add(new SetCardInfo("Fyndhorn Bow", 318, Rarity.UNCOMMON, mage.cards.f.FyndhornBow.class)); + cards.add(new SetCardInfo("Fyndhorn Brownie", 242, Rarity.COMMON, mage.cards.f.FyndhornBrownie.class)); + cards.add(new SetCardInfo("Fyndhorn Elder", 243, Rarity.UNCOMMON, mage.cards.f.FyndhornElder.class)); + cards.add(new SetCardInfo("Fyndhorn Elves", 244, Rarity.COMMON, mage.cards.f.FyndhornElves.class)); + cards.add(new SetCardInfo("Fyndhorn Pollen", 245, Rarity.RARE, mage.cards.f.FyndhornPollen.class)); + cards.add(new SetCardInfo("Game of Chaos", 186, Rarity.RARE, mage.cards.g.GameOfChaos.class)); + cards.add(new SetCardInfo("Gangrenous Zombies", 127, Rarity.COMMON, mage.cards.g.GangrenousZombies.class)); + cards.add(new SetCardInfo("Gaze of Pain", 128, Rarity.COMMON, mage.cards.g.GazeOfPain.class)); + cards.add(new SetCardInfo("General Jarkeld", 27, Rarity.RARE, mage.cards.g.GeneralJarkeld.class)); + cards.add(new SetCardInfo("Giant Growth", 246, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Giant Trap Door Spider", 293, Rarity.UNCOMMON, mage.cards.g.GiantTrapDoorSpider.class)); + cards.add(new SetCardInfo("Glacial Chasm", 353, Rarity.UNCOMMON, mage.cards.g.GlacialChasm.class)); + cards.add(new SetCardInfo("Glacial Crevasses", 187, Rarity.RARE, mage.cards.g.GlacialCrevasses.class)); + cards.add(new SetCardInfo("Glacial Wall", 71, Rarity.UNCOMMON, mage.cards.g.GlacialWall.class)); + cards.add(new SetCardInfo("Glaciers", 294, Rarity.RARE, mage.cards.g.Glaciers.class)); + cards.add(new SetCardInfo("Goblin Lyre", 319, Rarity.RARE, mage.cards.g.GoblinLyre.class)); + cards.add(new SetCardInfo("Goblin Mutant", 188, Rarity.UNCOMMON, mage.cards.g.GoblinMutant.class)); + cards.add(new SetCardInfo("Goblin Snowman", 191, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); + cards.add(new SetCardInfo("Gorilla Pack", 247, Rarity.COMMON, mage.cards.g.GorillaPack.class)); + cards.add(new SetCardInfo("Gravebind", 129, Rarity.RARE, mage.cards.g.Gravebind.class)); + cards.add(new SetCardInfo("Green Scarab", 28, Rarity.UNCOMMON, mage.cards.g.GreenScarab.class)); + cards.add(new SetCardInfo("Hallowed Ground", 29, Rarity.UNCOMMON, mage.cards.h.HallowedGround.class)); + cards.add(new SetCardInfo("Halls of Mist", 354, Rarity.RARE, mage.cards.h.HallsOfMist.class)); + cards.add(new SetCardInfo("Heal", 30, Rarity.COMMON, mage.cards.h.Heal.class)); + cards.add(new SetCardInfo("Hecatomb", 130, Rarity.RARE, mage.cards.h.Hecatomb.class)); + cards.add(new SetCardInfo("Hematite Talisman", 320, Rarity.UNCOMMON, mage.cards.h.HematiteTalisman.class)); + cards.add(new SetCardInfo("Hoar Shade", 131, Rarity.COMMON, mage.cards.h.HoarShade.class)); + cards.add(new SetCardInfo("Hot Springs", 248, Rarity.RARE, mage.cards.h.HotSprings.class)); + cards.add(new SetCardInfo("Howl from Beyond", 132, Rarity.COMMON, mage.cards.h.HowlFromBeyond.class)); + cards.add(new SetCardInfo("Hurricane", 249, Rarity.UNCOMMON, mage.cards.h.Hurricane.class)); + cards.add(new SetCardInfo("Hyalopterous Lemure", 133, Rarity.UNCOMMON, mage.cards.h.HyalopterousLemure.class)); + cards.add(new SetCardInfo("Hydroblast", 72, Rarity.COMMON, mage.cards.h.Hydroblast.class)); + cards.add(new SetCardInfo("Hymn of Rebirth", 295, Rarity.UNCOMMON, mage.cards.h.HymnOfRebirth.class)); + cards.add(new SetCardInfo("Ice Cauldron", 321, Rarity.RARE, mage.cards.i.IceCauldron.class)); + cards.add(new SetCardInfo("Ice Floe", 355, Rarity.UNCOMMON, mage.cards.i.IceFloe.class)); + cards.add(new SetCardInfo("Iceberg", 73, Rarity.UNCOMMON, mage.cards.i.Iceberg.class)); + cards.add(new SetCardInfo("Icequake", 134, Rarity.UNCOMMON, mage.cards.i.Icequake.class)); + cards.add(new SetCardInfo("Icy Manipulator", 322, Rarity.UNCOMMON, mage.cards.i.IcyManipulator.class)); + cards.add(new SetCardInfo("Icy Prison", 74, Rarity.RARE, mage.cards.i.IcyPrison.class)); + cards.add(new SetCardInfo("Illusionary Forces", 75, Rarity.COMMON, mage.cards.i.IllusionaryForces.class)); + cards.add(new SetCardInfo("Illusionary Presence", 76, Rarity.RARE, mage.cards.i.IllusionaryPresence.class)); + cards.add(new SetCardInfo("Illusionary Terrain", 77, Rarity.UNCOMMON, mage.cards.i.IllusionaryTerrain.class)); + cards.add(new SetCardInfo("Illusionary Wall", 78, Rarity.COMMON, mage.cards.i.IllusionaryWall.class)); + cards.add(new SetCardInfo("Illusions of Grandeur", 79, Rarity.RARE, mage.cards.i.IllusionsOfGrandeur.class)); + cards.add(new SetCardInfo("Imposing Visage", 193, Rarity.COMMON, mage.cards.i.ImposingVisage.class)); + cards.add(new SetCardInfo("Incinerate", 194, Rarity.COMMON, mage.cards.i.Incinerate.class)); + cards.add(new SetCardInfo("Infernal Darkness", 135, Rarity.RARE, mage.cards.i.InfernalDarkness.class)); + cards.add(new SetCardInfo("Infernal Denizen", 136, Rarity.RARE, mage.cards.i.InfernalDenizen.class)); + cards.add(new SetCardInfo("Infinite Hourglass", 323, Rarity.RARE, mage.cards.i.InfiniteHourglass.class)); + cards.add(new SetCardInfo("Infuse", 80, Rarity.COMMON, mage.cards.i.Infuse.class)); + cards.add(new SetCardInfo("Island", 368, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 369, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 370, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jester's Cap", 324, Rarity.RARE, mage.cards.j.JestersCap.class)); + cards.add(new SetCardInfo("Jester's Mask", 325, Rarity.RARE, mage.cards.j.JestersMask.class)); + cards.add(new SetCardInfo("Jeweled Amulet", 326, Rarity.UNCOMMON, mage.cards.j.JeweledAmulet.class)); + cards.add(new SetCardInfo("Johtull Wurm", 250, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class)); + cards.add(new SetCardInfo("Jokulhaups", 195, Rarity.RARE, mage.cards.j.Jokulhaups.class)); + cards.add(new SetCardInfo("Juniper Order Druid", 251, Rarity.COMMON, mage.cards.j.JuniperOrderDruid.class)); + cards.add(new SetCardInfo("Justice", 32, Rarity.UNCOMMON, mage.cards.j.Justice.class)); + cards.add(new SetCardInfo("Karplusan Forest", 356, Rarity.RARE, mage.cards.k.KarplusanForest.class)); + cards.add(new SetCardInfo("Karplusan Giant", 196, Rarity.UNCOMMON, mage.cards.k.KarplusanGiant.class)); + cards.add(new SetCardInfo("Karplusan Yeti", 197, Rarity.RARE, mage.cards.k.KarplusanYeti.class)); + cards.add(new SetCardInfo("Kelsinko Ranger", 33, Rarity.COMMON, mage.cards.k.KelsinkoRanger.class)); + cards.add(new SetCardInfo("Kjeldoran Dead", 137, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); + cards.add(new SetCardInfo("Kjeldoran Frostbeast", 296, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class)); + cards.add(new SetCardInfo("Kjeldoran Knight", 36, Rarity.RARE, mage.cards.k.KjeldoranKnight.class)); + cards.add(new SetCardInfo("Kjeldoran Phalanx", 37, Rarity.RARE, mage.cards.k.KjeldoranPhalanx.class)); + cards.add(new SetCardInfo("Kjeldoran Royal Guard", 38, Rarity.RARE, mage.cards.k.KjeldoranRoyalGuard.class)); + cards.add(new SetCardInfo("Kjeldoran Skycaptain", 39, Rarity.UNCOMMON, mage.cards.k.KjeldoranSkycaptain.class)); + cards.add(new SetCardInfo("Kjeldoran Skyknight", 40, Rarity.COMMON, mage.cards.k.KjeldoranSkyknight.class)); + cards.add(new SetCardInfo("Kjeldoran Warrior", 41, Rarity.COMMON, mage.cards.k.KjeldoranWarrior.class)); + cards.add(new SetCardInfo("Knight of Stromgald", 138, Rarity.UNCOMMON, mage.cards.k.KnightOfStromgald.class)); + cards.add(new SetCardInfo("Krovikan Elementalist", 139, Rarity.UNCOMMON, mage.cards.k.KrovikanElementalist.class)); + cards.add(new SetCardInfo("Krovikan Fetish", 140, Rarity.COMMON, mage.cards.k.KrovikanFetish.class)); + cards.add(new SetCardInfo("Krovikan Sorcerer", 81, Rarity.COMMON, mage.cards.k.KrovikanSorcerer.class)); + cards.add(new SetCardInfo("Krovikan Vampire", 141, Rarity.UNCOMMON, mage.cards.k.KrovikanVampire.class)); + cards.add(new SetCardInfo("Land Cap", 357, Rarity.RARE, mage.cards.l.LandCap.class)); + cards.add(new SetCardInfo("Lapis Lazuli Talisman", 327, Rarity.UNCOMMON, mage.cards.l.LapisLazuliTalisman.class)); + cards.add(new SetCardInfo("Lava Tubes", 358, Rarity.RARE, mage.cards.l.LavaTubes.class)); + cards.add(new SetCardInfo("Legions of Lim-Dul", 142, Rarity.COMMON, mage.cards.l.LegionsOfLimDul.class)); + cards.add(new SetCardInfo("Leshrac's Rite", 143, Rarity.UNCOMMON, mage.cards.l.LeshracsRite.class)); + cards.add(new SetCardInfo("Leshrac's Sigil", 144, Rarity.UNCOMMON, mage.cards.l.LeshracsSigil.class)); + cards.add(new SetCardInfo("Lhurgoyf", 252, Rarity.RARE, mage.cards.l.Lhurgoyf.class)); + cards.add(new SetCardInfo("Lightning Blow", 42, Rarity.RARE, mage.cards.l.LightningBlow.class)); + cards.add(new SetCardInfo("Lim-Dul's Cohort", 145, Rarity.COMMON, mage.cards.l.LimDulsCohort.class)); + cards.add(new SetCardInfo("Lim-Dul's Hex", 146, Rarity.UNCOMMON, mage.cards.l.LimDulsHex.class)); + cards.add(new SetCardInfo("Lost Order of Jarkeld", 43, Rarity.RARE, mage.cards.l.LostOrderOfJarkeld.class)); + cards.add(new SetCardInfo("Lure", 253, Rarity.UNCOMMON, mage.cards.l.Lure.class)); + cards.add(new SetCardInfo("Magus of the Unseen", 82, Rarity.RARE, mage.cards.m.MagusOfTheUnseen.class)); + cards.add(new SetCardInfo("Malachite Talisman", 328, Rarity.UNCOMMON, mage.cards.m.MalachiteTalisman.class)); + cards.add(new SetCardInfo("Marton Stromgald", 199, Rarity.RARE, mage.cards.m.MartonStromgald.class)); + cards.add(new SetCardInfo("Melee", 200, Rarity.UNCOMMON, mage.cards.m.Melee.class)); + cards.add(new SetCardInfo("Melting", 201, Rarity.UNCOMMON, mage.cards.m.Melting.class)); + cards.add(new SetCardInfo("Merieke Ri Berit", 297, Rarity.RARE, mage.cards.m.MeriekeRiBerit.class)); + cards.add(new SetCardInfo("Mesmeric Trance", 83, Rarity.RARE, mage.cards.m.MesmericTrance.class)); + cards.add(new SetCardInfo("Meteor Shower", 202, Rarity.COMMON, mage.cards.m.MeteorShower.class)); + cards.add(new SetCardInfo("Mind Ravel", 147, Rarity.COMMON, mage.cards.m.MindRavel.class)); + cards.add(new SetCardInfo("Mind Warp", 148, Rarity.UNCOMMON, mage.cards.m.MindWarp.class)); + cards.add(new SetCardInfo("Mind Whip", 149, Rarity.RARE, mage.cards.m.MindWhip.class)); + cards.add(new SetCardInfo("Minion of Leshrac", 150, Rarity.RARE, mage.cards.m.MinionOfLeshrac.class)); + cards.add(new SetCardInfo("Minion of Tevesh Szat", 151, Rarity.RARE, mage.cards.m.MinionOfTeveshSzat.class)); + cards.add(new SetCardInfo("Mistfolk", 84, Rarity.COMMON, mage.cards.m.Mistfolk.class)); + cards.add(new SetCardInfo("Mole Worms", 152, Rarity.UNCOMMON, mage.cards.m.MoleWorms.class)); + cards.add(new SetCardInfo("Monsoon", 298, Rarity.RARE, mage.cards.m.Monsoon.class)); + cards.add(new SetCardInfo("Moor Fiend", 153, Rarity.COMMON, mage.cards.m.MoorFiend.class)); + cards.add(new SetCardInfo("Mountain Goat", 203, Rarity.COMMON, mage.cards.m.MountainGoat.class)); + cards.add(new SetCardInfo("Mountain Titan", 299, Rarity.RARE, mage.cards.m.MountainTitan.class)); + cards.add(new SetCardInfo("Mountain", 376, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 377, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 378, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mudslide", 204, Rarity.RARE, mage.cards.m.Mudslide.class)); + cards.add(new SetCardInfo("Musician", 85, Rarity.RARE, mage.cards.m.Musician.class)); + cards.add(new SetCardInfo("Mystic Might", 86, Rarity.RARE, mage.cards.m.MysticMight.class)); + cards.add(new SetCardInfo("Mystic Remora", 87, Rarity.COMMON, mage.cards.m.MysticRemora.class)); + cards.add(new SetCardInfo("Nacre Talisman", 329, Rarity.UNCOMMON, mage.cards.n.NacreTalisman.class)); + cards.add(new SetCardInfo("Naked Singularity", 330, Rarity.RARE, mage.cards.n.NakedSingularity.class)); + cards.add(new SetCardInfo("Nature's Lore", 255, Rarity.UNCOMMON, mage.cards.n.NaturesLore.class)); + cards.add(new SetCardInfo("Necropotence", 154, Rarity.RARE, mage.cards.n.Necropotence.class)); + cards.add(new SetCardInfo("Norritt", 155, Rarity.COMMON, mage.cards.n.Norritt.class)); + cards.add(new SetCardInfo("Oath of Lim-Dul", 156, Rarity.RARE, mage.cards.o.OathOfLimDul.class)); + cards.add(new SetCardInfo("Onyx Talisman", 331, Rarity.UNCOMMON, mage.cards.o.OnyxTalisman.class)); + cards.add(new SetCardInfo("Orcish Cannoneers", 205, Rarity.UNCOMMON, mage.cards.o.OrcishCannoneers.class)); + cards.add(new SetCardInfo("Orcish Healer", 208, Rarity.UNCOMMON, mage.cards.o.OrcishHealer.class)); + cards.add(new SetCardInfo("Orcish Librarian", 209, Rarity.RARE, mage.cards.o.OrcishLibrarian.class)); + cards.add(new SetCardInfo("Orcish Lumberjack", 210, Rarity.COMMON, mage.cards.o.OrcishLumberjack.class)); + cards.add(new SetCardInfo("Orcish Squatters", 211, Rarity.RARE, mage.cards.o.OrcishSquatters.class)); + cards.add(new SetCardInfo("Order of the Sacred Torch", 45, Rarity.RARE, mage.cards.o.OrderOfTheSacredTorch.class)); + cards.add(new SetCardInfo("Order of the White Shield", 46, Rarity.UNCOMMON, mage.cards.o.OrderOfTheWhiteShield.class)); + cards.add(new SetCardInfo("Pale Bears", 256, Rarity.RARE, mage.cards.p.PaleBears.class)); + cards.add(new SetCardInfo("Panic", 212, Rarity.COMMON, mage.cards.p.Panic.class)); + cards.add(new SetCardInfo("Pentagram of the Ages", 332, Rarity.RARE, mage.cards.p.PentagramOfTheAges.class)); + cards.add(new SetCardInfo("Pestilence Rats", 157, Rarity.COMMON, mage.cards.p.PestilenceRats.class)); + cards.add(new SetCardInfo("Phantasmal Mount", 88, Rarity.UNCOMMON, mage.cards.p.PhantasmalMount.class)); + cards.add(new SetCardInfo("Pit Trap", 333, Rarity.UNCOMMON, mage.cards.p.PitTrap.class)); + cards.add(new SetCardInfo("Plains", 364, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 365, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 366, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Polar Kraken", 89, Rarity.RARE, mage.cards.p.PolarKraken.class)); + cards.add(new SetCardInfo("Portent", 90, Rarity.COMMON, mage.cards.p.Portent.class)); + cards.add(new SetCardInfo("Power Sink", 91, Rarity.COMMON, mage.cards.p.PowerSink.class)); + cards.add(new SetCardInfo("Pox", 158, Rarity.RARE, mage.cards.p.Pox.class)); + cards.add(new SetCardInfo("Prismatic Ward", 47, Rarity.COMMON, mage.cards.p.PrismaticWard.class)); + cards.add(new SetCardInfo("Pygmy Allosaurus", 257, Rarity.RARE, mage.cards.p.PygmyAllosaurus.class)); + cards.add(new SetCardInfo("Pyknite", 258, Rarity.COMMON, mage.cards.p.Pyknite.class)); + cards.add(new SetCardInfo("Pyroblast", 213, Rarity.COMMON, mage.cards.p.Pyroblast.class)); + cards.add(new SetCardInfo("Pyroclasm", 214, Rarity.UNCOMMON, mage.cards.p.Pyroclasm.class)); + cards.add(new SetCardInfo("Rally", 48, Rarity.COMMON, mage.cards.r.Rally.class)); + cards.add(new SetCardInfo("Ray of Command", 92, Rarity.COMMON, mage.cards.r.RayOfCommand.class)); + cards.add(new SetCardInfo("Ray of Erasure", 93, Rarity.COMMON, mage.cards.r.RayOfErasure.class)); + cards.add(new SetCardInfo("Reality Twist", 94, Rarity.RARE, mage.cards.r.RealityTwist.class)); + cards.add(new SetCardInfo("Reclamation", 300, Rarity.RARE, mage.cards.r.Reclamation.class)); + cards.add(new SetCardInfo("Red Scarab", 49, Rarity.UNCOMMON, mage.cards.r.RedScarab.class)); + cards.add(new SetCardInfo("Regeneration", 259, Rarity.COMMON, mage.cards.r.Regeneration.class)); + cards.add(new SetCardInfo("Rime Dryad", 260, Rarity.COMMON, mage.cards.r.RimeDryad.class)); + cards.add(new SetCardInfo("Ritual of Subdual", 261, Rarity.RARE, mage.cards.r.RitualOfSubdual.class)); + cards.add(new SetCardInfo("River Delta", 359, Rarity.RARE, mage.cards.r.RiverDelta.class)); + cards.add(new SetCardInfo("Runed Arch", 334, Rarity.RARE, mage.cards.r.RunedArch.class)); + cards.add(new SetCardInfo("Sabretooth Tiger", 215, Rarity.COMMON, mage.cards.s.SabretoothTiger.class)); + cards.add(new SetCardInfo("Scaled Wurm", 262, Rarity.COMMON, mage.cards.s.ScaledWurm.class)); + cards.add(new SetCardInfo("Sea Spirit", 95, Rarity.UNCOMMON, mage.cards.s.SeaSpirit.class)); + cards.add(new SetCardInfo("Seizures", 159, Rarity.COMMON, mage.cards.s.Seizures.class)); + cards.add(new SetCardInfo("Seraph", 51, Rarity.RARE, mage.cards.s.Seraph.class)); + cards.add(new SetCardInfo("Shambling Strider", 263, Rarity.COMMON, mage.cards.s.ShamblingStrider.class)); + cards.add(new SetCardInfo("Shatter", 216, Rarity.COMMON, mage.cards.s.Shatter.class)); + cards.add(new SetCardInfo("Shield Bearer", 52, Rarity.COMMON, mage.cards.s.ShieldBearer.class)); + cards.add(new SetCardInfo("Shield of the Ages", 335, Rarity.UNCOMMON, mage.cards.s.ShieldOfTheAges.class)); + cards.add(new SetCardInfo("Shyft", 96, Rarity.RARE, mage.cards.s.Shyft.class)); + cards.add(new SetCardInfo("Sibilant Spirit", 97, Rarity.RARE, mage.cards.s.SibilantSpirit.class)); + cards.add(new SetCardInfo("Silver Erne", 98, Rarity.UNCOMMON, mage.cards.s.SilverErne.class)); + cards.add(new SetCardInfo("Skeleton Ship", 301, Rarity.RARE, mage.cards.s.SkeletonShip.class)); + cards.add(new SetCardInfo("Skull Catapult", 336, Rarity.UNCOMMON, mage.cards.s.SkullCatapult.class)); + cards.add(new SetCardInfo("Snow Devil", 100, Rarity.COMMON, mage.cards.s.SnowDevil.class)); + cards.add(new SetCardInfo("Snow Fortress", 337, Rarity.RARE, mage.cards.s.SnowFortress.class)); + cards.add(new SetCardInfo("Snow Hound", 53, Rarity.UNCOMMON, mage.cards.s.SnowHound.class)); + cards.add(new SetCardInfo("Snow-Covered Forest", 383, Rarity.LAND, mage.cards.s.SnowCoveredForest.class)); + cards.add(new SetCardInfo("Snow-Covered Island", 371, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class)); + cards.add(new SetCardInfo("Snow-Covered Mountain", 379, Rarity.LAND, mage.cards.s.SnowCoveredMountain.class)); + cards.add(new SetCardInfo("Snow-Covered Plains", 367, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class)); + cards.add(new SetCardInfo("Snow-Covered Swamp", 372, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class)); + cards.add(new SetCardInfo("Soldevi Golem", 338, Rarity.RARE, mage.cards.s.SoldeviGolem.class)); + cards.add(new SetCardInfo("Soldevi Machinist", 102, Rarity.UNCOMMON, mage.cards.s.SoldeviMachinist.class)); + cards.add(new SetCardInfo("Soldevi Simulacrum", 339, Rarity.UNCOMMON, mage.cards.s.SoldeviSimulacrum.class)); + cards.add(new SetCardInfo("Songs of the Damned", 160, Rarity.COMMON, mage.cards.s.SongsOfTheDamned.class)); + cards.add(new SetCardInfo("Soul Barrier", 103, Rarity.UNCOMMON, mage.cards.s.SoulBarrier.class)); + cards.add(new SetCardInfo("Soul Burn", 161, Rarity.COMMON, mage.cards.s.SoulBurn.class)); + cards.add(new SetCardInfo("Soul Kiss", 162, Rarity.COMMON, mage.cards.s.SoulKiss.class)); + cards.add(new SetCardInfo("Spoils of Evil", 163, Rarity.RARE, mage.cards.s.SpoilsOfEvil.class)); + cards.add(new SetCardInfo("Staff of the Ages", 340, Rarity.RARE, mage.cards.s.StaffOfTheAges.class)); + cards.add(new SetCardInfo("Stampede", 265, Rarity.RARE, mage.cards.s.Stampede.class)); + cards.add(new SetCardInfo("Stench of Evil", 165, Rarity.UNCOMMON, mage.cards.s.StenchOfEvil.class)); + cards.add(new SetCardInfo("Stone Rain", 217, Rarity.COMMON, mage.cards.s.StoneRain.class)); + cards.add(new SetCardInfo("Stone Spirit", 218, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); + cards.add(new SetCardInfo("Stonehands", 219, Rarity.COMMON, mage.cards.s.Stonehands.class)); + cards.add(new SetCardInfo("Storm Spirit", 303, Rarity.RARE, mage.cards.s.StormSpirit.class)); + cards.add(new SetCardInfo("Stormbind", 304, Rarity.RARE, mage.cards.s.Stormbind.class)); + cards.add(new SetCardInfo("Stromgald Cabal", 166, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); + cards.add(new SetCardInfo("Stunted Growth", 266, Rarity.RARE, mage.cards.s.StuntedGrowth.class)); + cards.add(new SetCardInfo("Sulfurous Springs", 360, Rarity.RARE, mage.cards.s.SulfurousSprings.class)); + cards.add(new SetCardInfo("Sunstone", 341, Rarity.UNCOMMON, mage.cards.s.Sunstone.class)); + cards.add(new SetCardInfo("Swamp", 373, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 374, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 375, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swords to Plowshares", 54, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Tarpan", 267, Rarity.COMMON, mage.cards.t.Tarpan.class)); + cards.add(new SetCardInfo("Thermokarst", 268, Rarity.UNCOMMON, mage.cards.t.Thermokarst.class)); + cards.add(new SetCardInfo("Thoughtleech", 269, Rarity.UNCOMMON, mage.cards.t.Thoughtleech.class)); + cards.add(new SetCardInfo("Thunder Wall", 104, Rarity.UNCOMMON, mage.cards.t.ThunderWall.class)); + cards.add(new SetCardInfo("Timberline Ridge", 361, Rarity.RARE, mage.cards.t.TimberlineRidge.class)); + cards.add(new SetCardInfo("Time Bomb", 342, Rarity.RARE, mage.cards.t.TimeBomb.class)); + cards.add(new SetCardInfo("Tinder Wall", 270, Rarity.COMMON, mage.cards.t.TinderWall.class)); + cards.add(new SetCardInfo("Tor Giant", 220, Rarity.COMMON, mage.cards.t.TorGiant.class)); + cards.add(new SetCardInfo("Total War", 221, Rarity.RARE, mage.cards.t.TotalWar.class)); + cards.add(new SetCardInfo("Touch of Death", 167, Rarity.COMMON, mage.cards.t.TouchOfDeath.class)); + cards.add(new SetCardInfo("Trailblazer", 272, Rarity.RARE, mage.cards.t.Trailblazer.class)); + cards.add(new SetCardInfo("Underground River", 362, Rarity.RARE, mage.cards.u.UndergroundRiver.class)); + cards.add(new SetCardInfo("Updraft", 105, Rarity.UNCOMMON, mage.cards.u.Updraft.class)); + cards.add(new SetCardInfo("Urza's Bauble", 343, Rarity.UNCOMMON, mage.cards.u.UrzasBauble.class)); + cards.add(new SetCardInfo("Veldt", 363, Rarity.RARE, mage.cards.v.Veldt.class)); + cards.add(new SetCardInfo("Venomous Breath", 273, Rarity.UNCOMMON, mage.cards.v.VenomousBreath.class)); + cards.add(new SetCardInfo("Vertigo", 222, Rarity.UNCOMMON, mage.cards.v.Vertigo.class)); + cards.add(new SetCardInfo("Vexing Arcanix", 344, Rarity.RARE, mage.cards.v.VexingArcanix.class)); + cards.add(new SetCardInfo("Vibrating Sphere", 345, Rarity.RARE, mage.cards.v.VibratingSphere.class)); + cards.add(new SetCardInfo("Walking Wall", 346, Rarity.UNCOMMON, mage.cards.w.WalkingWall.class)); + cards.add(new SetCardInfo("Wall of Lava", 223, Rarity.UNCOMMON, mage.cards.w.WallOfLava.class)); + cards.add(new SetCardInfo("Wall of Pine Needles", 274, Rarity.UNCOMMON, mage.cards.w.WallOfPineNeedles.class)); + cards.add(new SetCardInfo("Wall of Shields", 347, Rarity.UNCOMMON, mage.cards.w.WallOfShields.class)); + cards.add(new SetCardInfo("War Chariot", 348, Rarity.UNCOMMON, mage.cards.w.WarChariot.class)); + cards.add(new SetCardInfo("Warning", 55, Rarity.COMMON, mage.cards.w.Warning.class)); + cards.add(new SetCardInfo("Whalebone Glider", 349, Rarity.UNCOMMON, mage.cards.w.WhaleboneGlider.class)); + cards.add(new SetCardInfo("White Scarab", 56, Rarity.UNCOMMON, mage.cards.w.WhiteScarab.class)); + cards.add(new SetCardInfo("Whiteout", 275, Rarity.UNCOMMON, mage.cards.w.Whiteout.class)); + cards.add(new SetCardInfo("Wiitigo", 276, Rarity.RARE, mage.cards.w.Wiitigo.class)); + cards.add(new SetCardInfo("Wild Growth", 277, Rarity.COMMON, mage.cards.w.WildGrowth.class)); + cards.add(new SetCardInfo("Wind Spirit", 106, Rarity.UNCOMMON, mage.cards.w.WindSpirit.class)); + cards.add(new SetCardInfo("Wings of Aesthir", 305, Rarity.UNCOMMON, mage.cards.w.WingsOfAesthir.class)); + cards.add(new SetCardInfo("Withering Wisps", 168, Rarity.UNCOMMON, mage.cards.w.WitheringWisps.class)); + cards.add(new SetCardInfo("Woolly Mammoths", 278, Rarity.COMMON, mage.cards.w.WoollyMammoths.class)); + cards.add(new SetCardInfo("Woolly Spider", 279, Rarity.COMMON, mage.cards.w.WoollySpider.class)); + cards.add(new SetCardInfo("Word of Blasting", 224, Rarity.UNCOMMON, mage.cards.w.WordOfBlasting.class)); + cards.add(new SetCardInfo("Word of Undoing", 108, Rarity.COMMON, mage.cards.w.WordOfUndoing.class)); + cards.add(new SetCardInfo("Wrath of Marit Lage", 109, Rarity.RARE, mage.cards.w.WrathOfMaritLage.class)); + cards.add(new SetCardInfo("Yavimaya Gnats", 280, Rarity.UNCOMMON, mage.cards.y.YavimayaGnats.class)); + cards.add(new SetCardInfo("Zur's Weirding", 110, Rarity.RARE, mage.cards.z.ZursWeirding.class)); + cards.add(new SetCardInfo("Zuran Enchanter", 111, Rarity.COMMON, mage.cards.z.ZuranEnchanter.class)); + cards.add(new SetCardInfo("Zuran Orb", 350, Rarity.UNCOMMON, mage.cards.z.ZuranOrb.class)); + cards.add(new SetCardInfo("Zuran Spellcaster", 112, Rarity.COMMON, mage.cards.z.ZuranSpellcaster.class)); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java index 520f3a8a6f5..d696229bc96 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; @@ -70,8 +69,8 @@ public class PreventDamageToAttachedEffect extends PreventionEffectImpl { } sb.append("damage to "); sb.append(attachmentType.verb()); - sb.append("creature, prevent ").append(amountToPrevent);; - sb.append("of that damage"); + sb.append(" creature, prevent ").append(amountToPrevent);; + sb.append(" of that damage"); } return sb.toString(); } From e6823e442362fbe00c5adcdcf1f7a2ccd24cfb15 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 19:52:05 +0400 Subject: [PATCH 20/76] Test framework: added realtime check for damage (checkDamage, #4936) --- .../test/java/org/mage/test/player/TestPlayer.java | 13 +++++++++++++ .../serverside/base/impl/CardTestPlayerAPIImpl.java | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) 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 85fd011dc02..1e587b995b1 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 @@ -619,6 +619,13 @@ public class TestPlayer implements Player { wasProccessed = true; } + // check damage: card name, damage + if (params[0].equals(CHECK_COMMAND_DAMAGE) && params.length == 3) { + assertDamage(action, game, computerPlayer, params[1], Integer.parseInt(params[2])); + actions.remove(action); + wasProccessed = true; + } + // check life: life if (params[0].equals(CHECK_COMMAND_LIFE) && params.length == 2) { assertLife(action, game, computerPlayer, Integer.parseInt(params[1])); @@ -930,6 +937,12 @@ public class TestPlayer implements Player { Toughness, perm.getToughness().getValue()); } + private void assertDamage(PlayerAction action, Game game, Player player, String permanentName, int damage) { + Permanent perm = findPermanentWithAssert(action, game, player, permanentName); + + Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " have wrong damage: " + perm.getDamage() + " <> " + damage, damage, perm.getDamage()); + } + private void assertLife(PlayerAction action, Game game, Player player, int Life) { Assert.assertEquals(action.getActionName() + " - " + player.getName() + " have wrong life: " + player.getLife() + " <> " + Life, Life, player.getLife()); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index b7ccdb1bfc6..55863694081 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -52,6 +52,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement // TODO: add target player param to commands public static final String CHECK_COMMAND_PT = "PT"; + public static final String CHECK_COMMAND_DAMAGE = "DAMAGE"; public static final String CHECK_COMMAND_LIFE = "LIFE"; public static final String CHECK_COMMAND_ABILITY = "ABILITY"; public static final String CHECK_COMMAND_PERMANENT_COUNT = "PERMANENT_COUNT"; @@ -297,6 +298,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement check(checkName, turnNum, step, player, CHECK_COMMAND_PT, permanentName, power.toString(), toughness.toString()); } + public void checkDamage(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer damage) { + //Assert.assertNotEquals("", permanentName); + check(checkName, turnNum, step, player, CHECK_COMMAND_DAMAGE, permanentName, damage.toString()); + } + public void checkLife(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer life) { check(checkName, turnNum, step, player, CHECK_COMMAND_LIFE, life.toString()); } @@ -1251,7 +1257,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement if (!player.getActions().isEmpty()) { System.out.println("Remaining actions for " + player.getName() + " (" + player.getActions().size() + "):"); player.getActions().stream().forEach(a -> { - System.out.println("* turn " + a.getTurnNum() + " - " + a.getStep() + ": " + a.getActionName()); + System.out.println("* turn " + a.getTurnNum() + " - " + a.getStep() + ": " + (a.getActionName().isEmpty() ? a.getAction() : a.getActionName())); }); Assert.fail("Player " + player.getName() + " must have 0 actions but found " + player.getActions().size()); } From 0aeab75552303f9b890f20a3151c501daf9ba0bb Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 19:53:54 +0400 Subject: [PATCH 21/76] Added ConditionalCostModificationEffect to support cost modification effects with conditions (#5738) --- .../ConditionalCostModificationTest.java | 139 ++++++++++++++++++ .../ConditionalCostModificationEffect.java | 86 +++++++++++ 2 files changed, 225 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java create mode 100644 Mage/src/main/java/mage/abilities/decorator/ConditionalCostModificationEffect.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java new file mode 100644 index 00000000000..8dbe972ea37 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java @@ -0,0 +1,139 @@ +package org.mage.test.cards.continuous; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.condition.common.NotMyTurnCondition; +import mage.abilities.decorator.ConditionalCostModificationEffect; +import mage.abilities.effects.common.cost.AbilitiesCostReductionControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect; +import mage.abilities.keyword.EquipAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ConditionalCostModificationTest extends CardTestPlayerBase { + + // Dagger of the Worthy {2} + // Equipped creature gets +2/+0 and has afflict 1. + // Equip {2} + + @Test + public void test_NoModification() { + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Dagger of the Worthy", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTappedCount("Mountain", true, 2); + assertTappedCount("Mountain", false, 0); + } + + @Test + public void test_ModificationNormal() { + addCustomCardWithAbility("mod", playerA, new SimpleStaticAbility(new AbilitiesCostReductionControllerEffect(EquipAbility.class, "equip"))); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Dagger of the Worthy", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTappedCount("Mountain", true, 1); + assertTappedCount("Mountain", false, 1); + } + + @Test + public void test_ModificationConditionalActive() { + addCustomCardWithAbility("mod", playerA, new SimpleStaticAbility( + new ConditionalCostModificationEffect( + new AbilitiesCostReductionControllerEffect(EquipAbility.class, "equip"), + MyTurnCondition.instance, + "" + ) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Dagger of the Worthy", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTappedCount("Mountain", true, 1); + assertTappedCount("Mountain", false, 1); + } + + @Test + public void test_ModificationConditionalNotActive() { + addCustomCardWithAbility("mod", playerA, new SimpleStaticAbility( + new ConditionalCostModificationEffect( + new AbilitiesCostReductionControllerEffect(EquipAbility.class, "equip"), + NotMyTurnCondition.instance, + "" + ) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Dagger of the Worthy", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTappedCount("Mountain", true, 2); + assertTappedCount("Mountain", false, 0); + } + + @Test + public void test_ModificationConditionalNotActiveWithOtherEffect() { + addCustomCardWithAbility("mod", playerA, new SimpleStaticAbility( + new ConditionalCostModificationEffect( + new AbilitiesCostReductionControllerEffect(EquipAbility.class, "equip"), + NotMyTurnCondition.instance, + new SpellsCostIncreasementAllEffect(1), + "" + ) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Dagger of the Worthy", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Balduvian Bears"); // no mod, 2 cost + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); // +1 for spell, 2 cost + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTappedCount("Mountain", true, 4); + assertTappedCount("Mountain", false, 0); + assertLife(playerB, 20 - 3); + } + +} diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalCostModificationEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalCostModificationEffect.java new file mode 100644 index 00000000000..cecf01c6245 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalCostModificationEffect.java @@ -0,0 +1,86 @@ +package mage.abilities.decorator; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.effects.CostModificationEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.constants.Duration; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public class ConditionalCostModificationEffect extends CostModificationEffectImpl { + + protected CostModificationEffect effect; + protected CostModificationEffect otherwiseEffect; + protected Condition condition; + protected boolean conditionState; + + public ConditionalCostModificationEffect(CostModificationEffect effect, Condition condition, String text) { + this(effect, condition, null, text); + } + + public ConditionalCostModificationEffect(CostModificationEffect effect, Condition condition, CostModificationEffect otherwiseEffect, + String text) { + super(effect.getDuration(), effect.getOutcome(), effect.getModificationType()); + this.effect = effect; + this.condition = condition; + this.otherwiseEffect = otherwiseEffect; + if (text != null) { + this.setText(text); + } + } + + public ConditionalCostModificationEffect(final ConditionalCostModificationEffect effect) { + super(effect); + this.effect = (CostModificationEffect) effect.effect.copy(); + if (effect.otherwiseEffect != null) { + this.otherwiseEffect = (CostModificationEffect) effect.otherwiseEffect.copy(); + } + this.condition = effect.condition; + this.conditionState = effect.conditionState; + } + + @Override + public boolean isDiscarded() { + return effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded()); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + conditionState = condition.apply(game, source); + if (conditionState) { + effect.setTargetPointer(this.targetPointer); + return effect.apply(game, source, abilityToModify); + } else if (otherwiseEffect != null) { + otherwiseEffect.setTargetPointer(this.targetPointer); + return otherwiseEffect.apply(game, source, abilityToModify); + } + if (!conditionState && effect.getDuration() == Duration.OneUse) { + used = true; + } + if (!conditionState && effect.getDuration() == Duration.Custom) { + this.discard(); + } + return false; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + conditionState = condition.apply(game, source); + if (conditionState) { + effect.setTargetPointer(this.targetPointer); + return effect.applies(abilityToModify, source, game); + } else if (otherwiseEffect != null) { + otherwiseEffect.setTargetPointer(this.targetPointer); + return otherwiseEffect.applies(abilityToModify, source, game); + } + return false; + } + + @Override + public ConditionalCostModificationEffect copy() { + return new ConditionalCostModificationEffect(this); + } +} From 14274d8eaf41366f9e9104f074b08cac167ff83d Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 20:55:21 +0400 Subject: [PATCH 22/76] * Threshold abilities - fixed that restriction part of ability is not apply in some cards (#5738); --- Mage.Sets/src/mage/cards/f/Frightcrawler.java | 28 ++++++++----------- Mage.Sets/src/mage/cards/k/KirtarsDesire.java | 23 ++++++--------- .../src/mage/cards/o/OtarianJuggernaut.java | 18 ++++-------- .../src/mage/cards/s/StoneTongueBasilisk.java | 17 +++++------ .../ConditionalContinuousEffect.java | 9 +++--- .../ConditionalRequirementEffect.java | 20 ++++++++----- .../ConditionalRestrictionEffect.java | 13 ++++++++- 7 files changed, 66 insertions(+), 62 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/Frightcrawler.java b/Mage.Sets/src/mage/cards/f/Frightcrawler.java index d1123538e1e..dcca2568ac3 100644 --- a/Mage.Sets/src/mage/cards/f/Frightcrawler.java +++ b/Mage.Sets/src/mage/cards/f/Frightcrawler.java @@ -1,31 +1,27 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; import mage.abilities.effects.common.combat.CantBlockSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; + +import java.util.UUID; /** - * * @author cbt33 */ public final class Frightcrawler extends CardImpl { public Frightcrawler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.HORROR); this.power = new MageInt(1); @@ -37,14 +33,14 @@ public final class Frightcrawler extends CardImpl { Ability thresholdAbility = new SimpleStaticAbility( Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), - new CardsInControllerGraveCondition(7), - "If seven or more cards are in your graveyard, {this} gets +2/+2 " + new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), + new CardsInControllerGraveCondition(7), + "If seven or more cards are in your graveyard, {this} gets +2/+2 " )); - thresholdAbility.addEffect(new ConditionalContinuousEffect( - new CantBlockSourceEffect(Duration.WhileOnBattlefield), - new CardsInControllerGraveCondition(7), - "and can't block.")); + thresholdAbility.addEffect(new ConditionalRestrictionEffect( + new CantBlockSourceEffect(Duration.WhileOnBattlefield), + new CardsInControllerGraveCondition(7), + "and can't block.")); thresholdAbility.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(thresholdAbility); } diff --git a/Mage.Sets/src/mage/cards/k/KirtarsDesire.java b/Mage.Sets/src/mage/cards/k/KirtarsDesire.java index f42a3e25b13..9f987a41c7d 100644 --- a/Mage.Sets/src/mage/cards/k/KirtarsDesire.java +++ b/Mage.Sets/src/mage/cards/k/KirtarsDesire.java @@ -1,28 +1,22 @@ - package mage.cards.k; -import java.util.UUID; -import mage.target.common.TargetCreaturePermanent; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.combat.CantAttackAttachedEffect; import mage.abilities.effects.common.combat.CantAttackBlockAttachedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class KirtarsDesire extends CardImpl { @@ -43,8 +37,9 @@ public final class KirtarsDesire extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAttachedEffect(AttachmentType.AURA))); // Threshold - Enchanted creature can't block as long as seven or more cards are in your graveyard. - ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new CantAttackBlockAttachedEffect(AttachmentType.AURA), new CardsInControllerGraveCondition(7), + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalRestrictionEffect( + new CantAttackBlockAttachedEffect(AttachmentType.AURA), + new CardsInControllerGraveCondition(7), "Enchanted creature can't block as long as seven or more cards are in your graveyard")); ability.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/o/OtarianJuggernaut.java b/Mage.Sets/src/mage/cards/o/OtarianJuggernaut.java index 3a5f723f346..39369dff34d 100644 --- a/Mage.Sets/src/mage/cards/o/OtarianJuggernaut.java +++ b/Mage.Sets/src/mage/cards/o/OtarianJuggernaut.java @@ -1,29 +1,24 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleEvasionAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.Effect; +import mage.abilities.decorator.ConditionalRequirementEffect; import mage.abilities.effects.common.combat.AttacksIfAbleSourceEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class OtarianJuggernaut extends CardImpl { @@ -49,12 +44,11 @@ public final class OtarianJuggernaut extends CardImpl { new BoostSourceEffect(3, 0, Duration.WhileOnBattlefield), new CardsInControllerGraveCondition(7), "As long as seven or more cards are in your graveyard, {this} gets +3/+0")); - Effect effect = new ConditionalContinuousEffect( + ability.addEffect(new ConditionalRequirementEffect( new AttacksIfAbleSourceEffect(Duration.WhileOnBattlefield, true), new CardsInControllerGraveCondition(7), "and attacks each combat if able" - ); - ability.addEffect(effect); + )); ability.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java b/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java index d26774fce18..a64a30f7459 100644 --- a/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java +++ b/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java @@ -1,14 +1,12 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.condition.common.CardsInControllerGraveCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRequirementEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -20,14 +18,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author fireshoes */ public final class StoneTongueBasilisk extends CardImpl { public StoneTongueBasilisk(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}{G}"); this.subtype.add(SubType.BASILISK); this.power = new MageInt(4); this.toughness = new MageInt(5); @@ -39,9 +38,11 @@ public final class StoneTongueBasilisk extends CardImpl { this.addAbility(new DealsCombatDamageToACreatureTriggeredAbility(effect, false, true)); // Threshold - As long as seven or more cards are in your graveyard, all creatures able to block Stone-Tongue Basilisk do so. - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new MustBeBlockedByAllSourceEffect(), new CardsInControllerGraveCondition(7), - "As long as seven or more cards are in your graveyard, all creatures able to block {this} do so")); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalRequirementEffect( + new MustBeBlockedByAllSourceEffect(), + new CardsInControllerGraveCondition(7), + "As long as seven or more cards are in your graveyard, all creatures able to block {this} do so" + )); ability.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(ability); } diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java index 254f20dd03e..df8c2f51e57 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java @@ -49,11 +49,12 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { this.staticText = text; // checks for compatibility - if (effect != null && !effect.getEffectType().equals(EffectType.CONTINUOUS)) { - Assert.fail("ConditionalContinuousEffect supports only " + EffectType.CONTINUOUS.toString() + " but found " + effect.getEffectType().toString()); + EffectType needType = EffectType.CONTINUOUS; + if (effect != null && !effect.getEffectType().equals(needType)) { + Assert.fail("ConditionalContinuousEffect supports only " + needType.toString() + " but found " + effect.getEffectType().toString()); } - if (otherwiseEffect != null && !otherwiseEffect.getEffectType().equals(EffectType.CONTINUOUS)) { - Assert.fail("ConditionalContinuousEffect supports only " + EffectType.CONTINUOUS.toString() + " but found " + effect.getEffectType().toString()); + if (otherwiseEffect != null && !otherwiseEffect.getEffectType().equals(needType)) { + Assert.fail("ConditionalContinuousEffect supports only " + needType.toString() + " but found " + effect.getEffectType().toString()); } if (effect != null && otherwiseEffect != null && !effect.getEffectType().equals(otherwiseEffect.getEffectType())) { Assert.fail("ConditionalContinuousEffect must be same but found " + effect.getEffectType().toString() + " and " + otherwiseEffect.getEffectType().toString()); diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalRequirementEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalRequirementEffect.java index 5ebe3b61720..148be62172d 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalRequirementEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalRequirementEffect.java @@ -1,7 +1,5 @@ - package mage.abilities.decorator; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.abilities.condition.FixedCondition; @@ -12,12 +10,13 @@ import mage.constants.EffectType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author LevelX2 */ -public class ConditionalRequirementEffect extends RequirementEffect { +public class ConditionalRequirementEffect extends RequirementEffect { protected RequirementEffect effect; protected RequirementEffect otherwiseEffect; @@ -27,7 +26,14 @@ public class ConditionalRequirementEffect extends RequirementEffect { protected boolean initDone = false; public ConditionalRequirementEffect(RequirementEffect effect, Condition condition) { - this(Duration.WhileOnBattlefield, effect, condition, null, false); + this(effect, condition, null); + } + + public ConditionalRequirementEffect(RequirementEffect effect, Condition condition, String text) { + this(effect.getDuration(), effect, condition, null, false); + if (text != null) { + setText(text); + } } public ConditionalRequirementEffect(Duration duration, RequirementEffect effect, Condition condition, RequirementEffect otherwiseEffect, boolean lockedInCondition) { @@ -75,7 +81,7 @@ public class ConditionalRequirementEffect extends RequirementEffect { conditionState = condition.apply(game, source); if (conditionState) { effect.setTargetPointer(this.targetPointer); - return effect.applies(permanent, source,game); + return effect.applies(permanent, source, game); } else if (otherwiseEffect != null) { otherwiseEffect.setTargetPointer(this.targetPointer); return otherwiseEffect.applies(permanent, source, game); @@ -138,7 +144,7 @@ public class ConditionalRequirementEffect extends RequirementEffect { } return null; } - + @Override public ConditionalRequirementEffect copy() { return new ConditionalRequirementEffect(this); diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalRestrictionEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalRestrictionEffect.java index 5f1e6318d58..f9dec0248a3 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalRestrictionEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalRestrictionEffect.java @@ -22,14 +22,25 @@ public class ConditionalRestrictionEffect extends RestrictionEffect { protected boolean initDone = false; public ConditionalRestrictionEffect(RestrictionEffect effect, Condition condition) { - this(Duration.WhileOnBattlefield, effect, condition, null); + this(effect, condition, null); + } + + public ConditionalRestrictionEffect(RestrictionEffect effect, Condition condition, String text) { + this(effect.getDuration(), effect, condition, null, text); } public ConditionalRestrictionEffect(Duration duration, RestrictionEffect effect, Condition condition, RestrictionEffect otherwiseEffect) { + this(duration, effect, condition, otherwiseEffect, null); + } + + public ConditionalRestrictionEffect(Duration duration, RestrictionEffect effect, Condition condition, RestrictionEffect otherwiseEffect, String text) { super(duration); this.effect = effect; this.baseCondition = condition; this.otherwiseEffect = otherwiseEffect; + if (text != null) { + this.setText(text); + } } public ConditionalRestrictionEffect(final ConditionalRestrictionEffect effect) { From 2090a8c91e1158430191a193b5cd4121f5162f76 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 20:56:43 +0400 Subject: [PATCH 23/76] * Nahiri, Storm of Stone - fixed not working reduce cost ability; --- Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java b/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java index 0b044b2aeaf..133cb368781 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java +++ b/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java @@ -8,6 +8,7 @@ import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalCostModificationEffect; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; @@ -56,7 +57,7 @@ public final class NahiriStormOfStone extends CardImpl { ), MyTurnCondition.instance, "As long as it's your turn, " + "creatures you control have first strike" )); - ability.addEffect(new ConditionalContinuousEffect( + ability.addEffect(new ConditionalCostModificationEffect( new AbilitiesCostReductionControllerEffect( EquipAbility.class, "Equip" ), MyTurnCondition.instance, "and equip abilities you activate cost {1} less to activate" From 60eb9ff5cebee0efc0b59aa87b442a59c0116662 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 20:59:07 +0400 Subject: [PATCH 24/76] * Jace's Sentinel - fixed that can't be block effect doesn't work (#4468); --- Mage.Sets/src/mage/cards/j/JacesSentinel.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Mage.Sets/src/mage/cards/j/JacesSentinel.java b/Mage.Sets/src/mage/cards/j/JacesSentinel.java index a60f5c14b26..da20a95e49f 100644 --- a/Mage.Sets/src/mage/cards/j/JacesSentinel.java +++ b/Mage.Sets/src/mage/cards/j/JacesSentinel.java @@ -1,28 +1,24 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.ControllerPredicate; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class JacesSentinel extends CardImpl { @@ -48,7 +44,7 @@ public final class JacesSentinel extends CardImpl { new BoostSourceEffect(1, 0, Duration.WhileOnBattlefield), new PermanentsOnTheBattlefieldCondition(filter), "As long as you control a Jace planeswalker, {this} gets +1/+0")); - ability.addEffect(new ConditionalContinuousEffect( + ability.addEffect(new ConditionalRestrictionEffect( new CantBeBlockedSourceEffect(), new PermanentsOnTheBattlefieldCondition(filter), "and can't be blocked")); From a3fee30ed6822a560cbaed119b8023cf59cd5a3d Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 21:01:35 +0400 Subject: [PATCH 25/76] * Martyrs of Korlis - fixed that redirect damage effect doesn't work; --- .../src/mage/cards/m/MartyrsOfKorlis.java | 28 ++++++++----- .../cards/continuous/MartyrsOfKorlisTest.java | 42 +++++++++++++++++++ 2 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/MartyrsOfKorlisTest.java diff --git a/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java b/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java index bdd3efdb713..15edb14fd07 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java +++ b/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java @@ -1,40 +1,44 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.SourceTappedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalReplacementEffect; +import mage.abilities.effects.Effect; import mage.abilities.effects.RedirectionEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author MarcoMarin */ public final class MartyrsOfKorlis extends CardImpl { public MartyrsOfKorlis(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.HUMAN); this.power = new MageInt(1); this.toughness = new MageInt(6); // As long as Martyrs of Korlis is untapped, all damage that would be dealt to you by artifacts is dealt to Martyrs of Korlis instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + Effect effect = new ConditionalReplacementEffect( new RedirectArtifactDamageFromPlayerToSourceEffect(Duration.WhileOnBattlefield), new InvertCondition(SourceTappedCondition.instance), - "{this} redirects artifact damage from controller as long as it's untapped"))); + null); + effect.setText("{this} redirects artifact damage from controller as long as it's untapped"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public MartyrsOfKorlis(final MartyrsOfKorlis card) { @@ -50,7 +54,7 @@ public final class MartyrsOfKorlis extends CardImpl { class RedirectArtifactDamageFromPlayerToSourceEffect extends RedirectionEffect { public RedirectArtifactDamageFromPlayerToSourceEffect(Duration duration) { - super(duration); + super(duration); } public RedirectArtifactDamageFromPlayerToSourceEffect(final RedirectArtifactDamageFromPlayerToSourceEffect effect) { @@ -64,9 +68,13 @@ class RedirectArtifactDamageFromPlayerToSourceEffect extends RedirectionEffect { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getControllerId())&& + if (event.getTargetId().equals(source.getControllerId()) && game.getPermanentOrLKIBattlefield(event.getSourceId()).isArtifact()) { - this.redirectTarget.updateTarget(source.getSourceId(), game); + + TargetPermanent target = new TargetPermanent(); + target.add(source.getSourceId(), game); + this.redirectTarget = target; + return true; } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MartyrsOfKorlisTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MartyrsOfKorlisTest.java new file mode 100644 index 00000000000..6cb3214d4fb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MartyrsOfKorlisTest.java @@ -0,0 +1,42 @@ +package org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class MartyrsOfKorlisTest extends CardTestPlayerBase { + + // Martyrs of Korlis 1/6 + // As long as Martyrs of Korlis is untapped, all damage that would be dealt to you by artifacts is dealt to Martyrs of Korlis instead. + + @Test + public void test_PreventDamageToGideonOnYourTurn() { + addCard(Zone.BATTLEFIELD, playerB, "Martyrs of Korlis"); + addCard(Zone.BATTLEFIELD, playerA, "Alloy Myr"); // 2/2 + + // with redirect + checkDamage("turn 1 before", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Martyrs of Korlis", 0); + checkLife("turn 1 before", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20); + attack(1, playerA, "Alloy Myr", playerB); + checkDamage("turn 1 after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Martyrs of Korlis", 2); + checkLife("turn 1 after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, 20); + + attack(2, playerB, "Martyrs of Korlis", playerA); + + // without redirect + checkDamage("turn 3 before", 3, PhaseStep.PRECOMBAT_MAIN, playerB, "Martyrs of Korlis", 0); + checkLife("turn 3 before", 3, PhaseStep.PRECOMBAT_MAIN, playerB, 20); + attack(3, playerA, "Alloy Myr", playerB); + checkDamage("turn 3 after", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Martyrs of Korlis", 0); + checkLife("turn 3 after", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 2); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} From b1a27b4d2151a4490f3375e66078662ba4a46698 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 21:03:33 +0400 Subject: [PATCH 26/76] * Terentatek Cub - fixed that attacks if able effect doesn't work; --- Mage.Sets/src/mage/cards/t/TerentatekCub.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TerentatekCub.java b/Mage.Sets/src/mage/cards/t/TerentatekCub.java index 77f191bad00..0634db6c4cf 100644 --- a/Mage.Sets/src/mage/cards/t/TerentatekCub.java +++ b/Mage.Sets/src/mage/cards/t/TerentatekCub.java @@ -1,11 +1,12 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.OpponentControlsPermanentCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRequirementEffect; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.AttacksIfAbleSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -18,8 +19,9 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate; +import java.util.UUID; + /** - * * @author Styxo */ public final class TerentatekCub extends CardImpl { @@ -36,17 +38,15 @@ public final class TerentatekCub extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // As long as an opponent controls a Jedi or Sith, {this} gets +1/+1 and attacks each turn if able - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + // As long as an opponent controls a Jedi or Sith, {this} gets +1/+1 and attacks each turn if able + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new BoostSourceEffect(1, 1, Duration.Custom), new OpponentControlsPermanentCondition(filter), - "As long as an opponent controls a Jedi or Sith, {this} gets +1/+1 ") - )); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new AttacksIfAbleSourceEffect(Duration.Custom), - new OpponentControlsPermanentCondition(filter), - "and attacks each turn if able.") - )); + "As long as an opponent controls a Jedi or Sith, {this} gets +1/+1")); + Effect effect = new ConditionalRequirementEffect(new AttacksIfAbleSourceEffect(Duration.Custom), new OpponentControlsPermanentCondition(filter)); + effect.setText("and attacks each turn if able"); + ability.addEffect(effect); + this.addAbility(ability); } public TerentatekCub(final TerentatekCub card) { From a5ef7129241b5ed719f335cac79410ab440dea2b Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 1 May 2019 21:04:30 +0400 Subject: [PATCH 27/76] * Veteran Bodyguard - fixed that redirect damage effect doesn't work; --- .../src/mage/cards/v/VeteranBodyguard.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java b/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java index d1442674c93..483b834f2b6 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java +++ b/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java @@ -1,20 +1,17 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.SourceTappedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.decorator.ConditionalPreventionEffect; +import mage.abilities.effects.PreventionEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.UnblockedPredicate; @@ -24,8 +21,9 @@ import mage.game.events.DamagePlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author MTGfan */ public final class VeteranBodyguard extends CardImpl { @@ -38,7 +36,11 @@ public final class VeteranBodyguard extends CardImpl { this.toughness = new MageInt(5); // As long as Veteran Bodyguard is untapped, all damage that would be dealt to you by unblocked creatures is dealt to Veteran Bodyguard instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new VeteranBodyguardEffect(), new InvertCondition(SourceTappedCondition.instance), "As long as {this} is untapped, all damage that would be dealt to you by unblocked creatures is dealt to {this} instead."))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalPreventionEffect( + new VeteranBodyguardEffect(), + new InvertCondition(SourceTappedCondition.instance), + "As long as {this} is untapped, all damage that would be dealt to you by unblocked creatures is dealt to {this} instead." + ))); } public VeteranBodyguard(final VeteranBodyguard card) { @@ -51,7 +53,7 @@ public final class VeteranBodyguard extends CardImpl { } } -class VeteranBodyguardEffect extends ReplacementEffectImpl { +class VeteranBodyguardEffect extends PreventionEffectImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("unblocked creatures"); @@ -60,7 +62,7 @@ class VeteranBodyguardEffect extends ReplacementEffectImpl { } VeteranBodyguardEffect() { - super(Duration.WhileOnBattlefield, Outcome.RedirectDamage); + super(Duration.WhileOnBattlefield); staticText = "all combat damage that would be dealt to you by unblocked creatures is dealt to {source} instead"; } @@ -76,7 +78,7 @@ class VeteranBodyguardEffect extends ReplacementEffectImpl { permanent.damage(damageEvent.getAmount(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable()); return true; } - return true; + return false; } @Override From 3ff871c6de6fe87c0bb96aafa58ebcc4bc74f50c Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 2 May 2019 17:25:46 +0400 Subject: [PATCH 28/76] * Proliferate - fixed that it highlights all permanents instead with counters only; --- .../src/mage/cards/g/GuildpactInformant.java | 3 +- ...CombatDamageToAPlayerTriggeredAbility.java | 2 +- .../common/counter/ProliferateEffect.java | 5 +- .../FilterPermanentOrPlayerWithCounter.java | 28 +++--- .../common/TargetOpponentOrPlaneswalker.java | 11 ++- .../common/TargetPermanentOrPlayer.java | 34 +++----- .../TargetPermanentOrPlayerWithCounter.java | 87 ------------------- 7 files changed, 37 insertions(+), 133 deletions(-) delete mode 100644 Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerWithCounter.java diff --git a/Mage.Sets/src/mage/cards/g/GuildpactInformant.java b/Mage.Sets/src/mage/cards/g/GuildpactInformant.java index 47d833255f3..564d26a6375 100644 --- a/Mage.Sets/src/mage/cards/g/GuildpactInformant.java +++ b/Mage.Sets/src/mage/cards/g/GuildpactInformant.java @@ -27,7 +27,8 @@ public final class GuildpactInformant extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Whenever Guildpact Informant deals combat damage to a player or planeswalker, proliferate. (Choose any number of permanents and/or players, then give each another counter of each kind already there.) + // Whenever Guildpact Informant deals combat damage to a player or planeswalker, + // proliferate. (Choose any number of permanents and/or players, then give each another counter of each kind already there.) this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( new ProliferateEffect(), false ).setOrPlaneswalker(true)); diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java index acf69c2ba12..e05f4cf3bba 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -45,6 +44,7 @@ public class DealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbility this.text = ability.text; this.setTargetPointer = ability.setTargetPointer; this.onlyOpponents = ability.onlyOpponents; + this.orPlaneswalker = ability.orPlaneswalker; } public DealsCombatDamageToAPlayerTriggeredAbility setOrPlaneswalker(boolean orPlaneswalker) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java index 005e6c0712d..e82e3c94def 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java @@ -4,11 +4,12 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.counters.Counter; +import mage.filter.common.FilterPermanentOrPlayerWithCounter; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetPermanentOrPlayerWithCounter; +import mage.target.common.TargetPermanentOrPlayer; import java.io.Serializable; import java.util.HashMap; @@ -47,7 +48,7 @@ public class ProliferateEffect extends OneShotEffect { if (controller == null) { return false; } - Target target = new TargetPermanentOrPlayerWithCounter(0, Integer.MAX_VALUE, true); + Target target = new TargetPermanentOrPlayer(0, Integer.MAX_VALUE, new FilterPermanentOrPlayerWithCounter(), true); Map options = new HashMap<>(); options.put("UI.right.btn.text", "Done"); controller.choose(Outcome.Benefit, target, source.getSourceId(), game, options); diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java index 6f5dfa5dba4..16c0eb36233 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java @@ -1,13 +1,11 @@ - - package mage.filter.common; +import mage.MageItem; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import java.util.UUID; -import mage.MageItem; /** * @author nantuko @@ -28,24 +26,24 @@ public class FilterPermanentOrPlayerWithCounter extends FilterPermanentOrPlayer @Override public boolean match(MageItem o, Game game) { - if (o instanceof Player) { - if (((Player)o).getCounters().isEmpty()) { - return false; - } - } else if (o instanceof Permanent) { - if (((Permanent)o).getCounters(game).isEmpty()) { - return false; + if (super.match(o, game)) { + if (o instanceof Player) { + return !((Player) o).getCounters().isEmpty(); + } else if (o instanceof Permanent) { + return !((Permanent) o).getCounters(game).isEmpty(); } } - return super.match(o, game); + return false; } @Override public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) { - if (o instanceof Player) { - return playerFilter.match((Player) o, sourceId, playerId, game); - } else if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, sourceId, playerId, game); + if (super.match(o, sourceId, playerId, game)) { + if (o instanceof Player) { + return !((Player) o).getCounters().isEmpty(); + } else if (o instanceof Permanent) { + return !((Permanent) o).getCounters(game).isEmpty(); + } } return false; } diff --git a/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java b/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java index ac89c3f5375..20f4fa974e2 100644 --- a/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java +++ b/Mage/src/main/java/mage/target/common/TargetOpponentOrPlaneswalker.java @@ -8,23 +8,22 @@ package mage.target.common; import mage.filter.common.FilterOpponentOrPlaneswalker; /** - * * @author LevelX2 */ public class TargetOpponentOrPlaneswalker extends TargetPermanentOrPlayer { public TargetOpponentOrPlaneswalker() { - this(1, 1, new FilterOpponentOrPlaneswalker("opponent or planeswalker"), false); - } - - public TargetOpponentOrPlaneswalker(int numTargets) { - this(numTargets, numTargets, new FilterOpponentOrPlaneswalker(), false); + this(1); } public TargetOpponentOrPlaneswalker(FilterOpponentOrPlaneswalker filter) { this(1, 1, filter, false); } + public TargetOpponentOrPlaneswalker(int numTargets) { + this(numTargets, numTargets, new FilterOpponentOrPlaneswalker("opponent or planeswalker"), false); + } + public TargetOpponentOrPlaneswalker(int minNumTargets, int maxNumTargets, FilterOpponentOrPlaneswalker filter, boolean notTarget) { super(minNumTargets, maxNumTargets, filter, notTarget); } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java index 70dab46b02e..cee4973c4f8 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java @@ -1,28 +1,26 @@ package mage.target.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; import mage.filter.Filter; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentOrPlayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetImpl; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author nantuko */ public class TargetPermanentOrPlayer extends TargetImpl { protected FilterPermanentOrPlayer filter; - protected FilterPermanent filterPermanent; public TargetPermanentOrPlayer() { this(1, 1); @@ -46,14 +44,12 @@ public class TargetPermanentOrPlayer extends TargetImpl { this.zone = Zone.ALL; this.filter = filter; this.targetName = filter.getMessage(); - this.filterPermanent = this.filter.getPermanentFilter(); this.notTarget = notTarget; } public TargetPermanentOrPlayer(final TargetPermanentOrPlayer target) { super(target); this.filter = target.filter.copy(); - this.filterPermanent = target.filterPermanent.copy(); } @Override @@ -61,10 +57,6 @@ public class TargetPermanentOrPlayer extends TargetImpl { return filter; } - public void setFilter(FilterPermanentOrPlayer filter) { - this.filter = filter; - } - @Override public boolean canTarget(UUID id, Game game) { Permanent permanent = game.getPermanent(id); @@ -118,7 +110,7 @@ public class TargetPermanentOrPlayer extends TargetImpl { * {@link mage.players.Player} that can be chosen. Should only be used for * Ability targets since this checks for protection, shroud etc. * - * @param sourceId - the target event source + * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source * @param game * @return - true if enough valid {@link mage.game.permanent.Permanent} or @@ -130,14 +122,14 @@ public class TargetPermanentOrPlayer extends TargetImpl { MageObject targetSource = game.getObject(sourceId); for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.getPlayerFilter().match(player, sourceId, sourceControllerId, game)) { + if (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(player, sourceId, sourceControllerId, game)) { count++; if (count >= this.minNumberOfTargets) { return true; } } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, sourceId, sourceControllerId, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -170,7 +162,7 @@ public class TargetPermanentOrPlayer extends TargetImpl { } } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanent, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if (filter.match(permanent, null, sourceControllerId, game) && filter.match(permanent, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -187,11 +179,11 @@ public class TargetPermanentOrPlayer extends TargetImpl { MageObject targetSource = game.getObject(sourceId); for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.getPlayerFilter().match(player, sourceId, sourceControllerId, game)) { + if (player != null && (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(player, sourceId, sourceControllerId, game)) { possibleTargets.add(playerId); } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPermanent(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, sourceId, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); } @@ -204,11 +196,11 @@ public class TargetPermanentOrPlayer extends TargetImpl { Set possibleTargets = new HashSet<>(); for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.getPlayerFilter().match(player, game)) { + if (player != null && filter.match(player, game)) { possibleTargets.add(playerId); } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if (filter.match(permanent, null, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); } @@ -237,6 +229,6 @@ public class TargetPermanentOrPlayer extends TargetImpl { } public FilterPermanent getFilterPermanent() { - return filterPermanent.copy(); + return filter.getPermanentFilter().copy(); } } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerWithCounter.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerWithCounter.java deleted file mode 100644 index d6a515991df..00000000000 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerWithCounter.java +++ /dev/null @@ -1,87 +0,0 @@ - - -package mage.target.common; - -import mage.abilities.Ability; -import mage.filter.common.FilterPermanentOrPlayerWithCounter; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import java.util.UUID; -import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.CounterPredicate; - -/** - * - * @author nantuko - */ -public class TargetPermanentOrPlayerWithCounter extends TargetPermanentOrPlayer { - - protected final FilterPermanentOrPlayerWithCounter targetFilter; - - public TargetPermanentOrPlayerWithCounter() { - this(1, 1); - } - - public TargetPermanentOrPlayerWithCounter(int numTargets) { - this(numTargets, numTargets); - } - - public TargetPermanentOrPlayerWithCounter(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, false); - } - - public TargetPermanentOrPlayerWithCounter(int minNumTargets, int maxNumTargets, boolean notTarget) { - super(minNumTargets, maxNumTargets, notTarget); - this.targetFilter = new FilterPermanentOrPlayerWithCounter(); - this.filterPermanent = new FilterPermanent(); - this.filterPermanent.add(new CounterPredicate(null)); - this.targetName = targetFilter.getMessage(); - } - - public TargetPermanentOrPlayerWithCounter(final TargetPermanentOrPlayerWithCounter target) { - super(target); - this.targetFilter = target.targetFilter.copy(); - super.setFilter(this.targetFilter); - } - - @Override - public TargetPermanentOrPlayerWithCounter copy() { - return new TargetPermanentOrPlayerWithCounter(this); - } - - @Override - public boolean canTarget(UUID id, Game game) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - if (permanent.getCounters(game).isEmpty()) { - return false; - } - } - Player player = game.getPlayer(id); - if (player != null) { - if (player.getCounters().isEmpty()) { - return false; - } - } - return super.canTarget(id, game); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - if (permanent.getCounters(game).isEmpty()) { - return false; - } - } - Player player = game.getPlayer(id); - if (player != null) { - if (player.getCounters().isEmpty()) { - return false; - } - } - return super.canTarget(id, source, game); - } - -} From 011f1b7a7496840116cde56a2ed92dff1841d45f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 2 May 2019 17:34:24 +0400 Subject: [PATCH 29/76] * Ashiok, Dream Render - fixed that it allows to target two players instead one; --- Mage.Sets/src/mage/cards/a/AshiokDreamRender.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java b/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java index 9b383b89f57..ac680020c0d 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java +++ b/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java @@ -7,6 +7,7 @@ import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; +import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -17,8 +18,6 @@ import mage.players.Player; import mage.target.TargetPlayer; import java.util.UUID; -import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; -import mage.target.TargetPlayer; /** * @author TheElk801 @@ -37,7 +36,6 @@ public final class AshiokDreamRender extends CardImpl { // -1: Target player puts the top four cards of their library into their graveyard. Then exile each opponent's graveyard. Ability ability = new LoyaltyAbility(new PutLibraryIntoGraveTargetEffect(4), -1); - ability.addTarget(new TargetPlayer()); ability.addEffect(new ExileGraveyardAllPlayersEffect(StaticFilters.FILTER_CARD, TargetController.OPPONENT).setText("Then exile each opponent's graveyard.")); ability.addTarget(new TargetPlayer()); this.addAbility(ability); From 6c1ae7a049a9073b9fb1f141c7a02c678a7a1da5 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 2 May 2019 11:08:01 -0500 Subject: [PATCH 30/76] - Fixed Orim's Prayer. #5776 --- Mage.Sets/src/mage/cards/o/OrimsPrayer.java | 29 +++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java index b171246ae47..61a6a42b8d7 100644 --- a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java +++ b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java @@ -2,7 +2,6 @@ package mage.cards.o; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,6 +10,8 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; /** * @@ -36,9 +37,11 @@ public final class OrimsPrayer extends CardImpl { } class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { + + int numberAttackingController = 0; public OrimsPrayerTriggeredAbility() { - super(Zone.BATTLEFIELD, new GainLifeEffect(new AttackingCreatureCount())); + super(Zone.BATTLEFIELD, null); } public OrimsPrayerTriggeredAbility(final OrimsPrayerTriggeredAbility ability) { @@ -57,12 +60,28 @@ class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return game.getCombat().getDefenders().contains(getControllerId()) - && game.getCombat().getAttackers().size() > 0; + boolean applied = false; + Player controller = game.getPlayer(getControllerId()); + if (controller == null) { + return false; + } + for (UUID attackersId : game.getCombat().getAttackers()) { + Permanent attackingCreature = game.getPermanent(attackersId); + if (attackingCreature != null + && game.getCombat().getDefenderId(attackersId) == this.getControllerId()) { + numberAttackingController += 1; + applied = true; + } + } + if (applied + && numberAttackingController > 0) { + this.getEffects().add(new GainLifeEffect(numberAttackingController)); + } + return applied; } @Override public String getRule() { - return "Whenever one or more creatures attack you, " + super.getRule(); + return "Whenever one or more creatures attack you, you gain 1 life for each attacking creature."; } } From 648026ac0bfe759bc85c24d4f3bfad94b2dfe2be Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 2 May 2019 23:14:56 +0400 Subject: [PATCH 31/76] Test framework: fixed not working choices for replacement effects; --- .../src/test/java/org/mage/test/player/TestPlayer.java | 6 ++++-- Mage/src/main/java/mage/players/Player.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) 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 1e587b995b1..ebd8da1cd0f 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 @@ -1406,11 +1406,13 @@ public class TestPlayer implements Player { } if (!choices.isEmpty()) { for (String choice : choices) { - for (int index = 0; index < rEffects.size(); index++) { - if (choice.equals(rEffects.get(Integer.toString(index)))) { + int index = 0; + for (Map.Entry entry : rEffects.entrySet()) { + if (entry.getValue().startsWith(choice)) { choices.remove(choice); return index; } + index++; } } // TODO: enable fail checks and fix tests diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 6e5c31dc3bb..6dc09c8f9dd 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -569,6 +569,7 @@ public interface Player extends MageItem, Copyable { // set the value for non mana X costs int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost); + // TODO: rework choose replacement effects to use array, not map (it'a random order now) int chooseReplacementEffect(Map abilityMap, Game game); TriggeredAbility chooseTriggeredAbility(List abilities, Game game); From 735d2a16a2cf75bde4d663c5408f5d121a15ab2c Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 2 May 2019 23:15:43 +0400 Subject: [PATCH 32/76] * Feather, the Redeemed - fixed that it returns to hand re-casted/blinked cards; --- .../src/mage/cards/f/FeatherTheRedeemed.java | 6 +- .../other/FeatherTheRedeemedTest.java | 121 ++++++++++++++++++ 2 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java diff --git a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java index 74de65b44f7..ebb28bb5b93 100644 --- a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java +++ b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java @@ -167,11 +167,9 @@ class FeatherTheRedeemedEffect extends ReplacementEffectImpl { if (zEvent.getFromZone() == Zone.STACK && zEvent.getToZone() == Zone.GRAVEYARD && event.getSourceId() != null) { - if (event.getSourceId().equals(event.getTargetId())) { + if (event.getSourceId().equals(event.getTargetId()) && mor.getZoneChangeCounter() == game.getState().getZoneChangeCounter(event.getSourceId())) { Spell spell = game.getStack().getSpell(mor.getSourceId()); - if (spell != null && spell.isInstantOrSorcery()) { - return true; - } + return spell != null && spell.isInstantOrSorcery(); } } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java new file mode 100644 index 00000000000..bc353d8bd08 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java @@ -0,0 +1,121 @@ +package org.mage.test.cards.abilities.other; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneAllEffect; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class FeatherTheRedeemedTest extends CardTestPlayerBase { + + // Feather, the Redeemed {R}{W}{W} + /* + Whenever you cast an instant or sorcery spell that targets a creature you control, exile that card + instead of putting it into your graveyard as it resolves. If you do, return it to your hand at the beginning of the next end step. + */ + + @Test + public void test_ExileSpellWithReturnAtTheEnd() { + // cast bolt, put to exile, return to hand + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 2); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + checkPermanentCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1); + checkExileCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // return to hand at the next end step + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ExileSpellAndRecastWithReturnAtTheEnd() { + // cast bolt, put to exile, cast from exile, put to exile, return to hand + addCustomCardWithAbility("cast from exile", playerA, new SimpleStaticAbility( + new PlayFromNotOwnHandZoneAllEffect(StaticFilters.FILTER_CARD, Zone.EXILED, false, TargetController.ANY, Duration.WhileOnBattlefield) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 3); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 1); + checkExileCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // cast from exile and put to exile again + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 2); + checkExileCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // return to hand at the next end step + setChoice(playerA, "At the beginning of the next end step"); // two triggeres from two cast (card's code adds two same effects on each trigger) + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ExileSpellAndRecastWithoutReturn() { + // cast bolt, put to exile, cast from exile with different target, put to graveyard, not return + addCustomCardWithAbility("cast from exile", playerA, new SimpleStaticAbility( + new PlayFromNotOwnHandZoneAllEffect(StaticFilters.FILTER_CARD, Zone.EXILED, false, TargetController.ANY, Duration.WhileOnBattlefield) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 3); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 1); + checkExileCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // cast from exile to target player (without trigger) and put to graveyard + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 1); // no changes + checkLife("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 3); + checkExileCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // not return to hand at the next end step + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } +} From ef48d23c4a20a8a688da2226f465aa7dfdb0a2dc Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 2 May 2019 17:35:11 -0500 Subject: [PATCH 33/76] - Fixed #5777. --- .../src/mage/cards/b/BridgeFromBelow.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BridgeFromBelow.java b/Mage.Sets/src/mage/cards/b/BridgeFromBelow.java index 21da232f164..f442083da90 100644 --- a/Mage.Sets/src/mage/cards/b/BridgeFromBelow.java +++ b/Mage.Sets/src/mage/cards/b/BridgeFromBelow.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -13,7 +12,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.other.OwnerPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; @@ -28,23 +27,24 @@ import mage.players.Player; */ public final class BridgeFromBelow extends CardImpl { - private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("Whenever a nontoken creature is put into your graveyard from the battlefield"); private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("When a creature is put into an opponent's graveyard from the battlefield"); - - static{ - filter1.add(new ControllerPredicate(TargetController.YOU)); + + static { + filter1.add(new OwnerPredicate(TargetController.YOU)); filter1.add(Predicates.not(TokenPredicate.instance)); - filter2.add(new ControllerPredicate(TargetController.OPPONENT)); + filter2.add(new OwnerPredicate(TargetController.OPPONENT)); } - + public BridgeFromBelow(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}{B}{B}"); // Whenever a nontoken creature is put into your graveyard from the battlefield, if Bridge from Below is in your graveyard, create a 2/2 black Zombie creature token. this.addAbility(new BridgeFromBelowAbility(new CreateTokenEffect(new ZombieToken()), filter1)); + // When a creature is put into an opponent's graveyard from the battlefield, if Bridge from Below is in your graveyard, exile Bridge from Below. this.addAbility(new BridgeFromBelowAbility(new ExileSourceEffect(), filter2)); + } public BridgeFromBelow(final BridgeFromBelow card) { @@ -86,7 +86,8 @@ class BridgeFromBelowAbility extends TriggeredAbilityImpl { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (permanent != null && filter.match(permanent, sourceId, controllerId, game)) { + if (permanent != null + && filter.match(permanent, sourceId, controllerId, game)) { return true; } } @@ -96,11 +97,12 @@ class BridgeFromBelowAbility extends TriggeredAbilityImpl { @Override public boolean checkInterveningIfClause(Game game) { Player controller = game.getPlayer(this.getControllerId()); - return controller != null && controller.getGraveyard().contains(this.getSourceId()); + return controller != null + && controller.getGraveyard().contains(this.getSourceId()); } - + @Override public String getRule() { - return filter.getMessage() +", if {this} is in your graveyard, " + super.getRule(); + return filter.getMessage() + ", if {this} is in your graveyard, " + super.getRule(); } } From af574443f8d83e1ef5f7c5f7c06b4771b8442cd7 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 3 May 2019 10:29:06 +0400 Subject: [PATCH 34/76] Prepare 35v4 hotfix --- Mage.Common/src/main/java/mage/utils/MageVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index f1741344447..20cc7b2c565 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -13,7 +13,7 @@ public class MageVersion implements Serializable, Comparable { public static final int MAGE_VERSION_MINOR = 4; public static final int MAGE_VERSION_PATCH = 35; public static final String MAGE_EDITION_INFO = ""; // set "-beta" for 1.4.32-betaV0 - public static final String MAGE_VERSION_MINOR_PATCH = "V3"; // default + public static final String MAGE_VERSION_MINOR_PATCH = "V4"; // default // strict mode private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes) From c1dea3456c1f56d6b507ae7e6aa2718a157dd127 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 3 May 2019 19:07:09 +0400 Subject: [PATCH 35/76] * Bolas's Citadel - fixed error on land play from library's top; --- Mage.Sets/src/mage/cards/b/BolassCitadel.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java index dce1e6898d0..b66b461f2bf 100644 --- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java +++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java @@ -104,8 +104,10 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { Costs costs = new CostsImpl(); costs.add(cost); // check for additional costs that must be paid - for (Cost additionalCost : cardOnTop.getSpellAbility().getCosts()) { - costs.add(additionalCost); + if (cardOnTop.getSpellAbility() != null) { + for (Cost additionalCost : cardOnTop.getSpellAbility().getCosts()) { + costs.add(additionalCost); + } } controller.setCastSourceIdWithAlternateMana(cardOnTop.getId(), null, costs); return true; From 878d602b58afb20c14138b9e55e9f5c860800145 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 3 May 2019 19:10:17 +0400 Subject: [PATCH 36/76] Fixed potentional NPE error --- ...DragonOnTheBattlefieldWhileSpellWasCastWatcher.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java b/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java index 46266a82184..a0c6c70725a 100644 --- a/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DragonOnTheBattlefieldWhileSpellWasCastWatcher.java @@ -42,10 +42,12 @@ public class DragonOnTheBattlefieldWhileSpellWasCastWatcher extends Watcher { // revealed a Dragon card or controlled a Dragon as you cast the spell if (spell != null) { boolean revealedOrOnBattlefield = false; - for (Cost cost : spell.getSpellAbility().getCosts()) { - if (cost instanceof RevealTargetFromHandCost) { - revealedOrOnBattlefield = ((RevealTargetFromHandCost) cost).getNumberRevealedCards() > 0; - break; + if (spell.getSpellAbility() != null) { + for (Cost cost : spell.getSpellAbility().getCosts()) { + if (cost instanceof RevealTargetFromHandCost) { + revealedOrOnBattlefield = ((RevealTargetFromHandCost) cost).getNumberRevealedCards() > 0; + break; + } } } if (!revealedOrOnBattlefield) { From e980240553d763af8f3cda4b6252ad01e7accc84 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 3 May 2019 10:18:29 -0500 Subject: [PATCH 37/76] - small fixes to some "Curse" cards. --- Mage.Sets/src/mage/cards/c/CurseOfPredation.java | 12 ++---------- Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java | 5 ++--- Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java | 11 ++++++++--- Mage.Sets/src/mage/cards/c/CurseOfTheForsaken.java | 10 ++-------- Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java | 12 ++++++++---- Mage.Sets/src/mage/cards/c/CurseOfThirst.java | 12 ++++++------ Mage.Sets/src/mage/cards/c/CurseOfVengeance.java | 7 ++++--- Mage.Sets/src/mage/cards/e/EverythingamajigE.java | 2 +- 8 files changed, 33 insertions(+), 38 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CurseOfPredation.java b/Mage.Sets/src/mage/cards/c/CurseOfPredation.java index a130d376858..7616a3cc564 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfPredation.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfPredation.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -29,10 +28,9 @@ import mage.target.targetpointer.FixedTarget; public final class CurseOfPredation extends CardImpl { public CurseOfPredation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); this.subtype.add(SubType.AURA, SubType.CURSE); - // Enchant player TargetPlayer auraTarget = new TargetPlayer(); this.getSpellAbility().addTarget(auraTarget); @@ -75,17 +73,11 @@ class CurseOfPredationTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Player defender = game.getPlayer(event.getTargetId()); - if (defender == null) { - Permanent planeswalker = game.getPermanent(event.getTargetId()); - if (planeswalker != null) { - defender = game.getPlayer(planeswalker.getControllerId()); - } - } if (defender != null) { Permanent enchantment = game.getPermanent(this.getSourceId()); if (enchantment != null && enchantment.isAttachedTo(defender.getId())) { - for (Effect effect: this.getEffects()) { + for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); } return true; diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java index 18040ebb79d..1d12b1daf59 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -29,16 +28,16 @@ import java.util.UUID; public final class CurseOfTheBloodyTome extends CardImpl { public CurseOfTheBloodyTome(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); this.subtype.add(SubType.AURA, SubType.CURSE); - // Enchant player TargetPlayer target = new TargetPlayer(); this.getSpellAbility().addTarget(target); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); Ability ability = new EnchantAbility(target.getTargetName()); this.addAbility(ability); + // At the beginning of enchanted player's upkeep, that player puts the top two cards of their library into their graveyard. this.addAbility(new CurseOfTheBloodyTomeAbility()); diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java b/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java index 8e2ab315d4e..0c4554afc9d 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -42,10 +41,13 @@ public final class CurseOfTheCabal extends CardImpl { // Target player sacrifices half the permanents he or she controls, rounded down. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new CurseOfTheCabalSacrificeEffect()); + // Suspend 2-{2}{B}{B} this.addAbility(new SuspendAbility(2, new ManaCostsImpl("{2}{B}{B}"), this)); + // At the beginning of each player's upkeep, if Curse of the Cabal is suspended, that player may sacrifice a permanent. If he or she does, put two time counters on Curse of the Cabal. this.addAbility(new CurseOfTheCabalInterveningIfTriggeredAbility()); + } public CurseOfTheCabal(final CurseOfTheCabal card) { @@ -84,7 +86,8 @@ class CurseOfTheCabalSacrificeEffect extends OneShotEffect { } Target target = new TargetControlledPermanent(amount, amount, StaticFilters.FILTER_CONTROLLED_PERMANENT, true); if (target.canChoose(targetPlayer.getId(), game)) { - while (!target.isChosen() && target.canChoose(targetPlayer.getId(), game) && targetPlayer.canRespond()) { + while (!target.isChosen() + && target.canChoose(targetPlayer.getId(), game) && targetPlayer.canRespond()) { targetPlayer.choose(Outcome.Sacrifice, target, source.getSourceId(), game); } //sacrifice all chosen (non null) permanents @@ -107,7 +110,9 @@ class CurseOfTheCabalInterveningIfTriggeredAbility extends ConditionalIntervenin TargetController.ANY, false, true ), SuspendedCondition.instance, - "At the beginning of each player's upkeep, if {this} is suspended, that player may sacrifice a permanent. If he or she does, put two time counters on {this}." + "At the beginning of each player's upkeep, if {this} is suspended, " + + "that player may sacrifice a permanent. If he or she does, " + + "put two time counters on {this}." ); // controller has to sac a permanent // counters aren't placed diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheForsaken.java b/Mage.Sets/src/mage/cards/c/CurseOfTheForsaken.java index f240a0e5d6f..ee09485bc1f 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheForsaken.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheForsaken.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.TriggeredAbilityImpl; @@ -29,7 +28,7 @@ import java.util.UUID; public final class CurseOfTheForsaken extends CardImpl { public CurseOfTheForsaken(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); this.subtype.add(SubType.AURA, SubType.CURSE); // Enchant player @@ -40,6 +39,7 @@ public final class CurseOfTheForsaken extends CardImpl { // Whenever a creature attacks enchanted player, its controller gains 1 life. this.addAbility(new CurseOfTheForsakenTriggeredAbility()); + } public CurseOfTheForsaken(final CurseOfTheForsaken card) { @@ -74,12 +74,6 @@ class CurseOfTheForsakenTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Player defender = game.getPlayer(event.getTargetId()); - if (defender == null) { - Permanent planeswalker = game.getPermanent(event.getTargetId()); - if (planeswalker != null) { - defender = game.getPlayer(planeswalker.getControllerId()); - } - } if (defender != null) { Permanent enchantment = game.getPermanent(this.getSourceId()); if (enchantment != null diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java b/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java index 6332c7987e7..ece104889a3 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -29,8 +28,10 @@ public final class CurseOfTheSwine extends CardImpl { // Exile X target creatures. For each creature exiled this way, its controller creates a 2/2 green Boar creature token. this.getSpellAbility().addEffect(new CurseOfTheSwineEffect()); + // Correct number of targets will be set in adjustTargets this.getSpellAbility().setTargetAdjuster(CurseOfTheSwineAdjuster.instance); + } public CurseOfTheSwine(final CurseOfTheSwine card) { @@ -57,7 +58,8 @@ class CurseOfTheSwineEffect extends OneShotEffect { public CurseOfTheSwineEffect() { super(Outcome.Exile); - this.staticText = "Exile X target creatures. For each creature exiled this way, its controller creates a 2/2 green Boar creature token"; + this.staticText = "Exile X target creatures. For each creature exiled this way, " + + "its controller creates a 2/2 green Boar creature token"; } public CurseOfTheSwineEffect(final CurseOfTheSwineEffect effect) { @@ -78,13 +80,15 @@ class CurseOfTheSwineEffect extends OneShotEffect { Permanent creature = game.getPermanent(targetId); if (creature != null) { if (controller.moveCards(creature, Zone.EXILED, source, game)) { - playersWithTargets.put(creature.getControllerId(), playersWithTargets.getOrDefault(creature.getControllerId(), 0) + 1); + playersWithTargets.put(creature.getControllerId(), + playersWithTargets.getOrDefault(creature.getControllerId(), 0) + 1); } } } CurseOfTheSwineBoarToken swineToken = new CurseOfTheSwineBoarToken(); for (Map.Entry exiledByController : playersWithTargets.entrySet()) { - swineToken.putOntoBattlefield(exiledByController.getValue(), game, source.getSourceId(), exiledByController.getKey()); + swineToken.putOntoBattlefield(exiledByController.getValue(), + game, source.getSourceId(), exiledByController.getKey()); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfThirst.java b/Mage.Sets/src/mage/cards/c/CurseOfThirst.java index 97c1c9c1eac..05e564079e5 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfThirst.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfThirst.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.Ability; @@ -31,10 +30,9 @@ import java.util.UUID; public final class CurseOfThirst extends CardImpl { public CurseOfThirst(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}"); this.subtype.add(SubType.AURA, SubType.CURSE); - // Enchant player TargetPlayer auraTarget = new TargetPlayer(); this.getSpellAbility().addTarget(auraTarget); @@ -91,7 +89,8 @@ class CurseOfThirstAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "At the beginning of enchanted player's upkeep, Curse of Thirst deals damage to that player equal to the number of Curses attached to him or her."; + return "At the beginning of enchanted player's upkeep, Curse of Thirst " + + "deals damage to that player equal to the number of Curses attached to him or her."; } } @@ -108,10 +107,11 @@ class CursesAttachedCount implements DynamicValue { if (enchantment != null && enchantment.getAttachedTo() != null) { Player player = game.getPlayer(enchantment.getAttachedTo()); if (player != null) { - for (UUID attachmentId: player.getAttachments()) { + for (UUID attachmentId : player.getAttachments()) { Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.hasSubtype(SubType.CURSE, game)) + if (attachment != null && attachment.hasSubtype(SubType.CURSE, game)) { count++; + } } } } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java b/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java index 318a99d37e8..e12fb3e6da9 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -130,7 +129,8 @@ class CurseOfVengeancePlayerLosesTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "When enchanted player loses the game, you gain X life and draw X cards, where X is the number of spite counters on {this}"; + return "When enchanted player loses the game, you gain X life and " + + "draw X cards, where X is the number of spite counters on {this}"; } } @@ -138,7 +138,8 @@ class CurseOfVengeanceDrawLifeEffect extends OneShotEffect { public CurseOfVengeanceDrawLifeEffect() { super(Outcome.Benefit); - staticText = "you gain X life and draw X cards, where X is the number of spite counters on {this}"; + staticText = "you gain X life and draw X cards, where X is the " + + "number of spite counters on {this}"; } public CurseOfVengeanceDrawLifeEffect(final CurseOfVengeanceDrawLifeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java index 439a921abd7..bad33f9ba52 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigE.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigE.java @@ -129,7 +129,7 @@ class UrzasHotTubPredicate implements Predicate { } private boolean sharesWordWithName(String str) { - if (referenceName == null || referenceName == "") { + if (referenceName == null || referenceName.equals("")) { return false; } String[] arr = referenceName.split("\\s+"); From 0a2c81ad7b710dfc4f3ec88504bf273e2a2ca740 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Sat, 4 May 2019 14:08:27 -0700 Subject: [PATCH 38/76] DOM set codes get exported as DAR for MTGA. --- .../cards/decks/exporter/MtgArenaDeckExporter.java | 13 +++++++++---- .../decks/exporter/MtgArenaDeckExporterTest.java | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporter.java b/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporter.java index 220637ecff6..68073ae2ccc 100644 --- a/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporter.java +++ b/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporter.java @@ -1,5 +1,6 @@ package mage.cards.decks.exporter; +import com.google.common.collect.ImmutableMap; import mage.cards.decks.DeckCardInfo; import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckFileFilter; @@ -13,9 +14,11 @@ import java.util.*; */ public class MtgArenaDeckExporter extends DeckExporter { - private final String ext = "mtga"; - private final String description = "MTG Arena's deck format (*.mtga)"; - private final FileFilter fileFilter = new DeckFileFilter(ext, description); + private static final String ext = "mtga"; + private static final String description = "MTG Arena's deck format (*.mtga)"; + private static final FileFilter fileFilter = new DeckFileFilter(ext, description); + + private static final Map SET_CODE_REPLACEMENTS = ImmutableMap.of("DOM", "DAR"); @Override public void writeDeck(PrintWriter out, DeckCardLists deck) { @@ -33,7 +36,9 @@ public class MtgArenaDeckExporter extends DeckExporter { private List prepareCardsList(List sourceCards, Map amount, String prefix) { List res = new ArrayList<>(); for (DeckCardInfo card : sourceCards) { - String name = card.getCardName() + " (" + card.getSetCode().toUpperCase(Locale.ENGLISH) + ") " + card.getCardNum(); + String setCode = card.getSetCode().toUpperCase(Locale.ENGLISH); + setCode = SET_CODE_REPLACEMENTS.getOrDefault(setCode, setCode); + String name = card.getCardName() + " (" + setCode + ") " + card.getCardNum(); String code = prefix + name; int curAmount = amount.getOrDefault(code, 0); if (curAmount == 0) { diff --git a/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporterTest.java b/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporterTest.java index 773a0d19a38..af7666a45d9 100644 --- a/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporterTest.java +++ b/Mage/src/main/java/mage/cards/decks/exporter/MtgArenaDeckExporterTest.java @@ -22,6 +22,7 @@ public class MtgArenaDeckExporterTest { deck.getCards().add(new DeckCardInfo("Plains", "2", "RNA", 3)); deck.getCards().add(new DeckCardInfo("Plains", "2", "RNA", 5)); // must combine deck.getCards().add(new DeckCardInfo("Mountain", "3", "RNA", 1)); + deck.getCards().add(new DeckCardInfo("Goblin Chainwhirler", "129", "DOM", 4)); deck.getSideboard().add(new DeckCardInfo("Island", "1", "RNA", 2)); deck.getSideboard().add(new DeckCardInfo("Island", "1", "RNA", 5)); // must combine deck.getSideboard().add(new DeckCardInfo("Mountain", "2", "RNA", 3)); @@ -30,6 +31,7 @@ public class MtgArenaDeckExporterTest { assertEquals("2 Forest (RNA) 1" + System.lineSeparator() + "8 Plains (RNA) 2" + System.lineSeparator() + "1 Mountain (RNA) 3" + System.lineSeparator() + + "4 Goblin Chainwhirler (DAR) 129" + System.lineSeparator() + System.lineSeparator() + "7 Island (RNA) 1" + System.lineSeparator() + "3 Mountain (RNA) 2" + System.lineSeparator(), From dccea47889e0bd22478309ebd3fc14315d85cbc2 Mon Sep 17 00:00:00 2001 From: Derek Monturo Date: Sun, 5 May 2019 05:39:27 -0400 Subject: [PATCH 39/76] UT for Kiora Behemoth untap ability --- .gitignore | 1 + .../mage/test/cards/single/war/KioraTest.java | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java diff --git a/.gitignore b/.gitignore index 15d874097cc..6246ef7be5e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ Mage.Server.Plugins/Mage.Game.FreeForAll/target Mage.Server.Plugins/Mage.Game.MomirDuel/target Mage.Server.Plugins/Mage.Game.MomirGame/target/ Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/target +Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/target/ Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/target/ Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/target diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java new file mode 100644 index 00000000000..18ba5479c25 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/KioraTest.java @@ -0,0 +1,50 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.mage.test.cards.single.war; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author escplan9 + */ +public class KioraTest extends CardTestPlayerBase { + + /* + {2} {U/G} + 7 loyalty + Whenever a creature with power 4 or greater enters the battlefield, draw a card. + [-1]: Untap target permanent. + */ + public final String kiora = "Kiora, Behemoth Beckoner"; + + + @Test + public void kioraUntapLand() { + + addCard(Zone.BATTLEFIELD, playerA, kiora); + addCard(Zone.BATTLEFIELD, playerA, "Bronze Sable"); // {2} 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.HAND, playerA, "Giant Growth"); + + // cast a spell to tap the only land + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", "Bronze Sable"); + + // untap that only land + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-1: Untap target permanent", "Forest"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Giant Growth", 1); + assertHandCount(playerA, 0); + assertPowerToughness(playerA, "Bronze Sable", 5, 4); // giant growthed 2/1 + 3/3 = 5/4 + assertCounterCount(playerA, kiora, CounterType.LOYALTY, 6); + assertTapped("Forest", false); // Kiora's untap + } +} \ No newline at end of file From 1648b84d884e164c1def6d1c51587c1b441caab8 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 5 May 2019 15:48:31 +0400 Subject: [PATCH 40/76] Fixed missing developers list in about form; --- .../java/mage/client/dialog/AboutDialog.form | 53 +++++++++++------- .../java/mage/client/dialog/AboutDialog.java | 56 +++++++++++-------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.form b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.form index 3055978fc8e..3fb27cbf15f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.form @@ -23,26 +23,26 @@ - + - - + + - - - + + + - + @@ -60,10 +60,10 @@ - + + + - - @@ -94,17 +94,7 @@ - - - - - - - - - - - + @@ -115,5 +105,26 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java index c9b8c1165c4..d93a3bf714d 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java @@ -6,15 +6,20 @@ import mage.utils.MageVersion; import javax.swing.*; import java.awt.event.KeyEvent; - /** * @author JayDi85 */ public class AboutDialog extends MageDialog { + private static String devsList = "BetaSteward, Noxx, Eugen.Rivniy, North, LevelX2, " + + "Jeff, Plopman, dustinconrad, emerald000, fireshoes, lunaskyrise, " + + "mnapoleon, jgod, LoneFox, drmDev, spjspj, TheElk801, L_J, JayDi85, " + + "jmharmon, Ketsuban, hitch17"; + public AboutDialog() { initComponents(); this.modal = false; + panelDevs.setText(devsList + " and hundreds of other developers from https://github.com/magefree/mage/graphs/contributors"); } public void showDialog(MageVersion version) { @@ -54,9 +59,10 @@ public class AboutDialog extends MageDialog { jLabel1 = new javax.swing.JLabel(); lblVersion = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); - jLabel4 = new javax.swing.JLabel(); btnWhatsNew = new javax.swing.JButton(); + scrollDevs = new javax.swing.JScrollPane(); + panelDevs = new javax.swing.JTextPane(); + labelDevs = new javax.swing.JLabel(); setMaximizable(true); setTitle("About XMage"); @@ -72,11 +78,7 @@ public class AboutDialog extends MageDialog { lblVersion.setText("0.0.0"); - jLabel2.setText("Courtesy: BetaSteward@googlemail.com. Site: http://XMage.de/"); - - jLabel3.setText("Devs: BetaSteward, Noxx, Eugen.Rivniy, North, LevelX2, Jeff, Plopman, dustinconrad, emerald000.,"); - - jLabel4.setText("fireshoes, lunaskyrise, mnapoleon, jgod, LoneFox."); + jLabel2.setText("Courtesy: BetaSteward@googlemail.com. Site: http://xmage.de/"); btnWhatsNew.setText("What's new"); btnWhatsNew.addActionListener(new java.awt.event.ActionListener() { @@ -85,27 +87,34 @@ public class AboutDialog extends MageDialog { } }); + scrollDevs.setBorder(null); + + panelDevs.setEditable(false); + scrollDevs.setViewportView(panelDevs); + + labelDevs.setText("Developers:"); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollDevs) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(btnWhatsNew, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(btnOk, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(jLabel4, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(lblVersion)) - .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING)) - .addGap(0, 0, Short.MAX_VALUE))) + .addComponent(jLabel2) + .addComponent(labelDevs)) + .addGap(0, 80, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( @@ -118,10 +127,10 @@ public class AboutDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelDevs) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(scrollDevs, javax.swing.GroupLayout.DEFAULT_SIZE, 161, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel4) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 68, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnOk, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnWhatsNew, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)) @@ -144,9 +153,10 @@ public class AboutDialog extends MageDialog { private javax.swing.JButton btnWhatsNew; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JLabel jLabel4; + private javax.swing.JLabel labelDevs; private javax.swing.JLabel lblVersion; + private javax.swing.JTextPane panelDevs; + private javax.swing.JScrollPane scrollDevs; // End of variables declaration//GEN-END:variables } From e333aa2d45057ca10f3f9587ed62e71b2e35cab2 Mon Sep 17 00:00:00 2001 From: Derek Monturo Date: Sun, 5 May 2019 12:34:15 -0400 Subject: [PATCH 41/76] UTs added for Challenger Troll block restrictions. closes #5730 --- .../requirement/BlockRequirementTest.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java index 85bb3f87a38..d7a8819c87d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/requirement/BlockRequirementTest.java @@ -225,4 +225,77 @@ public class BlockRequirementTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Dimensional Infiltrator", 1); assertGraveyardCount(playerB, "Llanowar Elves", 1); } + + /* + Reported bug: Challenger Troll on field not enforcing block restrictions + */ + @Test + public void testChallengerTrollTryBlockWithMany() { + /* + Challenger Troll {4}{G} - 6/5 + Creature — Troll + Each creature you control with power 4 or greater can’t be blocked by more than one creature. + */ + String cTroll = "Challenger Troll"; + + String bSable = "Bronze Sable"; // {2} 2/1 + String hGiant = "Hill Giant"; // {3}{R} 3/3 + + addCard(Zone.BATTLEFIELD, playerA, cTroll); + addCard(Zone.BATTLEFIELD, playerB, bSable); + addCard(Zone.BATTLEFIELD, playerB, hGiant); + + attack(1, playerA, cTroll); + + // only 1 should be able to block it since Troll >=4 power block restriction + block(1, playerB, bSable, cTroll); + block(1, playerB, hGiant, cTroll); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + try { + execute(); + fail("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + assertEquals("Challenger Troll is blocked by 2 creature(s). It can only be blocked by 1 or less.", e.getMessage()); + } + } + + /* + Reported bug: Challenger Troll on field not enforcing block restrictions + */ + @Test + public void testChallengerTrollAndOtherFourPowerCreaturesBlocks() { + /* + Challenger Troll {4}{G} - 6/5 + Creature — Troll + Each creature you control with power 4 or greater can’t be blocked by more than one creature. + */ + String cTroll = "Challenger Troll"; + String bHulk = "Bloom Hulk"; // {3}{G} 4/4 ETB: proliferate + + String bSable = "Bronze Sable"; // {2} 2/1 + String hGiant = "Hill Giant"; // {3}{R} 3/3 + + addCard(Zone.BATTLEFIELD, playerA, cTroll); + addCard(Zone.BATTLEFIELD, playerA, bHulk); + addCard(Zone.BATTLEFIELD, playerB, bSable); + addCard(Zone.BATTLEFIELD, playerB, hGiant); + + attack(1, playerA, cTroll); + attack(1, playerA, bHulk); + + // only 1 should be able to block Bloom Hulk since >=4 power and Troll on field + block(1, playerB, bSable, bHulk); + block(1, playerB, hGiant, bHulk); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + try { + execute(); + fail("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + assertEquals("Bloom Hulk is blocked by 2 creature(s). It can only be blocked by 1 or less.", e.getMessage()); + } + } } From 7089a09061670dc1576320b4d2b211bb327e0704 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 6 May 2019 01:41:28 +0400 Subject: [PATCH 42/76] * Standard Bearer - fixed that it can broke spells on false Flagbearer checks (#5784); --- Mage/src/main/java/mage/target/Targets.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/target/Targets.java b/Mage/src/main/java/mage/target/Targets.java index 64c67c104a2..7b3af1ca0ca 100644 --- a/Mage/src/main/java/mage/target/Targets.java +++ b/Mage/src/main/java/mage/target/Targets.java @@ -66,7 +66,7 @@ public class Targets extends ArrayList { if (!canChoose(source.getSourceId(), playerId, game)) { return false; } - int state = game.bookmarkState(); + //int state = game.bookmarkState(); while (!isChosen()) { Target target = this.getUnchosen().get(0); UUID targetController = playerId; @@ -93,7 +93,7 @@ public class Targets extends ArrayList { // Check if there are some rules for targets are violated, if so reset the targets and start again if (this.getUnchosen().isEmpty() && game.replaceEvent(new GameEvent(GameEvent.EventType.TARGETS_VALID, source.getSourceId(), source.getSourceId(), source.getControllerId()), source)) { - game.restoreState(state, "Targets"); + //game.restoreState(state, "Targets"); clearChosen(); } } From e77b21f4e92239261deb9b0e501c4db366372d88 Mon Sep 17 00:00:00 2001 From: Ingmar Goudt Date: Mon, 6 May 2019 11:18:53 +0200 Subject: [PATCH 43/76] fix gonti NPE #5748 --- .../src/mage/cards/g/GontiLordOfLuxury.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GontiLordOfLuxury.java b/Mage.Sets/src/mage/cards/g/GontiLordOfLuxury.java index cae0b235300..0755e3a2824 100644 --- a/Mage.Sets/src/mage/cards/g/GontiLordOfLuxury.java +++ b/Mage.Sets/src/mage/cards/g/GontiLordOfLuxury.java @@ -184,7 +184,15 @@ class GontiLordOfLuxurySpendAnyManaEffect extends AsThoughEffectImpl implements @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - objectId = game.getCard(objectId).getMainCard().getId(); // for split cards + Card theCard = game.getCard(objectId); + if(theCard == null){ + return false; + } + Card mainCard = theCard.getMainCard(); + if(mainCard == null){ + return false; + } + objectId = mainCard.getId(); // for split cards if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget()) && game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) { if (affectedControllerId.equals(source.getControllerId())) { @@ -229,7 +237,15 @@ class GontiLordOfLuxuryLookEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - objectId = game.getCard(objectId).getMainCard().getId(); // for split cards + Card theCard = game.getCard(objectId); + if(theCard == null){ + return false; + } + Card mainCard = theCard.getMainCard(); + if(mainCard == null){ + return false; + } + objectId = mainCard.getId(); // for split cards if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { Player controller = game.getPlayer(source.getControllerId()); From b9b8415c997b57c4410c8b144388419a418d78de Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 6 May 2019 17:03:56 +0400 Subject: [PATCH 44/76] Extra tests to catch card creation errors; --- Mage.Sets/src/mage/cards/e/Expropriate.java | 10 ++++--- .../utils/ExportJsonGameplayDataTest.java | 12 +++++++-- .../java/mage/verify/VerifyCardDataTest.java | 27 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/Expropriate.java b/Mage.Sets/src/mage/cards/e/Expropriate.java index 81761c64af3..318fa8342f4 100644 --- a/Mage.Sets/src/mage/cards/e/Expropriate.java +++ b/Mage.Sets/src/mage/cards/e/Expropriate.java @@ -1,8 +1,5 @@ package mage.cards.e; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; @@ -22,6 +19,10 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * @author JRHerlehy */ @@ -30,7 +31,8 @@ public final class Expropriate extends CardImpl { public Expropriate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{U}{U}"); - // Council's dilemma — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it. Exile Expropriate + // Council's dilemma — Starting with you, each player votes for time or money. For each time vote, + // take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it. Exile Expropriate this.getSpellAbility().addEffect(new ExpropriateDilemmaEffect()); this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); } diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/ExportJsonGameplayDataTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/ExportJsonGameplayDataTest.java index 67ef4b64317..624c07c13f9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/ExportJsonGameplayDataTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/ExportJsonGameplayDataTest.java @@ -37,8 +37,16 @@ public class ExportJsonGameplayDataTest { Collection sets = Sets.getInstance().values(); for (ExpansionSet set : sets) { for (ExpansionSet.SetCardInfo setInfo : set.getSetCardInfo()) { - cards.add(CardImpl.createCard(setInfo.getCardClass(), new CardSetInfo(setInfo.getName(), set.getCode(), - setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()))); + // catch cards creation errors and report (e.g. on wrong card code) + try { + Card card = CardImpl.createCard(setInfo.getCardClass(), new CardSetInfo(setInfo.getName(), set.getCode(), + setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo())); + if (card != null) { + cards.add(card); + } + } catch (Throwable e) { + logger.error("Can't create card " + setInfo.getName() + ": " + e.getMessage(), e); + } } JsonObject res = new JsonObject(); diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 36ce86fb2d5..4ea8fb15cb9 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -15,6 +15,7 @@ import mage.game.draft.RateCard; import mage.game.permanent.token.Token; import mage.game.permanent.token.TokenImpl; import mage.watchers.Watcher; +import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; @@ -41,6 +42,8 @@ import java.util.stream.Collectors; */ public class VerifyCardDataTest { + private static final Logger logger = Logger.getLogger(VerifyCardDataTest.class); + // right now this is very noisy, and not useful enough to make any assertions on private static final boolean CHECK_SOURCE_TOKENS = false; @@ -971,4 +974,28 @@ public class VerifyCardDataTest { } } } + + @Test + public void testCardsCreatingAndConstructorErrors() { + int errorsCount = 0; + Collection sets = Sets.getInstance().values(); + for (ExpansionSet set : sets) { + for (ExpansionSet.SetCardInfo setInfo : set.getSetCardInfo()) { + // catch cards creation errors and report (e.g. on wrong card code or construction checks fail) + try { + Card card = CardImpl.createCard(setInfo.getCardClass(), new CardSetInfo(setInfo.getName(), set.getCode(), + setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo())); + if (card == null) { + errorsCount++; + } + } catch (Throwable e) { + logger.error("Can't create card " + setInfo.getName() + ": " + e.getMessage(), e); + } + } + } + + if (errorsCount > 0) { + Assert.fail("Founded " + errorsCount + " broken cards, look at logs for stack error"); + } + } } From e307f92db20891832976a99b9cee074f65bf0713 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 6 May 2019 10:38:37 -0500 Subject: [PATCH 45/76] - small clarity fixes of Niv-Mizzet Reborn and Ugin, the Ineffable. --- Mage.Sets/src/mage/cards/n/NivMizzetReborn.java | 12 ++++++++---- Mage.Sets/src/mage/cards/u/UginTheIneffable.java | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/n/NivMizzetReborn.java b/Mage.Sets/src/mage/cards/n/NivMizzetReborn.java index 2e034d48786..fe55aa3aae4 100644 --- a/Mage.Sets/src/mage/cards/n/NivMizzetReborn.java +++ b/Mage.Sets/src/mage/cards/n/NivMizzetReborn.java @@ -113,9 +113,9 @@ class NivMizzetRebornEffect extends OneShotEffect { NivMizzetRebornEffect() { super(Outcome.Benefit); - staticText = "reveal the top ten cards of your library. For each color pair, " + - "choose a card that's exactly those colors from among them. " + - "Put the chosen cards into your hand and the rest on the bottom of your library in a random order."; + staticText = "reveal the top ten cards of your library. For each color pair, " + + "choose a card that's exactly those colors from among them. " + + "Put the chosen cards into your hand and the rest on the bottom of your library in a random order."; } private NivMizzetRebornEffect(final NivMizzetRebornEffect effect) { @@ -153,7 +153,11 @@ class NivMizzetRebornEffect extends OneShotEffect { } cards.removeAll(cards2); player.putCardsOnBottomOfLibrary(cards, game, source, false); - player.moveCards(cards2, Zone.HAND, source, game); + if (player.moveCards(cards2, Zone.HAND, source, game)) { + for (Card card : cards2.getCards(game)) { + game.informPlayers(player.getName() + " chose " + card.getName() + " and put it into his or her hand."); + } + } return true; } } diff --git a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java index 9a589358775..18a511fffac 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java +++ b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java @@ -116,7 +116,8 @@ class UginTheIneffableEffect extends OneShotEffect { for (UUID addedTokenId : effect.getLastAddedTokenIds()) { // display referenced exiled face-down card on token - SimpleStaticAbility sa = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("Referenced object: " + card.getIdName())); + SimpleStaticAbility sa = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("Referenced object: " + + card.getId().toString().substring(0, 3))); GainAbilityTargetEffect gainAbilityEffect = new GainAbilityTargetEffect(sa, Duration.WhileOnBattlefield); gainAbilityEffect.setTargetPointer(new FixedTarget(addedTokenId)); game.addEffect(gainAbilityEffect, source); From 051c3c4ac050f98da7f4838c28d4b172fff075ad Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 6 May 2019 20:15:39 +0400 Subject: [PATCH 46/76] UI: added test card render modes dialog (available from debug menu); --- .../src/main/java/mage/client/MageFrame.form | 8 + .../src/main/java/mage/client/MageFrame.java | 31 ++- .../client/dialog/TestCardRenderDialog.form | 77 ++++++ .../client/dialog/TestCardRenderDialog.java | 223 ++++++++++++++++++ .../mage/test/mulligan/MulliganTestBase.java | 222 +---------------- .../main/java/mage/players/StubPlayer.java | 221 +++++++++++++++++ 6 files changed, 559 insertions(+), 223 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form create mode 100644 Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java create mode 100644 Mage/src/main/java/mage/players/StubPlayer.java diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.form b/Mage.Client/src/main/java/mage/client/MageFrame.form index 1c0968259a3..40d70fa386a 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.form +++ b/Mage.Client/src/main/java/mage/client/MageFrame.form @@ -16,6 +16,14 @@ + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 10d85cbc47e..08c29ba8ad2 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -827,6 +827,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { popupDebug = new javax.swing.JPopupMenu(); menuDebugTestModalDialog = new javax.swing.JMenuItem(); + menuDebugTestCardRenderModesDialog = new javax.swing.JMenuItem(); desktopPane = new MageJDesktop(); mageToolbar = new javax.swing.JToolBar(); btnPreferences = new javax.swing.JButton(); @@ -857,6 +858,14 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { }); popupDebug.add(menuDebugTestModalDialog); + menuDebugTestCardRenderModesDialog.setText("Test Card Render Modes"); + menuDebugTestCardRenderModesDialog.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + menuDebugTestCardRenderModesDialogActionPerformed(evt); + } + }); + popupDebug.add(menuDebugTestCardRenderModesDialog); + setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); setMinimumSize(new java.awt.Dimension(1024, 768)); @@ -995,16 +1004,16 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE) - .addComponent(mageToolbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE) + .addComponent(mageToolbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(mageToolbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(2, 2, 2) - .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 145, Short.MAX_VALUE)) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(mageToolbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(2, 2, 2) + .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 145, Short.MAX_VALUE)) ); pack(); @@ -1079,6 +1088,11 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { popupDebug.show(evt.getComponent(), 0, evt.getComponent().getHeight()); }//GEN-LAST:event_btnDebugMouseClicked + private void menuDebugTestCardRenderModesDialogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuDebugTestCardRenderModesDialogActionPerformed + final TestCardRenderDialog dialog = new TestCardRenderDialog(); + dialog.showDialog(); + }//GEN-LAST:event_menuDebugTestCardRenderModesDialogActionPerformed + public void downloadImages() { DownloadPicturesService.startDownload(); } @@ -1328,6 +1342,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { private javax.swing.JToolBar.Separator jSeparatorImages; private javax.swing.JToolBar.Separator jSeparatorSymbols; private javax.swing.JToolBar mageToolbar; + private javax.swing.JMenuItem menuDebugTestCardRenderModesDialog; private javax.swing.JMenuItem menuDebugTestModalDialog; private javax.swing.JPopupMenu popupDebug; private javax.swing.JToolBar.Separator separatorDebug; diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form new file mode 100644 index 00000000000..bdc2ec168ac --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form @@ -0,0 +1,77 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java new file mode 100644 index 00000000000..7833195372c --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -0,0 +1,223 @@ +package mage.client.dialog; + +import mage.cards.Card; +import mage.cards.CardGraphicInfo; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.decks.Deck; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.cards.repository.ExpansionInfo; +import mage.cards.repository.ExpansionRepository; +import mage.client.MageFrame; +import mage.client.cards.BigCard; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.Game; +import mage.game.GameImpl; +import mage.game.match.MatchType; +import mage.game.mulligan.Mulligan; +import mage.game.mulligan.VancouverMulligan; +import mage.game.permanent.PermanentCard; +import mage.players.Player; +import mage.players.StubPlayer; +import mage.view.CardsView; +import mage.view.PermanentView; +import org.apache.log4j.Logger; + +import javax.swing.*; +import java.awt.event.KeyEvent; +import java.util.UUID; + +/** + * @author JayDi85 + */ +public class TestCardRenderDialog extends MageDialog { + + private static final Logger logger = Logger.getLogger(TestCardRenderDialog.class); + + public TestCardRenderDialog() { + initComponents(); + } + + public void showDialog() { + this.setModal(false); + getRootPane().setDefaultButton(buttonCancel); + + // windows settings + MageFrame.getDesktop().remove(this); + if (this.isModal()) { + MageFrame.getDesktop().add(this, JLayeredPane.MODAL_LAYER); + } else { + MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); + } + this.makeWindowCentered(); + + // Close on "ESC" + registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + this.setVisible(true); + } + + private void onCancel() { + this.removeDialog(); + } + + private PermanentView createCard(Game game, UUID controllerId, String code, String cardNumber, int damage) { + CardInfo cardInfo = CardRepository.instance.findCard(code, cardNumber); + ExpansionInfo setInfo = ExpansionRepository.instance.getSetByCode(code); + CardSetInfo testSet = new CardSetInfo(cardInfo.getName(), setInfo.getCode(), cardNumber, cardInfo.getRarity(), + new CardGraphicInfo(cardInfo.getFrameStyle(), cardInfo.usesVariousArt())); + Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); + + PermanentCard perm = new PermanentCard(card, controllerId, game); + if (damage > 0) { + perm.damage(damage, null, game); + } + PermanentView cardView = new PermanentView(perm, card, controllerId, game); + cardView.setInViewerOnly(true); + + return cardView; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonCancel = new javax.swing.JButton(); + cardArea1 = new mage.client.cards.CardArea(); + jButton1 = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + buttonCancel.setText("Close"); + buttonCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + buttonCancelActionPerformed(evt); + } + }); + + jButton1.setText("jButton1"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 578, Short.MAX_VALUE) + .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(cardArea1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(jButton1) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(jButton1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 88, Short.MAX_VALUE) + .addComponent(cardArea1, javax.swing.GroupLayout.PREFERRED_SIZE, 327, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void buttonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCancelActionPerformed + onCancel(); + }//GEN-LAST:event_buttonCancelActionPerformed + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + Game game = new TestGame(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20); + Player player = new StubPlayer("player1", RangeOfInfluence.ALL); + Deck deck = new Deck(); + game.addPlayer(player, deck); + + BigCard big = new BigCard(); + CardsView view = new CardsView(); + PermanentView card; + card = createCard(game, player.getId(), "RNA", "263", 0); // mountain + view.put(card.getId(), card); + card = createCard(game, player.getId(), "RNA", "185", 0); // Judith, the Scourge Diva + view.put(card.getId(), card); + card = createCard(game, player.getId(), "RNA", "14", 1); // Knight of Sorrows + view.put(card.getId(), card); + + cardArea1.loadCards(view, big, null); + }//GEN-LAST:event_jButton1ActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton buttonCancel; + private mage.client.cards.CardArea cardArea1; + private javax.swing.JButton jButton1; + // End of variables declaration//GEN-END:variables +} + +class TestGame extends GameImpl { + + private int numPlayers; + + public TestGame(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { + super(attackOption, range, mulligan, startLife); + } + + public TestGame(final TestGame game) { + super(game); + this.numPlayers = game.numPlayers; + } + + @Override + public MatchType getGameType() { + return new TestGameType(); + } + + @Override + public int getNumPlayers() { + return numPlayers; + } + + @Override + public TestGame copy() { + return new TestGame(this); + } + +} + +class TestGameType extends MatchType { + + public TestGameType() { + this.name = "Test Game Type"; + this.maxPlayers = 10; + this.minPlayers = 3; + this.numTeams = 0; + this.useAttackOption = true; + this.useRange = true; + this.sideboardingAllowed = true; + } + + protected TestGameType(final TestGameType matchType) { + super(matchType); + } + + @Override + public TestGameType copy() { + return new TestGameType(this); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/mulligan/MulliganTestBase.java b/Mage.Tests/src/test/java/org/mage/test/mulligan/MulliganTestBase.java index 711d9792b7b..7f6dd82569e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/mulligan/MulliganTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/mulligan/MulliganTestBase.java @@ -1,41 +1,21 @@ - package org.mage.test.mulligan; -import mage.MageItem; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.Modes; -import mage.abilities.TriggeredAbility; -import mage.abilities.costs.VariableCost; -import mage.abilities.costs.mana.ManaCost; -import mage.cards.Card; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.cards.basiclands.Forest; import mage.cards.decks.Deck; -import mage.choices.Choice; -import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.game.Game; import mage.game.GameOptions; import mage.game.TwoPlayerDuel; -import mage.game.combat.CombatGroup; -import mage.game.draft.Draft; -import mage.game.match.Match; import mage.game.mulligan.Mulligan; import mage.game.mulligan.MulliganType; -import mage.game.permanent.Permanent; -import mage.game.tournament.Tournament; -import mage.players.Player; -import mage.players.PlayerImpl; -import mage.target.Target; -import mage.target.TargetAmount; -import mage.target.TargetCard; -import mage.target.TargetPlayer; +import mage.players.StubPlayer; import org.apache.log4j.Logger; -import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkArgument; @@ -43,7 +23,6 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableSet; -import static java.util.stream.Collectors.toList; import static mage.constants.MultiplayerAttackOption.LEFT; import static mage.constants.RangeOfInfluence.ONE; import static mage.constants.Rarity.LAND; @@ -167,7 +146,8 @@ public class MulliganTestBase { return deck; } - interface Step {} + interface Step { + } interface MulliganStep extends Step { boolean mulligan(); @@ -242,192 +222,4 @@ public class MulliganTestBase { } - static class StubPlayer extends PlayerImpl implements Player { - - public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { - if (target instanceof TargetPlayer) { - for (Player player : game.getPlayers().values()) { - if (player.getId().equals(getId()) && target.canTarget(getId(), game)) { - target.add(player.getId(), game); - return true; - } - } - } - return false; - } - - public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { - cards.getCards(game).stream().map(MageItem::getId).forEach(cardId -> target.add(cardId, game)); - return true; - } - - @Override - public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { - if ("cards to PUT on the BOTTOM of your library (Discard for Mulligan)".equals(target.getFilter().getMessage())) { - chooseDiscardBottom(game, target.getMinNumberOfTargets(), cards.getCards(game) - .stream().map(MageItem::getId).collect(toList())).forEach(cardId -> target.add(cardId, game)); - } else { - UUID cardId = getOnlyElement(cards.getCards(game)).getId(); - if (chooseScry(game, cardId)) { - target.add(cardId, game); - return true; - } - } - return false; - } - - public List chooseDiscardBottom(Game game, int count, List cardIds) { - return cardIds.subList(0, count); - } - - public boolean chooseScry(Game game, UUID cardId) { - return false; - } - - @Override - public void shuffleLibrary(Ability source, Game game) { - - } - - public StubPlayer(String name, RangeOfInfluence range) { - super(name, range); - } - - @Override - public void abort() { - - } - - @Override - public void skip() { - - } - - @Override - public Player copy() { - return null; - } - - @Override - public boolean priority(Game game) { - return false; - } - - @Override - public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { - return false; - } - - @Override - public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { - return false; - } - - @Override - public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { - return false; - } - - @Override - public boolean chooseMulligan(Game game) { - return false; - } - - @Override - public boolean chooseUse(Outcome outcome, String message, Ability source, Game game) { - return false; - } - - @Override - public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) { - return false; - } - - @Override - public boolean choose(Outcome outcome, Choice choice, Game game) { - return false; - } - - @Override - public boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game) { - return false; - } - - @Override - public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) { - return false; - } - - @Override - public int announceXMana(int min, int max, String message, Game game, Ability ability) { - return 0; - } - - @Override - public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) { - return 0; - } - - @Override - public int chooseReplacementEffect(Map abilityMap, Game game) { - return 0; - } - - @Override - public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { - return null; - } - - @Override - public Mode chooseMode(Modes modes, Ability source, Game game) { - return null; - } - - @Override - public void selectAttackers(Game game, UUID attackingPlayerId) { - - } - - @Override - public void selectBlockers(Game game, UUID defendingPlayerId) { - - } - - @Override - public UUID chooseAttackerOrder(List attacker, Game game) { - return null; - } - - @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { - return null; - } - - @Override - public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { - - } - - @Override - public int getAmount(int min, int max, String message, Game game) { - return 0; - } - - @Override - public void sideboard(Match match, Deck deck) { - - } - - @Override - public void construct(Tournament tournament, Deck deck) { - - } - - @Override - public void pickCard(List cards, Deck deck, Draft draft) { - - } - - } - } \ No newline at end of file diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java new file mode 100644 index 00000000000..04665fbbc2e --- /dev/null +++ b/Mage/src/main/java/mage/players/StubPlayer.java @@ -0,0 +1,221 @@ +package mage.players; + +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.Modes; +import mage.abilities.TriggeredAbility; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.mana.ManaCost; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.decks.Deck; +import mage.choices.Choice; +import mage.constants.Outcome; +import mage.constants.RangeOfInfluence; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.draft.Draft; +import mage.game.match.Match; +import mage.game.permanent.Permanent; +import mage.game.tournament.Tournament; +import mage.target.Target; +import mage.target.TargetAmount; +import mage.target.TargetCard; +import mage.target.TargetPlayer; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static java.util.stream.Collectors.toList; + +public class StubPlayer extends PlayerImpl implements Player { + + public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { + if (target instanceof TargetPlayer) { + for (Player player : game.getPlayers().values()) { + if (player.getId().equals(getId()) && target.canTarget(getId(), game)) { + target.add(player.getId(), game); + return true; + } + } + } + return false; + } + + public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { + cards.getCards(game).stream().map(MageItem::getId).forEach(cardId -> target.add(cardId, game)); + return true; + } + + @Override + public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { + if ("cards to PUT on the BOTTOM of your library (Discard for Mulligan)".equals(target.getFilter().getMessage())) { + chooseDiscardBottom(game, target.getMinNumberOfTargets(), cards.getCards(game) + .stream().map(MageItem::getId).collect(toList())).forEach(cardId -> target.add(cardId, game)); + } else { + UUID cardId = getOnlyElement(cards.getCards(game)).getId(); + if (chooseScry(game, cardId)) { + target.add(cardId, game); + return true; + } + } + return false; + } + + public List chooseDiscardBottom(Game game, int count, List cardIds) { + return cardIds.subList(0, count); + } + + public boolean chooseScry(Game game, UUID cardId) { + return false; + } + + @Override + public void shuffleLibrary(Ability source, Game game) { + + } + + public StubPlayer(String name, RangeOfInfluence range) { + super(name, range); + } + + @Override + public void abort() { + + } + + @Override + public void skip() { + + } + + @Override + public Player copy() { + return null; + } + + @Override + public boolean priority(Game game) { + return false; + } + + @Override + public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { + return false; + } + + @Override + public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { + return false; + } + + @Override + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + return false; + } + + @Override + public boolean chooseMulligan(Game game) { + return false; + } + + @Override + public boolean chooseUse(Outcome outcome, String message, Ability source, Game game) { + return false; + } + + @Override + public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) { + return false; + } + + @Override + public boolean choose(Outcome outcome, Choice choice, Game game) { + return false; + } + + @Override + public boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game) { + return false; + } + + @Override + public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) { + return false; + } + + @Override + public int announceXMana(int min, int max, String message, Game game, Ability ability) { + return 0; + } + + @Override + public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) { + return 0; + } + + @Override + public int chooseReplacementEffect(Map abilityMap, Game game) { + return 0; + } + + @Override + public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { + return null; + } + + @Override + public Mode chooseMode(Modes modes, Ability source, Game game) { + return null; + } + + @Override + public void selectAttackers(Game game, UUID attackingPlayerId) { + + } + + @Override + public void selectBlockers(Game game, UUID defendingPlayerId) { + + } + + @Override + public UUID chooseAttackerOrder(List attacker, Game game) { + return null; + } + + @Override + public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { + return null; + } + + @Override + public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { + + } + + @Override + public int getAmount(int min, int max, String message, Game game) { + return 0; + } + + @Override + public void sideboard(Match match, Deck deck) { + + } + + @Override + public void construct(Tournament tournament, Deck deck) { + + } + + @Override + public void pickCard(List cards, Deck deck, Draft draft) { + + } + +} \ No newline at end of file From 998044068d8363a394a3496e3e5c75f1bdfec4f1 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 6 May 2019 11:23:34 -0500 Subject: [PATCH 47/76] - small fix Guided Strike --- Mage.Sets/src/mage/cards/g/GuidedStrike.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GuidedStrike.java b/Mage.Sets/src/mage/cards/g/GuidedStrike.java index 486caf88f37..6df08e69568 100644 --- a/Mage.Sets/src/mage/cards/g/GuidedStrike.java +++ b/Mage.Sets/src/mage/cards/g/GuidedStrike.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -16,23 +15,25 @@ import mage.target.common.TargetCreaturePermanent; /** * * @author LoneFox - + * */ public final class GuidedStrike extends CardImpl { public GuidedStrike(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // Target creature gets +1/+0 and gains first strike until end of turn. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn); effect.setText("Target creature gets +1/+0"); this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gains first strike until end of turn"); this.getSpellAbility().addEffect(effect); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + } public GuidedStrike(final GuidedStrike card) { From c45f64bdecf3c49ed9d10b354810f570ab41640e Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 6 May 2019 23:16:58 +0400 Subject: [PATCH 48/76] UI: improved card render test dialog (added render mode and size choose); --- .../main/java/mage/client/cards/CardArea.java | 21 +- .../client/cards/CardDraggerGlassPane.java | 32 +- .../main/java/mage/client/cards/CardGrid.java | 756 ++++----- .../main/java/mage/client/cards/Cards.java | 3 +- .../java/mage/client/cards/CardsList.java | 1412 ++++++++--------- .../java/mage/client/cards/DraftGrid.java | 35 +- .../java/mage/client/cards/DragCardGrid.java | 44 +- .../components/ext/dlg/impl/ChoiceDialog.java | 3 +- .../components/ext/dlg/impl/StackDialog.java | 19 +- .../collection/viewer/MageBook.java | 5 +- .../mage/client/dialog/PreferencesDialog.java | 8 + .../client/dialog/TestCardRenderDialog.form | 74 +- .../client/dialog/TestCardRenderDialog.java | 214 ++- .../mage/client/game/BattlefieldPanel.java | 545 ++++--- .../java/mage/client/plugins/MagePlugins.java | 15 +- .../mage/client/plugins/impl/Plugins.java | 27 +- .../org/mage/plugins/card/CardPluginImpl.java | 24 +- .../mage/interfaces/plugin/CardPlugin.java | 20 +- 18 files changed, 1710 insertions(+), 1547 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/cards/CardArea.java b/Mage.Client/src/main/java/mage/client/cards/CardArea.java index c98be7e7b09..dfa36502609 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardArea.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardArea.java @@ -1,6 +1,7 @@ package mage.client.cards; import mage.cards.MageCard; +import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.ClientEventType; import mage.client.util.Event; @@ -30,6 +31,9 @@ public class CardArea extends JPanel implements MouseListener { private Dimension cardDimension; private int verticalCardOffset; + private int customRenderMode = -1; // custom render mode tests + private Dimension customCardSize = null; // custom size for tests + /** * Create the panel. */ @@ -62,7 +66,11 @@ public class CardArea extends JPanel implements MouseListener { } private void setGUISize() { - setCardDimension(GUISizeHelper.otherZonesCardDimension, GUISizeHelper.otherZonesCardVerticalOffset); + if (customCardSize != null) { + setCardDimension(customCardSize, GUISizeHelper.otherZonesCardVerticalOffset); + } else { + setCardDimension(GUISizeHelper.otherZonesCardDimension, GUISizeHelper.otherZonesCardVerticalOffset); + } } public void setCardDimension(Dimension dimension, int verticalCardOffset) { @@ -129,7 +137,8 @@ public class CardArea extends JPanel implements MouseListener { tmp.setAbility(card); // cross-reference, required for ability picker card = tmp; } - MageCard cardPanel = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true); + MageCard cardPanel = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true, + customRenderMode != -1 ? customRenderMode : PreferencesDialog.getRenderMode()); cardPanel.setBounds(rectangle); cardPanel.addMouseListener(this); @@ -265,6 +274,14 @@ public class CardArea extends JPanel implements MouseListener { } } + public void setCustomRenderMode(int customRenderMode) { + this.customRenderMode = customRenderMode; + } + + public void setCustomCardSize(Dimension customCardSize) { + this.customCardSize = customCardSize; + } + @Override public void mouseEntered(MouseEvent e) { } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java b/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java index e79ea3944c4..73c4a1dc090 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardDraggerGlassPane.java @@ -2,6 +2,7 @@ package mage.client.cards; import mage.cards.MageCard; import mage.client.MagePane; +import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.view.CardView; @@ -45,7 +46,7 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener currentRoot = SwingUtilities.getRootPane(c); // Pane - glassPane = (JComponent)currentRoot.getGlassPane(); + glassPane = (JComponent) currentRoot.getGlassPane(); glassPane.setLayout(null); glassPane.setOpaque(false); glassPane.setVisible(true); @@ -58,7 +59,7 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener if (rootMagePane == null) { throw new RuntimeException("CardDraggerGlassPane::beginDrag not in a MagePane?"); } else { - currentEventRootMagePane = (MagePane)rootMagePane; + currentEventRootMagePane = (MagePane) rootMagePane; } // Hook up events @@ -72,8 +73,8 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener currentCards = new ArrayList<>(source.dragCardList()); // Make a view for the first one and add it to us - dragView = Plugins.instance.getMageCard(currentCards.get(0), null, new Dimension(100, 140), null, true, false); - for (MouseListener l: dragView.getMouseListeners()) { + dragView = Plugins.instance.getMageCard(currentCards.get(0), null, new Dimension(100, 140), null, true, false, PreferencesDialog.getRenderMode()); + for (MouseListener l : dragView.getMouseListeners()) { dragView.removeMouseListener(l); } for (MouseMotionListener l : dragView.getMouseMotionListeners()) { @@ -95,7 +96,7 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener Component mouseOver = SwingUtilities.getDeepestComponentAt(currentEventRootMagePane, e.getX(), e.getY()); while (mouseOver != null) { if (mouseOver instanceof DragCardTarget) { - DragCardTarget target = (DragCardTarget)mouseOver; + DragCardTarget target = (DragCardTarget) mouseOver; MouseEvent targetEvent = SwingUtilities.convertMouseEvent(currentEventRootMagePane, e, mouseOver); if (target != currentDragTarget) { if (currentDragTarget != null) { @@ -116,7 +117,7 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener mouseOver = mouseOver.getParent(); } if (currentDragTarget != null) { - MouseEvent oldTargetEvent = SwingUtilities.convertMouseEvent(currentEventRootMagePane, e, (Component)currentDragTarget); + MouseEvent oldTargetEvent = SwingUtilities.convertMouseEvent(currentEventRootMagePane, e, (Component) currentDragTarget); currentDragTarget.dragCardExit(oldTargetEvent); } currentDragTarget = null; @@ -164,13 +165,22 @@ public class CardDraggerGlassPane implements MouseListener, MouseMotionListener } @Override - public void mouseClicked(MouseEvent e) {} + public void mouseClicked(MouseEvent e) { + } + @Override - public void mousePressed(MouseEvent e) {} + public void mousePressed(MouseEvent e) { + } + @Override - public void mouseEntered(MouseEvent e) {} + public void mouseEntered(MouseEvent e) { + } + @Override - public void mouseExited(MouseEvent e) {} + public void mouseExited(MouseEvent e) { + } + @Override - public void mouseMoved(MouseEvent e) {} + public void mouseMoved(MouseEvent e) { + } } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java index 4a094c6655c..bd4b6dbf76c 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java @@ -1,430 +1,430 @@ /* - * CardGrid.java - * - * Created on 30-Mar-2010, 9:25:40 PM - */ -package mage.client.cards; + * CardGrid.java + * + * Created on 30-Mar-2010, 9:25:40 PM + */ + package mage.client.cards; -import mage.cards.MageCard; -import mage.client.deckeditor.SortSetting; -import mage.client.plugins.impl.Plugins; -import mage.client.util.ClientEventType; -import mage.client.util.Event; -import mage.client.util.GUISizeHelper; -import mage.client.util.Listener; -import mage.utils.CardColorUtil; -import mage.view.CardView; -import mage.view.CardsView; -import org.mage.card.arcane.CardPanel; + import mage.cards.MageCard; + import mage.client.deckeditor.SortSetting; + import mage.client.dialog.PreferencesDialog; + import mage.client.plugins.impl.Plugins; + import mage.client.util.ClientEventType; + import mage.client.util.Event; + import mage.client.util.GUISizeHelper; + import mage.client.util.Listener; + import mage.utils.CardColorUtil; + import mage.view.CardView; + import mage.view.CardsView; + import org.mage.card.arcane.CardPanel; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.*; -import java.util.List; -import java.util.Map.Entry; + import java.awt.*; + import java.awt.event.MouseEvent; + import java.awt.event.MouseListener; + import java.util.List; + import java.util.*; + import java.util.Map.Entry; -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, ICardGrid { + /** + * @author BetaSteward_at_googlemail.com + */ + public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, ICardGrid { - protected final CardEventSource cardEventSource = new CardEventSource(); - protected BigCard bigCard; - protected UUID gameId; - private final Map cards = new HashMap<>(); - private Dimension cardDimension; + protected final CardEventSource cardEventSource = new CardEventSource(); + protected BigCard bigCard; + protected UUID gameId; + private final Map cards = new HashMap<>(); + private Dimension cardDimension; - /** - * Max amount of cards in card grid for which card images will be drawn. - * Done so to solve issue with memory for big piles of cards. - */ - public static final int MAX_IMAGES = 350; + /** + * Max amount of cards in card grid for which card images will be drawn. + * Done so to solve issue with memory for big piles of cards. + */ + public static final int MAX_IMAGES = 350; - public CardGrid() { - initComponents(); - setGUISize(); - setOpaque(false); - } + public CardGrid() { + initComponents(); + setGUISize(); + setOpaque(false); + } - public void clear() { - for (MouseListener ml : this.getMouseListeners()) { - this.removeMouseListener(ml); - } - this.clearCardEventListeners(); - this.clearCards(); - this.bigCard = null; - } + public void clear() { + for (MouseListener ml : this.getMouseListeners()) { + this.removeMouseListener(ml); + } + this.clearCardEventListeners(); + this.clearCards(); + this.bigCard = null; + } - public void changeGUISize() { - setGUISize(); - } + public void changeGUISize() { + setGUISize(); + } - private void setGUISize() { - cardDimension = GUISizeHelper.editorCardDimension; - } + private void setGUISize() { + cardDimension = GUISizeHelper.editorCardDimension; + } - @Override - public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { - this.loadCards(showCards, sortSetting, bigCard, gameId, true); - } + @Override + public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { + this.loadCards(showCards, sortSetting, bigCard, gameId, true); + } - @Override - public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId, boolean merge) { - boolean drawImage = showCards.size() <= MAX_IMAGES; - this.bigCard = bigCard; - this.gameId = gameId; - if (merge) { - for (CardView card : showCards.values()) { - if (!cards.containsKey(card.getId())) { - addCard(card, bigCard, gameId, drawImage); - } - } - for (Iterator> i = cards.entrySet().iterator(); i.hasNext();) { - Entry entry = i.next(); - if (!showCards.containsKey(entry.getKey())) { - removeCardImg(entry.getKey()); - i.remove(); - } - } - } else { - this.clearCards(); - for (CardView card : showCards.values()) { - addCard(card, bigCard, gameId, drawImage); - } - } - drawCards(sortSetting); - this.setVisible(true); - } + @Override + public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId, boolean merge) { + boolean drawImage = showCards.size() <= MAX_IMAGES; + this.bigCard = bigCard; + this.gameId = gameId; + if (merge) { + for (CardView card : showCards.values()) { + if (!cards.containsKey(card.getId())) { + addCard(card, bigCard, gameId, drawImage); + } + } + for (Iterator> i = cards.entrySet().iterator(); i.hasNext(); ) { + Entry entry = i.next(); + if (!showCards.containsKey(entry.getKey())) { + removeCardImg(entry.getKey()); + i.remove(); + } + } + } else { + this.clearCards(); + for (CardView card : showCards.values()) { + addCard(card, bigCard, gameId, drawImage); + } + } + drawCards(sortSetting); + this.setVisible(true); + } - private void addCard(CardView card, BigCard bigCard, UUID gameId, boolean drawImage) { - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, drawImage, true); - cards.put(card.getId(), cardImg); - cardImg.addMouseListener(this); - add(cardImg); - cardImg.update(card); - cards.put(card.getId(), cardImg); - } + private void addCard(CardView card, BigCard bigCard, UUID gameId, boolean drawImage) { + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, drawImage, true, PreferencesDialog.getRenderMode()); + cards.put(card.getId(), cardImg); + cardImg.addMouseListener(this); + add(cardImg); + cardImg.update(card); + cards.put(card.getId(), cardImg); + } - @Override - public void drawCards(SortSetting sortSetting) { - int maxWidth = this.getParent().getWidth(); - int cardVerticalOffset = GUISizeHelper.editorCardOffsetSize; - int numColumns = maxWidth / cardDimension.width; - int curColumn = 0; - int curRow = 0; - if (!cards.isEmpty()) { - Rectangle rectangle = new Rectangle(cardDimension.width, cardDimension.height); - List sortedCards = new ArrayList<>(cards.values()); - switch (sortSetting.getSortBy()) { - case NAME: - sortedCards.sort(new CardNameComparator()); - break; - case CARD_TYPE: - sortedCards.sort(new CardTypeComparator()); - break; - case RARITY: - sortedCards.sort(new CardRarityComparator()); - break; - case COLOR: - sortedCards.sort(new CardColorComparator()); - break; - case COLOR_IDENTITY: - sortedCards.sort(new CardColorDetailedIdentity()); - break; - case CASTING_COST: - sortedCards.sort(new CardCostComparator()); - break; + @Override + public void drawCards(SortSetting sortSetting) { + int maxWidth = this.getParent().getWidth(); + int cardVerticalOffset = GUISizeHelper.editorCardOffsetSize; + int numColumns = maxWidth / cardDimension.width; + int curColumn = 0; + int curRow = 0; + if (!cards.isEmpty()) { + Rectangle rectangle = new Rectangle(cardDimension.width, cardDimension.height); + List sortedCards = new ArrayList<>(cards.values()); + switch (sortSetting.getSortBy()) { + case NAME: + sortedCards.sort(new CardNameComparator()); + break; + case CARD_TYPE: + sortedCards.sort(new CardTypeComparator()); + break; + case RARITY: + sortedCards.sort(new CardRarityComparator()); + break; + case COLOR: + sortedCards.sort(new CardColorComparator()); + break; + case COLOR_IDENTITY: + sortedCards.sort(new CardColorDetailedIdentity()); + break; + case CASTING_COST: + sortedCards.sort(new CardCostComparator()); + break; - } - MageCard lastCard = null; - for (MageCard cardImg : sortedCards) { - if (sortSetting.isPilesToggle()) { - if (lastCard == null) { - lastCard = cardImg; - } - switch (sortSetting.getSortBy()) { - case NAME: - if (!cardImg.getOriginal().getName().equals(lastCard.getOriginal().getName())) { - curColumn++; - curRow = 0; - } - break; - case CARD_TYPE: - if (!cardImg.getOriginal().getCardTypes().equals(lastCard.getOriginal().getCardTypes())) { - curColumn++; - curRow = 0; - } - break; - case RARITY: - if (cardImg.getOriginal().getRarity() != lastCard.getOriginal().getRarity()) { - curColumn++; - curRow = 0; - } - break; - case COLOR: - if (cardImg.getOriginal().getColor().compareTo(lastCard.getOriginal().getColor()) != 0) { - curColumn++; - curRow = 0; - } - break; - case COLOR_IDENTITY: - if (CardColorUtil.getColorIdentitySortValue(cardImg.getOriginal().getManaCost(), cardImg.getOriginal().getColor(), cardImg.getOriginal().getRules()) - != CardColorUtil.getColorIdentitySortValue(lastCard.getOriginal().getManaCost(), lastCard.getOriginal().getColor(), lastCard.getOriginal().getRules())) { - curColumn++; - curRow = 0; - } - break; - case CASTING_COST: - if (cardImg.getOriginal().getConvertedManaCost() != lastCard.getOriginal().getConvertedManaCost()) { - curColumn++; - curRow = 0; - } - break; - } - rectangle.setLocation(curColumn * cardDimension.width, curRow * cardVerticalOffset); - cardImg.setBounds(rectangle); - cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); - moveToFront(cardImg); - curRow++; - lastCard = cardImg; - } else { - rectangle.setLocation(curColumn * cardDimension.width, curRow * cardVerticalOffset); - cardImg.setBounds(rectangle); - cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); - moveToFront(cardImg); - curColumn++; - if (curColumn == numColumns) { - curColumn = 0; - curRow++; - } - } - } - } - resizeArea(); - revalidate(); - repaint(); - } + } + MageCard lastCard = null; + for (MageCard cardImg : sortedCards) { + if (sortSetting.isPilesToggle()) { + if (lastCard == null) { + lastCard = cardImg; + } + switch (sortSetting.getSortBy()) { + case NAME: + if (!cardImg.getOriginal().getName().equals(lastCard.getOriginal().getName())) { + curColumn++; + curRow = 0; + } + break; + case CARD_TYPE: + if (!cardImg.getOriginal().getCardTypes().equals(lastCard.getOriginal().getCardTypes())) { + curColumn++; + curRow = 0; + } + break; + case RARITY: + if (cardImg.getOriginal().getRarity() != lastCard.getOriginal().getRarity()) { + curColumn++; + curRow = 0; + } + break; + case COLOR: + if (cardImg.getOriginal().getColor().compareTo(lastCard.getOriginal().getColor()) != 0) { + curColumn++; + curRow = 0; + } + break; + case COLOR_IDENTITY: + if (CardColorUtil.getColorIdentitySortValue(cardImg.getOriginal().getManaCost(), cardImg.getOriginal().getColor(), cardImg.getOriginal().getRules()) + != CardColorUtil.getColorIdentitySortValue(lastCard.getOriginal().getManaCost(), lastCard.getOriginal().getColor(), lastCard.getOriginal().getRules())) { + curColumn++; + curRow = 0; + } + break; + case CASTING_COST: + if (cardImg.getOriginal().getConvertedManaCost() != lastCard.getOriginal().getConvertedManaCost()) { + curColumn++; + curRow = 0; + } + break; + } + rectangle.setLocation(curColumn * cardDimension.width, curRow * cardVerticalOffset); + cardImg.setBounds(rectangle); + cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); + moveToFront(cardImg); + curRow++; + lastCard = cardImg; + } else { + rectangle.setLocation(curColumn * cardDimension.width, curRow * cardVerticalOffset); + cardImg.setBounds(rectangle); + cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); + moveToFront(cardImg); + curColumn++; + if (curColumn == numColumns) { + curColumn = 0; + curRow++; + } + } + } + } + resizeArea(); + revalidate(); + repaint(); + } - private void clearCards() { - // remove possible mouse listeners, preventing gc - for (MageCard mageCard : cards.values()) { - if (mageCard instanceof CardPanel) { - ((CardPanel) mageCard).cleanUp(); - } - } - this.cards.clear(); - removeAllCardImg(); - } + private void clearCards() { + // remove possible mouse listeners, preventing gc + for (MageCard mageCard : cards.values()) { + if (mageCard instanceof CardPanel) { + ((CardPanel) mageCard).cleanUp(); + } + } + this.cards.clear(); + removeAllCardImg(); + } - private void removeAllCardImg() { - for (Component comp : getComponents()) { - if (comp instanceof Card || comp instanceof MageCard) { - remove(comp); - } - } - } + private void removeAllCardImg() { + for (Component comp : getComponents()) { + if (comp instanceof Card || comp instanceof MageCard) { + remove(comp); + } + } + } - private void removeCardImg(UUID cardId) { - for (Component comp : getComponents()) { - if (comp instanceof Card) { - if (((Card) comp).getCardId().equals(cardId)) { - remove(comp); - comp = null; - } - } else if (comp instanceof MageCard) { - if (((MageCard) comp).getOriginal().getId().equals(cardId)) { - remove(comp); - comp = null; - } - } - } - } + private void removeCardImg(UUID cardId) { + for (Component comp : getComponents()) { + if (comp instanceof Card) { + if (((Card) comp).getCardId().equals(cardId)) { + remove(comp); + comp = null; + } + } else if (comp instanceof MageCard) { + if (((MageCard) comp).getOriginal().getId().equals(cardId)) { + remove(comp); + comp = null; + } + } + } + } - public void removeCard(UUID cardId) { - removeCardImg(cardId); - cards.remove(cardId); - } + public void removeCard(UUID cardId) { + removeCardImg(cardId); + cards.remove(cardId); + } - @Override - public void addCardEventListener(Listener listener) { - cardEventSource.addListener(listener); - } + @Override + public void addCardEventListener(Listener listener) { + cardEventSource.addListener(listener); + } - @Override - public void clearCardEventListeners() { - cardEventSource.clearListeners(); - } + @Override + public void clearCardEventListeners() { + cardEventSource.clearListeners(); + } - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 294, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 197, Short.MAX_VALUE) - ); - }// //GEN-END:initComponents + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 294, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 197, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents - // Variables declaration - do not modify//GEN-BEGIN:variables - // End of variables declaration//GEN-END:variables - @Override - public void mouseClicked(MouseEvent e) { - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks - e.consume(); - Object obj = e.getSource(); - if (obj instanceof Card) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } else if (obj instanceof MageCard) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } - } - } + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables + @Override + public void mouseClicked(MouseEvent e) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks + e.consume(); + Object obj = e.getSource(); + if (obj instanceof Card) { + if (e.isAltDown()) { + cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); + } else { + cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); + } + } else if (obj instanceof MageCard) { + if (e.isAltDown()) { + cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); + } else { + cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); + } + } + } + } - @Override - public void mousePressed(MouseEvent e) { - } + @Override + public void mousePressed(MouseEvent e) { + } - @Override - public void mouseReleased(MouseEvent e) { - } + @Override + public void mouseReleased(MouseEvent e) { + } - @Override - public void mouseEntered(MouseEvent e) { - } + @Override + public void mouseEntered(MouseEvent e) { + } - @Override - public void mouseExited(MouseEvent e) { - } + @Override + public void mouseExited(MouseEvent e) { + } - private void resizeArea() { - Dimension area = new Dimension(0, 0); - Dimension size = getPreferredSize(); + private void resizeArea() { + Dimension area = new Dimension(0, 0); + Dimension size = getPreferredSize(); - for (Component comp : getComponents()) { - Rectangle r = comp.getBounds(); - if (r.x + r.width > area.width) { - area.width = r.x + r.width; - } - if (r.y + r.height > area.height) { - area.height = r.y + r.height; - } - } - if (size.height != area.height || size.width != area.width) { - setPreferredSize(area); - } - } + for (Component comp : getComponents()) { + Rectangle r = comp.getBounds(); + if (r.x + r.width > area.width) { + area.width = r.x + r.width; + } + if (r.y + r.height > area.height) { + area.height = r.y + r.height; + } + } + if (size.height != area.height || size.width != area.width) { + setPreferredSize(area); + } + } - @Override - public void refresh() { - revalidate(); - repaint(); - } + @Override + public void refresh() { + revalidate(); + repaint(); + } - @Override - public int cardsSize() { - return cards.size(); - } -} + @Override + public int cardsSize() { + return cards.size(); + } + } -class CardNameComparator implements Comparator { + class CardNameComparator implements Comparator { - @Override - public int compare(MageCard o1, MageCard o2) { - return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } + @Override + public int compare(MageCard o1, MageCard o2) { + return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); + } -} + } -class CardRarityComparator implements Comparator { + class CardRarityComparator implements Comparator { - @Override - public int compare(MageCard o1, MageCard o2) { - int val = o1.getOriginal().getRarity().compareTo(o2.getOriginal().getRarity()); - if (val == 0) { - return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } else { - return val; - } - } + @Override + public int compare(MageCard o1, MageCard o2) { + int val = o1.getOriginal().getRarity().compareTo(o2.getOriginal().getRarity()); + if (val == 0) { + return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); + } else { + return val; + } + } -} + } -class CardCostComparator implements Comparator { + class CardCostComparator implements Comparator { - @Override - public int compare(MageCard o1, MageCard o2) { - int val = Integer.valueOf(o1.getOriginal().getConvertedManaCost()).compareTo(o2.getOriginal().getConvertedManaCost()); - if (val == 0) { - return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } else { - return val; - } - } + @Override + public int compare(MageCard o1, MageCard o2) { + int val = Integer.valueOf(o1.getOriginal().getConvertedManaCost()).compareTo(o2.getOriginal().getConvertedManaCost()); + if (val == 0) { + return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); + } else { + return val; + } + } -} + } -class CardColorComparator implements Comparator { + class CardColorComparator implements Comparator { - @Override - public int compare(MageCard o1, MageCard o2) { - int val = o1.getOriginal().getColor().compareTo(o2.getOriginal().getColor()); - if (val == 0) { - return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } else { - return val; - } - } + @Override + public int compare(MageCard o1, MageCard o2) { + int val = o1.getOriginal().getColor().compareTo(o2.getOriginal().getColor()); + if (val == 0) { + return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); + } else { + return val; + } + } -} + } -class CardColorDetailedIdentity implements Comparator { + class CardColorDetailedIdentity implements Comparator { - @Override - public int compare(MageCard o1, MageCard o2) { - int val = CardColorUtil.getColorIdentitySortValue(o1.getOriginal().getManaCost(), o1.getOriginal().getColor(), o1.getOriginal().getRules()) - - CardColorUtil.getColorIdentitySortValue(o2.getOriginal().getManaCost(), o2.getOriginal().getColor(), o2.getOriginal().getRules()); - if (val == 0) { - return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } else { - return val; - } - } + @Override + public int compare(MageCard o1, MageCard o2) { + int val = CardColorUtil.getColorIdentitySortValue(o1.getOriginal().getManaCost(), o1.getOriginal().getColor(), o1.getOriginal().getRules()) + - CardColorUtil.getColorIdentitySortValue(o2.getOriginal().getManaCost(), o2.getOriginal().getColor(), o2.getOriginal().getRules()); + if (val == 0) { + return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); + } else { + return val; + } + } -} + } -class CardTypeComparator implements Comparator { + class CardTypeComparator implements Comparator { - @Override - public int compare(MageCard o1, MageCard o2) { - int val = o1.getOriginal().getCardTypes().toString().compareTo(o2.getOriginal().getCardTypes().toString()); - if (val == 0) { - return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } else { - return val; - } - } + @Override + public int compare(MageCard o1, MageCard o2) { + int val = o1.getOriginal().getCardTypes().toString().compareTo(o2.getOriginal().getCardTypes().toString()); + if (val == 0) { + return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); + } else { + return val; + } + } -} + } diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index 32fa2a2060b..8c7f885d25f 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -8,6 +8,7 @@ package mage.client.cards; import mage.cards.MageCard; + import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.CardsViewUtil; import mage.client.util.Config; @@ -227,7 +228,7 @@ } private void addCard(CardView card, BigCard bigCard, UUID gameId) { - MageCard mageCard = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true); + MageCard mageCard = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode()); if (zone != null) { mageCard.setZone(zone); } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java index 857a8cf2864..3384cfe8a2b 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java @@ -1,709 +1,709 @@ /* - * CardsList.java - * - * Created on Dec 18, 2009, 10:40:12 AM - */ -package mage.client.cards; - -import mage.cards.MageCard; -import mage.client.constants.Constants.DeckEditorMode; -import mage.client.constants.Constants.SortBy; -import mage.client.deckeditor.SortSetting; -import mage.client.deckeditor.table.TableModel; -import mage.client.deckeditor.table.UpdateCountsCallback; -import mage.client.dialog.PreferencesDialog; -import mage.client.plugins.impl.Plugins; -import mage.client.util.*; -import mage.client.util.Event; -import mage.client.util.gui.TableSpinnerEditor; -import mage.view.CardView; -import mage.view.CardsView; -import mage.view.SimpleCardView; -import org.mage.card.arcane.CardPanel; -import org.mage.card.arcane.ManaSymbolsCellRenderer; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.beans.Beans; -import java.util.*; -import java.util.List; - -/** - * @author BetaSteward_at_googlemail.com - */ -public class CardsList extends javax.swing.JPanel implements MouseListener, ICardGrid { - - protected final CardEventSource cardEventSource = new CardEventSource(); - private Dimension cardDimension; - private int rowHeight; - private CardsView cards; - private Map mageCards = new LinkedHashMap<>(); - protected BigCard bigCard; - protected UUID gameId; - private SortSetting sortSetting; - - private TableModel mainModel; - private JTable mainTable; - private ICardGrid currentView; - - /** - * Creates new form Cards - */ - public CardsList() { - initComponents(); - makeTransparent(); - initListViewComponents(); - setGUISize(); - } - - public void cleanUp() { - this.clearCardEventListeners(); - if (cards != null) { - cards.clear(); - } - if (mainModel != null) { - mainModel.removeTableModelListener(mainTable); - mainModel.clear(); - } - if (cardArea != null) { - for (MouseListener ml : cardArea.getMouseListeners()) { - cardArea.removeMouseListener(ml); - } - for (Component comp : cardArea.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel) comp).cleanUp(); - } - } - cardArea.removeAll(); - } - if (mainTable != null) { - for (MouseListener ml : mainTable.getMouseListeners()) { - mainTable.removeMouseListener(ml); - } - } - if (currentView != null) { - currentView.clearCardEventListeners(); - } - - mageCards.clear(); - this.bigCard = null; - - } - - public void changeGUISize() { - setGUISize(); - redrawCards(); - } - - private void setGUISize() { - mainTable.getTableHeader().setFont(GUISizeHelper.tableFont); - mainTable.setFont(GUISizeHelper.tableFont); - mainTable.setRowHeight(GUISizeHelper.getTableRowHeight()); - cardDimension = GUISizeHelper.editorCardDimension; - rowHeight = GUISizeHelper.editorCardOffsetSize; - } - - private void makeTransparent() { - panelCardArea.setOpaque(false); - cardArea.setOpaque(false); - panelCardArea.getViewport().setOpaque(false); - panelControl.setBackground(new Color(250, 250, 250, 150)); - panelControl.setOpaque(true); - cbSortBy.setModel(new DefaultComboBoxModel<>(SortBy.values())); - } - - private void initListViewComponents() { - mainTable = new JTable(); - - mainModel = new TableModel(); - mainModel.addListeners(mainTable); - - mainTable.setModel(mainModel); - mainTable.setForeground(Color.white); - DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class); - myRenderer.setBackground(new Color(0, 0, 0, 100)); - mainTable.getColumnModel().getColumn(0).setMaxWidth(25); - mainTable.getColumnModel().getColumn(0).setPreferredWidth(25); - mainTable.getColumnModel().getColumn(1).setPreferredWidth(110); - mainTable.getColumnModel().getColumn(2).setPreferredWidth(90); - mainTable.getColumnModel().getColumn(3).setPreferredWidth(50); - mainTable.getColumnModel().getColumn(4).setPreferredWidth(170); - mainTable.getColumnModel().getColumn(5).setPreferredWidth(30); - mainTable.getColumnModel().getColumn(6).setPreferredWidth(15); - mainTable.getColumnModel().getColumn(7).setPreferredWidth(15); - - // new mana render (svg support) - mainTable.getColumnModel().getColumn(mainModel.COLUMN_INDEX_COST).setCellRenderer(new ManaSymbolsCellRenderer()); - - if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView").equals("listView")) { - jToggleListView.setSelected(true); - panelCardArea.setViewportView(mainTable); - currentView = mainModel; - cbSortBy.setEnabled(false); - chkPiles.setEnabled(false); - } else { - jToggleCardView.setSelected(true); - currentView = this; - panelCardArea.setViewportView(cardArea); - cbSortBy.setEnabled(true); - chkPiles.setEnabled(true); - } - - cardArea.addMouseListener(this); - - mainTable.setOpaque(false); - mainTable.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks - e.consume(); - if (e.isAltDown()) { - handleAltDoubleClick(); - } else { - handleDoubleClick(); - } - } - } - }); - - mainModel.setUpdateCountsCallback(new UpdateCountsCallback(lblCount, lblCreatureCount, lblLandCount, null, null, null, null)); - } - - // if you use the deck ediot to build a free deck, numbers can be set directly in deck and sideboard - public void setDeckEditorMode(DeckEditorMode mode) { - if (mode == DeckEditorMode.FREE_BUILDING) { - // activate spinner for card number change - mainModel.setNumberEditable(true); - TableColumnModel tcm = mainTable.getColumnModel(); - TableColumn tc = tcm.getColumn(0); - tc.setMaxWidth(55); - tc.setMinWidth(55); - tc.setPreferredWidth(55); - tc.setCellEditor(new TableSpinnerEditor(this)); - } - } - - public void handleSetNumber(int number) { - if (mainTable.getSelectedRowCount() == 1) { - mainModel.setNumber(mainTable.getSelectedRow(), number); - } - } - - public void handleDoubleClick() { - if (mainTable.getSelectedRowCount() > 0) { - int[] n = mainTable.getSelectedRows(); - List indexes = asList(n); - Collections.reverse(indexes); - for (Integer index : indexes) { - mainModel.doubleClick(index); - } - } - } - - public void handleAltDoubleClick() { - if (mainTable.getSelectedRowCount() > 0) { - int[] n = mainTable.getSelectedRows(); - List indexes = asList(n); - Collections.reverse(indexes); - for (Integer index : indexes) { - mainModel.altDoubleClick(index); - } - } - } - - public ICardGrid getMainModel() { - return mainModel; - } - - public List asList(final int[] is) { - List list = new ArrayList<>(); - for (int i : is) { - list.add(i); - } - return list; - } - - public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) { - int selectedRow = -1; - if (currentView.equals(mainModel)) { - selectedRow = mainTable.getSelectedRow(); - } - this.cards = showCards; - this.bigCard = bigCard; - this.gameId = gameId; - - cbSortBy.setSelectedItem(sortSetting.getSortBy()); - chkPiles.setSelected(sortSetting.isPilesToggle()); - currentView.loadCards(showCards, sortSetting, bigCard, gameId); - if (selectedRow >= 0) { - selectedRow = Math.min(selectedRow, mainTable.getRowCount() - 1); - if (selectedRow >= 0) { - mainTable.setRowSelectionInterval(selectedRow, selectedRow); - } - } - } - - private void redrawCards() { - if (cards == null) { - cards = new CardsView(); - } - currentView.loadCards(cards, sortSetting, bigCard, gameId); - } - - @Override - public void drawCards(SortSetting sortSetting) { - int maxWidth = this.getParent().getWidth(); - int numColumns = maxWidth / cardDimension.width; - int curColumn = 0; - int curRow = 0; - int maxRow = 0; - int maxColumn = 0; - Comparator comparator = null; - Map oldMageCards = mageCards; - mageCards = new LinkedHashMap<>(); - - //Find card view - for (Map.Entry view : cards.entrySet()) { - UUID uuid = view.getKey(); - CardView cardView = view.getValue(); - if (oldMageCards.containsKey(uuid)) { - mageCards.put(uuid, oldMageCards.get(uuid)); - oldMageCards.remove(uuid); - } else { - mageCards.put(uuid, addCard(cardView, bigCard, gameId)); - } - } - //Remove unused cards - for (MageCard card : oldMageCards.values()) { - cardArea.remove(card); - } - - if (cards != null && !cards.isEmpty()) { - Rectangle rectangle = new Rectangle(cardDimension.width, cardDimension.height); - List sortedCards = new ArrayList<>(cards.values()); - switch (sortSetting.getSortBy()) { - case NAME: - comparator = new CardViewNameComparator(); - break; - case RARITY: - comparator = new CardViewRarityComparator(); - break; - case CARD_TYPE: - comparator = new CardViewCardTypeComparator(); - break; - case COLOR: - comparator = new CardViewColorComparator(); - break; - case COLOR_IDENTITY: - comparator = new CardViewColorIdentityComparator(); - break; - case CASTING_COST: - comparator = new CardViewCostComparator(); - break; - } - if (comparator != null) { - sortedCards.sort(new CardViewNameComparator()); - sortedCards.sort(comparator); - } - CardView lastCard = null; - for (CardView card : sortedCards) { - if (sortSetting.isPilesToggle()) { - if (lastCard == null) { - lastCard = card; - } - if (comparator != null) { - if (comparator.compare(card, lastCard) > 0) { - curColumn++; - maxRow = Math.max(maxRow, curRow); - curRow = 0; - } - } - rectangle.setLocation(curColumn * cardDimension.width, curRow * rowHeight); - setCardBounds(mageCards.get(card.getId()), rectangle); - - curRow++; - lastCard = card; - } else { - rectangle.setLocation(curColumn * cardDimension.width, curRow * rowHeight); - setCardBounds(mageCards.get(card.getId()), rectangle); - curColumn++; - if (curColumn == numColumns) { - maxColumn = Math.max(maxColumn, curColumn); - curColumn = 0; - curRow++; - } - } - } - } - maxRow = Math.max(maxRow, curRow); - maxColumn = Math.max(maxColumn, curColumn); - updateCounts(); - cardArea.setPreferredSize(new Dimension((maxColumn + 1) * cardDimension.width, cardDimension.height + maxRow * rowHeight)); - cardArea.revalidate(); - this.revalidate(); - this.repaint(); - this.setVisible(true); - } - - private void updateCounts() { - int landCount = 0; - int creatureCount = 0; - int sorceryCount = 0; - int instantCount = 0; - int enchantmentCount = 0; - int artifactCount = 0; - - for (CardView card : cards.values()) { - if (card.isLand()) { - landCount++; - } - if (card.isCreature()) { - creatureCount++; - } - if (card.isSorcery()) { - sorceryCount++; - } - if (card.isInstant()) { - instantCount++; - } - if (card.isEnchantment()) { - enchantmentCount++; - } - if (card.isArtifact()) { - artifactCount++; - } - } - - int count = cards != null ? cards.size() : 0; - this.lblCount.setText(Integer.toString(count)); - this.lblCreatureCount.setText(Integer.toString(creatureCount)); - this.lblLandCount.setText(Integer.toString(landCount)); - } - - private MageCard addCard(CardView card, BigCard bigCard, UUID gameId) { - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true); - cardArea.add(cardImg); - cardImg.update(card); - cardImg.addMouseListener(this); - return cardImg; - } - - private void setCardBounds(MageCard card, Rectangle rectangle) { - card.setBounds(rectangle); - card.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); - cardArea.moveToFront(card); - } - - @Override - public void addCardEventListener(Listener listener) { - cardEventSource.addListener(listener); - mainModel.addCardEventListener(listener); - } - - @Override - public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { - this.loadCards(showCards, sortSetting, bigCard, gameId, true); - } - - @Override - public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId, boolean merge) { - cards = showCards; - this.bigCard = bigCard; - this.gameId = gameId; - drawCards(sortSetting); - } - - @Override - public void refresh() { - redrawCards(); - } - - @Override - public void clearCardEventListeners() { - cardEventSource.clearListeners(); - mainModel.clearCardEventListeners(); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - bgView = new javax.swing.ButtonGroup(); - panelControl = new javax.swing.JPanel(); - lblCount = new javax.swing.JLabel(); - lblLandCount = new javax.swing.JLabel(); - lblCreatureCount = new javax.swing.JLabel(); - chkPiles = new javax.swing.JCheckBox(); - cbSortBy = new javax.swing.JComboBox(); - jToggleListView = new javax.swing.JToggleButton(); - jToggleCardView = new javax.swing.JToggleButton(); - panelCardArea = new javax.swing.JScrollPane(); - cardArea = new javax.swing.JLayeredPane(); - - setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); - setMinimumSize(new java.awt.Dimension(30, 30)); - setPreferredSize((!Beans.isDesignTime()) ? - (GUISizeHelper.editorCardDimension) - : (new Dimension(600, 600))); - setRequestFocusEnabled(false); - - panelControl.setMaximumSize(new java.awt.Dimension(32767, 23)); - panelControl.setMinimumSize(new java.awt.Dimension(616, 23)); - panelControl.setName(""); // NOI18N - panelControl.setPreferredSize(new java.awt.Dimension(616, 23)); - panelControl.setRequestFocusEnabled(false); - - lblCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/deck_pack.png"))); // NOI18N - lblCount.setText("999"); - lblCount.setToolTipText("Number of all cards in this area."); - lblCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - lblCount.setFocusable(false); - lblCount.setInheritsPopupMenu(false); - lblCount.setRequestFocusEnabled(false); - lblCount.setVerifyInputWhenFocusTarget(false); - - lblLandCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblLandCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_land.png"))); // NOI18N - lblLandCount.setText("999"); - lblLandCount.setToolTipText("Number of lands."); - lblLandCount.setVerticalAlignment(javax.swing.SwingConstants.TOP); - lblLandCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - lblLandCount.setFocusable(false); - lblLandCount.setInheritsPopupMenu(false); - lblLandCount.setRequestFocusEnabled(false); - lblLandCount.setVerifyInputWhenFocusTarget(false); - - lblCreatureCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - lblCreatureCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_creatures.png"))); // NOI18N - lblCreatureCount.setText("999"); - lblCreatureCount.setToolTipText("Number of creatures."); - lblCreatureCount.setVerticalAlignment(javax.swing.SwingConstants.TOP); - lblCreatureCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - lblCreatureCount.setFocusable(false); - lblCreatureCount.setInheritsPopupMenu(false); - lblCreatureCount.setRequestFocusEnabled(false); - lblCreatureCount.setVerifyInputWhenFocusTarget(false); - - chkPiles.setText("Piles"); - chkPiles.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - chkPiles.setMargin(new java.awt.Insets(3, 2, 2, 2)); - chkPiles.addActionListener(evt -> chkPilesActionPerformed(evt)); - - cbSortBy.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"SortBy"})); - cbSortBy.setToolTipText("Sort the cards if card view is active."); - cbSortBy.setMaximumSize(new java.awt.Dimension(120, 20)); - cbSortBy.setMinimumSize(new java.awt.Dimension(120, 20)); - cbSortBy.setName("SortBy"); // NOI18N - cbSortBy.setOpaque(false); - cbSortBy.setPreferredSize(new java.awt.Dimension(120, 20)); - cbSortBy.addActionListener(evt -> cbSortByActionPerformed(evt)); - - bgView.add(jToggleListView); - jToggleListView.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/list_panel.png"))); // NOI18N - jToggleListView.setToolTipText("Shows the cards as a list."); - jToggleListView.setBorder(null); - jToggleListView.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); - jToggleListView.setMargin(new java.awt.Insets(2, 6, 2, 6)); - jToggleListView.setMaximumSize(new java.awt.Dimension(37, 25)); - jToggleListView.setMinimumSize(new java.awt.Dimension(37, 25)); - jToggleListView.setPreferredSize(new java.awt.Dimension(44, 22)); - jToggleListView.addActionListener(evt -> jToggleListViewActionPerformed(evt)); - - bgView.add(jToggleCardView); - jToggleCardView.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/card_panel.png"))); // NOI18N - jToggleCardView.setToolTipText("Shows the card as images."); - jToggleCardView.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); - jToggleCardView.setMargin(new java.awt.Insets(2, 6, 2, 6)); - jToggleCardView.setPreferredSize(new java.awt.Dimension(40, 22)); - jToggleCardView.addActionListener(evt -> jToggleCardViewActionPerformed(evt)); - - javax.swing.GroupLayout panelControlLayout = new javax.swing.GroupLayout(panelControl); - panelControl.setLayout(panelControlLayout); - panelControlLayout.setHorizontalGroup( - panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelControlLayout.createSequentialGroup() - .addComponent(lblCount) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblLandCount) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblCreatureCount) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(chkPiles) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - panelControlLayout.setVerticalGroup( - panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelControlLayout.createSequentialGroup() - .addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblCount) - .addComponent(lblLandCount) - .addComponent(lblCreatureCount) - .addComponent(chkPiles)) - .addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 0, 0)) - ); - - jToggleListView.getAccessibleContext().setAccessibleDescription("Switch between image and table view."); - - panelCardArea.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED)); - panelCardArea.setViewportView(cardArea); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, 467, Short.MAX_VALUE) - .addComponent(panelCardArea) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(2, 2, 2) - .addComponent(panelCardArea, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - - private void jToggleCardViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleCardViewActionPerformed - currentView = this; - panelCardArea.setViewportView(cardArea); - cbSortBy.setEnabled(true); - chkPiles.setEnabled(true); - PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView"); - redrawCards(); - }//GEN-LAST:event_jToggleCardViewActionPerformed - - private void jToggleListViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleListViewActionPerformed - currentView = mainModel; - panelCardArea.setViewportView(mainTable); - cbSortBy.setEnabled(false); - chkPiles.setEnabled(false); - PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "listView"); - redrawCards(); - }//GEN-LAST:event_jToggleListViewActionPerformed - - private void cbSortByActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSortByActionPerformed - sortSetting.setSortBy((SortBy) cbSortBy.getSelectedItem()); - drawCards(sortSetting); - }//GEN-LAST:event_cbSortByActionPerformed - - private void chkPilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkPilesActionPerformed - sortSetting.setPilesToggle(chkPiles.isSelected()); - drawCards(sortSetting); - }//GEN-LAST:event_chkPilesActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.ButtonGroup bgView; - private javax.swing.JLayeredPane cardArea; - private javax.swing.JComboBox cbSortBy; - private javax.swing.JCheckBox chkPiles; - private javax.swing.JToggleButton jToggleCardView; - private javax.swing.JToggleButton jToggleListView; - private javax.swing.JLabel lblCount; - private javax.swing.JLabel lblCreatureCount; - private javax.swing.JLabel lblLandCount; - private javax.swing.JScrollPane panelCardArea; - private javax.swing.JPanel panelControl; - // End of variables declaration//GEN-END:variables - - @Override - public void mouseClicked(MouseEvent e) { - } - - @Override - public void mousePressed(MouseEvent e) { - if (e.getClickCount() >= 1 && !e.isConsumed()) { - Object obj = e.getSource(); - if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks - e.consume(); - if (obj instanceof Card) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } else if (obj instanceof MageCard) { - if (e.isAltDown()) { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); - } else { - cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); - } - } - } - if (obj instanceof MageCard) { - checkMenu(e, ((MageCard) obj).getOriginal()); - } else { - checkMenu(e, null); - } - } - } - - @Override - public void mouseReleased(MouseEvent e) { - if (!e.isConsumed()) { - Object obj = e.getSource(); - if (obj instanceof MageCard) { - checkMenu(e, ((MageCard) obj).getOriginal()); - } else { - checkMenu(e, null); - } - } - } - - private void checkMenu(MouseEvent Me, SimpleCardView card) { - if (Me.isPopupTrigger()) { - Me.consume(); - cardEventSource.fireEvent(card, Me.getComponent(), Me.getX(), Me.getY(), ClientEventType.SHOW_POP_UP_MENU); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - } - - public void setDisplayNoCopies(boolean value) { - mainModel.setDisplayNoCopies(value); - } - - @Override - public int cardsSize() { - return cards.size(); - } - - public void setSortBy(SortBy sortBy) { - if (sortBy != null) { - cbSortBy.setSelectedIndex(sortBy.ordinal()); - } - } - - public void setSortSetting(SortSetting sortSetting) { - this.sortSetting = sortSetting; - } - -} + * CardsList.java + * + * Created on Dec 18, 2009, 10:40:12 AM + */ + package mage.client.cards; + + import mage.cards.MageCard; + import mage.client.constants.Constants.DeckEditorMode; + import mage.client.constants.Constants.SortBy; + import mage.client.deckeditor.SortSetting; + import mage.client.deckeditor.table.TableModel; + import mage.client.deckeditor.table.UpdateCountsCallback; + import mage.client.dialog.PreferencesDialog; + import mage.client.plugins.impl.Plugins; + import mage.client.util.Event; + import mage.client.util.*; + import mage.client.util.gui.TableSpinnerEditor; + import mage.view.CardView; + import mage.view.CardsView; + import mage.view.SimpleCardView; + import org.mage.card.arcane.CardPanel; + import org.mage.card.arcane.ManaSymbolsCellRenderer; + + import javax.swing.*; + import javax.swing.table.DefaultTableCellRenderer; + import javax.swing.table.TableColumn; + import javax.swing.table.TableColumnModel; + import java.awt.*; + import java.awt.event.MouseAdapter; + import java.awt.event.MouseEvent; + import java.awt.event.MouseListener; + import java.beans.Beans; + import java.util.List; + import java.util.*; + + /** + * @author BetaSteward_at_googlemail.com + */ + public class CardsList extends javax.swing.JPanel implements MouseListener, ICardGrid { + + protected final CardEventSource cardEventSource = new CardEventSource(); + private Dimension cardDimension; + private int rowHeight; + private CardsView cards; + private Map mageCards = new LinkedHashMap<>(); + protected BigCard bigCard; + protected UUID gameId; + private SortSetting sortSetting; + + private TableModel mainModel; + private JTable mainTable; + private ICardGrid currentView; + + /** + * Creates new form Cards + */ + public CardsList() { + initComponents(); + makeTransparent(); + initListViewComponents(); + setGUISize(); + } + + public void cleanUp() { + this.clearCardEventListeners(); + if (cards != null) { + cards.clear(); + } + if (mainModel != null) { + mainModel.removeTableModelListener(mainTable); + mainModel.clear(); + } + if (cardArea != null) { + for (MouseListener ml : cardArea.getMouseListeners()) { + cardArea.removeMouseListener(ml); + } + for (Component comp : cardArea.getComponents()) { + if (comp instanceof CardPanel) { + ((CardPanel) comp).cleanUp(); + } + } + cardArea.removeAll(); + } + if (mainTable != null) { + for (MouseListener ml : mainTable.getMouseListeners()) { + mainTable.removeMouseListener(ml); + } + } + if (currentView != null) { + currentView.clearCardEventListeners(); + } + + mageCards.clear(); + this.bigCard = null; + + } + + public void changeGUISize() { + setGUISize(); + redrawCards(); + } + + private void setGUISize() { + mainTable.getTableHeader().setFont(GUISizeHelper.tableFont); + mainTable.setFont(GUISizeHelper.tableFont); + mainTable.setRowHeight(GUISizeHelper.getTableRowHeight()); + cardDimension = GUISizeHelper.editorCardDimension; + rowHeight = GUISizeHelper.editorCardOffsetSize; + } + + private void makeTransparent() { + panelCardArea.setOpaque(false); + cardArea.setOpaque(false); + panelCardArea.getViewport().setOpaque(false); + panelControl.setBackground(new Color(250, 250, 250, 150)); + panelControl.setOpaque(true); + cbSortBy.setModel(new DefaultComboBoxModel<>(SortBy.values())); + } + + private void initListViewComponents() { + mainTable = new JTable(); + + mainModel = new TableModel(); + mainModel.addListeners(mainTable); + + mainTable.setModel(mainModel); + mainTable.setForeground(Color.white); + DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class); + myRenderer.setBackground(new Color(0, 0, 0, 100)); + mainTable.getColumnModel().getColumn(0).setMaxWidth(25); + mainTable.getColumnModel().getColumn(0).setPreferredWidth(25); + mainTable.getColumnModel().getColumn(1).setPreferredWidth(110); + mainTable.getColumnModel().getColumn(2).setPreferredWidth(90); + mainTable.getColumnModel().getColumn(3).setPreferredWidth(50); + mainTable.getColumnModel().getColumn(4).setPreferredWidth(170); + mainTable.getColumnModel().getColumn(5).setPreferredWidth(30); + mainTable.getColumnModel().getColumn(6).setPreferredWidth(15); + mainTable.getColumnModel().getColumn(7).setPreferredWidth(15); + + // new mana render (svg support) + mainTable.getColumnModel().getColumn(mainModel.COLUMN_INDEX_COST).setCellRenderer(new ManaSymbolsCellRenderer()); + + if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView").equals("listView")) { + jToggleListView.setSelected(true); + panelCardArea.setViewportView(mainTable); + currentView = mainModel; + cbSortBy.setEnabled(false); + chkPiles.setEnabled(false); + } else { + jToggleCardView.setSelected(true); + currentView = this; + panelCardArea.setViewportView(cardArea); + cbSortBy.setEnabled(true); + chkPiles.setEnabled(true); + } + + cardArea.addMouseListener(this); + + mainTable.setOpaque(false); + mainTable.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks + e.consume(); + if (e.isAltDown()) { + handleAltDoubleClick(); + } else { + handleDoubleClick(); + } + } + } + }); + + mainModel.setUpdateCountsCallback(new UpdateCountsCallback(lblCount, lblCreatureCount, lblLandCount, null, null, null, null)); + } + + // if you use the deck ediot to build a free deck, numbers can be set directly in deck and sideboard + public void setDeckEditorMode(DeckEditorMode mode) { + if (mode == DeckEditorMode.FREE_BUILDING) { + // activate spinner for card number change + mainModel.setNumberEditable(true); + TableColumnModel tcm = mainTable.getColumnModel(); + TableColumn tc = tcm.getColumn(0); + tc.setMaxWidth(55); + tc.setMinWidth(55); + tc.setPreferredWidth(55); + tc.setCellEditor(new TableSpinnerEditor(this)); + } + } + + public void handleSetNumber(int number) { + if (mainTable.getSelectedRowCount() == 1) { + mainModel.setNumber(mainTable.getSelectedRow(), number); + } + } + + public void handleDoubleClick() { + if (mainTable.getSelectedRowCount() > 0) { + int[] n = mainTable.getSelectedRows(); + List indexes = asList(n); + Collections.reverse(indexes); + for (Integer index : indexes) { + mainModel.doubleClick(index); + } + } + } + + public void handleAltDoubleClick() { + if (mainTable.getSelectedRowCount() > 0) { + int[] n = mainTable.getSelectedRows(); + List indexes = asList(n); + Collections.reverse(indexes); + for (Integer index : indexes) { + mainModel.altDoubleClick(index); + } + } + } + + public ICardGrid getMainModel() { + return mainModel; + } + + public List asList(final int[] is) { + List list = new ArrayList<>(); + for (int i : is) { + list.add(i); + } + return list; + } + + public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) { + int selectedRow = -1; + if (currentView.equals(mainModel)) { + selectedRow = mainTable.getSelectedRow(); + } + this.cards = showCards; + this.bigCard = bigCard; + this.gameId = gameId; + + cbSortBy.setSelectedItem(sortSetting.getSortBy()); + chkPiles.setSelected(sortSetting.isPilesToggle()); + currentView.loadCards(showCards, sortSetting, bigCard, gameId); + if (selectedRow >= 0) { + selectedRow = Math.min(selectedRow, mainTable.getRowCount() - 1); + if (selectedRow >= 0) { + mainTable.setRowSelectionInterval(selectedRow, selectedRow); + } + } + } + + private void redrawCards() { + if (cards == null) { + cards = new CardsView(); + } + currentView.loadCards(cards, sortSetting, bigCard, gameId); + } + + @Override + public void drawCards(SortSetting sortSetting) { + int maxWidth = this.getParent().getWidth(); + int numColumns = maxWidth / cardDimension.width; + int curColumn = 0; + int curRow = 0; + int maxRow = 0; + int maxColumn = 0; + Comparator comparator = null; + Map oldMageCards = mageCards; + mageCards = new LinkedHashMap<>(); + + //Find card view + for (Map.Entry view : cards.entrySet()) { + UUID uuid = view.getKey(); + CardView cardView = view.getValue(); + if (oldMageCards.containsKey(uuid)) { + mageCards.put(uuid, oldMageCards.get(uuid)); + oldMageCards.remove(uuid); + } else { + mageCards.put(uuid, addCard(cardView, bigCard, gameId)); + } + } + //Remove unused cards + for (MageCard card : oldMageCards.values()) { + cardArea.remove(card); + } + + if (cards != null && !cards.isEmpty()) { + Rectangle rectangle = new Rectangle(cardDimension.width, cardDimension.height); + List sortedCards = new ArrayList<>(cards.values()); + switch (sortSetting.getSortBy()) { + case NAME: + comparator = new CardViewNameComparator(); + break; + case RARITY: + comparator = new CardViewRarityComparator(); + break; + case CARD_TYPE: + comparator = new CardViewCardTypeComparator(); + break; + case COLOR: + comparator = new CardViewColorComparator(); + break; + case COLOR_IDENTITY: + comparator = new CardViewColorIdentityComparator(); + break; + case CASTING_COST: + comparator = new CardViewCostComparator(); + break; + } + if (comparator != null) { + sortedCards.sort(new CardViewNameComparator()); + sortedCards.sort(comparator); + } + CardView lastCard = null; + for (CardView card : sortedCards) { + if (sortSetting.isPilesToggle()) { + if (lastCard == null) { + lastCard = card; + } + if (comparator != null) { + if (comparator.compare(card, lastCard) > 0) { + curColumn++; + maxRow = Math.max(maxRow, curRow); + curRow = 0; + } + } + rectangle.setLocation(curColumn * cardDimension.width, curRow * rowHeight); + setCardBounds(mageCards.get(card.getId()), rectangle); + + curRow++; + lastCard = card; + } else { + rectangle.setLocation(curColumn * cardDimension.width, curRow * rowHeight); + setCardBounds(mageCards.get(card.getId()), rectangle); + curColumn++; + if (curColumn == numColumns) { + maxColumn = Math.max(maxColumn, curColumn); + curColumn = 0; + curRow++; + } + } + } + } + maxRow = Math.max(maxRow, curRow); + maxColumn = Math.max(maxColumn, curColumn); + updateCounts(); + cardArea.setPreferredSize(new Dimension((maxColumn + 1) * cardDimension.width, cardDimension.height + maxRow * rowHeight)); + cardArea.revalidate(); + this.revalidate(); + this.repaint(); + this.setVisible(true); + } + + private void updateCounts() { + int landCount = 0; + int creatureCount = 0; + int sorceryCount = 0; + int instantCount = 0; + int enchantmentCount = 0; + int artifactCount = 0; + + for (CardView card : cards.values()) { + if (card.isLand()) { + landCount++; + } + if (card.isCreature()) { + creatureCount++; + } + if (card.isSorcery()) { + sorceryCount++; + } + if (card.isInstant()) { + instantCount++; + } + if (card.isEnchantment()) { + enchantmentCount++; + } + if (card.isArtifact()) { + artifactCount++; + } + } + + int count = cards != null ? cards.size() : 0; + this.lblCount.setText(Integer.toString(count)); + this.lblCreatureCount.setText(Integer.toString(creatureCount)); + this.lblLandCount.setText(Integer.toString(landCount)); + } + + private MageCard addCard(CardView card, BigCard bigCard, UUID gameId) { + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true, PreferencesDialog.getRenderMode()); + cardArea.add(cardImg); + cardImg.update(card); + cardImg.addMouseListener(this); + return cardImg; + } + + private void setCardBounds(MageCard card, Rectangle rectangle) { + card.setBounds(rectangle); + card.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); + cardArea.moveToFront(card); + } + + @Override + public void addCardEventListener(Listener listener) { + cardEventSource.addListener(listener); + mainModel.addCardEventListener(listener); + } + + @Override + public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) { + this.loadCards(showCards, sortSetting, bigCard, gameId, true); + } + + @Override + public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId, boolean merge) { + cards = showCards; + this.bigCard = bigCard; + this.gameId = gameId; + drawCards(sortSetting); + } + + @Override + public void refresh() { + redrawCards(); + } + + @Override + public void clearCardEventListeners() { + cardEventSource.clearListeners(); + mainModel.clearCardEventListeners(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + bgView = new javax.swing.ButtonGroup(); + panelControl = new javax.swing.JPanel(); + lblCount = new javax.swing.JLabel(); + lblLandCount = new javax.swing.JLabel(); + lblCreatureCount = new javax.swing.JLabel(); + chkPiles = new javax.swing.JCheckBox(); + cbSortBy = new javax.swing.JComboBox(); + jToggleListView = new javax.swing.JToggleButton(); + jToggleCardView = new javax.swing.JToggleButton(); + panelCardArea = new javax.swing.JScrollPane(); + cardArea = new javax.swing.JLayeredPane(); + + setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + setMinimumSize(new java.awt.Dimension(30, 30)); + setPreferredSize((!Beans.isDesignTime()) ? + (GUISizeHelper.editorCardDimension) + : (new Dimension(600, 600))); + setRequestFocusEnabled(false); + + panelControl.setMaximumSize(new java.awt.Dimension(32767, 23)); + panelControl.setMinimumSize(new java.awt.Dimension(616, 23)); + panelControl.setName(""); // NOI18N + panelControl.setPreferredSize(new java.awt.Dimension(616, 23)); + panelControl.setRequestFocusEnabled(false); + + lblCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/deck_pack.png"))); // NOI18N + lblCount.setText("999"); + lblCount.setToolTipText("Number of all cards in this area."); + lblCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + lblCount.setFocusable(false); + lblCount.setInheritsPopupMenu(false); + lblCount.setRequestFocusEnabled(false); + lblCount.setVerifyInputWhenFocusTarget(false); + + lblLandCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblLandCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_land.png"))); // NOI18N + lblLandCount.setText("999"); + lblLandCount.setToolTipText("Number of lands."); + lblLandCount.setVerticalAlignment(javax.swing.SwingConstants.TOP); + lblLandCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + lblLandCount.setFocusable(false); + lblLandCount.setInheritsPopupMenu(false); + lblLandCount.setRequestFocusEnabled(false); + lblLandCount.setVerifyInputWhenFocusTarget(false); + + lblCreatureCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + lblCreatureCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_creatures.png"))); // NOI18N + lblCreatureCount.setText("999"); + lblCreatureCount.setToolTipText("Number of creatures."); + lblCreatureCount.setVerticalAlignment(javax.swing.SwingConstants.TOP); + lblCreatureCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + lblCreatureCount.setFocusable(false); + lblCreatureCount.setInheritsPopupMenu(false); + lblCreatureCount.setRequestFocusEnabled(false); + lblCreatureCount.setVerifyInputWhenFocusTarget(false); + + chkPiles.setText("Piles"); + chkPiles.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + chkPiles.setMargin(new java.awt.Insets(3, 2, 2, 2)); + chkPiles.addActionListener(evt -> chkPilesActionPerformed(evt)); + + cbSortBy.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"SortBy"})); + cbSortBy.setToolTipText("Sort the cards if card view is active."); + cbSortBy.setMaximumSize(new java.awt.Dimension(120, 20)); + cbSortBy.setMinimumSize(new java.awt.Dimension(120, 20)); + cbSortBy.setName("SortBy"); // NOI18N + cbSortBy.setOpaque(false); + cbSortBy.setPreferredSize(new java.awt.Dimension(120, 20)); + cbSortBy.addActionListener(evt -> cbSortByActionPerformed(evt)); + + bgView.add(jToggleListView); + jToggleListView.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/list_panel.png"))); // NOI18N + jToggleListView.setToolTipText("Shows the cards as a list."); + jToggleListView.setBorder(null); + jToggleListView.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + jToggleListView.setMargin(new java.awt.Insets(2, 6, 2, 6)); + jToggleListView.setMaximumSize(new java.awt.Dimension(37, 25)); + jToggleListView.setMinimumSize(new java.awt.Dimension(37, 25)); + jToggleListView.setPreferredSize(new java.awt.Dimension(44, 22)); + jToggleListView.addActionListener(evt -> jToggleListViewActionPerformed(evt)); + + bgView.add(jToggleCardView); + jToggleCardView.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/card_panel.png"))); // NOI18N + jToggleCardView.setToolTipText("Shows the card as images."); + jToggleCardView.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + jToggleCardView.setMargin(new java.awt.Insets(2, 6, 2, 6)); + jToggleCardView.setPreferredSize(new java.awt.Dimension(40, 22)); + jToggleCardView.addActionListener(evt -> jToggleCardViewActionPerformed(evt)); + + javax.swing.GroupLayout panelControlLayout = new javax.swing.GroupLayout(panelControl); + panelControl.setLayout(panelControlLayout); + panelControlLayout.setHorizontalGroup( + panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelControlLayout.createSequentialGroup() + .addComponent(lblCount) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblLandCount) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblCreatureCount) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkPiles) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + panelControlLayout.setVerticalGroup( + panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelControlLayout.createSequentialGroup() + .addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblCount) + .addComponent(lblLandCount) + .addComponent(lblCreatureCount) + .addComponent(chkPiles)) + .addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, 0)) + ); + + jToggleListView.getAccessibleContext().setAccessibleDescription("Switch between image and table view."); + + panelCardArea.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED)); + panelCardArea.setViewportView(cardArea); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, 467, Short.MAX_VALUE) + .addComponent(panelCardArea) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(2, 2, 2) + .addComponent(panelCardArea, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void jToggleCardViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleCardViewActionPerformed + currentView = this; + panelCardArea.setViewportView(cardArea); + cbSortBy.setEnabled(true); + chkPiles.setEnabled(true); + PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView"); + redrawCards(); + }//GEN-LAST:event_jToggleCardViewActionPerformed + + private void jToggleListViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleListViewActionPerformed + currentView = mainModel; + panelCardArea.setViewportView(mainTable); + cbSortBy.setEnabled(false); + chkPiles.setEnabled(false); + PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "listView"); + redrawCards(); + }//GEN-LAST:event_jToggleListViewActionPerformed + + private void cbSortByActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSortByActionPerformed + sortSetting.setSortBy((SortBy) cbSortBy.getSelectedItem()); + drawCards(sortSetting); + }//GEN-LAST:event_cbSortByActionPerformed + + private void chkPilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkPilesActionPerformed + sortSetting.setPilesToggle(chkPiles.isSelected()); + drawCards(sortSetting); + }//GEN-LAST:event_chkPilesActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.ButtonGroup bgView; + private javax.swing.JLayeredPane cardArea; + private javax.swing.JComboBox cbSortBy; + private javax.swing.JCheckBox chkPiles; + private javax.swing.JToggleButton jToggleCardView; + private javax.swing.JToggleButton jToggleListView; + private javax.swing.JLabel lblCount; + private javax.swing.JLabel lblCreatureCount; + private javax.swing.JLabel lblLandCount; + private javax.swing.JScrollPane panelCardArea; + private javax.swing.JPanel panelControl; + // End of variables declaration//GEN-END:variables + + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.getClickCount() >= 1 && !e.isConsumed()) { + Object obj = e.getSource(); + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks + e.consume(); + if (obj instanceof Card) { + if (e.isAltDown()) { + cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); + } else { + cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); + } + } else if (obj instanceof MageCard) { + if (e.isAltDown()) { + cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK); + } else { + cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK); + } + } + } + if (obj instanceof MageCard) { + checkMenu(e, ((MageCard) obj).getOriginal()); + } else { + checkMenu(e, null); + } + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (!e.isConsumed()) { + Object obj = e.getSource(); + if (obj instanceof MageCard) { + checkMenu(e, ((MageCard) obj).getOriginal()); + } else { + checkMenu(e, null); + } + } + } + + private void checkMenu(MouseEvent Me, SimpleCardView card) { + if (Me.isPopupTrigger()) { + Me.consume(); + cardEventSource.fireEvent(card, Me.getComponent(), Me.getX(), Me.getY(), ClientEventType.SHOW_POP_UP_MENU); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + public void setDisplayNoCopies(boolean value) { + mainModel.setDisplayNoCopies(value); + } + + @Override + public int cardsSize() { + return cards.size(); + } + + public void setSortBy(SortBy sortBy) { + if (sortBy != null) { + cbSortBy.setSelectedIndex(sortBy.ordinal()); + } + } + + public void setSortSetting(SortSetting sortSetting) { + this.sortSetting = sortSetting; + } + + } diff --git a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java index 18c9980eb87..c3b6a3346ea 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java @@ -10,6 +10,7 @@ package mage.client.cards; import mage.cards.CardDimensions; import mage.cards.MageCard; +import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.CardViewRarityComparator; import mage.client.util.ClientEventType; @@ -28,7 +29,6 @@ import java.util.ArrayList; import java.util.List; /** - * * @author BetaSteward_at_googlemail.com */ public class DraftGrid extends javax.swing.JPanel implements MouseListener { @@ -40,17 +40,19 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { protected MageCard markedCard; protected boolean emptyGrid; - /** Creates new form DraftGrid */ + /** + * Creates new form DraftGrid + */ public DraftGrid() { initComponents(); markedCard = null; - emptyGrid= true; + emptyGrid = true; } public void clear() { markedCard = null; this.clearCardEventListeners(); - for (Component comp: getComponents()) { + for (Component comp : getComponents()) { if (comp instanceof Card || comp instanceof MageCard) { this.remove(comp); } @@ -79,10 +81,10 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { CardDimensions cardDimension = null; int maxCards; - double scale ; + double scale; for (int i = 1; i < maxRows; i++) { - scale = (double) (this.getHeight()/i) / Constants.FRAME_MAX_HEIGHT; + scale = (double) (this.getHeight() / i) / Constants.FRAME_MAX_HEIGHT; cardDimension = new CardDimensions(scale); maxCards = this.getWidth() / (cardDimension.getFrameWidth() + offsetX); if ((maxCards * i) >= booster.size()) { @@ -100,8 +102,8 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { List sortedCards = new ArrayList<>(booster.values()); sortedCards.sort(new CardViewRarityComparator()); - for (CardView card: sortedCards) { - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, dimension, null, true, true); + for (CardView card : sortedCards) { + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, dimension, null, true, true, PreferencesDialog.getRenderMode()); cardImg.addMouseListener(this); add(cardImg); cardImg.update(card); @@ -133,7 +135,8 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { Plugins.instance.getActionCallback().mouseExited(null, null); } - /** This method is called from within the constructor to + /** + * This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. @@ -145,12 +148,12 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 400, Short.MAX_VALUE) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 400, Short.MAX_VALUE) ); layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 300, Short.MAX_VALUE) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 300, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -160,7 +163,7 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { if (e.getButton() == MouseEvent.BUTTON1) { Object obj = e.getSource(); if (obj instanceof MageCard) { - this.cardEventSource.fireEvent(((MageCard)obj).getOriginal(), ClientEventType.PICK_A_CARD); + this.cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.PICK_A_CARD); this.hidePopup(); AudioManager.playOnDraftSelect(); } @@ -177,8 +180,8 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { if (this.markedCard != null) { markedCard.setSelected(false); } - this.cardEventSource.fireEvent(((MageCard)obj).getOriginal(), ClientEventType.MARK_A_CARD); - markedCard = ((MageCard)obj); + this.cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.MARK_A_CARD); + markedCard = ((MageCard) obj); markedCard.setSelected(true); repaint(); } diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 12ca7ef2f7a..2733c0861f3 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -1,16 +1,5 @@ package mage.client.cards; -import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.*; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import javax.swing.*; import mage.cards.Card; import mage.cards.MageCard; import mage.cards.decks.DeckCardInfo; @@ -31,6 +20,18 @@ import mage.view.CardsView; import org.apache.log4j.Logger; import org.mage.card.arcane.CardRenderer; +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + /** * Created by StravantUser on 2016-09-20. */ @@ -456,6 +457,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg --count; } } + private int count = 0; } @@ -511,14 +513,14 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg }; private final CardTypeCounter[] allCounters = { - creatureCounter, - landCounter, - artifactCounter, - enchantmentCounter, - instantCounter, - planeswalkerCounter, - sorceryCounter, - tribalCounter + creatureCounter, + landCounter, + artifactCounter, + enchantmentCounter, + instantCounter, + planeswalkerCounter, + sorceryCounter, + tribalCounter }; // Listener @@ -659,7 +661,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg @Override public String toString() { - return '(' + sort.toString() + ',' + Boolean.toString(separateCreatures) + ',' + Integer.toString(cardSize) + ')'; + return '(' + sort.toString() + ',' + separateCreatures + ',' + cardSize + ')'; } } @@ -1767,7 +1769,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg updateCounts(); // Create the card view - final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true); + final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true, PreferencesDialog.getRenderMode()); cardPanel.update(card); cardPanel.setCardCaptionTopOffset(0); diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java index 13594995e7e..bb537f8b4b4 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/ChoiceDialog.java @@ -8,6 +8,7 @@ import mage.client.components.ext.dlg.DialogContainer; import mage.client.components.ext.dlg.DialogManager; import mage.client.components.ext.dlg.DlgParams; import mage.client.components.ext.dlg.IDialogPanel; +import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.Command; import mage.client.util.SettingsManager; @@ -162,7 +163,7 @@ public class ChoiceDialog extends IDialogPanel { } CardView card = cardList.get(i); - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true); + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode()); cardImg.setLocation(dx, dy + j * (height + 30)); add(cardImg); diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java index 8645e5f3de1..db4624c7695 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/impl/StackDialog.java @@ -7,6 +7,7 @@ import mage.client.components.ext.dlg.DialogContainer; import mage.client.components.ext.dlg.DialogManager; import mage.client.components.ext.dlg.DlgParams; import mage.client.components.ext.dlg.IDialogPanel; +import mage.client.dialog.PreferencesDialog; import mage.client.game.FeedbackPanel; import mage.client.plugins.impl.Plugins; import mage.client.util.Command; @@ -33,14 +34,14 @@ public class StackDialog extends IDialogPanel { private JLayeredPane jLayeredPane; private final FeedbackPanel feedbackPanel; - + private final UUID gameId; private static class CustomLabel extends JLabel { @Override public void paintComponent(Graphics g) { - Graphics2D g2D = (Graphics2D)g; + Graphics2D g2D = (Graphics2D) g; g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); @@ -66,7 +67,7 @@ public class StackDialog extends IDialogPanel { /** * This method initializes this - * + * * @return void */ private void initialize() { @@ -111,7 +112,7 @@ public class StackDialog extends IDialogPanel { for (CardView card : cards.values()) { if (card instanceof StackAbilityView) { - CardView tmp = ((StackAbilityView)card).getSourceCard(); + CardView tmp = ((StackAbilityView) card).getSourceCard(); tmp.overrideRules(card.getRules()); tmp.setIsAbility(true); tmp.overrideTargets(card.getTargets()); @@ -119,7 +120,7 @@ public class StackDialog extends IDialogPanel { card = tmp; } - MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true); + MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, getCardDimension(), gameId, true, true, PreferencesDialog.getRenderMode()); //cardImg.setBorder(BorderFactory.createLineBorder(Color.red)); cardImg.setLocation(dx, dy); @@ -143,10 +144,11 @@ public class StackDialog extends IDialogPanel { jButtonAccept.setObserver(new Command() { @Override public void execute() { - DialogManager.getManager(gameId).fadeOut((DialogContainer)getParent()); + DialogManager.getManager(gameId).fadeOut((DialogContainer) getParent()); //GameManager.getInputControl().getInput().selectButtonOK(); StackDialog.this.feedbackPanel.doClick(); } + private static final long serialVersionUID = 1L; }); } @@ -166,11 +168,12 @@ public class StackDialog extends IDialogPanel { jButtonResponse.setObserver(new Command() { @Override public void execute() { - DialogManager.getManager(gameId).fadeOut((DialogContainer)getParent()); + DialogManager.getManager(gameId).fadeOut((DialogContainer) getParent()); } + private static final long serialVersionUID = 1L; }); } return jButtonResponse; } - } \ No newline at end of file +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index 80b4c18fbf0..e7fea575ad6 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -8,6 +8,7 @@ import mage.cards.repository.ExpansionRepository; import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.components.HoverButton; +import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.Config; import mage.client.util.ImageHelper; @@ -406,7 +407,7 @@ public class MageBook extends JComponent { if (cardDimension == null) { cardDimension = new Dimension(Config.dimensions.getFrameWidth(), Config.dimensions.getFrameHeight()); } - final MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true); + final MageCard cardImg = Plugins.instance.getMageCard(card, bigCard, cardDimension, gameId, true, true, PreferencesDialog.getRenderMode()); cardImg.setBounds(rectangle); jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 10); cardImg.update(card); @@ -447,7 +448,7 @@ public class MageBook extends JComponent { newToken.removeSummoningSickness(); PermanentView theToken = new PermanentView(newToken, null, null, null); theToken.setInViewerOnly(true); - final MageCard cardImg = Plugins.instance.getMagePermanent(theToken, bigCard, cardDimension, gameId, true); + final MageCard cardImg = Plugins.instance.getMagePermanent(theToken, bigCard, cardDimension, gameId, true, PreferencesDialog.getRenderMode()); cardImg.setBounds(rectangle); jLayeredPane.add(cardImg, JLayeredPane.DEFAULT_LAYER, 10); cardImg.update(theToken); diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index c264e762690..0f26b4f9daa 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -3714,6 +3714,14 @@ public class PreferencesDialog extends javax.swing.JDialog { } } + public static int getRenderMode() { + if (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_FALLBACK, "false").equals("false")) { + return 0; // mtgo + } else { + return 1; // image + } + } + private static int getDefaultControlMofier(String key) { switch (key) { default: diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form index bdc2ec168ac..8e62e1f98fe 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form @@ -1,12 +1,8 @@ -
- - - + - @@ -30,9 +26,17 @@ - + - + + + + + + + + + @@ -44,9 +48,17 @@ - - - + + + + + + + + + + + @@ -63,15 +75,49 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 7833195372c..710f4d26dc2 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -11,6 +11,7 @@ import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionRepository; import mage.client.MageFrame; import mage.client.cards.BigCard; +import mage.client.util.GUISizeHelper; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; import mage.game.Game; @@ -26,6 +27,7 @@ import mage.view.PermanentView; import org.apache.log4j.Logger; import javax.swing.*; +import java.awt.*; import java.awt.event.KeyEvent; import java.util.UUID; @@ -35,6 +37,7 @@ import java.util.UUID; public class TestCardRenderDialog extends MageDialog { private static final Logger logger = Logger.getLogger(TestCardRenderDialog.class); + float cardSizeMod = 1.0f; public TestCardRenderDialog() { initComponents(); @@ -43,6 +46,7 @@ public class TestCardRenderDialog extends MageDialog { public void showDialog() { this.setModal(false); getRootPane().setDefaultButton(buttonCancel); + reloadCards(); // windows settings MageFrame.getDesktop().remove(this); @@ -74,77 +78,17 @@ public class TestCardRenderDialog extends MageDialog { if (damage > 0) { perm.damage(damage, null, game); } + perm.removeSummoningSickness(); PermanentView cardView = new PermanentView(perm, card, controllerId, game); cardView.setInViewerOnly(true); return cardView; } - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { + private void reloadCards() { + cardsPanel.cleanUp(); + cardsPanel.setCustomRenderMode(comboRenderMode.getSelectedIndex()); - buttonCancel = new javax.swing.JButton(); - cardArea1 = new mage.client.cards.CardArea(); - jButton1 = new javax.swing.JButton(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - - buttonCancel.setText("Close"); - buttonCancel.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - buttonCancelActionPerformed(evt); - } - }); - - jButton1.setText("jButton1"); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 578, Short.MAX_VALUE) - .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(cardArea1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(jButton1) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addComponent(jButton1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 88, Short.MAX_VALUE) - .addComponent(cardArea1, javax.swing.GroupLayout.PREFERRED_SIZE, 327, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void buttonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCancelActionPerformed - onCancel(); - }//GEN-LAST:event_buttonCancelActionPerformed - - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed Game game = new TestGame(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20); Player player = new StubPlayer("player1", RangeOfInfluence.ALL); Deck deck = new Deck(); @@ -160,13 +104,147 @@ public class TestCardRenderDialog extends MageDialog { card = createCard(game, player.getId(), "RNA", "14", 1); // Knight of Sorrows view.put(card.getId(), card); - cardArea1.loadCards(view, big, null); - }//GEN-LAST:event_jButton1ActionPerformed + cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); + cardsPanel.changeGUISize(); + + cardsPanel.loadCards(view, big, null); + } + + private int getCardWidth() { + if (GUISizeHelper.editorCardDimension == null) { + return 200; + } + return (int) (GUISizeHelper.editorCardDimension.width * cardSizeMod); + } + + private int getCardHeight() { + return (int) (1.4 * getCardWidth()); + } + + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonCancel = new javax.swing.JButton(); + cardsPanel = new mage.client.cards.CardArea(); + buttonReloadCards = new javax.swing.JButton(); + labelRenderMode = new javax.swing.JLabel(); + comboRenderMode = new javax.swing.JComboBox<>(); + sliderSize = new javax.swing.JSlider(); + labelSize = new javax.swing.JLabel(); + + buttonCancel.setText("Close"); + buttonCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + buttonCancelActionPerformed(evt); + } + }); + + buttonReloadCards.setText("Reload cards"); + buttonReloadCards.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + buttonReloadCardsActionPerformed(evt); + } + }); + + labelRenderMode.setText("Render mode:"); + + comboRenderMode.setModel(new javax.swing.DefaultComboBoxModel<>(new String[]{"MTGO", "Image"})); + comboRenderMode.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + comboRenderModeItemStateChanged(evt); + } + }); + + sliderSize.setValue(25); + sliderSize.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sliderSizeStateChanged(evt); + } + }); + + labelSize.setText("Card size:"); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 578, Short.MAX_VALUE) + .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(cardsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(buttonReloadCards) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelRenderMode) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboRenderMode, javax.swing.GroupLayout.PREFERRED_SIZE, 131, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(labelSize) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(sliderSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(buttonReloadCards) + .addComponent(labelRenderMode) + .addComponent(comboRenderMode, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(labelSize)) + .addComponent(sliderSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cardsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void buttonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCancelActionPerformed + onCancel(); + }//GEN-LAST:event_buttonCancelActionPerformed + + private void buttonReloadCardsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonReloadCardsActionPerformed + reloadCards(); + }//GEN-LAST:event_buttonReloadCardsActionPerformed + + private void comboRenderModeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_comboRenderModeItemStateChanged + reloadCards(); + }//GEN-LAST:event_comboRenderModeItemStateChanged + + private void sliderSizeStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sliderSizeStateChanged + // from DragCardGrid + // Fraction in [-1, 1] + float sliderFrac = ((float) (sliderSize.getValue() - 50)) / 50; + // Convert to frac in [0.5, 2.0] exponentially + cardSizeMod = (float) Math.pow(2, sliderFrac); + reloadCards(); + }//GEN-LAST:event_sliderSizeStateChanged // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton buttonCancel; - private mage.client.cards.CardArea cardArea1; - private javax.swing.JButton jButton1; + private javax.swing.JButton buttonReloadCards; + private mage.client.cards.CardArea cardsPanel; + private javax.swing.JComboBox comboRenderMode; + private javax.swing.JLabel labelRenderMode; + private javax.swing.JLabel labelSize; + private javax.swing.JSlider sliderSize; // End of variables declaration//GEN-END:variables } diff --git a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java index 08a6e2a41e5..c8bb72c81a8 100644 --- a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java @@ -1,254 +1,245 @@ /* - * BattlefieldPanel.java - * - * Created on 10-Jan-2010, 10:43:14 PM - */ -package mage.client.game; + * BattlefieldPanel.java + * + * Created on 10-Jan-2010, 10:43:14 PM + */ + package mage.client.game; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import javax.swing.JComponent; -import javax.swing.JLayeredPane; -import javax.swing.JScrollPane; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import mage.cards.MagePermanent; -import mage.client.cards.BigCard; -import mage.client.cards.Permanent; -import mage.client.plugins.impl.Plugins; -import mage.client.util.Config; -import mage.client.util.GUISizeHelper; -import mage.client.util.audio.AudioManager; -import mage.client.util.layout.CardLayoutStrategy; -import mage.client.util.layout.impl.OldCardLayoutStrategy; -import mage.view.CounterView; -import mage.view.PermanentView; + import mage.cards.MagePermanent; + import mage.client.cards.BigCard; + import mage.client.cards.Permanent; + import mage.client.dialog.PreferencesDialog; + import mage.client.plugins.impl.Plugins; + import mage.client.util.Config; + import mage.client.util.GUISizeHelper; + import mage.client.util.audio.AudioManager; + import mage.client.util.layout.CardLayoutStrategy; + import mage.client.util.layout.impl.OldCardLayoutStrategy; + import mage.view.CounterView; + import mage.view.PermanentView; -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class BattlefieldPanel extends javax.swing.JLayeredPane { + import javax.swing.*; + import javax.swing.border.Border; + import javax.swing.border.EmptyBorder; + import java.awt.*; + import java.awt.event.ComponentAdapter; + import java.awt.event.ComponentEvent; + import java.util.List; + import java.util.*; + import java.util.Map.Entry; - private final Map permanents = new LinkedHashMap<>(); - private UUID gameId; - private BigCard bigCard; - private final Map uiComponentsList = new HashMap<>(); + /** + * @author BetaSteward_at_googlemail.com + */ + public class BattlefieldPanel extends javax.swing.JLayeredPane { - protected Map battlefield; - private Dimension cardDimension; + private final Map permanents = new LinkedHashMap<>(); + private UUID gameId; + private BigCard bigCard; + private final Map uiComponentsList = new HashMap<>(); - private JLayeredPane jPanel; - private JScrollPane jScrollPane; - private int width; + protected Map battlefield; + private Dimension cardDimension; - private final CardLayoutStrategy layoutStrategy = new OldCardLayoutStrategy(); + private JLayeredPane jPanel; + private JScrollPane jScrollPane; + private int width; - //private static int iCounter = 0; - private boolean addedPermanent; - private boolean addedArtifact; - private boolean addedCreature; + private final CardLayoutStrategy layoutStrategy = new OldCardLayoutStrategy(); - private boolean removedCreature; - // defines if the battlefield is within a top (means top row of player panels) or a bottom player panel - private boolean topPanelBattlefield; + //private static int iCounter = 0; + private boolean addedPermanent; + private boolean addedArtifact; + private boolean addedCreature; - /** - * Creates new form BattlefieldPanel - */ - public BattlefieldPanel() { - uiComponentsList.put("battlefieldPanel", this); - initComponents(); - uiComponentsList.put("jPanel", jPanel); - setGUISize(); + private boolean removedCreature; + // defines if the battlefield is within a top (means top row of player panels) or a bottom player panel + private boolean topPanelBattlefield; - addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - int width = e.getComponent().getWidth(); - int height = e.getComponent().getHeight(); - BattlefieldPanel.this.jScrollPane.setSize(width, height); - BattlefieldPanel.this.width = width; - sortLayout(); - } - }); - } + /** + * Creates new form BattlefieldPanel + */ + public BattlefieldPanel() { + uiComponentsList.put("battlefieldPanel", this); + initComponents(); + uiComponentsList.put("jPanel", jPanel); + setGUISize(); - public void init(UUID gameId, BigCard bigCard) { - this.gameId = gameId; - this.bigCard = bigCard; - } + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + int width = e.getComponent().getWidth(); + int height = e.getComponent().getHeight(); + BattlefieldPanel.this.jScrollPane.setSize(width, height); + BattlefieldPanel.this.width = width; + sortLayout(); + } + }); + } - public void cleanUp() { - for (Component c : this.jPanel.getComponents()) { - if (c instanceof Permanent || c instanceof MagePermanent) { - this.jPanel.remove(c); - } - } - permanents.clear(); - // Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values()); - this.bigCard = null; - } + public void init(UUID gameId, BigCard bigCard) { + this.gameId = gameId; + this.bigCard = bigCard; + } - public void changeGUISize() { - setGUISize(); - sortLayout(); - } + public void cleanUp() { + for (Component c : this.jPanel.getComponents()) { + if (c instanceof Permanent || c instanceof MagePermanent) { + this.jPanel.remove(c); + } + } + permanents.clear(); + // Plugins.getInstance().sortPermanents(uiComponentsList, permanents.values()); + this.bigCard = null; + } - private void setGUISize() { - jScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); - jScrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); - cardDimension = GUISizeHelper.battlefieldCardMaxDimension; - } + public void changeGUISize() { + setGUISize(); + sortLayout(); + } - public boolean isTopPanelBattlefield() { - return topPanelBattlefield; - } + private void setGUISize() { + jScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); + jScrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); + cardDimension = GUISizeHelper.battlefieldCardMaxDimension; + } - public void setTopPanelBattlefield(boolean topPanelBattlefield) { - this.topPanelBattlefield = topPanelBattlefield; - } + public boolean isTopPanelBattlefield() { + return topPanelBattlefield; + } - public void update(Map battlefield) { - boolean changed = false; + public void setTopPanelBattlefield(boolean topPanelBattlefield) { + this.topPanelBattlefield = topPanelBattlefield; + } - List permanentsToAdd = new ArrayList<>(); - for (PermanentView permanent : battlefield.values()) { - if (!permanent.isPhasedIn()) { - continue; - } - MagePermanent oldMagePermanent = permanents.get(permanent.getId()); - if (oldMagePermanent == null) { - permanentsToAdd.add(permanent); - changed = true; - } else { - if (!changed) { - changed = oldMagePermanent.getOriginalPermanent().isCreature() != permanent.isCreature(); - // Check if there was a chnage in the permanets that are the permanent attached to - if (!changed) { - int attachments = permanent.getAttachments() == null ? 0 : permanent.getAttachments().size(); - int attachmentsBefore = oldMagePermanent.getLinks().size(); - if (attachments != attachmentsBefore) { - changed = true; - } else if (attachments > 0) { - Set attachmentIds = new HashSet<>(permanent.getAttachments()); - for (MagePermanent magePermanent : oldMagePermanent.getLinks()) { - if (!attachmentIds.contains(magePermanent.getOriginalPermanent().getId())) { - // that means that the amount of attachments is the same - // but they are different: - // we've just found an attachment on previous view - // that doesn't exist anymore on current view - changed = true; - break; - } - } - } - } - // Check if permanents it now attached to another or no permanent - if (!changed) { - UUID attachedToIdBefore = oldMagePermanent.getOriginalPermanent().getAttachedTo(); - UUID attachedToId = permanent.getAttachedTo(); - if (attachedToIdBefore == null && attachedToId != null || attachedToId == null && attachedToIdBefore != null - || (attachedToIdBefore != null && !attachedToIdBefore.equals(attachedToId))) { - changed = true; - } - } - // Check for changes in the counters of the permanent - if (!changed) { - List counters1 = oldMagePermanent.getOriginalPermanent().getCounters(); - List counters2 = permanent.getCounters(); - if (counters1 == null && counters2 != null || counters1 != null && counters2 == null) { - changed = true; - } else if (counters1 != null && counters2 != null && counters1.size() != counters2.size()) { - changed = true; - } - } + public void update(Map battlefield) { + boolean changed = false; - } - oldMagePermanent.update(permanent); - } - } + List permanentsToAdd = new ArrayList<>(); + for (PermanentView permanent : battlefield.values()) { + if (!permanent.isPhasedIn()) { + continue; + } + MagePermanent oldMagePermanent = permanents.get(permanent.getId()); + if (oldMagePermanent == null) { + permanentsToAdd.add(permanent); + changed = true; + } else { + if (!changed) { + changed = oldMagePermanent.getOriginalPermanent().isCreature() != permanent.isCreature(); + // Check if there was a chnage in the permanets that are the permanent attached to + if (!changed) { + int attachments = permanent.getAttachments() == null ? 0 : permanent.getAttachments().size(); + int attachmentsBefore = oldMagePermanent.getLinks().size(); + if (attachments != attachmentsBefore) { + changed = true; + } else if (attachments > 0) { + Set attachmentIds = new HashSet<>(permanent.getAttachments()); + for (MagePermanent magePermanent : oldMagePermanent.getLinks()) { + if (!attachmentIds.contains(magePermanent.getOriginalPermanent().getId())) { + // that means that the amount of attachments is the same + // but they are different: + // we've just found an attachment on previous view + // that doesn't exist anymore on current view + changed = true; + break; + } + } + } + } + // Check if permanents it now attached to another or no permanent + if (!changed) { + UUID attachedToIdBefore = oldMagePermanent.getOriginalPermanent().getAttachedTo(); + UUID attachedToId = permanent.getAttachedTo(); + if (attachedToIdBefore == null && attachedToId != null || attachedToId == null && attachedToIdBefore != null + || (attachedToIdBefore != null && !attachedToIdBefore.equals(attachedToId))) { + changed = true; + } + } + // Check for changes in the counters of the permanent + if (!changed) { + List counters1 = oldMagePermanent.getOriginalPermanent().getCounters(); + List counters2 = permanent.getCounters(); + if (counters1 == null && counters2 != null || counters1 != null && counters2 == null) { + changed = true; + } else if (counters1 != null && counters2 != null && counters1.size() != counters2.size()) { + changed = true; + } + } - addedArtifact = addedCreature = addedPermanent = false; + } + oldMagePermanent.update(permanent); + } + } - int count = permanentsToAdd.size(); - for (PermanentView permanent : permanentsToAdd) { - addPermanent(permanent, count); - } + addedArtifact = addedCreature = addedPermanent = false; - if (addedArtifact) { - AudioManager.playAddArtifact(); - } else if (addedCreature) { - AudioManager.playSummon(); - } else if (addedPermanent) { - AudioManager.playAddPermanent(); - } + int count = permanentsToAdd.size(); + for (PermanentView permanent : permanentsToAdd) { + addPermanent(permanent, count); + } - removedCreature = false; + if (addedArtifact) { + AudioManager.playAddArtifact(); + } else if (addedCreature) { + AudioManager.playSummon(); + } else if (addedPermanent) { + AudioManager.playAddPermanent(); + } - for (Iterator> iterator = permanents.entrySet().iterator(); iterator.hasNext();) { - Entry entry = iterator.next(); - if (!battlefield.containsKey(entry.getKey()) || !battlefield.get(entry.getKey()).isPhasedIn()) { - removePermanent(entry.getKey(), 1); - iterator.remove(); - changed = true; - } - } + removedCreature = false; - if (removedCreature) { - AudioManager.playDiedCreature(); - } + for (Iterator> iterator = permanents.entrySet().iterator(); iterator.hasNext(); ) { + Entry entry = iterator.next(); + if (!battlefield.containsKey(entry.getKey()) || !battlefield.get(entry.getKey()).isPhasedIn()) { + removePermanent(entry.getKey(), 1); + iterator.remove(); + changed = true; + } + } - if (changed) { - this.battlefield = battlefield; - sortLayout(); - } - } + if (removedCreature) { + AudioManager.playDiedCreature(); + } - public void sortLayout() { - if (battlefield == null || this.getWidth() < 1) { // Can't do layout when panel is not sized yet - return; - } + if (changed) { + this.battlefield = battlefield; + sortLayout(); + } + } - layoutStrategy.doLayout(this, width); + public void sortLayout() { + if (battlefield == null || this.getWidth() < 1) { // Can't do layout when panel is not sized yet + return; + } - this.jScrollPane.repaint(); - this.jScrollPane.revalidate(); + layoutStrategy.doLayout(this, width); - invalidate(); - repaint(); - } + this.jScrollPane.repaint(); + this.jScrollPane.revalidate(); - private void addPermanent(PermanentView permanent, final int count) { - if (cardDimension == null) { - cardDimension = new Dimension(Config.dimensions.getFrameWidth(), Config.dimensions.getFrameHeight()); - } - final MagePermanent perm = Plugins.instance.getMagePermanent(permanent, bigCard, cardDimension, gameId, true); + invalidate(); + repaint(); + } - permanents.put(permanent.getId(), perm); + private void addPermanent(PermanentView permanent, final int count) { + if (cardDimension == null) { + cardDimension = new Dimension(Config.dimensions.getFrameWidth(), Config.dimensions.getFrameHeight()); + } + final MagePermanent perm = Plugins.instance.getMagePermanent(permanent, bigCard, cardDimension, gameId, true, PreferencesDialog.getRenderMode()); - BattlefieldPanel.this.jPanel.add(perm, 10); - //this.jPanel.add(perm); - if (!Plugins.instance.isCardPluginLoaded()) { - moveToFront(perm); - perm.update(permanent); - } else { - moveToFront(jPanel); - Plugins.instance.onAddCard(perm, 1); + permanents.put(permanent.getId(), perm); + + BattlefieldPanel.this.jPanel.add(perm, 10); + //this.jPanel.add(perm); + if (!Plugins.instance.isCardPluginLoaded()) { + moveToFront(perm); + perm.update(permanent); + } else { + moveToFront(jPanel); + Plugins.instance.onAddCard(perm, 1); /*Thread t = new Thread(new Runnable() { @Override public void run() { @@ -258,76 +249,76 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { synchronized (this) { threads.add(t); }*/ - } + } - if (permanent.isArtifact()) { - addedArtifact = true; - } else if (permanent.isCreature()) { - addedCreature = true; - } else { - addedPermanent = true; - } - } + if (permanent.isArtifact()) { + addedArtifact = true; + } else if (permanent.isCreature()) { + addedCreature = true; + } else { + addedPermanent = true; + } + } - private void removePermanent(UUID permanentId, final int count) { - for (Component c : this.jPanel.getComponents()) { - final Component comp = c; - if (comp instanceof Permanent) { - if (((Permanent) comp).getPermanentId().equals(permanentId)) { - comp.setVisible(false); - this.jPanel.remove(comp); - } - } else if (comp instanceof MagePermanent) { - if (((MagePermanent) comp).getOriginal().getId().equals(permanentId)) { - Thread t = new Thread(() -> { - Plugins.instance.onRemoveCard((MagePermanent) comp, count); - comp.setVisible(false); - BattlefieldPanel.this.jPanel.remove(comp); - }); - t.start(); - } - if (((MagePermanent) comp).getOriginal().isCreature()) { - removedCreature = true; - } - } - } - } + private void removePermanent(UUID permanentId, final int count) { + for (Component c : this.jPanel.getComponents()) { + final Component comp = c; + if (comp instanceof Permanent) { + if (((Permanent) comp).getPermanentId().equals(permanentId)) { + comp.setVisible(false); + this.jPanel.remove(comp); + } + } else if (comp instanceof MagePermanent) { + if (((MagePermanent) comp).getOriginal().getId().equals(permanentId)) { + Thread t = new Thread(() -> { + Plugins.instance.onRemoveCard((MagePermanent) comp, count); + comp.setVisible(false); + BattlefieldPanel.this.jPanel.remove(comp); + }); + t.start(); + } + if (((MagePermanent) comp).getOriginal().isCreature()) { + removedCreature = true; + } + } + } + } - @Override - public boolean isOptimizedDrawingEnabled() { - return false; - } + @Override + public boolean isOptimizedDrawingEnabled() { + return false; + } - public Map getPermanents() { - return permanents; - } + public Map getPermanents() { + return permanents; + } - private void initComponents() { - setOpaque(false); + private void initComponents() { + setOpaque(false); - jPanel = new JLayeredPane(); - jPanel.setLayout(null); - jPanel.setOpaque(false); - jScrollPane = new JScrollPane(jPanel); + jPanel = new JLayeredPane(); + jPanel.setLayout(null); + jPanel.setOpaque(false); + jScrollPane = new JScrollPane(jPanel); - Border empty = new EmptyBorder(0, 0, 0, 0); - jScrollPane.setBorder(empty); - jScrollPane.setViewportBorder(empty); - jScrollPane.setOpaque(false); - jScrollPane.getViewport().setOpaque(false); + Border empty = new EmptyBorder(0, 0, 0, 0); + jScrollPane.setBorder(empty); + jScrollPane.setViewportBorder(empty); + jScrollPane.setOpaque(false); + jScrollPane.getViewport().setOpaque(false); - this.add(jScrollPane); - } + this.add(jScrollPane); + } - public JLayeredPane getMainPanel() { - return jPanel; - } + public JLayeredPane getMainPanel() { + return jPanel; + } - public Map getBattlefield() { - return battlefield; - } + public Map getBattlefield() { + return battlefield; + } - public Map getUiComponentsList() { - return uiComponentsList; - } -} + public Map getUiComponentsList() { + return uiComponentsList; + } + } diff --git a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java index 5574f1c8c38..73b5565800c 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java @@ -1,10 +1,5 @@ package mage.client.plugins; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.Map; -import java.util.UUID; -import javax.swing.*; import mage.cards.MageCard; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; @@ -12,6 +7,12 @@ import mage.client.cards.BigCard; import mage.view.CardView; import mage.view.PermanentView; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.Map; +import java.util.UUID; + public interface MagePlugins { void loadPlugins(); @@ -22,9 +23,9 @@ public interface MagePlugins { JComponent updateTablePanel(Map ui); - MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage); + MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, int renderMode); - MageCard getMageCard(CardView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable); + MageCard getMageCard(CardView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable, int renderMode); boolean isThemePluginLoaded(); diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java index e63d1042194..f9419441785 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java @@ -1,12 +1,5 @@ package mage.client.plugins.impl; -import java.awt.Dimension; -import java.awt.image.BufferedImage; -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import javax.swing.JComponent; import mage.cards.MageCard; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; @@ -27,12 +20,20 @@ import mage.view.PermanentView; import net.xeoh.plugins.base.PluginManager; import net.xeoh.plugins.base.impl.PluginManagerFactory; import net.xeoh.plugins.base.util.uri.ClassURI; - import org.apache.log4j.Logger; import org.mage.plugins.card.CardPluginImpl; -import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; import org.mage.plugins.theme.ThemePluginImpl; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; + public enum Plugins implements MagePlugins { instance; public static final String PLUGINS_DIRECTORY = "plugins"; @@ -92,24 +93,24 @@ public enum Plugins implements MagePlugins { } @Override - public MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage) { + public MagePermanent getMagePermanent(PermanentView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, int renderMode) { if (cardPlugin != null) { mageActionCallback.refreshSession(); mageActionCallback.setCardPreviewComponent(bigCard); - return cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage); + return cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode); } else { return new Permanent(card, bigCard, Config.dimensions, gameId); } } @Override - public MageCard getMageCard(CardView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable) { + public MageCard getMageCard(CardView card, BigCard bigCard, Dimension dimension, UUID gameId, boolean loadImage, boolean previewable, int renderMode) { if (cardPlugin != null) { if (previewable) { mageActionCallback.refreshSession(); mageActionCallback.setCardPreviewComponent(bigCard); } - return cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage); + return cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode); } else { return new Card(card, bigCard, Config.dimensions, gameId); } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 4e690bf1a56..c43151d60ac 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -2,7 +2,6 @@ package org.mage.plugins.card; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; -import mage.client.dialog.PreferencesDialog; import mage.client.util.GUISizeHelper; import mage.interfaces.plugin.CardPlugin; import mage.view.CardView; @@ -99,25 +98,28 @@ public class CardPluginImpl implements CardPlugin { * Temporary card rendering shim. Split card rendering isn't implemented * yet, so use old component based rendering for the split cards. */ - private CardPanel makePanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback, boolean isFoil, Dimension dimension) { - String fallback = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_FALLBACK, "false"); - if (fallback.equals("true")) { - return new CardPanelComponentImpl(view, gameId, loadImage, callback, isFoil, dimension); - } else { - return new CardPanelRenderImpl(view, gameId, loadImage, callback, isFoil, dimension); + private CardPanel makePanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback, boolean isFoil, Dimension dimension, int renderMode) { + switch (renderMode) { + case 0: + return new CardPanelRenderImpl(view, gameId, loadImage, callback, isFoil, dimension); + case 1: + return new CardPanelComponentImpl(view, gameId, loadImage, callback, isFoil, dimension); + default: + throw new IllegalStateException("Unknown render mode " + renderMode); + } } @Override - public MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage) { - CardPanel cardPanel = makePanel(permanent, gameId, loadImage, callback, false, dimension); + public MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode) { + CardPanel cardPanel = makePanel(permanent, gameId, loadImage, callback, false, dimension, renderMode); cardPanel.setShowCastingCost(true); return cardPanel; } @Override - public MagePermanent getMageCard(CardView cardView, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage) { - CardPanel cardPanel = makePanel(cardView, gameId, loadImage, callback, false, dimension); + public MagePermanent getMageCard(CardView cardView, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode) { + CardPanel cardPanel = makePanel(cardView, gameId, loadImage, callback, false, dimension, renderMode); cardPanel.setShowCastingCost(true); return cardPanel; } diff --git a/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java b/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java index 7aab71f8b5f..138c71073f3 100644 --- a/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java +++ b/Mage.Common/src/main/java/mage/interfaces/plugin/CardPlugin.java @@ -1,30 +1,28 @@ package mage.interfaces.plugin; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.Map; -import java.util.UUID; -import javax.swing.*; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; import mage.view.CardView; import mage.view.PermanentView; import net.xeoh.plugins.base.Plugin; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.Map; +import java.util.UUID; + /** * Interface for card plugins * - * @version 0.6 17.07.2011 added options to #sortPermanents - * @version 0.3 21.11.2010 #getMageCard - * @version 0.2 07.11.2010 #downloadImages - * @version 0.1 31.10.2010 #getMagePermanent, #sortPermanents * @author nantuko + * @version 0.1 31.10.2010 #getMagePermanent, #sortPermanents */ public interface CardPlugin extends Plugin { - MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage); + MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode); - MagePermanent getMageCard(CardView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage); + MagePermanent getMageCard(CardView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage, int renderMode); int sortPermanents(Map ui, Map cards, boolean nonPermanentsOwnRow, boolean topPanel); From d1e24581b77579995c980c4b1c5c80f49b8e7c21 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 7 May 2019 17:04:19 -0500 Subject: [PATCH 49/76] - Fixed #5788 --- Mage.Sets/src/mage/cards/t/TidehollowSculler.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TidehollowSculler.java b/Mage.Sets/src/mage/cards/t/TidehollowSculler.java index b1e2bacc962..16341e86931 100644 --- a/Mage.Sets/src/mage/cards/t/TidehollowSculler.java +++ b/Mage.Sets/src/mage/cards/t/TidehollowSculler.java @@ -17,7 +17,6 @@ import mage.constants.Zone; import mage.filter.common.FilterNonlandCard; import mage.game.ExileZone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.TargetCard; @@ -79,11 +78,9 @@ class TidehollowScullerExileEffect extends OneShotEffect { // 6/7/2013 If Tidehollow Sculler leaves the battlefield before its first ability has resolved, // its second ability will trigger. This ability will do nothing when it resolves. // Then its first ability will resolve and exile the chosen card forever. - Permanent sourcePermanent = (Permanent) source.getSourcePermanentIfItStillExists(game); if (controller != null - && opponent != null - && sourcePermanent != null) { - opponent.revealCards(sourcePermanent.getName(), opponent.getHand(), game); + && opponent != null) { + opponent.revealCards("Tidehollow Sculler", opponent.getHand(), game); TargetCard target = new TargetCard(Zone.HAND, new FilterNonlandCard("nonland card to exile")); if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) { Card card = opponent.getHand().get(target.getFirstTarget(), game); @@ -96,7 +93,7 @@ class TidehollowScullerExileEffect extends OneShotEffect { CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), - sourcePermanent.getIdName()); + "Tidehollow Sculler"); } } return true; @@ -126,8 +123,8 @@ class TidehollowScullerLeaveEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (controller != null && sourceObject != null) { - int zoneChangeCounter = (sourceObject instanceof PermanentToken) - ? source.getSourceObjectZoneChangeCounter() + int zoneChangeCounter = (sourceObject instanceof PermanentToken) + ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1; ExileZone exZone = game.getExile().getExileZone( CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); From 00a1dcb57393a32f3c7ea521608f28d10afbd7c6 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 8 May 2019 15:55:19 +0200 Subject: [PATCH 50/76] Implemented Bosium Strip (untested) (#5785) * Implemented Bosium Strip * Implemented Bosium Strip * Updated Bosium Strip --- Mage.Sets/src/mage/cards/b/BosiumStrip.java | 142 ++++++++++++++++++++ Mage.Sets/src/mage/sets/Weatherlight.java | 1 + 2 files changed, 143 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BosiumStrip.java diff --git a/Mage.Sets/src/mage/cards/b/BosiumStrip.java b/Mage.Sets/src/mage/cards/b/BosiumStrip.java new file mode 100644 index 00000000000..1a008842c76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BosiumStrip.java @@ -0,0 +1,142 @@ + +package mage.cards.b; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.watchers.common.CastFromGraveyardWatcher; + +/** + * + * @author spjspj & L_J + */ +public final class BosiumStrip extends CardImpl { + + public BosiumStrip(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {3}, {T}: Until end of turn, if the top card of your graveyard is an instant or sorcery card, you may cast that card. If a card cast this way would be put into a graveyard this turn, exile it instead. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BosiumStripCastFromGraveyardEffect(), new ManaCostsImpl("{3}")); + ability.addCost(new TapSourceCost()); + ability.addEffect(new BosiumStripReplacementEffect()); + this.addAbility(ability, new CastFromGraveyardWatcher()); + } + + public BosiumStrip(final BosiumStrip card) { + super(card); + } + + @Override + public BosiumStrip copy() { + return new BosiumStrip(this); + } +} + +class BosiumStripCastFromGraveyardEffect extends AsThoughEffectImpl { + + BosiumStripCastFromGraveyardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + staticText = "Until end of turn, if the top card of your graveyard is an instant or sorcery card, you may cast that card"; + } + + BosiumStripCastFromGraveyardEffect(final BosiumStripCastFromGraveyardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public BosiumStripCastFromGraveyardEffect copy() { + return new BosiumStripCastFromGraveyardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!(source instanceof FlashbackAbility) + && affectedControllerId.equals(source.getControllerId())) { + Player player = game.getPlayer(affectedControllerId); + Card card = game.getCard(objectId); + if (card != null + && player != null + && card.equals(player.getGraveyard().getTopCard(game)) + && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(card, game) + && game.getState().getZone(objectId) == Zone.GRAVEYARD) { + game.getState().setValue("BosiumStrip", card); + return true; + } + } + return false; + } +} + +class BosiumStripReplacementEffect extends ReplacementEffectImpl { + + BosiumStripReplacementEffect() { + super(Duration.EndOfTurn, Outcome.Exile); + staticText = "If a card cast this way would be put into a graveyard this turn, exile it instead"; + } + + BosiumStripReplacementEffect(final BosiumStripReplacementEffect effect) { + super(effect); + } + + @Override + public BosiumStripReplacementEffect copy() { + return new BosiumStripReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card card = (Card) game.getState().getValue("BosiumStrip"); + if (card != null) { + controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.STACK, true); + return true; + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getToZone() == Zone.GRAVEYARD) { + Card card = game.getCard(event.getSourceId()); + if (card != null + && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(card, game)) { + CastFromGraveyardWatcher watcher = game.getState().getWatcher(CastFromGraveyardWatcher.class); + return watcher != null + && watcher.spellWasCastFromGraveyard(event.getTargetId(), + game.getState().getZoneChangeCounter(event.getTargetId())); + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java index 23770049ec3..b8be7cb40e9 100644 --- a/Mage.Sets/src/mage/sets/Weatherlight.java +++ b/Mage.Sets/src/mage/sets/Weatherlight.java @@ -55,6 +55,7 @@ public final class Weatherlight extends ExpansionSet { cards.add(new SetCardInfo("Bogardan Firefiend", 91, Rarity.COMMON, mage.cards.b.BogardanFirefiend.class)); cards.add(new SetCardInfo("Boiling Blood", 92, Rarity.COMMON, mage.cards.b.BoilingBlood.class)); cards.add(new SetCardInfo("Bone Dancer", 62, Rarity.RARE, mage.cards.b.BoneDancer.class)); + cards.add(new SetCardInfo("Bosium Strip", 147, Rarity.RARE, mage.cards.b.BosiumStrip.class)); cards.add(new SetCardInfo("Briar Shield", 121, Rarity.COMMON, mage.cards.b.BriarShield.class)); cards.add(new SetCardInfo("Bubble Matrix", 146, Rarity.RARE, mage.cards.b.BubbleMatrix.class)); cards.add(new SetCardInfo("Buried Alive", 63, Rarity.UNCOMMON, mage.cards.b.BuriedAlive.class)); From ac3c78031242530212dd98395aba8b556eafcf38 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 8 May 2019 09:33:05 -0500 Subject: [PATCH 51/76] - The Wanderer will now prevent noncombat damage to the controller. --- Mage.Sets/src/mage/cards/t/TheWanderer.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TheWanderer.java b/Mage.Sets/src/mage/cards/t/TheWanderer.java index 4bdfe787fce..f1820f59683 100644 --- a/Mage.Sets/src/mage/cards/t/TheWanderer.java +++ b/Mage.Sets/src/mage/cards/t/TheWanderer.java @@ -20,12 +20,15 @@ import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; +import mage.abilities.effects.common.PreventDamageToControllerEffect; /** * @author TheElk801 */ public final class TheWanderer extends CardImpl { + private static final String rule = "Prevent all noncombat damage that " + + "would be dealt to you and other permanents you control."; private static final FilterPermanent filter = new FilterControlledPermanent("other permanents you control"); private static final FilterPermanent filter2 @@ -43,11 +46,15 @@ public final class TheWanderer extends CardImpl { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // Prevent all noncombat damage that would be dealt to you and other permanents you control. + this.addAbility(new SimpleStaticAbility( + new PreventDamageToControllerEffect( + Duration.WhileOnBattlefield, true, false, + Integer.MAX_VALUE + ).setText(rule))); this.addAbility(new SimpleStaticAbility( new PreventAllNonCombatDamageToAllEffect( Duration.WhileOnBattlefield, filter, true - ) - )); + ).setText(""))); // -2: Exile target creature with power 4 or greater. Ability ability = new LoyaltyAbility(new ExileTargetEffect(), -2); From 060766bb0a2f284304506142d49fe49ef06dbfea Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 8 May 2019 17:36:31 +0200 Subject: [PATCH 52/76] * Updated UntapAllDuringEachOtherPlayersUntapStepEffect to also work correct with EndTurn effects played last turn. --- ...lDuringEachOtherPlayersUntapStepEffect.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/UntapAllDuringEachOtherPlayersUntapStepEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/UntapAllDuringEachOtherPlayersUntapStepEffect.java index 0b8c11b397c..bc2b0c89358 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/UntapAllDuringEachOtherPlayersUntapStepEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/UntapAllDuringEachOtherPlayersUntapStepEffect.java @@ -33,13 +33,13 @@ public class UntapAllDuringEachOtherPlayersUntapStepEffect extends ContinuousEff @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Boolean applied = (Boolean) game.getState().getValue(source.getSourceId() + "applied"); - if (applied == null) { - applied = Boolean.FALSE; - } - if (!applied && layer == Layer.RulesEffects) { - if (!source.isControlledBy(game.getActivePlayerId()) && game.getStep().getType() == PhaseStep.UNTAP) { - game.getState().setValue(source.getSourceId() + "applied", true); + if (layer == Layer.RulesEffects && game.getStep().getType() == PhaseStep.UNTAP && !source.isControlledBy(game.getActivePlayerId())) { + Integer appliedTurn = (Integer) game.getState().getValue(source.getSourceId() + "appliedTurn"); + if (appliedTurn == null) { + appliedTurn = 0; + } + if (appliedTurn < game.getTurnNum()) { + game.getState().setValue(source.getSourceId() + "appliedTurn", game.getTurnNum()); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { boolean untap = true; for (RestrictionEffect effect : game.getContinuousEffects().getApplicableRestrictionEffects(permanent, game).keySet()) { @@ -50,10 +50,6 @@ public class UntapAllDuringEachOtherPlayersUntapStepEffect extends ContinuousEff } } } - } else if (applied && layer == Layer.RulesEffects) { - if (game.getStep().getType() == PhaseStep.END_TURN) { - game.getState().setValue(source.getSourceId() + "applied", false); - } } return true; } From 5c48803ef9dbd425929330773cf45b5739ffb2ef Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 10 May 2019 10:01:51 +0400 Subject: [PATCH 53/76] * UI: improved cards appearance: * added colorized PT values (boosted is green, unboosted is red); * added toughness with damage calcs (damaged is red); * image render: now title and PT texts are readable/big in small cards; * mtgo render: improved image quality (less pixelated now); * mtgo render: improved PT font (bold now); --- .../client/dialog/TestCardRenderDialog.form | 3 - .../client/dialog/TestCardRenderDialog.java | 30 +++-- .../mage/client/util/gui/GuiDisplayUtil.java | 8 +- .../card/arcane/CardPanelComponentImpl.java | 116 ++++++++++++------ .../mage/card/arcane/CardPanelRenderImpl.java | 2 + .../mage/card/arcane/CardRendererUtils.java | 72 +++++++++-- .../java/org/mage/card/arcane/GlowText.java | 27 ++-- .../mage/card/arcane/ModernCardRenderer.java | 95 +++++++++++--- .../main/java/mage/MageObjectReference.java | 4 + 9 files changed, 268 insertions(+), 89 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form index 8e62e1f98fe..db55a2d6c57 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form @@ -107,9 +107,6 @@
- - - diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 710f4d26dc2..d52bb111729 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -29,6 +29,8 @@ import org.apache.log4j.Logger; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; /** @@ -67,18 +69,25 @@ public class TestCardRenderDialog extends MageDialog { this.removeDialog(); } - private PermanentView createCard(Game game, UUID controllerId, String code, String cardNumber, int damage) { + private PermanentView createCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage) { CardInfo cardInfo = CardRepository.instance.findCard(code, cardNumber); ExpansionInfo setInfo = ExpansionRepository.instance.getSetByCode(code); CardSetInfo testSet = new CardSetInfo(cardInfo.getName(), setInfo.getCode(), cardNumber, cardInfo.getRarity(), new CardGraphicInfo(cardInfo.getFrameStyle(), cardInfo.usesVariousArt())); Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); + Set cardsList = new HashSet<>(); + cardsList.add(card); + game.loadCards(cardsList, controllerId); + PermanentCard perm = new PermanentCard(card, controllerId, game); - if (damage > 0) { - perm.damage(damage, null, game); - } + if (damage > 0) perm.damage(damage, controllerId, game); + if (power > 0) perm.getPower().setValue(power); + if (toughness > 0) perm.getToughness().setValue(toughness); perm.removeSummoningSickness(); + if (perm.isTransformable()) { + perm.setTransformed(true); + } PermanentView cardView = new PermanentView(perm, card, controllerId, game); cardView.setInViewerOnly(true); @@ -97,11 +106,17 @@ public class TestCardRenderDialog extends MageDialog { BigCard big = new BigCard(); CardsView view = new CardsView(); PermanentView card; - card = createCard(game, player.getId(), "RNA", "263", 0); // mountain + card = createCard(game, player.getId(), "RNA", "263", 0, 0, 0); // mountain view.put(card.getId(), card); - card = createCard(game, player.getId(), "RNA", "185", 0); // Judith, the Scourge Diva + card = createCard(game, player.getId(), "RNA", "185", 0, 0, 0); // Judith, the Scourge Diva view.put(card.getId(), card); - card = createCard(game, player.getId(), "RNA", "14", 1); // Knight of Sorrows + card = createCard(game, player.getId(), "RNA", "78", 125, 89, 0); // Noxious Groodion + view.put(card.getId(), card); + card = createCard(game, player.getId(), "RNA", "14", 3, 5, 2); // Knight of Sorrows + view.put(card.getId(), card); + card = createCard(game, player.getId(), "DKA", "140", 5, 2, 2); // Huntmaster of the Fells, transforms + view.put(card.getId(), card); + card = createCard(game, player.getId(), "RNA", "221", 0, 0, 0); // Bedeck // Bedazzle view.put(card.getId(), card); cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); @@ -162,7 +177,6 @@ public class TestCardRenderDialog extends MageDialog { } }); - sliderSize.setValue(25); sliderSize.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { sliderSizeStateChanged(evt); diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index d075cefeeb3..aa1cce0b27a 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -181,10 +181,14 @@ public final class GuiDisplayUtil { public static TextLines getTextLinesfromCardView(CardView card) { TextLines textLines = new TextLines(); + + // rules textLines.setLines(new ArrayList<>(card.getRules())); for (String rule : card.getRules()) { textLines.setBasicTextLength(textLines.getBasicTextLength() + rule.length()); } + + // counters if (card.getMageObjectType().canHaveCounters()) { ArrayList counters = new ArrayList<>(); if (card instanceof PermanentView) { @@ -212,10 +216,12 @@ public final class GuiDisplayUtil { textLines.setBasicTextLength(textLines.getBasicTextLength() + 50); } } + + // damage if (card.getMageObjectType().isPermanent() && card instanceof PermanentView) { int damage = ((PermanentView) card).getDamage(); if (damage > 0) { - textLines.getLines().add("Damage dealt: " + damage + ""); + textLines.getLines().add("Damage dealt: " + damage + ""); // TODO textLines.setBasicTextLength(textLines.getBasicTextLength() + 50); } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java index a237318a174..97d84f38138 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java @@ -1,5 +1,6 @@ package org.mage.card.arcane; +import mage.MageInt; import mage.cards.action.ActionCallback; import mage.client.constants.Constants; import mage.client.dialog.PreferencesDialog; @@ -49,11 +50,16 @@ public class CardPanelComponentImpl extends CardPanel { private static final int CARD_MIN_SIZE_FOR_ICONS = 60; private static final int CARD_MAX_SIZE_FOR_ICONS = 200; + // text min size for image render mode + private static final int CARD_TITLE_FONT_MIN_SIZE = 13; + private static final int CARD_PT_FONT_MIN_SIZE = 17; + public final ScaledImagePanel imagePanel; private ImagePanel overlayPanel; private JPanel iconPanel; private JButton typeButton; + private JPanel ptPanel; private JPanel counterPanel; private JLabel loyaltyCounterLabel; @@ -67,7 +73,9 @@ public class CardPanelComponentImpl extends CardPanel { private int lastCardWidth; private final GlowText titleText; - private final GlowText ptText; + private final GlowText ptText1; + private final GlowText ptText2; + private final GlowText ptText3; private final JLabel fullImageText; private String fullImagePath = null; @@ -280,7 +288,7 @@ public class CardPanelComponentImpl extends CardPanel { // Title Text titleText = new GlowText(); - setText(getGameCard()); + setTitle(getGameCard()); // int fontSize = (int) cardHeight / 11; // titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); titleText.setForeground(Color.white); @@ -295,16 +303,19 @@ public class CardPanelComponentImpl extends CardPanel { add(fullImageText); // PT Text - ptText = new GlowText(); - if (getGameCard().isCreature()) { - ptText.setText(getGameCard().getPower() + '/' + getGameCard().getToughness()); - } else if (getGameCard().isPlanesWalker()) { - ptText.setText(getGameCard().getLoyalty()); - } -// ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); - ptText.setForeground(Color.white); - ptText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); - add(ptText); + ptPanel = new JPanel(); + ptPanel.setOpaque(false); + ptPanel.setLayout(new BoxLayout(ptPanel, BoxLayout.X_AXIS)); + ptPanel.add(new Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(Integer.MAX_VALUE, 0))); + ptText1 = new GlowText(); + ptText2 = new GlowText(); + ptText3 = new GlowText(); + updatePTTexts(getGameCard()); + ptPanel.add(ptText1); + ptPanel.add(ptText2); + ptPanel.add(ptText3); + // + add(ptPanel); // Sickness overlay BufferedImage sickness = ImageManagerImpl.instance.getSicknessImage(); @@ -349,7 +360,7 @@ public class CardPanelComponentImpl extends CardPanel { this.setCounterPanel(null); } - private void setText(CardView card) { + private void setTitle(CardView card) { titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getName()); } @@ -585,12 +596,14 @@ public class CardPanelComponentImpl extends CardPanel { boolean showText = !isAnimationPanel() && canShowCardIcons(cardWidth, hasImage); titleText.setVisible(showText); - ptText.setVisible(showText); + ptText1.setVisible(showText && !ptText1.getText().isEmpty()); + ptText2.setVisible(showText && !ptText2.getText().isEmpty()); + ptText3.setVisible(showText && !ptText3.getText().isEmpty()); fullImageText.setVisible(fullImagePath != null); if (showText) { int fontSize = cardHeight / 13; // startup font size (it same size on all zoom levels) - titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); + titleText.setFont(getFont().deriveFont(Font.BOLD, Math.max(CARD_TITLE_FONT_MIN_SIZE, fontSize))); // margins from card black border to text, not need? text show up good without margins int titleMarginLeft = 0; //Math.round(28f / 672f * cardWidth); @@ -607,24 +620,58 @@ public class CardPanelComponentImpl extends CardPanel { fullImageText.setFont(getFont().deriveFont(Font.PLAIN, 10)); fullImageText.setBounds(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height); - // life points location (font as title) - ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); - Dimension ptSize = ptText.getPreferredSize(); - ptText.setSize(ptSize.width, ptSize.height); + // PT (font as title) + prepareGlowFont(ptText1, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getPower(), false); + prepareGlowFont(ptText2, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), null, false); + prepareGlowFont(ptText3, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(getGameCard())); // right bottom corner with margin (sizes from any sample card) int ptMarginRight = Math.round(64f / 672f * cardWidth); int ptMarginBottom = Math.round(62f / 936f * cardHeight); - int ptX = cardXOffset + cardWidth - ptMarginRight - ptSize.width; - int ptY = cardYOffset + cardHeight - ptMarginBottom - ptSize.height; - ptText.setLocation(ptX, ptY); + int ptWidth = cardWidth - ptMarginRight * 2; + int ptHeight = ptText2.getHeight(); + int ptX = cardXOffset + ptMarginRight; + int ptY = cardYOffset + cardHeight - ptMarginBottom - ptHeight; + ptPanel.setBounds(ptX, ptY, ptWidth, ptHeight); // old version was with TEXT_GLOW_SIZE //ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); } } + private void prepareGlowFont(GlowText label, int fontSize, MageInt value, boolean drawAsDamaged) { + label.setFont(getFont().deriveFont(Font.BOLD, fontSize)); + label.setForeground(CardRendererUtils.getCardTextColor(value, drawAsDamaged, titleText.getForeground(), true)); + Dimension ptSize = label.getPreferredSize(); + label.setSize(ptSize.width, ptSize.height); + } + + private void updatePTTexts(CardView card) { + if (card.isCreature()) { + ptText1.setText(getGameCard().getPower()); + ptText2.setText("/"); + ptText3.setText(CardRendererUtils.getCardLifeWithDamage(getGameCard())); + } else if (card.isPlanesWalker()) { + ptText1.setText(""); + ptText2.setText(""); + ptText3.setText(getGameCard().getLoyalty()); + } else { + ptText1.setText(""); + ptText2.setText(""); + ptText3.setText(""); + } + + ptText1.setForeground(Color.white); + ptText1.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + + ptText2.setForeground(Color.white); + ptText2.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + + ptText3.setForeground(Color.white); + ptText3.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + } + @Override public String toString() { return getGameCard().toString(); @@ -647,10 +694,14 @@ public class CardPanelComponentImpl extends CardPanel { // Update components if (alpha == 0) { - this.ptText.setVisible(false); + this.ptText1.setVisible(false); + this.ptText2.setVisible(false); + this.ptText3.setVisible(false); this.titleText.setVisible(false); } else if (alpha == 1.0f) { - this.ptText.setVisible(true); + this.ptText1.setVisible(true); + this.ptText2.setVisible(true); + this.ptText3.setVisible(true); this.titleText.setVisible(true); } } @@ -683,7 +734,7 @@ public class CardPanelComponentImpl extends CardPanel { UI.invokeLater(() -> { if (stamp == updateArtImageStamp) { hasImage = srcImage != null; - setText(getGameCard()); + setTitle(getGameCard()); setImage(srcImage); } }); @@ -712,7 +763,7 @@ public class CardPanelComponentImpl extends CardPanel { @Override public void showCardTitle() { displayTitleAnyway = true; - setText(getGameCard()); + setTitle(getGameCard()); } @Override @@ -720,17 +771,8 @@ public class CardPanelComponentImpl extends CardPanel { // Super super.update(card); - // Update card text - if (card.isCreature() && card.isPlanesWalker()) { - ptText.setText(card.getPower() + '/' + card.getToughness() + " (" + card.getLoyalty() + ')'); - } else if (card.isCreature()) { - ptText.setText(card.getPower() + '/' + card.getToughness()); - } else if (card.isPlanesWalker()) { - ptText.setText(card.getLoyalty()); - } else { - ptText.setText(""); - } - setText(card); + updatePTTexts(card); + setTitle(card); // Summoning Sickness overlay if (hasSickness() && card.isCreature() && isPermanent()) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java index cf62e7cb439..6b7dbc22745 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java @@ -297,6 +297,8 @@ public class CardPanelRenderImpl extends CardPanel { // Render with Antialialsing g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // Attributes CardPanelAttributes attribs diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index f32c08e972d..42caffce72b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -1,5 +1,9 @@ package org.mage.card.arcane; +import mage.MageInt; +import mage.view.CardView; +import mage.view.PermanentView; + import java.awt.*; import java.awt.image.BufferedImage; import java.util.HashMap; @@ -10,12 +14,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * @author stravant@gmail.com + * @author stravant@gmail.com, JayDi85 *

* Various static utilities for use in the card renderer */ public final class CardRendererUtils { + // text colors for PT (mtgo and image render modes) + private static final Color CARD_TEXT_COLOR_GOOD_LIGHT = new Color(182, 235, 168); + private static final Color CARD_TEXT_COLOR_GOOD_DARK = new Color(52, 135, 88); + private static final Color CARD_TEXT_COLOR_BAD_LIGHT = new Color(234, 153, 153); + private static final Color CARD_TEXT_COLOR_BAD_DARK = new Color(200, 33, 33); + /** * Convert an abstract image, whose underlying implementation may or may not * be a BufferedImage into a BufferedImage by creating one and coping the @@ -53,9 +63,9 @@ public final class CardRendererUtils { int b = c.getBlue(); int alpha = c.getAlpha(); - int plus_r = (int) ((255 - r) / 2); - int plus_g = (int) ((255 - g) / 2); - int plus_b = (int) ((255 - b) / 2); + int plus_r = (255 - r) / 2; + int plus_g = (255 - g) / 2; + int plus_b = (255 - b) / 2; return new Color(r + plus_r, g + plus_g, @@ -69,9 +79,9 @@ public final class CardRendererUtils { int b = c.getBlue(); int alpha = c.getAlpha(); - int plus_r = (int) (Math.min(255 - r, r) / 2); - int plus_g = (int) (Math.min(255 - g, g) / 2); - int plus_b = (int) (Math.min(255 - b, b) / 2); + int plus_r = Math.min(255 - r, r) / 2; + int plus_g = Math.min(255 - g, g) / 2; + int plus_b = Math.min(255 - b, b) / 2; return new Color(r - plus_r, g - plus_g, @@ -195,4 +205,52 @@ public final class CardRendererUtils { return null; } } + + public static String getCardLifeWithDamage(CardView cardView) { + // life with damage + String originLife = cardView.getToughness(); + if (cardView instanceof PermanentView) { + int damage = ((PermanentView) cardView).getDamage(); + int life; + try { + life = Integer.parseInt(originLife); + originLife = String.valueOf(Math.max(0, life - damage)); + } catch (NumberFormatException e) { + // + } + } + return originLife; + } + + public static boolean isCardWithDamage(CardView cardView) { + boolean haveDamage = false; + if (cardView instanceof PermanentView) { + haveDamage = ((PermanentView) cardView).getDamage() > 0; + } + return haveDamage; + } + + public static Color getCardTextColor(MageInt value, boolean drawAsDamaged, Color defaultColor, boolean textLight) { + if (drawAsDamaged) { + return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; + } + + // boost colorizing + if (value != null) { + int current = value.getValue(); + int origin = value.getBaseValue(); + if (origin != 0) { + if (current < origin) { + return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; + } else if (current > origin) { + return textLight ? CARD_TEXT_COLOR_GOOD_LIGHT : CARD_TEXT_COLOR_GOOD_DARK; + } else { + return defaultColor; + } + } + } + + return defaultColor; + } + } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java index 8d95a11ac04..5600c59b812 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java @@ -1,5 +1,10 @@ package org.mage.card.arcane; +import mage.client.util.ImageCaches; +import mage.client.util.SoftValuesLoadingCache; +import org.jdesktop.swingx.graphics.GraphicsUtilities; + +import javax.swing.*; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; @@ -14,17 +19,6 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; -import javax.swing.*; - -import org.jdesktop.swingx.graphics.GraphicsUtilities; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; - -import mage.client.util.ImageCaches; -import mage.client.util.SoftValuesLoadingCache; - public class GlowText extends JLabel { private static final long serialVersionUID = 1827677946939348001L; @@ -123,10 +117,7 @@ public class GlowText extends JLabel { if (!Objects.equals(this.color, other.color)) { return false; } - if (!Objects.equals(this.glowColor, other.glowColor)) { - return false; - } - return true; + return Objects.equals(this.glowColor, other.glowColor); } } @@ -158,7 +149,11 @@ public class GlowText extends JLabel { return; } - g.drawImage(IMAGE_CACHE.getOrThrow(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null); + g.drawImage(getGlowImage(), 0, 0, null); + } + + public BufferedImage getGlowImage() { + return IMAGE_CACHE.getOrThrow(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)); } private static BufferedImage createGlowImage(Key key) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index fbdac1c3fa4..3bbcb3fc055 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import mage.ObjectColor; @@ -56,13 +51,14 @@ import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol; */ /** - * @author stravant@gmail.com + * @author stravant@gmail.com, JayDi85 *

* Base rendering class for new border cards */ public class ModernCardRenderer extends CardRenderer { private static final Logger LOGGER = Logger.getLogger(ModernCardRenderer.class); + private static final GlowText glowTextRenderer = new GlowText(); /////////////////////////////////////////////////////////////////////////// // Textures for modern frame cards @@ -261,7 +257,7 @@ public class ModernCardRenderer extends CardRenderer { ptTextHeight = getPTTextHeightForLineHeight(boxHeight); ptTextOffset = (boxHeight - ptTextHeight) / 2; // Beleren font does work well for numbers though - ptTextFont = BASE_BELEREN_FONT.deriveFont(Font.PLAIN, ptTextHeight); + ptTextFont = BASE_BELEREN_FONT.deriveFont(Font.BOLD, ptTextHeight); } @Override @@ -933,6 +929,51 @@ public class ModernCardRenderer extends CardRenderer { } } + public void paintOutlineTextByGlow(Graphics2D g, String text, Color color, int x, int y) { + GlowText label = new GlowText(); + label.setGlow(Color.black, 6, 3); + label.setText(text); + label.setFont(g.getFont().deriveFont(Font.BOLD)); + label.setForeground(color); + Dimension ptSize = label.getPreferredSize(); + label.setSize(ptSize.width, ptSize.height); + g.drawImage(label.getGlowImage(), x, y, null); + } + + public void paintOutlineTextByStroke(Graphics2D g, String text, Color color, int x, int y) { + // https://stackoverflow.com/a/35222059/1276632 + Color outlineColor = Color.black; + Color fillColor = color; + BasicStroke outlineStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + + // remember original settings + Color originalColor = g.getColor(); + Stroke originalStroke = g.getStroke(); + RenderingHints originalHints = g.getRenderingHints(); + + // create a glyph vector from your text + GlyphVector glyphVector = g.getFont().createGlyphVector(g.getFontRenderContext(), text); + // get the shape object + Shape textShape = glyphVector.getOutline(x, y); + + // activate anti aliasing for text rendering (if you want it to look nice) + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + g.setColor(outlineColor); + g.setStroke(outlineStroke); + g.draw(textShape); // draw outline + + g.setColor(fillColor); + g.fill(textShape); // fill the shape + + // reset to original settings after painting + g.setColor(originalColor); + g.setStroke(originalStroke); + g.setRenderingHints(originalHints); + } + // Draw the P/T and/or Loyalty boxes protected void drawBottomRight(Graphics2D g, Paint borderPaint, Color fill) { // No bottom right for abilities @@ -966,24 +1007,44 @@ public class ModernCardRenderer extends CardRenderer { partWidth - 2 * contentInset, 1); // Draw text - Color textColor; + Color defaultTextColor; + boolean defaultTextLight; if (isVehicle) { boolean isAnimated = !(cardView instanceof PermanentView) || cardView.isCreature(); if (isAnimated) { - textColor = Color.white; + defaultTextColor = Color.white; } else { - textColor = new Color(180, 180, 180); + defaultTextColor = new Color(180, 180, 180); } - + defaultTextLight = true; } else { - textColor = getBoxTextColor(); + defaultTextColor = getBoxTextColor(); + defaultTextLight = !defaultTextColor.equals(Color.black); } - g.setColor(textColor); + g.setColor(defaultTextColor); g.setFont(ptTextFont); - String ptText = cardView.getPower() + '/' + cardView.getToughness(); - int ptTextWidth = g.getFontMetrics().stringWidth(ptText); - g.drawString(ptText, - x + (partWidth - ptTextWidth) / 2, curY - ptTextOffset - 1); + + // draws p/t by parts + String ptText1 = cardView.getPower(); + String ptText2 = "/"; + String ptText3 = CardRendererUtils.getCardLifeWithDamage(cardView); + + int ptTextWidth1 = g.getFontMetrics().stringWidth(ptText1); + int ptTextWidth2 = g.getFontMetrics().stringWidth(ptText2); + + // draws / by center, P and T from left/right sides of / + int ptCenterX = x + partWidth / 2; + // p + g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), false, defaultTextColor, defaultTextLight)); + g.drawString(ptText1, ptCenterX - ptTextWidth2 / 2 - ptTextWidth1, curY - ptTextOffset - 1); // left + // / + g.setColor(defaultTextColor); + g.drawString(ptText2, ptCenterX - ptTextWidth2 / 2, curY - ptTextOffset - 1); // center + // t + g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight)); + g.drawString(ptText3, ptCenterX + ptTextWidth2 / 2, curY - ptTextOffset - 1); // right + // + g.setColor(defaultTextColor); // Advance curY -= boxHeight; diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index 44f63bebc66..a4f2a81302f 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -53,6 +53,10 @@ public class MageObjectReference implements Comparable, Ser public MageObjectReference(UUID sourceId, Game game) { this.sourceId = sourceId; + if (sourceId == null) { + throw new IllegalArgumentException("MageObjectReference contains nullable sourceId"); + } + MageObject mageObject = game.getObject(sourceId); if (mageObject != null) { this.zoneChangeCounter = mageObject.getZoneChangeCounter(game); From 95183060169746f77633d49e200b6e349f87f983 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 11 May 2019 14:55:55 +0400 Subject: [PATCH 54/76] * UI: added auto-size of PT box in mtgo render mode (large values are now visible); --- .../mage/client/util/gui/GuiDisplayUtil.java | 2 +- .../mage/card/arcane/ModernCardRenderer.java | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index aa1cce0b27a..76a8886767d 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -221,7 +221,7 @@ public final class GuiDisplayUtil { if (card.getMageObjectType().isPermanent() && card instanceof PermanentView) { int damage = ((PermanentView) card).getDamage(); if (damage > 0) { - textLines.getLines().add("Damage dealt: " + damage + ""); // TODO + textLines.getLines().add("Damage dealt: " + damage + ""); textLines.setBasicTextLength(textLines.getBasicTextLength() + 50); } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 3bbcb3fc055..459c5017662 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -990,6 +990,18 @@ public class ModernCardRenderer extends CardRenderer { // Is it a creature? boolean isVehicle = cardView.getSubTypes().contains(SubType.VEHICLE); if (cardView.isCreature() || isVehicle) { + + // draws p/t by parts + String ptText1 = cardView.getPower(); + String ptText2 = "/"; + String ptText3 = CardRendererUtils.getCardLifeWithDamage(cardView); + int ptTextWidth1 = g.getFontMetrics(ptTextFont).stringWidth(ptText1); + int ptTextWidth2 = g.getFontMetrics(ptTextFont).stringWidth(ptText2); + + // PT max size + int partMinWidth = g.getFontMetrics(ptTextFont).stringWidth(ptText1 + ptText2 + ptText3) + 2 * contentInset; + partWidth = Math.max(partMinWidth, partWidth); + int x = cardWidth - borderWidth - partWidth; // Draw PT box @@ -1024,14 +1036,6 @@ public class ModernCardRenderer extends CardRenderer { g.setColor(defaultTextColor); g.setFont(ptTextFont); - // draws p/t by parts - String ptText1 = cardView.getPower(); - String ptText2 = "/"; - String ptText3 = CardRendererUtils.getCardLifeWithDamage(cardView); - - int ptTextWidth1 = g.getFontMetrics().stringWidth(ptText1); - int ptTextWidth2 = g.getFontMetrics().stringWidth(ptText2); - // draws / by center, P and T from left/right sides of / int ptCenterX = x + partWidth / 2; // p From 5c69c661228ab95569f75a4b3a9baac33f4717bd Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 11 May 2019 16:32:35 +0400 Subject: [PATCH 55/76] Removed beleron copyrighted font, fixed PT centering in mtgo render mode; --- .../mage/card/arcane/ModernCardRenderer.java | 50 +++++++++--------- .../resources/cardrender/beleren-bold.ttf | Bin 92612 -> 0 bytes 2 files changed, 26 insertions(+), 24 deletions(-) delete mode 100644 Mage.Client/src/main/resources/cardrender/beleren-bold.ttf diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 459c5017662..566931ef971 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -94,7 +94,7 @@ public class ModernCardRenderer extends CardRenderer { return new Font("Arial", Font.PLAIN, 1); } - public static final Font BASE_BELEREN_FONT = loadFont("beleren-bold"); + // public static final Font BASE_BELEREN_FONT = loadFont("beleren-bold"); public static final Paint BG_TEXTURE_WHITE = loadBackgroundTexture("white"); public static final Paint BG_TEXTURE_BLUE = loadBackgroundTexture("blue"); @@ -248,16 +248,13 @@ public class ModernCardRenderer extends CardRenderer { // Box text height boxTextHeight = getTextHeightForBoxHeight(boxHeight); boxTextOffset = (boxHeight - boxTextHeight) / 2; - // Not using Beleren for now because it looks bad at small font sizes. Maybe we want to in the future? - //boxTextFont = BASE_BELEREN_FONT.deriveFont(Font.PLAIN, boxTextHeight); boxTextFont = new Font("Arial", Font.PLAIN, boxTextHeight); boxTextFontNarrow = new Font("Arial Narrow", Font.PLAIN, boxTextHeight); // Box text height ptTextHeight = getPTTextHeightForLineHeight(boxHeight); ptTextOffset = (boxHeight - ptTextHeight) / 2; - // Beleren font does work well for numbers though - ptTextFont = BASE_BELEREN_FONT.deriveFont(Font.BOLD, ptTextHeight); + ptTextFont = new Font("Arial", Font.BOLD, ptTextHeight); } @Override @@ -985,29 +982,31 @@ public class ModernCardRenderer extends CardRenderer { int curY = cardHeight - (int) (0.03f * cardHeight); // Width of the boxes - int partWidth = (int) Math.max(30, 0.20f * cardWidth); + int partBoxWidth = (int) Math.max(30, 0.20f * cardWidth); // Is it a creature? boolean isVehicle = cardView.getSubTypes().contains(SubType.VEHICLE); if (cardView.isCreature() || isVehicle) { // draws p/t by parts + int ptDeviderSpace = 1; // Arial font is too narrow for devider (2/2) and needs extra space String ptText1 = cardView.getPower(); String ptText2 = "/"; String ptText3 = CardRendererUtils.getCardLifeWithDamage(cardView); int ptTextWidth1 = g.getFontMetrics(ptTextFont).stringWidth(ptText1); - int ptTextWidth2 = g.getFontMetrics(ptTextFont).stringWidth(ptText2); + int ptTextWidth2 = g.getFontMetrics(ptTextFont).stringWidth(ptText2) + 2 * ptDeviderSpace; + int ptTextWidth3 = g.getFontMetrics(ptTextFont).stringWidth(ptText3); // PT max size - int partMinWidth = g.getFontMetrics(ptTextFont).stringWidth(ptText1 + ptText2 + ptText3) + 2 * contentInset; - partWidth = Math.max(partMinWidth, partWidth); + int ptContentWidth = contentInset + ptTextWidth1 + ptDeviderSpace + ptTextWidth2 + ptDeviderSpace + ptTextWidth3 + contentInset; + partBoxWidth = Math.max(ptContentWidth, partBoxWidth); - int x = cardWidth - borderWidth - partWidth; + int x = cardWidth - borderWidth - partBoxWidth; // Draw PT box CardRendererUtils.drawRoundedBox(g, x, curY - boxHeight, - partWidth, boxHeight, + partBoxWidth, boxHeight, contentInset, borderPaint, isVehicle ? BOX_VEHICLE : fill); @@ -1016,7 +1015,7 @@ public class ModernCardRenderer extends CardRenderer { g.setColor(new Color(0, 0, 0, 150)); g.fillRect( x + contentInset, curY - boxHeight - 1, - partWidth - 2 * contentInset, 1); + partBoxWidth - 2 * contentInset, 1); // Draw text Color defaultTextColor; @@ -1036,17 +1035,20 @@ public class ModernCardRenderer extends CardRenderer { g.setColor(defaultTextColor); g.setFont(ptTextFont); - // draws / by center, P and T from left/right sides of / - int ptCenterX = x + partWidth / 2; + // draws + int ptEmptySpace = (partBoxWidth - ptContentWidth) / 2; + int ptPosStart1 = x + contentInset + ptEmptySpace; + int ptPosStart2 = ptPosStart1 + ptTextWidth1 + ptDeviderSpace; + int ptPosStart3 = ptPosStart2 + ptTextWidth2 + ptDeviderSpace; // p g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), false, defaultTextColor, defaultTextLight)); - g.drawString(ptText1, ptCenterX - ptTextWidth2 / 2 - ptTextWidth1, curY - ptTextOffset - 1); // left + g.drawString(ptText1, ptPosStart1, curY - ptTextOffset - 1); // left // / g.setColor(defaultTextColor); - g.drawString(ptText2, ptCenterX - ptTextWidth2 / 2, curY - ptTextOffset - 1); // center + g.drawString(ptText2, ptPosStart2, curY - ptTextOffset - 1); // center // t g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight)); - g.drawString(ptText3, ptCenterX + ptTextWidth2 / 2, curY - ptTextOffset - 1); // right + g.drawString(ptText3, ptPosStart3, curY - ptTextOffset - 1); // right // g.setColor(defaultTextColor); @@ -1059,9 +1061,9 @@ public class ModernCardRenderer extends CardRenderer { if (cardView.isPlanesWalker() && (cardView instanceof PermanentView || !cardView.getStartingLoyalty().equals("0"))) { // Draw the PW loyalty box - int w = partWidth; - int h = partWidth / 2; - int x = cardWidth - partWidth - borderWidth; + int w = partBoxWidth; + int h = partBoxWidth / 2; + int x = cardWidth - partBoxWidth - borderWidth; int y = curY - h; Polygon symbol = new Polygon( @@ -1112,16 +1114,16 @@ public class ModernCardRenderer extends CardRenderer { // does it have damage on it? if ((cardView instanceof PermanentView) && ((PermanentView) cardView).getDamage() > 0) { - int x = cardWidth - partWidth - borderWidth; + int x = cardWidth - partBoxWidth - borderWidth; int y = curY - boxHeight; String damage = String.valueOf(((PermanentView) cardView).getDamage()); g.setFont(ptTextFont); int txWidth = g.getFontMetrics().stringWidth(damage); g.setColor(Color.red); - g.fillRect(x, y, partWidth, boxHeight); + g.fillRect(x, y, partBoxWidth, boxHeight); g.setColor(Color.white); - g.drawRect(x, y, partWidth, boxHeight); - g.drawString(damage, x + (partWidth - txWidth) / 2, curY - 1); + g.drawRect(x, y, partBoxWidth, boxHeight); + g.drawString(damage, x + (partBoxWidth - txWidth) / 2, curY - 1); } } diff --git a/Mage.Client/src/main/resources/cardrender/beleren-bold.ttf b/Mage.Client/src/main/resources/cardrender/beleren-bold.ttf deleted file mode 100644 index 7dd1bff6a756375020ea4e080ac02b6e04877a1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92612 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 Date: Sat, 11 May 2019 17:38:01 +0400 Subject: [PATCH 56/76] Fixed vehicle PT draws in image render mode; --- .../src/main/java/mage/client/dialog/TestCardRenderDialog.java | 2 ++ .../main/java/org/mage/card/arcane/CardPanelComponentImpl.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index d52bb111729..e306d28bd58 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -118,6 +118,8 @@ public class TestCardRenderDialog extends MageDialog { view.put(card.getId(), card); card = createCard(game, player.getId(), "RNA", "221", 0, 0, 0); // Bedeck // Bedazzle view.put(card.getId(), card); + card = createCard(game, player.getId(), "XLN", "234", 0, 0, 0); // Conqueror's Galleon + view.put(card.getId(), card); cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); cardsPanel.changeGUISize(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java index 97d84f38138..ea0ea08b1f1 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java @@ -10,6 +10,7 @@ import mage.client.util.SoftValuesLoadingCache; import mage.components.ImagePanel; import mage.components.ImagePanelStyle; import mage.constants.AbilityType; +import mage.constants.SubType; import mage.view.CardView; import mage.view.CounterView; import mage.view.PermanentView; @@ -648,7 +649,7 @@ public class CardPanelComponentImpl extends CardPanel { } private void updatePTTexts(CardView card) { - if (card.isCreature()) { + if (card.isCreature() || card.getSubTypes().contains(SubType.VEHICLE)) { ptText1.setText(getGameCard().getPower()); ptText2.setText("/"); ptText3.setText(CardRendererUtils.getCardLifeWithDamage(getGameCard())); From d34fa0ef22ce0d3679d0b34e139002bf28a49884 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 11 May 2019 17:40:23 +0400 Subject: [PATCH 57/76] Prepare next release --- Mage.Common/src/main/java/mage/utils/MageVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 20cc7b2c565..858c0dc6eb3 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -13,7 +13,7 @@ public class MageVersion implements Serializable, Comparable { public static final int MAGE_VERSION_MINOR = 4; public static final int MAGE_VERSION_PATCH = 35; public static final String MAGE_EDITION_INFO = ""; // set "-beta" for 1.4.32-betaV0 - public static final String MAGE_VERSION_MINOR_PATCH = "V4"; // default + public static final String MAGE_VERSION_MINOR_PATCH = "V5"; // default // strict mode private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes) From 89cf3e39696676dd73fc541e272f326f89f71bdc Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 11 May 2019 22:44:53 +0400 Subject: [PATCH 58/76] * Teferi, Time Raveler - fixed that it uses one target instead up to one; --- Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java b/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java index f35b55d7b48..803f9475ec0 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java @@ -58,7 +58,7 @@ public final class TeferiTimeRaveler extends CardImpl { // -3: Return up to one target artifact, creature, or enchantment to its owner's hand. Draw a card. Ability ability = new LoyaltyAbility(new ReturnToHandTargetEffect(), -3); ability.addEffect(new DrawCardSourceControllerEffect(1).setText("Draw a card")); - ability.addTarget(new TargetPermanent(filter2)); + ability.addTarget(new TargetPermanent(0, 1, filter2, false)); this.addAbility(ability); } From 24b221ff34669e89bcc28cc771bfc2856bbec819 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 13 May 2019 10:47:53 +0400 Subject: [PATCH 59/76] Test framework: added default test deck (no more test.dck file required to start test game); --- .../java/mage/client/table/TablesPanel.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 877fb9d9b43..00b5775fb47 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1,5 +1,6 @@ package mage.client.table; +import mage.cards.decks.DeckCardLists; import mage.cards.decks.importer.DeckImporter; import mage.client.MageFrame; import mage.client.SessionHandler; @@ -16,6 +17,7 @@ import mage.constants.*; import mage.game.match.MatchOptions; import mage.players.PlayerType; import mage.remote.MageRemoteException; +import mage.util.DeckUtil; import mage.util.RandomUtil; import mage.view.MatchView; import mage.view.RoomUsersView; @@ -1538,12 +1540,20 @@ public class TablesPanel extends javax.swing.JPanel { private void btnQuickStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartActionPerformed TableView table; try { - File f = new File("test.dck"); + String testDeckFile = "test.dck"; + File f = new File(testDeckFile); if (!f.exists()) { - JOptionPane.showMessageDialog(null, "Couldn't find test.dck file for quick game start", "Error", JOptionPane.ERROR_MESSAGE); - return; + // default test deck + testDeckFile = DeckUtil.writeTextToTempFile("" + + "5 Swamp" + System.lineSeparator() + + "5 Forest" + System.lineSeparator() + + "5 Island" + System.lineSeparator() + + "5 Mountain" + System.lineSeparator() + + "5 Plains"); } + DeckCardLists testDeck = DeckImporter.importDeckFromFile(testDeckFile); + MatchOptions options = new MatchOptions("1", "Two Player Duel", false, 2); options.getPlayerTypes().add(PlayerType.HUMAN); options.getPlayerTypes().add(PlayerType.COMPUTER_MAD); @@ -1561,8 +1571,8 @@ public class TablesPanel extends javax.swing.JPanel { options.setBannedUsers(IgnoreList.ignoreList(serverAddress)); table = SessionHandler.createTable(roomId, options); - SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, DeckImporter.importDeckFromFile("test.dck"), ""); - SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, DeckImporter.importDeckFromFile("test.dck"), ""); + SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, testDeck, ""); + SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, testDeck, ""); SessionHandler.startMatch(roomId, table.getTableId()); } catch (HeadlessException ex) { handleError(ex); From 8448afc709a73ab6d8cf54b2cb094ff4911d9cdc Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 13 May 2019 13:20:41 +0400 Subject: [PATCH 60/76] Test framework: added commander games support (quick start button, "commander" command to put card as commander); --- .../java/mage/client/table/TablesPanel.form | 34 ++++++++--- .../java/mage/client/table/TablesPanel.java | 59 +++++++++++++------ .../java/mage/server/util/SystemUtil.java | 25 +++++++- .../common/CastCommanderAbility.java | 5 +- .../java/mage/game/GameCommanderImpl.java | 10 ++-- .../java/mage/game/GameTinyLeadersImpl.java | 13 ++-- 6 files changed, 104 insertions(+), 42 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form index 52b892737de..70f04d7ec34 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form @@ -43,8 +43,11 @@ - - + + + + + @@ -60,10 +63,16 @@ - + - + + + + + + + @@ -506,15 +515,26 @@ - + - + - + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 00b5775fb47..da43f352734 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -688,7 +688,7 @@ public class TablesPanel extends javax.swing.JPanel { this.roomId = roomId; UUID chatRoomId = null; if (SessionHandler.getSession() != null) { - btnQuickStart.setVisible(SessionHandler.isTestMode()); + btnQuickStartDuel.setVisible(SessionHandler.isTestMode()); gameChooser.init(); chatRoomId = SessionHandler.getRoomChatId(roomId).orElse(null); } @@ -973,7 +973,8 @@ public class TablesPanel extends javax.swing.JPanel { jSeparator5 = new javax.swing.JToolBar.Separator(); btnOpen = new javax.swing.JToggleButton(); btnPassword = new javax.swing.JToggleButton(); - btnQuickStart = new javax.swing.JButton(); + btnQuickStartDuel = new javax.swing.JButton(); + btnQuickStartCommander = new javax.swing.JButton(); jSplitPane1 = new javax.swing.JSplitPane(); jPanelTables = new javax.swing.JPanel(); jSplitPaneTables = new javax.swing.JSplitPane(); @@ -1393,13 +1394,23 @@ public class TablesPanel extends javax.swing.JPanel { }); filterBar2.add(btnPassword); - btnQuickStart.setText("Quick Start"); - btnQuickStart.setFocusable(false); - btnQuickStart.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnQuickStart.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnQuickStart.addActionListener(new java.awt.event.ActionListener() { + btnQuickStartDuel.setText("Quick start duel"); + btnQuickStartDuel.setFocusable(false); + btnQuickStartDuel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnQuickStartDuel.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnQuickStartDuel.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - btnQuickStartActionPerformed(evt); + btnQuickStartDuelActionPerformed(evt); + } + }); + + btnQuickStartCommander.setText("Quick start commander"); + btnQuickStartCommander.setFocusable(false); + btnQuickStartCommander.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnQuickStartCommander.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnQuickStartCommander.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnQuickStartCommanderActionPerformed(evt); } }); @@ -1417,8 +1428,10 @@ public class TablesPanel extends javax.swing.JPanel { .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnQuickStart) - .addContainerGap(792, Short.MAX_VALUE)) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(btnQuickStartDuel) + .addComponent(btnQuickStartCommander)) + .addContainerGap(734, Short.MAX_VALUE)) ); jPanelTopLayout.setVerticalGroup( jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1431,9 +1444,13 @@ public class TablesPanel extends javax.swing.JPanel { .addGroup(jPanelTopLayout.createSequentialGroup() .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(btnQuickStart)) + .addComponent(btnQuickStartDuel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addComponent(btnQuickStartCommander) + .addGap(0, 0, Short.MAX_VALUE))))) .addContainerGap()) ); @@ -1537,7 +1554,7 @@ public class TablesPanel extends javax.swing.JPanel { newTournamentDialog.showDialog(roomId); }//GEN-LAST:event_btnNewTournamentActionPerformed - private void btnQuickStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartActionPerformed + private void createTestGame(String gameName, String gameType) { TableView table; try { String testDeckFile = "test.dck"; @@ -1551,10 +1568,9 @@ public class TablesPanel extends javax.swing.JPanel { + "5 Mountain" + System.lineSeparator() + "5 Plains"); } - DeckCardLists testDeck = DeckImporter.importDeckFromFile(testDeckFile); - MatchOptions options = new MatchOptions("1", "Two Player Duel", false, 2); + MatchOptions options = new MatchOptions(gameName, gameType, false, 2); options.getPlayerTypes().add(PlayerType.HUMAN); options.getPlayerTypes().add(PlayerType.COMPUTER_MAD); options.setDeckType("Limited"); @@ -1577,7 +1593,11 @@ public class TablesPanel extends javax.swing.JPanel { } catch (HeadlessException ex) { handleError(ex); } - }//GEN-LAST:event_btnQuickStartActionPerformed + } + + private void btnQuickStartDuelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartDuelActionPerformed + createTestGame("Test duel", "Two Player Duel"); + }//GEN-LAST:event_btnQuickStartDuelActionPerformed private void btnNewTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewTableActionPerformed newTableDialog.showDialog(roomId); @@ -1630,6 +1650,10 @@ public class TablesPanel extends javax.swing.JPanel { MageFrame.getInstance().showWhatsNewDialog(true); }//GEN-LAST:event_buttonWhatsNewActionPerformed + private void btnQuickStartCommanderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartCommanderActionPerformed + createTestGame("Test commander", "Commander Two Player Duel"); + }//GEN-LAST:event_btnQuickStartCommanderActionPerformed + private void handleError(Exception ex) { LOGGER.fatal("Error loading deck: ", ex); JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error loading deck.", "Error", JOptionPane.ERROR_MESSAGE); @@ -1650,7 +1674,8 @@ public class TablesPanel extends javax.swing.JPanel { private javax.swing.JButton btnNewTournament; private javax.swing.JToggleButton btnOpen; private javax.swing.JToggleButton btnPassword; - private javax.swing.JButton btnQuickStart; + private javax.swing.JButton btnQuickStartCommander; + private javax.swing.JButton btnQuickStartDuel; private javax.swing.JToggleButton btnRated; private javax.swing.JToggleButton btnSkillBeginner; private javax.swing.JToggleButton btnSkillCasual; diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java index 382021e4cf6..ae2a758128b 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -12,6 +12,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; +import mage.game.GameCommanderImpl; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.RandomUtil; @@ -31,7 +32,8 @@ import java.util.stream.Collectors; */ public final class SystemUtil { - private SystemUtil(){} + private SystemUtil() { + } public static final DateFormat dateFormat = new SimpleDateFormat("yy-M-dd HH:mm:ss"); @@ -485,6 +487,8 @@ public final class SystemUtil { gameZone = Zone.COMMAND; } else if ("plane".equalsIgnoreCase(command.zone)) { gameZone = Zone.COMMAND; + } else if ("commander".equalsIgnoreCase(command.zone)) { + gameZone = Zone.COMMAND; } else { logger.warn("Unknown zone [" + command.zone + "]: " + line); continue; @@ -513,8 +517,23 @@ public final class SystemUtil { } } game.loadCards(cardsToLoad, player.getId()); - for (Card card : cardsToLoad) { - swapWithAnyCard(game, player, card, gameZone); + + if ("commander".equalsIgnoreCase(command.zone) && cardsToLoad.size() > 0) { + // as commander (only commander games, look at init code in GameCommanderImpl) + if (game instanceof GameCommanderImpl) { + GameCommanderImpl gameCommander = (GameCommanderImpl) game; + for (Card card : cardsToLoad) { + player.addCommanderId(card.getId()); + gameCommander.initCommander(card, player); + } + } else { + logger.fatal("Commander card can be used in commander game only: " + command.cardName); + } + } else { + // as other card + for (Card card : cardsToLoad) { + swapWithAnyCard(game, player, card, gameZone); + } } } } catch (Exception e) { diff --git a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java index be7896e11b0..a549a72310f 100644 --- a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java @@ -1,7 +1,7 @@ - package mage.abilities.common; import mage.abilities.SpellAbility; +import mage.abilities.costs.CostsImpl; import mage.cards.Card; import mage.constants.SpellAbilityType; import mage.constants.TimingRule; @@ -9,14 +9,13 @@ import mage.constants.Zone; import mage.game.Game; /** - * * @author Plopman */ public class CastCommanderAbility extends SpellAbility { public CastCommanderAbility(Card card) { super(card.getManaCost(), card.getName(), Zone.COMMAND, SpellAbilityType.BASE); - this.costs = card.getSpellAbility().getCosts().copy(); + this.costs = card.getSpellAbility() != null ? card.getSpellAbility().getCosts().copy() : new CostsImpl<>(); this.timing = TimingRule.SORCERY; this.usesStack = true; this.controllerId = card.getOwnerId(); diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java index 96de6b83069..47a6ee79a44 100644 --- a/Mage/src/main/java/mage/game/GameCommanderImpl.java +++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java @@ -40,7 +40,6 @@ public abstract class GameCommanderImpl extends GameImpl { @Override protected void init(UUID choosingPlayerId) { - Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); //Move commander to command zone for (UUID playerId : state.getPlayerList(startingPlayerId)) { Player player = getPlayer(playerId); @@ -49,7 +48,7 @@ public abstract class GameCommanderImpl extends GameImpl { for (UUID commanderId : player.getCommandersIds()) { Card commander = this.getCard(commanderId); if (commander != null) { - initCommander(commander, ability, player); + initCommander(commander, player); } } } else { @@ -57,20 +56,20 @@ public abstract class GameCommanderImpl extends GameImpl { Card commander = this.getCard(player.getSideboard().iterator().next()); if (commander != null) { player.addCommanderId(commander.getId()); - initCommander(commander, ability, player); + initCommander(commander, player); } } } } } - this.getState().addAbility(ability, null); super.init(choosingPlayerId); if (startingPlayerSkipsDraw) { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } } - private void initCommander(Card commander, Ability ability, Player player) { + public void initCommander(Card commander, Player player) { + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); commander.moveToZone(Zone.COMMAND, null, this, true); commander.getAbilities().setControllerId(player.getId()); ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary)); @@ -79,6 +78,7 @@ public abstract class GameCommanderImpl extends GameImpl { CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), checkCommanderDamage); getState().addWatcher(watcher); watcher.addCardInfoToCommander(this); + this.getState().addAbility(ability, null); } //20130711 diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java index 78c7fdb0ec7..48e1efa89ff 100644 --- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java @@ -1,9 +1,5 @@ - package mage.game; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -21,8 +17,11 @@ import mage.game.turn.TurnMod; import mage.players.Player; import mage.watchers.common.CommanderInfoWatcher; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author JRHerlehy */ public abstract class GameTinyLeadersImpl extends GameImpl { @@ -43,7 +42,6 @@ public abstract class GameTinyLeadersImpl extends GameImpl { @Override protected void init(UUID choosingPlayerId) { - Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); //Move tiny leader to command zone for (UUID playerId : state.getPlayerList(startingPlayerId)) { Player player = getPlayer(playerId); @@ -55,6 +53,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl { 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)); ability.addEffect(new CommanderCostModification(commander.getId())); // Commander rule #4 was removed Jan. 18, 2016 @@ -63,13 +62,13 @@ public abstract class GameTinyLeadersImpl extends GameImpl { CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), false); getState().addWatcher(watcher); watcher.addCardInfoToCommander(this); + this.getState().addAbility(ability, null); } else { throw new UnknownError("Commander card could not be created. Name: [" + player.getMatchPlayer().getDeck().getName() + ']'); } } } - this.getState().addAbility(ability, null); super.init(choosingPlayerId); if (startingPlayerSkipsDraw) { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); From bf20e7d656a63b9beea5702188e368d8e1788282 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 13 May 2019 13:30:18 +0400 Subject: [PATCH 61/76] Test button fix --- Mage.Client/src/main/java/mage/client/table/TablesPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index da43f352734..9544426506d 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -689,6 +689,7 @@ public class TablesPanel extends javax.swing.JPanel { UUID chatRoomId = null; if (SessionHandler.getSession() != null) { btnQuickStartDuel.setVisible(SessionHandler.isTestMode()); + btnQuickStartCommander.setVisible(SessionHandler.isTestMode()); gameChooser.init(); chatRoomId = SessionHandler.getRoomChatId(roomId).orElse(null); } From 28924c1cb70c36b014bb12e50c49a136fbfe3d12 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 13 May 2019 16:07:29 +0400 Subject: [PATCH 62/76] * Commander - fixed that instant/sourcery/enchantment don't work from command zone (#5795); --- .../mage/abilities/common/CastCommanderAbility.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java index a549a72310f..c31137dfe88 100644 --- a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java @@ -15,8 +15,15 @@ public class CastCommanderAbility extends SpellAbility { public CastCommanderAbility(Card card) { super(card.getManaCost(), card.getName(), Zone.COMMAND, SpellAbilityType.BASE); - this.costs = card.getSpellAbility() != null ? card.getSpellAbility().getCosts().copy() : new CostsImpl<>(); - this.timing = TimingRule.SORCERY; + if (card.getSpellAbility() != null) { + this.getCosts().addAll(card.getSpellAbility().getCosts().copy()); + this.getEffects().addAll(card.getSpellAbility().getEffects().copy()); + this.getTargets().addAll(card.getSpellAbility().getTargets().copy()); + this.timing = card.getSpellAbility().getTiming(); + } else { + this.costs = new CostsImpl<>(); + this.timing = TimingRule.SORCERY; + } this.usesStack = true; this.controllerId = card.getOwnerId(); this.sourceId = card.getId(); From e63ba294272a1286f26018f6ee1532745d0267c0 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Mon, 13 May 2019 22:51:30 -0700 Subject: [PATCH 63/76] fix NPE when trying to draw PT on an emblem. --- .../card/arcane/CardPanelComponentImpl.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java index ea0ea08b1f1..5c97f2c59ea 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java @@ -622,19 +622,21 @@ public class CardPanelComponentImpl extends CardPanel { fullImageText.setBounds(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height); // PT (font as title) - prepareGlowFont(ptText1, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getPower(), false); - prepareGlowFont(ptText2, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), null, false); - prepareGlowFont(ptText3, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(getGameCard())); + if (getGameCard().getOriginalCard() != null) { + prepareGlowFont(ptText1, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getPower(), false); + prepareGlowFont(ptText2, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), null, false); + prepareGlowFont(ptText3, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(getGameCard())); - // right bottom corner with margin (sizes from any sample card) - int ptMarginRight = Math.round(64f / 672f * cardWidth); - int ptMarginBottom = Math.round(62f / 936f * cardHeight); + // right bottom corner with margin (sizes from any sample card) + int ptMarginRight = Math.round(64f / 672f * cardWidth); + int ptMarginBottom = Math.round(62f / 936f * cardHeight); - int ptWidth = cardWidth - ptMarginRight * 2; - int ptHeight = ptText2.getHeight(); - int ptX = cardXOffset + ptMarginRight; - int ptY = cardYOffset + cardHeight - ptMarginBottom - ptHeight; - ptPanel.setBounds(ptX, ptY, ptWidth, ptHeight); + int ptWidth = cardWidth - ptMarginRight * 2; + int ptHeight = ptText2.getHeight(); + int ptX = cardXOffset + ptMarginRight; + int ptY = cardYOffset + cardHeight - ptMarginBottom - ptHeight; + ptPanel.setBounds(ptX, ptY, ptWidth, ptHeight); + } // old version was with TEXT_GLOW_SIZE //ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); From 06c9d7941c82addb3498f309d3d88120f0d8d020 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 14 May 2019 15:03:25 +0400 Subject: [PATCH 64/76] * Ascend ability - added card hint with permanents count; --- .../src/mage/cards/e/ExpelFromOrazca.java | 11 +++--- Mage.Sets/src/mage/cards/g/GoldenDemise.java | 6 ++-- .../src/mage/cards/p/PrideOfConquerors.java | 4 ++- .../mage/cards/s/SecretsOfTheGoldenCity.java | 4 ++- Mage.Sets/src/mage/cards/v/VonasHunger.java | 4 ++- .../common/PermanentsYouControlCount.java | 35 +++++++++++++++++++ .../hint/common/PermanentsYouControlHint.java | 26 ++++++++++++++ 7 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsYouControlCount.java create mode 100644 Mage/src/main/java/mage/abilities/hint/common/PermanentsYouControlHint.java diff --git a/Mage.Sets/src/mage/cards/e/ExpelFromOrazca.java b/Mage.Sets/src/mage/cards/e/ExpelFromOrazca.java index 8feec699189..9c979fb627b 100644 --- a/Mage.Sets/src/mage/cards/e/ExpelFromOrazca.java +++ b/Mage.Sets/src/mage/cards/e/ExpelFromOrazca.java @@ -1,10 +1,10 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.keyword.AscendEffect; +import mage.abilities.hint.common.CitysBlessingHint; +import mage.abilities.hint.common.PermanentsYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -16,8 +16,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetNonlandPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ExpelFromOrazca extends CardImpl { @@ -25,8 +26,10 @@ public final class ExpelFromOrazca extends CardImpl { public ExpelFromOrazca(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); - // Ascend + // Ascend (If you control ten or more permanents, you get the city’s blessing for the rest of the game.) this.getSpellAbility().addEffect(new AscendEffect()); + this.getSpellAbility().addHint(CitysBlessingHint.instance); + this.getSpellAbility().addHint(PermanentsYouControlHint.instance); // Return target nonland permanent to its owner's hand. If you have the city's blessing, you may put that permanent on top of its owner's library instead. this.getSpellAbility().addEffect(new ExpelFromOrazcaEffect()); diff --git a/Mage.Sets/src/mage/cards/g/GoldenDemise.java b/Mage.Sets/src/mage/cards/g/GoldenDemise.java index 91641ac77da..f4a14a8c98a 100644 --- a/Mage.Sets/src/mage/cards/g/GoldenDemise.java +++ b/Mage.Sets/src/mage/cards/g/GoldenDemise.java @@ -5,6 +5,7 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.keyword.AscendEffect; import mage.abilities.hint.common.CitysBlessingHint; +import mage.abilities.hint.common.PermanentsYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -23,8 +24,10 @@ public final class GoldenDemise extends CardImpl { public GoldenDemise(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); - // Ascend + // Ascend (If you control ten or more permanents, you get the city’s blessing for the rest of the game.) this.getSpellAbility().addEffect(new AscendEffect()); + this.getSpellAbility().addHint(CitysBlessingHint.instance); + this.getSpellAbility().addHint(PermanentsYouControlHint.instance); // All creatures get -2/-2 until end of turn. If you have the city's blessing, instead only creatures your opponents control get -2/-2 until end of turn. FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponents control"); @@ -35,7 +38,6 @@ public final class GoldenDemise extends CardImpl { CitysBlessingCondition.instance, "All creatures get -2/-2 until end of turn. If you have the city's blessing, instead only creatures your opponents control get -2/-2 until end of turn" )); - this.getSpellAbility().addHint(CitysBlessingHint.instance); } public GoldenDemise(final GoldenDemise card) { diff --git a/Mage.Sets/src/mage/cards/p/PrideOfConquerors.java b/Mage.Sets/src/mage/cards/p/PrideOfConquerors.java index 00b7eefb386..02a41fe5383 100644 --- a/Mage.Sets/src/mage/cards/p/PrideOfConquerors.java +++ b/Mage.Sets/src/mage/cards/p/PrideOfConquerors.java @@ -5,6 +5,7 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.keyword.AscendEffect; import mage.abilities.hint.common.CitysBlessingHint; +import mage.abilities.hint.common.PermanentsYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -22,12 +23,13 @@ public final class PrideOfConquerors extends CardImpl { // Ascend (If you control ten or more permanents, you get the city's blessing for the rest of the game.) this.getSpellAbility().addEffect(new AscendEffect()); + this.getSpellAbility().addHint(CitysBlessingHint.instance); + this.getSpellAbility().addHint(PermanentsYouControlHint.instance); // Creatures you control get +1/+1 until end of turn. If you have the city's blessing, those creatures get +2/+2 until end of turn instead. this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn), new BoostControlledEffect(1, 1, Duration.EndOfTurn), CitysBlessingCondition.instance, "Creatures you control get +1/+1 until end of turn. If you have the city's blessing, those creatures get +2/+2 until end of turn instead")); - this.getSpellAbility().addHint(CitysBlessingHint.instance); } public PrideOfConquerors(final PrideOfConquerors card) { diff --git a/Mage.Sets/src/mage/cards/s/SecretsOfTheGoldenCity.java b/Mage.Sets/src/mage/cards/s/SecretsOfTheGoldenCity.java index 705800cd94b..02245199ab0 100644 --- a/Mage.Sets/src/mage/cards/s/SecretsOfTheGoldenCity.java +++ b/Mage.Sets/src/mage/cards/s/SecretsOfTheGoldenCity.java @@ -5,6 +5,7 @@ import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.keyword.AscendEffect; import mage.abilities.hint.common.CitysBlessingHint; +import mage.abilities.hint.common.PermanentsYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -21,6 +22,8 @@ public final class SecretsOfTheGoldenCity extends CardImpl { // Ascend this.getSpellAbility().addEffect(new AscendEffect()); + this.getSpellAbility().addHint(CitysBlessingHint.instance); + this.getSpellAbility().addHint(PermanentsYouControlHint.instance); // Draw two cards. If you have the city's blessing, draw three cards instead. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( @@ -28,7 +31,6 @@ public final class SecretsOfTheGoldenCity extends CardImpl { new DrawCardSourceControllerEffect(2), CitysBlessingCondition.instance, "Draw two cards. If you have the city's blessing, draw three cards instead")); - this.getSpellAbility().addHint(CitysBlessingHint.instance); } public SecretsOfTheGoldenCity(final SecretsOfTheGoldenCity card) { diff --git a/Mage.Sets/src/mage/cards/v/VonasHunger.java b/Mage.Sets/src/mage/cards/v/VonasHunger.java index 3d4c435349b..ce015602733 100644 --- a/Mage.Sets/src/mage/cards/v/VonasHunger.java +++ b/Mage.Sets/src/mage/cards/v/VonasHunger.java @@ -8,6 +8,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeOpponentsEffect; import mage.abilities.effects.keyword.AscendEffect; import mage.abilities.hint.common.CitysBlessingHint; +import mage.abilities.hint.common.PermanentsYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -32,6 +33,8 @@ public final class VonasHunger extends CardImpl { // Ascend (If you control ten or more permanents, you get the city's blessing for the rest of the game.) this.getSpellAbility().addEffect(new AscendEffect()); + this.getSpellAbility().addHint(CitysBlessingHint.instance); + this.getSpellAbility().addHint(PermanentsYouControlHint.instance); // Each opponent sacrifices a creature. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( @@ -43,7 +46,6 @@ public final class VonasHunger extends CardImpl { new VonasHungerEffect(), CitysBlessingCondition.instance, "If you have the city's blessing, instead each opponent sacrifices half the creatures he or she controls rounded up")); - this.getSpellAbility().addHint(CitysBlessingHint.instance); } public VonasHunger(final VonasHunger card) { diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsYouControlCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsYouControlCount.java new file mode 100644 index 00000000000..8eef2b5ca7a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsYouControlCount.java @@ -0,0 +1,35 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.filter.StaticFilters; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum PermanentsYouControlCount implements DynamicValue { + + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_PERMANENT, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); + } + + @Override + public PermanentsYouControlCount copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "permanents you control"; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/PermanentsYouControlHint.java b/Mage/src/main/java/mage/abilities/hint/common/PermanentsYouControlHint.java new file mode 100644 index 00000000000..cf47e835cd6 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/PermanentsYouControlHint.java @@ -0,0 +1,26 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.PermanentsYouControlCount; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum PermanentsYouControlHint implements Hint { + + instance; + private static final Hint hint = new ValueHint("Permanents you control", PermanentsYouControlCount.instance); + + @Override + public String getText(Game game, Ability ability) { + return hint.getText(game, ability); + } + + @Override + public Hint copy() { + return instance; + } +} From 639c4fab13c237967622ec6da0f6ca7bd6805eb2 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 14 May 2019 15:14:40 +0400 Subject: [PATCH 65/76] Improved card render modes dialog (emblems and planes support); --- .../src/main/java/mage/client/MageFrame.java | 22 +++++++++------ .../src/main/java/mage/client/cards/Card.java | 22 +++++---------- .../mage/client/combat/CombatManager.java | 10 +++---- .../client/dialog/TestCardRenderDialog.java | 27 ++++++++++++++++--- .../plugins/adapters/MageActionCallback.java | 2 -- .../java/mage/client/util/gui/ArrowUtil.java | 19 ++++++------- .../plugins/card/utils/CardImageUtils.java | 2 +- 7 files changed, 60 insertions(+), 44 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 08c29ba8ad2..d27f510ea8a 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -20,6 +20,7 @@ import mage.client.draft.DraftPane; import mage.client.draft.DraftPanel; import mage.client.game.GamePane; import mage.client.game.GamePanel; +import mage.client.game.PlayAreaPanel; import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; import mage.client.preference.MagePreferences; @@ -1004,16 +1005,16 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE) - .addComponent(mageToolbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE) + .addComponent(mageToolbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(mageToolbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(2, 2, 2) - .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 145, Short.MAX_VALUE)) + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(mageToolbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(2, 2, 2) + .addComponent(desktopPane, javax.swing.GroupLayout.DEFAULT_SIZE, 145, Short.MAX_VALUE)) ); pack(); @@ -1387,6 +1388,11 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { return GAMES.get(gameId); } + public static Map getGamePlayers(UUID gameId) { + GamePanel p = GAMES.get(gameId); + return p != null ? p.getPlayers() : new HashMap<>(); + } + public static void removeGame(UUID gameId) { GAMES.remove(gameId); } diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index 9cd5b3bc872..cde4acf2ded 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -1,10 +1,3 @@ - - - /* - * Card.java - * - * Created on 17-Dec-2009, 9:20:50 PM - */ package mage.client.cards; import mage.cards.CardDimensions; @@ -37,7 +30,6 @@ import java.util.UUID; import static mage.client.constants.Constants.*; /** - * * @author BetaSteward_at_googlemail.com */ @SuppressWarnings("serial") @@ -132,7 +124,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis gSmall.drawImage(ImageHelper.scaleImage(image, Config.dimensions.getFrameWidth(), Config.dimensions.getFrameHeight()), 0, 0, this); gImage.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE)); - gImage.drawString(card.getName()+"TEST", CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET); + gImage.drawString(card.getName() + "TEST", CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET); if (card.isCreature()) { gImage.drawString(card.getPower() + '/' + card.getToughness(), POWBOX_TEXT_MAX_LEFT, POWBOX_TEXT_MAX_TOP); } else if (card.isPlanesWalker()) { @@ -146,7 +138,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis gImage.dispose(); gSmall.setFont(new Font("Arial", Font.PLAIN, Config.dimensions.getNameFontSize())); - gSmall.drawString(card.getName()+"TEST2", Config.dimensions.getContentXOffset(), Config.dimensions.getNameYOffset()); + gSmall.drawString(card.getName() + "TEST2", Config.dimensions.getContentXOffset(), Config.dimensions.getNameYOffset()); if (card.isCreature()) { gSmall.drawString(card.getPower() + "/-/" + card.getToughness(), Config.dimensions.getPowBoxTextLeft(), Config.dimensions.getPowBoxTextTop()); } else if (card.isPlanesWalker()) { @@ -259,12 +251,12 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis return sbType.toString(); } - + protected void drawDetailed(Graphics2D g) { // Get the size of the card int width = getWidth(); int height = getHeight(); - + g.setColor(Color.black); g.drawRoundRect(0, 0, width, height, 4, 4); g.setColor(Color.white); @@ -309,7 +301,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis @Override public void paintComponent(Graphics graphics) { - drawDetailed((Graphics2D)graphics); + drawDetailed((Graphics2D) graphics); /* Graphics2D g2 = (Graphics2D) graphics; g2.drawImage(small, 0, 0, this); @@ -367,13 +359,13 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis List targets = card.getTargets(); if (targets != null) { for (UUID uuid : targets) { - PlayAreaPanel playAreaPanel = MageFrame.getGame(gameId).getPlayers().get(uuid); + PlayAreaPanel playAreaPanel = MageFrame.getGamePlayers(gameId).get(uuid); if (playAreaPanel != null) { Point target = playAreaPanel.getLocationOnScreen(); Point me = this.getLocationOnScreen(); ArrowBuilder.getBuilder().addArrow(gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() - 40, Color.red, ArrowBuilder.Type.TARGET); } else { - for (PlayAreaPanel pa : MageFrame.getGame(gameId).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point target = permanent.getLocationOnScreen(); diff --git a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java index b9be142a832..6c80cbd74a8 100644 --- a/Mage.Client/src/main/java/mage/client/combat/CombatManager.java +++ b/Mage.Client/src/main/java/mage/client/combat/CombatManager.java @@ -3,8 +3,8 @@ package mage.client.combat; import mage.cards.MagePermanent; import mage.client.MageFrame; import mage.client.game.PlayAreaPanel; -import mage.client.util.audio.AudioManager; import mage.client.util.SettingsManager; +import mage.client.util.audio.AudioManager; import mage.client.util.gui.ArrowBuilder; import mage.view.CardView; import mage.view.CombatGroupView; @@ -67,7 +67,7 @@ public enum CombatManager { } private void drawAttacker(CombatGroupView group, CardView attacker, UUID gameId) { - for (PlayAreaPanel pa2 : MageFrame.getGame(gameId).getPlayers().values()) { + for (PlayAreaPanel pa2 : MageFrame.getGamePlayers(gameId).values()) { MagePermanent attackerCard = pa2.getBattlefieldPanel().getPermanents().get(attacker.getId()); if (attackerCard != null) { drawDefender(group, attackerCard, gameId); @@ -80,7 +80,7 @@ public enum CombatManager { UUID defenderId = group.getDefenderId(); if (defenderId != null) { parentPoint = getParentPoint(attackerCard); - PlayAreaPanel p = MageFrame.getGame(gameId).getPlayers().get(defenderId); + PlayAreaPanel p = MageFrame.getGamePlayers(gameId).get(defenderId); if (p != null) { Point target = p.getLocationOnScreen(); target.translate(-parentPoint.x, -parentPoint.y); @@ -88,7 +88,7 @@ public enum CombatManager { attackerPoint.translate(-parentPoint.x, -parentPoint.y); ArrowBuilder.getBuilder().addArrow(gameId, (int) attackerPoint.getX() + 45, (int) attackerPoint.getY() + 25, (int) target.getX() + 40, (int) target.getY() - 20, Color.red, ArrowBuilder.Type.COMBAT); } else { - for (PlayAreaPanel pa : MageFrame.getGame(gameId).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(defenderId); if (permanent != null) { Point target = permanent.getLocationOnScreen(); @@ -104,7 +104,7 @@ public enum CombatManager { private void drawBlockers(CombatGroupView group, MagePermanent attackerCard, UUID gameId) { for (CardView blocker : group.getBlockers().values()) { - for (PlayAreaPanel pa : MageFrame.getGame(gameId).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(gameId).values()) { MagePermanent blockerCard = pa.getBattlefieldPanel().getPermanents().get(blocker.getId()); if (blockerCard != null) { parentPoint = getParentPoint(blockerCard); diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index e306d28bd58..29b37987a66 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -16,14 +16,17 @@ import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; import mage.game.Game; import mage.game.GameImpl; +import mage.game.command.Emblem; +import mage.game.command.Plane; +import mage.game.command.emblems.AjaniAdversaryOfTyrantsEmblem; +import mage.game.command.planes.AkoumPlane; import mage.game.match.MatchType; import mage.game.mulligan.Mulligan; import mage.game.mulligan.VancouverMulligan; import mage.game.permanent.PermanentCard; import mage.players.Player; import mage.players.StubPlayer; -import mage.view.CardsView; -import mage.view.PermanentView; +import mage.view.*; import org.apache.log4j.Logger; import javax.swing.*; @@ -94,6 +97,18 @@ public class TestCardRenderDialog extends MageDialog { return cardView; } + private AbilityView createEmblem(Emblem emblem) { + AbilityView emblemView = new AbilityView(emblem.getAbilities().get(0), emblem.getName(), new CardView(new EmblemView(emblem))); + emblemView.setName(emblem.getName()); + return emblemView; + } + + private AbilityView createPlane(Plane plane) { + AbilityView planeView = new AbilityView(plane.getAbilities().get(0), plane.getName(), new CardView(new PlaneView(plane))); + planeView.setName(plane.getName()); + return planeView; + } + private void reloadCards() { cardsPanel.cleanUp(); cardsPanel.setCustomRenderMode(comboRenderMode.getSelectedIndex()); @@ -105,7 +120,7 @@ public class TestCardRenderDialog extends MageDialog { BigCard big = new BigCard(); CardsView view = new CardsView(); - PermanentView card; + CardView card; card = createCard(game, player.getId(), "RNA", "263", 0, 0, 0); // mountain view.put(card.getId(), card); card = createCard(game, player.getId(), "RNA", "185", 0, 0, 0); // Judith, the Scourge Diva @@ -120,11 +135,15 @@ public class TestCardRenderDialog extends MageDialog { view.put(card.getId(), card); card = createCard(game, player.getId(), "XLN", "234", 0, 0, 0); // Conqueror's Galleon view.put(card.getId(), card); + card = createEmblem(new AjaniAdversaryOfTyrantsEmblem()); // Emblem Ajani + view.put(card.getId(), card); + card = createPlane(new AkoumPlane()); // Plane - Akoum + view.put(card.getId(), card); cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); cardsPanel.changeGUISize(); - cardsPanel.loadCards(view, big, null); + cardsPanel.loadCards(view, big, game.getId()); } private int getCardWidth() { diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index bd3d34052b7..cf5a65eee09 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -363,8 +363,6 @@ public class MageActionCallback implements ActionCallback { if (!((GamePane) topPane).getGameId().equals(data.getGameId())) { return; } - } else if (data.getGameId() != null) { - return; } hideTooltipPopup(); diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java index c6088f2089f..243b47dc7c7 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java @@ -16,14 +16,15 @@ import java.util.UUID; */ public final class ArrowUtil { - private ArrowUtil() {} + private ArrowUtil() { + } public static void drawArrowsForPairedCards(TransferData data, Point parentPoint) { if (data.getCard().getPairedCard() != null) { Point me = new Point(data.getLocationOnScreen()); me.translate(-parentPoint.x, -parentPoint.y); UUID uuid = data.getCard().getPairedCard(); - for (PlayAreaPanel pa : MageFrame.getGame(data.getGameId()).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point target = permanent.getLocationOnScreen(); @@ -38,7 +39,7 @@ public final class ArrowUtil { if (data.getCard().getBandedCards() != null && !data.getCard().getBandedCards().isEmpty()) { Point me = new Point(data.getLocationOnScreen()); me.translate(-parentPoint.x, -parentPoint.y); - for (PlayAreaPanel pa : MageFrame.getGame(data.getGameId()).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { for (UUID uuid : data.getCard().getBandedCards()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { @@ -53,7 +54,7 @@ public final class ArrowUtil { public static void drawArrowsForEnchantPlayers(TransferData data, Point parentPoint) { if (data.getGameId() != null && MageFrame.getGame(data.getGameId()) != null) { - for (PlayAreaPanel pa : MageFrame.getGame(data.getGameId()).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { PlayerPanelExt playAreaPanel = pa.getPlayerPanel(); if (playAreaPanel != null && playAreaPanel.getPlayer() != null && playAreaPanel.getPlayer().hasAttachments()) { Point me = new Point(data.getLocationOnScreen()); @@ -62,7 +63,7 @@ public final class ArrowUtil { if (attachmentId.equals(data.getCard().getId())) { Point player = pa.getLocationOnScreen(); player.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(data.getGameId(),(int) me.getX() + 35, (int) me.getY(), (int) player.getX() + 40, (int) player.getY() - 40, Color.magenta, ArrowBuilder.Type.ENCHANT_PLAYERS); + ArrowBuilder.getBuilder().addArrow(data.getGameId(), (int) me.getX() + 35, (int) me.getY(), (int) player.getX() + 40, (int) player.getY() - 40, Color.magenta, ArrowBuilder.Type.ENCHANT_PLAYERS); } } } @@ -75,7 +76,7 @@ public final class ArrowUtil { Point me = new Point(data.getLocationOnScreen()); me.translate(-parentPoint.x, -parentPoint.y); UUID uuid = data.getCard().getParentId(); - for (PlayAreaPanel pa : MageFrame.getGame(data.getGameId()).getPlayers().values()) { + for (PlayAreaPanel pa : MageFrame.getGamePlayers(data.getGameId()).values()) { MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point source = permanent.getLocationOnScreen(); @@ -96,7 +97,7 @@ public final class ArrowUtil { me.translate(-parentPoint.x, -parentPoint.y); for (UUID uuid : targets) { - PlayAreaPanel p = MageFrame.getGame(data.getGameId()).getPlayers().get(uuid); + PlayAreaPanel p = MageFrame.getGamePlayers(data.getGameId()).get(uuid); if (p != null) { Point target = p.getLocationOnScreen(); target.translate(-parentPoint.x, -parentPoint.y); @@ -104,7 +105,7 @@ public final class ArrowUtil { continue; } - for (PlayAreaPanel panel : MageFrame.getGame(data.getGameId()).getPlayers().values()) { + for (PlayAreaPanel panel : MageFrame.getGamePlayers(data.getGameId()).values()) { MagePermanent permanent = panel.getBattlefieldPanel().getPermanents().get(uuid); if (permanent != null) { Point target = permanent.getLocationOnScreen(); @@ -117,7 +118,7 @@ public final class ArrowUtil { if (view != null) { CardsView graveyard = view.getGraveyard(); if (graveyard.containsKey(uuid)) { - p = MageFrame.getGame(data.getGameId()).getPlayers().get(view.getPlayerId()); + p = MageFrame.getGamePlayers(data.getGameId()).get(view.getPlayerId()); if (p != null) { Point target = p.getLocationOnScreen(); target.translate(-parentPoint.x, -parentPoint.y); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java index c7dae393558..a22e0667b3f 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java @@ -52,7 +52,7 @@ public final class CardImageUtils { return filePath; } - log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + getTokenImagePath(card)); + //log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + getTokenImagePath(card)); } else { log.warn("Trying to get token path for non token card. Set: " + card.getSet() + " Set Code: " + card.getTokenSetCode() + " Name: " + card.getName()); } From 1dcdff58d250bf9a6a2e7b1ab03ed03c655c1a6f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 14 May 2019 15:36:33 +0400 Subject: [PATCH 66/76] Fixed html visible title in choose cards dialog; --- .../common/discard/DiscardCardYouChooseTargetEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java index ad8a25bbdea..e658515c4c8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java @@ -106,7 +106,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { Cards revealedCards = new CardsImpl(); numberToReveal = Math.min(player.getHand().size(), numberToReveal); if (player.getHand().size() > numberToReveal) { - TargetCardInHand chosenCards = new TargetCardInHand(numberToReveal, numberToReveal, new FilterCard("card in " + player.getLogName() + "'s hand")); + TargetCardInHand chosenCards = new TargetCardInHand(numberToReveal, numberToReveal, new FilterCard("card in " + player.getName() + "'s hand")); chosenCards.setNotTarget(true); if (chosenCards.canChoose(player.getId(), game) && player.chooseTarget(Outcome.Discard, player.getHand(), chosenCards, source, game)) { if (!chosenCards.getTargets().isEmpty()) { From 79a99a0866a3ee5ff8be3e7b722279ecfd9a99d8 Mon Sep 17 00:00:00 2001 From: Derek Monturo Date: Tue, 14 May 2019 08:12:31 -0400 Subject: [PATCH 67/76] Detection Tower and Witchbane Orb interaction bug confirmed for #5796 --- .../cards/abilities/curses/CursesTest.java | 2 +- .../asthough/DidNotHaveHexproofTest.java | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index fd43fd20614..4d208738027 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -426,7 +426,7 @@ public class CursesTest extends CardTestPlayerBase { } /* - * Reported bug issue #3326 (NOTE test is failing due to bug in code) + * Reported bug issue #3326 * When {Witchbane Orb} triggers when entering the field and there IS a curse attached to you, an error message (I sadly skipped) appears and your turn is reset. This happened to me in a 4-player Commander game with {Curse of the Shallow Graves} on the field. */ diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java new file mode 100644 index 00000000000..a4117f7b3e3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java @@ -0,0 +1,54 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.mage.test.cards.asthough; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author drmDev + */ +public class DidNotHaveHexproofTest extends CardTestPlayerBase { + + /* + Witchbane Orb (4) + When Witchbane Orb enters the battlefield, destroy all Curses attached to you. + You have hexproof. (You can't be the target of spells or abilities your opponents control, including Aura spells.) + */ + public static final String wOrb = "Witchbane Orb"; + + /* + Detection Tower (Land) + {T}: Add Colorless. + 1, {T}: Until end of turn, your opponents and creatures your opponents control with hexproof can be the targets of spells and abilities you control + as though they didn't have hexproof. + */ + public static final String dTower = "Detection Tower"; + + @Test + public void detectionTowerAllowsTargettingPlayerWithWitchbaneOrb() { + + addCard(Zone.BATTLEFIELD, playerA, dTower); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Shock"); // {R} 2 dmg to any target + addCard(Zone.BATTLEFIELD, playerB, wOrb); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertTappedCount("Mountain", true, 2); + assertTapped(dTower, true); + assertGraveyardCount(playerA, "Shock", 1); + assertLife(playerB, 18); + } +} From bf8dd78b6e1a33d404ad29d6808fbbec18cd6ff0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 15 May 2019 17:33:34 +0200 Subject: [PATCH 68/76] * Fixed player Hexproof ThoughtAsIf handling (fixes #5796). --- .../src/mage/cards/d/DetectionTower.java | 8 +-- .../main/java/mage/players/PlayerImpl.java | 68 +++++++++---------- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DetectionTower.java b/Mage.Sets/src/mage/cards/d/DetectionTower.java index 3f0ec089949..6737f549a01 100644 --- a/Mage.Sets/src/mage/cards/d/DetectionTower.java +++ b/Mage.Sets/src/mage/cards/d/DetectionTower.java @@ -72,12 +72,12 @@ class DetectionTowerEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - if (game.getOpponents(source.getControllerId()).contains(sourceId)) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (affectedControllerId.equals(source.getControllerId())) { // + if (game.getOpponents(source.getControllerId()).contains(objectId)) { return true; } - Permanent creature = game.getPermanent(sourceId); + Permanent creature = game.getPermanent(objectId); if (creature != null && game.getOpponents(source.getControllerId()).contains(creature.getControllerId())) { return true; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index ee6ac479b7a..bed49eb5737 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1,6 +1,10 @@ package mage.players; import com.google.common.collect.ImmutableMap; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; import mage.ConditionalMana; import mage.MageObject; import mage.MageObjectReference; @@ -66,11 +70,6 @@ import mage.util.GameLog; import mage.util.RandomUtil; import org.apache.log4j.Logger; -import java.io.Serializable; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.Map.Entry; - public abstract class PlayerImpl implements Player, Serializable { private static final Logger logger = Logger.getLogger(PlayerImpl.class); @@ -182,7 +181,6 @@ public abstract class PlayerImpl implements Player, Serializable { protected final Map silentPhaseSteps = ImmutableMap.builder(). put(PhaseStep.DECLARE_ATTACKERS, Step.StepPart.PRE).build(); - public PlayerImpl(String name, RangeOfInfluence range) { this(UUID.randomUUID()); this.name = name; @@ -611,7 +609,7 @@ public abstract class PlayerImpl implements Player, Serializable { } if (abilities.containsKey(HexproofAbility.getInstance().getId())) { if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, this.getId(), game)) { + && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) { return false; } } @@ -1418,10 +1416,10 @@ public abstract class PlayerImpl implements Player, Serializable { != null // if anyone sees an issue with this code, please report it. Worked in my testing. || game.getContinuousEffects().asThough(object.getId(), - AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, - ability, - this.getId(), - game) + AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, + ability, + this.getId(), + game) != null) { if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) { @@ -2639,7 +2637,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param game * @param appliedEffects - * @param numSides Number of sides the dice has + * @param numSides Number of sides the dice has * @return the number that the player rolled */ @Override @@ -2673,10 +2671,10 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param game * @param appliedEffects - * @param numberChaosSides The number of chaos sides the planar die - * currently has (normally 1 but can be 5) + * @param numberChaosSides The number of chaos sides the planar die + * currently has (normally 1 but can be 5) * @param numberPlanarSides The number of chaos sides the planar die - * currently has (normally 1) + * currently has (normally 1) * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll * or NilRoll */ @@ -2833,7 +2831,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param ability - * @param available if null, it won't be checked if enough mana is available + * @param available if null, it won't be checked if enough mana is available * @param sourceObject * @param game * @return @@ -3383,7 +3381,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId, - UUID controllerId, Game game + UUID controllerId, Game game ) { return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game); } @@ -3534,8 +3532,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Card card, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { Set cardList = new HashSet<>(); if (card != null) { @@ -3546,22 +3544,22 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Cards cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return moveCards(cards.getCards(game), toZone, source, game); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return moveCards(cards, toZone, source, game, false, false, false, null); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { if (cards.isEmpty()) { return true; @@ -3647,8 +3645,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardsToExile(Card card, Ability source, - Game game, boolean withName, UUID exileId, - String exileZoneName + Game game, boolean withName, UUID exileId, + String exileZoneName ) { Set cards = new HashSet<>(); cards.add(card); @@ -3657,8 +3655,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardsToExile(Set cards, Ability source, - Game game, boolean withName, UUID exileId, - String exileZoneName + Game game, boolean withName, UUID exileId, + String exileZoneName ) { if (cards.isEmpty()) { return true; @@ -3673,14 +3671,14 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToHandWithInfo(Card card, UUID sourceId, - Game game + Game game ) { return this.moveCardToHandWithInfo(card, sourceId, game, true); } @Override public boolean moveCardToHandWithInfo(Card card, UUID sourceId, - Game game, boolean withName + Game game, boolean withName ) { boolean result = false; Zone fromZone = game.getState().getZone(card.getId()); @@ -3705,7 +3703,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source, - Game game, Zone fromZone + Game game, Zone fromZone ) { UUID sourceId = source == null ? null : source.getSourceId(); Set movedCards = new LinkedHashSet<>(); @@ -3713,7 +3711,7 @@ public abstract class PlayerImpl implements Player, Serializable { // identify cards from one owner Cards cards = new CardsImpl(); UUID ownerId = null; - for (Iterator it = allCards.iterator(); it.hasNext(); ) { + for (Iterator it = allCards.iterator(); it.hasNext();) { Card card = it.next(); if (cards.isEmpty()) { ownerId = card.getOwnerId(); @@ -3774,7 +3772,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, - Game game, Zone fromZone + Game game, Zone fromZone ) { if (card == null) { return false; @@ -3803,8 +3801,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, - Game game, Zone fromZone, - boolean toTop, boolean withName + Game game, Zone fromZone, + boolean toTop, boolean withName ) { if (card == null) { return false; @@ -3838,7 +3836,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId, - Game game, Zone fromZone, boolean withName) { + Game game, Zone fromZone, boolean withName) { if (card == null) { return false; } From 45c7d453b567d0d9c27b647d4512bd088db0b766 Mon Sep 17 00:00:00 2001 From: Derek Monturo Date: Wed, 15 May 2019 20:59:55 -0400 Subject: [PATCH 69/76] adjusted unit test phases --- .../mage/test/cards/asthough/DidNotHaveHexproofTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java index a4117f7b3e3..369faa3aecb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/DidNotHaveHexproofTest.java @@ -41,14 +41,15 @@ public class DidNotHaveHexproofTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, wOrb); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", playerB); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Shock", playerB); - setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStopAt(1, PhaseStep.END_COMBAT); execute(); - + assertTappedCount("Mountain", true, 2); assertTapped(dTower, true); assertGraveyardCount(playerA, "Shock", 1); assertLife(playerB, 18); + assertAllCommandsUsed(); } } From 1592a4b3f36ea4920092a15be38ea9560b5c1efb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 16 May 2019 14:40:57 +0200 Subject: [PATCH 70/76] * Sower of Discord - Fixed not working triggered ability. --- Mage.Sets/src/mage/cards/s/SowerOfDiscord.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java b/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java index 18d91f47571..a277ee931e7 100644 --- a/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java +++ b/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java @@ -8,12 +8,12 @@ import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -124,7 +124,7 @@ class SowerOfDiscordTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; } @Override From 144613eed8d1d61ed3dd824db501c96cd38692fe Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 17 May 2019 12:12:42 +0200 Subject: [PATCH 71/76] * Vraska, Swarm's Eminence - Fixed hat ability did not trigger on attacked Planeswalker, fixed filter. --- .../mage/cards/v/VraskaSwarmsEminence.java | 71 +++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java b/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java index e1a7d3f2a5f..3dc008f4edb 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java +++ b/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java @@ -1,31 +1,35 @@ package mage.cards.v; +import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.game.permanent.token.AssassinToken2; - -import java.util.UUID; +import mage.target.targetpointer.FixedTarget; /** * @author TheElk801 */ public final class VraskaSwarmsEminence extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you control with deathtouch"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creature you control with deathtouch"); static { filter.add(new AbilityPredicate(DeathtouchAbility.class)); @@ -39,12 +43,9 @@ public final class VraskaSwarmsEminence extends CardImpl { this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); // Whenever a creature you control with deathtouch deals damage to a player or planeswalker, put a +1/+1 counter on that creature. - // TODO: make this trigger on planeswalkers - this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( - new AddCountersTargetEffect( - CounterType.P1P1.createInstance() - ).setText("put a +1/+1 counter on that creature"), - filter, false, SetTargetPointer.PERMANENT, false) + this.addAbility(new VraskaSwarmsEminenceTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersTargetEffect(CounterType.P1P1.createInstance()).setText("put a +1/+1 counter on that creature"), + filter) ); // -2: Create a 1/1 black Assassin creature token with deathtouch and "Whenever this creature deals damage to a planeswalker, destroy that planeswalker." @@ -60,3 +61,49 @@ public final class VraskaSwarmsEminence extends CardImpl { return new VraskaSwarmsEminence(this); } } + +class VraskaSwarmsEminenceTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filter; + + public VraskaSwarmsEminenceTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter) { + super(zone, effect, false); + this.filter = filter; + } + + public VraskaSwarmsEminenceTriggeredAbility(final VraskaSwarmsEminenceTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + } + + @Override + public VraskaSwarmsEminenceTriggeredAbility copy() { + return new VraskaSwarmsEminenceTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER + || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { + for (Effect effect : this.getEffects()) { + effect.setValue("damage", event.getAmount()); + effect.setValue("sourceId", event.getSourceId()); + effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); + } + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature you control with deathtouch deals damage to a player or planeswalker," + super.getRule(); + } + +} From ffbd5d373b13466f9087d15191d42c4b098d4cfa Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 17 May 2019 16:10:34 +0400 Subject: [PATCH 72/76] * Planeswalker abilities - fixed that plus cost's counters is not affected by replacement effects (#5802, example combo: Planeswalker + Pir, Imaginative Rascal + Doubling Season); --- .../src/mage/cards/c/CorpsejackMenace.java | 26 +++--- .../src/mage/cards/d/DoublingSeason.java | 8 +- .../src/mage/cards/h/HardenedScales.java | 20 ++--- .../src/mage/cards/m/MeliraSylvokOutcast.java | 18 ++-- .../src/mage/cards/m/MowuLoyalCompanion.java | 13 +-- .../mage/cards/p/PirImaginativeRascal.java | 19 ++--- Mage.Sets/src/mage/cards/p/PrimalVigor.java | 16 ++-- Mage.Sets/src/mage/cards/s/Solemnity.java | 25 ++---- .../src/mage/cards/v/VizierOfRemedies.java | 19 ++--- .../src/mage/cards/w/WindingConstrictor.java | 20 ++--- .../cards/replacement/DoublingSeasonTest.java | 36 +++++++- Mage/src/main/java/mage/cards/CardImpl.java | 26 +++--- .../main/java/mage/game/events/GameEvent.java | 14 +++ .../main/java/mage/players/PlayerImpl.java | 85 ++++++++++--------- 14 files changed, 176 insertions(+), 169 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java b/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java index cb0f555675a..5032f414fce 100644 --- a/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java +++ b/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java @@ -1,29 +1,25 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * http://www.wizards.com/magic/magazine/article.aspx?x=mtg/faq/rtr - * + *

* If a creature you control would enter the battlefield with a number of +1/+1 * counters on it, it enters with twice that many instead. - * + *

* If you control two Corpsejack Menaces, the number of +1/+1 counters placed is * four times the original number. Three Corpsejack Menaces multiplies the * original number by eight, and so on. @@ -33,7 +29,7 @@ import mage.game.permanent.Permanent; public final class CorpsejackMenace extends CardImpl { public CorpsejackMenace(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); this.subtype.add(SubType.FUNGUS); this.power = new MageInt(4); @@ -67,7 +63,7 @@ class CorpsejackMenaceReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() * 2); + event.setAmountForCounters(event.getAmount() * 2, true); return false; } @@ -78,15 +74,13 @@ class CorpsejackMenaceReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getData().equals(CounterType.P1P1.getName())) { + if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - if (permanent != null && permanent.isControlledBy(source.getControllerId()) - && permanent.isCreature()) { - return true; - } + return permanent != null && permanent.isControlledBy(source.getControllerId()) + && permanent.isCreature(); } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DoublingSeason.java b/Mage.Sets/src/mage/cards/d/DoublingSeason.java index fab6ae021d2..5620ad24036 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingSeason.java +++ b/Mage.Sets/src/mage/cards/d/DoublingSeason.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; @@ -16,8 +14,9 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DoublingSeason extends CardImpl { @@ -58,7 +57,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() * 2); + event.setAmountForCounters(event.getAmount() * 2, true); return false; } @@ -80,6 +79,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl { } return permanent != null && permanent.isControlledBy(source.getControllerId()) + && event.getAmount() > 0 && !landPlayed; // example: gemstone mine being played as a land drop } diff --git a/Mage.Sets/src/mage/cards/h/HardenedScales.java b/Mage.Sets/src/mage/cards/h/HardenedScales.java index ce764544f94..8d4390296f9 100644 --- a/Mage.Sets/src/mage/cards/h/HardenedScales.java +++ b/Mage.Sets/src/mage/cards/h/HardenedScales.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; @@ -17,14 +15,15 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HardenedScales extends CardImpl { public HardenedScales(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HardenedScalesEffect())); @@ -54,10 +53,7 @@ class HardenedScalesEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - int amount = event.getAmount(); - if (amount >= 1) { - event.setAmount(amount + 1); - } + event.setAmountForCounters(event.getAmount() + 1, true); return false; } @@ -68,15 +64,13 @@ class HardenedScalesEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getData().equals(CounterType.P1P1.getName())) { + if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - if (permanent != null && permanent.isControlledBy(source.getControllerId()) - && permanent.isCreature()) { - return true; - } + return permanent != null && permanent.isControlledBy(source.getControllerId()) + && permanent.isCreature(); } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java index d3d868a2e25..3c6124c9463 100644 --- a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java +++ b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java @@ -1,8 +1,5 @@ - package mage.cards.m; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -19,14 +16,16 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class MeliraSylvokOutcast extends CardImpl { public MeliraSylvokOutcast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SCOUT); @@ -78,7 +77,7 @@ class MeliraSylvokOutcastEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ADD_COUNTER; + return event.getType() == EventType.ADD_COUNTERS; } @Override @@ -111,7 +110,7 @@ class MeliraSylvokOutcastEffect2 extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ADD_COUNTER; + return event.getType() == EventType.ADD_COUNTERS; } @Override @@ -121,13 +120,10 @@ class MeliraSylvokOutcastEffect2 extends ReplacementEffectImpl { if (perm == null) { perm = game.getPermanentEntering(event.getTargetId()); } - if (perm != null && perm.isCreature() && perm.isControlledBy(source.getControllerId())) { - return true; - } + return perm != null && perm.isCreature() && perm.isControlledBy(source.getControllerId()); } return false; } - } class MeliraSylvokOutcastEffect3 extends ContinuousEffectImpl { diff --git a/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java b/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java index 6b3fdc2c7ca..946b515178e 100644 --- a/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java +++ b/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java @@ -63,10 +63,7 @@ class MowuLoyalCompanionEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - int amount = event.getAmount(); - if (amount > 0) { - event.setAmount(amount + 1); - } + event.setAmountForCounters(event.getAmount() + 1, true); return false; } @@ -77,15 +74,13 @@ class MowuLoyalCompanionEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getData().equals(CounterType.P1P1.getName())) { + if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - if (permanent != null && permanent.getId().equals(source.getSourceId()) - && permanent.isCreature()) { - return true; - } + return permanent != null && permanent.getId().equals(source.getSourceId()) + && permanent.isCreature(); } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PirImaginativeRascal.java b/Mage.Sets/src/mage/cards/p/PirImaginativeRascal.java index 3679e5533d3..3f2bc145daa 100644 --- a/Mage.Sets/src/mage/cards/p/PirImaginativeRascal.java +++ b/Mage.Sets/src/mage/cards/p/PirImaginativeRascal.java @@ -1,27 +1,21 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.keyword.PartnerWithAbility; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class PirImaginativeRascal extends CardImpl { @@ -65,10 +59,7 @@ class PirImaginativeRascalEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - int amount = event.getAmount(); - if (amount >= 1) { - event.setAmount(amount + 1); - } + event.setAmountForCounters(event.getAmount() + 1, true); return false; } @@ -84,7 +75,7 @@ class PirImaginativeRascalEffect extends ReplacementEffectImpl { if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - return permanent != null && player != null + return permanent != null && player != null && event.getAmount() > 0 && !player.hasOpponent(permanent.getControllerId(), game); } diff --git a/Mage.Sets/src/mage/cards/p/PrimalVigor.java b/Mage.Sets/src/mage/cards/p/PrimalVigor.java index 184b1bc499e..7443eca630f 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalVigor.java +++ b/Mage.Sets/src/mage/cards/p/PrimalVigor.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; @@ -15,14 +13,15 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PrimalVigor extends CardImpl { public PrimalVigor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}"); // If one or more tokens would be created, twice that many of those tokens are created instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PrimalVigorTokenEffect())); @@ -93,7 +92,7 @@ class PrimalVigorCounterEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() * 2); + event.setAmountForCounters(event.getAmount() * 2, true); return false; } @@ -108,11 +107,8 @@ class PrimalVigorCounterEffect extends ReplacementEffectImpl { if (permanent == null) { permanent = game.getPermanentEntering(event.getTargetId()); } - if (permanent != null && permanent.isCreature() - && event.getData() != null && event.getData().equals("+1/+1")) { - return true; - } - return false; + return permanent != null && event.getAmount() > 0 && permanent.isCreature() + && event.getData() != null && event.getData().equals("+1/+1"); } @Override diff --git a/Mage.Sets/src/mage/cards/s/Solemnity.java b/Mage.Sets/src/mage/cards/s/Solemnity.java index 0ce74644dac..858d9c85f29 100644 --- a/Mage.Sets/src/mage/cards/s/Solemnity.java +++ b/Mage.Sets/src/mage/cards/s/Solemnity.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -21,8 +19,9 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author spjspj */ public final class Solemnity extends CardImpl { @@ -70,7 +69,7 @@ class SolemnityEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ADD_COUNTER; + return event.getType() == EventType.ADD_COUNTERS; } @Override @@ -113,7 +112,7 @@ class SolemnityEffect2 extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ADD_COUNTER || event.getType() == EventType.ADD_COUNTERS; + return event.getType() == EventType.ADD_COUNTERS; } @Override @@ -124,21 +123,13 @@ class SolemnityEffect2 extends ReplacementEffectImpl { Permanent permanent3 = game.getPermanentEntering(event.getTargetId()); if (object instanceof Permanent) { - if (filter.match((Permanent) object, game)) { - return true; - } + return filter.match((Permanent) object, game); } else if (permanent != null) { - if (filter.match(permanent, game)) { - return true; - } + return filter.match(permanent, game); } else if (permanent2 != null) { - if (filter.match(permanent2, game)) { - return true; - } + return filter.match(permanent2, game); } else if (permanent3 != null) { - if (filter.match(permanent3, game)) { - return true; - } + return filter.match(permanent3, game); } return false; diff --git a/Mage.Sets/src/mage/cards/v/VizierOfRemedies.java b/Mage.Sets/src/mage/cards/v/VizierOfRemedies.java index 2823ffa75b1..29e2cd99528 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfRemedies.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfRemedies.java @@ -1,24 +1,19 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; +import java.util.UUID; + /** - * * @author Stravant */ public final class VizierOfRemedies extends CardImpl { @@ -64,7 +59,7 @@ class VizierOfRemediesReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() - 1); + event.setAmountForCounters(event.getAmount() - 1, true); return false; } @@ -76,11 +71,9 @@ class VizierOfRemediesReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (source != null && source.getControllerId() != null) { - if (source.isControlledBy(game.getControllerId(event.getTargetId())) + return source.isControlledBy(game.getControllerId(event.getTargetId())) && event.getData() != null && event.getData().equals(CounterType.M1M1.getName()) - && event.getAmount() > 0) { - return true; - } + && event.getAmount() > 0; } return false; } diff --git a/Mage.Sets/src/mage/cards/w/WindingConstrictor.java b/Mage.Sets/src/mage/cards/w/WindingConstrictor.java index 3d113229f5e..c5db174c387 100644 --- a/Mage.Sets/src/mage/cards/w/WindingConstrictor.java +++ b/Mage.Sets/src/mage/cards/w/WindingConstrictor.java @@ -1,25 +1,20 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class WindingConstrictor extends CardImpl { @@ -32,7 +27,7 @@ public final class WindingConstrictor extends CardImpl { this.toughness = new MageInt(3); // If one or more counters would be put on an artifact or creature you control, that many plus one of each of those kinds of counters are put on that permanent instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WindingConstrictorPermanentEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WindingConstrictorPermanentEffect())); // If you would get one or more counters, you get that many plus one of each of those kinds of counters instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WindingConstrictorPlayerEffect())); @@ -62,7 +57,7 @@ class WindingConstrictorPermanentEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() + 1); + event.setAmountForCounters(event.getAmount() + 1, true); return false; } @@ -78,6 +73,7 @@ class WindingConstrictorPermanentEffect extends ReplacementEffectImpl { permanent = game.getPermanentEntering(event.getTargetId()); } return permanent != null + && event.getAmount() > 0 && (permanent.isCreature() || permanent.isArtifact()) && permanent.isControlledBy(source.getControllerId()); } @@ -106,7 +102,7 @@ class WindingConstrictorPlayerEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(event.getAmount() + 1); + event.setAmountForCounters(event.getAmount() + 1, true); return false; } @@ -118,7 +114,7 @@ class WindingConstrictorPlayerEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getTargetId()); - return player != null && player.getId().equals(source.getControllerId()); + return player != null && player.getId().equals(source.getControllerId()) && event.getAmount() > 0; } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java index 1d428a30a08..173bb38a70d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java @@ -205,7 +205,7 @@ public class DoublingSeasonTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Doubling Season"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA,"+1: Draw a card, then discard a card at random."); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Draw a card, then discard a card at random."); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -216,4 +216,38 @@ public class DoublingSeasonTest extends CardTestPlayerBase { //Should not be doubled assertCounterCount("Tibalt, the Fiend-Blooded", CounterType.LOYALTY, 3); } + + /** + * +1 cost is not affected by double, but replace event like Pir, Imaginative Rascal will be affected + * https://github.com/magefree/mage/issues/5802 + */ + @Test + public void testPlaneswalkerWithoutReplacementEffect() { + //addCard(Zone.BATTLEFIELD, playerA, "Pir, Imaginative Rascal"); + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Fire Artisan"); + addCard(Zone.BATTLEFIELD, playerA, "Doubling Season"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Chandra, Fire Artisan", CounterType.LOYALTY, 4 + 1); + } + + @Test + public void testPlaneswalkerWithReplacementEffect() { + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Fire Artisan"); + addCard(Zone.BATTLEFIELD, playerA, "Doubling Season"); // x2 counters + addCard(Zone.BATTLEFIELD, playerA, "Pir, Imaginative Rascal"); // +1 counter + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Chandra, Fire Artisan", CounterType.LOYALTY, 4 + (1 + 1) * 2); + } } diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 1bb7efb4e42..317bec4adda 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -690,27 +690,33 @@ public abstract class CardImpl extends MageObjectImpl implements Card { sourceId = source.getSourceId(); } } - GameEvent countersEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount()); - countersEvent.setAppliedEffects(appliedEffects); - countersEvent.setFlag(isEffect); - if (!game.replaceEvent(countersEvent)) { - int amount = countersEvent.getAmount(); + GameEvent addingAllEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount()); + addingAllEvent.setAppliedEffects(appliedEffects); + addingAllEvent.setFlag(isEffect); + if (!game.replaceEvent(addingAllEvent)) { + int amount = addingAllEvent.getAmount(); + boolean isEffectFlag = addingAllEvent.getFlag(); int finalAmount = amount; for (int i = 0; i < amount; i++) { Counter eventCounter = counter.copy(); eventCounter.remove(eventCounter.getCount() - 1); - GameEvent event = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1); - event.setAppliedEffects(appliedEffects); - if (!game.replaceEvent(event)) { + GameEvent addingOneEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1); + addingOneEvent.setAppliedEffects(appliedEffects); + addingOneEvent.setFlag(isEffectFlag); + if (!game.replaceEvent(addingOneEvent)) { getCounters(game).addCounter(eventCounter); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1)); + GameEvent addedOneEvent = GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1); + addedOneEvent.setFlag(addingOneEvent.getFlag()); + game.fireEvent(addedOneEvent); } else { finalAmount--; returnCode = false; } } if (finalAmount > 0) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), amount)); + GameEvent addedAllEvent = GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), amount); + addedAllEvent.setFlag(isEffectFlag); + game.fireEvent(addedAllEvent); } } else { returnCode = false; diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 2d20ce98a0e..6d1705abe18 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -18,6 +18,9 @@ public class GameEvent implements Serializable { protected UUID sourceId; protected UUID playerId; protected int amount; + // flags: + // for counters: event is result of effect (+1 from planeswalkers is cost, not effect) + // for combat damage: event is preventable damage protected boolean flag; protected String data; protected Zone zone; @@ -433,6 +436,17 @@ public class GameEvent implements Serializable { this.amount = amount; } + public void setAmountForCounters(int amount, boolean isEffect) { + this.amount = amount; + + // cost event must be "transformed" to effect event, as example: + // planeswalker's +1 cost will be affected by Pir, Imaginative Rascal (1 + 1) and applied as effect by Doubling Season (2 * 2) + // https://github.com/magefree/mage/issues/5802 + if (isEffect) { + setFlag(true); + } + } + public boolean getFlag() { return flag; } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index bed49eb5737..9df46c83eae 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1,10 +1,6 @@ package mage.players; import com.google.common.collect.ImmutableMap; -import java.io.Serializable; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.Map.Entry; import mage.ConditionalMana; import mage.MageObject; import mage.MageObjectReference; @@ -70,6 +66,11 @@ import mage.util.GameLog; import mage.util.RandomUtil; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; + public abstract class PlayerImpl implements Player, Serializable { private static final Logger logger = Logger.getLogger(PlayerImpl.class); @@ -1416,10 +1417,10 @@ public abstract class PlayerImpl implements Player, Serializable { != null // if anyone sees an issue with this code, please report it. Worked in my testing. || game.getContinuousEffects().asThough(object.getId(), - AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, - ability, - this.getId(), - game) + AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, + ability, + this.getId(), + game) != null) { if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) { @@ -2009,24 +2010,30 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean addCounters(Counter counter, Game game) { boolean returnCode = true; - GameEvent countersEvent = GameEvent.getEvent(EventType.ADD_COUNTERS, playerId, null, playerId, counter.getName(), counter.getCount()); - if (!game.replaceEvent(countersEvent)) { - int amount = countersEvent.getAmount(); + GameEvent addingAllEvent = GameEvent.getEvent(EventType.ADD_COUNTERS, playerId, null, playerId, counter.getName(), counter.getCount()); + if (!game.replaceEvent(addingAllEvent)) { + int amount = addingAllEvent.getAmount(); int finalAmount = amount; + boolean isEffectFlag = addingAllEvent.getFlag(); for (int i = 0; i < amount; i++) { Counter eventCounter = counter.copy(); eventCounter.remove(eventCounter.getCount() - 1); - GameEvent event = GameEvent.getEvent(EventType.ADD_COUNTER, playerId, null, playerId, counter.getName(), 1); - if (!game.replaceEvent(event)) { + GameEvent addingOneEvent = GameEvent.getEvent(EventType.ADD_COUNTER, playerId, null, playerId, counter.getName(), 1); + addingOneEvent.setFlag(isEffectFlag); + if (!game.replaceEvent(addingOneEvent)) { getCounters().addCounter(eventCounter); - game.fireEvent(GameEvent.getEvent(EventType.COUNTER_ADDED, playerId, null, playerId, counter.getName(), 1)); + GameEvent addedOneEvent = GameEvent.getEvent(EventType.COUNTER_ADDED, playerId, null, playerId, counter.getName(), 1); + addedOneEvent.setFlag(addingOneEvent.getFlag()); + game.fireEvent(addedOneEvent); } else { finalAmount--; returnCode = false; } } if (finalAmount > 0) { - game.fireEvent(GameEvent.getEvent(EventType.COUNTERS_ADDED, playerId, null, playerId, counter.getName(), amount)); + GameEvent addedAllEvent = GameEvent.getEvent(EventType.COUNTERS_ADDED, playerId, null, playerId, counter.getName(), amount); + addedAllEvent.setFlag(addingAllEvent.getFlag()); + game.fireEvent(addedAllEvent); } } else { returnCode = false; @@ -2637,7 +2644,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param game * @param appliedEffects - * @param numSides Number of sides the dice has + * @param numSides Number of sides the dice has * @return the number that the player rolled */ @Override @@ -2671,10 +2678,10 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param game * @param appliedEffects - * @param numberChaosSides The number of chaos sides the planar die - * currently has (normally 1 but can be 5) + * @param numberChaosSides The number of chaos sides the planar die + * currently has (normally 1 but can be 5) * @param numberPlanarSides The number of chaos sides the planar die - * currently has (normally 1) + * currently has (normally 1) * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll * or NilRoll */ @@ -2831,7 +2838,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param ability - * @param available if null, it won't be checked if enough mana is available + * @param available if null, it won't be checked if enough mana is available * @param sourceObject * @param game * @return @@ -3381,7 +3388,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId, - UUID controllerId, Game game + UUID controllerId, Game game ) { return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game); } @@ -3532,8 +3539,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Card card, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { Set cardList = new HashSet<>(); if (card != null) { @@ -3544,22 +3551,22 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Cards cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return moveCards(cards.getCards(game), toZone, source, game); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return moveCards(cards, toZone, source, game, false, false, false, null); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { if (cards.isEmpty()) { return true; @@ -3645,8 +3652,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardsToExile(Card card, Ability source, - Game game, boolean withName, UUID exileId, - String exileZoneName + Game game, boolean withName, UUID exileId, + String exileZoneName ) { Set cards = new HashSet<>(); cards.add(card); @@ -3655,8 +3662,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardsToExile(Set cards, Ability source, - Game game, boolean withName, UUID exileId, - String exileZoneName + Game game, boolean withName, UUID exileId, + String exileZoneName ) { if (cards.isEmpty()) { return true; @@ -3671,14 +3678,14 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToHandWithInfo(Card card, UUID sourceId, - Game game + Game game ) { return this.moveCardToHandWithInfo(card, sourceId, game, true); } @Override public boolean moveCardToHandWithInfo(Card card, UUID sourceId, - Game game, boolean withName + Game game, boolean withName ) { boolean result = false; Zone fromZone = game.getState().getZone(card.getId()); @@ -3703,7 +3710,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source, - Game game, Zone fromZone + Game game, Zone fromZone ) { UUID sourceId = source == null ? null : source.getSourceId(); Set movedCards = new LinkedHashSet<>(); @@ -3711,7 +3718,7 @@ public abstract class PlayerImpl implements Player, Serializable { // identify cards from one owner Cards cards = new CardsImpl(); UUID ownerId = null; - for (Iterator it = allCards.iterator(); it.hasNext();) { + for (Iterator it = allCards.iterator(); it.hasNext(); ) { Card card = it.next(); if (cards.isEmpty()) { ownerId = card.getOwnerId(); @@ -3772,7 +3779,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, - Game game, Zone fromZone + Game game, Zone fromZone ) { if (card == null) { return false; @@ -3801,8 +3808,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, - Game game, Zone fromZone, - boolean toTop, boolean withName + Game game, Zone fromZone, + boolean toTop, boolean withName ) { if (card == null) { return false; @@ -3836,7 +3843,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId, - Game game, Zone fromZone, boolean withName) { + Game game, Zone fromZone, boolean withName) { if (card == null) { return false; } From 4d95d72c6627f1fb1bb7763244da6088cf1d8fe5 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 17 May 2019 21:42:35 +0400 Subject: [PATCH 73/76] Test framework: added support of commander cards (just add card to command zone by addCard); --- .../cards/continuous/CommandersCastTest.java | 66 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 11 ++++ .../base/CardTestCommander4Players.java | 28 ++++++++ .../serverside/base/MageTestPlayerBase.java | 31 ++++++--- .../base/impl/CardTestPlayerAPIImpl.java | 9 ++- Mage/src/main/java/mage/game/Game.java | 2 +- Mage/src/main/java/mage/game/GameImpl.java | 15 ++++- 7 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4Players.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java new file mode 100644 index 00000000000..02d57293aa6 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java @@ -0,0 +1,66 @@ +package org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * @author JayDi85 + */ +public class CommandersCastTest extends CardTestCommander4Players { + + // Player order: A -> D -> C -> B + + @Test + public void test_CastToBattlefieldOneTime() { + addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + showCommand("commanders", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCommandZoneCount(playerA, "Balduvian Bears", 0); + assertPermanentCount(playerA, "Balduvian Bears", 1); + } + + @Test + public void test_CastToBattlefieldTwoTimes() { + // Player order: A -> D -> C -> B + addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); // 2 + 4 + // + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + // cast 1 + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after cast 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + // destroy commander + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Balduvian Bears"); + setChoice(playerA, "Yes"); // put to command zone again + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after destroy", 1, PhaseStep.PRECOMBAT_MAIN, playerB, playerA, "Balduvian Bears", 0); + + // cast 2 + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + checkPermanentCount("after cast 2", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertCommandZoneCount(playerA, "Balduvian Bears", 0); + assertPermanentCount(playerA, "Balduvian Bears", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + } +} 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 ebd8da1cd0f..e0e3b0006b3 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 @@ -15,6 +15,7 @@ import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.cards.decks.Deck; import mage.choices.Choice; import mage.constants.*; @@ -746,6 +747,16 @@ public class TestPlayer implements Player { wasProccessed = true; } + // show command + if (params[0].equals(SHOW_COMMAND_COMMAND) && params.length == 1) { + printStart(action.getActionName()); + CardsImpl cards = new CardsImpl(computerPlayer.getCommandersIds()); + printCards(cards.getCards(game)); + printEnd(); + actions.remove(action); + wasProccessed = true; + } + // show battlefield if (params[0].equals(SHOW_COMMAND_BATTLEFIELD) && params.length == 1) { printStart(action.getActionName()); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4Players.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4Players.java new file mode 100644 index 00000000000..a122945927a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommander4Players.java @@ -0,0 +1,28 @@ +package org.mage.test.serverside.base; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.CommanderFreeForAll; +import mage.game.Game; +import mage.game.GameException; +import mage.game.mulligan.VancouverMulligan; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +import java.io.FileNotFoundException; + +/** + * @author JayDi85 + */ +public abstract class CardTestCommander4Players extends CardTestPlayerAPIImpl { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new CommanderFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20); + // Player order: A -> D -> C -> B + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + playerD = createPlayer(game, playerD, "PlayerD"); + return game; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 5d64d3d2460..45eda5e7fe5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -52,6 +52,7 @@ public abstract class MageTestPlayerBase { protected Map> battlefieldCards = new HashMap<>(); protected Map> graveyardCards = new HashMap<>(); protected Map> libraryCards = new HashMap<>(); + protected Map> commandCards = new HashMap<>(); protected Map> commands = new HashMap<>(); @@ -211,6 +212,9 @@ public abstract class MageTestPlayerBase { } else if ("library".equalsIgnoreCase(zone)) { gameZone = Zone.LIBRARY; cards = getLibraryCards(getPlayer(nickname)); + } else if ("command".equalsIgnoreCase(zone)) { + gameZone = Zone.COMMAND; + cards = getCommandCards(getPlayer(nickname)); } else if ("player".equalsIgnoreCase(zone)) { String command = m.group(3); if ("life".equals(command)) { @@ -280,27 +284,36 @@ public abstract class MageTestPlayerBase { if (graveyardCards.containsKey(player)) { return graveyardCards.get(player); } - List grave = new ArrayList<>(); - graveyardCards.put(player, grave); - return grave; + List res = new ArrayList<>(); + graveyardCards.put(player, res); + return res; } protected List getLibraryCards(TestPlayer player) { if (libraryCards.containsKey(player)) { return libraryCards.get(player); } - List library = new ArrayList<>(); - libraryCards.put(player, library); - return library; + List res = new ArrayList<>(); + libraryCards.put(player, res); + return res; + } + + protected List getCommandCards(TestPlayer player) { + if (commandCards.containsKey(player)) { + return commandCards.get(player); + } + List res = new ArrayList<>(); + commandCards.put(player, res); + return res; } protected List getBattlefieldCards(TestPlayer player) { if (battlefieldCards.containsKey(player)) { return battlefieldCards.get(player); } - List battlefield = new ArrayList<>(); - battlefieldCards.put(player, battlefield); - return battlefield; + List res = new ArrayList<>(); + battlefieldCards.put(player, res); + return res; } protected Map getCommands(TestPlayer player) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 55863694081..8b66bcc20ad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -70,6 +70,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement // TODO: add target player param to commands public static final String SHOW_COMMAND_LIBRARY = "LIBRARY"; public static final String SHOW_COMMAND_HAND = "HAND"; + public static final String SHOW_COMMAND_COMMAND = "COMMAND"; public static final String SHOW_COMMAND_BATTLEFIELD = "BATTLEFIELD"; public static final String SHOW_COMMAND_GRAVEYEARD = "GRAVEYARD"; public static final String SHOW_COMMAND_EXILE = "EXILE"; @@ -240,7 +241,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement TestPlayer testPlayer = (TestPlayer) player; currentGame.cheat(player.getId(), getCommands(testPlayer)); currentGame.cheat(player.getId(), activePlayer.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), - getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); + getBattlefieldCards(testPlayer), getGraveCards(testPlayer), getCommandCards(testPlayer)); } long t1 = System.nanoTime(); @@ -389,6 +390,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement show(showName, turnNum, step, player, SHOW_COMMAND_HAND); } + public void showCommand(String showName, int turnNum, PhaseStep step, TestPlayer player) { + show(showName, turnNum, step, player, SHOW_COMMAND_COMMAND); + } + public void showBattlefield(String showName, int turnNum, PhaseStep step, TestPlayer player) { show(showName, turnNum, step, player, SHOW_COMMAND_BATTLEFIELD); } @@ -536,6 +541,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement return getGraveCards(player); case LIBRARY: return getLibraryCards(player); + case COMMAND: + return getCommandCards(player); default: break; } diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 7ee0bfdcc03..30638c63a1c 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -432,7 +432,7 @@ public interface Game extends MageItem, Serializable { // game cheats (for tests only) void cheat(UUID ownerId, Map commands); - void cheat(UUID ownerId, UUID activePlayerId, List library, List hand, List battlefield, List graveyard); + void cheat(UUID ownerId, UUID activePlayerId, List library, List hand, List battlefield, List graveyard, List command); // controlling the behaviour of replacement effects while permanents entering the battlefield void setScopeRelevant(boolean scopeRelevant); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 3594cf3062f..59c93e2ab12 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -2845,26 +2845,39 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public void cheat(UUID ownerId, UUID activePlayerId, List library, List hand, List battlefield, List graveyard) { + public void cheat(UUID ownerId, UUID activePlayerId, List library, List hand, List battlefield, List graveyard, List command) { Player player = getPlayer(ownerId); if (player != null) { loadCards(ownerId, library); loadCards(ownerId, hand); loadCards(ownerId, battlefield); loadCards(ownerId, graveyard); + loadCards(ownerId, command); for (Card card : library) { player.getLibrary().putOnTop(card, this); } + for (Card card : hand) { card.setZone(Zone.HAND, this); player.getHand().add(card); } + for (Card card : graveyard) { card.setZone(Zone.GRAVEYARD, this); player.getGraveyard().add(card); } + // as commander (only commander games, look at init code in GameCommanderImpl) + if (this instanceof GameCommanderImpl) { + for (Card card : command) { + player.addCommanderId(card.getId()); + // no needs in initCommander call -- it's uses on game startup (init) + } + } else { + throw new IllegalArgumentException("Command zone supports in commander test games"); + } + // warning, permanents go to battlefield without resolve, continuus effects must be init for (PermanentCard permanentCard : battlefield) { permanentCard.setZone(Zone.BATTLEFIELD, this); From ec9198fb2245f5252d698ce624dfca8eb797dd25 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 17 May 2019 21:52:50 +0400 Subject: [PATCH 74/76] Merge fix --- Mage/src/main/java/mage/game/GameImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 59c93e2ab12..a2d57f858c5 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -2874,7 +2874,7 @@ public abstract class GameImpl implements Game, Serializable { player.addCommanderId(card.getId()); // no needs in initCommander call -- it's uses on game startup (init) } - } else { + } else if (!command.isEmpty()) { throw new IllegalArgumentException("Command zone supports in commander test games"); } From a0805327d1aa319b840693b672da7c0a0ceca9ca Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 17 May 2019 22:54:23 +0400 Subject: [PATCH 75/76] * Armory Automaton - fixed infinite AI choose (#5023); --- Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java b/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java index f4fe5b25bb4..f56d6acc10b 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java +++ b/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java @@ -88,7 +88,9 @@ class ArmoryAutomatonEffect extends OneShotEffect { while (player.canRespond() && countBattlefield > 0 && player.chooseUse(Outcome.Benefit, "Select and attach a target Equipment?", source, game)) { Target targetEquipment = new TargetPermanent(currentFilter); targetEquipment.setRequired(false); - if (player.choose(Outcome.Benefit, targetEquipment, source.getSourceId(), game)) { + if (player.choose(Outcome.Benefit, targetEquipment, source.getSourceId(), game) && targetEquipment.getFirstTarget() != null) { + currentFilter.add(Predicates.not(new PermanentIdPredicate(targetEquipment.getFirstTarget()))); // exclude selected for next time + Permanent aura = game.getPermanent(targetEquipment.getFirstTarget()); if (aura != null) { Permanent attachedTo = game.getPermanent(aura.getAttachedTo()); @@ -96,10 +98,9 @@ class ArmoryAutomatonEffect extends OneShotEffect { attachedTo.removeAttachment(aura.getId(), game); } sourcePermanent.addAttachment(aura.getId(), game); - - // exclude selected - currentFilter.add(Predicates.not(new PermanentIdPredicate(aura.getId()))); } + } else { + break; } countBattlefield = game.getBattlefield().getAllActivePermanents(currentFilter, game).size(); } From e80ba6383f8f8f3997fe75dcc8ac890aad88a939 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 18 May 2019 10:58:30 +0400 Subject: [PATCH 76/76] Added verify tests for watchers copy; --- Mage.Sets/src/mage/cards/w/WookieeMystic.java | 40 ++++++++----------- .../java/mage/verify/VerifyCardDataTest.java | 37 ++++++++++++++++- Mage/src/main/java/mage/watchers/Watcher.java | 14 +++---- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/Mage.Sets/src/mage/cards/w/WookieeMystic.java b/Mage.Sets/src/mage/cards/w/WookieeMystic.java index 1d8f1d879d9..655ffc900a7 100644 --- a/Mage.Sets/src/mage/cards/w/WookieeMystic.java +++ b/Mage.Sets/src/mage/cards/w/WookieeMystic.java @@ -1,16 +1,12 @@ - package mage.cards.w; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.effects.common.ManaEffect; +import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,14 +22,17 @@ import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * @author Styxo */ public final class WookieeMystic extends CardImpl { public WookieeMystic(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}{G}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{G}{W}"); this.subtype.add(SubType.WOOKIEE); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); @@ -45,21 +44,21 @@ public final class WookieeMystic extends CardImpl { ManaEffect effect = new BasicManaEffect(mana); effect.setText("Add {R}. If that mana is spent on a creature spell, it enters the battlefield with a +1/+1 counter on it"); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - this.addAbility(ability, new WookieeMysticWatcher(ability)); + this.addAbility(ability, new WookieeMysticWatcher().withParams(ability)); mana = Mana.GreenMana(1); mana.setFlag(true); effect = new BasicManaEffect(mana); effect.setText("Add {G}. If that mana is spent on a creature spell, it enters the battlefield with a +1/+1 counter on it"); ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - this.addAbility(ability, new WookieeMysticWatcher(ability)); + this.addAbility(ability, new WookieeMysticWatcher().withParams(ability)); mana = Mana.WhiteMana(1); mana.setFlag(true); effect = new BasicManaEffect(mana); effect.setText("Add {W}. If that mana is spent on a creature spell, it enters the battlefield with a +1/+1 counter on it"); ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - this.addAbility(ability, new WookieeMysticWatcher(ability)); + this.addAbility(ability, new WookieeMysticWatcher().withParams(ability)); } public WookieeMystic(final WookieeMystic card) { @@ -74,23 +73,16 @@ public final class WookieeMystic extends CardImpl { class WookieeMysticWatcher extends Watcher { - private final Ability source; - private final List creatures = new ArrayList<>(); + private Ability source; + private List creatures = new ArrayList<>(); - WookieeMysticWatcher(Ability source) { + WookieeMysticWatcher() { super(WatcherScope.CARD); + } + + Watcher withParams(Ability source) { this.source = source; - } - - WookieeMysticWatcher(final WookieeMysticWatcher watcher) { - super(watcher); - this.creatures.addAll(watcher.creatures); - this.source = watcher.source; - } - - @Override - public WookieeMysticWatcher copy() { - return new WookieeMysticWatcher(this); + return this; } @Override diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 4ea8fb15cb9..b3fcd37e40d 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -414,7 +414,7 @@ public class VerifyCardDataTest { } @Test - @Ignore // TODO: enable it on copy() methods removing + //@Ignore // TODO: enable it on copy() methods removing public void checkWatcherCopyMethods() { Collection errorsList = new ArrayList<>(); @@ -424,6 +424,13 @@ public class VerifyCardDataTest { Set> watcherClassesList = reflections.getSubTypesOf(Watcher.class); for (Class watcherClass : watcherClassesList) { + + // only watcher class can be extended (e.g. final) + if (!watcherClass.getSuperclass().equals(Watcher.class)) { + errorsList.add("error, only Watcher class can be extended: " + watcherClass.getName()); + } + + // no copy methods try { Method m = watcherClass.getMethod("copy"); if (!m.getGenericReturnType().getTypeName().equals("T")) { @@ -432,6 +439,34 @@ public class VerifyCardDataTest { } catch (NoSuchMethodException e) { errorsList.add("error, can't find copy() method in watcher class: " + watcherClass.getName()); } + + // no constructor for copy + try { + Constructor constructor = watcherClass.getDeclaredConstructor(watcherClass); + errorsList.add("error, copy constructor is not allowed in watcher class: " + watcherClass.getName()); + } catch (NoSuchMethodException e) { + // all fine, no needs in copy constructors + } + + // errors on create + try { + Constructor constructor = watcherClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Watcher w1 = constructor.newInstance(); + + // errors on copy + try { + Watcher w2 = w1.copy(); + if (w2 == null) { + errorsList.add("error, can't copy watcher with unknown error, look at error logs above: " + watcherClass.getName()); + } + } catch (Exception e) { + errorsList.add("error, can't copy watcher: " + watcherClass.getName() + " (" + e.getMessage() + ")"); + } + } catch (Exception e) { + errorsList.add("error, can't create watcher: " + watcherClass.getName() + " (" + e.getMessage() + ")"); + } + } printMessages(warningsList); diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java index 05efdc76f39..1beb4e73930 100644 --- a/Mage/src/main/java/mage/watchers/Watcher.java +++ b/Mage/src/main/java/mage/watchers/Watcher.java @@ -1,10 +1,7 @@ - package mage.watchers; -import mage.Mana; import mage.constants.WatcherScope; import mage.game.Game; -import mage.game.turn.Step; import mage.game.events.GameEvent; import org.apache.log4j.Logger; @@ -26,8 +23,7 @@ public abstract class Watcher implements Serializable { protected UUID controllerId; protected UUID sourceId; protected boolean condition; - protected final WatcherScope scope; - + protected WatcherScope scope; public Watcher(WatcherScope scope) { this.scope = scope; @@ -81,11 +77,15 @@ public abstract class Watcher implements Serializable { return getClass().getSimpleName(); } + public void setScope(WatcherScope scope) { + this.scope = scope; + } + public abstract void watch(GameEvent event, Game game); public T copy() { try { - List constructors = Arrays.asList(this.getClass().getConstructors()); + List constructors = Arrays.asList(this.getClass().getDeclaredConstructors()); // needs private constructor if (constructors.size() > 1) { logger.error(getClass().getSimpleName() + " has multiple constructors"); return null; @@ -103,7 +103,7 @@ public abstract class Watcher implements Serializable { allFields.addAll(Arrays.asList(getClass().getSuperclass().getDeclaredFields())); for (Field field : allFields) { field.setAccessible(true); - if (field.getType() == Set.class) { + if (field.getType() == Set.class) { ((Set) field.get(watcher)).clear(); ((Set) field.get(watcher)).addAll((Set) field.get(this)); } else if (field.getType() == Map.class) {