[CLB] Implemented Balor

This commit is contained in:
Evan Kranzler 2022-06-06 19:49:42 -04:00
parent a6f977c0e4
commit 1899fa0def
10 changed files with 191 additions and 122 deletions

View file

@ -0,0 +1,117 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawDiscardTargetEffect;
import mage.abilities.effects.common.SacrificeEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.meta.OrTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterOpponent;
import mage.filter.FilterPermanent;
import mage.filter.FilterPlayer;
import mage.filter.common.FilterArtifactPermanent;
import mage.filter.predicate.other.AnotherTargetPredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPlayer;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class Balor extends CardImpl {
private static final FilterPlayer filter0 = new FilterPlayer("a different player");
private static final FilterPlayer filter1 = new FilterOpponent();
private static final FilterPlayer filter2 = new FilterOpponent();
private static final FilterPlayer filter3 = new FilterOpponent();
private static final FilterPermanent filter4 = new FilterArtifactPermanent("a nontoken artifact");
static {
filter1.add(new AnotherTargetPredicate(1, true));
filter2.add(new AnotherTargetPredicate(2, true));
filter3.add(new AnotherTargetPredicate(3, true));
filter4.add(TokenPredicate.FALSE);
}
public Balor(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
this.subtype.add(SubType.DEMON);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Balor attacks or dies, choose one or more. Each mode must target a different player.
// Target opponent draws three cards, then discards three cards at random.
Ability ability = new OrTriggeredAbility(
Zone.BATTLEFIELD,
new DrawDiscardTargetEffect(
1, 1, true
), false, "Whenever {this} attacks or dies, ",
new AttacksTriggeredAbility(null, false),
new DiesSourceTriggeredAbility(null, false)
);
ability.getModes().setMinModes(1);
ability.getModes().setMaxModes(3);
ability.getModes().setMaxModesFilter(filter0);
ability.addTarget(new TargetPlayer(filter1).setTargetTag(1).withChooseHint("to draw and discard"));
// Target opponent sacrifices a nontoken artifact.
ability.addMode(new Mode(new SacrificeEffect(
filter4, 1, null
)).addTarget(new TargetPlayer(filter2).setTargetTag(2).withChooseHint("to sacrifice an artifact")));
// Balor deals damage to target opponent equal to the number of cards in their hand.
ability.addMode(new Mode(new BalorEffect()).addTarget(new TargetPlayer(filter3)
.setTargetTag(3).withChooseHint("to deal damage")));
}
private Balor(final Balor card) {
super(card);
}
@Override
public Balor copy() {
return new Balor(this);
}
}
class BalorEffect extends OneShotEffect {
BalorEffect() {
super(Outcome.Benefit);
staticText = "{this} deals damage to target opponent equal to the number of cards in their hand";
}
private BalorEffect(final BalorEffect effect) {
super(effect);
}
@Override
public BalorEffect copy() {
return new BalorEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(game.getActivePlayerId());
return player != null
&& player.getHand().size() >= 1
&& player.damage(player.getHand().size(), source.getSourceId(), source, game) > 0;
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -11,17 +9,17 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.target.TargetPlayer;
import java.util.UUID;
/**
*
* @author cbt33, Loki (Merfolk Looter)
*/
public final class CephalidLooter extends CardImpl {
public CephalidLooter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.subtype.add(SubType.CEPHALID);
this.subtype.add(SubType.ROGUE);
@ -29,7 +27,7 @@ public final class CephalidLooter extends CardImpl {
this.toughness = new MageInt(1);
// {tap}: Target player draws a card, then discards a card.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardTargetEffect(), new TapSourceCost());
Ability ability = new SimpleActivatedAbility(new DrawDiscardTargetEffect(1, 1), new TapSourceCost());
ability.addTarget(new TargetPlayer());
this.addAbility(ability);
}

View file

@ -1,24 +1,23 @@
package mage.cards.g;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.ExileXFromYourGraveCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.keyword.DeathtouchAbility;
import mage.abilities.meta.OrTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
import mage.util.CardUtil;
@ -51,7 +50,12 @@ public final class GorexTheTombshell extends CardImpl {
this.addAbility(DeathtouchAbility.getInstance());
// Whenever Gorex, the Tombshell attacks or dies, choose a card at random exiled with Gorex and put that card into its owner's hand.
this.addAbility(new GorexTheTombshellTriggeredAbility());
this.addAbility(new OrTriggeredAbility(
Zone.BATTLEFIELD, new GorexTheTombshellReturnEffect(), false,
"Whenever {this} attacks or dies, ",
new AttacksTriggeredAbility(null, false),
new DiesSourceTriggeredAbility(null, false)
));
}
private GorexTheTombshell(final GorexTheTombshell card) {
@ -106,52 +110,11 @@ class GorexTheTombshellCostReductionEffect extends CostModificationEffectImpl {
}
}
class GorexTheTombshellTriggeredAbility extends TriggeredAbilityImpl {
GorexTheTombshellTriggeredAbility() {
super(Zone.BATTLEFIELD, new GorexTheTombshellReturnEffect());
}
private GorexTheTombshellTriggeredAbility(final GorexTheTombshellTriggeredAbility ability) {
super(ability);
}
@Override
public GorexTheTombshellTriggeredAbility copy() {
return new GorexTheTombshellTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ATTACKER_DECLARED
|| event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) {
return event.getSourceId().equals(getSourceId());
}
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
return zEvent.isDiesEvent() && event.getTargetId().equals(getSourceId());
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
@Override
public String getRule() {
return "Whenever {this} attacks or dies, choose a card at random " +
"exiled with {this} and put that card into its owner's hand.";
}
}
class GorexTheTombshellReturnEffect extends OneShotEffect {
GorexTheTombshellReturnEffect() {
super(Outcome.Benefit);
staticText = "choose a card at random exiled with {this} and put that card into its owner's hand";
}
private GorexTheTombshellReturnEffect(final GorexTheTombshellReturnEffect effect) {

View file

@ -64,24 +64,16 @@ public final class ShadrixSilverquill extends CardImpl {
// Target player creates a 2/1 white and black Inkling creature token with flying.
ability.addEffect(new CreateTokenTargetEffect(new InklingToken()));
TargetPlayer target = new TargetPlayer(filter1);
target.setTargetTag(1);
ability.addTarget(target.withChooseHint("to create a token"));
ability.addTarget(new TargetPlayer(filter1).setTargetTag(1).withChooseHint("to create a token"));
// Target player draws a card and loses 1 life.
Mode mode = new Mode(new DrawCardTargetEffect(1));
mode.addEffect(new LoseLifeTargetEffect(1).setText("and loses 1 life"));
target = new TargetPlayer(filter2);
target.setTargetTag(2);
mode.addTarget(target.withChooseHint("to draw a card and lose 1 life"));
ability.addMode(mode);
ability.addMode(new Mode(new DrawCardTargetEffect(1))
.addEffect(new LoseLifeTargetEffect(1).setText("and loses 1 life"))
.addTarget(new TargetPlayer(filter2).setTargetTag(2).withChooseHint("to draw a card and lose 1 life")));
// Target player puts a +1/+1 counter on each creature they control.
mode = new Mode(new ShadrixSilverquillEffect());
target = new TargetPlayer(filter3);
target.setTargetTag(3);
mode.addTarget(target.withChooseHint("to put a counter on each creature"));
ability.addMode(mode);
ability.addMode(new Mode(new ShadrixSilverquillEffect())
.addTarget(new TargetPlayer(filter3).setTargetTag(3).withChooseHint("to put a counter on each creature")));
this.addAbility(ability);
}

View file

@ -12,10 +12,10 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.FilterOpponent;
import mage.filter.FilterPlayer;
import mage.filter.StaticFilters;
import mage.filter.predicate.other.AnotherTargetPredicate;
import mage.target.Target;
import mage.target.common.TargetOpponent;
import mage.target.TargetPlayer;
import java.util.UUID;
@ -24,6 +24,17 @@ import java.util.UUID;
*/
public final class VindictiveLich extends CardImpl {
private static final FilterPlayer filter0 = new FilterPlayer("a different player");
private static final FilterPlayer filter1 = new FilterOpponent();
private static final FilterPlayer filter2 = new FilterOpponent();
private static final FilterPlayer filter3 = new FilterOpponent();
static {
filter1.add(new AnotherTargetPredicate(1, true));
filter2.add(new AnotherTargetPredicate(2, true));
filter3.add(new AnotherTargetPredicate(3, true));
}
public VindictiveLich(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
@ -34,34 +45,22 @@ public final class VindictiveLich extends CardImpl {
// When Vindictive Lich dies, choose one or more. Each mode must target a different player.
// * Target opponent sacrifices a creature.
Ability ability = new DiesSourceTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent"));
Ability ability = new DiesSourceTriggeredAbility(new SacrificeEffect(
StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent"
));
ability.getModes().setMinModes(1);
ability.getModes().setMaxModes(3);
ability.getModes().setEachModeOnlyOnce(true);
ability.getModes().setMaxModesFilter(new FilterOpponent("a different player"));
FilterOpponent filter = new FilterOpponent();
filter.add(new AnotherTargetPredicate(1, true));
Target target = new TargetOpponent(filter, false).withChooseHint("who sacrifice a creature");
target.setTargetTag(1);
ability.addTarget(target);
ability.getModes().setMaxModesFilter(filter0);
ability.addTarget(new TargetPlayer(filter1).setTargetTag(1).withChooseHint("to sacrifice a creature"));
// * Target opponent discards two cards.
Mode mode = new Mode(new DiscardTargetEffect(2, false));
filter = new FilterOpponent();
filter.add(new AnotherTargetPredicate(2, true));
target = new TargetOpponent(filter, false);
target.setTargetTag(2);
mode.addTarget(target.withChooseHint("who discard a card"));
ability.addMode(mode);
ability.addMode(new Mode(new DiscardTargetEffect(2, false))
.addTarget(new TargetPlayer(filter2).setTargetTag(2).withChooseHint("to discard a card")));
// * Target opponent loses 5 life.
mode = new Mode(new LoseLifeTargetEffect(5));
filter = new FilterOpponent();
filter.add(new AnotherTargetPredicate(3, true));
target = new TargetOpponent(filter, false);
target.setTargetTag(3);
mode.addTarget(target.withChooseHint("who lose 5 life"));
ability.addMode(mode);
ability.addMode(new Mode(new LoseLifeTargetEffect(5))
.addTarget(new TargetPlayer(filter3).setTargetTag(3).withChooseHint("to lose 5 life")));
this.addAbility(ability);
}
@ -73,5 +72,4 @@ public final class VindictiveLich extends CardImpl {
public VindictiveLich copy() {
return new VindictiveLich(this);
}
}

View file

@ -63,6 +63,7 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet {
cards.add(new SetCardInfo("Baba Lysaga, Night Witch", 266, Rarity.RARE, mage.cards.b.BabaLysagaNightWitch.class));
cards.add(new SetCardInfo("Bag of Holding", 299, Rarity.UNCOMMON, mage.cards.b.BagOfHolding.class));
cards.add(new SetCardInfo("Baldur's Gate", 345, Rarity.RARE, mage.cards.b.BaldursGate.class));
cards.add(new SetCardInfo("Balor", 162, Rarity.MYTHIC, mage.cards.b.Balor.class));
cards.add(new SetCardInfo("Band Together", 216, Rarity.COMMON, mage.cards.b.BandTogether.class));
cards.add(new SetCardInfo("Bane's Contingency", 57, Rarity.UNCOMMON, mage.cards.b.BanesContingency.class));
cards.add(new SetCardInfo("Bane's Invoker", 7, Rarity.COMMON, mage.cards.b.BanesInvoker.class));

View file

@ -1,5 +1,3 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
@ -10,34 +8,40 @@ import mage.players.Player;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class DrawDiscardTargetEffect extends OneShotEffect {
private int cardsToDraw;
private int cardsToDiscard;
public DrawDiscardTargetEffect() {
this(1,1);
}
private final int cardsToDraw;
private final int cardsToDiscard;
private final boolean random;
public DrawDiscardTargetEffect(int cardsToDraw, int cardsToDiscard) {
this(cardsToDraw, cardsToDiscard, false);
}
public DrawDiscardTargetEffect(int cardsToDraw, int cardsToDiscard, boolean random) {
super(Outcome.DrawCard);
this.cardsToDraw = cardsToDraw;
this.cardsToDiscard = cardsToDiscard;
staticText = new StringBuilder("Target player draws ")
.append(cardsToDraw == 1?"a": CardUtil.numberToText(cardsToDraw))
.append(" card").append(cardsToDraw == 1?"": "s")
this.random = random;
staticText = new StringBuilder("target player draws ")
.append(CardUtil.numberToText(cardsToDraw, "a"))
.append(" card")
.append(cardsToDraw > 1 ? "s" : "")
.append(", then discards ")
.append(cardsToDiscard == 1?"a": CardUtil.numberToText(cardsToDiscard))
.append(" card").append(cardsToDiscard == 1?"": "s").toString();
.append(CardUtil.numberToText(cardsToDiscard, "a"))
.append(" card")
.append(cardsToDiscard > 1 ? "s" : "")
.append(random ? "at random" : "")
.toString();
}
public DrawDiscardTargetEffect(final DrawDiscardTargetEffect effect) {
private DrawDiscardTargetEffect(final DrawDiscardTargetEffect effect) {
super(effect);
this.cardsToDraw = effect.cardsToDraw;
this.cardsToDiscard = effect.cardsToDiscard;
this.random = effect.random;
}
@Override
@ -50,10 +54,9 @@ public class DrawDiscardTargetEffect extends OneShotEffect {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) {
player.drawCards(cardsToDraw, source, game);
player.discard(cardsToDiscard, false, false, source, game);
player.discard(cardsToDiscard, random, false, source, game);
return true;
}
return false;
}
}

View file

@ -39,10 +39,10 @@ public interface Target extends Serializable {
/**
* Returns a set of all possible targets that match the criteria of the implemented Target class.
*
* @param sourceControllerId UUID of the ability's controller
* @param source Ability which requires the targets
* @param game Current game
* @return Set of the UUIDs of possible targets
* @param sourceControllerId UUID of the ability's controller
* @param source Ability which requires the targets
* @param game Current game
* @return Set of the UUIDs of possible targets
*/
Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game);
@ -143,7 +143,7 @@ public interface Target extends Serializable {
int getTargetTag();
void setTargetTag(int tag);
Target setTargetTag(int tag);
Target getOriginalTarget();

View file

@ -554,8 +554,9 @@ public abstract class TargetImpl implements Target {
* @param targetTag
*/
@Override
public void setTargetTag(int targetTag) {
public TargetImpl setTargetTag(int targetTag) {
this.targetTag = targetTag;
return this;
}
@Override

View file

@ -1,29 +1,25 @@
package mage.target.common;
import mage.filter.FilterOpponent;
import mage.target.TargetPlayer;
/**
*
* @author BetaSteward_at_googlemail.com
* @author North
*/
public class TargetOpponent extends TargetPlayer {
private static final FilterOpponent filter = new FilterOpponent();
public TargetOpponent() {
this(false);
}
public TargetOpponent(boolean notTarget) {
this(new FilterOpponent(), notTarget);
}
public TargetOpponent(FilterOpponent filter, boolean notTarget) {
super(1, 1, notTarget, filter);
}
public TargetOpponent(final TargetOpponent target) {
private TargetOpponent(final TargetOpponent target) {
super(target);
}