[NEO] Implemented Mechtitan Core

This commit is contained in:
Evan Kranzler 2022-02-08 18:38:40 -05:00
parent 33ba01e093
commit b100890cdf
5 changed files with 264 additions and 30 deletions

View file

@ -0,0 +1,183 @@
package mage.cards.m;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.CompositeCost;
import mage.abilities.costs.common.ExileSourceCost;
import mage.abilities.costs.common.ExileTargetCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.CrewAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.*;
import mage.filter.common.FilterControlledArtifactPermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.token.MechtitanToken;
import mage.game.permanent.token.Token;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class MechtitanCore extends CardImpl {
private static final FilterControlledPermanent filter
= new FilterControlledArtifactPermanent("other artifact creatures and/or Vehicles you control");
static {
filter.add(AnotherPredicate.instance);
filter.add(Predicates.or(
CardType.CREATURE.getPredicate(),
SubType.VEHICLE.getPredicate()
));
}
public MechtitanCore(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
this.subtype.add(SubType.VEHICLE);
this.power = new MageInt(2);
this.toughness = new MageInt(4);
// {5}, Exile Mechtitan Core and four other artifact creatures and/or Vehicles you control: Create Mechtitan, a legendary 10/10 Construct artifact creature token with flying, vigilance, trample, lifelink, and haste that's all colors. When that token leaves the battlefield, return all cards exiled with Mechtitan Core except Mechtitan Core to the battlefield tapped under their owners' control.
Ability ability = new SimpleActivatedAbility(new MechtitanCoreTokenEffect(), new GenericManaCost(5));
ability.addCost(new CompositeCost(
new ExileSourceCost(), new ExileTargetCost(new TargetControlledPermanent(4, filter)),
"exile {this} and four other artifact creatures and/or Vehicles you control"));
this.addAbility(ability);
// Crew 2
this.addAbility(new CrewAbility(2));
}
private MechtitanCore(final MechtitanCore card) {
super(card);
}
@Override
public MechtitanCore copy() {
return new MechtitanCore(this);
}
}
class MechtitanCoreTokenEffect extends OneShotEffect {
MechtitanCoreTokenEffect() {
super(Outcome.Benefit);
staticText = "create Mechtitan, a legendary 10/10 Construct artifact creature token with flying, " +
"vigilance, trample, lifelink, and haste that's all colors. " +
"When that token leaves the battlefield, return all cards exiled with {this} except " +
"{this} to the battlefield tapped under their owners' control";
}
private MechtitanCoreTokenEffect(final MechtitanCoreTokenEffect effect) {
super(effect);
}
@Override
public MechtitanCoreTokenEffect copy() {
return new MechtitanCoreTokenEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Token token = new MechtitanToken();
token.putOntoBattlefield(1, game, source);
game.addDelayedTriggeredAbility(new MechtitanCoreTriggeredAbility(token, source, game), source);
return true;
}
}
class MechtitanCoreTriggeredAbility extends DelayedTriggeredAbility {
private final Set<UUID> tokenIds = new HashSet<>();
MechtitanCoreTriggeredAbility(Token token, Ability source, Game game) {
super(new MechtitanCoreReturnEffect(), Duration.Custom, false, false);
this.getEffects().setTargetPointer(new FixedTargets(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)), game));
tokenIds.addAll(token.getLastAddedTokenIds());
}
private MechtitanCoreTriggeredAbility(final MechtitanCoreTriggeredAbility ability) {
super(ability);
this.tokenIds.addAll(ability.tokenIds);
}
@Override
public MechtitanCoreTriggeredAbility copy() {
return new MechtitanCoreTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return tokenIds.contains(event.getTargetId())
&& ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD;
}
@Override
public boolean isInactive(Game game) {
return tokenIds
.stream()
.map(game::getPermanent)
.noneMatch(Objects::nonNull);
}
@Override
public String getRule() {
return "When that token leaves the battlefield, return all cards exiled with {this} except " +
"{this} to the battlefield tapped under their owners' control";
}
}
class MechtitanCoreReturnEffect extends OneShotEffect {
MechtitanCoreReturnEffect() {
super(Outcome.Benefit);
}
private MechtitanCoreReturnEffect(final MechtitanCoreReturnEffect effect) {
super(effect);
}
@Override
public MechtitanCoreReturnEffect copy() {
return new MechtitanCoreReturnEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source));
return player.moveCards(
cards.getCards(game), Zone.BATTLEFIELD, source, game,
true, false, true, null
);
}
}
// and I'll form the head!

View file

