Added Tarrian's Journal // The Tomb of Aclazotz

This commit is contained in:
Jeff Wadsworth 2023-12-23 00:01:59 -06:00
parent 5c677b8cce
commit 7adbf45258
4 changed files with 349 additions and 1 deletions

View file

@ -0,0 +1,69 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package mage.cards.t;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AnotherPredicate;
/**
*
* @author jeffwadsworth
*/
public class TarriansJournal extends CardImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("another artifact or creature");
static {
filter.add(AnotherPredicate.instance);
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
CardType.CREATURE.getPredicate()
));
}
public TarriansJournal(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}");
this.supertype.add(SuperType.LEGENDARY);
this.secondSideCardClazz = mage.cards.t.TheTombOfAclazotz.class;
this.color.setBlack(true);
// T: Sacrifice another artifact or creature: Draw a card. Activate only as a sorcery.
Ability ability = new ActivateAsSorceryActivatedAbility(new DrawCardSourceControllerEffect(1), new TapSourceCost());
ability.addCost(new SacrificeTargetCost(filter));
this.addAbility(ability);
this.addAbility(new TransformAbility());
Ability transformAbility = new SimpleActivatedAbility(new TransformSourceEffect(), new ManaCostsImpl("{2}"));
transformAbility.addCost(new TapSourceCost());
this.addAbility(transformAbility);
}
private TarriansJournal(final TarriansJournal card) {
super(card);
}
@Override
public TarriansJournal copy() {
return new TarriansJournal(this);
}
}

View file

