mirror of
https://github.com/magefree/mage.git
synced 2026-01-10 21:02:08 -08:00
[LCI] Implement The Skullspore Nexus (#11327)
This commit is contained in:
parent
59929d2860
commit
a37fc0589a
6 changed files with 283 additions and 50 deletions
|
|
@ -1,25 +1,24 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.mana.SimpleManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -34,7 +33,9 @@ public final class TheGreatHenge extends CardImpl {
|
|||
this.supertype.add(SuperType.LEGENDARY);
|
||||
|
||||
// This spell costs {X} less to cast, where X is the greatest power among creatures you control.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheGreatHengeCostReductionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
Zone.ALL, new SpellCostReductionSourceEffect(GreatestPowerAmongControlledCreaturesValue.instance)
|
||||
).setRuleAtTheTop(true).addHint(GreatestPowerAmongControlledCreaturesValue.getHint()));
|
||||
|
||||
// {T}: Add {G}{G}. You gain 2 life.
|
||||
Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost());
|
||||
|
|
@ -59,42 +60,4 @@ public final class TheGreatHenge extends CardImpl {
|
|||
public TheGreatHenge copy() {
|
||||
return new TheGreatHenge(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TheGreatHengeCostReductionEffect extends CostModificationEffectImpl {
|
||||
|
||||
TheGreatHengeCostReductionEffect() {
|
||||
super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
staticText = "This spell costs {X} less to cast, where X is the greatest power among creatures you control";
|
||||
}
|
||||
|
||||
private TheGreatHengeCostReductionEffect(final TheGreatHengeCostReductionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
int reductionAmount = game.getBattlefield()
|
||||
.getAllActivePermanents(
|
||||
StaticFilters.FILTER_PERMANENT_CREATURE, abilityToModify.getControllerId(), game
|
||||
).stream()
|
||||
.map(Permanent::getPower)
|
||||
.mapToInt(MageInt::getValue)
|
||||
.max()
|
||||
.orElse(0);
|
||||
CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
return abilityToModify instanceof SpellAbility
|
||||
&& abilityToModify.getSourceId().equals(source.getSourceId())
|
||||
&& game.getCard(abilityToModify.getSourceId()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheGreatHengeCostReductionEffect copy() {
|
||||
return new TheGreatHengeCostReductionEffect(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
167
Mage.Sets/src/mage/cards/t/TheSkullsporeNexus.java
Normal file
167
Mage.Sets/src/mage/cards/t/TheSkullsporeNexus.java
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.MageItem;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeGroupEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.FungusDinosaurToken;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class TheSkullsporeNexus extends CardImpl {
|
||||
|
||||
public TheSkullsporeNexus(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}{G}{G}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
|
||||
// This spell costs {X} less to cast, where X is the greatest power among creatures you control.
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
Zone.ALL, new SpellCostReductionSourceEffect(GreatestPowerAmongControlledCreaturesValue.instance)
|
||||
).setRuleAtTheTop(true).addHint(GreatestPowerAmongControlledCreaturesValue.getHint()));
|
||||
|
||||
// Whenever one or more nontoken creatures you control die, create a green Fungus Dinosaur creature token with base power and toughness each equal to the total power of those creatures.
|
||||
this.addAbility(new TheSkullsporeNexusTrigger());
|
||||
|
||||
// {2}, {T}: Double target creature's power until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
new TheSkullsporeNexusDoubleEffect(),
|
||||
new GenericManaCost(2)
|
||||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private TheSkullsporeNexus(final TheSkullsporeNexus card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheSkullsporeNexus copy() {
|
||||
return new TheSkullsporeNexus(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TheSkullsporeNexusTrigger extends TriggeredAbilityImpl {
|
||||
|
||||
TheSkullsporeNexusTrigger() {
|
||||
super(Zone.BATTLEFIELD, null, false);
|
||||
}
|
||||
|
||||
private TheSkullsporeNexusTrigger(final TheSkullsporeNexusTrigger ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheSkullsporeNexusTrigger copy() {
|
||||
return new TheSkullsporeNexusTrigger(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event;
|
||||
if (!zEvent.isDiesEvent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Permanent> permanents = zEvent.getCards().stream()
|
||||
.map(MageItem::getId)
|
||||
.map(game::getPermanentOrLKIBattlefield)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(p -> p.isCreature(game) && p.isControlledBy(getControllerId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (permanents.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int amount = permanents.stream().mapToInt(p -> p.getPower().getValue()).sum();
|
||||
|
||||
this.getEffects().clear();
|
||||
Effect effect = new CreateTokenEffect(new FungusDinosaurToken(amount));
|
||||
effect.setValue(infoKey, amount);
|
||||
this.getEffects().add(effect);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final String infoKey = "totalpower";
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
// this trigger might want to expose extra info on the stack
|
||||
String triggeredInfo = "";
|
||||
if (!this.getEffects().isEmpty()) {
|
||||
triggeredInfo += "<br><br><i>Total power: " + this.getEffects().get(0).getValue(infoKey) + "</i>";
|
||||
}
|
||||
return "Whenever one or more nontoken creatures you control die, "
|
||||
+ "create a green Fungus Dinosaur creature token with base power and toughness "
|
||||
+ "each equal to the total power of those creatures." + triggeredInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
|
||||
}
|
||||
}
|
||||
|
||||
class TheSkullsporeNexusDoubleEffect extends OneShotEffect {
|
||||
|
||||
TheSkullsporeNexusDoubleEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "double target creature's power until end of turn";
|
||||
}
|
||||
|
||||
private TheSkullsporeNexusDoubleEffect(final TheSkullsporeNexusDoubleEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheSkullsporeNexusDoubleEffect copy() {
|
||||
return new TheSkullsporeNexusDoubleEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContinuousEffect boost = new BoostTargetEffect(permanent.getPower().getValue(), 0, Duration.EndOfTurn)
|
||||
.setTargetPointer(new FixedTarget(permanent, game));
|
||||
game.addEffect(boost, source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -30,5 +30,6 @@ public final class LostCavernsOfIxalan extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Plains", 287, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS));
|
||||
cards.add(new SetCardInfo("Swamp", 289, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS));
|
||||
cards.add(new SetCardInfo("Temple of Power", 317, Rarity.MYTHIC, mage.cards.t.TempleOfPower.class));
|
||||
cards.add(new SetCardInfo("The Skullspore Nexus", 212, Rarity.MYTHIC, mage.cards.t.TheSkullsporeNexus.class));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package org.mage.test.cards.single.lci;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class TheSkullsporeNexusTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.t.TheSkullsporeNexus} <br>
|
||||
* The Skullspore Nexus {6}{G}{G} <br>
|
||||
* Legendary Artifact <br>
|
||||
* This spell costs {X} less to cast, where X is the greatest power among creatures you control. <br>
|
||||
* Whenever one or more nontoken creatures you control die, create a green Fungus Dinosaur creature token with base power and toughness each equal to the total power of those creatures. <br>
|
||||
* {2}, {T}: Double target creature’s power until end of turn. <br>
|
||||
*/
|
||||
private static final String nexus = "The Skullspore Nexus";
|
||||
|
||||
@Test
|
||||
public void test_trigger() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, nexus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Butcher Ghoul"); // 1/1 undying
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); // 2/2 doesn't count as not controlled
|
||||
addCard(Zone.HAND, playerA, "Grave Titan"); // 6/6, etb with 2 2/2 tokens
|
||||
addCard(Zone.HAND, playerA, "Damnation", 2); // destroy all creatures.
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6 + 4 * 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grave Titan", true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Damnation", true);
|
||||
setChoice(playerA, "Whenever one or more nontoken creatures you control die"); // ordering triggers
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
// Only 2 creatures do count: 6 from Titan + 1 from Ghoul
|
||||
checkPT("after first damnation", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fungus Dinosaur Token", 7, 7);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Damnation", true);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
// only undying ghoul counts (for 2)
|
||||
assertPowerToughness(playerA, "Fungus Dinosaur Token", 2, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_activation() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, nexus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}: Double", "Grizzly Bears");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Grizzly Bears", 4, 2);
|
||||
assertTappedCount("Swamp", true, 2);
|
||||
assertTapped(nexus, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
package mage.game.events;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -18,7 +19,7 @@ public class ZoneChangeGroupEvent extends GameEvent {
|
|||
private final Set<PermanentToken> tokens;
|
||||
/* added this */ Ability source;
|
||||
|
||||
public ZoneChangeGroupEvent(Set<Card> cards, Set<PermanentToken> tokens, UUID sourceId, Ability source, UUID playerId, Zone fromZone, Zone toZone) {
|
||||
public ZoneChangeGroupEvent(Set<Card> cards, Set<PermanentToken> tokens, UUID sourceId, Ability source, UUID playerId, Zone fromZone, Zone toZone) {
|
||||
super(GameEvent.EventType.ZONE_CHANGE_GROUP, null, null, playerId);
|
||||
this.fromZone = fromZone;
|
||||
this.toZone = toZone;
|
||||
|
|
@ -35,6 +36,10 @@ public class ZoneChangeGroupEvent extends GameEvent {
|
|||
return toZone;
|
||||
}
|
||||
|
||||
public boolean isDiesEvent() {
|
||||
return (toZone == Zone.GRAVEYARD && fromZone == Zone.BATTLEFIELD);
|
||||
}
|
||||
|
||||
public Set<Card> getCards() {
|
||||
return cards;
|
||||
}
|
||||
|
|
@ -42,7 +47,7 @@ public class ZoneChangeGroupEvent extends GameEvent {
|
|||
public Set<PermanentToken> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
public Ability getSource() {
|
||||
return source;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class FungusDinosaurToken extends TokenImpl {
|
||||
|
||||
public FungusDinosaurToken(int xValue) {
|
||||
super("Fungus Dinosaur Token", "X/X green Fungus Dinosaur creature token");
|
||||
cardType.add(CardType.CREATURE);
|
||||
subtype.add(SubType.FUNGUS);
|
||||
subtype.add(SubType.DINOSAUR);
|
||||
color.setGreen(true);
|
||||
power = new MageInt(xValue);
|
||||
toughness = new MageInt(xValue);
|
||||
}
|
||||
|
||||
private FungusDinosaurToken(final FungusDinosaurToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public FungusDinosaurToken copy() {
|
||||
return new FungusDinosaurToken(this);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue