[FIC] Implement Lifestream's Blessing

This commit is contained in:
theelk801 2025-06-02 09:56:43 -04:00
parent db4b076b93
commit c7006017ca
3 changed files with 235 additions and 0 deletions

View file

@ -0,0 +1,141 @@
package mage.cards.l;
import mage.MageInt;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.keyword.ForetellAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class LifestreamsBlessing extends CardImpl {
public LifestreamsBlessing(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{G}{G}");
// Draw X cards, where X is the greatest power among creatures you controlled as you cast this spell. If this spell was cast from exile, you gain twice X life.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(LifestreamsBlessingValue.ONCE));
this.getSpellAbility().addEffect(new GainLifeEffect(LifestreamsBlessingValue.TWICE)
.setText("if this spell was cast from exile, you gain twice X life"));
this.getSpellAbility().addWatcher(new LifestreamsBlessingWatcher());
// Foretell {4}{G}
this.addAbility(new ForetellAbility(this, "{4}{G}"));
}
private LifestreamsBlessing(final LifestreamsBlessing card) {
super(card);
}
@Override
public LifestreamsBlessing copy() {
return new LifestreamsBlessing(this);
}
}
enum LifestreamsBlessingValue implements DynamicValue {
ONCE(false),
TWICE(true);
private final boolean flag;
LifestreamsBlessingValue(boolean flag) {
this.flag = flag;
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
if (flag && !Optional
.ofNullable(sourceAbility)
.map(Ability::getSourceId)
.map(game::getSpell)
.map(Spell::getFromZone)
.map(Zone.EXILED::match)
.orElse(false)) {
return 0;
}
return (flag ? 2 : 1) * LifestreamsBlessingWatcher.getValue(game, sourceAbility);
}
@Override
public LifestreamsBlessingValue copy() {
return this;
}
@Override
public String getMessage() {
return "the greatest power among creatures you controlled as you cast this spell";
}
@Override
public String toString() {
return "X";
}
}
class LifestreamsBlessingWatcher extends Watcher {
private final Map<MageObjectReference, Integer> map = new HashMap<>();
LifestreamsBlessingWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpell(event.getTargetId());
if (spell == null) {
return;
}
map.put(
new MageObjectReference(spell.getSpellAbility().getSourceId(), game),
game.getBattlefield()
.getActivePermanents(
StaticFilters.FILTER_CONTROLLED_CREATURE, spell.getControllerId(), game
)
.stream()
.map(MageObject::getPower)
.mapToInt(MageInt::getValue)
.max()
.orElse(0)
);
}
@Override
public void reset() {
super.reset();
map.clear();
}
static int getValue(Game game, Ability source) {
return game
.getState()
.getWatcher(LifestreamsBlessingWatcher.class)
.map
.getOrDefault(new MageObjectReference(
source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
), 0);
}
}

View file

@ -226,6 +226,8 @@ public final class FinalFantasyCommander extends ExpansionSet {
cards.add(new SetCardInfo("Krile Baldesion", 86, Rarity.RARE, mage.cards.k.KrileBaldesion.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Legions to Ashes", 326, Rarity.RARE, mage.cards.l.LegionsToAshes.class));
cards.add(new SetCardInfo("Lethal Scheme", 277, Rarity.RARE, mage.cards.l.LethalScheme.class));
cards.add(new SetCardInfo("Lifestream's Blessing", 122, Rarity.RARE, mage.cards.l.LifestreamsBlessing.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Lifestream's Blessing", 67, Rarity.RARE, mage.cards.l.LifestreamsBlessing.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Lightning Greaves", 349, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class));
cards.add(new SetCardInfo("Lingering Souls", 245, Rarity.UNCOMMON, mage.cards.l.LingeringSouls.class));
cards.add(new SetCardInfo("Locke, Treasure Hunter", 177, Rarity.RARE, mage.cards.l.LockeTreasureHunter.class, NON_FULL_USE_VARIOUS));

View file

@ -0,0 +1,92 @@
package org.mage.test.cards.single.fic;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author TheElk801
*/
public class LifestreamsBlessingTest extends CardTestPlayerBase {
private static final String blessing = "Lifestream's Blessing";
private static final String jackal = "Trained Jackal";
private static final String bear = "Ashcoat Bear";
private static final String giant = "Hill Giant";
@Test
public void testRegular() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);
addCard(Zone.BATTLEFIELD, playerA, jackal);
addCard(Zone.BATTLEFIELD, playerB, giant);
addCard(Zone.HAND, playerA, blessing);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blessing);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertHandCount(playerA, 1);
assertLife(playerA, 20);
}
@Test
public void testFlashIn22() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6 + 2);
addCard(Zone.BATTLEFIELD, playerA, jackal);
addCard(Zone.BATTLEFIELD, playerB, giant);
addCard(Zone.HAND, playerA, blessing);
addCard(Zone.HAND, playerA, bear);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blessing);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertHandCount(playerA, 1);
assertLife(playerA, 20);
}
private static final String twincast = "Twincast";
@Test
public void testTwincast() {
addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 6 + 2);
addCard(Zone.BATTLEFIELD, playerA, jackal);
addCard(Zone.BATTLEFIELD, playerB, giant);
addCard(Zone.HAND, playerA, blessing);
addCard(Zone.HAND, playerA, twincast);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blessing);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, twincast, blessing);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertHandCount(playerA, 1);
assertLife(playerA, 20);
}
@Test
public void testForetell() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
addCard(Zone.BATTLEFIELD, playerA, jackal);
addCard(Zone.BATTLEFIELD, playerB, giant);
addCard(Zone.HAND, playerA, blessing);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "For");
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "For");
setStrictChooseMode(true);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertHandCount(playerA, 1 + 1);
assertLife(playerA, 20 + 2);
}
}