@ -0,0 +1,276 @@
package mage.cards.t;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.MageIdentifier;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.counter.AddCounterEnteringCreatureEffect;
import mage.abilities.mana.BlackManaAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.watchers.Watcher;
/**
*
* @author jeffwadsworth
*/
public class TheTombOfAclazotz extends CardImpl {
public TheTombOfAclazotz(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.LAND}, null);
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.CAVE);
// this is the second face of Tarrian's Journal
this.nightCard = true;
// {T}: Add {B}.
this.addAbility(new BlackManaAbility());
// You may cast a creature spell from your graveyard this turn. If you do, it enters the battlefield with a finality counter on it and is a Vampire in addition to its other types.
Ability castSpellAbility = new SimpleActivatedAbility(new TheTombOfAclazotzEffect(), new TapSourceCost());
castSpellAbility.setIdentifier(MageIdentifier.TheTombOfAclazotzWatcher);
castSpellAbility.addWatcher(new TheTombOfAclazotzWatcher());
this.addAbility(castSpellAbility);
}
private TheTombOfAclazotz(final TheTombOfAclazotz card) {
super(card);
}
@Override
public TheTombOfAclazotz copy() {
return new TheTombOfAclazotz(this);
}
}
class TheTombOfAclazotzEffect extends AsThoughEffectImpl {
TheTombOfAclazotzEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
staticText = "You may cast a creature spell from your graveyard this turn. If you do, it enters the battlefield with a finality counter on it and is a Vampire in addition to its other types. (If a creature with a finality counter on it would die, exile it instead.)";
}
private TheTombOfAclazotzEffect(final TheTombOfAclazotzEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public TheTombOfAclazotzEffect copy() {
return new TheTombOfAclazotzEffect(this);
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
TheTombOfAclazotzWatcher watcher = game.getState().getWatcher(TheTombOfAclazotzWatcher.class);
if (watcher != null) {
watcher.addPlayable(source, game);
watcher.addPlayFromAnywhereEffect(this.getId());
}
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
TheTombOfAclazotzWatcher watcher = game.getState().getWatcher(TheTombOfAclazotzWatcher.class);
if (watcher == null
|| !watcher.checkPermission(playerId, source, game)
|| game.getState().getZone(objectId) != Zone.GRAVEYARD) {
return false;
}
Card card = game.getCard(objectId);
if (card != null
&& affectedAbility instanceof SpellAbility
&& card.getOwnerId().equals(playerId)
&& card.isCreature(game)) {
return true;
}
return false;
}
}
class TheTombOfAclazotzWatcher extends Watcher {
private final Map<MageObjectReference, Map<UUID, Integer>> morMap = new HashMap<>();
private MageObjectReference mor;
private UUID playFromAnywhereEffectId;
TheTombOfAclazotzWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (GameEvent.EventType.SPELL_CAST.equals(event.getType())
&& event.hasApprovingIdentifier(MageIdentifier.TheTombOfAclazotzWatcher)) {
Spell target = game.getSpell(event.getTargetId());
Card card = target.getCard();
if (card != null) {
mor = new MageObjectReference(card.getId(), card.getZoneChangeCounter(game), game);
if (mor != null) {
game.getState().addEffect(new AddCounterEnteringCreatureEffect(new MageObjectReference(target.getCard(), game),
CounterType.FINALITY.createInstance(), Outcome.Neutral),
target.getSpellAbility());
game.getState().addEffect(new AddSubtypeEnteringCreatureEffect(new MageObjectReference(target.getCard(), game), SubType.VAMPIRE, Outcome.Benefit), target.getSpellAbility());
// Rule 728.2 we must insure the effect is used (creature is cast successfully) before discarding the play effect
UUID playEffectId = this.getPlayFromAnywhereEffect();
if (playEffectId != null
&& game.getContinuousEffects().getApplicableAsThoughEffects(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, game).listIterator().next().getId().equals(playEffectId)) {
// discard the play effect
game.getContinuousEffects().getApplicableAsThoughEffects(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, game).listIterator().next().discard();
}
}
}
}
}
boolean checkPermission(UUID playerId, Ability source, Game game) {
if (!playerId.equals(source.getControllerId())) {
return false;
}
MageObjectReference mor = new MageObjectReference(
source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
);
return morMap.computeIfAbsent(mor, m -> new HashMap<>()).getOrDefault(playerId, 0) > 0;
}
void addPlayable(Ability source, Game game) {
MageObjectReference mor = new MageObjectReference(
source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
);
morMap.computeIfAbsent(mor, m -> new HashMap<>())
.compute(source.getControllerId(), CardUtil::setOrIncrementValue);
}
void addPlayFromAnywhereEffect(UUID uuid) {
playFromAnywhereEffectId = uuid;
}
UUID getPlayFromAnywhereEffect() {
return playFromAnywhereEffectId;
}
@Override
public void reset() {
morMap.clear();
super.reset();
}
}
class AddSubtypeEnteringCreatureEffect extends ReplacementEffectImpl {
private final MageObjectReference mor;
private final SubType subType;
public AddSubtypeEnteringCreatureEffect(MageObjectReference mor, SubType subType, Outcome outcome) {
super(Duration.WhileOnBattlefield, outcome);
this.mor = mor;
this.subType = subType;
}
private AddSubtypeEnteringCreatureEffect(final AddSubtypeEnteringCreatureEffect effect) {
super(effect);
this.mor = effect.mor;
this.subType = effect.subType;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
return permanent != null
&& mor.refersTo(permanent, game);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent target = ((EntersTheBattlefieldEvent) event).getTarget();
if (target != null) {
AddCardSubTypeEnteringTargetEffect effect = new AddCardSubTypeEnteringTargetEffect(subType, Duration.WhileOnBattlefield);
effect.setTargetPointer(new FixedTarget(target, game));
game.addEffect(effect, source);
}
return false;
}
@Override
public AddSubtypeEnteringCreatureEffect copy() {
return new AddSubtypeEnteringCreatureEffect(this);
}
}
class AddCardSubTypeEnteringTargetEffect extends ContinuousEffectImpl {
private final SubType addedSubType;
public AddCardSubTypeEnteringTargetEffect(SubType addedSubType, Duration duration) {
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
this.addedSubType = addedSubType;
}
protected AddCardSubTypeEnteringTargetEffect(final AddCardSubTypeEnteringTargetEffect effect) {
super(effect);
this.addedSubType = effect.addedSubType;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent target = game.getPermanent(game.getObject(targetPointer.getFixedTarget(game, source).getTarget()).getId());
if (target != null) {
target.addSubType(game, addedSubType);
}
if (target == null) {
discard();
}
return false;
}
@Override
public AddCardSubTypeEnteringTargetEffect copy() {
return new AddCardSubTypeEnteringTargetEffect(this);
}
}

View file

@ -313,6 +313,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet {
cards.add(new SetCardInfo("Swamp", 397, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 397, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Swashbuckler's Whip", 263, Rarity.UNCOMMON, mage.cards.s.SwashbucklersWhip.class)); cards.add(new SetCardInfo("Swashbuckler's Whip", 263, Rarity.UNCOMMON, mage.cards.s.SwashbucklersWhip.class));
cards.add(new SetCardInfo("Synapse Necromage", 125, Rarity.UNCOMMON, mage.cards.s.SynapseNecromage.class)); cards.add(new SetCardInfo("Synapse Necromage", 125, Rarity.UNCOMMON, mage.cards.s.SynapseNecromage.class));
cards.add(new SetCardInfo("Tarrian's Journal", 126, Rarity.RARE, mage.cards.t.TarriansJournal.class));
cards.add(new SetCardInfo("Tarrian's Soulcleaver", 264, Rarity.RARE, mage.cards.t.TarriansSoulcleaver.class)); cards.add(new SetCardInfo("Tarrian's Soulcleaver", 264, Rarity.RARE, mage.cards.t.TarriansSoulcleaver.class));
cards.add(new SetCardInfo("Tectonic Hazard", 169, Rarity.COMMON, mage.cards.t.TectonicHazard.class)); cards.add(new SetCardInfo("Tectonic Hazard", 169, Rarity.COMMON, mage.cards.t.TectonicHazard.class));
cards.add(new SetCardInfo("Tecutlan, the Searing Rift", 135, Rarity.RARE, mage.cards.t.TecutlanTheSearingRift.class)); cards.add(new SetCardInfo("Tecutlan, the Searing Rift", 135, Rarity.RARE, mage.cards.t.TecutlanTheSearingRift.class));
@ -333,6 +334,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet {
cards.add(new SetCardInfo("The Mycotyrant", 235, Rarity.MYTHIC, mage.cards.t.TheMycotyrant.class)); cards.add(new SetCardInfo("The Mycotyrant", 235, Rarity.MYTHIC, mage.cards.t.TheMycotyrant.class));
cards.add(new SetCardInfo("The Myriad Pools", 363, Rarity.RARE, mage.cards.t.TheMyriadPools.class)); cards.add(new SetCardInfo("The Myriad Pools", 363, Rarity.RARE, mage.cards.t.TheMyriadPools.class));
cards.add(new SetCardInfo("The Skullspore Nexus", 212, Rarity.MYTHIC, mage.cards.t.TheSkullsporeNexus.class)); cards.add(new SetCardInfo("The Skullspore Nexus", 212, Rarity.MYTHIC, mage.cards.t.TheSkullsporeNexus.class));
cards.add(new SetCardInfo("The Tomb of Aclazotz", 126, Rarity.RARE, mage.cards.t.TheTombOfAclazotz.class));
cards.add(new SetCardInfo("Thousand Moons Crackshot", 37, Rarity.COMMON, mage.cards.t.ThousandMoonsCrackshot.class)); cards.add(new SetCardInfo("Thousand Moons Crackshot", 37, Rarity.COMMON, mage.cards.t.ThousandMoonsCrackshot.class));
cards.add(new SetCardInfo("Thousand Moons Infantry", 38, Rarity.COMMON, mage.cards.t.ThousandMoonsInfantry.class)); cards.add(new SetCardInfo("Thousand Moons Infantry", 38, Rarity.COMMON, mage.cards.t.ThousandMoonsInfantry.class));
cards.add(new SetCardInfo("Thousand Moons Smithy", 39, Rarity.RARE, mage.cards.t.ThousandMoonsSmithy.class)); cards.add(new SetCardInfo("Thousand Moons Smithy", 39, Rarity.RARE, mage.cards.t.ThousandMoonsSmithy.class));

View file

@ -67,7 +67,8 @@ public enum MageIdentifier {
ScourgeOfNelTothAlternateCast, ScourgeOfNelTothAlternateCast,
SqueeDubiousMonarchAlternateCast, SqueeDubiousMonarchAlternateCast,
WorldheartPhoenixAlternateCast, WorldheartPhoenixAlternateCast,
XandersPactAlternateCast; XandersPactAlternateCast,
TheTombOfAclazotzWatcher;
/** /**
* Additional text if there is need to differentiate two very similar effects * Additional text if there is need to differentiate two very similar effects