[WHO] Implement Barbara Wright (#13602)

* [WHO] Implement Barbara Wright

* add test

* add more tests

* update reminder text
This commit is contained in:
Evan Kranzler 2025-05-08 12:38:10 -04:00 committed by GitHub
parent daf390d632
commit 5a7d6805e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 231 additions and 37 deletions

View file

@ -6,11 +6,13 @@ import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.ReadAheadAbility;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.SagaChapter;
import mage.constants.Zone;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -30,7 +32,6 @@ public class SagaAbility extends SimpleStaticAbility {
private final SagaChapter maxChapter;
private final boolean showSacText;
private final boolean readAhead;
public SagaAbility(Card card) {
this(card, SagaChapter.CHAPTER_III, false);
@ -44,19 +45,20 @@ public class SagaAbility extends SimpleStaticAbility {
super(Zone.ALL, null);
this.maxChapter = maxChapter;
this.showSacText = card.getSecondCardFace() == null && !card.isNightCard();
this.readAhead = readAhead;
this.setRuleVisible(true);
this.setRuleAtTheTop(true);
Ability ability = new EntersBattlefieldAbility(new SagaLoreCountersEffect(readAhead, maxChapter));
Ability ability = new EntersBattlefieldAbility(new SagaLoreCountersEffect(maxChapter));
ability.setRuleVisible(false);
card.addAbility(ability);
if (readAhead) {
card.addAbility(ReadAheadAbility.getInstance());
}
}
protected SagaAbility(final SagaAbility ability) {
super(ability);
this.maxChapter = ability.maxChapter;
this.showSacText = ability.showSacText;
this.readAhead = ability.readAhead;
}
public void addChapterEffect(Card card, SagaChapter chapter, Effect... effects) {
@ -115,7 +117,7 @@ public class SagaAbility extends SimpleStaticAbility {
public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, boolean optional, Consumer<TriggeredAbility> applier) {
for (int i = fromChapter.getNumber(); i <= toChapter.getNumber(); i++) {
ChapterTriggeredAbility ability = new ChapterTriggeredAbility(
SagaChapter.getChapter(i), toChapter, optional, readAhead
SagaChapter.getChapter(i), toChapter, optional
);
applier.accept(ability);
if (i > fromChapter.getNumber()) {
@ -131,10 +133,7 @@ public class SagaAbility extends SimpleStaticAbility {
@Override
public String getRule() {
return (readAhead
? "Read ahead <i>(Choose a chapter and start with that many lore counters. " +
"Add one after your draw step. Skipped chapters don't trigger."
: "<i>(As this Saga enters and after your draw step, add a lore counter.")
return "<i>(As this Saga enters and after your draw step, add a lore counter."
+ (showSacText ? " Sacrifice after " + maxChapter.toString() + '.' : "") + ")</i> ";
}
@ -158,22 +157,23 @@ public class SagaAbility extends SimpleStaticAbility {
return ability instanceof ChapterTriggeredAbility
&& ((ChapterTriggeredAbility) ability).getChapterFrom().getNumber() == maxChapter;
}
public static Ability makeGainReadAheadAbility() {
return new GainReadAheadAbility();
}
}
class SagaLoreCountersEffect extends OneShotEffect {
private final boolean readAhead;
private final SagaChapter maxChapter;
SagaLoreCountersEffect(boolean readAhead, SagaChapter maxChapter) {
SagaLoreCountersEffect(SagaChapter maxChapter) {
super(Outcome.Benefit);
this.readAhead = readAhead;
this.maxChapter = maxChapter;
}
private SagaLoreCountersEffect(final SagaLoreCountersEffect effect) {
super(effect);
this.readAhead = effect.readAhead;
this.maxChapter = effect.maxChapter;
}
@ -188,7 +188,8 @@ class SagaLoreCountersEffect extends OneShotEffect {
if (permanent == null) {
return false;
}
if (!readAhead) {
if (!permanent.hasAbility(ReadAheadAbility.getInstance(), game)
&& !GainReadAheadAbility.checkForAbility(game, source)) {
return permanent.addCounters(CounterType.LORE.createInstance(), source, game);
}
Player player = game.getPlayer(source.getControllerId());
@ -206,20 +207,17 @@ class SagaLoreCountersEffect extends OneShotEffect {
class ChapterTriggeredAbility extends TriggeredAbilityImpl {
private final SagaChapter chapterFrom, chapterTo;
private final boolean readAhead;
ChapterTriggeredAbility(SagaChapter chapterFrom, SagaChapter chapterTo, boolean optional, boolean readAhead) {
ChapterTriggeredAbility(SagaChapter chapterFrom, SagaChapter chapterTo, boolean optional) {
super(Zone.ALL, null, optional);
this.chapterFrom = chapterFrom;
this.chapterTo = chapterTo;
this.readAhead = readAhead;
}
private ChapterTriggeredAbility(final ChapterTriggeredAbility ability) {
super(ability);
this.chapterFrom = ability.chapterFrom;
this.chapterTo = ability.chapterTo;
this.readAhead = ability.readAhead;
}
@Override
@ -238,7 +236,9 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl {
return false;
}
int loreCounters = permanent.getCounters(game).getCount(CounterType.LORE);
if (readAhead && permanent.getTurnsOnBattlefield() == 0) {
if (permanent.getTurnsOnBattlefield() == 0
&& (permanent.hasAbility(ReadAheadAbility.getInstance(), game)
|| GainReadAheadAbility.checkForAbility(game, this))) {
return chapterFrom.getNumber() == loreCounters;
}
return loreCounters - event.getAmount() < chapterFrom.getNumber()
@ -275,3 +275,35 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl {
+ " - " + CardUtil.getTextWithFirstCharUpperCase(super.getRule());
}
}
class GainReadAheadAbility extends SimpleStaticAbility {
private static final FilterPermanent filter = new FilterPermanent(SubType.SAGA, "Sagas");
GainReadAheadAbility() {
super(new GainAbilityControlledEffect(
ReadAheadAbility.getInstance(), Duration.WhileOnBattlefield, filter
).setText("Sagas you control have read ahead. <i>(As a Saga enters, choose a chapter " +
"and start with that many lore counters. Skipped chapters don't trigger.)</i>"));
}
private GainReadAheadAbility(final GainReadAheadAbility ability) {
super(ability);
}
@Override
public GainReadAheadAbility copy() {
return new GainReadAheadAbility(this);
}
static boolean checkForAbility(Game game, Ability source) {
return game
.getBattlefield()
.getActivePermanents(
StaticFilters.FILTER_CONTROLLED_PERMANENT,
source.getControllerId(), source, game
)
.stream()
.anyMatch(p -> p.getAbilities(game).containsClass(GainReadAheadAbility.class));
}
}

View file

@ -0,0 +1,43 @@
package mage.abilities.keyword;
import mage.abilities.MageSingleton;
import mage.abilities.StaticAbility;
import mage.constants.Zone;
import java.io.ObjectStreamException;
/**
* @author TheElk801
*/
public class ReadAheadAbility extends StaticAbility implements MageSingleton {
private static final ReadAheadAbility instance;
static {
instance = new ReadAheadAbility();
}
private Object readResolve() throws ObjectStreamException {
return instance;
}
public static ReadAheadAbility getInstance() {
return instance;
}
private ReadAheadAbility() {
super(Zone.BATTLEFIELD, null);
this.setRuleAtTheTop(true);
}
@Override
public String getRule() {
return "read ahead";
}
@Override
public ReadAheadAbility copy() {
return instance;
}
}