@ -182,6 +182,7 @@ public final class KamigawaNeonDynasty extends ExpansionSet {
cards.add(new SetCardInfo("March of Wretched Sorrow", 111, Rarity.RARE, mage.cards.m.MarchOfWretchedSorrow.class));
cards.add(new SetCardInfo("Master's Rebuke", 202, Rarity.COMMON, mage.cards.m.MastersRebuke.class));
cards.add(new SetCardInfo("Mech Hangar", 270, Rarity.UNCOMMON, mage.cards.m.MechHangar.class));
cards.add(new SetCardInfo("Mechtitan Core", 249, Rarity.RARE, mage.cards.m.MechtitanCore.class));
cards.add(new SetCardInfo("Memory of Toshiro", 108, Rarity.UNCOMMON, mage.cards.m.MemoryOfToshiro.class));
cards.add(new SetCardInfo("Michiko's Reign of Truth", 29, Rarity.UNCOMMON, mage.cards.m.MichikosReignOfTruth.class));
cards.add(new SetCardInfo("Mindlink Mech", 62, Rarity.RARE, mage.cards.m.MindlinkMech.class));

View file

@ -1,7 +1,6 @@
package mage.abilities.costs.common;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -11,23 +10,23 @@ import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ExileSourceCost extends CostImpl {
private boolean toUniqueExileZone;
private final boolean toUniqueExileZone;
public ExileSourceCost() {
this.text = "exile {this}";
this(false);
}
/**
*
* @param toUniqueExileZone moves the card to a source object dependant
* unique exile zone, so another effect of the same source object (e.g.
* Deadeye Navigator) can identify the card
* unique exile zone, so another effect of the same source object (e.g.
* Deadeye Navigator) can identify the card
*/
public ExileSourceCost(boolean toUniqueExileZone) {
this.text = "exile {this}";
@ -52,7 +51,7 @@ public class ExileSourceCost extends CostImpl {
game.getState().setValue(sourceObject.getId().toString(), ability.getSourceObjectZoneChangeCounter());
}
controller.moveCardToExileWithInfo((Card) sourceObject, exileZoneId, exileZoneName, source, game, game.getState().getZone(sourceObject.getId()), true);
// 117.11. The actions performed when paying a cost may be modified by effects.
// 117.11. The actions performed when paying a cost may be modified by effects.
// Even if they are, meaning the actions that are performed don't match the actions
// that are called for, the cost has still been paid.
// so return state here is not important because the user indended to exile the target anyway

View file

@ -2,20 +2,23 @@
package mage.abilities.costs.common;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
import mage.util.CardUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author emerald000
*/
public class ExileTargetCost extends CostImpl {
@ -26,35 +29,43 @@ public class ExileTargetCost extends CostImpl {
this.addTarget(target);
this.text = "Exile " + target.getTargetName();
}
public ExileTargetCost(TargetControlledPermanent target, boolean noText) {
this.addTarget(target);
}
public ExileTargetCost(ExileTargetCost cost) {
super(cost);
for (Permanent permanent: cost.permanents) {
for (Permanent permanent : cost.permanents) {
this.permanents.add(permanent.copy());
}
}
@Override
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
if (targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) {
for (UUID targetId: targets.get(0).getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent == null) {
return false;
}
permanents.add(permanent.copy());
// 117.11. The actions performed when paying a cost may be modified by effects.
// Even if they are, meaning the actions that are performed don't match the actions
// that are called for, the cost has still been paid.
// so return state here is not important because the user indended to exile the target anyway
game.getPlayer(ability.getControllerId()).moveCardToExileWithInfo(permanent, null, null, source, game, Zone.BATTLEFIELD, true);
}
paid = true;
Player player = game.getPlayer(ability.getControllerId());
if (player == null || !targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) {
return paid;
}
Cards cards = new CardsImpl();
for (UUID targetId : targets.get(0).getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent == null) {
return false;
}
cards.add(permanent);
permanents.add(permanent.copy());
// 117.11. The actions performed when paying a cost may be modified by effects.
// Even if they are, meaning the actions that are performed don't match the actions
// that are called for, the cost has still been paid.
// so return state here is not important because the user indended to exile the target anyway
}
player.moveCardsToExile(
cards.getCards(game), source, game, false,
CardUtil.getExileZoneId(game, source),
CardUtil.getSourceName(game, source)
);
paid = true;
return paid;
}
@ -71,5 +82,4 @@ public class ExileTargetCost extends CostImpl {
public List<Permanent> getPermanents() {
return permanents;
}
}

View file

@ -0,0 +1,41 @@
package mage.game.permanent.token;
import mage.MageInt;
import mage.abilities.keyword.*;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
/**
* @author TheElk801
*/
public final class MechtitanToken extends TokenImpl {
public MechtitanToken() {
super("Mechtitan", "Mechtitan, a legendary 10/10 Construct artifact creature token with flying, vigilance, trample, lifelink, and haste that's all colors");
addSuperType(SuperType.LEGENDARY);
cardType.add(CardType.ARTIFACT);
cardType.add(CardType.CREATURE);
subtype.add(SubType.CONSTRUCT);
color.setWhite(true);
color.setBlue(true);
color.setBlack(true);
color.setRed(true);
color.setGreen(true);
power = new MageInt(10);
toughness = new MageInt(10);
addAbility(FlyingAbility.getInstance());
addAbility(VigilanceAbility.getInstance());
addAbility(TrampleAbility.getInstance());
addAbility(LifelinkAbility.getInstance());
addAbility(HasteAbility.getInstance());
}
public MechtitanToken(final MechtitanToken token) {
super(token);
}
public MechtitanToken copy() {
return new MechtitanToken(this);
}
}