Skip to content

[FIN] Implement Summon: Brynhildr #13699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions Mage.Sets/src/mage/cards/s/SummonBrynhildr.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package mage.cards.s;

import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.SagaAbility;
import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.abilities.keyword.HasteAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.watchers.Watcher;

import java.util.*;

/**
* @author TheElk801
*/
public final class SummonBrynhildr extends CardImpl {

public SummonBrynhildr(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}");

this.subtype.add(SubType.SAGA);
this.subtype.add(SubType.KNIGHT);
this.power = new MageInt(2);
this.toughness = new MageInt(1);

// (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)
SagaAbility sagaAbility = new SagaAbility(this);

// I -- Chain -- Exile the top card of your library. During any turn you put a lore counter on this Saga, you may play that card.
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> {
ability.addEffect(new SummonBrynhildrExileEffect());
ability.withFlavorWord("Chain");
});

// II, III -- Gestalt Mode -- When you next cast a creature spell this turn, it gains haste until end of turn.
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_III, ability -> {
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(
new CastNextSpellDelayedTriggeredAbility(
new SummonBrynhildrHasteEffect(), StaticFilters.FILTER_SPELL_A_CREATURE
)
));
ability.withFlavorWord("Gestalt Mode");
});
this.addAbility(sagaAbility.addHint(SummonBrynhildrCondition.getHint()), new SummonBrynhildrWatcher());
}

private SummonBrynhildr(final SummonBrynhildr card) {
super(card);
}

@Override
public SummonBrynhildr copy() {
return new SummonBrynhildr(this);
}
}

class SummonBrynhildrExileEffect extends OneShotEffect {

SummonBrynhildrExileEffect() {
super(Outcome.Benefit);
staticText = "exile the top card of your library. During any turn you " +
"put a lore counter on this Saga, you may play that card";
}

private SummonBrynhildrExileEffect(final SummonBrynhildrExileEffect effect) {
super(effect);
}

@Override
public SummonBrynhildrExileEffect copy() {
return new SummonBrynhildrExileEffect(this);
}

@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Card card = player.getLibrary().getFromTop(game);
if (card == null) {
return false;
}
player.moveCards(card, Zone.EXILED, source, game);
CardUtil.makeCardPlayable(
game, source, card, false, Duration.Custom, false,
source.getControllerId(), SummonBrynhildrCondition.instance
);
return true;
}
}

enum SummonBrynhildrCondition implements Condition {
instance;
private static final Hint hint = new ConditionHint(instance);

public static Hint getHint() {
return hint;
}

@Override
public boolean apply(Game game, Ability source) {
return SummonBrynhildrWatcher.check(game, source);
}

@Override
public String toString() {
return "You put a lore counter on this permanent this turn";
}
}

class SummonBrynhildrWatcher extends Watcher {

private final Map<MageObjectReference, Set<UUID>> map = new HashMap<>();

SummonBrynhildrWatcher() {
super(WatcherScope.GAME);
}

@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.COUNTER_ADDED
|| !CounterType.STUN.getName().equals(event.getData())) {
return;
}
Optional.ofNullable(event)
.map(GameEvent::getTargetId)
.map(game::getPermanent)
.ifPresent(permanent -> map.computeIfAbsent(
new MageObjectReference(permanent, game), x -> new HashSet<>()
).add(event.getPlayerId()));
}

@Override
public void reset() {
super.reset();
map.clear();
}

static boolean check(Game game, Ability source) {
return game.getState()
.getWatcher(SummonBrynhildrWatcher.class)
.map
.getOrDefault(new MageObjectReference(
source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
), Collections.emptySet())
.contains(source.getControllerId());
}
}

