forked from External/mage
* Improve text generation for some counter abilities * Magus Lucea Kane must apply to permanent spells (bugfix, related to #11440) * Further improve text generation of counter text * Text improvements to a couple cards * Graft text improvements * Further remove unused variable * Be consistent on Backup ability setup, revert Thalia's Lieutenant (filter reuse means it doesn't work) * Thalia's Lieutenant original text was slightly wrong, fixed
173 lines
6.4 KiB
Java
173 lines
6.4 KiB
Java
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
|
||
* <p>
|
||
* 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.)
|
||
* <p>
|
||
* 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.
|
||
* <p>
|
||
* 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.
|
||
* <p>
|
||
* 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<Ability> 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 + " <i>(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.)</i>";
|
||
}
|
||
|
||
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<Ability> abilitiesToAdd = new ArrayList<>();
|
||
|
||
public BackupEffect(int amount, List<Ability> 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;
|
||
}
|
||
}
|