From ff690700b76a6ee342a71c27753a22c54ea9371a Mon Sep 17 00:00:00 2001 From: L_J Date: Thu, 24 Jan 2019 22:56:59 +0100 Subject: [PATCH 01/21] Implemented Blazing Effigy, Infinite Authority, Voodoo Doll --- Mage.Sets/src/mage/cards/b/BlazingEffigy.java | 124 +++++++++++++++ .../src/mage/cards/i/InfiniteAuthority.java | 148 ++++++++++++++++++ Mage.Sets/src/mage/cards/v/VoodooDoll.java | 80 ++++++++++ Mage.Sets/src/mage/sets/Legends.java | 3 + 4 files changed, 355 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BlazingEffigy.java create mode 100644 Mage.Sets/src/mage/cards/i/InfiniteAuthority.java create mode 100644 Mage.Sets/src/mage/cards/v/VoodooDoll.java diff --git a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java new file mode 100644 index 0000000000..bc7d5800af --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java @@ -0,0 +1,124 @@ + +package mage.cards.b; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +/** + * + * @author TheElk801 & L_J + */ +public final class BlazingEffigy extends CardImpl { + + public BlazingEffigy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // When Blazing Effigy dies, it deals X damage to target creature, where X is 3 plus the amount of damage dealt to Blazing Effigy this turn by other sources named Blazing Effigy. + Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(BlazingEffigyCount.instance), false); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability, new BlazingEffigyWatcher()); + } + + public BlazingEffigy(final BlazingEffigy card) { + super(card); + } + + @Override + public BlazingEffigy copy() { + return new BlazingEffigy(this); + } +} + +enum BlazingEffigyCount implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + BlazingEffigyWatcher watcher = (BlazingEffigyWatcher) game.getState().getWatchers().get(BlazingEffigyWatcher.class.getSimpleName()); + if (watcher == null) { + return 3; + } + int effigyDamage = watcher.damageDoneTo(sourceAbility.getSourceId(), sourceAbility.getSourceObjectZoneChangeCounter() - 1, game); + return CardUtil.addWithOverflowCheck(3, effigyDamage); + } + + @Override + public BlazingEffigyCount copy() { + return BlazingEffigyCount.instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "3 plus the amount of damage dealt to {this} this turn by other sources named Blazing Effigy"; + } +} + +class BlazingEffigyWatcher extends Watcher { + + public final Map damagedObjects = new HashMap<>(); + + public BlazingEffigyWatcher() { + super(BlazingEffigyWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public BlazingEffigyWatcher(final BlazingEffigyWatcher watcher) { + super(watcher); + this.damagedObjects.putAll(watcher.damagedObjects); + } + + @Override + public BlazingEffigyWatcher copy() { + return new BlazingEffigyWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { + if (!event.getSourceId().equals(event.getTargetId())) { + MageObjectReference damageSourceRef = new MageObjectReference(event.getSourceId(), game); + MageObjectReference damageTargetRef = new MageObjectReference(event.getTargetId(), game); + if (game.getPermanentOrLKIBattlefield(event.getSourceId()) != null && game.getPermanentOrLKIBattlefield(event.getSourceId()).getName().equals("Blazing Effigy")) { + damagedObjects.putIfAbsent(damageTargetRef, 0); + damagedObjects.compute(damageTargetRef, (k, damage) -> damage + event.getAmount()); + } + } + } + } + + @Override + public void reset() { + super.reset(); + damagedObjects.clear(); + } + + public int damageDoneTo(UUID objectId, int zoneChangeCounter, Game game) { + MageObjectReference mor = new MageObjectReference(objectId, zoneChangeCounter, game); + return damagedObjects.getOrDefault(mor, 0); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java b/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java new file mode 100644 index 0000000000..b5155d9a87 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java @@ -0,0 +1,148 @@ + +package mage.cards.i; + +import java.util.Objects; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth & L_J + */ +public final class InfiniteAuthority extends CardImpl { + + public InfiniteAuthority(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{W}{W}"); + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever enchanted creature blocks or becomes blocked by a creature with toughness 3 or less, destroy the other creature at end of combat. At the beginning of the next end step, if that creature was destroyed this way, put a +1/+1 counter on the first creature. + this.addAbility(new InfiniteAuthorityTriggeredAbility()); + } + + public InfiniteAuthority(final InfiniteAuthority card) { + super(card); + } + + @Override + public InfiniteAuthority copy() { + return new InfiniteAuthority(this); + } +} + +class InfiniteAuthorityTriggeredAbility extends TriggeredAbilityImpl { + + InfiniteAuthorityTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new InfiniteAuthorityEffect()))); + } + + InfiniteAuthorityTriggeredAbility(final InfiniteAuthorityTriggeredAbility ability) { + super(ability); + } + + @Override + public InfiniteAuthorityTriggeredAbility copy() { + return new InfiniteAuthorityTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent aura = game.getPermanentOrLKIBattlefield(sourceId); + if (aura != null) { + Permanent enchantedCreature = game.getPermanentOrLKIBattlefield(aura.getAttachedTo()); + if (enchantedCreature != null) { + Permanent blocker = game.getPermanent(event.getSourceId()); + Permanent blocked = game.getPermanent(event.getTargetId()); + Effect effect = this.getEffects().get(0); + if (blocker != null + && Objects.equals(blocked, enchantedCreature) + && blocker.getToughness().getValue() <= 3) { + effect.setTargetPointer(new FixedTarget(blocker.getId())); + return true; + } + if (blocked != null + && Objects.equals(blocker, enchantedCreature) + && blocked.getToughness().getValue() <= 3) { + effect.setTargetPointer(new FixedTarget(blocked.getId())); + return true; + } + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever enchanted creature blocks or becomes blocked by a creature with toughness 3 or less, " + super.getRule(); + } +} + +class InfiniteAuthorityEffect extends OneShotEffect { + + InfiniteAuthorityEffect() { + super(Outcome.Detriment); + staticText = "destroy the other creature at end of combat. At the beginning of the next end step, if that creature was destroyed this way, put a +1/+1 counter on the first creature"; + } + + InfiniteAuthorityEffect(final InfiniteAuthorityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent aura = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (aura != null) { + Permanent enchantedCreature = game.getPermanentOrLKIBattlefield(aura.getAttachedTo()); + if (enchantedCreature != null) { + Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + if (permanent != null) { + if (permanent.destroy(source.getSourceId(), game, false)) { + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(enchantedCreature, game)); + game.addDelayedTriggeredAbility(delayedAbility, source); + } + return true; + } + } + } + return false; + } + + @Override + public InfiniteAuthorityEffect copy() { + return new InfiniteAuthorityEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoodooDoll.java b/Mage.Sets/src/mage/cards/v/VoodooDoll.java new file mode 100644 index 0000000000..b1828f6a0b --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoodooDoll.java @@ -0,0 +1,80 @@ + +package mage.cards.v; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.common.SourceTappedCondition; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.DamageControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetAnyTarget; + +/** + * + * @author L_J + */ +public final class VoodooDoll extends CardImpl { + + public VoodooDoll(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}"); + + // At the beginning of your upkeep, put a pin counter on Voodoo Doll. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.PIN.createInstance()), TargetController.YOU, false)); + // At the beginning of your end step, if Voodoo Doll is untapped, destroy Voodoo Doll and it deals damage to you equal to the number of pin counters on it. + Ability ability = new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new DestroySourceEffect(), TargetController.YOU, + new InvertCondition(SourceTappedCondition.instance), false); + ability.addEffect(new DamageControllerEffect(new CountersSourceCount(CounterType.PIN))); + this.addAbility(ability); + // {X}{X}, {T}: Voodoo Doll deals damage equal to the number of pin counters on it to any target. X is the number of pin counters on Voodoo Doll. + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new CountersSourceCount(CounterType.PIN)), new ManaCostsImpl("{X}{X}")); + ability2.addCost(new TapSourceCost()); + ability2.addTarget(new TargetAnyTarget()); + for (VariableCost cost : ability2.getManaCosts().getVariableCosts()) { + if (cost instanceof VariableManaCost) { + ((VariableManaCost) cost).setMaxX(0); + break; + } + } + this.addAbility(ability2); + } + + @Override + public void adjustCosts(Ability ability, Game game) { + if (ability instanceof SimpleActivatedAbility) { + Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); + if (sourcePermanent != null) { + int pin = sourcePermanent.getCounters(game).getCount(CounterType.PIN); + ability.getManaCostsToPay().clear(); + ability.getManaCostsToPay().add(0, new GenericManaCost(pin * 2)); + } + } + } + + public VoodooDoll(final VoodooDoll card) { + super(card); + } + + @Override + public VoodooDoll copy() { + return new VoodooDoll(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index 44a5d2d450..87857da7c8 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -54,6 +54,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Bartel Runeaxe", 222, Rarity.RARE, mage.cards.b.BartelRuneaxe.class)); cards.add(new SetCardInfo("Beasts of Bogardan", 133, Rarity.UNCOMMON, mage.cards.b.BeastsOfBogardan.class)); cards.add(new SetCardInfo("Black Mana Battery", 274, Rarity.UNCOMMON, mage.cards.b.BlackManaBattery.class)); + cards.add(new SetCardInfo("Blazing Effigy", 134, Rarity.COMMON, mage.cards.b.BlazingEffigy.class)); cards.add(new SetCardInfo("Blight", 89, Rarity.UNCOMMON, mage.cards.b.Blight.class)); cards.add(new SetCardInfo("Blood Lust", 135, Rarity.UNCOMMON, mage.cards.b.BloodLust.class)); cards.add(new SetCardInfo("Blue Mana Battery", 275, Rarity.UNCOMMON, mage.cards.b.BlueManaBattery.class)); @@ -152,6 +153,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("In the Eye of Chaos", 61, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class)); cards.add(new SetCardInfo("Indestructible Aura", 21, Rarity.COMMON, mage.cards.i.IndestructibleAura.class)); cards.add(new SetCardInfo("Infernal Medusa", 108, Rarity.UNCOMMON, mage.cards.i.InfernalMedusa.class)); + cards.add(new SetCardInfo("Infinite Authority", 22, Rarity.RARE, mage.cards.i.InfiniteAuthority.class)); cards.add(new SetCardInfo("Invoke Prejudice", 62, Rarity.RARE, mage.cards.i.InvokePrejudice.class)); cards.add(new SetCardInfo("Ivory Guardians", 23, Rarity.UNCOMMON, mage.cards.i.IvoryGuardians.class)); cards.add(new SetCardInfo("Jacques le Vert", 232, Rarity.RARE, mage.cards.j.JacquesLeVert.class)); @@ -299,6 +301,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Vaevictis Asmadi", 269, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); cards.add(new SetCardInfo("Vampire Bats", 125, Rarity.COMMON, mage.cards.v.VampireBats.class)); cards.add(new SetCardInfo("Visions", 41, Rarity.UNCOMMON, mage.cards.v.Visions.class)); + cards.add(new SetCardInfo("Voodoo Doll", 298, Rarity.RARE, mage.cards.v.VoodooDoll.class)); cards.add(new SetCardInfo("Walking Dead", 126, Rarity.COMMON, mage.cards.w.WalkingDead.class)); cards.add(new SetCardInfo("Wall of Caltrops", 42, Rarity.COMMON, mage.cards.w.WallOfCaltrops.class)); cards.add(new SetCardInfo("Wall of Dust", 168, Rarity.UNCOMMON, mage.cards.w.WallOfDust.class)); From 481b5f04ea281729e082302bdf3f7309176586dc Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 25 Jan 2019 10:41:50 +0400 Subject: [PATCH 02/21] Fixed NPE error/freeze on broken server connection --- .../main/java/mage/client/table/TablesPanel.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 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 88e70cd985..be6b96ce6e 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -626,16 +626,21 @@ public class TablesPanel extends javax.swing.JPanel { // reload server messages java.util.List serverMessages = SessionHandler.getServerMessages(); synchronized (this) { - this.messages = serverMessages; + if (serverMessages != null) { + this.messages = serverMessages; + } else { + this.messages = new ArrayList<>(); + } + this.currentMessage = 0; } - if (serverMessages.isEmpty()) { + if (this.messages.isEmpty()) { this.jPanelBottom.setVisible(false); } else { this.jPanelBottom.setVisible(true); URLHandler.RemoveMouseAdapter(jLabelFooterText); - URLHandler.handleMessage(serverMessages.get(0), this.jLabelFooterText); - this.jButtonFooterNext.setVisible(serverMessages.size() > 1); + URLHandler.handleMessage(this.messages.get(0), this.jLabelFooterText); + this.jButtonFooterNext.setVisible(this.messages.size() > 1); } } From 34e3bb42db9063dee6fd69874080e940bd60ee4f Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 25 Jan 2019 10:32:29 -0500 Subject: [PATCH 03/21] fixed Rhythm of the Wild not giving Riot to creatures that weren't cast --- .../src/mage/cards/m/MasterBiomancer.java | 21 +++--- .../src/mage/cards/r/RhythmOfTheWild.java | 71 +++++++++++++++++-- .../cards/abilities/keywords/RiotTest.java | 55 +++++++++++++- .../mage/abilities/keyword/RiotAbility.java | 6 +- 4 files changed, 134 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MasterBiomancer.java b/Mage.Sets/src/mage/cards/m/MasterBiomancer.java index 79f1c60538..b810e3965c 100644 --- a/Mage.Sets/src/mage/cards/m/MasterBiomancer.java +++ b/Mage.Sets/src/mage/cards/m/MasterBiomancer.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -10,7 +9,10 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; @@ -19,14 +21,15 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MasterBiomancer extends CardImpl { public MasterBiomancer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.WIZARD); @@ -34,10 +37,10 @@ public final class MasterBiomancer extends CardImpl { this.toughness = new MageInt(4); // Each other creature you control enters the battlefield with a number of additional +1/+1 counters on it equal to Master Biomancer's power and as a Mutant in addition to its other types. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MasterBiomancerEntersBattlefieldEffect())); + this.addAbility(new SimpleStaticAbility(new MasterBiomancerEntersBattlefieldEffect())); } - public MasterBiomancer(final MasterBiomancer card) { + private MasterBiomancer(final MasterBiomancer card) { super(card); } @@ -49,12 +52,12 @@ public final class MasterBiomancer extends CardImpl { class MasterBiomancerEntersBattlefieldEffect extends ReplacementEffectImpl { - public MasterBiomancerEntersBattlefieldEffect() { + MasterBiomancerEntersBattlefieldEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature); - staticText = "Each other creature you control enters the battlefield with a number of additional +1/+1 counters on it equal to Master Biomancer's power and as a Mutant in addition to its other types"; + staticText = "Each other creature you control enters the battlefield with a number of additional +1/+1 counters on it equal to {this}'s power and as a Mutant in addition to its other types"; } - public MasterBiomancerEntersBattlefieldEffect(MasterBiomancerEntersBattlefieldEffect effect) { + private MasterBiomancerEntersBattlefieldEffect(MasterBiomancerEntersBattlefieldEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java b/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java index be889e972f..f6899c2e47 100644 --- a/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java +++ b/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java @@ -1,17 +1,21 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CantBeCounteredControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.RiotAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.TargetController; +import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; @@ -19,6 +23,14 @@ import mage.filter.common.FilterCreatureSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @author TheElk801 @@ -44,8 +56,7 @@ public final class RhythmOfTheWild extends CardImpl { ))); // Nontoken creatures you control have riot. - Ability ability = new SimpleStaticAbility(new GainAbilityControlledSpellsEffect( - new RiotAbility(), new FilterCreatureSpell()).setText("Nontoken creatures you control have riot. (They enter the battlefield with your choice of a +1/+1 counter or haste.)")); + Ability ability = new SimpleStaticAbility(new RhythmOfTheWildEffect()); ability.addEffect(new GainAbilityControlledEffect( new RiotAbility(), Duration.WhileOnBattlefield, filter2 ).setText("")); @@ -61,3 +72,55 @@ public final class RhythmOfTheWild extends CardImpl { return new RhythmOfTheWild(this); } } + +class RhythmOfTheWildEffect extends ReplacementEffectImpl { + + RhythmOfTheWildEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature); + staticText = "Nontoken creatures you control have riot. " + + "(They enter the battlefield with your choice of a +1/+1 counter or haste.)"; + } + + private RhythmOfTheWildEffect(RhythmOfTheWildEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + return creature != null + && creature.isControlledBy(source.getControllerId()) + && creature.isCreature(); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + Player player = game.getPlayer(source.getControllerId()); + if (creature == null || player == null) { + return false; + } + if (player.chooseUse( + outcome, "Have " + creature.getLogName() + " enter the battlefield with a +1/+1 counter on it or with haste?", + null, "+1/+1 counter", "Haste", source, game + )) { + game.informPlayers(player.getLogName() + " choose to put a +1/+1 counter on " + creature.getName()); + creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects()); + } else { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + effect.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game) + 1)); + game.addEffect(effect, source); + } + return false; + } + + @Override + public RhythmOfTheWildEffect copy() { + return new RhythmOfTheWildEffect(this); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java index 41b7b56991..178d1a1e64 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java @@ -8,7 +8,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class RiotTest extends CardTestPlayerBase { @@ -17,7 +16,7 @@ public class RiotTest extends CardTestPlayerBase { * A creature with riot enters the battlefield with a +1/+1 counter on it or * with haste, its controller's choice. This choice is made as the creature * enters the battlefield, so no one can respond to the choice. - * + *

* The creature will have the chosen bonus the moment it enters the * battlefield. If you choose to have the creature gain haste, it keeps * haste even after the turn ends. This could matter if another player gains @@ -113,4 +112,56 @@ public class RiotTest extends CardTestPlayerBase { assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), true); } + + @Test + public void RiotRhythmOfTheWildNotCastBoost() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // Creature spells you control can't be countered. + // Nontoken creatures you control have riot. + addCard(Zone.BATTLEFIELD, playerA, "Rhythm of the Wild", 1); + + // Riot (This creature enters the battleifled with your choice of a +1/+1 counter or haste.) + addCard(Zone.HAND, playerA, "Exhume", 1); // Each player returns a creature card from their graveyard to the battlefield + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 1); // Creature {1}{W} 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhume"); + setChoice(playerA, "Yes"); // yes - counter + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), false); + assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), true); + } + + @Test + public void RiotRhythmOfTheWildNotCastHaste() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // Creature spells you control can't be countered. + // Nontoken creatures you control have riot. + addCard(Zone.BATTLEFIELD, playerA, "Rhythm of the Wild", 1); + + // Riot (This creature enters the battleifled with your choice of a +1/+1 counter or haste.) + addCard(Zone.HAND, playerA, "Exhume", 1); // Each player returns a creature card from their graveyard to the battlefield + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 1); // Creature {1}{W} 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exhume"); + setChoice(playerA, "No"); // no - haste + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); + assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), false); + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/RiotAbility.java b/Mage/src/main/java/mage/abilities/keyword/RiotAbility.java index 8ce3e97bb5..fe667a08ce 100644 --- a/Mage/src/main/java/mage/abilities/keyword/RiotAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/RiotAbility.java @@ -71,10 +71,8 @@ class RiotReplacementEffect extends ReplacementEffectImpl { Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); Player controller = game.getPlayer(source.getControllerId()); if (creature != null && controller != null) { - if (controller.chooseUse(outcome, "Have " + creature.getLogName() + " enter the battlefield with a +1/+1 counter on it or with haste?",null, "+1/+1 counter", "Haste", source, game)) { - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " choose to put a +1/+1 counter on " + creature.getName()); - } + if (controller.chooseUse(outcome, "Have " + creature.getLogName() + " enter the battlefield with a +1/+1 counter on it or with haste?", null, "+1/+1 counter", "Haste", source, game)) { + game.informPlayers(controller.getLogName() + " choose to put a +1/+1 counter on " + creature.getName()); creature.addCounters(CounterType.P1P1.createInstance(), source, game, event.getAppliedEffects()); } else { game.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom), source); From 1c483c6f2a6f9ff098180c2cca2dc9d3741fb812 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 25 Jan 2019 10:41:11 -0500 Subject: [PATCH 04/21] small fix --- .../java/org/mage/test/cards/abilities/keywords/RiotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java index 178d1a1e64..5c0cd5bce1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java @@ -162,6 +162,6 @@ public class RiotTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Silvercoat Lion", 1); assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); - assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), false); + assertAbility(playerA, "Silvercoat Lion", new RiotAbility(), true); } } From 379e6629f2343e47ed7f8dddd2c85214fa81ee95 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 25 Jan 2019 09:49:01 -0600 Subject: [PATCH 05/21] - Fixed Intet, The Dreamer. --- .../src/mage/cards/i/IntetTheDreamer.java | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java index 1073af6244..dad56547b8 100644 --- a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java +++ b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java @@ -1,8 +1,5 @@ - package mage.cards.i; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.MageObject; @@ -40,13 +37,17 @@ public final class IntetTheDreamer extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl("{2}{U}")), false, true)); + // You may look at that card for as long as it remains exiled. this.addAbility(new SimpleStaticAbility(Zone.ALL, new IntetTheDreamerLookEffect())); + // You may play that card without paying its mana cost for as long as Intet remains on the battlefield. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IntetTheDreamerCastEffect())); + } public IntetTheDreamer(final IntetTheDreamer card) { @@ -76,17 +77,20 @@ class IntetTheDreamerExileEffect extends OneShotEffect { if (controller != null) { Card card = controller.getLibrary().getFromTop(game); MageObject sourceObject = source.getSourceObject(game); - if (card != null && sourceObject != null) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + if (card != null + && sourceObject != null) { card.setFaceDown(true, game); - controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName()); + controller.moveCardsToExile( + card, + source, + game, + false, + CardUtil.getExileZoneId(game, + source.getSourceId(), + sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly + sourceObject.getIdName()); card.setFaceDown(true, game); - Set exileZones = (Set) game.getState().getValue(IntetTheDreamer.VALUE_PREFIX + source.getSourceId().toString()); - if (exileZones == null) { - exileZones = new HashSet<>(); - game.getState().setValue(IntetTheDreamer.VALUE_PREFIX + source.getSourceId().toString(), exileZones); - } - exileZones.add(exileZoneId); + game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); return true; } } @@ -122,20 +126,29 @@ class IntetTheDreamerCastEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { + if (affectedControllerId.equals(source.getControllerId())) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { + if (controller != null + && sourceObject != null) { Card card = game.getCard(objectId); - if (card != null && card.isFaceDown(game)) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); - if (zone != null && zone.contains(card.getId())/* && CardUtil.cardCanBePlayedNow(card, controller.getId(), game)*/) { + if (card != null + && card.isFaceDown(game)) { + ExileZone zone = game.getExile().getExileZone( + CardUtil.getExileZoneId(game, + source.getSourceId(), + sourceObject.getZoneChangeCounter(game))); // sourceObject must be used due to source not working correctly + if (zone != null + && zone.contains(card.getId())) { if (card.isLand()) { - if (game.canPlaySorcery(controller.getId()) && game.getPlayer(controller.getId()).canPlayLand()) { + if (game.canPlaySorcery(controller.getId()) + && game.getPlayer(controller.getId()).canPlayLand()) { return controller.chooseUse(outcome, "Play " + card.getIdName() + '?', source, game); } } else { - controller.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); + controller.setCastSourceIdWithAlternateMana(objectId, + null, + card.getSpellAbility().getCosts()); return true; } } @@ -170,24 +183,14 @@ class IntetTheDreamerLookEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { + if (affectedControllerId.equals(source.getControllerId())) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { + if (controller != null) { Card card = game.getCard(objectId); - if (card != null && card.isFaceDown(game)) { - Set exileZones = (Set) game.getState().getValue(IntetTheDreamer.VALUE_PREFIX + source.getSourceId().toString()); - if (exileZones != null) { - for (ExileZone exileZone : game.getExile().getExileZones()) { - if (exileZone.contains(objectId)) { - if (!exileZones.contains(exileZone.getId())) { - return false; - } - } - } - return true; - } - } + return (card != null + && card.isFaceDown(game) + && game.getExile().containsId(card.getId(), game) + && (Boolean) game.getState().getValue("Exiled_IntetTheDreamer" + card.getId())); } } return false; From 126a59510f40277e8b1c012c064fdaa808580e2c Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 25 Jan 2019 11:57:32 -0500 Subject: [PATCH 06/21] another small fix --- Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java b/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java index f6899c2e47..85519b4e26 100644 --- a/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java +++ b/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java @@ -27,6 +27,7 @@ import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.targetpointer.FixedTarget; @@ -95,7 +96,8 @@ class RhythmOfTheWildEffect extends ReplacementEffectImpl { Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); return creature != null && creature.isControlledBy(source.getControllerId()) - && creature.isCreature(); + && creature.isCreature() + && !(creature instanceof PermanentToken); } @Override From e4ca6749c02f38b00d68e5b503387e39867760cc Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 26 Jan 2019 01:20:43 +0400 Subject: [PATCH 07/21] * UI: added free seats colorozing in tables list (green color on free seats to joins) --- .../java/mage/client/table/TablesPanel.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 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 be6b96ce6e..08f307fdc9 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -28,6 +28,7 @@ import org.ocpsoft.prettytime.units.JustNow; import javax.swing.*; import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; @@ -163,13 +164,33 @@ public class TablesPanel extends javax.swing.JPanel { } }; - // center text render - TableCellRenderer centerCellRenderer = new DefaultTableCellRenderer() { + // seats render + TableCellRenderer seatsCellRenderer = new DefaultTableCellRenderer() { + + JLabel greenLabel = new JLabel(); + @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - label.setHorizontalAlignment(JLabel.CENTER); - return label; + JLabel defaultLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + defaultLabel.setHorizontalAlignment(JLabel.CENTER); + // colors + String val = (String) value; + String[] valsList = val.split("/"); + if (valsList.length == 2 && !valsList[0].equals(valsList[1])) { + // green draw + Color defaultBack = defaultLabel.getBackground(); + greenLabel.setText(val); + greenLabel.setHorizontalAlignment(JLabel.CENTER); + greenLabel.setFont(defaultLabel.getFont()); + greenLabel.setForeground(Color.black); + greenLabel.setOpaque(true); + greenLabel.setBackground(new Color(156, 240, 146)); + greenLabel.setBorder(new LineBorder(defaultBack, 1)); + return greenLabel; + } else { + // default draw + return defaultLabel; + } } }; @@ -200,7 +221,7 @@ public class TablesPanel extends javax.swing.JPanel { // skill level tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_SKILL).setCellRenderer(skillCellRenderer); // seats - tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_SEATS).setCellRenderer(centerCellRenderer); + tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_SEATS).setCellRenderer(seatsCellRenderer); /* date sorter (not need, default is good - see getColumnClass) activeTablesSorter.setComparator(TablesTableModel.COLUMN_CREATED, new Comparator() { From 8c89772c3d9957fbd898f22b58d0487424683c86 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 25 Jan 2019 16:31:39 -0600 Subject: [PATCH 08/21] - Fixed Gustha's Scepter --- .../src/mage/cards/g/GusthasScepter.java | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GusthasScepter.java b/Mage.Sets/src/mage/cards/g/GusthasScepter.java index cd0cbddf39..15ba334632 100644 --- a/Mage.Sets/src/mage/cards/g/GusthasScepter.java +++ b/Mage.Sets/src/mage/cards/g/GusthasScepter.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.HashSet; @@ -10,7 +9,6 @@ import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; @@ -30,7 +28,6 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInHand; -import mage.util.CardUtil; /** * @@ -84,7 +81,11 @@ class GusthasScepterExileEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); MageObject sourceObject = game.getObject(source.getSourceId()); if (card != null && sourceObject != null) { - if (card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourceObject.getIdName(), source.getSourceId(), game)) { + UUID exileId = source.getSourceId(); + if (card.moveToExile(exileId, + sourceObject.getIdName(), + source.getSourceId(), + game)) { card.setFaceDown(true, game); game.addEffect(new GusthasScepterLookAtCardEffect(card.getId()), source); return true; @@ -116,7 +117,7 @@ class TargetCardInGusthasScepterExile extends TargetCardInExile { Set possibleTargets = new HashSet<>(); Card sourceCard = game.getCard(sourceId); if (sourceCard != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); + UUID exileId = sourceId; ExileZone exile = game.getExile().getExileZone(exileId); if (exile != null && !exile.isEmpty()) { possibleTargets.addAll(exile); @@ -129,7 +130,7 @@ class TargetCardInGusthasScepterExile extends TargetCardInExile { public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { Card sourceCard = game.getCard(sourceId); if (sourceCard != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); + UUID exileId = sourceId; ExileZone exile = game.getExile().getExileZone(exileId); if (exile != null && !exile.isEmpty()) { return true; @@ -141,14 +142,16 @@ class TargetCardInGusthasScepterExile extends TargetCardInExile { @Override public boolean canTarget(UUID id, Ability source, Game game) { Card card = game.getCard(id); - if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED) { + if (card != null + && game.getState().getZone(card.getId()) == Zone.EXILED) { ExileZone exile = null; Card sourceCard = game.getCard(source.getSourceId()); if (sourceCard != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); + UUID exileId = source.getSourceId(); exile = game.getExile().getExileZone(exileId); } - if (exile != null && exile.contains(id)) { + if (exile != null + && exile.contains(id)) { return filter.match(card, source.getControllerId(), game); } } @@ -190,13 +193,16 @@ class GusthasScepterLookAtCardEffect extends AsThoughEffectImpl { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { MageObject sourceObject = source.getSourceObject(game); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - if (exileId != null && sourceObject != null) { + if (sourceObject != null) { + UUID exileId = source.getSourceId(); ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null && exileZone.contains(cardId)) { + if (exileZone != null + && exileZone.contains(cardId)) { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(cardId); - if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { + if (controller != null + && card != null + && game.getState().getZone(cardId) == Zone.EXILED) { return true; } } else { @@ -233,16 +239,10 @@ class GusthasScepterLoseControlAbility extends DelayedTriggeredAbility { if (event.getType() == GameEvent.EventType.LOST_CONTROL) { return event.getPlayerId().equals(controllerId) && event.getTargetId().equals(this.getSourceId()); - } - else if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { + } else if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { if (event.getTargetId().equals(this.getSourceId())) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - for (Effect effect : getEffects()) { - effect.setValue("permanentLeftBattlefield", ((ZoneChangeEvent) event).getTarget()); - } - return true; - } + return (zEvent.getFromZone() == Zone.BATTLEFIELD); } } return false; @@ -268,13 +268,12 @@ class GusthasScepterPutExiledCardsInOwnersGraveyard extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null - && sourceObject != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); - controller.moveCardsToGraveyardWithInfo(cardsInExile, source, game, Zone.EXILED); - return true; + if (controller != null) { + UUID exileId = source.getSourceId(); + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null) { + return controller.moveCards(exileZone.getCards(game), Zone.GRAVEYARD, source, game); + } } return false; } From b800585cc542c16aef2dc71f5500358d1d1ba12f Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 25 Jan 2019 22:19:01 -0500 Subject: [PATCH 09/21] fixed implementation of Knight of the Mists --- .../src/mage/cards/k/KnightOfTheMists.java | 63 ++++--------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/Mage.Sets/src/mage/cards/k/KnightOfTheMists.java b/Mage.Sets/src/mage/cards/k/KnightOfTheMists.java index f2eb1cd069..8cf5ac9bcf 100644 --- a/Mage.Sets/src/mage/cards/k/KnightOfTheMists.java +++ b/Mage.Sets/src/mage/cards/k/KnightOfTheMists.java @@ -1,37 +1,30 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.InfoEffect; import mage.abilities.keyword.FlankingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class KnightOfTheMists extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Knight"); - - static { - filter.add(new SubtypePredicate(SubType.KNIGHT)); - } + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.KNIGHT, "Knight"); public KnightOfTheMists(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); @@ -45,12 +38,14 @@ public final class KnightOfTheMists extends CardImpl { this.addAbility(new FlankingAbility()); // When Knight of the Mists enters the battlefield, you may pay {U}. If you don't, destroy target Knight and it can't be regenerated. - Ability ability = new EntersBattlefieldTriggeredAbility(new KnightOfTheMistsEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + Ability ability = new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( + new InfoEffect(""), new DestroyTargetEffect(), new ManaCostsImpl("{U}") + ).setText("you may pay {U}. If you don't, destroy target Knight and it can't be regenerated.")); + ability.addTarget(new TargetPermanent(filter)); addAbility(ability); } - public KnightOfTheMists(final KnightOfTheMists card) { + private KnightOfTheMists(final KnightOfTheMists card) { super(card); } @@ -59,35 +54,3 @@ public final class KnightOfTheMists extends CardImpl { return new KnightOfTheMists(this); } } - -class KnightOfTheMistsEffect extends OneShotEffect { - - KnightOfTheMistsEffect() { - super(Outcome.Neutral); - this.staticText = "When {this} enters the battlefield, you may pay {U}. If you don't, destroy target Knight and it can't be regenerated."; - } - - KnightOfTheMistsEffect(final KnightOfTheMistsEffect effect) { - super(effect); - } - - @Override - public KnightOfTheMistsEffect copy() { - return new KnightOfTheMistsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getSourceId()); - if (player == null) { - return false; - } - Cost cost = new ManaCostsImpl("{U}"); - if (!(cost.canPay(source, source.getSourceId(), player.getId(), game) - && player.chooseUse(outcome, "Pay {U}?", source, game) - && cost.pay(source, game, source.getSourceId(), player.getId(), false))) { - return new DestroyTargetEffect(true).apply(game, source); - } - return true; - } -} From 6e49f4a21c1272d1dd7c8a94eaa7efaaf5f8154c Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Fri, 25 Jan 2019 21:53:01 -0600 Subject: [PATCH 10/21] - little fix Deadbridge Chant. Now works well with Humility, etc. --- Mage.Sets/src/mage/cards/d/DeadbridgeChant.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DeadbridgeChant.java b/Mage.Sets/src/mage/cards/d/DeadbridgeChant.java index 3437bb03a0..a7624a76ce 100644 --- a/Mage.Sets/src/mage/cards/d/DeadbridgeChant.java +++ b/Mage.Sets/src/mage/cards/d/DeadbridgeChant.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -21,13 +20,10 @@ import mage.players.Player; * * @author LevelX2 */ - - public final class DeadbridgeChant extends CardImpl { public DeadbridgeChant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{B}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}{G}"); // When Deadbridge Chant enters the battlefield, put the top ten cards of your library into your graveyard. this.addAbility(new EntersBattlefieldTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(10))); @@ -64,9 +60,10 @@ class DeadbridgeChantEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null && !player.getGraveyard().isEmpty()) { - Card card = player.getGraveyard().getRandom(game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null + && !controller.getGraveyard().isEmpty()) { + Card card = controller.getGraveyard().getRandom(game); if (card != null) { Zone targetZone = Zone.HAND; String text = " put into hand of "; @@ -74,8 +71,8 @@ class DeadbridgeChantEffect extends OneShotEffect { targetZone = Zone.BATTLEFIELD; text = " put onto battlefield for "; } - card.moveToZone(targetZone, source.getSourceId(), game, false); - game.informPlayers("Deadbridge Chant: " + card.getName() + text + player.getLogName()); + controller.moveCards(card, targetZone, source, game); + game.informPlayers("Deadbridge Chant: " + card.getName() + text + controller.getLogName()); return true; } } From c9de200b042596bca3e9bea7ff643d2983f36660 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 25 Jan 2019 23:17:06 -0500 Subject: [PATCH 11/21] fixed implementation of Phyrexian Processor --- .../src/mage/cards/p/PhyrexianProcessor.java | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java b/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java index 20b78322d0..e6a289cbe1 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianProcessor.java @@ -1,18 +1,20 @@ package mage.cards.p; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.token.MinionToken; import mage.players.Player; import mage.util.CardUtil; @@ -28,14 +30,17 @@ public final class PhyrexianProcessor extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // As {this} enters the battlefield, pay any amount of life. - this.addAbility(new EntersBattlefieldTriggeredAbility(new PhyrexianProcessorEffect())); + this.addAbility(new AsEntersBattlefieldAbility(new PhyrexianProcessorPayLifeEffect())); + // {4}, {tap}: Create an X/X black Minion creature token, where X is the life paid as {this} entered the battlefield. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhyrexianProcessorCreateTokenEffect(), new ManaCostsImpl("{4}")); + Ability ability = new SimpleActivatedAbility( + new PhyrexianProcessorCreateTokenEffect(), new GenericManaCost(4) + ); ability.addCost(new TapSourceCost()); this.addAbility(ability); } - public PhyrexianProcessor(final PhyrexianProcessor card) { + private PhyrexianProcessor(final PhyrexianProcessor card) { super(card); } @@ -45,47 +50,53 @@ public final class PhyrexianProcessor extends CardImpl { } } -class PhyrexianProcessorEffect extends OneShotEffect { +class PhyrexianProcessorPayLifeEffect extends OneShotEffect { - public PhyrexianProcessorEffect() { + PhyrexianProcessorPayLifeEffect() { super(Outcome.LoseLife); - staticText = "Pay any amount of life."; + staticText = "pay any amount of life."; } - public PhyrexianProcessorEffect(final PhyrexianProcessorEffect effect) { + private PhyrexianProcessorPayLifeEffect(final PhyrexianProcessorPayLifeEffect effect) { super(effect); } @Override - public PhyrexianProcessorEffect copy() { - return new PhyrexianProcessorEffect(this); + public PhyrexianProcessorPayLifeEffect copy() { + return new PhyrexianProcessorPayLifeEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int payAmount = controller.getAmount(0, controller.getLife(), staticText, game); - controller.loseLife(payAmount, game, false); - Card sourceCard = game.getCard(source.getSourceId()); - game.informPlayers((sourceCard != null ? sourceCard.getName() : "") + ": " + controller.getLogName() + - " pays " + payAmount + " life."); - String key = CardUtil.getCardZoneString("lifePaid", source.getSourceId(), game); - game.getState().setValue(key, payAmount); - return true; + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (controller == null || permanent == null) { + return false; } - return false; + int payAmount = controller.getAmount(0, controller.getLife(), "Pay any amount of life", game); + Cost cost = new PayLifeCost(payAmount); + if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), true)) { + return false; + } + Card sourceCard = game.getCard(source.getSourceId()); + game.informPlayers((sourceCard != null ? sourceCard.getName() : "") + ": " + controller.getLogName() + + " pays " + payAmount + " life."); + String key = CardUtil.getCardZoneString("lifePaid", source.getSourceId(), game); + game.getState().setValue(key, payAmount); + permanent.addInfo("life paid", CardUtil.addToolTipMarkTags("Life paid: " + payAmount), game); + return true; } } class PhyrexianProcessorCreateTokenEffect extends OneShotEffect { - public PhyrexianProcessorCreateTokenEffect() { + PhyrexianProcessorCreateTokenEffect() { super(Outcome.PutCreatureInPlay); - staticText = "Create an X/X black Minion creature token"; + staticText = "Create an X/X black Minion creature token, " + + "where X is the life paid as {this} entered the battlefield."; } - public PhyrexianProcessorCreateTokenEffect(PhyrexianProcessorCreateTokenEffect ability) { + private PhyrexianProcessorCreateTokenEffect(PhyrexianProcessorCreateTokenEffect ability) { super(ability); } @@ -96,7 +107,7 @@ class PhyrexianProcessorCreateTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - String key = CardUtil.getCardZoneString("lifePaid", source.getSourceId(), game); + String key = CardUtil.getCardZoneString("lifePaid", source.getSourceId(), game, true); Object object = game.getState().getValue(key); if (object instanceof Integer) { int lifePaid = (int) object; From c145beb38ccc4ea91d341beac48a218ddd1c33a5 Mon Sep 17 00:00:00 2001 From: Brik Royster Date: Sat, 26 Jan 2019 00:48:04 -0800 Subject: [PATCH 12/21] Fix Psychic Surgery putting cards on bottom of library #5126 Effect now properly puts cards on top of opponent's library. --- Mage.Sets/src/mage/cards/p/PsychicSurgery.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/p/PsychicSurgery.java b/Mage.Sets/src/mage/cards/p/PsychicSurgery.java index b50c23af47..1c21405e4e 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicSurgery.java +++ b/Mage.Sets/src/mage/cards/p/PsychicSurgery.java @@ -114,7 +114,7 @@ class PsychicSurgeryEffect extends OneShotEffect { } } } - controller.putCardsOnBottomOfLibrary(cards, game, source, true); + controller.putCardsOnTopOfLibrary(cards, game, source, true); return true; } return false; From 8ef30e4cdec7af703ff7725236e85618f3fd4885 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 26 Jan 2019 14:16:44 +0400 Subject: [PATCH 13/21] Cheats: added command to add any card/spell to stack without cost (only simple cast supports, without targets and modes); --- .../java/mage/server/util/SystemUtil.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 41e8b9e691..e398fc67bb 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -442,6 +442,26 @@ public final class SystemUtil { perm.addCounters(CounterType.LOYALTY.createInstance(command.Amount), null, game); } } + continue; + } else if ("stack".equalsIgnoreCase(command.zone)) { + // simple cast (without targets or modes) + + // find card info + CardInfo cardInfo = CardRepository.instance.findCard(command.cardName); + if (cardInfo == null) { + logger.warn("Unknown card for stack command [" + command.cardName + "]: " + line); + continue; + } + + // put card to game + Card card = cardInfo.getCard(); + Set cardsToLoad = new HashSet<>(); + cardsToLoad.add(card); + game.loadCards(cardsToLoad, player.getId()); + + // move card from exile to stack + swapWithAnyCard(game, player, card, Zone.STACK); + continue; } @@ -516,6 +536,8 @@ public final class SystemUtil { game.getExile().getPermanentExile().remove(card); player.getLibrary().putOnTop(card, game); break; + case STACK: + card.cast(game, Zone.EXILED, card.getSpellAbility(), player.getId()); default: card.moveToZone(zone, null, game, false); } From f1ba813518032f007b3d878bc42363b425987bb7 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 26 Jan 2019 14:32:32 +0400 Subject: [PATCH 14/21] Cheats: added amount supports in stack command; --- .../src/main/java/mage/server/util/SystemUtil.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 e398fc67bb..96a69327d1 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -445,7 +445,7 @@ public final class SystemUtil { continue; } else if ("stack".equalsIgnoreCase(command.zone)) { // simple cast (without targets or modes) - + // find card info CardInfo cardInfo = CardRepository.instance.findCard(command.cardName); if (cardInfo == null) { @@ -454,13 +454,16 @@ public final class SystemUtil { } // put card to game - Card card = cardInfo.getCard(); Set cardsToLoad = new HashSet<>(); - cardsToLoad.add(card); + for (int i = 0; i < command.Amount; i++) { + cardsToLoad.add(cardInfo.getCard()); + } game.loadCards(cardsToLoad, player.getId()); // move card from exile to stack - swapWithAnyCard(game, player, card, Zone.STACK); + for (Card card : cardsToLoad) { + swapWithAnyCard(game, player, card, Zone.STACK); + } continue; } From fd0292001e9276b57c1076553806c4d2cd106687 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 26 Jan 2019 14:46:54 +0400 Subject: [PATCH 15/21] * Kheru Spellsnatcher - added special list with no mana cost cards; --- Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java index 075f42fd6b..f6779f259a 100644 --- a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java +++ b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java @@ -19,6 +19,7 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -81,7 +82,8 @@ class KheruSpellsnatcherEffect extends OneShotEffect { if (!stackObject.isCopy()) { MageObject card = game.getObject(stackObject.getSourceId()); if (card instanceof Card) { - ((Card) card).moveToZone(Zone.EXILED, sourceId, game, true); + UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); + ((Card) card).moveToExile(exileId, "Kheru Spellsnatcher - cast without mana cost", sourceId, game); ContinuousEffect effect = new KheruSpellsnatcherCastFromExileEffect(); effect.setTargetPointer(new FixedTarget(card.getId())); game.addEffect(effect, source); From e5f6980b2d917d1a392d78da9a77b8b3fa39205c Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 26 Jan 2019 09:41:48 -0500 Subject: [PATCH 16/21] updated implementation of Minion of the Wastes and Nameless Race --- .../src/mage/cards/m/MinionOfTheWastes.java | 35 ++++++++----- Mage.Sets/src/mage/cards/n/NamelessRace.java | 51 ++++++++++++------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java index 25ff9a740d..bf506fabc6 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java @@ -4,6 +4,8 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; @@ -13,7 +15,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import java.util.UUID; @@ -38,7 +42,7 @@ public final class MinionOfTheWastes extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the life paid as it entered the battlefield"))); } - public MinionOfTheWastes(final MinionOfTheWastes card) { + private MinionOfTheWastes(final MinionOfTheWastes card) { super(card); } @@ -50,12 +54,12 @@ public final class MinionOfTheWastes extends CardImpl { class MinionOfTheWastesEffect extends OneShotEffect { - public MinionOfTheWastesEffect() { + MinionOfTheWastesEffect() { super(Outcome.LoseLife); staticText = "pay any amount of life"; } - public MinionOfTheWastesEffect(final MinionOfTheWastesEffect effect) { + private MinionOfTheWastesEffect(final MinionOfTheWastesEffect effect) { super(effect); } @@ -67,15 +71,22 @@ class MinionOfTheWastesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int payAmount = controller.getAmount(0, controller.getLife(), "Pay any amount of life", game); - controller.loseLife(payAmount, game, false); - Card sourceCard = game.getCard(source.getSourceId()); - game.informPlayers((sourceCard != null ? sourceCard.getLogName() : "") + ": " + controller.getLogName() + - " pays " + payAmount + " life"); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); - return true; + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (controller == null || permanent == null) { + return false; } - return false; + int payAmount = controller.getAmount(0, controller.getLife(), "Pay any amount of life", game); + Cost cost = new PayLifeCost(payAmount); + if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), true)) { + return false; + } + Card sourceCard = game.getCard(source.getSourceId()); + game.informPlayers((sourceCard != null ? sourceCard.getLogName() : "") + ": " + controller.getLogName() + + " pays " + payAmount + " life"); + game.addEffect(new SetPowerToughnessSourceEffect( + payAmount, payAmount, Duration.Custom, SubLayer.CharacteristicDefining_7a + ), source); + permanent.addInfo("life paid", CardUtil.addToolTipMarkTags("Life paid: " + payAmount), game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/n/NamelessRace.java b/Mage.Sets/src/mage/cards/n/NamelessRace.java index 73afab8657..eca9d51b5a 100644 --- a/Mage.Sets/src/mage/cards/n/NamelessRace.java +++ b/Mage.Sets/src/mage/cards/n/NamelessRace.java @@ -5,6 +5,8 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; @@ -23,7 +25,9 @@ import mage.filter.predicate.other.OwnerPredicate; import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import java.util.UUID; @@ -47,7 +51,7 @@ public final class NamelessRace extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the life paid as it entered the battlefield"))); } - public NamelessRace(final NamelessRace card) { + private NamelessRace(final NamelessRace card) { super(card); } @@ -59,8 +63,10 @@ public final class NamelessRace extends CardImpl { class NamelessRaceEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent("white nontoken permanents your opponents control"); - private static final FilterCard filter2 = new FilterCard("white cards in their graveyards"); + private static final FilterPermanent filter + = new FilterPermanent("white nontoken permanents your opponents control"); + private static final FilterCard filter2 + = new FilterCard("white cards in their graveyards"); static { filter.add(new ColorPredicate(ObjectColor.WHITE)); @@ -70,12 +76,14 @@ class NamelessRaceEffect extends OneShotEffect { filter2.add(new OwnerPredicate(TargetController.OPPONENT)); } - public NamelessRaceEffect() { + NamelessRaceEffect() { super(Outcome.LoseLife); - staticText = "pay any amount of life. The amount you pay can't be more than the total number of white nontoken permanents your opponents control plus the total number of white cards in their graveyards"; + staticText = "pay any amount of life. The amount you pay can't be more than " + + "the total number of white nontoken permanents your opponents control " + + "plus the total number of white cards in their graveyards"; } - public NamelessRaceEffect(final NamelessRaceEffect effect) { + private NamelessRaceEffect(final NamelessRaceEffect effect) { super(effect); } @@ -87,18 +95,25 @@ class NamelessRaceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int permanentsInPlay = new PermanentsOnBattlefieldCount(filter).calculate(game, source, null); - int cardsInGraveyards = new CardsInAllGraveyardsCount(filter2).calculate(game, source, null); - int maxAmount = Math.min(permanentsInPlay + cardsInGraveyards, controller.getLife()); - int payAmount = controller.getAmount(0, maxAmount, "Pay up to " + maxAmount + " life", game); - controller.loseLife(payAmount, game, false); - Card sourceCard = game.getCard(source.getSourceId()); - game.informPlayers((sourceCard != null ? sourceCard.getLogName() : "") + ": " + controller.getLogName() + - " pays " + payAmount + " life"); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); - return true; + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (controller == null || permanent == null) { + return false; } - return false; + int permanentsInPlay = new PermanentsOnBattlefieldCount(filter).calculate(game, source, null); + int cardsInGraveyards = new CardsInAllGraveyardsCount(filter2).calculate(game, source, null); + int maxAmount = Math.min(permanentsInPlay + cardsInGraveyards, controller.getLife()); + int payAmount = controller.getAmount(0, maxAmount, "Pay up to " + maxAmount + " life", game); + Cost cost = new PayLifeCost(payAmount); + if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), true)) { + return false; + } + Card sourceCard = game.getCard(source.getSourceId()); + game.informPlayers((sourceCard != null ? sourceCard.getLogName() : "") + ": " + controller.getLogName() + + " pays " + payAmount + " life"); + game.addEffect(new SetPowerToughnessSourceEffect( + payAmount, payAmount, Duration.Custom, SubLayer.CharacteristicDefining_7a + ), source); + permanent.addInfo("life paid", CardUtil.addToolTipMarkTags("Life paid: " + payAmount), game); + return true; } } From 37393250005fda47cd36092ecc5dce0407f509eb Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 26 Jan 2019 19:31:12 +0400 Subject: [PATCH 17/21] UI: fixed that chat messages history was editable; --- Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.form | 1 + Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java | 1 + 2 files changed, 2 insertions(+) diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.form b/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.form index 80e38b5831..adf961e87e 100644 --- a/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.form +++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.form @@ -47,6 +47,7 @@ + diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java b/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java index 97392d0e58..c20a6ac867 100644 --- a/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java +++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java @@ -366,6 +366,7 @@ public class ChatPanelBasic extends javax.swing.JPanel { jScrollPaneTxt.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); jScrollPaneTxt.setPreferredSize(new java.awt.Dimension(32767, 32767)); + txtConversation.setEditable(false); txtConversation.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); txtConversation.setFont(new java.awt.Font("Arial", 0, 14)); // NOI18N txtConversation.setFocusCycleRoot(false); From bd652632f94ae74dd7451518980607f4c440c622 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 26 Jan 2019 13:44:39 -0500 Subject: [PATCH 18/21] various em dash text fixes --- Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java | 2 +- .../src/main/java/mage/abilities/keyword/FlashbackAbility.java | 2 +- Mage/src/main/java/mage/abilities/keyword/ModularAbility.java | 2 +- Mage/src/main/java/mage/abilities/keyword/MorphAbility.java | 3 ++- .../src/main/java/mage/abilities/keyword/ReinforceAbility.java | 2 +- .../java/mage/abilities/keyword/SpliceOntoArcaneAbility.java | 2 +- Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java b/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java index a889f8dd79..4cc80bd106 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java @@ -51,7 +51,7 @@ public class BuybackAbility extends StaticAbility implements OptionalAdditionalS public BuybackAbility(Cost cost) { super(Zone.STACK, new BuybackEffect()); - this.buybackCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, cost); + this.buybackCost = new OptionalAdditionalCostImpl(keywordText, "—", reminderTextCost, cost); setRuleAtTheTop(true); } diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index b07f6dbd32..ba192daadf 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -134,7 +134,7 @@ public class FlashbackAbility extends SpellAbility { public String getRule() { StringBuilder sbRule = new StringBuilder("Flashback"); if (!costs.isEmpty()) { - sbRule.append(" - "); + sbRule.append("—"); } else { sbRule.append(' '); } diff --git a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java index 250992d472..2592e16aa9 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java @@ -93,7 +93,7 @@ public class ModularAbility extends DiesTriggeredAbility { public String getRule() { StringBuilder sb = new StringBuilder("Modular"); if (sunburst) { - sb.append("-Sunburst (This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. When it dies, you may put its +1/+1 counters on target artifact creature.)"); + sb.append("—Sunburst (This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. When it dies, you may put its +1/+1 counters on target artifact creature.)"); } else { sb.append(' ').append(amount).append(" (This enters the battlefield with ") .append(CardUtil.numberToText(amount, "a")) diff --git a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java index 6a1b3c449e..66ba57de9e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java @@ -108,7 +108,8 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost name = ABILITY_KEYWORD; for (Cost cost : morphCosts) { if (!(cost instanceof ManaCosts)) { - sb.append("- "); + sb.setLength(sb.length() - 1); + sb.append("—"); break; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/ReinforceAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReinforceAbility.java index df70c1df60..96e54314c3 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ReinforceAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ReinforceAbility.java @@ -43,7 +43,7 @@ public class ReinforceAbility extends SimpleActivatedAbility { @Override public String getRule() { StringBuilder sb = new StringBuilder("Reinforce "); - sb.append(count.toString()).append(" - "); + sb.append(count.toString()).append("—"); sb.append(cost.getText()); sb.append(" (").append(cost.getText()).append(", Discard this card: Put "); if (count.toString().equals("1")) { diff --git a/Mage/src/main/java/mage/abilities/keyword/SpliceOntoArcaneAbility.java b/Mage/src/main/java/mage/abilities/keyword/SpliceOntoArcaneAbility.java index f2880befeb..c6a604730c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SpliceOntoArcaneAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SpliceOntoArcaneAbility.java @@ -112,7 +112,7 @@ public class SpliceOntoArcaneAbility extends SimpleStaticAbility { @Override public String getRule() { StringBuilder sb = new StringBuilder(); - sb.append(KEYWORD_TEXT).append(nonManaCosts ? "-" : " "); + sb.append(KEYWORD_TEXT).append(nonManaCosts ? "—" : " "); sb.append(spliceCosts.getText()).append(nonManaCosts ? ". " : " "); sb.append("(As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.)"); return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index 7c4d3afdc4..f1e16d47bf 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -140,7 +140,7 @@ public class SuspendAbility extends SpecialAction { } StringBuilder sb = new StringBuilder("Suspend "); if (cost != null) { - sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append(" - ").append(cost.getText()).append(suspend == Integer.MAX_VALUE ? ". X can't be 0" : ""); + sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("—").append(cost.getText()).append(suspend == Integer.MAX_VALUE ? ". X can't be 0" : ""); if (!shortRule) { sb.append(" (Rather than cast this card from your hand, pay ") .append(cost.getText()) From 9a5ac6a8489dcb8ffcabed15e70b056daa36cc2e Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 26 Jan 2019 14:19:19 -0500 Subject: [PATCH 19/21] fixed several of my old implementations --- .../src/mage/cards/a/AmuletOfUnmaking.java | 17 ++++-- Mage.Sets/src/mage/cards/b/BloodHound.java | 57 ++++++------------- Mage.Sets/src/mage/cards/b/BrineShaman.java | 29 +++------- .../src/mage/cards/c/CephalidInkshrouder.java | 20 ++++--- .../src/mage/cards/c/ChieftainEnDal.java | 16 +++--- Mage.Sets/src/mage/cards/c/ChillHaunting.java | 15 ++--- Mage.Sets/src/mage/cards/c/CoffinPuppets.java | 27 ++++++--- .../src/mage/cards/c/CoilingWoodworm.java | 18 +++--- Mage.Sets/src/mage/cards/c/CommonCause.java | 18 +++--- .../src/mage/cards/c/ComplexAutomaton.java | 26 ++++++--- .../src/mage/cards/c/CrookclawElder.java | 36 +++++++----- Mage.Sets/src/mage/cards/d/DarkTriumph.java | 24 ++++---- Mage.Sets/src/mage/cards/d/Delraich.java | 18 +++--- .../src/mage/cards/d/DestructiveUrge.java | 18 +++--- .../src/mage/cards/d/DivineCongregation.java | 14 ++--- Mage.Sets/src/mage/cards/e/Epicenter.java | 16 +++--- .../src/mage/cards/i/InfestedRoothold.java | 20 +++---- .../mage/cards/m/MirrorOfTheForebears.java | 34 ++++++----- Mage.Sets/src/mage/cards/s/SecondSight.java | 20 +++---- .../main/java/mage/filter/StaticFilters.java | 6 ++ 20 files changed, 228 insertions(+), 221 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AmuletOfUnmaking.java b/Mage.Sets/src/mage/cards/a/AmuletOfUnmaking.java index 70272a0b5a..79decffc5d 100644 --- a/Mage.Sets/src/mage/cards/a/AmuletOfUnmaking.java +++ b/Mage.Sets/src/mage/cards/a/AmuletOfUnmaking.java @@ -1,11 +1,11 @@ package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.ExileSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ColorlessManaCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,27 +16,32 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class AmuletOfUnmaking extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact, creature, or enchantment"); + private static final FilterPermanent filter = new FilterPermanent("artifact, creature, or land"); static { filter.add(Predicates.or( new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.CREATURE), - new CardTypePredicate(CardType.ENCHANTMENT))); + new CardTypePredicate(CardType.LAND) + )); } public AmuletOfUnmaking(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // {5}, {tap}, Exile Amulet of Unmaking: Exile target artifact, creature, or land. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect("Exile target artifact, creature or land"), new ColorlessManaCost(5)); + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, new ExileTargetEffect(), new GenericManaCost(5) + ); ability.addCost(new TapSourceCost()); + ability.addCost(new ExileSourceCost()); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BloodHound.java b/Mage.Sets/src/mage/cards/b/BloodHound.java index 7a438904db..f182a4b5e3 100644 --- a/Mage.Sets/src/mage/cards/b/BloodHound.java +++ b/Mage.Sets/src/mage/cards/b/BloodHound.java @@ -1,23 +1,24 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.RemoveAllCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class BloodHound extends CardImpl { @@ -33,10 +34,12 @@ public final class BloodHound extends CardImpl { this.addAbility(new BloodHoundTriggeredAbility()); // At the beginning of your end step, remove all +1/+1 counters from Blood Hound. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.P1P1), TargetController.YOU, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new RemoveAllCountersSourceEffect(CounterType.P1P1), TargetController.YOU, false + )); } - public BloodHound(final BloodHound card) { + private BloodHound(final BloodHound card) { super(card); } @@ -48,11 +51,11 @@ public final class BloodHound extends CardImpl { class BloodHoundTriggeredAbility extends TriggeredAbilityImpl { - public BloodHoundTriggeredAbility() { - super(Zone.BATTLEFIELD, new BloodHoundEffect(), true); + BloodHoundTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), true); } - public BloodHoundTriggeredAbility(final BloodHoundTriggeredAbility ability) { + private BloodHoundTriggeredAbility(final BloodHoundTriggeredAbility ability) { super(ability); } @@ -68,8 +71,9 @@ class BloodHoundTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getControllerId())) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); + if (event.getTargetId().equals(this.getControllerId()) && event.getAmount() > 0) { + this.getEffects().clear(); + this.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(event.getAmount()))); return true; } return false; @@ -77,31 +81,6 @@ class BloodHoundTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever you are dealt damage, you may put that many +1/+1 counters on {this}."; - } -} - -class BloodHoundEffect extends OneShotEffect { - - public BloodHoundEffect() { - super(Outcome.Benefit); - } - - public BloodHoundEffect(final BloodHoundEffect effect) { - super(effect); - } - - @Override - public BloodHoundEffect copy() { - return new BloodHoundEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.addCounters(CounterType.P1P1.createInstance((Integer) this.getValue("damageAmount")), source, game); - } - return true; + return "Whenever you're dealt damage, you may put that many +1/+1 counters on {this}."; } } diff --git a/Mage.Sets/src/mage/cards/b/BrineShaman.java b/Mage.Sets/src/mage/cards/b/BrineShaman.java index 2a2d0398ca..a19445b707 100644 --- a/Mage.Sets/src/mage/cards/b/BrineShaman.java +++ b/Mage.Sets/src/mage/cards/b/BrineShaman.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -13,31 +12,21 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; - import mage.filter.StaticFilters; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetSpell; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; + /** - * * @author TheElk801 */ public final class BrineShaman extends CardImpl { - private static final FilterSpell filter = new FilterSpell("creature spell"); - - static { - filter.add(Predicates.not(new CardTypePredicate(CardType.CREATURE))); - } - public BrineShaman(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); @@ -46,16 +35,14 @@ public final class BrineShaman extends CardImpl { this.toughness = new MageInt(1); // {tap}, Sacrifice a creature: Target creature gets +2/+2 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(2, 2, Duration.EndOfTurn), - new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); - ability.addCost(new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(2, 2), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // {1}{U}{U}, Sacrifice a creature: Counter target creature spell. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), - new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); - ability.addCost(new ManaCostsImpl("{1}{U}{U}")); + ability = new SimpleActivatedAbility(new CounterTargetEffect(), new ManaCostsImpl("{1}{U}{U}")); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CephalidInkshrouder.java b/Mage.Sets/src/mage/cards/c/CephalidInkshrouder.java index e1c1f54fe6..e01b5375fd 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidInkshrouder.java +++ b/Mage.Sets/src/mage/cards/c/CephalidInkshrouder.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,30 +11,35 @@ import mage.abilities.keyword.ShroudAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class CephalidInkshrouder extends CardImpl { public CephalidInkshrouder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); - + this.subtype.add(SubType.CEPHALID); this.power = new MageInt(2); this.toughness = new MageInt(1); // Discard a card: Cephalid Inkshrouder gains shroud until end of turn and is unblockable this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(ShroudAbility.getInstance(),Duration.EndOfTurn), new DiscardCardCost()); - ability.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn)); + Ability ability = new SimpleActivatedAbility( + new GainAbilitySourceEffect( + ShroudAbility.getInstance(), + Duration.EndOfTurn + ), new DiscardCardCost() + ); + ability.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn).setText("and can't be blocked this turn")); this.addAbility(ability); } - public CephalidInkshrouder(final CephalidInkshrouder card) { + private CephalidInkshrouder(final CephalidInkshrouder card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/ChieftainEnDal.java b/Mage.Sets/src/mage/cards/c/ChieftainEnDal.java index 94468b8b24..2cddae6857 100644 --- a/Mage.Sets/src/mage/cards/c/ChieftainEnDal.java +++ b/Mage.Sets/src/mage/cards/c/ChieftainEnDal.java @@ -1,21 +1,20 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class ChieftainEnDal extends CardImpl { @@ -29,11 +28,12 @@ public final class ChieftainEnDal extends CardImpl { this.toughness = new MageInt(2); // Whenever Chieftain en-Dal attacks, attacking creatures gain first strike until end of turn. - Ability ability = new AttacksTriggeredAbility(new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature()), false); - this.addAbility(ability); + this.addAbility(new AttacksTriggeredAbility(new GainAbilityAllEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES + ), false)); } - public ChieftainEnDal(final ChieftainEnDal card) { + private ChieftainEnDal(final ChieftainEnDal card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/ChillHaunting.java b/Mage.Sets/src/mage/cards/c/ChillHaunting.java index d13c2420f2..c35832ceb8 100644 --- a/Mage.Sets/src/mage/cards/c/ChillHaunting.java +++ b/Mage.Sets/src/mage/cards/c/ChillHaunting.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.costs.common.ExileXFromYourGraveCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.GetXValue; @@ -11,28 +10,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ChillHaunting extends CardImpl { + private static final DynamicValue xval = new SignInversionDynamicValue(GetXValue.instance); + public ChillHaunting(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); // As an additional cost to cast Chill Haunting, exile X creature cards from your graveyard. - this.getSpellAbility().addCost(new ExileXFromYourGraveCost(new FilterCreatureCard("creature cards from your graveyard"), true)); + this.getSpellAbility().addCost(new ExileXFromYourGraveCost(StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD, true)); // Target creature gets -X/-X until end of turn. - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - DynamicValue xval = new SignInversionDynamicValue(GetXValue.instance); this.getSpellAbility().addEffect(new BoostTargetEffect(xval, xval, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } - public ChillHaunting(final ChillHaunting card) { + private ChillHaunting(final ChillHaunting card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/CoffinPuppets.java b/Mage.Sets/src/mage/cards/c/CoffinPuppets.java index abd123b793..621ce06321 100644 --- a/Mage.Sets/src/mage/cards/c/CoffinPuppets.java +++ b/Mage.Sets/src/mage/cards/c/CoffinPuppets.java @@ -1,9 +1,7 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.condition.CompoundCondition; import mage.abilities.condition.Condition; @@ -22,18 +20,28 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class CoffinPuppets extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("you control a Swamp"); + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("you control a Swamp"); + private static final FilterControlledPermanent filter2 + = new FilterControlledLandPermanent("two lands"); static { filter.add(new SubtypePredicate(SubType.SWAMP)); } + private static final Condition condition = new CompoundCondition( + "during your upkeep and only if you control a Swamp", + new PermanentsOnTheBattlefieldCondition(filter), + new IsStepCondition(PhaseStep.UPKEEP) + ); + public CoffinPuppets(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); @@ -42,12 +50,13 @@ public final class CoffinPuppets extends CardImpl { this.toughness = new MageInt(3); // Sacrifice two lands: Return Coffin Puppets from your graveyard to the battlefield. Activate this ability only during your upkeep and only if you control a Swamp. - Condition condition = new CompoundCondition("during your upkeep and only if you control a Swamp",new PermanentsOnTheBattlefieldCondition(filter), new IsStepCondition(PhaseStep.UPKEEP)); - Ability ability = new ActivateIfConditionActivatedAbility(Zone.GRAVEYARD, + this.addAbility(new ActivateIfConditionActivatedAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), - new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("two lands"), true)), - condition); - this.addAbility(ability); + new SacrificeTargetCost( + new TargetControlledPermanent(2, 2, filter2, true) + ), condition + )); } public CoffinPuppets(final CoffinPuppets card) { diff --git a/Mage.Sets/src/mage/cards/c/CoilingWoodworm.java b/Mage.Sets/src/mage/cards/c/CoilingWoodworm.java index 489ce9d8c6..b56c057680 100644 --- a/Mage.Sets/src/mage/cards/c/CoilingWoodworm.java +++ b/Mage.Sets/src/mage/cards/c/CoilingWoodworm.java @@ -1,9 +1,9 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.SetPowerSourceEffect; import mage.cards.CardImpl; @@ -13,19 +13,17 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class CoilingWoodworm extends CardImpl { - final static FilterPermanent filterLands = new FilterPermanent("Forests you control"); - - static { - filterLands.add(new SubtypePredicate(SubType.FOREST)); - } + private static final DynamicValue count = new PermanentsOnBattlefieldCount( + new FilterPermanent(SubType.FOREST, "Forests on the battlefield") + ); public CoilingWoodworm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); @@ -36,10 +34,10 @@ public final class CoilingWoodworm extends CardImpl { this.toughness = new MageInt(1); // Coiling Woodworm's power is equal to the number of Forests on the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect(new PermanentsOnBattlefieldCount(filterLands), Duration.EndOfGame))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect(count, Duration.EndOfGame))); } - public CoilingWoodworm(final CoilingWoodworm card) { + private CoilingWoodworm(final CoilingWoodworm card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/CommonCause.java b/Mage.Sets/src/mage/cards/c/CommonCause.java index 3fd5e226bb..bfcff613fe 100644 --- a/Mage.Sets/src/mage/cards/c/CommonCause.java +++ b/Mage.Sets/src/mage/cards/c/CommonCause.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -12,15 +11,15 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class CommonCause extends CardImpl { @@ -35,9 +34,11 @@ public final class CommonCause extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); // Nonartifact creatures get +2/+2 as long as they all share a color. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostAllEffect(2, 2, Duration.WhileOnBattlefield, filter, false), - new AllColorCondition(), - "nonartifact creatures get +2/+2 as long as they all share a color."))); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostAllEffect(2, 2, Duration.WhileOnBattlefield, filter, false), + AllColorCondition.instance, + "nonartifact creatures get +2/+2 as long as they all share a color.") + )); } public CommonCause(final CommonCause card) { @@ -50,7 +51,8 @@ public final class CommonCause extends CardImpl { } } -class AllColorCondition implements Condition { +enum AllColorCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { @@ -58,7 +60,7 @@ class AllColorCondition implements Condition { filter.add(Predicates.not(new CardTypePredicate(CardType.ARTIFACT))); ObjectColor allColor = new ObjectColor("WUBRG"); for (Permanent thing : game.getBattlefield().getAllActivePermanents(filter, game)) { - allColor = allColor.intersection(thing.getColor(game)); + allColor = allColor.intersection(thing.getColor(game)); } return !allColor.isColorless(); } diff --git a/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java b/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java index 3697c4ab9f..012152970d 100644 --- a/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java +++ b/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java @@ -1,27 +1,31 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class ComplexAutomaton extends CardImpl { + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT, ComparisonType.MORE_THAN, 6 + ); + public ComplexAutomaton(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); @@ -30,12 +34,16 @@ public final class ComplexAutomaton extends CardImpl { this.toughness = new MageInt(4); // At the beginning of your upkeep, if you control seven or more permanents, return Complex Automaton to its owner's hand. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new ReturnToHandSourceEffect(true), TargetController.YOU, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(), ComparisonType.MORE_THAN, 6), - "At the beginning of your upkeep, if you control seven or more permanents, return Complex Automaton to its owner's hand.")); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility( + new ReturnToHandSourceEffect(true), + TargetController.YOU, false + ), condition, "At the beginning of your upkeep, " + + "if you control seven or more permanents, return {this} to its owner's hand." + )); } - public ComplexAutomaton(final ComplexAutomaton card) { + private ComplexAutomaton(final ComplexAutomaton card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/c/CrookclawElder.java b/Mage.Sets/src/mage/cards/c/CrookclawElder.java index 54f59b87c5..6b2a76e853 100644 --- a/Mage.Sets/src/mage/cards/c/CrookclawElder.java +++ b/Mage.Sets/src/mage/cards/c/CrookclawElder.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,28 +13,29 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class CrookclawElder extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Bird you control"); - private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("untapped Wizards you control"); + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent(SubType.BIRD, "untapped Birds you control"); + private static final FilterControlledCreaturePermanent filter2 + = new FilterControlledCreaturePermanent(SubType.WIZARD, "untapped Wizards you control"); + private static final Predicate pred = Predicates.not(TappedPredicate.instance); static { - filter.add(new SubtypePredicate(SubType.BIRD)); - filter.add(Predicates.not(TappedPredicate.instance)); - filter2.add(new SubtypePredicate(SubType.WIZARD)); - filter2.add(Predicates.not(TappedPredicate.instance)); + filter.add(pred); + filter2.add(pred); } public CrookclawElder(UUID ownerId, CardSetInfo setInfo) { @@ -50,16 +50,26 @@ public final class CrookclawElder extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Tap two untapped Birds you control: Draw a card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter, true))); + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), + new TapTargetCost(new TargetControlledCreaturePermanent( + 2, 2, filter, true + )) + ); this.addAbility(ability); // Tap two untapped Wizards you control: Target creature gains flying until end of turn. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter2, true))); + ability = new SimpleActivatedAbility( + new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), + new TapTargetCost(new TargetControlledCreaturePermanent( + 2, 2, filter2, true + )) + ); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } - public CrookclawElder(final CrookclawElder card) { + private CrookclawElder(final CrookclawElder card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/d/DarkTriumph.java b/Mage.Sets/src/mage/cards/d/DarkTriumph.java index d2c98f3027..4af43095ae 100644 --- a/Mage.Sets/src/mage/cards/d/DarkTriumph.java +++ b/Mage.Sets/src/mage/cards/d/DarkTriumph.java @@ -1,7 +1,7 @@ package mage.cards.d; -import java.util.UUID; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -11,30 +11,30 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.FilterPermanent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; + /** - * * @author TheElk801 */ public final class DarkTriumph extends CardImpl { - private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("If you control a Swamp"); - - static { - filterSwamp.add(new SubtypePredicate(SubType.SWAMP)); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterPermanent(SubType.SWAMP, "If you control a Swamp") + ); public DarkTriumph(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); // If you control a Swamp, you may sacrifice a creature rather than pay Dark Triumph's mana cost. this.addAbility(new AlternativeCostSourceAbility( - new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT)), - new PermanentsOnTheBattlefieldCondition(filterSwamp), null + new SacrificeTargetCost( + new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + ), condition )); // Creatures you control get +2/+0 until end of turn. diff --git a/Mage.Sets/src/mage/cards/d/Delraich.java b/Mage.Sets/src/mage/cards/d/Delraich.java index 4691596016..135de65e67 100644 --- a/Mage.Sets/src/mage/cards/d/Delraich.java +++ b/Mage.Sets/src/mage/cards/d/Delraich.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; @@ -15,13 +14,15 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Delraich extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("black creature"); + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("black creature"); static { filter.add(new ColorPredicate(ObjectColor.BLACK)); @@ -34,15 +35,16 @@ public final class Delraich extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); + // You may sacrifice three black creatures rather than pay Delraich's mana cost. + this.addAbility(new AlternativeCostSourceAbility(new SacrificeTargetCost( + new TargetControlledPermanent(3, 3, filter, false) + ))); + // Trample this.addAbility(TrampleAbility.getInstance()); - - // You may sacrifice three black creatures rather than pay Delraich's mana cost. - AlternativeCostSourceAbility alternateCosts = new AlternativeCostSourceAbility(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, filter, false))); - this.addAbility(alternateCosts); } - public Delraich(final Delraich card) { + private Delraich(final Delraich card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/d/DestructiveUrge.java b/Mage.Sets/src/mage/cards/d/DestructiveUrge.java index a861a81c13..1ac31870ad 100644 --- a/Mage.Sets/src/mage/cards/d/DestructiveUrge.java +++ b/Mage.Sets/src/mage/cards/d/DestructiveUrge.java @@ -1,23 +1,23 @@ package mage.cards.d; -import java.util.UUID; -import mage.target.common.TargetCreaturePermanent; import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.SacrificeEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterLandPermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class DestructiveUrge extends CardImpl { @@ -35,8 +35,10 @@ public final class DestructiveUrge extends CardImpl { this.addAbility(ability); // Whenever enchanted creature deals combat damage to a player, that player sacrifices a land. - ability = new DealsDamageToAPlayerAttachedTriggeredAbility(new SacrificeEffect(new FilterLandPermanent(), 1, "that player"), "enchanted", false, true); - this.addAbility(ability); + this.addAbility(new DealsDamageToAPlayerAttachedTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that player"), + "enchanted", false, true + )); } public DestructiveUrge(final DestructiveUrge card) { diff --git a/Mage.Sets/src/mage/cards/d/DivineCongregation.java b/Mage.Sets/src/mage/cards/d/DivineCongregation.java index d135de316e..2d60861dc1 100644 --- a/Mage.Sets/src/mage/cards/d/DivineCongregation.java +++ b/Mage.Sets/src/mage/cards/d/DivineCongregation.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; @@ -10,13 +9,14 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DivineCongregation extends CardImpl { @@ -32,7 +32,7 @@ public final class DivineCongregation extends CardImpl { this.addAbility(new SuspendAbility(5, new ManaCostsImpl("{1}{W}"), this)); } - public DivineCongregation(final DivineCongregation card) { + private DivineCongregation(final DivineCongregation card) { super(card); } @@ -44,12 +44,12 @@ public final class DivineCongregation extends CardImpl { class DivineCongregationEffect extends OneShotEffect { - public DivineCongregationEffect() { + DivineCongregationEffect() { super(Outcome.Benefit); staticText = "You gain 2 life for each creature target player controls"; } - public DivineCongregationEffect(final DivineCongregationEffect effect) { + private DivineCongregationEffect(final DivineCongregationEffect effect) { super(effect); } @@ -63,7 +63,7 @@ class DivineCongregationEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getFirstTarget()); if (controller != null && player != null) { - int critters = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game).size(); + int critters = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game).size(); controller.gainLife(2 * critters, game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/e/Epicenter.java b/Mage.Sets/src/mage/cards/e/Epicenter.java index 9f2a1f4f4d..aa54475f9d 100644 --- a/Mage.Sets/src/mage/cards/e/Epicenter.java +++ b/Mage.Sets/src/mage/cards/e/Epicenter.java @@ -1,8 +1,6 @@ package mage.cards.e; -import java.util.Iterator; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.CardsInControllerGraveCondition; @@ -18,8 +16,10 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPlayer; +import java.util.Iterator; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Epicenter extends CardImpl { @@ -31,12 +31,15 @@ public final class Epicenter extends CardImpl { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "Target player"), new InvertCondition(new CardsInControllerGraveCondition(7)), - "Target player sacrifices a land")); + "Target player sacrifices a land" + )); // Threshold - Each player sacrifices all lands he or she controls instead if seven or more cards are in your graveyard. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new EpicenterEffect(), new CardsInControllerGraveCondition(7), - "

