* Kicker - added support of X and mana cost interactions like Rosheen Meanderer + Verdeloth the Ancient combo (#3538);

* Rosheen Meanderer - fixed that mana can be payed for mana cost with X instead any cost with X (#3538);
This commit is contained in:
Oleg Agafonov 2019-06-18 11:28:41 +04:00
parent 49fc094546
commit cc54a92daa
7 changed files with 216 additions and 147 deletions

View file

@ -0,0 +1,73 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.costs.OptionalAdditionalCost;
import mage.abilities.costs.OptionalAdditionalCostImpl;
import mage.abilities.costs.VariableCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.keyword.KickerAbility;
import mage.game.Game;
import mage.game.stack.Spell;
import java.util.List;
/**
* @author JayDi85
*/
public enum GetKickerXValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability source, Effect effect) {
// calcs only kicker with X values
// kicker adds additional costs to spell ability
// only one X value per card possible
// kicker can be calls multiple times (use getKickedCounter)
int finalValue = 0;
Spell spell = game.getSpellOrLKIStack(source.getSourceId());
if (spell != null && spell.getSpellAbility() != null) {
int xValue = spell.getSpellAbility().getManaCostsToPay().getX();
for (Ability ability : spell.getAbilities()) {
if (ability instanceof KickerAbility) {
// search that kicker used X value
KickerAbility kickerAbility = (KickerAbility) ability;
boolean haveVarCost = false;
for (OptionalAdditionalCost cost : kickerAbility.getKickerCosts()) {
List<VariableCost> varCosts = ((OptionalAdditionalCostImpl) cost).getVariableCosts();
if (!varCosts.isEmpty()) {
haveVarCost = true;
break;
}
}
if (haveVarCost) {
int kickedCount = ((KickerAbility) ability).getKickedCounter(game, source);
if (kickedCount > 0) {
finalValue += kickedCount * xValue;
}
}
}
}
}
return finalValue;
}
@Override
public GetKickerXValue copy() {
return GetKickerXValue.instance;
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "";
}
}

View file

@ -1,15 +1,10 @@
package mage.abilities.keyword;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.costs.*;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.mana.VariableManaCost;
import mage.constants.AbilityType;
import mage.constants.Outcome;
import mage.constants.Zone;
@ -17,6 +12,12 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 20121001 702.31. Kicker 702.31a Kicker is a static ability that functions
* while the spell with kicker is on the stack. "Kicker [cost]" means "You may
@ -57,7 +58,6 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
protected String keywordText;
protected String reminderText;
protected List<OptionalAdditionalCost> kickerCosts = new LinkedList<>();
private int xManaValue = 0;
public KickerAbility(String manaString) {
this(KICKER_KEYWORD, KICKER_REMINDER_MANA);
@ -79,10 +79,11 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
public KickerAbility(final KickerAbility ability) {
super(ability);
this.kickerCosts.addAll(ability.kickerCosts);
for (OptionalAdditionalCost cost : ability.kickerCosts) {
this.kickerCosts.add((OptionalAdditionalCost) cost.copy());
}
this.keywordText = ability.keywordText;
this.reminderText = ability.reminderText;
this.xManaValue = ability.xManaValue;
this.activations.putAll(ability.activations);
}
@ -108,7 +109,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
cost.reset();
}
String key = getActivationKey(source, "", game);
for (Iterator<String> iterator = activations.keySet().iterator(); iterator.hasNext();) {
for (Iterator<String> iterator = activations.keySet().iterator(); iterator.hasNext(); ) {
String activationKey = iterator.next();
if (activationKey.startsWith(key) && activations.get(activationKey) > 0) {
activations.put(key, 0);
@ -116,10 +117,6 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
}
}
public int getXManaValue() {
return xManaValue;
}
public int getKickedCounter(Game game, Ability source) {
String key = getActivationKey(source, "", game);
return activations.getOrDefault(key, 0);
@ -167,7 +164,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
if (zcc > 0 && (source.getAbilityType() == AbilityType.TRIGGERED)) {
--zcc;
}
return String.valueOf(zcc) + ((kickerCosts.size() > 1) ? costText : "");
return zcc + ((kickerCosts.size() > 1) ? costText : "");
}
@Override
@ -182,16 +179,16 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
String times = "";
if (kickerCost.isRepeatable()) {
int activatedCount = getKickedCounter(game, ability);
times = Integer.toString(activatedCount + 1) + (activatedCount == 0 ? " time " : " times ");
times = (activatedCount + 1) + (activatedCount == 0 ? " time " : " times ");
}
if (kickerCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) {
this.activateKicker(kickerCost, ability, game);
if (kickerCost instanceof Costs) {
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) {
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) {
Object kickerCostObject = itKickerCost.next();
if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) {
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) {
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext(); ) {
addKickerCostsToAbility(itDetails.next(), ability, game);
}
} else {
@ -199,7 +196,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
}
}
} else {
addKickerCostsToAbility((Cost) kickerCost, ability, game);
addKickerCostsToAbility(kickerCost, ability, game);
}
again = kickerCost.isRepeatable();
} else {
@ -212,26 +209,9 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
}
private void addKickerCostsToAbility(Cost cost, Ability ability, Game game) {
// can contains multiple costs from multikicker ability
if (cost instanceof ManaCostsImpl) {
@SuppressWarnings("unchecked")
List<VariableManaCost> varCosts = ((ManaCostsImpl) cost).getVariableCosts();
if (!varCosts.isEmpty()) {
// use only first variable cost
xManaValue = game.getPlayer(this.controllerId).announceXMana(varCosts.get(0).getMinX(), Integer.MAX_VALUE, "Announce kicker value for " + varCosts.get(0).getText(), game, this);
// kicker variable X costs handled internally as multikicker with {1} cost (no multikicker on card)
if (!game.isSimulation()) {
game.informPlayers(game.getPlayer(this.controllerId).getLogName() + " announced a value of " + xManaValue + " for " + " kicker X ");
}
ability.getManaCostsToPay().add(new GenericManaCost(xManaValue));
ManaCostsImpl<ManaCost> kickerManaCosts = (ManaCostsImpl) cost;
for (ManaCost manaCost : kickerManaCosts) {
if (!(manaCost instanceof VariableManaCost)) {
ability.getManaCostsToPay().add(manaCost.copy());
}
}
} else {
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
}
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
} else {
ability.getCosts().add(cost.copy());
}