[C21] Implemented Fractal Harness

This commit is contained in:
Evan Kranzler 2021-04-27 08:47:09 -04:00
parent 2e5619e268
commit 1fad23b9fb
7 changed files with 152 additions and 131 deletions

View file

@ -0,0 +1,116 @@
package mage.cards.f;
import mage.abilities.Ability;
import mage.abilities.common.AttacksAttachedTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.EquipAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.QuandrixToken;
import mage.game.permanent.token.Token;
import mage.watchers.common.ManaSpentToCastWatcher;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class FractalHarness extends CardImpl {
public FractalHarness(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{X}{2}{G}");
this.subtype.add(SubType.EQUIPMENT);
// When Fractal Harness enters the battlefield, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it and attach Fractal Harness to it.
this.addAbility(new EntersBattlefieldTriggeredAbility(new FractalHarnessTokenEffect()), new ManaSpentToCastWatcher());
// Whenever equipped creature attacks, double the number of +1/+1 counters on it.
this.addAbility(new AttacksAttachedTriggeredAbility(
new FractalHarnessDoubleEffect(), AttachmentType.EQUIPMENT, false
));
// Equip {2}
this.addAbility(new EquipAbility(2));
}
private FractalHarness(final FractalHarness card) {
super(card);
}
@Override
public FractalHarness copy() {
return new FractalHarness(this);
}
}
class FractalHarnessTokenEffect extends OneShotEffect {
FractalHarnessTokenEffect() {
super(Outcome.Benefit);
staticText = "create a 0/0 green and blue Fractal creature token. " +
"Put X +1/+1 counters on it and attach {this} to it";
}
private FractalHarnessTokenEffect(final FractalHarnessTokenEffect effect) {
super(effect);
}
@Override
public FractalHarnessTokenEffect copy() {
return new FractalHarnessTokenEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Token token = new QuandrixToken();
token.putOntoBattlefield(1, game, source, source.getControllerId());
int xValue = ManacostVariableValue.instance.calculate(game, source, this);
boolean flag = true;
for (UUID tokenId : token.getLastAddedTokenIds()) {
Permanent permanent = game.getPermanent(tokenId);
if (permanent == null) {
continue;
}
if (flag && permanent.addAttachment(tokenId, source, game)) {
flag = false;
}
permanent.addCounters(CounterType.P1P1.createInstance(xValue), source.getControllerId(), source, game);
}
return true;
}
}
class FractalHarnessDoubleEffect extends OneShotEffect {
FractalHarnessDoubleEffect() {
super(Outcome.Benefit);
staticText = "double the number of +1/+1 counters on it";
}
private FractalHarnessDoubleEffect(final FractalHarnessDoubleEffect effect) {
super(effect);
}
@Override
public FractalHarnessDoubleEffect copy() {
return new FractalHarnessDoubleEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = (Permanent) getValue("attachedPermanent");
return permanent != null && permanent.addCounters(CounterType.P1P1.createInstance(
permanent.getCounters(game).getCount(CounterType.P1P1)
), source.getControllerId(), source, game);
}
}

View file

@ -5,22 +5,21 @@ import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.FilterSpell;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.target.TargetPermanent;
import mage.watchers.Watcher;
import mage.watchers.common.ManaSpentToCastWatcher;
import java.util.UUID;
@ -50,8 +49,8 @@ public final class GadwickTheWizened extends CardImpl {
// When Gadwick, the Wizened enters the battlefield, draw X cards.
this.addAbility(new EntersBattlefieldTriggeredAbility(
new DrawCardSourceControllerEffect(GadwickTheWizenedValue.instance)
), new GadwickTheWizenedWatcher());
new DrawCardSourceControllerEffect(ManacostVariableValue.instance)
), new ManaSpentToCastWatcher());
// Whenever you cast a blue spell, tap target nonland permanent an opponent controls.
Ability ability = new SpellCastControllerTriggeredAbility(new TapTargetEffect(), filter, false);
@ -68,60 +67,3 @@ public final class GadwickTheWizened extends CardImpl {
return new GadwickTheWizened(this);
}
}
enum GadwickTheWizenedValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
// watcher in card's scope
GadwickTheWizenedWatcher watcher = game.getState().getWatcher(GadwickTheWizenedWatcher.class, sourceAbility.getSourceId());
if (watcher == null) {
return 0;
}
if (game.getState().getValue(sourceAbility.getSourceId().toString()
+ "cardsToDraw") == null) {
return 0;
}
return (Integer) game.getState().getValue(sourceAbility.getSourceId().toString()
+ "cardsToDraw");
}
@Override
public DynamicValue copy() {
return instance;
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "";
}
}
class GadwickTheWizenedWatcher extends Watcher {
GadwickTheWizenedWatcher() {
super(WatcherScope.CARD);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpellOrLKIStack(event.getTargetId());
if (spell == null) {
return;
}
if (spell.getSourceId() != super.getSourceId()) {
return; // the spell is not Gadwick, the Wizened
}
game.getState().setValue(spell.getSourceId().toString()
+ "cardsToDraw", spell.getSpellAbility().getManaCostsToPay().getX());
}
}

View file

