forked from External/mage
[FIC] Implement G'raha Tia, Scion Reborn, rework DoIfCostPaid and "do only once" effects (#13660)
* rework effects with DoIfCostPaid and "do this only once each turn" * [FIC] Implement G'raha Tia, Scion Reborn * [FIC] Implement Emet Selch of the Third Seat * rework Emet-Selch * add test * add static methods to handle whether ability was used this turn
This commit is contained in:
parent
71c4be03fb
commit
40d24869a8
11 changed files with 361 additions and 50 deletions
137
Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java
Normal file
137
Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package mage.cards.e;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
|
||||
import mage.abilities.effects.common.replacement.ThatSpellGraveyardExileReplacementEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.predicate.card.CastFromZonePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.LifeLostBatchEvent;
|
||||
import mage.game.events.LifeLostEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class EmetSelchOfTheThirdSeat extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("spells you cast from your graveyard");
|
||||
|
||||
static {
|
||||
filter.add(new CastFromZonePredicate(Zone.GRAVEYARD));
|
||||
}
|
||||
|
||||
public EmetSelchOfTheThirdSeat(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.ELDER);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Spells you cast from your graveyard cost {2} less to cast.
|
||||
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2)));
|
||||
|
||||
// Whenever one or more opponents lose life, you may cast target instant or sorcery card from your graveyard. If that spell would be put into your graveyard, exile it instead. Do this only once each turn.
|
||||
this.addAbility(new EmetSelchOfTheThirdSeatAbility());
|
||||
}
|
||||
|
||||
private EmetSelchOfTheThirdSeat(final EmetSelchOfTheThirdSeat card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmetSelchOfTheThirdSeat copy() {
|
||||
return new EmetSelchOfTheThirdSeat(this);
|
||||
}
|
||||
}
|
||||
|
||||
class EmetSelchOfTheThirdSeatAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<LifeLostEvent> {
|
||||
|
||||
EmetSelchOfTheThirdSeatAbility() {
|
||||
super(Zone.BATTLEFIELD, new EmetSelchOfTheThirdSeatEffect());
|
||||
this.setTriggerPhrase("Whenever one or more opponents lose life, ");
|
||||
this.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD));
|
||||
this.setDoOnlyOnceEachTurn(true);
|
||||
}
|
||||
|
||||
private EmetSelchOfTheThirdSeatAbility(final EmetSelchOfTheThirdSeatAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEvent(LifeLostEvent event, Game game) {
|
||||
return game.getOpponents(getControllerId()).contains(event.getTargetId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
List<LifeLostEvent> filteredEvents = getFilteredEvents((LifeLostBatchEvent) event, game);
|
||||
return !filteredEvents.isEmpty()
|
||||
&& CardUtil
|
||||
.getEventTargets(event)
|
||||
.stream()
|
||||
.anyMatch(uuid -> LifeLostBatchEvent.getLifeLostByPlayer(filteredEvents, uuid) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmetSelchOfTheThirdSeatAbility copy() {
|
||||
return new EmetSelchOfTheThirdSeatAbility(this);
|
||||
}
|
||||
}
|
||||
|
||||
class EmetSelchOfTheThirdSeatEffect extends OneShotEffect {
|
||||
|
||||
EmetSelchOfTheThirdSeatEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "cast target instant or sorcery card from your graveyard. " +
|
||||
"If that spell would be put into your graveyard, exile it instead";
|
||||
}
|
||||
|
||||
private EmetSelchOfTheThirdSeatEffect(final EmetSelchOfTheThirdSeatEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmetSelchOfTheThirdSeatEffect copy() {
|
||||
return new EmetSelchOfTheThirdSeatEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null || !CardUtil.castSingle(player, source, game, card)) {
|
||||
// if the spell isn't cast then the ability can be used again in the same turn
|
||||
TriggeredAbility.clearDidThisTurn(source, game);
|
||||
return false;
|
||||
}
|
||||
game.addEffect(new ThatSpellGraveyardExileReplacementEffect(true)
|
||||
.setTargetPointer(new FixedTarget(card, game)), source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
123
Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java
Normal file
123
Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.keyword.LifelinkAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.token.HeroToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class GrahaTiaScionReborn extends CardImpl {
|
||||
|
||||
public GrahaTiaScionReborn(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.CAT);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Lifelink
|
||||
this.addAbility(LifelinkAbility.getInstance());
|
||||
|
||||
// Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(
|
||||
new GrahaTiaScionRebornEffect(),
|
||||
new PayLifeCost(
|
||||
GrahaTiaScionRebornValue.instance,
|
||||
"pay X life, where X is that spell's mana value"
|
||||
)
|
||||
), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false).setDoOnlyOnceEachTurn(true).withFlavorWord("Throw Wide the Gates"));
|
||||
}
|
||||
|
||||
private GrahaTiaScionReborn(final GrahaTiaScionReborn card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrahaTiaScionReborn copy() {
|
||||
return new GrahaTiaScionReborn(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum GrahaTiaScionRebornValue implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return CardUtil.getEffectValueFromAbility(sourceAbility, "spellCast", Spell.class)
|
||||
.map(Spell::getManaValue)
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrahaTiaScionRebornValue copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
}
|
||||
}
|
||||
|
||||
class GrahaTiaScionRebornEffect extends OneShotEffect {
|
||||
|
||||
GrahaTiaScionRebornEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "create a 1/1 colorless Hero creature token and put X +1/+1 counters on it";
|
||||
}
|
||||
|
||||
private GrahaTiaScionRebornEffect(final GrahaTiaScionRebornEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrahaTiaScionRebornEffect copy() {
|
||||
return new GrahaTiaScionRebornEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Token token = new HeroToken();
|
||||
token.putOntoBattlefield(1, game, source);
|
||||
int count = GrahaTiaScionRebornValue.instance.calculate(game, source, this);
|
||||
if (count < 1) {
|
||||
return true;
|
||||
}
|
||||
for (UUID tokenId : token.getLastAddedTokenIds()) {
|
||||
Optional.ofNullable(tokenId)
|
||||
.map(game::getPermanent)
|
||||
.ifPresent(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(count), source, game));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards.i;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
|
|
@ -18,6 +16,8 @@ import mage.filter.common.FilterControlledCreaturePermanent;
|
|||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author paasar
|
||||
*/
|
||||
|
|
@ -41,15 +41,9 @@ public final class IrreverentGremlin extends CardImpl {
|
|||
this.addAbility(new MenaceAbility());
|
||||
|
||||
// Whenever another creature you control with power 2 or less enters, you may discard a card. If you do, draw a card. Do this only once each turn.
|
||||
this.addAbility(
|
||||
new EntersBattlefieldAllTriggeredAbility(
|
||||
new DoIfCostPaid(
|
||||
new DrawCardSourceControllerEffect(1),
|
||||
new DiscardCardCost(),
|
||||
null,
|
||||
false), // since triggered ability is optional (do only once), DoIfCostPaid must not be
|
||||
filter)
|
||||
.setDoOnlyOnceEachTurn(true));
|
||||
this.addAbility(new EntersBattlefieldAllTriggeredAbility(
|
||||
new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), filter
|
||||
).setDoOnlyOnceEachTurn(true));
|
||||
}
|
||||
|
||||
private IrreverentGremlin(final IrreverentGremlin card) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
|
||||
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -14,6 +12,8 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
|
|
@ -21,8 +21,9 @@ import mage.filter.predicate.permanent.AttackingPredicate;
|
|||
import mage.filter.predicate.permanent.TokenPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author alexander-novo
|
||||
*/
|
||||
public class ShaunFatherOfSynths extends CardImpl {
|
||||
|
|
@ -41,7 +42,7 @@ public class ShaunFatherOfSynths extends CardImpl {
|
|||
}
|
||||
|
||||
public ShaunFatherOfSynths(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{3}{U}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{R}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
|
|
@ -58,11 +59,14 @@ public class ShaunFatherOfSynths extends CardImpl {
|
|||
token.addCardType(CardType.CREATURE);
|
||||
token.addCardType(CardType.ARTIFACT);
|
||||
token.addSubType(SubType.SYNTH);
|
||||
}).setText(
|
||||
"create a tapped and attacking token that's a copy of target attacking legendary creature you control other than Shaun, except it's not legendary and it's a Synth artifact creature in addition to its other types");
|
||||
TriggeredAbility ability = new AttacksWithCreaturesTriggeredAbility(effect, 1);
|
||||
}).setText("create a tapped and attacking token that's a copy of target " +
|
||||
"attacking legendary creature you control other than {this}, " +
|
||||
"except it's not legendary and it's a Synth artifact creature in addition to its other types");
|
||||
Ability ability = new AttacksWithCreaturesTriggeredAbility(
|
||||
Zone.BATTLEFIELD, effect, 1,
|
||||
StaticFilters.FILTER_PERMANENT_CREATURES, false, true
|
||||
);
|
||||
ability.addTarget(new TargetPermanent(attackFilter));
|
||||
ability.setOptional();
|
||||
this.addAbility(ability);
|
||||
|
||||
// When Shaun leaves the battlefield, exile all Synth tokens you control.
|
||||
|
|
|
|||
|
|
@ -133,6 +133,8 @@ public final class FinalFantasyCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Edgar, Master Machinist", 80, Rarity.RARE, mage.cards.e.EdgarMasterMachinist.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Elena, Turk Recruit", 133, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Elena, Turk Recruit", 18, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Emet-Selch of the Third Seat", 170, Rarity.RARE, mage.cards.e.EmetSelchOfTheThirdSeat.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Emet-Selch of the Third Seat", 81, Rarity.RARE, mage.cards.e.EmetSelchOfTheThirdSeat.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Endless Detour", 324, Rarity.RARE, mage.cards.e.EndlessDetour.class));
|
||||
cards.add(new SetCardInfo("Espers to Magicite", 114, Rarity.RARE, mage.cards.e.EspersToMagicite.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Espers to Magicite", 43, Rarity.RARE, mage.cards.e.EspersToMagicite.class, NON_FULL_USE_VARIOUS));
|
||||
|
|
@ -160,6 +162,11 @@ public final class FinalFantasyCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Fortified Village", 396, Rarity.RARE, mage.cards.f.FortifiedVillage.class));
|
||||
cards.add(new SetCardInfo("Furious Rise", 294, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
|
||||
cards.add(new SetCardInfo("Furycalm Snarl", 397, Rarity.RARE, mage.cards.f.FurycalmSnarl.class));
|
||||
cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 172, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 203, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 211, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 222, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 3, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Game Trail", 398, Rarity.RARE, mage.cards.g.GameTrail.class));
|
||||
cards.add(new SetCardInfo("Gatta and Luzzu", 134, Rarity.RARE, mage.cards.g.GattaAndLuzzu.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Gatta and Luzzu", 19, Rarity.RARE, mage.cards.g.GattaAndLuzzu.class, NON_FULL_USE_VARIOUS));
|
||||
|
|
|
|||
|
|
@ -124,4 +124,31 @@ public class DoThisOnlyOnceEachTurnTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
}
|
||||
|
||||
private static final String emetSelch = "Emet-Selch of the Third Seat";
|
||||
private static final String hazard = "Tectonic Hazard";
|
||||
private static final String slinger = "Goblin Fireslinger";
|
||||
|
||||
@Test
|
||||
public void testEmetSelch() {
|
||||
addCard(Zone.HAND, playerA, "Mountain");
|
||||
addCard(Zone.BATTLEFIELD, playerA, emetSelch);
|
||||
addCard(Zone.BATTLEFIELD, playerA, slinger, 2);
|
||||
addCard(Zone.GRAVEYARD, playerA, hazard);
|
||||
|
||||
// player is unable to cast spell despite choosing to, choice is reset
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}:", playerB);
|
||||
addTarget(playerA, hazard);
|
||||
setChoice(playerA, true);
|
||||
|
||||
// this time player can cast and does, so ability doesn't trigger again
|
||||
playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mountain");
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}:", playerB);
|
||||
addTarget(playerA, hazard);
|
||||
setChoice(playerA, true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
assertLife(playerB, 20 - 1 - 1 - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package mage.abilities;
|
|||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -97,8 +99,6 @@ public interface TriggeredAbility extends Ability {
|
|||
|
||||
boolean isOptional();
|
||||
|
||||
TriggeredAbility setOptional();
|
||||
|
||||
/**
|
||||
* Allow trigger to fire after source leave the battlefield (example: will use LKI on itself sacrifice)
|
||||
*/
|
||||
|
|
@ -131,4 +131,34 @@ public interface TriggeredAbility extends Ability {
|
|||
TriggeredAbility setTriggerPhrase(String triggerPhrase);
|
||||
|
||||
String getTriggerPhrase();
|
||||
|
||||
static String makeDidThisTurnString(Ability ability, Game game) {
|
||||
return CardUtil.getCardZoneString("lastTurnUsed" + ability.getOriginalId(), ability.getSourceId(), game);
|
||||
}
|
||||
|
||||
static void setDidThisTurn(Ability ability, Game game) {
|
||||
game.getState().setValue(makeDidThisTurnString(ability, game), game.getTurnNum());
|
||||
}
|
||||
|
||||
/**
|
||||
* For abilities which say "Do this only once each turn".
|
||||
* Most of the time this is handled automatically by calling setDoOnlyOnceEachTurn(true),
|
||||
* but sometimes the ability will need a way to clear whether it's been used this turn within an effect.
|
||||
*
|
||||
* @param ability
|
||||
* @param game
|
||||
*/
|
||||
static void clearDidThisTurn(Ability ability, Game game) {
|
||||
game.getState().removeValue(makeDidThisTurnString(ability, game));
|
||||
}
|
||||
|
||||
static boolean checkDidThisTurn(Ability ability, Game game) {
|
||||
return Optional
|
||||
.ofNullable(makeDidThisTurnString(ability, game))
|
||||
.map(game.getState()::getValue)
|
||||
.filter(Integer.class::isInstance)
|
||||
.map(Integer.class::cast)
|
||||
.filter(x -> x == game.getTurnNum())
|
||||
.isPresent();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,13 +156,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
|
||||
@Override
|
||||
public boolean checkUsedAlready(Game game) {
|
||||
if (!doOnlyOnceEachTurn) {
|
||||
return false;
|
||||
}
|
||||
Integer lastTurnUsed = (Integer) game.getState().getValue(
|
||||
CardUtil.getCardZoneString("lastTurnUsed" + getOriginalId(), sourceId, game)
|
||||
);
|
||||
return lastTurnUsed != null && lastTurnUsed == game.getTurnNum();
|
||||
return doOnlyOnceEachTurn && TriggeredAbility.checkDidThisTurn(this, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -209,7 +203,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
@Override
|
||||
public TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce) {
|
||||
this.doOnlyOnceEachTurn = doOnlyOnce;
|
||||
setOptional();
|
||||
if (CardUtil.castStream(this.getAllEffects(), DoIfCostPaid.class).noneMatch(DoIfCostPaid::isOptional)) {
|
||||
this.optional = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -268,11 +264,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (doOnlyOnceEachTurn) {
|
||||
game.getState().setValue(CardUtil.getCardZoneString(
|
||||
"lastTurnUsed" + getOriginalId(), sourceId, game
|
||||
), game.getTurnNum());
|
||||
if (doOnlyOnceEachTurn) {
|
||||
TriggeredAbility.setDidThisTurn(this, game);
|
||||
}
|
||||
}
|
||||
//20091005 - 603.4
|
||||
if (!super.resolve(game)) {
|
||||
|
|
@ -387,6 +381,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
|
||||
private static boolean startsWithVerb(String ruleLow) {
|
||||
return ruleLow.startsWith("attach")
|
||||
|| ruleLow.startsWith("cast")
|
||||
|| ruleLow.startsWith("change")
|
||||
|| ruleLow.startsWith("counter")
|
||||
|| ruleLow.startsWith("create")
|
||||
|
|
@ -501,20 +496,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
return optional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TriggeredAbility setOptional() {
|
||||
this.optional = true;
|
||||
|
||||
if (getEffects().stream().anyMatch(
|
||||
effect -> effect instanceof DoIfCostPaid && ((DoIfCostPaid) effect).isOptional())) {
|
||||
throw new IllegalArgumentException(
|
||||
"DoIfCostPaid effect must have only one optional settings, but it have two (trigger + DoIfCostPaid): "
|
||||
+ this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TriggeredAbilityImpl setAbilityWord(AbilityWord abilityWord) {
|
||||
super.setAbilityWord(abilityWord);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,11 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
|
||||
public AttacksWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, boolean setTargetPointer) {
|
||||
super(zone, effect);
|
||||
this(zone, effect, minAttackers, filter, setTargetPointer, false);
|
||||
}
|
||||
|
||||
public AttacksWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, boolean setTargetPointer, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
this.minAttackers = minAttackers;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.abilities.effects.common;
|
|||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -110,6 +111,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
didPay = true;
|
||||
game.informPlayers(player.getLogName() + " paid for " + mageObject.getLogName() + " - " + message);
|
||||
applyEffects(game, source, executingEffects);
|
||||
TriggeredAbility.setDidThisTurn(source, game);
|
||||
player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek
|
||||
} else {
|
||||
// Paying cost was cancels so try to undo payment so far
|
||||
|
|
|
|||
|
|
@ -1710,6 +1710,8 @@ public final class CardUtil {
|
|||
player.setCastSourceIdWithAlternateMana(card.getMainCard().getId(), manaCost, additionalCostsNormalCard, MageIdentifier.Default);
|
||||
}
|
||||
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getMainCard().getId(), Boolean.TRUE);
|
||||
|
||||
// cast it
|
||||
boolean result = player.cast(player.chooseAbilityForCast(card.getMainCard(), game, noMana),
|
||||
game, noMana, new ApprovingObject(source, game));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue