diff --git a/Mage.Sets/src/mage/cards/b/BloodMoon.java b/Mage.Sets/src/mage/cards/b/BloodMoon.java index ca52c9989c7..1eef0357ac4 100644 --- a/Mage.Sets/src/mage/cards/b/BloodMoon.java +++ b/Mage.Sets/src/mage/cards/b/BloodMoon.java @@ -1,11 +1,14 @@ - package mage.cards.b; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -22,7 +25,7 @@ import mage.game.permanent.Permanent; public final class BloodMoon extends CardImpl { public BloodMoon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // Nonbasic lands are Mountains. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BloodMoonEffect())); @@ -72,12 +75,28 @@ class BloodMoonEffect extends ContinuousEffectImpl { case TypeChangingEffects_4: // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects // So the ability removing has to be done before Layer 6 - land.removeAllAbilities(source.getSourceId(), game); - land.getSubtype(game).removeAll(SubType.getLandTypes(false)); + //land.getSubtype(game).removeAll(SubType.getLandTypes(false)); + land.getSubtype(game).clear(); land.getSubtype(game).add(SubType.MOUNTAIN); + land.removeAllAbilities(source.getSourceId(), game); break; case AbilityAddingRemovingEffects_6: - land.addAbility(new RedManaAbility(), source.getSourceId(), game); + land.removeAllAbilities(source.getSourceId(), game); + if (land.getSubtype(game).contains(SubType.FOREST)) { + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.PLAINS)) { + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.ISLAND)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.SWAMP)) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } break; } } @@ -86,6 +105,7 @@ class BloodMoonEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/c/CavalcadeOfCalamity.java b/Mage.Sets/src/mage/cards/c/CavalcadeOfCalamity.java new file mode 100644 index 00000000000..eabf9118a8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CavalcadeOfCalamity.java @@ -0,0 +1,72 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CavalcadeOfCalamity extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creaure you control with power 1 or less"); + + static { + filter.add(new ControllerPredicate(TargetController.YOU)); + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 2)); + } + + public CavalcadeOfCalamity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); + + // Whenever a creature you control with power 1 or less attacks, Cavalcade of Calamity deals 1 damage to the player or planeswalker that creature is attacking. + this.addAbility(new AttacksAllTriggeredAbility( + new CavalcadeOfCalamityEffect(), false, filter, + SetTargetPointer.PERMANENT, false, false + )); + } + + private CavalcadeOfCalamity(final CavalcadeOfCalamity card) { + super(card); + } + + @Override + public CavalcadeOfCalamity copy() { + return new CavalcadeOfCalamity(this); + } +} + +class CavalcadeOfCalamityEffect extends OneShotEffect { + + CavalcadeOfCalamityEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 1 damage to the player or planeswalker that creature is attacking."; + } + + private CavalcadeOfCalamityEffect(final CavalcadeOfCalamityEffect effect) { + super(effect); + } + + @Override + public CavalcadeOfCalamityEffect copy() { + return new CavalcadeOfCalamityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return game.damagePlayerOrPlaneswalker( + game.getCombat().getDefenderId(source.getFirstTarget()), 1, + source.getSourceId(), game, false, true + ) > 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/Cindervines.java b/Mage.Sets/src/mage/cards/c/Cindervines.java new file mode 100644 index 00000000000..baff58a2668 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Cindervines.java @@ -0,0 +1,88 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Cindervines extends CardImpl { + + public Cindervines(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{G}"); + + // Whenever an opponent casts a noncreature spell, Cindervines deals 1 damage to that player. + this.addAbility(new SpellCastOpponentTriggeredAbility( + Zone.BATTLEFIELD, new DamageTargetEffect(1, false, "that player"), + StaticFilters.FILTER_SPELL_NON_CREATURE, false, SetTargetPointer.PLAYER + )); + + // {1}, Sacrifice Cindervines: Destroy target artifact or enchantment. Cindervines deals 2 damage to that permanent's controller. + Ability ability = new SimpleActivatedAbility( + new CindervinesEffect(), new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.addAbility(ability); + } + + private Cindervines(final Cindervines card) { + super(card); + } + + @Override + public Cindervines copy() { + return new Cindervines(this); + } +} + +class CindervinesEffect extends OneShotEffect { + + CindervinesEffect() { + super(Outcome.Benefit); + staticText = "Destroy target artifact or enchantment. " + + "{this} deals 2 damage to that permanent's controller."; + } + + private CindervinesEffect(final CindervinesEffect effect) { + super(effect); + } + + @Override + public CindervinesEffect copy() { + return new CindervinesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + if (player == null) { + return false; + } + permanent.destroy(source.getSourceId(), game, false); + player.damage(2, source.getSourceId(), game); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ClamorShaman.java b/Mage.Sets/src/mage/cards/c/ClamorShaman.java new file mode 100644 index 00000000000..3d8e5b0fea4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClamorShaman.java @@ -0,0 +1,49 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.abilities.keyword.RiotAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ClamorShaman extends CardImpl { + + public ClamorShaman(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Riot + this.addAbility(new RiotAbility()); + + // Whenever Clamor Shaman attacks, target creature an opponent controls can't block this turn. + Ability ability = new AttacksTriggeredAbility( + new CantBlockTargetEffect(Duration.EndOfTurn), false + ); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private ClamorShaman(final ClamorShaman card) { + super(card); + } + + @Override + public ClamorShaman copy() { + return new ClamorShaman(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/Conversion.java b/Mage.Sets/src/mage/cards/c/Conversion.java index 24f0ef8d1f5..8731aaba764 100644 --- a/Mage.Sets/src/mage/cards/c/Conversion.java +++ b/Mage.Sets/src/mage/cards/c/Conversion.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.List; @@ -13,11 +12,14 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -27,13 +29,15 @@ import mage.game.permanent.Permanent; */ public final class Conversion extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.MOUNTAIN, "Mountains"); - public Conversion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // At the beginning of your upkeep, sacrifice Conversion unless you pay {W}{W}. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl("{W}{W}")), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new SacrificeSourceUnlessPaysEffect( + new ManaCostsImpl("{W}{W}")), + TargetController.YOU, + false)); // All Mountains are Plains. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConversionEffect())); @@ -72,15 +76,45 @@ public final class Conversion extends CardImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent land : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + for (Permanent land : game.getBattlefield().getAllActivePermanents(CardType.LAND)) { switch (layer) { - case AbilityAddingRemovingEffects_6: - land.removeAllAbilities(source.getSourceId(), game); - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - break; case TypeChangingEffects_4: - land.getSubtype(game).clear(); - land.getSubtype(game).add(SubType.PLAINS); + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + land.getSubtype(game).clear(); + land.getSubtype(game).add(SubType.PLAINS); + game.getState().setValue("conversion" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game), + "true"); + } + break; + case AbilityAddingRemovingEffects_6: + if (game.getState().getValue("conversion" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game)) != null + && game.getState().getValue("conversion" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game)).equals("true")) { + land.removeAllAbilities(source.getSourceId(), game); + if (land.getSubtype(game).contains(SubType.FOREST)) { + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.PLAINS)) { + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.ISLAND)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.SWAMP)) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } + } break; } } @@ -89,20 +123,17 @@ public final class Conversion extends CardImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.TypeChangingEffects_4; } @Override public Set isDependentTo(List allEffectsInLayer) { - // the dependent classes needs to be an enclosed class for dependent check of continuous effects return allEffectsInLayer .stream() - .filter(effect->effect.getDependencyTypes().contains(DependencyType.BecomeMountain)) + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomePlains)) .map(Effect::getId) .collect(Collectors.toSet()); - } - } - } diff --git a/Mage.Sets/src/mage/cards/e/EliteArrester.java b/Mage.Sets/src/mage/cards/e/EliteArrester.java new file mode 100644 index 00000000000..570589d44e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EliteArrester.java @@ -0,0 +1,47 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EliteArrester extends CardImpl { + + public EliteArrester(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // {1}{U}, {T}: Tap target creature. + Ability ability = new SimpleActivatedAbility( + new TapTargetEffect(), new ManaCostsImpl("{1}{U}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private EliteArrester(final EliteArrester card) { + super(card); + } + + @Override + public EliteArrester copy() { + return new EliteArrester(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EyesEverywhere.java b/Mage.Sets/src/mage/cards/e/EyesEverywhere.java new file mode 100644 index 00000000000..701e6ef5606 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EyesEverywhere.java @@ -0,0 +1,53 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.ExchangeControlTargetEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EyesEverywhere extends CardImpl { + + public EyesEverywhere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // At the beginning of your upkeep, scry 1. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new ScryEffect(1), + TargetController.YOU, false + )); + + // {5}{U}: Exchange control of Eyes Everywhere and target nonland permanent. Activate this ability only any time you could cast a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + Zone.BATTLEFIELD, + new ExchangeControlTargetEffect( + Duration.EndOfGame, "Exchange control of {this} " + + "and target nonland permanent", true + ), new ManaCostsImpl("{5}{U}") + ); + ability.addTarget(new TargetNonlandPermanent()); + this.addAbility(ability); + } + + private EyesEverywhere(final EyesEverywhere card) { + super(card); + } + + @Override + public EyesEverywhere copy() { + return new EyesEverywhere(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java b/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java new file mode 100644 index 00000000000..b96e38211d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForbiddingSpirit.java @@ -0,0 +1,47 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ForbiddingSpirit extends CardImpl { + + public ForbiddingSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Forbidding Spirit enters the battlefield, until your next turn, creatures can't attack you or a planeswalker you control unless their controller pays {2} for each of those creatures. + ContinuousEffect effect = new CantAttackYouUnlessPayManaAllEffect( + new ManaCostsImpl("{2}"), true + ); + effect.setDuration(Duration.UntilYourNextTurn); + effect.setText("until your next turn, creatures can't attack you or a planeswalker you control " + + "unless their controller pays {2} for each of those creatures."); + this.addAbility(new EntersBattlefieldTriggeredAbility(effect)); + } + + private ForbiddingSpirit(final ForbiddingSpirit card) { + super(card); + } + + @Override + public ForbiddingSpirit copy() { + return new ForbiddingSpirit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GatebreakerRam.java b/Mage.Sets/src/mage/cards/g/GatebreakerRam.java new file mode 100644 index 00000000000..5a9a159ea42 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GatebreakerRam.java @@ -0,0 +1,76 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GatebreakerRam extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(new SubtypePredicate(SubType.GATE)); + } + + private static final DynamicValue xValue + = new PermanentsOnBattlefieldCount(filter); + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1); + + public GatebreakerRam(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.SHEEP); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Gatebreaker Ram gets +1/+1 for each Gate you control. + this.addAbility(new SimpleStaticAbility( + new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield) + .setText("{this} gets +1/+1 for each Gate you control.") + )); + + // As long as you control two or more Gates, Gatebreaker Ram has vigilance and trample. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(VigilanceAbility.getInstance()), + condition, "As long as you control two or more Gates, {this} has vigilance" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance()), + condition, "and trample" + )); + this.addAbility(ability); + } + + private GatebreakerRam(final GatebreakerRam card) { + super(card); + } + + @Override + public GatebreakerRam copy() { + return new GatebreakerRam(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GatewaySneak.java b/Mage.Sets/src/mage/cards/g/GatewaySneak.java new file mode 100644 index 00000000000..9d0596cb775 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GatewaySneak.java @@ -0,0 +1,51 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GatewaySneak extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.GATE, "a Gate"); + + public GatewaySneak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.VEDALKEN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever a Gate enters the battlefield under your control, Gateway Sneak can't be blocked this turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new CantBeBlockedSourceEffect(Duration.EndOfTurn), filter + )); + + // Whenever Gateway Sneak deals combat damage to a player, draw a card. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), false + )); + } + + private GatewaySneak(final GatewaySneak card) { + super(card); + } + + @Override + public GatewaySneak copy() { + return new GatewaySneak(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/Glaciers.java b/Mage.Sets/src/mage/cards/g/Glaciers.java index d0c6fa2be8a..6d2ae05de93 100644 --- a/Mage.Sets/src/mage/cards/g/Glaciers.java +++ b/Mage.Sets/src/mage/cards/g/Glaciers.java @@ -12,24 +12,23 @@ import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; - import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; /** * * @author jmharmon */ - public final class Glaciers extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.MOUNTAIN, "Mountains"); - public Glaciers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{U}"); @@ -72,15 +71,44 @@ public final class Glaciers extends CardImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent land : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + for (Permanent land : game.getBattlefield().getAllActivePermanents(CardType.LAND)) { switch (layer) { - case AbilityAddingRemovingEffects_6: - land.removeAllAbilities(source.getSourceId(), game); - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - break; case TypeChangingEffects_4: - land.getSubtype(game).clear(); - land.getSubtype(game).add(SubType.PLAINS); + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + land.getSubtype(game).clear(); + land.getSubtype(game).add(SubType.PLAINS); + game.getState().setValue("glaciers" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game), "true"); + } + break; + case AbilityAddingRemovingEffects_6: + if (game.getState().getValue("glaciers" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game)) != null + && game.getState().getValue("glaciers" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game)).equals("true")) { + land.removeAllAbilities(source.getSourceId(), game); + if (land.getSubtype(game).contains(SubType.FOREST)) { + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.PLAINS)) { + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.ISLAND)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + if (land.getSubtype(game).contains(SubType.SWAMP)) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } + } break; } } @@ -89,14 +117,15 @@ public final class Glaciers extends CardImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.TypeChangingEffects_4; } @Override public Set isDependentTo(List allEffectsInLayer) { return allEffectsInLayer .stream() - .filter(effect->effect.getDependencyTypes().contains(DependencyType.BecomeMountain)) + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomePlains)) .map(Effect::getId) .collect(Collectors.toSet()); } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java index 102e49dc619..d61f2916e11 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java @@ -89,76 +89,46 @@ class IllusionaryTerrainEffect extends ContinuousEffectImpl { && firstChoice != null && secondChoice != null) { for (Permanent land : lands) { - if (land != null - && land.isBasic()) { + if (land.isBasic()) { switch (layer) { case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { + if (land.getSubtype(game).contains(firstChoice)) { land.getSubtype(game).clear(); land.getSubtype(game).add(secondChoice); + game.getState().setValue("illusionaryTerrain" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game), + "true"); } break; case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - boolean addAbility = true; - land.getAbilities().clear(); - if (secondChoice.equals(SubType.FOREST)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof GreenManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } + if (game.getState().getValue("illusionaryTerrain" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game)) != null + && game.getState().getValue("illusionaryTerrain" + + source.getId() + + land.getId() + + land.getZoneChangeCounter(game)).equals("true")) { + land.removeAllAbilities(source.getSourceId(), game); + if (land.getSubtype(game).contains(SubType.FOREST)) { + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); } - if (secondChoice.equals(SubType.PLAINS)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof WhiteManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } + if (land.getSubtype(game).contains(SubType.PLAINS)) { + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); } - if (secondChoice.equals(SubType.MOUNTAIN)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof RedManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } + if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + land.addAbility(new RedManaAbility(), source.getSourceId(), game); } - if (secondChoice.equals(SubType.ISLAND)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof BlueManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } + if (land.getSubtype(game).contains(SubType.ISLAND)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); } - if (secondChoice.equals(SubType.SWAMP)) { - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof BlackManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } + if (land.getSubtype(game).contains(SubType.SWAMP)) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); } + break; } - break; } } } @@ -174,8 +144,9 @@ class IllusionaryTerrainEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.TypeChangingEffects_4; + return layer == Layer.TypeChangingEffects_4 + || layer == Layer.AbilityAddingRemovingEffects_6; + } } diff --git a/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java b/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java new file mode 100644 index 00000000000..8233331a889 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KnightOfTheLastBreath.java @@ -0,0 +1,63 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.AfterlifeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.WhiteBlackSpiritToken; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KnightOfTheLastBreath extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("another nontoken creature"); + + static { + filter.add(new AnotherPredicate()); + filter.add(Predicates.not(new TokenPredicate())); + } + + public KnightOfTheLastBreath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{B}"); + + this.subtype.add(SubType.GIANT); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // {3}, Sacrifice another nontoken creature: Create a 1/1 white and black spirit creature token with flying. + Ability ability = new SimpleActivatedAbility( + new CreateTokenEffect(new WhiteBlackSpiritToken()), new GenericManaCost(3) + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + + // Afterlife 3 + this.addAbility(new AfterlifeAbility(3)); + } + + private KnightOfTheLastBreath(final KnightOfTheLastBreath card) { + super(card); + } + + @Override + public KnightOfTheLastBreath copy() { + return new KnightOfTheLastBreath(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MacabreMockery.java b/Mage.Sets/src/mage/cards/m/MacabreMockery.java new file mode 100644 index 00000000000..519dffff174 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MacabreMockery.java @@ -0,0 +1,92 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInOpponentsGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MacabreMockery extends CardImpl { + + public MacabreMockery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{R}"); + + // Put target creature card from an opponent's graveyard onto the battlefield under your control. It gets +2/+0 and gains haste until end of turn. Sacrifice it at the beginning of the next end step. + this.getSpellAbility().addEffect(new MacabreMockeryEffect()); + this.getSpellAbility().addTarget(new TargetCardInOpponentsGraveyard(StaticFilters.FILTER_CARD_CREATURE)); + } + + private MacabreMockery(final MacabreMockery card) { + super(card); + } + + @Override + public MacabreMockery copy() { + return new MacabreMockery(this); + } +} + +class MacabreMockeryEffect extends OneShotEffect { + + MacabreMockeryEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "Put target creature card from an opponent's graveyard onto the battlefield under your control. " + + "It gets +2/+0 and gains haste until end of turn. Sacrifice it at the beginning of the next end step."; + } + + private MacabreMockeryEffect(final MacabreMockeryEffect effect) { + super(effect); + } + + @Override + public MacabreMockeryEffect copy() { + return new MacabreMockeryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card == null) { + return false; + } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + return false; + } + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + return false; + } + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + effect = new BoostTargetEffect(2, 0, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + Effect sacrificeEffect = new SacrificeTargetEffect("exile " + permanent.getLogName()); + sacrificeEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + sacrificeEffect, TargetController.YOU + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java index f5bb97875ca..cb425f1d88e 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java @@ -125,7 +125,8 @@ class PhantasmalTerrainContinuousEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage.Sets/src/mage/cards/p/PlazaOfHarmony.java b/Mage.Sets/src/mage/cards/p/PlazaOfHarmony.java new file mode 100644 index 00000000000..d88d7c09816 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PlazaOfHarmony.java @@ -0,0 +1,62 @@ +package mage.cards.p; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.mana.AnyColorLandsProduceManaAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PlazaOfHarmony extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final FilterPermanent filter2 = new FilterPermanent(SubType.GATE, "Gate"); + + static { + filter.add(new SubtypePredicate(SubType.GATE)); + } + + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1); + + public PlazaOfHarmony(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // When Plaza of Harmony enters the battlefield, if you control two or more Gates, you gain 3 life. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)), + condition, "When {this} enters the battlefield, " + + "if you control two or more Gates, you gain 3 life." + )); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {T}: Add one mana of any type a Gate you control could produce. + this.addAbility(new AnyColorLandsProduceManaAbility(TargetController.YOU, false, filter2)); + } + + private PlazaOfHarmony(final PlazaOfHarmony card) { + super(card); + } + + @Override + public PlazaOfHarmony copy() { + return new PlazaOfHarmony(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PuppeteerClique.java b/Mage.Sets/src/mage/cards/p/PuppeteerClique.java index 774fa5d6a56..0216d1093ff 100644 --- a/Mage.Sets/src/mage/cards/p/PuppeteerClique.java +++ b/Mage.Sets/src/mage/cards/p/PuppeteerClique.java @@ -1,7 +1,6 @@ package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -17,12 +16,7 @@ import mage.abilities.keyword.PersistAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; @@ -31,15 +25,15 @@ import mage.target.Target; import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author jeffwadsworth - * */ public final class PuppeteerClique extends CardImpl { public PuppeteerClique(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.WIZARD); @@ -59,7 +53,7 @@ public final class PuppeteerClique extends CardImpl { this.addAbility(new PersistAbility()); } - public PuppeteerClique(final PuppeteerClique card) { + private PuppeteerClique(final PuppeteerClique card) { super(card); } @@ -71,12 +65,12 @@ public final class PuppeteerClique extends CardImpl { class PuppeteerCliqueEffect extends OneShotEffect { - public PuppeteerCliqueEffect() { + PuppeteerCliqueEffect() { super(Outcome.PutCreatureInPlay); staticText = "put target creature card from an opponent's graveyard onto the battlefield under your control. It gains haste. At the beginning of your next end step, exile it"; } - public PuppeteerCliqueEffect(final PuppeteerCliqueEffect effect) { + private PuppeteerCliqueEffect(final PuppeteerCliqueEffect effect) { super(effect); } @@ -87,26 +81,25 @@ class PuppeteerCliqueEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean result = false; Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - hasteEffect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(hasteEffect, source); - ExileTargetEffect exileEffect = new ExileTargetEffect("exile " + permanent.getLogName()); - exileEffect.setTargetPointer(new FixedTarget(permanent, game)); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect, TargetController.YOU); - game.addDelayedTriggeredAbility(delayedAbility, source); - result = true; - } - } - } + if (card == null) { + return false; } - return result; + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + return false; + } + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + return false; + } + ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + hasteEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(hasteEffect, source); + ExileTargetEffect exileEffect = new ExileTargetEffect("exile " + permanent.getLogName()); + exileEffect.setTargetPointer(new FixedTarget(permanent, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect, TargetController.YOU); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/Ragefire.java b/Mage.Sets/src/mage/cards/r/Ragefire.java new file mode 100644 index 00000000000..4dbe92e7040 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Ragefire.java @@ -0,0 +1,32 @@ +package mage.cards.r; + +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Ragefire extends CardImpl { + + public Ragefire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); + + // Ragefire deals 3 damage to target creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private Ragefire(final Ragefire card) { + super(card); + } + + @Override + public Ragefire copy() { + return new Ragefire(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java b/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java new file mode 100644 index 00000000000..80f127f7006 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java @@ -0,0 +1,55 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ResoluteWatchdog extends CardImpl { + + public ResoluteWatchdog(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HOUND); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // {1}, Sacrifice Resolute Watchdog: Target creature you control gains indestructible until end of turn. + Ability ability = new SimpleActivatedAbility( + new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), + Duration.EndOfTurn + ), new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private ResoluteWatchdog(final ResoluteWatchdog card) { + super(card); + } + + @Override + public ResoluteWatchdog copy() { + return new ResoluteWatchdog(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkyTether.java b/Mage.Sets/src/mage/cards/s/SkyTether.java new file mode 100644 index 00000000000..d24831c3186 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkyTether.java @@ -0,0 +1,54 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.LoseAbilityAttachedEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkyTether extends CardImpl { + + public SkyTether(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has defender and loses flying. + ability = new SimpleStaticAbility(new GainAbilityAttachedEffect( + DefenderAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield + )); + ability.addEffect(new LoseAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.AURA + ).concatBy("and")); + this.addAbility(ability); + } + + private SkyTether(final SkyTether card) { + super(card); + } + + @Override + public SkyTether copy() { + return new SkyTether(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheGuildpact.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheGuildpact.java new file mode 100644 index 00000000000..97c09111cab --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheGuildpact.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HexproofFromMonocoloredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SphinxOfTheGuildpact extends CardImpl { + + public SphinxOfTheGuildpact(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}"); + + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Sphinx of the Guildpact is all colors. + this.color.setWhite(true); + this.color.setBlue(true); + this.color.setBlack(true); + this.color.setRed(true); + this.color.setGreen(true); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this} is all colors"))); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Hexproof from mono colored + this.addAbility(HexproofFromMonocoloredAbility.getInstance()); + } + + private SphinxOfTheGuildpact(final SphinxOfTheGuildpact card) { + super(card); + } + + @Override + public SphinxOfTheGuildpact copy() { + return new SphinxOfTheGuildpact(this); + } +} diff --git a/Mage.Sets/src/mage/sets/RavnicaAllegiance.java b/Mage.Sets/src/mage/sets/RavnicaAllegiance.java index bae8e85fb28..8cd69754cd6 100644 --- a/Mage.Sets/src/mage/sets/RavnicaAllegiance.java +++ b/Mage.Sets/src/mage/sets/RavnicaAllegiance.java @@ -60,7 +60,10 @@ public final class RavnicaAllegiance extends ExpansionSet { cards.add(new SetCardInfo("Burning-Tree Vandal", 94, Rarity.COMMON, mage.cards.b.BurningTreeVandal.class)); cards.add(new SetCardInfo("Captive Audience", 160, Rarity.MYTHIC, mage.cards.c.CaptiveAudience.class)); cards.add(new SetCardInfo("Carnival // Carnage", 222, Rarity.UNCOMMON, mage.cards.c.CarnivalCarnage.class)); + cards.add(new SetCardInfo("Cavalcade of Calamity", 95, Rarity.UNCOMMON, mage.cards.c.CavalcadeOfCalamity.class)); cards.add(new SetCardInfo("Charging War Boar", 271, Rarity.UNCOMMON, mage.cards.c.ChargingWarBoar.class)); + cards.add(new SetCardInfo("Cindervines", 161, Rarity.RARE, mage.cards.c.Cindervines.class)); + cards.add(new SetCardInfo("Clamor Shaman", 96, Rarity.UNCOMMON, mage.cards.c.ClamorShaman.class)); cards.add(new SetCardInfo("Clan Guildmage", 162, Rarity.UNCOMMON, mage.cards.c.ClanGuildmage.class)); cards.add(new SetCardInfo("Collision // Colossus", 223, Rarity.UNCOMMON, mage.cards.c.CollisionColossus.class)); cards.add(new SetCardInfo("Combine Guildmage", 163, Rarity.UNCOMMON, mage.cards.c.CombineGuildmage.class)); @@ -79,16 +82,21 @@ public final class RavnicaAllegiance extends ExpansionSet { cards.add(new SetCardInfo("Dovin, Grand Arbiter", 167, Rarity.MYTHIC, mage.cards.d.DovinGrandArbiter.class)); cards.add(new SetCardInfo("Drill Bit", 73, Rarity.UNCOMMON, mage.cards.d.DrillBit.class)); cards.add(new SetCardInfo("Electrodominance", 99, Rarity.RARE, mage.cards.e.Electrodominance.class)); + cards.add(new SetCardInfo("Elite Arrester", 266, Rarity.COMMON, mage.cards.e.EliteArrester.class)); cards.add(new SetCardInfo("Emergency Powers", 169, Rarity.MYTHIC, mage.cards.e.EmergencyPowers.class)); cards.add(new SetCardInfo("End-Raze Forerunners", 124, Rarity.RARE, mage.cards.e.EndRazeForerunners.class)); cards.add(new SetCardInfo("Essence Capture", 37, Rarity.UNCOMMON, mage.cards.e.EssenceCapture.class)); cards.add(new SetCardInfo("Ethereal Absolution", 170, Rarity.RARE, mage.cards.e.EtherealAbsolution.class)); + cards.add(new SetCardInfo("Eyes Everywhere", 38, Rarity.UNCOMMON, mage.cards.e.EyesEverywhere.class)); cards.add(new SetCardInfo("Fireblade Artist", 172, Rarity.UNCOMMON, mage.cards.f.FirebladeArtist.class)); cards.add(new SetCardInfo("Font of Agonies", 74, Rarity.RARE, mage.cards.f.FontOfAgonies.class)); + cards.add(new SetCardInfo("Forbidding Spirit", 9, Rarity.UNCOMMON, mage.cards.f.ForbiddingSpirit.class)); cards.add(new SetCardInfo("Frenzied Arynx", 173, Rarity.COMMON, mage.cards.f.FrenziedArynx.class)); cards.add(new SetCardInfo("Frilled Mystic", 174, Rarity.UNCOMMON, mage.cards.f.FrilledMystic.class)); cards.add(new SetCardInfo("Gate Colossus", 232, Rarity.UNCOMMON, mage.cards.g.GateColossus.class)); + cards.add(new SetCardInfo("Gatebreaker Ram", 126, Rarity.UNCOMMON, mage.cards.g.GatebreakerRam.class)); cards.add(new SetCardInfo("Gates Ablaze", 102, Rarity.UNCOMMON, mage.cards.g.GatesAblaze.class)); + cards.add(new SetCardInfo("Gateway Sneak", 40, Rarity.UNCOMMON, mage.cards.g.GatewaySneak.class)); cards.add(new SetCardInfo("Glass of the Guildpact", 233, Rarity.RARE, mage.cards.g.GlassOfTheGuildpact.class)); cards.add(new SetCardInfo("Godless Shrine", 248, Rarity.RARE, mage.cards.g.GodlessShrine.class)); cards.add(new SetCardInfo("Grasping Thrull", 177, Rarity.COMMON, mage.cards.g.GraspingThrull.class)); @@ -115,9 +123,11 @@ public final class RavnicaAllegiance extends ExpansionSet { cards.add(new SetCardInfo("Judith, the Scourge Diva", 185, Rarity.RARE, mage.cards.j.JudithTheScourgeDiva.class)); cards.add(new SetCardInfo("Kaya's Wrath", 187, Rarity.RARE, mage.cards.k.KayasWrath.class)); cards.add(new SetCardInfo("Kaya, Orzhov Usurper", 186, Rarity.MYTHIC, mage.cards.k.KayaOrzhovUsurper.class)); + cards.add(new SetCardInfo("Knight of the Last Breath", 188, Rarity.UNCOMMON, mage.cards.k.KnightOfTheLastBreath.class)); cards.add(new SetCardInfo("Lavinia, Azorius Renegade", 189, Rarity.RARE, mage.cards.l.LaviniaAzoriusRenegade.class)); cards.add(new SetCardInfo("Light Up the Stage", 107, Rarity.UNCOMMON, mage.cards.l.LightUpTheStage.class)); cards.add(new SetCardInfo("Lumbering Battlement", 15, Rarity.RARE, mage.cards.l.LumberingBattlement.class)); + cards.add(new SetCardInfo("Macabre Mockery", 191, Rarity.UNCOMMON, mage.cards.m.MacabreMockery.class)); cards.add(new SetCardInfo("Mass Manipulation", 42, Rarity.RARE, mage.cards.m.MassManipulation.class)); cards.add(new SetCardInfo("Mesmerizing Benthid", 43, Rarity.MYTHIC, mage.cards.m.MesmerizingBenthid.class)); cards.add(new SetCardInfo("Ministrant of Obligation", 16, Rarity.UNCOMMON, mage.cards.m.MinistrantOfObligation.class)); @@ -132,12 +142,14 @@ public final class RavnicaAllegiance extends ExpansionSet { cards.add(new SetCardInfo("Persistent Petitioners", 44, Rarity.COMMON, mage.cards.p.PersistentPetitioners.class)); cards.add(new SetCardInfo("Pestilent Spirit", 81, Rarity.RARE, mage.cards.p.PestilentSpirit.class)); cards.add(new SetCardInfo("Pitiless Pontiff", 194, Rarity.UNCOMMON, mage.cards.p.PitilessPontiff.class)); + cards.add(new SetCardInfo("Plaza of Harmony", 254, Rarity.RARE, mage.cards.p.PlazaOfHarmony.class)); cards.add(new SetCardInfo("Precognitive Perception", 45, Rarity.RARE, mage.cards.p.PrecognitivePerception.class)); cards.add(new SetCardInfo("Priest of Forgotten Gods", 83, Rarity.RARE, mage.cards.p.PriestOfForgottenGods.class)); cards.add(new SetCardInfo("Prime Speaker Vannifar", 195, Rarity.MYTHIC, mage.cards.p.PrimeSpeakerVannifar.class)); cards.add(new SetCardInfo("Pteramander", 47, Rarity.UNCOMMON, mage.cards.p.Pteramander.class)); cards.add(new SetCardInfo("Quench", 48, Rarity.COMMON, mage.cards.q.Quench.class)); cards.add(new SetCardInfo("Rafter Demon", 196, Rarity.COMMON, mage.cards.r.RafterDemon.class)); + cards.add(new SetCardInfo("Ragefire", 270, Rarity.COMMON, mage.cards.r.Ragefire.class)); cards.add(new SetCardInfo("Rakdos Firewheeler", 197, Rarity.UNCOMMON, mage.cards.r.RakdosFirewheeler.class)); cards.add(new SetCardInfo("Rakdos Guildgate", 255, Rarity.COMMON, mage.cards.r.RakdosGuildgate.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rakdos Guildgate", 256, Rarity.COMMON, mage.cards.r.RakdosGuildgate.class, NON_FULL_USE_VARIOUS)); @@ -148,6 +160,7 @@ public final class RavnicaAllegiance extends ExpansionSet { cards.add(new SetCardInfo("Ravager Wurm", 200, Rarity.MYTHIC, mage.cards.r.RavagerWurm.class)); cards.add(new SetCardInfo("Regenesis", 136, Rarity.UNCOMMON, mage.cards.r.Regenesis.class)); cards.add(new SetCardInfo("Repudiate // Replicate", 227, Rarity.RARE, mage.cards.r.RepudiateReplicate.class)); + cards.add(new SetCardInfo("Resolute Watchdog", 19, Rarity.UNCOMMON, mage.cards.r.ResoluteWatchdog.class)); cards.add(new SetCardInfo("Revival // Revenge", 228, Rarity.RARE, mage.cards.r.RevivalRevenge.class)); cards.add(new SetCardInfo("Rhythm of the Wild", 201, Rarity.UNCOMMON, mage.cards.r.RhythmOfTheWild.class)); cards.add(new SetCardInfo("Rix Maadi Reveler", 109, Rarity.RARE, mage.cards.r.RixMaadiReveler.class)); @@ -166,11 +179,13 @@ public final class RavnicaAllegiance extends ExpansionSet { cards.add(new SetCardInfo("Skarrgan Hellkite", 114, Rarity.MYTHIC, mage.cards.s.SkarrganHellkite.class)); cards.add(new SetCardInfo("Skatewing Spy", 52, Rarity.UNCOMMON, mage.cards.s.SkatewingSpy.class)); cards.add(new SetCardInfo("Skewer the Critics", 115, Rarity.COMMON, mage.cards.s.SkewerTheCritics.class)); + cards.add(new SetCardInfo("Sky Tether", 21, Rarity.UNCOMMON, mage.cards.s.SkyTether.class)); cards.add(new SetCardInfo("Smelt-Ward Ignus", 116, Rarity.UNCOMMON, mage.cards.s.SmeltWardIgnus.class)); cards.add(new SetCardInfo("Smothering Tithe", 22, Rarity.RARE, mage.cards.s.SmotheringTithe.class)); cards.add(new SetCardInfo("Spawn of Mayhem", 85, Rarity.MYTHIC, mage.cards.s.SpawnOfMayhem.class)); cards.add(new SetCardInfo("Sphinx of Foresight", 55, Rarity.RARE, mage.cards.s.SphinxOfForesight.class)); cards.add(new SetCardInfo("Sphinx of New Prahv", 208, Rarity.UNCOMMON, mage.cards.s.SphinxOfNewPrahv.class)); + cards.add(new SetCardInfo("Sphinx of the Guildpact", 241, Rarity.UNCOMMON, mage.cards.s.SphinxOfTheGuildpact.class)); cards.add(new SetCardInfo("Sphinx's Insight", 209, Rarity.COMMON, mage.cards.s.SphinxsInsight.class)); cards.add(new SetCardInfo("Spirit of the Spires", 23, Rarity.UNCOMMON, mage.cards.s.SpiritOfTheSpires.class)); cards.add(new SetCardInfo("Stomping Ground", 259, Rarity.RARE, mage.cards.s.StompingGround.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java index 12b594c155b..ce9d973e1bb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.continuous; import mage.abilities.keyword.IndestructibleAbility; @@ -11,7 +10,6 @@ import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.permanent.Permanent; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -114,10 +112,6 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { String bloodmoon = "Blood Moon"; String canopyvista = "Canopy Vista"; - /* - TODO: NOTE: this test is currently failing due to bug in code. See issue #3072 - */ - //@Ignore @Test public void testBloodMoonBeforeUrborg() { // Blood Moon 2R @@ -147,10 +141,6 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { Assert.assertTrue("The mana the land can produce should be [{R}] but it's " + playerB.getManaAvailable(currentGame).toString(), playerB.getManaAvailable(currentGame).toString().equals("[{R}]")); } - /* - TODO: NOTE: this test is currently failing due to bug in code. See issue #3072 - */ - //@Ignore @Test public void testBloodMoonAfterUrborg() { // Blood Moon 2R @@ -186,6 +176,7 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { In terms of time-stamp order, Urborg was down first, then Kormus Bell, then Quicksilver. When I put a flood counter on a basic swamp, it would become a 0/0 instead of a 1/1 and die. */ + @Test public void testCormusBellAfterUrborg() { // Land - Legendary @@ -250,19 +241,19 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Blood Sun"); // all lands lose all abilities except for mana-producing addCard(Zone.BATTLEFIELD, playerA, "Stormtide Leviathan"); // all lands are islands in addition to their other types addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel"); // land has indestructible ability - + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); - + Permanent darksteel = getPermanent("Darksteel Citadel", playerA.getId()); Assert.assertNotNull(darksteel); Assert.assertFalse(darksteel.getAbilities().contains(IndestructibleAbility.getInstance())); // The ability is removed - + /* If a continuous effect has started applying in an earlier layer, it will continue to apply in later layers even if the ability that created that effect has been removed. Urborg ability is applied in the 4th layer. The Blood Sun works in the 6th. So the effect still applies to the lands. - */ + */ assertType(urborgtoy, CardType.LAND, SubType.SWAMP); assertType("Mountain", CardType.LAND, SubType.SWAMP); assertType(urborgtoy, CardType.LAND, SubType.ISLAND); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java index 9dd49c7ea60..72b22fc266e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java @@ -1,7 +1,6 @@ package mage.abilities.effects.common; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -20,11 +19,13 @@ import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * FAQ 2013/01/11 - * + *

* 702.97. Cipher - * + *

* 702.97a Cipher appears on some instants and sorceries. It represents two * static abilities, one that functions while the spell is on the stack and one * that functions while the card with cipher is in the exile zone. "Cipher" @@ -33,17 +34,17 @@ import mage.target.targetpointer.FixedTarget; * that creature, that creature has 'Whenever this creature deals combat damage * to a player, you may copy this card and you may cast the copy without paying * its mana cost.'" - * + *

* 702.97b The term "encoded" describes the relationship between the card with * cipher while in the exile zone and the creature chosen when the spell * represented by that card resolves. - * + *

* 702.97c The card with cipher remains encoded on the chosen creature as long * as the card with cipher remains exiled and the creature remains on the * battlefield. The card remains encoded on that object even if it changes * controller or stops being a creature, as long as it remains on the * battlefield. - * + *

* TODO: Implement Cipher as two static abilities concerning the rules. * * @author LevelX2 @@ -114,20 +115,13 @@ class CipherStoreEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Card cipherCard = game.getCard(cipherCardId); - if (cipherCard != null) { + if (cipherCard != null && controller != null) { Card copyCard = game.copyCard(cipherCard, source, controller.getId()); SpellAbility ability = copyCard.getSpellAbility(); // remove the cipher effect from the copy - Effect cipherEffect = null; - for (Effect effect : ability.getEffects()) { - if (effect instanceof CipherEffect) { - cipherEffect = effect; - } - } - ability.getEffects().remove(cipherEffect); - if (ability instanceof SpellAbility) { - controller.cast(ability, game, true, new MageObjectReference(source.getSourceObject(game), game)); - } + ability.getEffects().removeIf(effect -> effect instanceof CipherEffect); + controller.cast(ability, game, true, new MageObjectReference(source.getSourceObject(game), game)); + } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java index 0eef90ee664..ca59b004149 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java @@ -12,7 +12,6 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; /** - * * @author LevelX2 */ public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEffectImpl { @@ -37,7 +36,7 @@ public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEff + (payAlsoForAttackingPlaneswalker ? "or a planeswalker you control " : "") + "unless their controller pays " + (manaCosts == null ? "" : manaCosts.getText()) - + " for each creature he or she controls that's attacking you"; + + " for each creature they controls that's attacking you"; } public CantAttackYouUnlessPayManaAllEffect(final CantAttackYouUnlessPayManaAllEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java new file mode 100644 index 00000000000..88fd558921d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java @@ -0,0 +1,44 @@ +package mage.abilities.keyword; + +import mage.abilities.MageSingleton; +import mage.abilities.common.SimpleStaticAbility; +import mage.constants.Zone; + +import java.io.ObjectStreamException; + +/** + * Hexproof from Monocolored (This creature or player can't be the target of monocolored + * spells or abilities your opponents control.) + * + * @author TheElk801 + */ +public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implements MageSingleton { + + private static final HexproofFromMonocoloredAbility instance; + + static { + instance = new HexproofFromMonocoloredAbility(); + } + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static HexproofFromMonocoloredAbility getInstance() { + return instance; + } + + private HexproofFromMonocoloredAbility() { + super(Zone.BATTLEFIELD, null); + } + + @Override + public HexproofFromMonocoloredAbility copy() { + return instance; + } + + @Override + public String getRule() { + return "hexproof from monocolored (This creature can't be the target of monocolored spells or abilities your opponents control.)"; + } +} diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java index 086a90ff431..288e48b1952 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java @@ -1,8 +1,6 @@ package mage.abilities.mana; -import java.util.ArrayList; -import java.util.List; import mage.Mana; import mage.abilities.Abilities; import mage.abilities.Ability; @@ -20,8 +18,10 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.ArrayList; +import java.util.List; + /** - * * @author LevelX2 */ public class AnyColorLandsProduceManaAbility extends ActivatedManaAbilityImpl { @@ -31,7 +31,11 @@ public class AnyColorLandsProduceManaAbility extends ActivatedManaAbilityImpl { } public AnyColorLandsProduceManaAbility(TargetController targetController, boolean onlyColors) { - super(Zone.BATTLEFIELD, new AnyColorLandsProduceManaEffect(targetController, onlyColors), new TapSourceCost()); + this(targetController, onlyColors, null); + } + + public AnyColorLandsProduceManaAbility(TargetController targetController, boolean onlyColors, FilterPermanent filter) { + super(Zone.BATTLEFIELD, new AnyColorLandsProduceManaEffect(targetController, onlyColors, filter), new TapSourceCost()); } public AnyColorLandsProduceManaAbility(final AnyColorLandsProduceManaAbility ability) { @@ -62,16 +66,21 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { private boolean inManaTypeCalculation = false; - public AnyColorLandsProduceManaEffect(TargetController targetController, boolean onlyColors) { + AnyColorLandsProduceManaEffect(TargetController targetController, boolean onlyColors, FilterPermanent filter) { super(); - filter = new FilterLandPermanent(); + if (filter == null) { + this.filter = new FilterLandPermanent(); + } else { + this.filter = filter.copy(); + } this.onlyColors = onlyColors; - filter.add(new ControllerPredicate(targetController)); + this.filter.add(new ControllerPredicate(targetController)); String text = targetController == TargetController.OPPONENT ? "an opponent controls" : "you control"; - staticText = "Add one mana of any " + (this.onlyColors ? "color" : "type") + " that a land " + text + " could produce"; + staticText = "Add one mana of any " + (this.onlyColors ? "color" : "type") + " that a " + + (filter == null ? "land " : filter.getMessage() + " ") + text + " could produce"; } - public AnyColorLandsProduceManaEffect(final AnyColorLandsProduceManaEffect effect) { + private AnyColorLandsProduceManaEffect(final AnyColorLandsProduceManaEffect effect) { super(effect); this.filter = effect.filter.copy(); this.onlyColors = effect.onlyColors; diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index ed908b5e146..0b646f11dc8 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -43,7 +43,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { private static final Logger logger = Logger.getLogger(PermanentImpl.class); - public class MarkedDamageInfo { + static class MarkedDamageInfo { public MarkedDamageInfo(Counter counter, MageObject sourceObject) { this.counter = counter; @@ -956,6 +956,14 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } } + if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) { + if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) + && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) + && !source.getColor(game).isColorless() && !source.getColor(game).isMulticolored()) { + return false; + } + } + if (hasProtectionFrom(source, game)) { return false; } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 026aad2e04a..7e1503986c3 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -34583,6 +34583,8 @@ Forbidding Spirit|Ravnica Allegiance|9|U|{1}{W}{W}|Creature - Spirit Cleric|3|3| Hero of Precinct One|Ravnica Allegiance|11|R|{1}{W}|Creature - Human Warrior|2|2|Whenever you cast a multicolored spell, create a 1/1 white Human creature token.| Lumbering Battlement|Ravnica Allegiance|15|R|{4}{W}|Creature - Beast|4|5|Vigilance$When Lumbering Battlement enters the battlefield, exile any number of other nontoken creatures you control until it leaves the battlefield.$Lumbering Battlement gets +2/+2 for each card exiled with it.| Ministrant of Obligation|Ravnica Allegiance|16|U|{2}{W}|Creature - Human Cleric|2|1|Afterlife 2| +Resolute Watchdog|Ravnica Allegiance|19|U|{W}|Creature - Hound|1|3|Defender${1}, Sacrifice Resolute Watchdog: Target creature you control gains indestructible until end of turn.| +Sky Tether|Ravnica Allegiance|21|U|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has defender and loses flying.| Smothering Tithe|Ravnica Allegiance|22|R|{3}{W}|Enchantment|||Whenever an opponent draws a card, that player may pay {2}. If the player doesn't, you create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color."| Spirit of the Spires|Ravnica Allegiance|23|U|{3}{W}|Creature - Spirit|2|4|Flying$Other creatures you control with flying get +0/+1.| Tithe Taker|Ravnica Allegiance|27|R|{1}{W}|Creature - Human Soldier|2|1|During your turn, spells your opponents cast cost {1} more to cast and abilities your opponents activate cost {1} more to activate unless they're mana abilities.$Afterlife 1| @@ -34591,6 +34593,7 @@ Arrester's Admonition|Ravnica Allegiance|31|C|{2}{U}|Instant|||Return target cre Benthic Biomancer|Ravnica Allegiance|32|R|{U}|Creature - Merfolk Wizard Mutant|1|1|{1}{U}: Adapt 1.$Whenever one or more +1/+1 counters are put on Benthic Biomancer, draw a card, then discard a card.| Essence Capture|Ravnica Allegiance|37|U|{U}{U}|Instant|||Counter target creature spell. Put a +1/+1 counter on up to one target creature you control.| Eyes Everywhere|Ravnica Allegiance|38|U|{2}{U}|Enchantment|||At the beginning of your upkeep, scry 1.${5}{U}: Exchange control of Eyes Everywhere and target nonland permanent. Activate this ability only any time you could cast a sorcery.| +Gateway Sneak|Ravnica Allegiance|40|U|{2}{U}|Creature - Vedalken Rogue|1|3|Whenever a Gate enters the battlefield under your control, Gateway Sneak can't be blocked this turn.$Whenever Gateway Sneak deals combat damage to a player, draw a card.| Humongulus|Ravnica Allegiance|41|C|{4}{U}|Creature - Homunculus|2|5|Hexproof| Mass Manipulation|Ravnica Allegiance|42|R|{X}{X}{U}{U}{U}{U}|Sorcery|||Gain control of X target creatures and/or planeswalkers.| Mesmerizing Benthid|Ravnica Allegiance|43|M|{3}{U}{U}|Creature - Octopus|4|5|When Mesmerizing Benthid enters the battlefield, create two 0/2 blue Illusion creature tokens with "Whenever this creature blocks a creature, that creature doesn't untap during its controller's next untap step."$Mesmerizing Benthid has hexproof as long as you control an Illusion.| @@ -34617,6 +34620,8 @@ Spawn of Mayhem|Ravnica Allegiance|85|M|{2}{B}{B}|Creature - Demon|4|4|Spectacle Amplifire|Ravnica Allegiance|92|R|{2}{R}{R}|Creature - Elemental|1|1|At the beginning of your upkeep, reveal cards from the top of your library until you reveal a creature card. Until your next turn, Amplifire's base power becomes twice that card's power and its base toughness becomes twice that card's toughness. Put the revealed cards on the bottom of your library in a random order.| Burn Bright|Ravnica Allegiance|93|C|{2}{R}|Instant|||Creatures you control get +2/+0 until end of turn.| Burning-Tree Vandal|Ravnica Allegiance|94|C|{2}{R}|Creature - Human Rogue|2|1|Riot$Whenever Burning-Tree Vandal attacks, you may discard a card. If you do, draw a card.| +Cavalcade of Calamity|Ravnica Allegiance|95|U|{1}{R}|Enchantment|||Whenever a creature you control with power 1 or less attacks, Cavalcade of Calamity deals 1 damage to the player or planeswalker that creature is attacking.| +Clamor Shaman|Ravnica Allegiance|96|U|{2}{R}|Creature - Goblin Shaman|1|1|Riot$Whenever Clamor Shaman attacks, target creature an opponent controls can't block this turn.| Electrodominance|Ravnica Allegiance|99|R|{X}{R}{R}|Instant|||Electrodominance deals X damage to any target. You may cast a card with converted mana cost X or less from your hand without paying its mana cost.| Gates Ablaze|Ravnica Allegiance|102|U|{2}{R}|Sorcery|||Gates Ablaze deals X damage to each creature, where X is the number of Gates you control.| Immolation Shaman|Ravnica Allegiance|105|R|{1}{R}|Creature - Viashino Shaman|1|3|Whenever an opponent activates an ability of an artifact, creature, or land that isn't a mana ability, Immolation Shaman deals 1 damage to that player.${3}{R}{R}: Immolation Shaman gets +3/+3 and gains menace until end of turn.| @@ -34630,6 +34635,7 @@ Smelt-Ward Ignus|Ravnica Allegiance|116|U|{1}{R}|Creature - Elemental|2|1|{2}{R} Biogenic Ooze|Ravnica Allegiance|122|M|{3}{G}{G}|Creature - Ooze|2|2|When Biogenic Ooze enters the battlefield, create a 2/2 green Ooze creature token.$At the beginning of your end step, put a +1/+1 counter on each Ooze you control.${1}{G}{G}{G}: Create a 2/2 green Ooze creature token.| Biogenic Upgrade|Ravnica Allegiance|123|U|{4}{G}{G}|Sorcery|||Distribute three +1/+1 counters among one, two, or three target creatures, then double the number of +1/+1 counters on each of those creatures.| End-Raze Forerunners|Ravnica Allegiance|124|R|{5}{G}{G}{G}|Creature - Boar|7|7|Vigilance, trample, haste$When End-Raze Forerunners enters the battlefield, other creatures you control get +2/+2 and gain vigilance and trample until end of turn.| +Gatebreaker Ram|Ravnica Allegiance|126|U|{2}{G}|Creature - Sheep|2|2|Gatebreaker Ram gets +1/+1 for each Gate you control.$As long as you control two or more Gates, Gatebreaker Ram has vigilance and trample.| Growth-Chamber Guardian|Ravnica Allegiance|128|R|{1}{G}|Creature - Elf Crab Warrior|2|2|{2}{G}: Adapt 2.$Whenever one or more +1/+1 counters are put on Growth-Chamber Guardian, you may search your library for a card named Growth-Chamber Guardian, reveal it, put it into your hand, then shuffle your library.| Gruul Beastmaster|Ravnica Allegiance|129|U|{3}{G}|Creature - Human Shaman|2|2|Riot$Whenever Gruul Beastmaster attacks, another target creature you control gets +X/+0 until end of turn, where X is Gruul Beastmaster's power.| Guardian Project|Ravnica Allegiance|130|R|{3}{G}|Enchantment|||Whenever a nontoken creature enters the battlefield under your control, if that creature does not have the same name as another creature you control or a creature card in your graveyard, draw a card.| @@ -34674,6 +34680,7 @@ Imperious Oligarch|Ravnica Allegiance|184|C|{W}{B}|Creature - Human Cleric|2|1|V Judith, the Scourge Diva|Ravnica Allegiance|185|R|{1}{B}{R}|Legendary Creature - Human Shaman|2|2|Other creatures you control get +1/+0.$Whenever a nontoken creature you control dies, Judith, the Scourge Diva deals 1 damage to any target.| Kaya, Orzhov Usurper|Ravnica Allegiance|186|M|{1}{W}{B}|Legendary Planeswalker - Kaya|3|+1: Exile up to two target cards from a single graveyard. You gain 2 life if at least one creature card was exiled this way.$-1: Exile target nonland permanent with converted mana cost 1 or less.$-5: Kaya, Orzhov Usurper deals damage to target player equal to the number of cards that player owns in exile and you gain that much life.| Kaya's Wrath|Ravnica Allegiance|187|R|{W}{W}{B}{B}|Sorcery|||Destroy all creatures. You gain life equal to the number of creatures you controlled that were destroyed this way.| +Knight of the Last Breath|Ravnica Allegiance|188|U|{5}{W}{B}|Creature - Giant Knight|4|4|{3}, Sacrifice another nontoken creature: Create a 1/1 white and black spirit creature token with flying.$Afterlife 3| Lavinia, Azorius Renegade|Ravnica Allegiance|189|R|{W}{U}|Legendary Creature - Human Soldier|2|2|Each opponent can't cast noncreature spells with converted mana cost greater than the number of lands that player controls.$Whenever an opponent casts a spell, if no mana was spent to cast it, counter that spell.| Macabre Mockery|Ravnica Allegiance|191|U|{2}{B}{R}|Instant|||Put target creature card from an opponent's graveyard onto the battlefield under your control. It gets +2/+0 and gains haste until end of turn. Sacrifice it at the beginning of the next end step.| Mortify|Ravnica Allegiance|192|U|{1}{W}{B}|Instant|||Destroy target creature or enchantment.| @@ -34727,6 +34734,7 @@ Orzhov Locket|Ravnica Allegiance|236|C|{3}|Artifact|||{T}: Add {W} or {B}.${W/B} Rakdos Locket|Ravnica Allegiance|237|C|{3}|Artifact|||{T}: Add {B} or {R}.${B/R}{B/R}{B/R}{B/R}, {T}, Sacrifice Rakdos Locket: Draw two cards.| Scrabbling Claws|Ravnica Allegiance|238|U|{1}|Artifact|||{T}: Target player exiles a card from their graveyard.${1}, Sacrifice Scrabbling Claws: Exile target card from a graveyard. Draw a card.| Simic Locket|Ravnica Allegiance|240|C|{3}|Artifact|||{T}: Add {G} or {U}.${G/U}{G/U}{G/U}{G/U}, {T}, Sacrifice Simic Locket: Draw two cards.| +Sphinx of the Guildpact|Ravnica Allegiance|241|U|{7}|Artifact Creature - Sphinx|5|5|Sphinx of the Guildpact is all colors.$Flying$Hexproof from mono colored| Tome of the Guildpact|Ravnica Allegiance|242|R|{5}|Artifact|||Whenever you cast a multicolored spell, draw a card.${T}: Add one mana of any color.| Azorius Guildgate|Ravnica Allegiance|243|C||Land - Gate|||Azorius Guildgate enters the battlefield tapped.${T}: Add {W} or {U}.| Blood Crypt|Ravnica Allegiance|245|R||Land - Swamp Mountain|||({T}: Add {B} or {R}.)$As Blood Crypt enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| @@ -34742,8 +34750,8 @@ Stomping Ground|Ravnica Allegiance|259|R||Land - Mountain Forest|||({T}: Add {R} Dovin, Architect of Law|Ravnica Allegiance|265|M|{4}{W}{U}|Legendary Planeswalker - Dovin|5|+1: You gain 2 life and draw a card.$-1: Tap target creature. It doesn't untap during its controller's next untap step.$-9: Tap all permanents target opponent controls. That player skips their next untap step.| Elite Arrester|Ravnica Allegiance|266|C|{W}|Creature - Human Soldier|0|3|{1}{U}, {T}: Tap target creature.| Dovin's Dismissal|Ravnica Allegiance|267|R|{2}{W}{U}|Instant|||Put up to one target tapped creature on top of its owner's library. You may search your library and/or graveyard for a card named Dovin, Architect of Law, reveal it, and put it into your hand. If you search your library this way, shuffle it.| -Dovin's Automaton|Ravnica Allegiance|268|C|{4}|Artifact Creature - Homunculus|3|3|As long as you control a Dovin planeswalker, Dovin's Automaton gets +2/+2 and has vigilance.| -Domri, City Smasher|Ravnica Allegiance|269|R|{4}{R}{G}|Legendary Planeswalker - Domri|4|+2: Creatures you control get +1/+1 and gain haste until end of turn.$-3: Domri, City Smasher deals 3 damage to any target.$-8: Put three +1/+1 counters on each creature you control. Those creatures gain trample until end of turn.| +Dovin's Automaton|Ravnica Allegiance|268|U|{4}|Artifact Creature - Homunculus|3|3|As long as you control a Dovin planeswalker, Dovin's Automaton gets +2/+2 and has vigilance.| +Domri, City Smasher|Ravnica Allegiance|269|M|{4}{R}{G}|Legendary Planeswalker - Domri|4|+2: Creatures you control get +1/+1 and gain haste until end of turn.$-3: Domri, City Smasher deals 3 damage to any target.$-8: Put three +1/+1 counters on each creature you control. Those creatures gain trample until end of turn.| Ragefire|Ravnica Allegiance|270|C|{1}{R}|Sorcery|||Ragefire deals 3 damage to target creature.| Charging War Boar|Ravnica Allegiance|271|U|{1}{R}{G}|Creature - Boar|3|1|Haste$As long as you control a Domri planeswalker, Charging War Boar gets +1/+1 and has trample.| Domri's Nodorog|Ravnica Allegiance|272|R|{3}{R}{G}|Creature - Beast|5|2|Trample$When Domri's Nodorog enters the battlefield, you may search your library and/or graveyard for a card named Domri, City Smasher, reveal it, and put it into your hand. If you search your library this way, shuffle it.|