@ -8,6 +8,7 @@ import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.MultipliedValue;
import mage.abilities.dynamicvalue.common.CardsDrawnThisTurnDynamicValue;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.CreateTokenEffect;
@ -16,18 +17,20 @@ import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.ShardToken;
import mage.game.stack.Spell;
import mage.target.TargetPermanent;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.watchers.Watcher;
import mage.watchers.common.CardsDrawnThisTurnWatcher;
import mage.watchers.common.ManaSpentToCastWatcher;
import java.util.Objects;
import java.util.UUID;
@ -54,8 +57,8 @@ public final class NikoAris extends CardImpl {
// When Niko Aris enters the battlefield, create X Shard tokens.
this.addAbility(new EntersBattlefieldTriggeredAbility(
new CreateTokenEffect(new ShardToken(), NikoArisValue.instance)
), new NikoArisWatcher());
new CreateTokenEffect(new ShardToken(), ManacostVariableValue.instance)
), new ManaSpentToCastWatcher());
// +1: Up to one target creature you control can't be blocked this turn. Whenever that creature deals damage this turn, return it to its owner's hand.
Ability ability = new LoyaltyAbility(new CantBeBlockedTargetEffect(Duration.EndOfTurn), 1);
@ -84,59 +87,6 @@ public final class NikoAris extends CardImpl {
}
}
enum NikoArisValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
NikoArisWatcher watcher = game.getState().getWatcher(NikoArisWatcher.class, sourceAbility.getSourceId());
if (watcher == null) {
return 0;
}
if (game.getState().getValue(sourceAbility.getSourceId().toString() + "xValue") == null) {
return 0;
}
return (Integer) game.getState().getValue(sourceAbility.getSourceId().toString() + "xValue");
}
@Override
public DynamicValue copy() {
return instance;
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "";
}
}
class NikoArisWatcher extends Watcher {
NikoArisWatcher() {
super(WatcherScope.CARD);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpellOrLKIStack(event.getTargetId());
if (spell == null || spell.getSourceId() != super.getSourceId()) {
return;
}
game.getState().setValue(
spell.getSourceId().toString() + "xValue",
spell.getSpellAbility().getManaCostsToPay().getX()
);
}
}
class NikoArisDamageTriggeredAbility extends DelayedTriggeredAbility {
NikoArisDamageTriggeredAbility() {
@ -176,4 +126,4 @@ class NikoArisDamageTriggeredAbility extends DelayedTriggeredAbility {
public String getRule() {
return "Whenever that creature deals damage this turn, return it to its owner's hand.";
}
}
}

View file

@ -128,6 +128,7 @@ public final class Commander2021Edition extends ExpansionSet {
cards.add(new SetCardInfo("Fiery Fall", 170, Rarity.COMMON, mage.cards.f.FieryFall.class));
cards.add(new SetCardInfo("Forgotten Ancient", 189, Rarity.RARE, mage.cards.f.ForgottenAncient.class));
cards.add(new SetCardInfo("Forgotten Cave", 289, Rarity.COMMON, mage.cards.f.ForgottenCave.class));
cards.add(new SetCardInfo("Fractal Harness", 61, Rarity.RARE, mage.cards.f.FractalHarness.class));
cards.add(new SetCardInfo("Garruk, Primal Hunter", 190, Rarity.MYTHIC, mage.cards.g.GarrukPrimalHunter.class));
cards.add(new SetCardInfo("Gaze of Granite", 217, Rarity.RARE, mage.cards.g.GazeOfGranite.class));
cards.add(new SetCardInfo("Geometric Nexus", 77, Rarity.RARE, mage.cards.g.GeometricNexus.class));

View file

@ -1,7 +1,6 @@
package mage.abilities.common;
import java.util.Locale;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.AttachmentType;
@ -10,6 +9,8 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.Locale;
/**
* "When enchanted/equipped creature attacks " triggered ability
*
@ -52,9 +53,8 @@ public class AttacksAttachedTriggeredAbility extends TriggeredAbilityImpl {
Permanent equipment = game.getPermanent(this.sourceId);
if (equipment != null && equipment.getAttachedTo() != null
&& event.getSourceId().equals(equipment.getAttachedTo())) {
for (Effect effect : this.getEffects()) {
effect.setValue("sourceId", event.getSourceId());
}
getEffects().setValue("sourceId", event.getSourceId());
getEffects().setValue("attachedPermanent", game.getPermanent(event.getSourceId()));
return true;
}
return false;

View file

@ -3,14 +3,20 @@ package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.constants.AbilityType;
import mage.game.Game;
import mage.watchers.common.ManaSpentToCastWatcher;
public enum ManacostVariableValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return sourceAbility.getManaCostsToPay().getX();
if (sourceAbility.getAbilityType() == AbilityType.SPELL) {
return sourceAbility.getManaCostsToPay().getX();
}
ManaSpentToCastWatcher watcher = game.getState().getWatcher(ManaSpentToCastWatcher.class, sourceAbility.getSourceId());
return watcher != null ? watcher.getAndResetLastXValue() : sourceAbility.getManaCostsToPay().getX();
}
@Override

View file

@ -17,6 +17,7 @@ import mage.watchers.Watcher;
public class ManaSpentToCastWatcher extends Watcher {
private Mana payment = null;
private Integer xValue = 0;
public ManaSpentToCastWatcher() {
super(WatcherScope.CARD);
@ -29,12 +30,14 @@ public class ManaSpentToCastWatcher extends Watcher {
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null && this.getSourceId().equals(spell.getSourceId())) {
payment = spell.getSpellAbility().getManaCostsToPay().getUsedManaToPay();
xValue = spell.getSpellAbility().getManaCostsToPay().getX();
}
}
if (event.getType() == GameEvent.EventType.ZONE_CHANGE
&& this.getSourceId().equals(event.getSourceId())) {
if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
payment = null;
xValue = 0;
}
}
}
@ -45,13 +48,16 @@ public class ManaSpentToCastWatcher extends Watcher {
returnPayment = payment.copy();
}
return returnPayment;
}
public int getAndResetLastXValue() {
return xValue;
}
@Override
public void reset() {
super.reset();
payment = null;
xValue = 0;
}
}