class SummonBrynhildrHasteEffect extends OneShotEffect {

SummonBrynhildrHasteEffect() {
super(Outcome.Benefit);
staticText = "it gains haste until end of turn";
}

private SummonBrynhildrHasteEffect(final SummonBrynhildrHasteEffect effect) {
super(effect);
}

@Override
public SummonBrynhildrHasteEffect copy() {
return new SummonBrynhildrHasteEffect(this);
}

@Override
public boolean apply(Game game, Ability source) {
Spell spell = (Spell) getValue("spellCast");
if (spell != null) {
game.getState().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance())
.setTargetPointer(new FixedTarget(spell.getCard().getId())), source);
}
return true;
}
}
2 changes: 2 additions & 0 deletions Mage.Sets/src/mage/sets/FinalFantasy.java
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ private FinalFantasy() {
cards.add(new SetCardInfo("Summon: Anima", 364, Rarity.UNCOMMON, mage.cards.s.SummonAnima.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Bahamut", 1, Rarity.MYTHIC, mage.cards.s.SummonBahamut.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Bahamut", 356, Rarity.MYTHIC, mage.cards.s.SummonBahamut.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Brynhildr", 160, Rarity.RARE, mage.cards.s.SummonBrynhildr.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Brynhildr", 366, Rarity.RARE, mage.cards.s.SummonBrynhildr.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Choco/Mog", 35, Rarity.COMMON, mage.cards.s.SummonChocoMog.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Choco/Mog", 358, Rarity.COMMON, mage.cards.s.SummonChocoMog.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Summon: Esper Maduin", 185, Rarity.RARE, mage.cards.s.SummonEsperMaduin.class, NON_FULL_USE_VARIOUS));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package mage.abilities.common.delayed;

import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
Expand All @@ -18,9 +17,7 @@
/**
* @author TheElk801
*/
public class AddCounterNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility {

private final FilterSpell filter;
public class AddCounterNextSpellDelayedTriggeredAbility extends CastNextSpellDelayedTriggeredAbility {

public AddCounterNextSpellDelayedTriggeredAbility() {
this(StaticFilters.FILTER_SPELL_A_CREATURE);
Expand All @@ -31,38 +28,17 @@ public AddCounterNextSpellDelayedTriggeredAbility(FilterSpell filter) {
}

public AddCounterNextSpellDelayedTriggeredAbility(int amount, FilterSpell filter) {
super(new AddCounterNextSpellEffect(amount), Duration.EndOfTurn, true, false);
this.filter = filter;
this.setTriggerPhrase("When you next cast " + filter.getMessage() + " this turn, ");
super(new AddCounterNextSpellEffect(amount), filter, null, false);
}

private AddCounterNextSpellDelayedTriggeredAbility(final AddCounterNextSpellDelayedTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
}

@Override
public AddCounterNextSpellDelayedTriggeredAbility copy() {
return new AddCounterNextSpellDelayedTriggeredAbility(this);
}

@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.SPELL_CAST;
}

@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!isControlledBy(event.getPlayerId())) {
return false;
}
Spell spell = game.getSpell(event.getTargetId());
if (spell == null || !filter.match(spell, getControllerId(), this, game)) {
return false;
}
this.getEffects().setValue("spellCast", spell);
return true;
}
}

class AddCounterNextSpellEffect extends ReplacementEffectImpl {
Expand All @@ -72,7 +48,8 @@ class AddCounterNextSpellEffect extends ReplacementEffectImpl {
AddCounterNextSpellEffect(int amount) {
super(Duration.EndOfStep, Outcome.BoostCreature);
this.amount = amount;
staticText = "that creature enters with " + CardUtil.numberToText(amount, "an") + " additional +1/+1 counter" + (amount > 1 ? "s" : "") + " on it";
staticText = "that creature enters with " + CardUtil.numberToText(amount, "an") +
" additional +1/+1 counter" + (amount > 1 ? "s" : "") + " on it";
}

private AddCounterNextSpellEffect(AddCounterNextSpellEffect effect) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package mage.abilities.common.delayed;

import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.constants.Duration;
import mage.filter.FilterSpell;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.target.targetpointer.FixedTarget;

/**
* @author TheElk801
*/
public class CastNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility {

private final FilterSpell filter;
private final String rule;
private final boolean setTargetPointer;

public CastNextSpellDelayedTriggeredAbility(Effect effect, FilterSpell filter) {
this(effect, filter, null, false);
}

public CastNextSpellDelayedTriggeredAbility(Effect effect, FilterSpell filter, String rule, boolean setTargetPointer) {
super(effect, Duration.EndOfTurn, true, false);
this.filter = filter;
this.setTriggerPhrase("When you next cast " + filter.getMessage() + " this turn, ");
this.rule = rule;
this.setTargetPointer = setTargetPointer;
}

protected CastNextSpellDelayedTriggeredAbility(final CastNextSpellDelayedTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
this.rule = ability.rule;
this.setTargetPointer = ability.setTargetPointer;
}

@Override
public CastNextSpellDelayedTriggeredAbility copy() {
return new CastNextSpellDelayedTriggeredAbility(this);
}

@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.SPELL_CAST;
}

@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!isControlledBy(event.getPlayerId())) {
return false;
}
Spell spell = game.getSpell(event.getTargetId());
if (spell == null || !filter.match(spell, getControllerId(), this, game)) {
return false;
}
this.getEffects().setValue("spellCast", spell);
if (setTargetPointer) {
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId()));
}
return true;
}

@Override
public String getRule() {
if (rule != null && !rule.isEmpty()) {
return rule;
}
return super.getRule();
}
}
Loading