package mage.abilities.keyword; import mage.abilities.Ability; import mage.abilities.StaticAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.Card; import mage.constants.Duration; import mage.constants.Outcome; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.ArrayList; import java.util.List; /** * 702.165. Backup *

* 702.165a Backup is a triggered ability. “Backup N” means “When this creature enters the battlefield, * put N +1/+1 counters on target creature. If that’s another creature, it also gains the non-backup * abilities of this creature printed below this one until end of turn.” Cards with backup have one or more * abilities printed after the backup ability. (Some cards with backup also have abilities printed before * the backup ability.) *

* 702.165b If a permanent enters the battlefield as a copy of a permanent with a backup ability or a token * is created that is a copy of that permanent, the order of abilities printed on it is maintained. *

* 702.165c Only abilities printed on the object with backup are granted by its backup ability. Any abilities * gained by a permanent, whether due to a copy effect, an effect that grants an ability to a permanent, or an * effect that creates a token with certain abilities, are not granted by a backup ability. *

* 702.165d The abilities that a backup ability grants are determined as the ability is put on the stack. * They won’t change if the permanent with backup loses any abilities after the ability is put on the stack * but before it resolves. * * @author TheElk801 */ public class BackupAbility extends EntersBattlefieldTriggeredAbility { private final Card card; private final int amount; private final List abilitiesToAdd = new ArrayList<>(); public BackupAbility(Card card, int amount) { super(null, false); this.addEffect(new BackupEffect(amount, abilitiesToAdd)); this.card = card; this.amount = amount; this.addTarget(new TargetCreaturePermanent()); } protected BackupAbility(final BackupAbility ability) { super(ability); this.amount = ability.amount; this.card = ability.card; this.abilitiesToAdd.addAll(ability.abilitiesToAdd); } @Override public BackupAbility copy() { return new BackupAbility(this); } @Override public String getRule() { return "Backup " + amount + " (When this creature enters the battlefield," + " put " + CardUtil.getOneOneCountersText(amount) + " on target creature. If that's another creature, it gains the following" + " abilit" + (abilitiesToAdd.size() > 1 ? "ies" : "y") + " until end of turn.)"; } public void addAbility(Ability ability) { addAbility(ability, null); } public void addAbility(Ability ability, Watcher watcher) { addAbility(ability, watcher, false); } public void addAbility(Ability ability, boolean dontAddToCard) { addAbility(ability, null, dontAddToCard); } /** * @param ability * @param watcher * @param dontAddToCard use it on multiple instances of backups (example: Conclave Sledge-Captain) */ public void addAbility(Ability ability, Watcher watcher, boolean dontAddToCard) { // runtime/verify check: wrong duration in backup's static effects if (ability instanceof StaticAbility) { Effect wrongEffect = ability.getEffects().stream() .filter(effect -> effect instanceof ContinuousEffect) .map(effect -> (ContinuousEffect) effect) .filter(effect -> effect.getDuration().equals(Duration.EndOfTurn)) .findFirst() .orElse(null); if (wrongEffect != null) { // how-to fix: add effect with Duration.EndOfGame (backup's abilities for permanent controls by GainAbility) throw new IllegalArgumentException("Wrong code usage. Backup ability and source card must contains static effects. Wrong effect: " + wrongEffect.getClass().getName()); } } if (watcher != null) { ability.addWatcher(watcher); } // parent card must have only 1 instance of ability if (!dontAddToCard) { card.addAbility(ability); } // target permanent can have multiple instances of ability abilitiesToAdd.add(ability); CardUtil.castStream(this.getEffects().stream(), BackupEffect.class) .forEach(backupEffect -> backupEffect.addAbility(ability)); } public boolean hasAbilities() { return !abilitiesToAdd.isEmpty(); } } class BackupEffect extends OneShotEffect { private final int amount; private final List abilitiesToAdd = new ArrayList<>(); public BackupEffect(int amount, List abilitiesToAdd) { super(Outcome.BoostCreature); this.amount = amount; this.abilitiesToAdd.addAll(abilitiesToAdd); } protected BackupEffect(final BackupEffect effect) { super(effect); this.amount = effect.amount; this.abilitiesToAdd.addAll(effect.abilitiesToAdd); } void addAbility(Ability ability) { this.abilitiesToAdd.add(ability); } @Override public BackupEffect copy() { return new BackupEffect(this); } @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent == null) { return false; } permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game); if (permanent.getId().equals(source.getSourceId())) { return true; } for (Ability ability : abilitiesToAdd) { game.addEffect(new GainAbilityTargetEffect(ability).setTargetPointer(new FixedTarget(permanent, game)), source); } return true; } }