mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
Introduce new batch event for life lost for a specific player (#13071)
* Introduce new batch event for life lost for a specific player closes #12202, fix #10805 * implement [DSC] Valgavoth, Harrower of Souls * text fixes
This commit is contained in:
parent
95e986dee7
commit
d6cf207a8b
28 changed files with 693 additions and 707 deletions
|
|
@ -1,8 +1,8 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedGainedLifeValue;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
|
|
@ -10,9 +10,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.constants.TargetController;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -36,7 +34,8 @@ public final class BloodthirstyConqueror extends CardImpl {
|
|||
this.addAbility(DeathtouchAbility.getInstance());
|
||||
|
||||
// Whenever an opponent loses life, you gain that much life.
|
||||
this.addAbility(new BloodthirstyConquerorTriggeredAbility());
|
||||
this.addAbility(new LoseLifeTriggeredAbility(new GainLifeEffect(SavedLifeLossValue.MUCH),
|
||||
TargetController.OPPONENT, false, false));
|
||||
}
|
||||
|
||||
private BloodthirstyConqueror(final BloodthirstyConqueror card) {
|
||||
|
|
@ -48,34 +47,3 @@ public final class BloodthirstyConqueror extends CardImpl {
|
|||
return new BloodthirstyConqueror(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BloodthirstyConquerorTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
BloodthirstyConquerorTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new GainLifeEffect(SavedGainedLifeValue.MUCH));
|
||||
this.setTriggerPhrase("Whenever an opponent loses life, ");
|
||||
}
|
||||
|
||||
private BloodthirstyConquerorTriggeredAbility(final BloodthirstyConquerorTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BloodthirstyConquerorTriggeredAbility copy() {
|
||||
return new BloodthirstyConquerorTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!game.getOpponents(getControllerId()).contains(event.getTargetId())) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setValue(SavedGainedLifeValue.VALUE_KEY, event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public final class CuratorBeastie extends CardImpl {
|
|||
// Colorless creatures you control enter with two additional +1/+1 counters on them.
|
||||
this.addAbility(new SimpleStaticAbility(new EntersWithCountersControlledEffect(
|
||||
filter, CounterType.P1P1.createInstance(2), false
|
||||
)));
|
||||
).setText("colorless creatures you control enter with two additional +1/+1 counters on them")));
|
||||
|
||||
// Whenever Curator Beastie enters or attacks, manifest dread.
|
||||
this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ManifestDreadEffect()));
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
|
||||
package mage.cards.e;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.constants.TargetController;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author noxx
|
||||
|
|
@ -18,12 +16,11 @@ import mage.game.events.GameEvent.EventType;
|
|||
public final class ExquisiteBlood extends CardImpl {
|
||||
|
||||
public ExquisiteBlood(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{B}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}");
|
||||
|
||||
// Whenever an opponent loses life, you gain that much life.
|
||||
ExquisiteBloodTriggeredAbility ability = new ExquisiteBloodTriggeredAbility();
|
||||
this.addAbility(ability);
|
||||
this.addAbility(new LoseLifeTriggeredAbility(new GainLifeEffect(SavedLifeLossValue.MUCH),
|
||||
TargetController.OPPONENT, false, false));
|
||||
}
|
||||
|
||||
private ExquisiteBlood(final ExquisiteBlood card) {
|
||||
|
|
@ -35,39 +32,3 @@ public final class ExquisiteBlood extends CardImpl {
|
|||
return new ExquisiteBlood(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ExquisiteBloodTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public ExquisiteBloodTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, null);
|
||||
}
|
||||
|
||||
private ExquisiteBloodTriggeredAbility(final ExquisiteBloodTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExquisiteBloodTriggeredAbility copy() {
|
||||
return new ExquisiteBloodTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) {
|
||||
this.getEffects().clear();
|
||||
this.addEffect(new GainLifeEffect(event.getAmount()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever an opponent loses life, you gain that much life.";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ public final class GlitchInterpreter extends CardImpl {
|
|||
this.toughness = new MageInt(3);
|
||||
|
||||
// When Glitch Interpreter enters, if you control no face-down permanents, return Glitch Interpreter to its owner's hand and manifest dread.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandSourceEffect()).withInterveningIf(condition);
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandSourceEffect())
|
||||
.withRuleTextReplacement(false).withInterveningIf(condition);
|
||||
ability.addEffect(new ManifestDreadEffect().concatBy("and"));
|
||||
this.addAbility(ability);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeFirstTimeEachTurnTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.PayEnergyCost;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
|
|
@ -10,14 +10,7 @@ import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -29,8 +22,8 @@ public final class GontisMachinations extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}");
|
||||
|
||||
// Whenever you lose life for the first time each turn, you get {E}.
|
||||
this.addAbility(new GontisMachinationsTriggeredAbility(),
|
||||
new GontisMachinationsFirstLostLifeThisTurnWatcher());
|
||||
this.addAbility(new LoseLifeFirstTimeEachTurnTriggeredAbility(
|
||||
new GetEnergyCountersControllerEffect(1)));
|
||||
|
||||
// Pay {E}{E}, Sacrifice Gonti's Machinations: Each opponent loses 3 life. You gain life equal to the life lost this way.
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
|
|
@ -50,68 +43,3 @@ public final class GontisMachinations extends CardImpl {
|
|||
return new GontisMachinations(this);
|
||||
}
|
||||
}
|
||||
|
||||
class GontisMachinationsTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public GontisMachinationsTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new GetEnergyCountersControllerEffect(1), false);
|
||||
setTriggerPhrase("Whenever you lose life for the first time each turn, ");
|
||||
}
|
||||
|
||||
private GontisMachinationsTriggeredAbility(final GontisMachinationsTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(getControllerId())) {
|
||||
GontisMachinationsFirstLostLifeThisTurnWatcher watcher
|
||||
= game.getState().getWatcher(GontisMachinationsFirstLostLifeThisTurnWatcher.class);
|
||||
if (watcher != null
|
||||
&& watcher.timesLostLifeThisTurn(event.getPlayerId()) < 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GontisMachinationsTriggeredAbility copy() {
|
||||
return new GontisMachinationsTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
||||
class GontisMachinationsFirstLostLifeThisTurnWatcher extends Watcher {
|
||||
|
||||
private final Map<UUID, Integer> playersLostLife = new HashMap<>();
|
||||
|
||||
public GontisMachinationsFirstLostLifeThisTurnWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
switch (event.getType()) {
|
||||
case LOST_LIFE:
|
||||
int timesLifeLost = playersLostLife.getOrDefault(event.getPlayerId(), 0);
|
||||
timesLifeLost++;
|
||||
playersLostLife.put(event.getPlayerId(), timesLifeLost);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
playersLostLife.clear();
|
||||
}
|
||||
|
||||
public int timesLostLifeThisTurn(UUID playerId) {
|
||||
return playersLostLife.getOrDefault(playerId, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
package mage.cards.l;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.GainLifeControllerTriggeredAbility;
|
||||
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.SavedGainedLifeValue;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -56,7 +56,7 @@ public final class LichsMastery extends CardImpl {
|
|||
));
|
||||
|
||||
// Whenever you lose life, for each 1 life you lost, exile a permanent you control or a card from your hand or graveyard.
|
||||
this.addAbility(new LichsMasteryLoseLifeTriggeredAbility());
|
||||
this.addAbility(new LoseLifeTriggeredAbility(new LichsMasteryLoseLifeEffect()));
|
||||
|
||||
// When Lich's Mastery leaves the battlefield, you lose the game.
|
||||
this.addAbility(new LeavesBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false));
|
||||
|
|
@ -99,57 +99,15 @@ class LichsMasteryCantLoseEffect extends ContinuousRuleModifyingEffectImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class LichsMasteryLoseLifeTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public LichsMasteryLoseLifeTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new LichsMasteryLoseLifeEffect(), false);
|
||||
}
|
||||
|
||||
private LichsMasteryLoseLifeTriggeredAbility(final LichsMasteryLoseLifeTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LichsMasteryLoseLifeTriggeredAbility copy() {
|
||||
return new LichsMasteryLoseLifeTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(this.getControllerId())) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
if (effect instanceof LichsMasteryLoseLifeEffect) {
|
||||
((LichsMasteryLoseLifeEffect) effect).setAmount(event.getAmount());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you lose life, for each 1 life you lost, exile a permanent you control or a card from your hand or graveyard.";
|
||||
}
|
||||
}
|
||||
|
||||
class LichsMasteryLoseLifeEffect extends OneShotEffect {
|
||||
|
||||
private int amount = 0;
|
||||
|
||||
public LichsMasteryLoseLifeEffect() {
|
||||
LichsMasteryLoseLifeEffect() {
|
||||
super(Outcome.Exile);
|
||||
this.staticText = "for each 1 life you lost, exile a permanent you control or a card from your hand or graveyard.";
|
||||
this.staticText = "for each 1 life you lost, exile a permanent you control or a card from your hand or graveyard";
|
||||
}
|
||||
|
||||
private LichsMasteryLoseLifeEffect(final LichsMasteryLoseLifeEffect effect) {
|
||||
super(effect);
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -165,7 +123,7 @@ class LichsMasteryLoseLifeEffect extends OneShotEffect {
|
|||
}
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
filter.add(new ControllerIdPredicate(controller.getId()));
|
||||
for (int i = 0; i < amount; i++) {
|
||||
for (int i = 0; i < SavedLifeLossValue.MANY.calculate(game, source, this); i++) {
|
||||
int handCount = controller.getHand().size();
|
||||
int graveCount = controller.getGraveyard().size();
|
||||
int permCount = game.getBattlefield().getActivePermanents(filter, controller.getId(), game).size();
|
||||
|
|
@ -182,7 +140,7 @@ class LichsMasteryLoseLifeEffect extends OneShotEffect {
|
|||
if (card != null) {
|
||||
controller.moveCards(card, Zone.EXILED, source, game);
|
||||
}
|
||||
} else if (graveCount > 0) {
|
||||
} else {
|
||||
Target target = new TargetCardInYourGraveyard(1, 1, new FilterCard(), true);
|
||||
target.choose(Outcome.Exile, source.getControllerId(), source.getSourceId(), source, game);
|
||||
Card card = controller.getGraveyard().get(target.getFirstTarget(), game);
|
||||
|
|
@ -194,7 +152,4 @@ class LichsMasteryLoseLifeEffect extends OneShotEffect {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,33 @@
|
|||
|
||||
package mage.cards.l;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.SacrificeControllerEffect;
|
||||
import mage.abilities.effects.common.SacrificeEffect;
|
||||
import mage.abilities.effects.common.continuous.DontLoseByZeroOrLessLifeEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
*/
|
||||
public final class LichsTomb extends CardImpl {
|
||||
|
||||
public LichsTomb(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
|
||||
|
||||
// You don't lose the game for having 0 or less life.
|
||||
this.addAbility(new SimpleStaticAbility(new DontLoseByZeroOrLessLifeEffect(Duration.WhileOnBattlefield)));
|
||||
|
||||
// Whenever you lose life, sacrifice a permanent for each 1 life you lost.
|
||||
this.addAbility(new LichsTombTriggeredAbility());
|
||||
this.addAbility(new LoseLifeTriggeredAbility(new SacrificeControllerEffect(
|
||||
StaticFilters.FILTER_PERMANENT, SavedLifeLossValue.MANY, ""
|
||||
).setText("sacrifice a permanent for each 1 life you lost")));
|
||||
}
|
||||
|
||||
private LichsTomb(final LichsTomb card) {
|
||||
|
|
@ -43,38 +39,3 @@ public final class LichsTomb extends CardImpl {
|
|||
return new LichsTomb(this);
|
||||
}
|
||||
}
|
||||
|
||||
class LichsTombTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
LichsTombTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new SacrificeControllerEffect(new FilterPermanent(), 0, ""), false);
|
||||
}
|
||||
|
||||
private LichsTombTriggeredAbility(final LichsTombTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LichsTombTriggeredAbility copy() {
|
||||
return new LichsTombTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(this.getControllerId())) {
|
||||
((SacrificeEffect) this.getEffects().get(0)).setAmount(StaticValue.get(event.getAmount()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you lose life, sacrifice a permanent for each 1 life you lost.";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
package mage.cards.m;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.GainLifeControllerTriggeredAbility;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.CastFromEverywhereSourceCondition;
|
||||
import mage.abilities.condition.common.HellbentCondition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.dynamicvalue.common.SavedDamageValue;
|
||||
import mage.abilities.dynamicvalue.common.SavedGainedLifeValue;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.LoseGameSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.DontLoseByZeroOrLessLifeEffect;
|
||||
|
|
@ -21,9 +21,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -58,7 +55,12 @@ public final class MarinaVendrellsGrimoire extends CardImpl {
|
|||
));
|
||||
|
||||
// Whenever you lose life, discard that many cards. Then if you have no cards in hand, you lose the game.
|
||||
this.addAbility(new MarinaVendrellsGrimoireTriggeredAbility());
|
||||
Ability ability2 = new LoseLifeTriggeredAbility(new DiscardControllerEffect(SavedLifeLossValue.MANY));
|
||||
ability2.addEffect(new ConditionalOneShotEffect(
|
||||
new LoseGameSourceControllerEffect(), HellbentCondition.instance,
|
||||
"Then if you have no cards in hand, you lose the game"
|
||||
));
|
||||
this.addAbility(ability2);
|
||||
}
|
||||
|
||||
private MarinaVendrellsGrimoire(final MarinaVendrellsGrimoire card) {
|
||||
|
|
@ -70,38 +72,3 @@ public final class MarinaVendrellsGrimoire extends CardImpl {
|
|||
return new MarinaVendrellsGrimoire(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MarinaVendrellsGrimoireTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
MarinaVendrellsGrimoireTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new DiscardControllerEffect(SavedDamageValue.MANY));
|
||||
this.addEffect(new ConditionalOneShotEffect(
|
||||
new LoseGameSourceControllerEffect(), HellbentCondition.instance,
|
||||
"Then if you have no cards in hand, you lose the game"
|
||||
));
|
||||
this.setTriggerPhrase("Whenever you lose life, ");
|
||||
}
|
||||
|
||||
private MarinaVendrellsGrimoireTriggeredAbility(final MarinaVendrellsGrimoireTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarinaVendrellsGrimoireTriggeredAbility copy() {
|
||||
return new MarinaVendrellsGrimoireTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!isControlledBy(event.getPlayerId())) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,13 @@
|
|||
|
||||
package mage.cards.m;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.MillCardsTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.constants.TargetController;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -26,9 +18,11 @@ public final class Mindcrank extends CardImpl {
|
|||
public Mindcrank(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
|
||||
|
||||
// Whenever an opponent loses life, that player puts that many cards from the top of their library into their graveyard.
|
||||
// (Damage dealt by sources without infect causes loss of life.)
|
||||
this.addAbility(new MindcrankTriggeredAbility());
|
||||
// Whenever an opponent loses life, that player mills that many cards.
|
||||
this.addAbility(new LoseLifeTriggeredAbility(
|
||||
new MillCardsTargetEffect(SavedLifeLossValue.MANY),
|
||||
TargetController.OPPONENT, false, true
|
||||
));
|
||||
}
|
||||
|
||||
private Mindcrank(final Mindcrank card) {
|
||||
|
|
@ -40,71 +34,3 @@ public final class Mindcrank extends CardImpl {
|
|||
return new Mindcrank(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MindcrankTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public MindcrankTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new MindcrankEffect(), false);
|
||||
}
|
||||
|
||||
private MindcrankTriggeredAbility(final MindcrankTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MindcrankTriggeredAbility copy() {
|
||||
return new MindcrankTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Set<UUID> opponents = game.getOpponents(this.getControllerId());
|
||||
if (opponents.contains(event.getPlayerId())) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("lostLife", event.getAmount());
|
||||
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever an opponent loses life, that player mills that many cards.";
|
||||
}
|
||||
}
|
||||
|
||||
class MindcrankEffect extends OneShotEffect {
|
||||
|
||||
MindcrankEffect() {
|
||||
super(Outcome.Detriment);
|
||||
}
|
||||
|
||||
private MindcrankEffect(final MindcrankEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MindcrankEffect copy() {
|
||||
return new MindcrankEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (targetPlayer != null) {
|
||||
Integer amount = (Integer) getValue("lostLife");
|
||||
if (amount == null) {
|
||||
amount = 0;
|
||||
}
|
||||
targetPlayer.millCards(amount, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,28 @@
|
|||
package mage.cards.o;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DoUnlessControllerPaysEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.SacrificeControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public final class OathOfLimDul extends CardImpl {
|
||||
|
|
@ -34,7 +31,7 @@ public final class OathOfLimDul extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}");
|
||||
|
||||
// Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than Oath of Lim-Dul unless you discard a card.
|
||||
this.addAbility(new OathOfLimDulTriggeredAbility());
|
||||
this.addAbility(new LoseLifeTriggeredAbility(new OathOfLimDulEffect()));
|
||||
|
||||
// {B}{B}: Draw a card.
|
||||
this.addAbility(new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{B}{B}")));
|
||||
|
|
@ -51,51 +48,17 @@ public final class OathOfLimDul extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class OathOfLimDulTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public OathOfLimDulTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new OathOfLimDulEffect());
|
||||
}
|
||||
|
||||
private OathOfLimDulTriggeredAbility(final OathOfLimDulTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OathOfLimDulTriggeredAbility copy() {
|
||||
return new OathOfLimDulTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(controllerId)) {
|
||||
game.getState().setValue(sourceId.toString() + "oathOfLimDul", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than {this} unless you discard a card.";
|
||||
}
|
||||
}
|
||||
|
||||
class OathOfLimDulEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("controlled permanent other than Oath of Lim-Dul to sacrifice");
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("controlled permanent other than {this} to sacrifice");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
public OathOfLimDulEffect() {
|
||||
super(Outcome.Neutral);
|
||||
OathOfLimDulEffect() {
|
||||
super(Outcome.Detriment);
|
||||
staticText = "for each 1 life you lost, sacrifice a permanent other than {this} unless you discard a card";
|
||||
}
|
||||
|
||||
private OathOfLimDulEffect(final OathOfLimDulEffect effect) {
|
||||
|
|
@ -104,34 +67,19 @@ class OathOfLimDulEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean sacrificeDone = false;
|
||||
int numberSacrificed = 0;
|
||||
int numberToDiscard = 0;
|
||||
int numberOfControlledPermanents = 0;
|
||||
int amountDamage = SavedLifeLossValue.MANY.calculate(game, source, this);
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
int amountDamage = (int) game.getState().getValue(source.getSourceId().toString() + "oathOfLimDul");
|
||||
if (amountDamage > 0
|
||||
&& controller != null) {
|
||||
TargetControlledPermanent target = new TargetControlledPermanent(0, numberOfControlledPermanents, filter, true);
|
||||
target.withNotTarget(true);
|
||||
if (controller.choose(Outcome.Detriment, target, source, game)) {
|
||||
for (UUID targetPermanentId : target.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(targetPermanentId);
|
||||
if (permanent != null
|
||||
&& permanent.sacrifice(source, game)) {
|
||||
numberSacrificed += 1;
|
||||
sacrificeDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
numberToDiscard = amountDamage - numberSacrificed;
|
||||
Cost cost = new DiscardTargetCost(new TargetCardInHand(numberToDiscard, new FilterCard("card(s) in your hand to discard")));
|
||||
if (numberToDiscard > 0
|
||||
&& cost.canPay(source, source, controller.getId(), game)) {
|
||||
return cost.pay(source, game, source, controller.getId(), true); // discard cost paid simultaneously
|
||||
}
|
||||
if (amountDamage <= 0 || controller == null) {
|
||||
return false;
|
||||
}
|
||||
return sacrificeDone;
|
||||
boolean didSomething = false;
|
||||
for (int i = 0; i < amountDamage; ++i) {
|
||||
didSomething |= new DoUnlessControllerPaysEffect(
|
||||
new SacrificeControllerEffect(StaticFilters.FILTER_CONTROLLED_ANOTHER_PERMANENT, 1, ""),
|
||||
new DiscardCardCost()
|
||||
).apply(game, source);
|
||||
}
|
||||
return didSomething;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.StateTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.MultipliedValue;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.LoseGameSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.DontLoseByZeroOrLessLifeEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
*/
|
||||
public final class Transcendence extends CardImpl {
|
||||
|
||||
private static final DynamicValue value = new MultipliedValue(SavedLifeLossValue.MUCH, 2);
|
||||
|
||||
public Transcendence(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}{W}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}{W}");
|
||||
|
||||
// You don't lose the game for having 0 or less life.
|
||||
this.addAbility(new SimpleStaticAbility(new DontLoseByZeroOrLessLifeEffect(Duration.WhileOnBattlefield)));
|
||||
|
|
@ -37,7 +37,9 @@ public final class Transcendence extends CardImpl {
|
|||
this.addAbility(new TranscendenceStateTriggeredAbility());
|
||||
|
||||
// Whenever you lose life, you gain 2 life for each 1 life you lost.
|
||||
this.addAbility(new TranscendenceLoseLifeTriggeredAbility());
|
||||
this.addAbility(new LoseLifeTriggeredAbility(
|
||||
new GainLifeEffect(value).setText("you gain 2 life for each 1 life you lost")
|
||||
));
|
||||
}
|
||||
|
||||
private Transcendence(final Transcendence card) {
|
||||
|
|
@ -79,76 +81,3 @@ class TranscendenceStateTriggeredAbility extends StateTriggeredAbility {
|
|||
return "When you have 20 or more life, you lose the game.";
|
||||
}
|
||||
}
|
||||
|
||||
class TranscendenceLoseLifeTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
TranscendenceLoseLifeTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new TranscendenceLoseLifeEffect(), false);
|
||||
}
|
||||
|
||||
private TranscendenceLoseLifeTriggeredAbility(final TranscendenceLoseLifeTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranscendenceLoseLifeTriggeredAbility copy() {
|
||||
return new TranscendenceLoseLifeTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(this.getControllerId())) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
if (effect instanceof TranscendenceLoseLifeEffect) {
|
||||
((TranscendenceLoseLifeEffect) effect).setAmount(event.getAmount());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you lose life, you gain 2 life for each 1 life you lost.";
|
||||
}
|
||||
}
|
||||
|
||||
class TranscendenceLoseLifeEffect extends OneShotEffect {
|
||||
|
||||
private int amount = 0;
|
||||
|
||||
TranscendenceLoseLifeEffect() {
|
||||
super(Outcome.GainLife);
|
||||
this.staticText = "you gain 2 life for each 1 life you lost";
|
||||
}
|
||||
|
||||
private TranscendenceLoseLifeEffect(final TranscendenceLoseLifeEffect effect) {
|
||||
super(effect);
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranscendenceLoseLifeEffect copy() {
|
||||
return new TranscendenceLoseLifeEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
controller.gainLife(2 * amount, game, source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
79
Mage.Sets/src/mage/cards/v/ValgavothHarrowerOfSouls.java
Normal file
79
Mage.Sets/src/mage/cards/v/ValgavothHarrowerOfSouls.java
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package mage.cards.v;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.LoseLifeFirstTimeEachTurnTriggeredAbility;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.WardAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public final class ValgavothHarrowerOfSouls extends CardImpl {
|
||||
|
||||
public ValgavothHarrowerOfSouls(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.ELDER);
|
||||
this.subtype.add(SubType.DEMON);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Ward--Pay 2 life.
|
||||
this.addAbility(new WardAbility(new PayLifeCost(2)));
|
||||
|
||||
// Whenever an opponent loses life for the first time during each of their turns, put a +1/+1 counter on Valgavoth, Harrower of Souls and draw a card.
|
||||
this.addAbility(new ValgavothHarrowerOfSoulsTriggeredAbility());
|
||||
|
||||
}
|
||||
|
||||
private ValgavothHarrowerOfSouls(final ValgavothHarrowerOfSouls card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValgavothHarrowerOfSouls copy() {
|
||||
return new ValgavothHarrowerOfSouls(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ValgavothHarrowerOfSoulsTriggeredAbility extends LoseLifeFirstTimeEachTurnTriggeredAbility {
|
||||
|
||||
ValgavothHarrowerOfSoulsTriggeredAbility() {
|
||||
super(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.OPPONENT, false, false);
|
||||
this.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and"));
|
||||
setTriggerPhrase("Whenever an opponent loses life for the first time during each of their turns, ");
|
||||
}
|
||||
|
||||
private ValgavothHarrowerOfSoulsTriggeredAbility(final ValgavothHarrowerOfSoulsTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValgavothHarrowerOfSoulsTriggeredAbility copy() {
|
||||
return new ValgavothHarrowerOfSoulsTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return game.isActivePlayer(event.getTargetId()) && super.checkTrigger(event, game);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package mage.cards.v;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.GainLifeControllerTriggeredAbility;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.condition.common.MyTurnCondition;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
|
|
@ -11,7 +11,6 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
|
@ -54,10 +53,11 @@ public final class VampireScrivener extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class VampireScrivenerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
class VampireScrivenerTriggeredAbility extends LoseLifeTriggeredAbility {
|
||||
|
||||
VampireScrivenerTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
|
||||
super(new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
|
||||
setTriggerPhrase("Whenever you lose life during your turn, ");
|
||||
}
|
||||
|
||||
private VampireScrivenerTriggeredAbility(final VampireScrivenerTriggeredAbility ability) {
|
||||
|
|
@ -69,18 +69,9 @@ class VampireScrivenerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return new VampireScrivenerTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return game.isActivePlayer(event.getPlayerId()) && game.isActivePlayer(getControllerId());
|
||||
return game.isActivePlayer(getControllerId()) && super.checkTrigger(event, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you lose life during your turn, put a +1/+1 counter on {this}.";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,14 @@
|
|||
package mage.cards.v;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeFirstTimeEachTurnTriggeredAbility;
|
||||
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.WatcherScope;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -32,7 +25,8 @@ public final class VengefulWarchief extends CardImpl {
|
|||
this.toughness = new MageInt(4);
|
||||
|
||||
// Whenever you lose life for the first time each turn, put a +1/+1 counter on Vengeful Warchief.
|
||||
this.addAbility(new VengefulWarchiefTriggeredAbility(), new VengefulWarchiefWatcher());
|
||||
this.addAbility(new LoseLifeFirstTimeEachTurnTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance())));
|
||||
}
|
||||
|
||||
private VengefulWarchief(final VengefulWarchief card) {
|
||||
|
|
@ -44,62 +38,3 @@ public final class VengefulWarchief extends CardImpl {
|
|||
return new VengefulWarchief(this);
|
||||
}
|
||||
}
|
||||
|
||||
class VengefulWarchiefTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
VengefulWarchiefTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false);
|
||||
setTriggerPhrase("Whenever you lose life for the first time each turn, ");
|
||||
}
|
||||
|
||||
private VengefulWarchiefTriggeredAbility(final VengefulWarchiefTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!event.getPlayerId().equals(getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
VengefulWarchiefWatcher watcher = game.getState().getWatcher(VengefulWarchiefWatcher.class);
|
||||
return watcher != null && watcher.timesLostLifeThisTurn(event.getPlayerId()) < 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VengefulWarchiefTriggeredAbility copy() {
|
||||
return new VengefulWarchiefTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
||||
class VengefulWarchiefWatcher extends Watcher {
|
||||
|
||||
private final Map<UUID, Integer> playersLostLife = new HashMap<>();
|
||||
|
||||
VengefulWarchiefWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.LOST_LIFE) {
|
||||
int timesLifeLost = playersLostLife.getOrDefault(event.getPlayerId(), 0);
|
||||
timesLifeLost++;
|
||||
playersLostLife.put(event.getPlayerId(), timesLifeLost);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
playersLostLife.clear();
|
||||
}
|
||||
|
||||
int timesLostLifeThisTurn(UUID playerId) {
|
||||
return playersLostLife.getOrDefault(playerId, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ package mage.cards.v;
|
|||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.LoseLifeTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
|
|
@ -14,9 +15,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -46,7 +44,7 @@ public final class VilisBrokerOfBlood extends CardImpl {
|
|||
this.addAbility(ability);
|
||||
|
||||
// Whenever you lose life, draw that many cards.
|
||||
this.addAbility(new VilisBrokerOfBloodTriggeredAbility());
|
||||
this.addAbility(new LoseLifeTriggeredAbility(new DrawCardSourceControllerEffect(SavedLifeLossValue.MANY)));
|
||||
}
|
||||
|
||||
private VilisBrokerOfBlood(final VilisBrokerOfBlood card) {
|
||||
|
|
@ -58,39 +56,3 @@ public final class VilisBrokerOfBlood extends CardImpl {
|
|||
return new VilisBrokerOfBlood(this);
|
||||
}
|
||||
}
|
||||
|
||||
class VilisBrokerOfBloodTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
VilisBrokerOfBloodTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, null, false);
|
||||
}
|
||||
|
||||
private VilisBrokerOfBloodTriggeredAbility(final VilisBrokerOfBloodTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VilisBrokerOfBloodTriggeredAbility copy() {
|
||||
return new VilisBrokerOfBloodTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(this.getControllerId())) {
|
||||
this.getEffects().clear();
|
||||
this.addEffect(new DrawCardSourceControllerEffect(event.getAmount()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you lose life, draw that many cards.";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Twilight Mire", 320, Rarity.RARE, mage.cards.t.TwilightMire.class));
|
||||
cards.add(new SetCardInfo("Underground River", 321, Rarity.RARE, mage.cards.u.UndergroundRiver.class));
|
||||
cards.add(new SetCardInfo("Utter End", 91, Rarity.RARE, mage.cards.u.UtterEnd.class));
|
||||
cards.add(new SetCardInfo("Valgavoth, Harrower of Souls", 6, Rarity.MYTHIC, mage.cards.v.ValgavothHarrowerOfSouls.class));
|
||||
cards.add(new SetCardInfo("Vault of Whispers", 322, Rarity.COMMON, mage.cards.v.VaultOfWhispers.class));
|
||||
cards.add(new SetCardInfo("Verge Rangers", 108, Rarity.RARE, mage.cards.v.VergeRangers.class));
|
||||
cards.add(new SetCardInfo("Vial Smasher the Fierce", 239, Rarity.MYTHIC, mage.cards.v.VialSmasherTheFierce.class));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package org.mage.test.cards.watchers;
|
||||
package org.mage.test.cards.single.aer;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -7,7 +7,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author escplan9
|
||||
*/
|
||||
public class GontisMachinationsTest extends CardTestPlayerBase {
|
||||
|
|
@ -19,57 +18,59 @@ Whenever you lose life for the first time each turn, you get {E}. (You get an en
|
|||
Pay {E}{E}, Sacrifice Gonti's Machinations: Each opponent loses 3 life. You gain life equal to the life lost this way.
|
||||
*/
|
||||
private final String gMachinations = "Gonti's Machinations";
|
||||
|
||||
|
||||
/*
|
||||
* Reported bug: [[Gonti's Machinations]] currently triggers and gain 1 energy whenever you lose life instead of only the first life loss of each turn.
|
||||
* See issue #3499 (test is currently failing due to bug in code)
|
||||
*/
|
||||
* See issue #3499 for context
|
||||
*/
|
||||
@Test
|
||||
public void machinations_ThreeCreaturesCombatDamage_OneTrigger() {
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
String gBears = "Grizzly Bears"; // {1}{G} 2/2
|
||||
String hGiant = "Hill Giant"; // {2}{R} 3/3
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, gMachinations);
|
||||
addCard(Zone.BATTLEFIELD, playerA, memnite);
|
||||
addCard(Zone.BATTLEFIELD, playerA, gBears);
|
||||
addCard(Zone.BATTLEFIELD, playerA, hGiant);
|
||||
|
||||
|
||||
attack(3, playerA, memnite);
|
||||
attack(3, playerA, gBears);
|
||||
attack(3, playerA, hGiant);
|
||||
|
||||
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
|
||||
assertTapped(memnite, true);
|
||||
assertTapped(gBears, true);
|
||||
assertTapped(hGiant, true);
|
||||
assertLife(playerB, 14); // 1 + 2 + 3 damage
|
||||
assertCounterCount(playerB, CounterType.ENERGY, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reported bug: [[Gonti's Machinations]] currently triggers and gain 1 energy whenever you lose life instead of only the first life loss of each turn.
|
||||
* See issue #3499 (test is currently failing due to bug in code)
|
||||
*/
|
||||
* See issue #3499 for context
|
||||
*/
|
||||
@Test
|
||||
public void machinations_NonCombatDamageThreeTimes_OneTrigger() {
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
||||
String bolt = "Lightning Bolt"; // {R} deal 3
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, gMachinations);
|
||||
addCard(Zone.HAND, playerA, bolt, 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
||||
assertTappedCount("Mountain", true, 3);
|
||||
assertGraveyardCount(playerA, bolt, 3);
|
||||
assertLife(playerB, 11); // 3 x 3 damage
|
||||
|
|
@ -7,13 +7,14 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author noxx
|
||||
*/
|
||||
public class ExquisiteBloodTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void BasicCardTest() {
|
||||
public void basicCardTest() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
|
||||
|
|
@ -45,10 +46,12 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
|
||||
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
|
||||
*/
|
||||
@Test
|
||||
public void triggerCascadeTest() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
// +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
|
||||
// −3: Exile target creature. Its controller gains 2 life.
|
||||
// −10: Creatures you control gain flying and double strike until end of turn.
|
||||
|
|
@ -57,14 +60,12 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
// Flying
|
||||
// Whenever you gain life, target opponent loses that much life.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B}
|
||||
|
||||
|
||||
// Whenever an opponent loses life, you gain that much life.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", "Defiant Bloodlord");
|
||||
addTarget(playerA, playerB); // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead)
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
|
|
@ -72,7 +73,9 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
@ -82,15 +85,17 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
|
||||
assertLife(playerB, 0); // Player B is dead, game ends
|
||||
assertLife(playerA, 40);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
|
||||
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
|
||||
*/
|
||||
@Test
|
||||
public void triggerCascadeAjaniSecondAbilityTest() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
// +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
|
||||
// −3: Exile target creature. Its controller gains 2 life.
|
||||
// −10: Creatures you control gain flying and double strike until end of turn.
|
||||
|
|
@ -100,14 +105,12 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
// Flying
|
||||
// Whenever you gain life, target opponent loses that much life.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B}
|
||||
|
||||
|
||||
// Whenever an opponent loses life, you gain that much life.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:", "Silvercoat Lion");
|
||||
addTarget(playerA, playerB); // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead)
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
|
|
@ -115,7 +118,9 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
@ -126,7 +131,28 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
|
|||
|
||||
assertLife(playerB, 0); // Player B is dead, game ends
|
||||
assertLife(playerA, 40);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attackWithTwoCreatures() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
// Whenever an opponent loses life, you gain that much life.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
|
||||
|
||||
attack(1, playerA, "Elite Vanguard", playerB);
|
||||
attack(1, playerA, "Memnite", playerB);
|
||||
|
||||
// no trigger stacking, only 1 trigger
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 2 - 1);
|
||||
assertLife(playerA, 20 + 3);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
package org.mage.test.cards.single.ice;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class OathOfLimDulTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.o.OathOfLimDul Oath of Lim-Dûl} {3}{B}
|
||||
* Enchantment
|
||||
* Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than Oath of Lim-Dûl unless you discard a card. (Damage dealt to you causes you to lose life.)
|
||||
* {B}{B}: Draw a card.
|
||||
*/
|
||||
private static final String oath = "Oath of Lim-Dul";
|
||||
|
||||
@Test
|
||||
public void test_3Sacrifice() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||
addCard(Zone.HAND, playerA, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
|
||||
setChoice(playerA, false); // No to discard on first instance.
|
||||
setChoice(playerA, "Mountain"); // sacrifice Mountain
|
||||
setChoice(playerA, false); // No to discard on second instance.
|
||||
setChoice(playerA, "Mountain"); // sacrifice Mountain
|
||||
setChoice(playerA, false); // No to discard on third instance.
|
||||
setChoice(playerA, "Mountain"); // sacrifice Mountain
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 3);
|
||||
assertGraveyardCount(playerA, "Mountain", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_3Discard() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||
addCard(Zone.HAND, playerA, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
|
||||
setChoice(playerA, true); // Yes to discard on first instance.
|
||||
setChoice(playerA, "Swamp"); // sacrifice Swamp
|
||||
setChoice(playerA, true); // Yes to discard on second instance.
|
||||
setChoice(playerA, "Swamp"); // sacrifice Swamp
|
||||
setChoice(playerA, true); // Yes to discard on third instance.
|
||||
setChoice(playerA, "Swamp"); // sacrifice Swamp
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 3);
|
||||
assertGraveyardCount(playerA, "Swamp", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_1Sacrifice1Discard_NoOther() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Swamp", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
|
||||
setChoice(playerA, true); // Yes to discard on first instance.
|
||||
setChoice(playerA, "Swamp"); // discard Swamp
|
||||
// No more possibility to Discard
|
||||
setChoice(playerA, "Mountain"); // sacrifice Mountain
|
||||
// No more things to Sacrifice
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 3);
|
||||
assertGraveyardCount(playerA, "Mountain", 1);
|
||||
assertGraveyardCount(playerA, "Swamp", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AllSacrificeNoDiscard_KeepCardInHand() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Swamp", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
|
||||
setChoice(playerA, false); // No to discard on first instance.
|
||||
setChoice(playerA, "Mountain"); // sacrifice Mountain
|
||||
setChoice(playerA, false); // No to discard on second instance.
|
||||
setChoice(playerA, false); // No to discard on third instance.
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 3);
|
||||
assertPermanentCount(playerA, oath, 1);
|
||||
assertGraveyardCount(playerA, "Mountain", 1);
|
||||
assertHandCount(playerA, "Swamp", 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package org.mage.test.cards.single.snc;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class VampireScrivenerTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.v.VampireScrivener Vampire Scrivener} {4}{B}
|
||||
* Creature — Vampire Warlock
|
||||
* Flying
|
||||
* Whenever you gain life during your turn, put a +1/+1 counter on Vampire Scrivener.
|
||||
* Whenever you lose life during your turn, put a +1/+1 counter on Vampire Scrivener.
|
||||
* 2/2
|
||||
*/
|
||||
private static final String scrivener = "Vampire Scrivener";
|
||||
|
||||
@Test
|
||||
public void test_LoseLife_Twice() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Forge"); // painland
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); // cause 1 trigger
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); // cause 1 trigger
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 3 - 1);
|
||||
assertCounterCount(playerA, scrivener, CounterType.P1P1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_RakdosCharm() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Kobolds of Kher Keep", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Badlands", 2);
|
||||
addCard(Zone.HAND, playerA, "Rakdos Charm");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rakdos Charm");
|
||||
setModeChoice(playerA, "3"); // Choose third mode
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 4);
|
||||
assertCounterCount(playerA, scrivener, CounterType.P1P1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_RakdosCharm_NotYourTurn() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Kobolds of Kher Keep", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Badlands", 2);
|
||||
addCard(Zone.HAND, playerA, "Rakdos Charm");
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Rakdos Charm");
|
||||
setModeChoice(playerA, "3"); // Choose third mode
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 4);
|
||||
assertCounterCount(playerA, scrivener, CounterType.P1P1, 0); // No trigger, as not your turn.
|
||||
}
|
||||
}
|
||||
|
|
@ -28,12 +28,12 @@ public class GainLoseLifeYourTurnTriggeredAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.GAINED_LIFE
|
||||
|| event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
|| event.getType() == GameEvent.EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return isControlledBy(game.getActivePlayerId())
|
||||
&& isControlledBy(event.getPlayerId());
|
||||
&& isControlledBy(event.getTargetId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.TargetController;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.common.LifeLostThisTurnWatcher;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LoseLifeFirstTimeEachTurnTriggeredAbility extends LoseLifeTriggeredAbility {
|
||||
|
||||
public LoseLifeFirstTimeEachTurnTriggeredAbility(Effect effect) {
|
||||
this(effect, TargetController.YOU, false, false);
|
||||
}
|
||||
|
||||
public LoseLifeFirstTimeEachTurnTriggeredAbility(Effect effect, TargetController targetController, boolean optional, boolean setTargetPointer) {
|
||||
super(effect, targetController, optional, setTargetPointer);
|
||||
addWatcher(new LifeLostThisTurnWatcher());
|
||||
}
|
||||
|
||||
protected LoseLifeFirstTimeEachTurnTriggeredAbility(final LoseLifeFirstTimeEachTurnTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoseLifeFirstTimeEachTurnTriggeredAbility copy() {
|
||||
return new LoseLifeFirstTimeEachTurnTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
LifeLostThisTurnWatcher watcher = game.getState().getWatcher(LifeLostThisTurnWatcher.class);
|
||||
return watcher != null
|
||||
&& watcher.timesLostLifeThisTurn(event.getTargetId()) <= 1
|
||||
&& super.checkTrigger(event, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateTriggerPhrase() {
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return "Whenever you lose life for the first time each turn, ";
|
||||
case OPPONENT:
|
||||
return "Whenever an opponent loses life for the first time each turn, ";
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage: not supported targetController: " + targetController);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.LifeLostEvent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LoseLifeTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<LifeLostEvent> {
|
||||
|
||||
protected final TargetController targetController;
|
||||
private final boolean setTargetPointer;
|
||||
|
||||
public LoseLifeTriggeredAbility(Effect effect) {
|
||||
this(effect, TargetController.YOU, false, false);
|
||||
}
|
||||
|
||||
public LoseLifeTriggeredAbility(Effect effect, TargetController targetController, boolean optional, boolean setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.targetController = targetController;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase(generateTriggerPhrase());
|
||||
}
|
||||
|
||||
protected LoseLifeTriggeredAbility(final LoseLifeTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.targetController = ability.targetController;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoseLifeTriggeredAbility copy() {
|
||||
return new LoseLifeTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
private boolean filterPlayer(UUID playerId, Game game) {
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return isControlledBy(playerId);
|
||||
case OPPONENT:
|
||||
return game.getOpponents(getControllerId()).contains(playerId);
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage: not supported targetController: " + targetController);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!filterPlayer(event.getTargetId(), game)) {
|
||||
return false;
|
||||
}
|
||||
// if target id matches, all events in the batch are relevant
|
||||
this.getEffects().setValue(SavedLifeLossValue.getValueKey(), event.getAmount());
|
||||
if (setTargetPointer) {
|
||||
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String generateTriggerPhrase() {
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return "Whenever you lose life, ";
|
||||
case OPPONENT:
|
||||
return "Whenever an opponent loses life, ";
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage: not supported targetController: " + targetController);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public enum SavedLifeLossValue implements DynamicValue {
|
||||
MANY("many"),
|
||||
MUCH("much");
|
||||
|
||||
private final String message;
|
||||
|
||||
private static final String key = "SavedLifeLoss";
|
||||
|
||||
/**
|
||||
* value key used to store the amount of life lost
|
||||
*/
|
||||
public static String getValueKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
SavedLifeLossValue(String message) {
|
||||
this.message = "that " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return (Integer) effect.getValue(getValueKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedLifeLossValue copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -975,12 +975,17 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
// Combine multiple life loss events in the single event (batch)
|
||||
// see GameEvent.LOST_LIFE_BATCH
|
||||
|
||||
// existing batch
|
||||
// existing batchs
|
||||
boolean isLifeLostBatchUsed = false;
|
||||
boolean isSingleBatchUsed = false;
|
||||
for (GameEvent event : simultaneousEvents) {
|
||||
if (event instanceof LifeLostBatchEvent) {
|
||||
((LifeLostBatchEvent) event).addEvent(lifeLossEvent);
|
||||
isLifeLostBatchUsed = true;
|
||||
} else if (event instanceof LifeLostBatchForOnePlayerEvent
|
||||
&& event.getTargetId().equals(lifeLossEvent.getTargetId())) {
|
||||
((LifeLostBatchForOnePlayerEvent) event).addEvent(lifeLossEvent);
|
||||
isSingleBatchUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -988,6 +993,9 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
if (!isLifeLostBatchUsed) {
|
||||
addSimultaneousEvent(new LifeLostBatchEvent(lifeLossEvent), game);
|
||||
}
|
||||
if (!isSingleBatchUsed) {
|
||||
addSimultaneousEvent(new LifeLostBatchForOnePlayerEvent(lifeLossEvent), game);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSimultaneousTappedToBatch(TappedEvent tappedEvent, Game game) {
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ public class GameEvent implements Serializable {
|
|||
DAMAGE_CAUSES_LIFE_LOSS,
|
||||
PLAYER_LIFE_CHANGE,
|
||||
GAIN_LIFE, GAINED_LIFE,
|
||||
LOSE_LIFE, LOST_LIFE,
|
||||
/* LOSE_LIFE + LOST_LIFE
|
||||
targetId the id of the player loosing life
|
||||
sourceId sourceId of the ability which caused the lose
|
||||
|
|
@ -174,10 +173,17 @@ public class GameEvent implements Serializable {
|
|||
amount amount of life loss
|
||||
flag true = from combat damage - other from non combat damage
|
||||
*/
|
||||
LOST_LIFE_BATCH(true),
|
||||
|
||||
LOSE_LIFE, LOST_LIFE,
|
||||
/* LOST_LIFE_BATCH_FOR_ONE_PLAYER
|
||||
combines all life lost events for a player to a single batch (event)
|
||||
*/
|
||||
LOST_LIFE_BATCH_FOR_ONE_PLAYER(true),
|
||||
/* LOST_LIFE_BATCH
|
||||
combines all player life lost events to a single batch (event)
|
||||
*/
|
||||
LOST_LIFE_BATCH(true),
|
||||
|
||||
PLAY_LAND, LAND_PLAYED,
|
||||
CREATURE_CHAMPIONED,
|
||||
/* CREATURE_CHAMPIONED
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package mage.game.events;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LifeLostBatchForOnePlayerEvent extends BatchEvent<LifeLostEvent> {
|
||||
|
||||
public LifeLostBatchForOnePlayerEvent(LifeLostEvent firstEvent) {
|
||||
super(EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER, true, false, false, firstEvent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package mage.watchers.common;
|
||||
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.util.CardUtil;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LifeLostThisTurnWatcher extends Watcher {
|
||||
|
||||
// player -> number of times (not amount!) that player lost life this turn.
|
||||
private final Map<UUID, Integer> playersLostLife = new HashMap<>();
|
||||
|
||||
public LifeLostThisTurnWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER) {
|
||||
playersLostLife.compute(event.getTargetId(), CardUtil::setOrIncrementValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
playersLostLife.clear();
|
||||
}
|
||||
|
||||
public int timesLostLifeThisTurn(UUID playerId) {
|
||||
return playersLostLife.getOrDefault(playerId, 0);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue