package mage.abilities.keyword; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.hint.ValueHint; import mage.abilities.mana.ManaOptions; import mage.constants.AbilityType; import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.ManaPool; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledPermanent; import java.util.UUID; /** * 702.125. Improvise *

* 702.125a Improvise is a static ability that functions while the spell with improvise is on the stack. “Improvise” * means “For each generic mana in this spell’s total cost, you may tap an untapped artifact you control rather * than pay that mana.” *

* 702.125b The improvise ability isn’t an additional or alternative cost and applies only after the total cost of * the spell with improvise is determined. *

* 702.125c Multiple instances of improvise on the same spell are redundant. * * @author LevelX2, JayDi85 */ public class ImproviseAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { private static final FilterControlledArtifactPermanent filterUntapped = new FilterControlledArtifactPermanent("untapped artifact you control"); static { filterUntapped.add(TappedPredicate.UNTAPPED); } private static final DynamicValue untappedCount = new PermanentsOnBattlefieldCount(filterUntapped); public ImproviseAbility() { super(Zone.ALL, null); this.setRuleAtTheTop(true); this.addHint(new ValueHint("Untapped artifacts you control", untappedCount)); } protected ImproviseAbility(final ImproviseAbility ability) { super(ability); } @Override public ImproviseAbility copy() { return new ImproviseAbility(this); } @Override public String getRule() { return "improvise (Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}.)"; } @Override public ActivationManaAbilityStep useOnActivationManaAbilityStep() { return ActivationManaAbilityStep.AFTER; } @Override public void addSpecialAction(Ability source, Game game, ManaCost unpaid) { Player controller = game.getPlayer(source.getControllerId()); int canPayCount = untappedCount.calculate(game, source, null); if (controller != null && canPayCount > 0) { if (source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() > 0) { SpecialAction specialAction = new ImproviseSpecialAction(unpaid, this); specialAction.setControllerId(source.getControllerId()); specialAction.setSourceId(source.getSourceId()); // create filter for possible artifacts to tap Target target = new TargetControlledPermanent(1, unpaid.getMana().getGeneric(), filterUntapped, true); target.setTargetName("artifact to tap as Improvise's pay"); specialAction.addTarget(target); if (specialAction.canActivate(source.getControllerId(), game).canActivate()) { game.getState().getSpecialActions().add(specialAction); } } } } @Override public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { ManaOptions options = new ManaOptions(); Player controller = game.getPlayer(source.getControllerId()); int canPayCount = untappedCount.calculate(game, source, null); if (controller != null && canPayCount > 0) { options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), canPayCount))); } return options; } } class ImproviseSpecialAction extends SpecialAction { public ImproviseSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) { super(Zone.ALL, manaAbility); this.abilityType = AbilityType.SPECIAL_MANA_PAYMENT; setRuleVisible(false); this.addEffect(new ImproviseEffect(unpaid)); } protected ImproviseSpecialAction(final ImproviseSpecialAction ability) { super(ability); } @Override public ImproviseSpecialAction copy() { return new ImproviseSpecialAction(this); } } class ImproviseEffect extends OneShotEffect { private final ManaCost unpaid; public ImproviseEffect(ManaCost unpaid) { super(Outcome.Benefit); this.unpaid = unpaid; this.staticText = "Improvise (Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}.)"; } protected ImproviseEffect(final ImproviseEffect effect) { super(effect); this.unpaid = effect.unpaid; } @Override public ImproviseEffect copy() { return new ImproviseEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Spell spell = game.getStack().getSpell(source.getSourceId()); if (controller == null || spell == null) { return false; } for (UUID artifactId : this.getTargetPointer().getTargets(game, source)) { Permanent perm = game.getPermanent(artifactId); if (perm == null) { continue; } if (!perm.isTapped() && perm.tap(source, game)) { ManaPool manaPool = controller.getManaPool(); manaPool.addMana(Mana.ColorlessMana(1), game, source); manaPool.unlockManaType(ManaType.COLORLESS); if (!game.isSimulation()) { game.informPlayers("Improvise: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay {1}"); } // can't use mana abilities after that (improvise cost must be payed after mana abilities only) spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER); } } return true; } }