Skip to content

Commit

Permalink
Introduce new batch event for life lost for a specific player (#13071)
Browse files Browse the repository at this point in the history
* Introduce new batch event for life lost for a specific player

closes #12202, fix #10805

* implement [DSC] Valgavoth, Harrower of Souls

* text fixes
  • Loading branch information
xenohedron authored Nov 20, 2024
1 parent 95e986d commit d6cf207
Show file tree
Hide file tree
Showing 28 changed files with 693 additions and 707 deletions.
42 changes: 5 additions & 37 deletions Mage.Sets/src/mage/cards/b/BloodthirstyConqueror.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
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;
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;

Expand All @@ -36,7 +34,8 @@ public BloodthirstyConqueror(UUID ownerId, CardSetInfo setInfo) {
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) {
Expand All @@ -48,34 +47,3 @@ public BloodthirstyConqueror copy() {
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;
}
}
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/c/CuratorBeastie.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public CuratorBeastie(UUID ownerId, CardSetInfo setInfo) {
// 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()));
Expand Down
55 changes: 8 additions & 47 deletions Mage.Sets/src/mage/cards/e/ExquisiteBlood.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@

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
*/
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) {
Expand All @@ -35,39 +32,3 @@ public ExquisiteBlood copy() {
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.";
}
}
3 changes: 2 additions & 1 deletion Mage.Sets/src/mage/cards/g/GlitchInterpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public GlitchInterpreter(UUID ownerId, CardSetInfo setInfo) {
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);

Expand Down
78 changes: 3 additions & 75 deletions Mage.Sets/src/mage/cards/g/GontisMachinations.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,14 +10,7 @@
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;

/**
Expand All @@ -29,8 +22,8 @@ public GontisMachinations(UUID ownerId, CardSetInfo setInfo) {
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(
Expand All @@ -50,68 +43,3 @@ public GontisMachinations copy() {
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);
}
}
59 changes: 7 additions & 52 deletions Mage.Sets/src/mage/cards/l/LichsMastery.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -56,7 +56,7 @@ public LichsMastery(UUID ownerId, CardSetInfo setInfo) {
));

// 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));
Expand Down Expand Up @@ -99,57 +99,15 @@ public boolean applies(GameEvent event, Ability source, Game game) {
}
}

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
Expand All @@ -165,7 +123,7 @@ public boolean apply(Game game, Ability source) {
}
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();
Expand All @@ -182,7 +140,7 @@ public boolean apply(Game game, Ability source) {
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);
Expand All @@ -194,7 +152,4 @@ public boolean apply(Game game, Ability source) {
return true;
}

public void setAmount(int amount) {
this.amount = amount;
}
}
Loading

0 comments on commit d6cf207

Please sign in to comment.