[WOE] Implement Curse of the Werefox (#11009)

* [WOE] Implement Curse of the Werefox

* apply review

* Fix aura (and equipment?) tokens not checking for protection on target

* fix targetting of reflexive trigger, by creating a custom fight effect.

* fix reflexive ability target.
This commit is contained in:
Susucre 2023-08-31 01:16:22 +02:00 committed by GitHub
parent 2d9599fbbd
commit fab00d2f27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 217 additions and 2 deletions

View file

@ -0,0 +1,118 @@
package mage.cards.c;
import mage.abilities.Ability;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateRoleAttachedTargetEffect;
import mage.abilities.effects.common.FightTargetsEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.RoleType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.common.TargetOpponentsCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.GameLog;
import java.util.UUID;
/**
* @author Susucr
*/
public final class CurseOfTheWerefox extends CardImpl {
public CurseOfTheWerefox(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}");
// Create a Monster Role token attached to target creature you control. When you do, that creature fights up to one target creature you don't control.
this.getSpellAbility().addEffect(new CurseOfTheWerefoxEffect());
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
}
private CurseOfTheWerefox(final CurseOfTheWerefox card) {
super(card);
}
@Override
public CurseOfTheWerefox copy() {
return new CurseOfTheWerefox(this);
}
}
class CurseOfTheWerefoxEffect extends OneShotEffect {
CurseOfTheWerefoxEffect() {
super(Outcome.Benefit);
staticText = "create a Monster Role token attached to target creature you control. "
+ "When you do, that creature fights up to one target creature you don't control";
}
private CurseOfTheWerefoxEffect(final CurseOfTheWerefoxEffect effect) {
super(effect);
}
@Override
public CurseOfTheWerefoxEffect copy() {
return new CurseOfTheWerefoxEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent target = game.getPermanent(source.getFirstTarget());
if (target == null) {
return false;
}
boolean didCreate =
new CreateRoleAttachedTargetEffect(RoleType.MONSTER)
.setTargetPointer(new FixedTarget(target, game))
.apply(game, source);
if (!didCreate) {
return false;
}
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
new CurseOfTheWerefoxFightEffect(), false,
"that creature fights up to one target creature you don't control"
);
ability.getEffects().setTargetPointer(new FixedTarget(target.getId(), game));
ability.addTarget(new TargetCreaturePermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false));
game.fireReflexiveTriggeredAbility(ability, source);
return true;
}
}
class CurseOfTheWerefoxFightEffect extends OneShotEffect {
CurseOfTheWerefoxFightEffect() {
super(Outcome.Damage);
}
private CurseOfTheWerefoxFightEffect(final CurseOfTheWerefoxFightEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent triggeredCreature = game.getPermanent(this.targetPointer.getFirst(game, source));
Permanent target = game.getPermanent(source.getFirstTarget());
if (triggeredCreature != null
&& target != null
&& triggeredCreature.isCreature(game)
&& target.isCreature(game)) {
return triggeredCreature.fight(target, source, game);
}
return false;
}
@Override
public CurseOfTheWerefoxFightEffect copy() {
return new CurseOfTheWerefoxFightEffect(this);
}
}

View file

@ -65,6 +65,7 @@ public final class WildsOfEldraine extends ExpansionSet {
cards.add(new SetCardInfo("Cooped Up", 8, Rarity.COMMON, mage.cards.c.CoopedUp.class));
cards.add(new SetCardInfo("Cruel Somnophage", 222, Rarity.RARE, mage.cards.c.CruelSomnophage.class));
cards.add(new SetCardInfo("Crystal Grotto", 254, Rarity.COMMON, mage.cards.c.CrystalGrotto.class));
cards.add(new SetCardInfo("Curse of the Werefox", 167, Rarity.COMMON, mage.cards.c.CurseOfTheWerefox.class));
cards.add(new SetCardInfo("Cursed Courtier", 9, Rarity.UNCOMMON, mage.cards.c.CursedCourtier.class));
cards.add(new SetCardInfo("Cut In", 125, Rarity.COMMON, mage.cards.c.CutIn.class));
cards.add(new SetCardInfo("Decadent Dragon", 223, Rarity.RARE, mage.cards.d.DecadentDragon.class));

View file

@ -0,0 +1,88 @@
package org.mage.test.cards.single.woe;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class CurseOfTheWerefoxTest extends CardTestPlayerBase {
/**
* Curse of the Werefox
* {2}{G}
* Sorcery
* <p>
* Create a Monster Role token attached to target creature you control. When you do, that creature fights up to one target creature you dont control.
*/
private static final String curseWerefox = "Curse of the Werefox";
/**
* Nexus Wardens
* Creature Satyr Archer
* <p>
* Reach
* Constellation Whenever an enchantment enters the battlefield under your control, you gain 2 life.
* <p>
* 1/4
*/
private static final String wardens = "Nexus Wardens";
/**
* Azorius First-Wing
* {W}{U}
* Creature Griffin
* <p>
* Flying, protection from enchantments
* <p>
* 2/2
*/
private static final String azoriusFirstWing = "Azorius First-Wing";
// Checks that the "When you do" part of the ability does not trigger.
@Test
public void noTokenCreated() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, curseWerefox);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, wardens);
addCard(Zone.BATTLEFIELD, playerA, azoriusFirstWing);
addCard(Zone.BATTLEFIELD, playerB, wardens);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curseWerefox, azoriusFirstWing);
setStopAt(1, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20); // no Constellation trigger.
assertDamageReceived(playerA, wardens, 0);
assertDamageReceived(playerA, azoriusFirstWing, 0);
assertDamageReceived(playerB, wardens, 0);
assertPermanentCount(playerA, "Monster", 0);
}
@Test
public void usualBehavior() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, curseWerefox);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, wardens);
addCard(Zone.BATTLEFIELD, playerB, wardens);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curseWerefox, wardens);
setChoice(playerA, "<i>Constellation</i>");
addTarget(playerA, wardens); // Choosing second target for the fight.
setStopAt(1, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20 + 2); // Constellation Trigger.
assertPermanentCount(playerA, wardens, 1);
assertPermanentCount(playerB, wardens, 1);
assertDamageReceived(playerA, wardens, 1);
assertDamageReceived(playerB, wardens, 2);
assertPermanentCount(playerA, "Monster", 1);
}
}

View file

@ -7,6 +7,7 @@ import mage.constants.Outcome;
import mage.constants.RoleType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
/**
* @author TheElk801
@ -36,8 +37,9 @@ public class CreateRoleAttachedTargetEffect extends OneShotEffect {
if (permanent == null) {
return false;
}
roleType.createToken(permanent, game, source);
return true;
Token token = roleType.createToken(permanent, game, source);
// The token may not be created, for instance if the creature has protection from enchantments.
return token.getLastAddedTokenIds().size() > 0;
}
@Override

View file

@ -10,6 +10,8 @@ import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.game.permanent.token.Token;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.util.CardUtil;
@ -89,6 +91,10 @@ public class ProtectionAbility extends StaticAbility {
return !((FilterCard) filter).match((Card) source, ((Permanent) source).getControllerId(), this, game);
} else if (source instanceof Card) {
return !((FilterCard) filter).match((Card) source, ((Card) source).getOwnerId(), this, game);
} else if (source instanceof Token) {
// Fake a permanent with the Token info.
PermanentToken token = new PermanentToken((Token) source, null, game);
return !((FilterCard) filter).match((Card) token, game);
}
return true;
}