[REX] Implement Grim Giganotosaurus (#12103)

* Implement Grim Giganotosaurus

* Refactor MonstrosityAbility to take cost adjuster

* Update Nemesis of Mortals' monstrous ability and add test for cost reduction
This commit is contained in:
jimga150 2024-04-17 20:24:37 -04:00 committed by GitHub
parent 9d7bf27d38
commit b0d7daa85e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 147 additions and 13 deletions

View file

@ -0,0 +1,83 @@
package mage.cards.g;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BecomesMonstrousSourceTriggeredAbility;
import mage.abilities.costs.CostAdjuster;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.abilities.keyword.MonstrosityAbility;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterOpponentsCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
*
* @author jimga150
*/
public final class GrimGiganotosaurus extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("artifacts and creatures other than {this}");
static {
filter.add(AnotherPredicate.instance);
filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate()));
}
public GrimGiganotosaurus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{G}");
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(10);
this.toughness = new MageInt(10);
// {10}{B}{G}: Monstrosity 10. This ability costs {1} less to activate for each creature with power 4 or greater your opponents control.
this.addAbility(new MonstrosityAbility("{10}{B}{G}", 10,
GrimGiganotosaurusAdjuster.instance,
"This ability costs {1} less to activate for each creature with power 4 or greater your opponents control. "));
// When Grim Giganotosaurus becomes monstrous, destroy all artifacts and creatures other than Grim Giganotosaurus.
this.addAbility(new BecomesMonstrousSourceTriggeredAbility(new DestroyAllEffect(filter)));
}
private GrimGiganotosaurus(final GrimGiganotosaurus card) {
super(card);
}
@Override
public GrimGiganotosaurus copy() {
return new GrimGiganotosaurus(this);
}
}
enum GrimGiganotosaurusAdjuster implements CostAdjuster {
instance;
private static final FilterCreaturePermanent filter = new FilterOpponentsCreaturePermanent("creature with power 4 or greater your opponents control");
static {
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
}
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
@Override
public void adjustCosts(Ability ability, Game game) {
Player controller = game.getPlayer(ability.getControllerId());
if (controller != null) {
CardUtil.reduceCost(ability, xValue.calculate(game, ability, null));
}
}
}

View file

@ -6,7 +6,6 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.CostAdjuster;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
@ -45,12 +44,9 @@ public final class NemesisOfMortals extends CardImpl {
this.addAbility(ability);
// {7}{G}{G}: Monstrosity 5. This ability costs {1} less to activate for each creature card in your graveyard.
ability = new MonstrosityAbility("{7}{G}{G}", 5);
for (Effect effect : ability.getEffects()) {
effect.setText("Monstrosity 5. This ability costs {1} less to activate for each creature card in your graveyard");
}
ability.setCostAdjuster(NemesisOfMortalsAdjuster.instance);
this.addAbility(ability);
this.addAbility(new MonstrosityAbility("{7}{G}{G}", 5,
NemesisOfMortalsAdjuster.instance,
"This ability costs {1} less to activate for each creature card in your graveyard"));
}
private NemesisOfMortals(final NemesisOfMortals card) {

View file

@ -30,6 +30,7 @@ public final class JurassicWorldCollection extends ExpansionSet {
cards.add(new SetCardInfo("Ellie and Alan, Paleontologists", 10, Rarity.RARE, mage.cards.e.EllieAndAlanPaleontologists.class));
cards.add(new SetCardInfo("Forest", 25, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("Forest", "25b", Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("Grim Giganotosaurus", 11, Rarity.RARE, mage.cards.g.GrimGiganotosaurus.class));
cards.add(new SetCardInfo("Henry Wu, InGen Geneticist", 12, Rarity.RARE, mage.cards.h.HenryWuInGenGeneticist.class));
cards.add(new SetCardInfo("Hunting Velociraptor", 4, Rarity.RARE, mage.cards.h.HuntingVelociraptor.class));
cards.add(new SetCardInfo("Island", 22, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS));

View file

@ -0,0 +1,42 @@
package org.mage.test.cards.abilities.activated;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
public class NemesisOfMortalsTests extends CardTestPlayerBase {
@Test
public void testNoCostReduction() {
addCard(Zone.BATTLEFIELD, playerA, "Nemesis of Mortals");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 9);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{7}{G}{G}");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertTappedCount("Forest", true, 9);
}
@Test
public void testWithCostReduction() {
addCard(Zone.BATTLEFIELD, playerA, "Nemesis of Mortals");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 9);
addCard(Zone.GRAVEYARD, playerA, "Memnite", 4);
addCard(Zone.GRAVEYARD, playerA, "Forest", 2);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{7}{G}{G}");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertTappedCount("Forest", true, 5);
}
}

View file

@ -2,7 +2,9 @@ package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.CostAdjuster;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.hint.common.MonstrousHint;
import mage.constants.Outcome;
@ -44,15 +46,21 @@ public class MonstrosityAbility extends ActivatedAbilityImpl {
private final int monstrosityValue;
public MonstrosityAbility(String manaString, int monstrosityValue) {
this(manaString, monstrosityValue, null, "");
}
/**
* @param manaString
* @param monstrosityValue use Integer.MAX_VALUE for monstrosity X.
* @param costAdjuster
* @param costAdjusterText Clarifies the cost adjusting condition(s).
*/
public MonstrosityAbility(String manaString, int monstrosityValue) {
super(Zone.BATTLEFIELD, new BecomeMonstrousSourceEffect(monstrosityValue), new ManaCostsImpl<>(manaString));
public MonstrosityAbility(String manaString, int monstrosityValue, CostAdjuster costAdjuster, String costAdjusterText) {
super(Zone.BATTLEFIELD, new BecomeMonstrousSourceEffect(monstrosityValue, costAdjusterText), new ManaCostsImpl<>(manaString));
this.monstrosityValue = monstrosityValue;
this.addHint(MonstrousHint.instance);
setCostAdjuster(costAdjuster);
}
protected MonstrosityAbility(final MonstrosityAbility ability) {
@ -74,8 +82,12 @@ public class MonstrosityAbility extends ActivatedAbilityImpl {
class BecomeMonstrousSourceEffect extends OneShotEffect {
public BecomeMonstrousSourceEffect(int monstrosityValue) {
this(monstrosityValue, "");
}
public BecomeMonstrousSourceEffect(int monstrosityValue, String costAdjusterText) {
super(Outcome.BoostCreature);
this.staticText = setText(monstrosityValue);
this.staticText = setText(monstrosityValue, costAdjusterText);
}
protected BecomeMonstrousSourceEffect(final BecomeMonstrousSourceEffect effect) {
@ -110,9 +122,9 @@ class BecomeMonstrousSourceEffect extends OneShotEffect {
return true;
}
private String setText(int monstrosityValue) {
private String setText(int monstrosityValue, String costAdjusterText) {
return "Monstrosity " + (monstrosityValue == Integer.MAX_VALUE ? "X" : monstrosityValue) +
". <i>(If this creature isn't monstrous, put " +
". " + costAdjusterText + "<i>(If this creature isn't monstrous, put " +
(monstrosityValue == Integer.MAX_VALUE ? "X" : CardUtil.numberToText(monstrosityValue)) +
" +1/+1 counters on it and it becomes monstrous.)</i>";
}