diff --git a/Mage.Sets/src/mage/cards/h/HELIOSOne.java b/Mage.Sets/src/mage/cards/h/HELIOSOne.java new file mode 100644 index 00000000000..74cdd4b18a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HELIOSOne.java @@ -0,0 +1,126 @@ +package mage.cards.h; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.PayEnergyCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourceControllerCountersCount; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.Filter; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.target.Target; +import mage.target.common.TargetNonlandPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class HELIOSOne extends CardImpl { + + public HELIOSOne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {1}, {T}: You get {E}. + Ability ability = new SimpleActivatedAbility( + new GetEnergyCountersControllerEffect(1), + new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {3}, {T}, Pay X {E}, Sacrifice HELIOS One: Destroy target nonland permanent with mana value X. Activate only as a sorcery. + ability = new ActivateAsSorceryActivatedAbility( + new DestroyTargetEffect().setText("Destroy target nonland permanent with mana value X"), + new GenericManaCost(3) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new PayEnergyCost(0).setText("Pay X {E}")); // Cost adjusted. + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetNonlandPermanent()); + ability.setTargetAdjuster(new HELIOSOneTargetAdjuster(SourceControllerCountersCount.ENERGY, ComparisonType.OR_LESS)); + ability.setCostAdjuster(HELIOSOneCostAdjuster.instance); + this.addAbility(ability); + } + + private HELIOSOne(final HELIOSOne card) { + super(card); + } + + @Override + public HELIOSOne copy() { + return new HELIOSOne(this); + } +} + +// TODO: replace with ManaValueTargetAdjuster in #12017 +class HELIOSOneTargetAdjuster implements TargetAdjuster { + private Target blueprintTarget = null; + private final DynamicValue dynamicValue; + private final ComparisonType comparison; + + HELIOSOneTargetAdjuster(DynamicValue value, ComparisonType compare) { + this.dynamicValue = value; + this.comparison = compare; + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (blueprintTarget == null) { + blueprintTarget = ability.getTargets().get(0).copy(); + blueprintTarget.clearChosen(); + } + Target newTarget = blueprintTarget.copy(); + int amount = dynamicValue.calculate(game, ability, ability.getEffects().get(0)); + Filter filter = newTarget.getFilter(); + filter.add(new ManaValuePredicate(comparison, amount)); + newTarget.setTargetName(filter.getMessage() + " (Mana Value " + comparison + " " + amount + ")"); + ability.getTargets().clear(); + ability.addTarget(newTarget); + } +} + +enum HELIOSOneCostAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + MageObject target = game.getObject(ability.getFirstTarget()); + if (target == null) { + return; + } + int mv = target.getManaValue(); + Costs costs = new CostsImpl<>(); + costs.addAll(ability.getCosts()); + ability.clearCosts(); + for (Cost cost : costs) { + if (cost instanceof PayEnergyCost) { + if (mv > 0) { + ability.addCost(new PayEnergyCost(mv)); + } + } else { + ability.addCost(cost); + } + } + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Fallout.java b/Mage.Sets/src/mage/sets/Fallout.java index cf99e62df9d..b9c86efd987 100644 --- a/Mage.Sets/src/mage/sets/Fallout.java +++ b/Mage.Sets/src/mage/sets/Fallout.java @@ -141,6 +141,7 @@ public final class Fallout extends ExpansionSet { cards.add(new SetCardInfo("Grave Titan", 346, Rarity.MYTHIC, mage.cards.g.GraveTitan.class)); cards.add(new SetCardInfo("Guardian Project", 199, Rarity.RARE, mage.cards.g.GuardianProject.class)); cards.add(new SetCardInfo("Gunner Conscript", 77, Rarity.UNCOMMON, mage.cards.g.GunnerConscript.class)); + cards.add(new SetCardInfo("HELIOS One", 149, Rarity.RARE, mage.cards.h.HELIOSOne.class)); cards.add(new SetCardInfo("Hancock, Ghoulish Mayor", 45, Rarity.RARE, mage.cards.h.HancockGhoulishMayor.class)); cards.add(new SetCardInfo("Hardened Scales", 200, Rarity.RARE, mage.cards.h.HardenedScales.class)); cards.add(new SetCardInfo("Harmonize", 201, Rarity.UNCOMMON, mage.cards.h.Harmonize.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/HELIOSOneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/HELIOSOneTest.java new file mode 100644 index 00000000000..3a961e76698 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/HELIOSOneTest.java @@ -0,0 +1,117 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class HELIOSOneTest extends CardTestPlayerBase { + + /** + * {@link HELIOSOneTest HELIOS One} + * Land + * {T}: Add {C}. + * {1}, {T}: You get {E} (an energy counter). + * {3}, {T}, Pay X {E}, Sacrifice HELIOS One: Destroy target nonland permanent with mana value X. Activate only as a sorcery. + */ + private static final String helios = "HELIOS One"; + + @Test + public void test_Target_0MV_0Energy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, helios, 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}"); + addTarget(playerA, "Memnite"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Memnite", 1); + assertGraveyardCount(playerA, helios, 1); + assertTappedCount("Plains", true, 3); + assertCounterCount(playerA, CounterType.ENERGY, 0); + } + + + @Test + public void test_NoTarget_1MV_0Energy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, helios, 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}"); + addTarget(playerA, "Elite Vanguard"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + try { + execute(); + Assert.fail("Should not be possible to activate the {3} ability without energy and without a 0 mana target"); + } catch (AssertionError e) { + Assert.assertEquals("Can't find ability to activate command: {3}", e.getMessage()); + } + } + + @Test + public void test_Target_1MV_1Energy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, helios, 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + + // TODO: for some reason the test suite does think the ability is playable, although it is not + // after target adjusting + cost adjusting for the target + // see above test for the try/catch version that fails on execute. + //checkPlayableAbility("can't activate due to lack of energy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}", false); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPlayableAbility("can activate with energy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}"); + addTarget(playerA, "Elite Vanguard"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Elite Vanguard", 1); + assertGraveyardCount(playerA, helios, 1); + assertTappedCount("Plains", true, 4); + assertCounterCount(playerA, CounterType.ENERGY, 0); + } + + @Test + public void test_Target_0MV_1Energy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, helios, 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}"); + addTarget(playerA, "Memnite"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Memnite", 1); + assertGraveyardCount(playerA, helios, 1); + assertTappedCount("Plains", true, 4); + assertCounterCount(playerA, CounterType.ENERGY, 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java index 97bcec56184..b4f151e8d39 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java @@ -11,6 +11,7 @@ import mage.players.Player; * @author Susucr */ public enum SourceControllerCountersCount implements DynamicValue { + ENERGY(CounterType.ENERGY), EXPERIENCE(CounterType.EXPERIENCE), RAD(CounterType.RAD);