mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 19:11:59 -08:00
[OTJ] Implement Vengeful Townsfolk
This commit is contained in:
parent
503368b54c
commit
559be38b00
4 changed files with 263 additions and 0 deletions
52
Mage.Sets/src/mage/cards/v/VengefulTownsfolk.java
Normal file
52
Mage.Sets/src/mage/cards/v/VengefulTownsfolk.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package mage.cards.v;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.DiesOneOrMoreCreatureTriggeredAbility;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class VengefulTownsfolk extends CardImpl {
|
||||
|
||||
public static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other creatures you control");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
filter.add(TargetController.YOU.getControllerPredicate());
|
||||
}
|
||||
|
||||
public VengefulTownsfolk(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.CITIZEN);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Whenever one or more other creatures you control die, put a +1/+1 counter on Vengeful Townsfolk.
|
||||
this.addAbility(new DiesOneOrMoreCreatureTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)),
|
||||
filter
|
||||
));
|
||||
}
|
||||
|
||||
private VengefulTownsfolk(final VengefulTownsfolk card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VengefulTownsfolk copy() {
|
||||
return new VengefulTownsfolk(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -171,6 +171,7 @@ public final class OutlawsOfThunderJunction extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Treasure Dredger", 110, Rarity.UNCOMMON, mage.cards.t.TreasureDredger.class));
|
||||
cards.add(new SetCardInfo("Tumbleweed Rising", 187, Rarity.COMMON, mage.cards.t.TumbleweedRising.class));
|
||||
cards.add(new SetCardInfo("Unscrupulous Contractor", 112, Rarity.UNCOMMON, mage.cards.u.UnscrupulousContractor.class));
|
||||
cards.add(new SetCardInfo("Vengeful Townsfolk", 37, Rarity.COMMON, mage.cards.v.VengefulTownsfolk.class));
|
||||
cards.add(new SetCardInfo("Vial Smasher, Gleeful Grenadier", 235, Rarity.UNCOMMON, mage.cards.v.VialSmasherGleefulGrenadier.class));
|
||||
cards.add(new SetCardInfo("Visage Bandit", 76, Rarity.UNCOMMON, mage.cards.v.VisageBandit.class));
|
||||
cards.add(new SetCardInfo("Voracious Varmint", 188, Rarity.COMMON, mage.cards.v.VoraciousVarmint.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
package org.mage.test.cards.triggers.dies;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* Tests the {@link mage.abilities.common.DiesOneOrMoreCreatureTriggeredAbility} batching.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class VengefulTownsfolkTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.v.VengefulTownsfolk Vengeful Townsfolk}
|
||||
* Creature — Human Citizen
|
||||
* Whenever one or more other creatures you control die, put a +1/+1 counter on Vengeful Townsfolk.
|
||||
* 3/3
|
||||
*/
|
||||
private static final String townsfolk = "Vengeful Townsfolk";
|
||||
|
||||
// Choose up to one creature. Destroy the rest.
|
||||
private static final String duneblast = "Duneblast";
|
||||
|
||||
@Test
|
||||
public void testOnTokens() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
|
||||
addCard(Zone.HAND, playerA, "Raise the Alarm", 1); // 2 1/1 tokens
|
||||
addCard(Zone.HAND, playerB, duneblast, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Plains", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raise the Alarm");
|
||||
setChoice(playerB, townsfolk); // do not destroy townsfolk
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, duneblast);
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, duneblast, 1);
|
||||
assertPermanentCount(playerA, 1 + 2);
|
||||
assertPermanentCount(playerA, townsfolk, 1);
|
||||
assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnNonToken() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 3);
|
||||
addCard(Zone.HAND, playerB, duneblast, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Plains", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
|
||||
setChoice(playerB, townsfolk); // do not destroy townsfolk
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, duneblast);
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, duneblast, 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 3);
|
||||
assertPermanentCount(playerA, 1);
|
||||
assertPermanentCount(playerA, townsfolk, 1);
|
||||
assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoSeparateDestroy() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1);
|
||||
addCard(Zone.HAND, playerB, "Doom Blade", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Elite Vanguard");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Grizzly Bears");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Doom Blade", 2);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
assertGraveyardCount(playerA, "Elite Vanguard", 1);
|
||||
assertPermanentCount(playerA, 1);
|
||||
assertPermanentCount(playerA, townsfolk, 1);
|
||||
assertPowerToughness(playerA, townsfolk, 3 + 2, 3 + 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControllerMatters() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1);
|
||||
addCard(Zone.HAND, playerB, "Doom Blade", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Elite Vanguard");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Grizzly Bears");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Doom Blade", 2);
|
||||
assertGraveyardCount(playerB, "Grizzly Bears", 1);
|
||||
assertGraveyardCount(playerA, "Elite Vanguard", 1);
|
||||
assertPermanentCount(playerA, 1);
|
||||
assertPermanentCount(playerA, townsfolk, 1);
|
||||
assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleSacrifice() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
|
||||
addCard(Zone.HAND, playerB, "Barter in Blood", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Barter in Blood");
|
||||
// sacrificing both, those are moved to graveyard at same time
|
||||
setChoice(playerA, "Grizzly Bears");
|
||||
setChoice(playerA, "Elite Vanguard");
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Barter in Blood", 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
assertGraveyardCount(playerA, "Elite Vanguard", 1);
|
||||
assertPermanentCount(playerA, 1);
|
||||
assertPermanentCount(playerA, townsfolk, 1);
|
||||
assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeBatchEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class DiesOneOrMoreCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final FilterCreaturePermanent filter;
|
||||
|
||||
public DiesOneOrMoreCreatureTriggeredAbility(Effect effect, FilterCreaturePermanent filter) {
|
||||
super(Zone.BATTLEFIELD, effect, false);
|
||||
this.filter = filter;
|
||||
this.setTriggerPhrase("Whenever one or more " + filter.getMessage() + " die,");
|
||||
}
|
||||
|
||||
private DiesOneOrMoreCreatureTriggeredAbility(final DiesOneOrMoreCreatureTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiesOneOrMoreCreatureTriggeredAbility copy() {
|
||||
return new DiesOneOrMoreCreatureTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return ((ZoneChangeBatchEvent) event)
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(ZoneChangeEvent::isDiesEvent)
|
||||
.map(ZoneChangeEvent::getTargetId)
|
||||
.map(game::getPermanentOrLKIBattlefield)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(p -> filter.match(p, getControllerId(), this, game));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
return ((ZoneChangeBatchEvent) event)
|
||||
.getEvents()
|
||||
.stream()
|
||||
.allMatch(e -> TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, e, game));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue