add PayVariableEnergyCost, fixing [PIP] HELIOS One & [MH3] Chtonian Nightmare

fix #12217
This commit is contained in:
Susucre 2024-05-03 16:44:19 +02:00
parent fc6c7b36e5
commit 534d1be175
6 changed files with 59 additions and 83 deletions

View file

@ -1,14 +1,9 @@
package mage.cards.c;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
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.PayVariableEnergyCost;
import mage.abilities.costs.common.ReturnToHandFromBattlefieldSourceCost;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
@ -17,7 +12,6 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetadjustment.XManaValueTargetAdjuster;
@ -38,13 +32,12 @@ public final class ChthonianNightmare extends CardImpl {
Ability ability = new ActivateAsSorceryActivatedAbility(
new ReturnFromGraveyardToBattlefieldTargetEffect()
.setText("Return target creature card with mana value X from your graveyard to the battlefield"),
new PayEnergyCost(0).setText("Pay X {E}") // TODO: replace with proper VariableEnergyCost
new PayVariableEnergyCost()
);
ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_CREATURE));
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE));
ability.addCost(new ReturnToHandFromBattlefieldSourceCost());
ability.setTargetAdjuster(new XManaValueTargetAdjuster());
ability.setCostAdjuster(ChthonianNightmareCostAdjuster.instance); // TODO: remove
this.addAbility(ability);
}
@ -56,29 +49,4 @@ public final class ChthonianNightmare extends CardImpl {
public ChthonianNightmare copy() {
return new ChthonianNightmare(this);
}
}
enum ChthonianNightmareCostAdjuster 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<Cost> 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);
}
}
}
}
}

View file

@ -1,14 +1,9 @@
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.PayVariableEnergyCost;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
@ -18,7 +13,6 @@ import mage.abilities.mana.ColorlessManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.Game;
import mage.target.common.TargetNonlandPermanent;
import mage.target.targetadjustment.XManaValueTargetAdjuster;
@ -49,11 +43,10 @@ public final class HELIOSOne extends CardImpl {
new GenericManaCost(3)
);
ability.addCost(new TapSourceCost());
ability.addCost(new PayEnergyCost(0).setText("Pay X {E}")); // TODO: replace with proper VariableEnergyCost
ability.addCost(new PayVariableEnergyCost());
ability.addCost(new SacrificeSourceCost());
ability.addTarget(new TargetNonlandPermanent());
ability.setTargetAdjuster(new XManaValueTargetAdjuster());
ability.setCostAdjuster(HELIOSOneCostAdjuster.instance); // TODO: remove
this.addAbility(ability);
}
@ -65,29 +58,4 @@ public final class HELIOSOne extends CardImpl {
public HELIOSOne copy() {
return new HELIOSOne(this);
}
}
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<Cost> 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);
}
}
}
}
}

View file

@ -142,7 +142,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)); //TODO: re-add when pay X energy is fixed.
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));

View file

@ -24,7 +24,7 @@ public final class ModernHorizons3 extends ExpansionSet {
cards.add(new SetCardInfo("Ajani, Nacatl Avenger", 237, Rarity.MYTHIC, mage.cards.a.AjaniNacatlAvenger.class));
cards.add(new SetCardInfo("Ajani, Nacatl Pariah", 237, Rarity.MYTHIC, mage.cards.a.AjaniNacatlPariah.class));
cards.add(new SetCardInfo("Bloodstained Mire", 216, Rarity.RARE, mage.cards.b.BloodstainedMire.class));
// cards.add(new SetCardInfo("Chthonian Nightmare", 83, Rarity.RARE, mage.cards.c.ChthonianNightmare.class)); //TODO: re-add when pay X energy is fixed.
cards.add(new SetCardInfo("Chthonian Nightmare", 83, Rarity.RARE, mage.cards.c.ChthonianNightmare.class));
cards.add(new SetCardInfo("Emrakul, the World Anew", 6, Rarity.MYTHIC, mage.cards.e.EmrakulTheWorldAnew.class));
cards.add(new SetCardInfo("Flare of Cultivation", 154, Rarity.RARE, mage.cards.f.FlareOfCultivation.class));
cards.add(new SetCardInfo("Flare of Denial", 62, Rarity.RARE, mage.cards.f.FlareOfDenial.class));

View file

@ -4,7 +4,6 @@ import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
@ -22,7 +21,6 @@ public class HELIOSOneTest extends CardTestPlayerBase {
*/
private static final String helios = "HELIOS One";
@Ignore // needs fix
@Test
public void test_Target_0MV_0Energy() {
setStrictChooseMode(true);
@ -31,6 +29,7 @@ public class HELIOSOneTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
setChoice(playerA, "X=0");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
addTarget(playerA, "Memnite");
@ -43,7 +42,6 @@ public class HELIOSOneTest extends CardTestPlayerBase {
assertCounterCount(playerA, CounterType.ENERGY, 0);
}
@Ignore // needs fix
@Test
public void test_NoTarget_1MV_0Energy() {
setStrictChooseMode(true);
@ -52,9 +50,12 @@ public class HELIOSOneTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
// TODO: So the test suite let's you activate the ability (as it does not go to adjust targets to check them.)
// But X=0 is not a valid choice once targets are checked (no nonland card with that MV in play).
setChoice(playerA, "X=0");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
addTarget(playerA, "Elite Vanguard");
addTarget(playerA, "Elite Vanguard"); // not a valid target for X=0 energy payment
setStopAt(1, PhaseStep.BEGIN_COMBAT);
@ -62,11 +63,13 @@ public class HELIOSOneTest extends CardTestPlayerBase {
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());
Assert.assertTrue(
"X=0 is not a valid choice. Error message:\n" + e.getMessage(),
e.getMessage().contains("Message: Announce the number of {E} to pay")
);
}
}
@Ignore // needs fix
@Test
public void test_Target_1MV_1Energy() {
setStrictChooseMode(true);
@ -75,14 +78,13 @@ public class HELIOSOneTest extends CardTestPlayerBase {
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);
// TODO: investigate why the checkPlayableAbility doesn't go
checkPlayableAbility("can't activate due to lack of energy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}", true); // should be 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);
setChoice(playerA, "X=1");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
addTarget(playerA, "Elite Vanguard");
@ -95,7 +97,6 @@ public class HELIOSOneTest extends CardTestPlayerBase {
assertCounterCount(playerA, CounterType.ENERGY, 0);
}
@Ignore // needs fix
@Test
public void test_Target_0MV_1Energy() {
setStrictChooseMode(true);
@ -107,6 +108,7 @@ public class HELIOSOneTest extends CardTestPlayerBase {
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setChoice(playerA, "X=0");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
addTarget(playerA, "Memnite");

View file

@ -0,0 +1,38 @@
package mage.abilities.costs.common;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.VariableCostImpl;
import mage.abilities.costs.VariableCostType;
import mage.abilities.dynamicvalue.common.SourceControllerCountersCount;
import mage.game.Game;
/**
* @author Susucr
*/
public class PayVariableEnergyCost extends VariableCostImpl {
public PayVariableEnergyCost() {
super(VariableCostType.NORMAL, "{E} to pay");
this.text = "Pay " + xText + " {E}";
}
protected PayVariableEnergyCost(final PayVariableEnergyCost cost) {
super(cost);
}
@Override
public PayVariableEnergyCost copy() {
return new PayVariableEnergyCost(this);
}
@Override
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
return new PayEnergyCost(xValue);
}
@Override
public int getMaxValue(Ability source, Game game) {
return SourceControllerCountersCount.ENERGY.calculate(game, source, null);
}
}