[J22] Implement Benevolent Hydra, Creeping Bloodsucker, Distinguished Conjurer, Instruments of War (#9910)

* [J22] Implement Benevolent Hydra

* [J22] Implement Creeping Bloodsucker, correct author on Benevolent Hydra

* [J22] Implement Distinguished Conjurer

* [J22] Implement Instruments of War

* Fix verify failure

* Use overflow-safe increment in BenevolentHydra on principle.

* Address code review
This commit is contained in:
Grath 2023-02-07 11:26:28 -05:00 committed by GitHub
parent a0bd316f36
commit 40f1f57dc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 308 additions and 0 deletions

View file

@ -0,0 +1,108 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author Grath
*/
public final class BenevolentHydra extends CardImpl {
public BenevolentHydra(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}{G}");
this.subtype.add(SubType.HYDRA);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Hungering Hydra enters the battlefield with X +1/+1 counters on it.
this.addAbility(new EntersBattlefieldAbility(
new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())
));
// If one or more +1/+1 counters would be put on another creature you control, that many plus one +1/+1 counters are put on it instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BenevolentHydraEffect()));
// {T}, Remove a +1/+1 counter from Benevolent Hydra: Put a +1/+1 counter on another target creature you control.
Ability ability = new SimpleActivatedAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()).setText("Put a +1/+1 counter on another target creature you control"), new TapSourceCost());
ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance()));
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE));
this.addAbility(ability);
}
private BenevolentHydra(final BenevolentHydra card) {
super(card);
}
@Override
public BenevolentHydra copy() {
return new BenevolentHydra(this);
}
}
class BenevolentHydraEffect extends ReplacementEffectImpl {
BenevolentHydraEffect() {
super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false);
staticText = "If one or more +1/+1 counters would be put on another creature you control, that many plus one +1/+1 counters are put on it instead";
}
BenevolentHydraEffect(final BenevolentHydraEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmountForCounters(CardUtil.overflowInc(event.getAmount(), 1), true);
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ADD_COUNTERS;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId());
}
return permanent != null && permanent.isControlledBy(source.getControllerId())
&& permanent.isCreature(game) && !event.getTargetId().equals(source.getSourceId());
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public BenevolentHydraEffect copy() {
return new BenevolentHydraEffect(this);
}
}

View file

@ -0,0 +1,76 @@
package mage.cards.c;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author Grath
*/
public final class CreepingBloodsucker extends CardImpl {
public CreepingBloodsucker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
this.subtype.add(SubType.VAMPIRE);
this.power = new MageInt(1);
this.toughness = new MageInt(2);
// At the beginning of your upkeep, Creeping Bloodsucker deals 1 damage to each opponent. You gain life equal to the damage dealt this way.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CreepingBloodsuckerEffect(), TargetController.YOU, false));
}
private CreepingBloodsucker(final CreepingBloodsucker card) {
super(card);
}
@Override
public CreepingBloodsucker copy() {
return new CreepingBloodsucker(this);
}
}
class CreepingBloodsuckerEffect extends OneShotEffect {
public CreepingBloodsuckerEffect() {
super(Outcome.Damage);
staticText = "{this} deals 1 damage to each opponent. You gain life equal to the damage dealt this way";
}
public CreepingBloodsuckerEffect(final CreepingBloodsuckerEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
int damageDealt = 0;
Player player = game.getPlayer(source.getControllerId());
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
if (player.hasOpponent(playerId, game)) {
damageDealt += game.getPlayer(playerId).damage(1, source.getSourceId(), source, game);
}
}
if (damageDealt > 0) {
game.getPlayer(source.getControllerId()).gainLife(damageDealt, game, source);
}
return true;
}
@Override
public CreepingBloodsuckerEffect copy() {
return new CreepingBloodsuckerEffect(this);
}
}

View file

@ -0,0 +1,65 @@
package mage.cards.d;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
/**
* @author Grath
*/
public final class DistinguishedConjurer extends CardImpl {
private static final FilterCreaturePermanent filter
= new FilterCreaturePermanent("another creature");
private static final FilterControlledCreaturePermanent filter2
= new FilterControlledCreaturePermanent("another target creature you control");
static {
filter.add(AnotherPredicate.instance);
filter2.add(AnotherPredicate.instance);
}
public DistinguishedConjurer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(1);
this.toughness = new MageInt(2);
// Whenever another creature enters the battlefield under your control, you gain 1 life.
this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), filter));
// {4}{W}, {T}: Exile another target creature you control, then return it to the battlefield under its owners control.
Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new ManaCostsImpl<>("{4}{W}"));
ability.addCost(new TapSourceCost());
ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).concatBy(", then"));
ability.addTarget(new TargetControlledCreaturePermanent(filter2));
this.addAbility(ability);
}
private DistinguishedConjurer(final DistinguishedConjurer card) {
super(card);
}
@Override
public DistinguishedConjurer copy() {
return new DistinguishedConjurer(this);
}
}

View file

@ -0,0 +1,55 @@
package mage.cards.i;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.abilities.keyword.FlashAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
import java.util.UUID;
/**
* @author Grath
*/
public final class InstrumentsOfWar extends CardImpl {
private static final FilterCreaturePermanent filter
= new FilterCreaturePermanent("creatures you control of the chosen type");
static {
filter.add(ChosenSubtypePredicate.TRUE);
filter.add(TargetController.YOU.getControllerPredicate());
}
public InstrumentsOfWar(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
// Flash
this.addAbility(FlashAbility.getInstance());
// As Instruments of War enters the battlefield, choose a creature type.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.BoostCreature)));
// Creatures you control of the chosen type get +1/+1.
this.addAbility(new SimpleStaticAbility(new BoostAllEffect(
1, 1, Duration.WhileOnBattlefield, filter, false
)));
}
private InstrumentsOfWar(final InstrumentsOfWar card) {
super(card);
}
@Override
public InstrumentsOfWar copy() {
return new InstrumentsOfWar(this);
}
}

View file

@ -74,6 +74,7 @@ public final class Jumpstart2022 extends ExpansionSet {
cards.add(new SetCardInfo("Basri's Acolyte", 154, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class));
cards.add(new SetCardInfo("Battle Squadron", 497, Rarity.UNCOMMON, mage.cards.b.BattleSquadron.class));
cards.add(new SetCardInfo("Benalish Honor Guard", 155, Rarity.COMMON, mage.cards.b.BenalishHonorGuard.class));
cards.add(new SetCardInfo("Benevolent Hydra", 38, Rarity.RARE, mage.cards.b.BenevolentHydra.class));
cards.add(new SetCardInfo("Berg Strider", 277, Rarity.COMMON, mage.cards.b.BergStrider.class));
cards.add(new SetCardInfo("Biblioplex Kraken", 10, Rarity.UNCOMMON, mage.cards.b.BiblioplexKraken.class));
cards.add(new SetCardInfo("Big Score", 498, Rarity.COMMON, mage.cards.b.BigScore.class));
@ -152,6 +153,7 @@ public final class Jumpstart2022 extends ExpansionSet {
cards.add(new SetCardInfo("Crashing Tide", 283, Rarity.COMMON, mage.cards.c.CrashingTide.class));
cards.add(new SetCardInfo("Crawling Sensation", 642, Rarity.UNCOMMON, mage.cards.c.CrawlingSensation.class));
cards.add(new SetCardInfo("Creeperhulk", 643, Rarity.RARE, mage.cards.c.Creeperhulk.class));
cards.add(new SetCardInfo("Creeping Bloodsucker", 21, Rarity.COMMON, mage.cards.c.CreepingBloodsucker.class));
cards.add(new SetCardInfo("Crippling Chill", 284, Rarity.COMMON, mage.cards.c.CripplingChill.class));
cards.add(new SetCardInfo("Crow of Dark Tidings", 390, Rarity.COMMON, mage.cards.c.CrowOfDarkTidings.class));
cards.add(new SetCardInfo("Cruel Sadist", 391, Rarity.RARE, mage.cards.c.CruelSadist.class));
@ -186,6 +188,7 @@ public final class Jumpstart2022 extends ExpansionSet {
cards.add(new SetCardInfo("Devouring Swarm", 401, Rarity.COMMON, mage.cards.d.DevouringSwarm.class));
cards.add(new SetCardInfo("Diabolic Edict", 67, Rarity.COMMON, mage.cards.d.DiabolicEdict.class));
cards.add(new SetCardInfo("Dismiss", 286, Rarity.UNCOMMON, mage.cards.d.Dismiss.class));
cards.add(new SetCardInfo("Distinguished Conjurer", 4, Rarity.UNCOMMON, mage.cards.d.DistinguishedConjurer.class));
cards.add(new SetCardInfo("Divine Arrow", 176, Rarity.COMMON, mage.cards.d.DivineArrow.class));
cards.add(new SetCardInfo("Divine Verdict", 177, Rarity.COMMON, mage.cards.d.DivineVerdict.class));
cards.add(new SetCardInfo("Djinn of Wishes", 287, Rarity.RARE, mage.cards.d.DjinnOfWishes.class));
@ -366,6 +369,7 @@ public final class Jumpstart2022 extends ExpansionSet {
cards.add(new SetCardInfo("Inner Demon", 428, Rarity.UNCOMMON, mage.cards.i.InnerDemon.class));
cards.add(new SetCardInfo("Inspiring Cleric", 199, Rarity.UNCOMMON, mage.cards.i.InspiringCleric.class));
cards.add(new SetCardInfo("Inspiring Overseer", 200, Rarity.COMMON, mage.cards.i.InspiringOverseer.class));
cards.add(new SetCardInfo("Instruments of War", 50, Rarity.UNCOMMON, mage.cards.i.InstrumentsOfWar.class));
cards.add(new SetCardInfo("Interpret the Signs", 309, Rarity.UNCOMMON, mage.cards.i.InterpretTheSigns.class));
cards.add(new SetCardInfo("Irencrag Pyromancer", 559, Rarity.RARE, mage.cards.i.IrencragPyromancer.class));
cards.add(new SetCardInfo("Iridescent Hornbeetle", 678, Rarity.UNCOMMON, mage.cards.i.IridescentHornbeetle.class));