foul-magics/Mage/src/main/java/mage/abilities/keyword/BackupAbility.java
ssk97 d806a6a3be
Counter text improvements (plus a Magus Lucea Kane gameplay fix) (#11503)
* 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
2023-12-03 21:42:16 -05:00

173 lines
6.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 thats 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 wont 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;
}
}