Threshold — Each player sacrifices all lands he or she controls instead if seven or more cards are in your graveyard.")); + "

Threshold — Each player sacrifices all lands they control instead " + + "if seven or more cards are in your graveyard." + )); this.getSpellAbility().addTarget(new TargetPlayer()); } @@ -55,10 +58,9 @@ class EpicenterEffect extends OneShotEffect { EpicenterEffect() { super(Outcome.DestroyPermanent); - staticText = "Each player sacrifices all lands he or she controls"; } - EpicenterEffect(final EpicenterEffect effect) { + private EpicenterEffect(final EpicenterEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/i/InfestedRoothold.java b/Mage.Sets/src/mage/cards/i/InfestedRoothold.java index c916d6075f..fd3c10075c 100644 --- a/Mage.Sets/src/mage/cards/i/InfestedRoothold.java +++ b/Mage.Sets/src/mage/cards/i/InfestedRoothold.java @@ -1,7 +1,6 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -11,22 +10,21 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterCard; import mage.filter.FilterSpell; import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.permanent.token.InsectToken; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class InfestedRoothold extends CardImpl { - private final static FilterSpell filter = new FilterSpell("an artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } + private static final FilterCard filter = new FilterArtifactCard("artifacts"); + private static final FilterSpell filter2 = new FilterArtifactSpell("an artifact spell"); public InfestedRoothold(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); @@ -39,10 +37,12 @@ public final class InfestedRoothold extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // Protection from artifacts - this.addAbility(new ProtectionAbility(new FilterArtifactCard("artifacts"))); + this.addAbility(new ProtectionAbility(filter)); // Whenever an opponent casts an artifact spell, you may create a 1/1 green Insect creature token. - this.addAbility(new SpellCastOpponentTriggeredAbility(new CreateTokenEffect(new InsectToken()), filter, true)); + this.addAbility(new SpellCastOpponentTriggeredAbility( + new CreateTokenEffect(new InsectToken()), filter2, true) + ); } diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java b/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java index 737385b8fe..f959f3abb8 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java +++ b/Mage.Sets/src/mage/cards/m/MirrorOfTheForebears.java @@ -1,35 +1,39 @@ package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseCreatureTypeEffect; -import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; +import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; 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.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ChosenSubtypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.targetpointer.FixedTarget; import mage.util.functions.EmptyApplyToPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class MirrorOfTheForebears extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new ChosenSubtypePredicate()); + } + public MirrorOfTheForebears(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); @@ -37,14 +41,12 @@ public final class MirrorOfTheForebears extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.Copy))); // 1: Until end of turn, Mirror of the Forebears becomes a copy of target creature you control of the chosen type, except it's an artifact in addition to its other types. - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - filter.add(new ChosenSubtypePredicate()); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MirrorOfTheForebearsCopyEffect(), new ManaCostsImpl("{1}")); + Ability ability = new SimpleActivatedAbility(new MirrorOfTheForebearsCopyEffect(), new GenericManaCost(1)); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } - public MirrorOfTheForebears(final MirrorOfTheForebears card) { + private MirrorOfTheForebears(final MirrorOfTheForebears card) { super(card); } @@ -56,12 +58,12 @@ public final class MirrorOfTheForebears extends CardImpl { class MirrorOfTheForebearsCopyEffect extends OneShotEffect { - public MirrorOfTheForebearsCopyEffect() { + MirrorOfTheForebearsCopyEffect() { super(Outcome.Copy); this.staticText = "until end of turn, {this} becomes a copy of target creature you control of the chosen type, except it's an artifact in addition to its other types"; } - public MirrorOfTheForebearsCopyEffect(final MirrorOfTheForebearsCopyEffect effect) { + private MirrorOfTheForebearsCopyEffect(final MirrorOfTheForebearsCopyEffect effect) { super(effect); } @@ -76,11 +78,7 @@ class MirrorOfTheForebearsCopyEffect extends OneShotEffect { Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (sourcePermanent != null && copyFromPermanent != null) { game.copyPermanent(Duration.EndOfTurn, copyFromPermanent, sourcePermanent.getId(), source, new EmptyApplyToPermanent()); - if (!copyFromPermanent.isArtifact()) { - ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.EndOfTurn, CardType.ARTIFACT); - effect.setTargetPointer(new FixedTarget(sourcePermanent, game)); - game.addEffect(effect, source); - } + game.addEffect(new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT), source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/SecondSight.java b/Mage.Sets/src/mage/cards/s/SecondSight.java index 80b3cb43df..b3daccbbbf 100644 --- a/Mage.Sets/src/mage/cards/s/SecondSight.java +++ b/Mage.Sets/src/mage/cards/s/SecondSight.java @@ -1,10 +1,8 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LookLibraryControllerEffect; import mage.abilities.keyword.EntwineAbility; @@ -18,8 +16,9 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SecondSight extends CardImpl { @@ -32,22 +31,17 @@ public final class SecondSight extends CardImpl { this.getSpellAbility().getModes().setMaxModes(1); //Look at the top five cards of target opponent's library, then put them back in any order; - Effect effect = new SecondSightEffect(); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new SecondSightEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); //or look at the top five cards of your library, then put them back in any order. - effect = new LookLibraryControllerEffect(5); - Mode mode = new Mode(); - mode.addEffect(effect); - this.getSpellAbility().getModes().addMode(mode); + this.getSpellAbility().getModes().addMode(new Mode(new LookLibraryControllerEffect(5))); // Entwine {U} this.addAbility(new EntwineAbility("{U}")); - } - public SecondSight(final SecondSight card) { + private SecondSight(final SecondSight card) { super(card); } @@ -59,12 +53,12 @@ public final class SecondSight extends CardImpl { class SecondSightEffect extends OneShotEffect { - public SecondSightEffect() { + SecondSightEffect() { super(Outcome.DrawCard); this.staticText = "look at the top five cards of target opponent's library, then put them back in any order"; } - public SecondSightEffect(final SecondSightEffect effect) { + private SecondSightEffect(final SecondSightEffect effect) { super(effect); } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index a21da63a26..fef00127f6 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -85,6 +85,12 @@ public final class StaticFilters { FILTER_CARD_CREATURE_YOUR_GRAVEYARD.setLockedFilter(true); } + public static final FilterCreatureCard FILTER_CARD_CREATURES_YOUR_GRAVEYARD = new FilterCreatureCard("creature cards from your graveyard"); + + static { + FILTER_CARD_CREATURES_YOUR_GRAVEYARD.setLockedFilter(true); + } + public static final FilterCard FILTER_CARD_FROM_YOUR_GRAVEYARD = new FilterCard("card from your graveyard"); static { From d07415e08c635b7889385a45cd00c5f1f6869dec Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sat, 26 Jan 2019 16:19:44 -0600 Subject: [PATCH 20/21] - Fixed #5440 --- Mage/src/main/java/mage/game/stack/Spell.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index bfc8fce81c..984535269d 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -989,6 +989,7 @@ public class Spell extends StackObjImpl implements Card { @Override public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { Spell copy = this.copySpell(newControllerId); + game.getState().setZone(copy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental game.getStack().push(copy); if (chooseNewTargets) { copy.chooseNewTargets(game, newControllerId); From 3b1a9388ac1e3472c23403421ef08bb05c3c27cd Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sat, 26 Jan 2019 23:47:56 -0600 Subject: [PATCH 21/21] - null pointer Nahin, The Harbinger. --- .../src/mage/cards/n/NahiriTheHarbinger.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java index 1ba32622d6..2e0fba2b4c 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -49,13 +48,13 @@ public final class NahiriTheHarbinger extends CardImpl { static { filter.add(Predicates.or(new CardTypePredicate(CardType.ENCHANTMENT), (Predicates.and(new CardTypePredicate(CardType.ARTIFACT), - TappedPredicate.instance)), + TappedPredicate.instance)), (Predicates.and(new CardTypePredicate(CardType.CREATURE), - TappedPredicate.instance)))); + TappedPredicate.instance)))); } public NahiriTheHarbinger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{R}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{W}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NAHIRI); @@ -114,16 +113,18 @@ class NahiriTheHarbingerEffect extends SearchEffect { if (controller.searchLibrary(target, game)) { if (!target.getTargets().isEmpty()) { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); - game.addEffect(effect, source); - Effect effect2 = new ReturnToHandTargetEffect(); - effect2.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect2); - game.addDelayedTriggeredAbility(delayedAbility, source); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); + game.addEffect(effect, source); + Effect effect2 = new ReturnToHandTargetEffect(); + effect2.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect2); + game.addDelayedTriggeredAbility(delayedAbility, source); + } } } controller.shuffleLibrary(source, game);