mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
implement [CLB] Hezrou
This commit is contained in:
parent
94db57e9d4
commit
24687eb4af
6 changed files with 237 additions and 55 deletions
121
Mage.Sets/src/mage/cards/h/Hezrou.java
Normal file
121
Mage.Sets/src/mage/cards/h/Hezrou.java
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
package mage.cards.h;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.MageObjectReference;
|
||||||
|
import mage.abilities.TriggeredAbilityImpl;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostAllEffect;
|
||||||
|
import mage.cards.AdventureCard;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
|
import mage.filter.predicate.permanent.BlockedThisTurnPredicate;
|
||||||
|
import mage.filter.predicate.permanent.BlockingPredicate;
|
||||||
|
import mage.game.Controllable;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.combat.CombatGroup;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.watchers.common.BlockedThisTurnWatcher;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xenohedron
|
||||||
|
*/
|
||||||
|
public final class Hezrou extends AdventureCard {
|
||||||
|
|
||||||
|
private static final FilterCreaturePermanent filterBlocked = new FilterCreaturePermanent("each creature that blocked this turn");
|
||||||
|
private static final FilterCreaturePermanent filterBlocking = new FilterCreaturePermanent("each blocking creature");
|
||||||
|
static {
|
||||||
|
filterBlocked.add(BlockedThisTurnPredicate.instance);
|
||||||
|
filterBlocking.add(BlockingPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Hezrou(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new CardType[]{CardType.INSTANT}, "{5}{B}{B}", "Demonic Stench", "{B}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.FROG);
|
||||||
|
this.subtype.add(SubType.DEMON);
|
||||||
|
this.power = new MageInt(6);
|
||||||
|
this.toughness = new MageInt(6);
|
||||||
|
|
||||||
|
// Whenever one or more creatures you control become blocked, each blocking creature gets -1/-1 until end of turn.
|
||||||
|
this.addAbility(new HezrouTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filterBlocking, false)));
|
||||||
|
|
||||||
|
// Demonic Stench
|
||||||
|
// Each creature that blocked this turn gets -1/-1 until end of turn.
|
||||||
|
this.getSpellCard().getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filterBlocked, false));
|
||||||
|
this.getSpellCard().getSpellAbility().addWatcher(new BlockedThisTurnWatcher());
|
||||||
|
|
||||||
|
this.finalizeAdventure();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Hezrou(final Hezrou card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Hezrou copy() {
|
||||||
|
return new Hezrou(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HezrouTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
|
HezrouTriggeredAbility(Effect effect) {
|
||||||
|
super(Zone.BATTLEFIELD, effect);
|
||||||
|
setTriggerPhrase("Whenever one or more creatures you control become blocked, ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private HezrouTriggeredAbility(final HezrouTriggeredAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP
|
||||||
|
|| event.getType() == GameEvent.EventType.BATCH_BLOCK_NONCOMBAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
|
switch (event.getType()) {
|
||||||
|
case DECLARE_BLOCKERS_STEP:
|
||||||
|
return game.getCombat()
|
||||||
|
.getGroups()
|
||||||
|
.stream()
|
||||||
|
.filter(CombatGroup::getBlocked)
|
||||||
|
.map(CombatGroup::getAttackers)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(game::getControllerId)
|
||||||
|
.anyMatch(this.getControllerId()::equals);
|
||||||
|
case BATCH_BLOCK_NONCOMBAT:
|
||||||
|
Object value = game.getState().getValue("becameBlocked_" + event.getData());
|
||||||
|
if (!(value instanceof Set)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Set<MageObjectReference> permanents = (Set<MageObjectReference>) value;
|
||||||
|
return permanents
|
||||||
|
.stream()
|
||||||
|
.map(mor -> mor.getPermanentOrLKIBattlefield(game))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(Controllable::getControllerId)
|
||||||
|
.anyMatch(this.getControllerId()::equals);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HezrouTriggeredAbility copy() {
|
||||||
|
return new HezrouTriggeredAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,15 @@
|
||||||
package mage.cards.s;
|
package mage.cards.s;
|
||||||
|
|
||||||
import mage.MageObjectReference;
|
|
||||||
import mage.abilities.effects.common.DamageTargetEffect;
|
import mage.abilities.effects.common.DamageTargetEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.WatcherScope;
|
|
||||||
import mage.filter.FilterPermanent;
|
import mage.filter.FilterPermanent;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.Predicate;
|
import mage.filter.predicate.permanent.BlockedThisTurnPredicate;
|
||||||
import mage.game.Game;
|
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.common.BlockedThisTurnWatcher;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -27,7 +20,7 @@ public final class SizzlingBarrage extends CardImpl {
|
||||||
private static final FilterPermanent filter = new FilterCreaturePermanent("creature that blocked this turn");
|
private static final FilterPermanent filter = new FilterCreaturePermanent("creature that blocked this turn");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filter.add(SizzlingBarragePredicate.instance);
|
filter.add(BlockedThisTurnPredicate.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SizzlingBarrage(UUID ownerId, CardSetInfo setInfo) {
|
public SizzlingBarrage(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
|
@ -36,7 +29,7 @@ public final class SizzlingBarrage extends CardImpl {
|
||||||
// Sizzling Barrage deals 4 damage to target creature that blocked this turn.
|
// Sizzling Barrage deals 4 damage to target creature that blocked this turn.
|
||||||
this.getSpellAbility().addEffect(new DamageTargetEffect(4));
|
this.getSpellAbility().addEffect(new DamageTargetEffect(4));
|
||||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||||
this.getSpellAbility().addWatcher(new SizzlingBarrageWatcher());
|
this.getSpellAbility().addWatcher(new BlockedThisTurnWatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SizzlingBarrage(final SizzlingBarrage card) {
|
private SizzlingBarrage(final SizzlingBarrage card) {
|
||||||
|
|
@ -48,40 +41,3 @@ public final class SizzlingBarrage extends CardImpl {
|
||||||
return new SizzlingBarrage(this);
|
return new SizzlingBarrage(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SizzlingBarragePredicate implements Predicate<Permanent> {
|
|
||||||
instance;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Permanent input, Game game) {
|
|
||||||
SizzlingBarrageWatcher watcher = game.getState().getWatcher(SizzlingBarrageWatcher.class);
|
|
||||||
return watcher != null && watcher.checkCreature(input, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SizzlingBarrageWatcher extends Watcher {
|
|
||||||
|
|
||||||
private final Set<MageObjectReference> blockers = new HashSet<>();
|
|
||||||
|
|
||||||
SizzlingBarrageWatcher() {
|
|
||||||
super(WatcherScope.GAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void watch(GameEvent event, Game game) {
|
|
||||||
if (event.getType() != GameEvent.EventType.BLOCKER_DECLARED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
blockers.add(new MageObjectReference(event.getSourceId(), game));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
super.reset();
|
|
||||||
this.blockers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean checkCreature(Permanent permanent, Game game) {
|
|
||||||
return blockers.stream().anyMatch(mor -> mor.refersTo(permanent, game));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,7 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Hedron Archive", 861, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
|
cards.add(new SetCardInfo("Hedron Archive", 861, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
|
||||||
cards.add(new SetCardInfo("Herald's Horn", 862, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class));
|
cards.add(new SetCardInfo("Herald's Horn", 862, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class));
|
||||||
cards.add(new SetCardInfo("Hex", 757, Rarity.RARE, mage.cards.h.Hex.class));
|
cards.add(new SetCardInfo("Hex", 757, Rarity.RARE, mage.cards.h.Hex.class));
|
||||||
|
cards.add(new SetCardInfo("Hezrou", 131, Rarity.COMMON, mage.cards.h.Hezrou.class));
|
||||||
cards.add(new SetCardInfo("High Priest of Penance", 848, Rarity.RARE, mage.cards.h.HighPriestOfPenance.class));
|
cards.add(new SetCardInfo("High Priest of Penance", 848, Rarity.RARE, mage.cards.h.HighPriestOfPenance.class));
|
||||||
cards.add(new SetCardInfo("Highland Forest", 896, Rarity.COMMON, mage.cards.h.HighlandForest.class));
|
cards.add(new SetCardInfo("Highland Forest", 896, Rarity.COMMON, mage.cards.h.HighlandForest.class));
|
||||||
cards.add(new SetCardInfo("Hoarding Ogre", 181, Rarity.COMMON, mage.cards.h.HoardingOgre.class));
|
cards.add(new SetCardInfo("Hoarding Ogre", 181, Rarity.COMMON, mage.cards.h.HoardingOgre.class));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.mage.test.cards.single.clb;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
public class HezrouTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
private static final String hezrou = "Hezrou"; // 6/6
|
||||||
|
// Whenever one or more creatures you control become blocked, each blocking creature gets -1/-1 until end of turn.
|
||||||
|
private static final String stench = "Demonic Stench"; // {B} Instant
|
||||||
|
// Each creature that blocked this turn gets -1/-1 until end of turn.
|
||||||
|
|
||||||
|
private static final String kraken = "Kraken Hatchling"; // 0/4
|
||||||
|
private static final String guard = "Maritime Guard"; // 1/3
|
||||||
|
private static final String fortress = "Fortress Crab"; // 1/6
|
||||||
|
private static final String turtle = "Aegis Turtle"; // 0/5
|
||||||
|
private static final String pangolin = "Gloom Pangolin"; // 1/5
|
||||||
|
private static final String wishcoin = "Wishcoin Crab"; // 2/5
|
||||||
|
|
||||||
|
private void setupCreatures() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, kraken);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, guard);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, fortress);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, turtle);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, pangolin);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, wishcoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTrigger() {
|
||||||
|
setupCreatures();
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, hezrou);
|
||||||
|
|
||||||
|
attack(1, playerA, kraken, playerB);
|
||||||
|
attack(1, playerA, guard, playerB);
|
||||||
|
attack(1, playerA, fortress, playerB);
|
||||||
|
block(1, playerB, turtle, kraken);
|
||||||
|
block(1, playerB, pangolin, guard);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 19);
|
||||||
|
assertPowerToughness(playerA, kraken, 0, 4);
|
||||||
|
assertPowerToughness(playerA, guard, 1, 3);
|
||||||
|
assertPowerToughness(playerA, fortress, 1, 6);
|
||||||
|
assertPowerToughness(playerB, turtle, -1, 4);
|
||||||
|
assertPowerToughness(playerB, pangolin, 0, 4);
|
||||||
|
assertPowerToughness(playerB, wishcoin, 2, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdventure() {
|
||||||
|
setupCreatures();
|
||||||
|
addCard(Zone.HAND, playerA, hezrou);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||||
|
|
||||||
|
attack(1, playerA, kraken, playerB);
|
||||||
|
attack(1, playerA, guard, playerB);
|
||||||
|
attack(1, playerA, fortress, playerB);
|
||||||
|
block(1, playerB, turtle, kraken);
|
||||||
|
block(1, playerB, pangolin, guard);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.END_COMBAT, playerA, stench);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 19);
|
||||||
|
assertPowerToughness(playerA, kraken, 0, 4);
|
||||||
|
assertPowerToughness(playerA, guard, 1, 3);
|
||||||
|
assertPowerToughness(playerA, fortress, 1, 6);
|
||||||
|
assertPowerToughness(playerB, turtle, -1, 4);
|
||||||
|
assertPowerToughness(playerB, pangolin, 0, 4);
|
||||||
|
assertPowerToughness(playerB, wishcoin, 2, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package mage.filter.predicate.permanent;
|
||||||
|
|
||||||
|
import mage.filter.predicate.Predicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.watchers.common.BlockedThisTurnWatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires BlockedThisTurnWatcher to be added to the card
|
||||||
|
*
|
||||||
|
* @author xenohedron
|
||||||
|
*/
|
||||||
|
public enum BlockedThisTurnPredicate implements Predicate<Permanent> {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Permanent input, Game game) {
|
||||||
|
BlockedThisTurnWatcher watcher = game.getState().getWatcher(BlockedThisTurnWatcher.class);
|
||||||
|
return watcher != null && watcher.checkIfBlocked(input, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "blocked this turn";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.watchers.common;
|
package mage.watchers.common;
|
||||||
|
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
|
|
@ -34,12 +33,7 @@ public class BlockedThisTurnWatcher extends Watcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkIfBlocked(Permanent permanent, Game game) {
|
public boolean checkIfBlocked(Permanent permanent, Game game) {
|
||||||
for (MageObjectReference mor : blockedThisTurnCreatures) {
|
return blockedThisTurnCreatures.stream().anyMatch(mor -> mor.refersTo(permanent, game));
|
||||||
if (mor.refersTo(permanent, game)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue