package mage.abilities.keyword; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.*; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; import java.util.Iterator; import java.util.UUID; /** * @author LevelX2 */ public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Replicate"; private static final String reminderTextMana = "When you cast this spell, " + "copy it for each time you paid its replicate cost." + " You may choose new targets for the copies."; protected OptionalAdditionalCost additionalCost; public ReplicateAbility(String manaString) { this(new ManaCostsImpl<>(manaString)); } public ReplicateAbility(Cost cost) { super(Zone.STACK, null); this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, cost); this.additionalCost.setRepeatable(true); setRuleAtTheTop(true); addSubAbility(new ReplicateTriggeredAbility(this.getId())); } protected ReplicateAbility(final ReplicateAbility ability) { super(ability); additionalCost = ability.additionalCost; } @Override public ReplicateAbility copy() { return new ReplicateAbility(this); } @Override public void addCost(Cost cost) { if (additionalCost != null) { ((Costs) additionalCost).add(cost); } } @Override public boolean isActivated() { return additionalCost != null && additionalCost.isActivated(); } public int getActivateCount() { return additionalCost == null ? 0 : additionalCost.getActivateCount(); } public void resetReplicate() { if (additionalCost != null) { additionalCost.reset(); } } @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { if (!(ability instanceof SpellAbility)) { return; } Player player = game.getPlayer(ability.getControllerId()); if (player == null) { return; } this.resetReplicate(); boolean again = true; while (player.canRespond() && again) { String times = ""; if (additionalCost.isRepeatable()) { int numActivations = additionalCost.getActivateCount(); times = (numActivations + 1) + (numActivations == 0 ? " time " : " times "); } String payPrompt = "Pay " + times + additionalCost.getText(false) + " ?"; // TODO: add AI support to find max number of possible activations (from available mana) // canPay checks only single mana available, not total mana usage boolean canPay = additionalCost.canPay(ability, this, ability.getControllerId(), game); if (!canPay || !player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, payPrompt, ability, game)) { again = false; } else { additionalCost.activate(); for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) { Cost cost = (Cost) it.next(); if (cost instanceof ManaCostsImpl) { ability.addManaCostsToPay((ManaCostsImpl) cost.copy()); } else { ability.addCost(cost.copy()); } } } } } @Override public String getRule() { return additionalCost == null ? "" : additionalCost.getText(false) + ' ' + additionalCost.getReminderText(); } @Override public String getCastMessageSuffix() { return additionalCost == null ? "" : additionalCost.getCastSuffixMessage(0); } public String getReminderText() { return additionalCost == null ? "" : additionalCost.getReminderText(); } } class ReplicateTriggeredAbility extends TriggeredAbilityImpl { private UUID replicateId; // need to correspond only to own replicate ability, not any other instances of replicate ability public ReplicateTriggeredAbility(UUID replicateId) { super(Zone.STACK, new ReplicateCopyEffect()); this.replicateId = replicateId; this.setRuleVisible(false); } private ReplicateTriggeredAbility(final ReplicateTriggeredAbility ability) { super(ability); this.replicateId = ability.replicateId; } @Override public ReplicateTriggeredAbility copy() { return new ReplicateTriggeredAbility(this); } @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.SPELL_CAST; } @Override public boolean checkTrigger(GameEvent event, Game game) { if (!event.getSourceId().equals(this.sourceId)) { return false; } StackObject spell = game.getStack().getStackObject(this.sourceId); if (!(spell instanceof Spell)) { return false; } Card card = ((Spell) spell).getCard(); if (card == null) { return false; } for (Ability ability : card.getAbilities(game)) { if (!(ability instanceof ReplicateAbility) || !ability.isActivated() || ability.getId() != replicateId) { continue; } for (Effect effect : this.getEffects()) { effect.setValue("ReplicateSpell", spell); effect.setValue("ReplicateCount", ((ReplicateAbility) ability).getActivateCount()); } return true; } return false; } @Override public String getRule() { return "Replicate (When you cast this spell, copy it for each time you paid " + "its replicate cost. You may choose new targets for the copies.)"; } } class ReplicateCopyEffect extends OneShotEffect { public ReplicateCopyEffect() { super(Outcome.Copy); } protected ReplicateCopyEffect(final ReplicateCopyEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Spell spell = (Spell) this.getValue("ReplicateSpell"); int replicateCount = (Integer) this.getValue("ReplicateCount"); if (controller == null || spell == null || replicateCount == 0) { return false; } // reset replicate now so the copies don't report x times Replicate Card card = game.getCard(spell.getSourceId()); if (card == null) { return false; } for (Ability ability : card.getAbilities(game)) { if ((ability instanceof ReplicateAbility) && ability.isActivated()) { ((ReplicateAbility) ability).resetReplicate(); } } // create the copies spell.createCopyOnStack(game, source, source.getControllerId(), true, replicateCount); return true; } @Override public ReplicateCopyEffect copy() { return new ReplicateCopyEffect(this); } }