implement [CLB] Hezrou

This commit is contained in:
xenohedron 2024-12-08 20:25:51 -05:00
parent 94db57e9d4
commit 24687eb4af
6 changed files with 237 additions and 55 deletions

View 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);
}
}

View file

@ -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));
}
}

View file

@ -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));

View file

@ -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);
}
}

View file

@ -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";
}
}

View file

@ -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