mirror of
https://github.com/correl/mage.git
synced 2025-01-13 03:00:10 +00:00
Reworked Kicker.
This commit is contained in:
parent
4eccfa716c
commit
67ed36e315
18 changed files with 681 additions and 275 deletions
|
@ -32,7 +32,6 @@ import java.io.Serializable;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.Constants.Zone;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.game.Game;
|
||||
|
@ -142,16 +141,6 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
*/
|
||||
public Abilities<ProtectionAbility> getProtectionAbilities();
|
||||
|
||||
/**
|
||||
* Retrieves all {@link KickerAbility kicker abilities}.
|
||||
*
|
||||
* @return All found {@link KickerAbility kicker abilities}.
|
||||
*
|
||||
* @see mage.players.PlayerImpl#cast(mage.abilities.SpellAbility, mage.game.Game, boolean)
|
||||
* @see mage.game.stack.Spell#resolveKicker(mage.game.Game)
|
||||
*/
|
||||
public Abilities<KickerAbility> getKickerAbilities();
|
||||
|
||||
/**
|
||||
* TODO Method is unused, keep it around?
|
||||
*
|
||||
|
|
|
@ -28,20 +28,18 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import mage.Constants.Zone;
|
||||
import mage.abilities.common.ZoneChangeTriggeredAbility;
|
||||
import mage.abilities.costs.AlternativeCost;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.Constants.Zone;
|
||||
import mage.abilities.common.ZoneChangeTriggeredAbility;
|
||||
import mage.abilities.costs.AlternativeCost;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.game.Game;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -81,10 +79,6 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
}
|
||||
}
|
||||
if (ability instanceof SpellAbility) {
|
||||
String kickerRule = getKickerRule(ability);
|
||||
if (!kickerRule.isEmpty()) {
|
||||
rules.add(kickerRule);
|
||||
}
|
||||
if (ability.getAlternativeCosts().size() > 0) {
|
||||
StringBuilder sbRule = new StringBuilder();
|
||||
for (AlternativeCost cost: ability.getAlternativeCosts()) {
|
||||
|
@ -122,25 +116,6 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
return rules;
|
||||
}
|
||||
|
||||
private String getKickerRule(Ability ability) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int numberKicker = 0;
|
||||
for (Object cost : ability.getOptionalCosts()) {
|
||||
if (cost instanceof KickerManaCost) {
|
||||
if (numberKicker == 0) {
|
||||
sb.append(((KickerManaCost)cost).getText(true));
|
||||
} else {
|
||||
sb.append(" and/or ").append(((KickerManaCost)cost).getText(true));
|
||||
}
|
||||
++numberKicker;
|
||||
}
|
||||
}
|
||||
if (numberKicker > 0) {
|
||||
return "Kicker " + sb.toString() + " <i>(You may pay an additional " + sb.toString() + " as you cast this spell.)</i>";
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<ActivatedAbility> getActivatedAbilities(Zone zone) {
|
||||
Abilities<ActivatedAbility> zonedAbilities = new AbilitiesImpl<ActivatedAbility>();
|
||||
|
@ -168,8 +143,9 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
Abilities<ManaAbility> abilities = new AbilitiesImpl<ManaAbility>();
|
||||
for (T ability: this) {
|
||||
if (ability instanceof ManaAbility && ability.getZone().match(zone)) {
|
||||
if ((((ManaAbility)ability).canActivate(ability.getControllerId(), game)))
|
||||
if ((((ManaAbility)ability).canActivate(ability.getControllerId(), game))) {
|
||||
abilities.add((ManaAbility)ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
return abilities;
|
||||
|
@ -225,17 +201,6 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
return abilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<KickerAbility> getKickerAbilities() {
|
||||
Abilities<KickerAbility> abilities = new AbilitiesImpl<KickerAbility>();
|
||||
for (T ability: this) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
abilities.add((KickerAbility)ability);
|
||||
}
|
||||
}
|
||||
return abilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControllerId(UUID controllerId) {
|
||||
for (Ability ability: this) {
|
||||
|
@ -276,11 +241,13 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
|
||||
@Override
|
||||
public boolean containsAll(Abilities<T> abilities) {
|
||||
if (this.size() < abilities.size())
|
||||
if (this.size() < abilities.size()) {
|
||||
return false;
|
||||
}
|
||||
for (T ability: abilities) {
|
||||
if (!contains(ability))
|
||||
if (!contains(ability)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -288,8 +255,9 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
@Override
|
||||
public boolean containsKey(UUID abilityId) {
|
||||
for (T ability: this) {
|
||||
if (ability.getId().equals(abilityId))
|
||||
if (ability.getId().equals(abilityId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -297,8 +265,9 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
@Override
|
||||
public T get(UUID abilityId) {
|
||||
for (T ability: this) {
|
||||
if (ability.getId().equals(abilityId))
|
||||
if (ability.getId().equals(abilityId)) {
|
||||
return ability;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -28,17 +28,28 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.Constants.AbilityType;
|
||||
import mage.Constants.EffectType;
|
||||
import mage.Constants.Outcome;
|
||||
import mage.Constants.Zone;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.abilities.costs.AdjustingSourceCosts;
|
||||
import mage.abilities.costs.AlternativeCost;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.PostResolveEffect;
|
||||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.choices.Choice;
|
||||
|
@ -49,9 +60,6 @@ import mage.target.Target;
|
|||
import mage.target.Targets;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -144,8 +152,9 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
if (checkIfClause(game)) {
|
||||
for (Effect effect: getEffects()) {
|
||||
if (effect instanceof OneShotEffect) {
|
||||
if (!(effect instanceof PostResolveEffect))
|
||||
if (!(effect instanceof PostResolveEffect)) {
|
||||
result &= effect.apply(game, this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
game.addEffect((ContinuousEffect) effect, this);
|
||||
|
@ -158,8 +167,9 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
@Override
|
||||
public boolean activate(Game game, boolean noMana) {
|
||||
// 20110204 - 700.2
|
||||
if (!modes.choose(game, this))
|
||||
if (!modes.choose(game, this)) {
|
||||
return false;
|
||||
}
|
||||
//20100716 - 601.2b
|
||||
Card card = game.getCard(sourceId);
|
||||
if (card != null) {
|
||||
|
@ -170,22 +180,29 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 20121001 - 601.2b
|
||||
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
||||
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
||||
// or her intentions to pay any or all of those costs (see rule 601.2e).
|
||||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof OptionalAdditionalSourceCosts) {
|
||||
((OptionalAdditionalSourceCosts)ability).addOptionalAdditionalCosts(this, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//20121001 - 601.2c
|
||||
if (card != null) {
|
||||
card.adjustTargets(this, game);
|
||||
}
|
||||
//20100716 - 601.2b
|
||||
if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) {
|
||||
logger.debug("activate failed - target");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Cost cost : optionalCosts) {
|
||||
if (cost instanceof KickerManaCost) {
|
||||
cost.clearPaid();
|
||||
if (game.getPlayer(this.controllerId).chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "?", game)) {
|
||||
manaCostsToPay.add((ManaCost) cost);
|
||||
}
|
||||
} else if (cost instanceof ManaCost) {
|
||||
if (cost instanceof ManaCost) {
|
||||
cost.clearPaid();
|
||||
if (game.getPlayer(this.controllerId).chooseUse(Outcome.Benefit, "Pay optional cost " + cost.getText() + "?", game)) {
|
||||
manaCostsToPay.add((ManaCost) cost);
|
||||
|
@ -454,8 +471,9 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
@Override
|
||||
public boolean canChooseTarget(Game game) {
|
||||
for (Mode mode: modes.values()) {
|
||||
if (mode.getTargets().canChoose(sourceId, controllerId, game))
|
||||
if (mode.getTargets().canChoose(sourceId, controllerId, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -477,15 +495,15 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
}
|
||||
|
||||
MageObject object;
|
||||
UUID sourceId;
|
||||
UUID parameterSourceId;
|
||||
// for singleton abilities like Flying we can't rely on abilities' source
|
||||
// so will use the one that came as a parameter if it is not null
|
||||
if (this instanceof MageSingleton && source != null) {
|
||||
object = source;
|
||||
sourceId = source.getId();
|
||||
parameterSourceId = source.getId();
|
||||
} else {
|
||||
object = game.getObject(getSourceId());
|
||||
sourceId = getSourceId();
|
||||
parameterSourceId = getSourceId();
|
||||
}
|
||||
|
||||
if (object != null && !object.getAbilities().contains(this)) {
|
||||
|
@ -501,7 +519,7 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
|||
}
|
||||
|
||||
// check against current state
|
||||
Zone test = game.getState().getZone(sourceId);
|
||||
Zone test = game.getState().getZone(parameterSourceId);
|
||||
return test != null && zone.match(test);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,26 +28,24 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.Constants.AbilityType;
|
||||
import mage.Constants.TimingRule;
|
||||
import mage.Constants.Zone;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.costs.mana.PhyrexianManaCost;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackAbility;
|
||||
import mage.target.Target;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.abilities.keyword.MultikickerAbility;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -82,8 +80,9 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
if (effect != null) {
|
||||
this.addEffect(effect);
|
||||
}
|
||||
if (cost != null)
|
||||
if (cost != null) {
|
||||
this.addManaCost(cost);
|
||||
}
|
||||
}
|
||||
|
||||
public ActivatedAbilityImpl(Zone zone, Effects effects, ManaCosts cost) {
|
||||
|
@ -93,8 +92,9 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
this.addEffect(effect);
|
||||
}
|
||||
}
|
||||
if (cost != null)
|
||||
if (cost != null) {
|
||||
this.addManaCost(cost);
|
||||
}
|
||||
}
|
||||
|
||||
public ActivatedAbilityImpl(Zone zone, Effect effect, Cost cost) {
|
||||
|
@ -130,8 +130,9 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
this.addEffect(effect);
|
||||
}
|
||||
}
|
||||
if (cost != null)
|
||||
if (cost != null) {
|
||||
this.addCost(cost);
|
||||
}
|
||||
}
|
||||
|
||||
public ActivatedAbilityImpl(Zone zone, Effects effects, Costs<Cost> costs) {
|
||||
|
@ -151,8 +152,9 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
@Override
|
||||
public boolean canActivate(UUID playerId, Game game) {
|
||||
//20091005 - 602.2
|
||||
if (!controlsAbility(playerId, game))
|
||||
if (!controlsAbility(playerId, game)) {
|
||||
return false;
|
||||
}
|
||||
//20091005 - 602.5d/602.5e
|
||||
if (timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)) {
|
||||
if (costs.canPay(sourceId, controllerId, game) && canChooseTarget(game)) {
|
||||
|
@ -163,12 +165,14 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
}
|
||||
|
||||
protected boolean controlsAbility(UUID playerId, Game game) {
|
||||
if (this.controllerId != null && this.controllerId.equals(playerId))
|
||||
if (this.controllerId != null && this.controllerId.equals(playerId)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Card card = (Card)game.getObject(this.sourceId);
|
||||
if (card != null && game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD)
|
||||
if (card != null && game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD) {
|
||||
return card.getOwnerId().equals(playerId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -200,7 +204,7 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
if (spell.getFromZone() == Zone.GRAVEYARD) {
|
||||
sb.append(" from graveyard");
|
||||
}
|
||||
sb.append(getKickerText(game, spell));
|
||||
sb.append(getOptionalTextSuffix(game, spell));
|
||||
} else {
|
||||
sb.append(object.getName());
|
||||
}
|
||||
|
@ -217,37 +221,13 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
String getKickerText(Game game, Spell spell) {
|
||||
String getOptionalTextSuffix(Game game, Spell spell) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int numberPaid = 0;
|
||||
for (Object cost : spell.getSpellAbility().getOptionalCosts()) {
|
||||
if (cost instanceof KickerManaCost) {
|
||||
if (((KickerManaCost) cost).isPaid()) {
|
||||
if (numberPaid == 0) {
|
||||
sb.append(" with ").append(((KickerManaCost)cost).getText(true));
|
||||
} else {
|
||||
sb.append(" and ").append(((KickerManaCost)cost).getText(true));
|
||||
}
|
||||
++numberPaid;
|
||||
}
|
||||
for (Ability ability : (Abilities<Ability>) spell.getAbilities()) {
|
||||
if (ability instanceof OptionalAdditionalSourceCosts) {
|
||||
sb.append(((OptionalAdditionalSourceCosts) ability).getCastMessageSuffix());
|
||||
}
|
||||
}
|
||||
if (numberPaid > 0) {
|
||||
sb.append(" kicker");
|
||||
}
|
||||
// Multikicker
|
||||
int multikickerCount = 0;
|
||||
Card card = game.getCard(this.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof MultikickerAbility) {
|
||||
multikickerCount = ((MultikickerAbility)ability).getActivateCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (multikickerCount > 0) {
|
||||
sb.append(" with ").append(multikickerCount).append(multikickerCount > 1? " times":" time").append(" multikicker");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,45 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
|
||||
/**
|
||||
* Describes condition when spell was kicked.
|
||||
*
|
||||
* @author nantuko
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class KickedCondition implements Condition {
|
||||
|
||||
|
@ -26,19 +56,23 @@ public class KickedCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Card p = game.getCard(source.getSourceId());
|
||||
|
||||
boolean kicked = false;
|
||||
if (p != null) {
|
||||
for (Object cost : p.getSpellAbility().getOptionalCosts()) {
|
||||
if (cost instanceof KickerManaCost) {
|
||||
if (((KickerManaCost) cost).isPaid()) {
|
||||
kicked = true;
|
||||
}
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
KickerAbility kickerAbility = null;
|
||||
for (Ability ability: card.getAbilities()) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
kickerAbility = (KickerAbility) ability;
|
||||
}
|
||||
}
|
||||
boolean kicked = false;
|
||||
if (kickerAbility != null) {
|
||||
for (OptionalAdditionalCost cost: kickerAbility.getKickerCosts()) {
|
||||
kicked = cost.isActivated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return kicked;
|
||||
}
|
||||
|
||||
return kicked;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ package mage.abilities.condition.common;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
|
@ -21,18 +23,22 @@ public class KickedCostCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Card p = game.getCard(source.getSourceId());
|
||||
|
||||
boolean kicked = false;
|
||||
if (p != null) {
|
||||
for (Object cost : p.getSpellAbility().getOptionalCosts()) {
|
||||
if (cost.equals(kickerManaCost)) {
|
||||
if (((KickerManaCost) cost).isPaid()) {
|
||||
kicked = true;
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
KickerAbility kickerAbility = null;
|
||||
for (Ability ability: card.getAbilities()) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
kickerAbility = (KickerAbility) ability;
|
||||
}
|
||||
}
|
||||
if (kickerAbility != null) {
|
||||
for (OptionalAdditionalCost cost: kickerAbility.getKickerCosts()) {
|
||||
if (cost.equals(kickerManaCost)) {
|
||||
return cost.isActivated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return kicked;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
97
Mage/src/mage/abilities/costs/OptionalAdditionalCost.java
Normal file
97
Mage/src/mage/abilities/costs/OptionalAdditionalCost.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.abilities.costs;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface OptionalAdditionalCost extends Cost {
|
||||
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns the complete text for the addional coast or if onlyCost is true
|
||||
* only the pure text fore the included native cost
|
||||
*
|
||||
* @param onlyCost
|
||||
* @return
|
||||
*/
|
||||
public String getText(boolean onlyCost);
|
||||
|
||||
/**
|
||||
* Returns a reminder text, if the cost has one
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getReminderText();
|
||||
|
||||
/**
|
||||
* Returns a text suffix for the game log, that can be added to
|
||||
* the cast message.
|
||||
*
|
||||
* @param position - if there are multiple costs, it's the postion the cost is set (starting with 0)
|
||||
* @return
|
||||
*/
|
||||
public String getCastSuffixMessage(int position);
|
||||
|
||||
|
||||
/**
|
||||
* If the player intends to pay the cost, the cost will be activated
|
||||
*
|
||||
* @param activated
|
||||
*/
|
||||
public void activate();
|
||||
|
||||
/**
|
||||
* Reset the activate and count information
|
||||
*
|
||||
*/
|
||||
public void reset();
|
||||
|
||||
/**
|
||||
* Can the cost be multiple times activated
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isRepeatable();
|
||||
|
||||
/**
|
||||
* Returns if the cost was activated
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isActivated();
|
||||
|
||||
/**
|
||||
* Returns the number of times the cost was activated
|
||||
* @return
|
||||
*/
|
||||
public int getActivateCount();
|
||||
|
||||
}
|
165
Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java
Normal file
165
Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.abilities.costs;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public class OptionalAdditionalCostImpl <T extends OptionalAdditionalCostImpl<T>> extends CostsImpl<Cost> implements OptionalAdditionalCost{
|
||||
|
||||
|
||||
protected String name;
|
||||
protected String reminderText;
|
||||
protected boolean activated;
|
||||
protected int activatedCounter;
|
||||
protected boolean repeatable;
|
||||
|
||||
public OptionalAdditionalCostImpl(String name, String reminderText, Cost cost) {
|
||||
this.activated = false;
|
||||
this.name = name;
|
||||
this.reminderText = reminderText;
|
||||
this.activatedCounter = 0;
|
||||
this.add((Cost) cost);
|
||||
}
|
||||
|
||||
public OptionalAdditionalCostImpl(final OptionalAdditionalCostImpl cost) {
|
||||
super(cost);
|
||||
this.name = cost.name;
|
||||
this.reminderText = cost.reminderText;
|
||||
this.activated = cost.activated;
|
||||
this.activatedCounter = cost.activatedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete text for the addional coast or if onlyCost is true
|
||||
* only the pure text fore the included native cost
|
||||
*
|
||||
* @param onlyCost
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getText(boolean onlyCost) {
|
||||
if (onlyCost) {
|
||||
return getText();
|
||||
} else {
|
||||
return name + " " + getText();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reminder text, if the cost has one
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getReminderText() {
|
||||
String replace = "";
|
||||
if (reminderText != null && !reminderText.isEmpty()) {
|
||||
replace = reminderText.replace("{cost}", this.getText(true));
|
||||
}
|
||||
return replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a text suffix for the game log, that can be added to
|
||||
* the cast message.
|
||||
*
|
||||
* @param position - if there are multiple costs, it's the postion the cost is set (starting with 0)
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCastSuffixMessage(int position) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the player intends to pay the cost, the cost will be activated
|
||||
*
|
||||
* @param activated
|
||||
*/
|
||||
@Override
|
||||
public void activate() {
|
||||
activated = true;
|
||||
++activatedCounter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the activate and count information
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
activated = false;
|
||||
activatedCounter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the cost be multiple times activated
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isRepeatable() {
|
||||
return repeatable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns if the cost was activated
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isActivated(){
|
||||
return activated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of times the cost was activated
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int getActivateCount(){
|
||||
return activatedCounter;
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public OptionalAdditionalCostImpl copy() {
|
||||
return new OptionalAdditionalCostImpl(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.costs;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Interface for abilities that add addional costs to the source.
|
||||
*
|
||||
* Example of such additional source costs: {@link mage.abilities.keyword.KickerAbility}
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface OptionalAdditionalSourceCosts {
|
||||
void addOptionalAdditionalCosts(Ability ability, Game game);
|
||||
String getCastMessageSuffix();
|
||||
}
|
|
@ -1,12 +1,44 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
|
||||
/**
|
||||
* This cost must be used only as optional mana cost in cards with Kicker
|
||||
* @author Loki
|
||||
*/
|
||||
public class KickerManaCost extends ManaCostsImpl {
|
||||
* This cost defines the Kicker cost
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class KickerManaCost extends OptionalAdditionalCostImpl {
|
||||
|
||||
public KickerManaCost(String manaString) {
|
||||
super(manaString);
|
||||
super("Kicker","(You may pay an additional {cost} as you cast this spell.)",new ManaCostsImpl(manaString));
|
||||
}
|
||||
|
||||
public KickerManaCost(final KickerManaCost cost) {
|
||||
|
@ -18,17 +50,12 @@ public class KickerManaCost extends ManaCostsImpl {
|
|||
return new KickerManaCost(this);
|
||||
}
|
||||
|
||||
|
||||
public String getText(boolean onlyMana) {
|
||||
if (onlyMana) {
|
||||
return super.getText();
|
||||
@Override
|
||||
public String getCastSuffixMessage(int position) {
|
||||
if (position == 0) {
|
||||
return " with " + getText(false);
|
||||
} else {
|
||||
return this.getText();
|
||||
return " and " + getText(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "Kicker - " + super.getText();
|
||||
}
|
||||
}
|
||||
}
|
58
Mage/src/mage/abilities/costs/mana/MultikickerManaCost.java
Normal file
58
Mage/src/mage/abilities/costs/mana/MultikickerManaCost.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public class MultikickerManaCost extends OptionalAdditionalCostImpl{
|
||||
|
||||
public MultikickerManaCost(String manaString) {
|
||||
super("Multikicker","(You may pay an additional {cost} any number of times as you cast this spell.)",new ManaCostsImpl(manaString));
|
||||
repeatable = true;
|
||||
}
|
||||
|
||||
public MultikickerManaCost(final MultikickerManaCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultikickerManaCost copy() {
|
||||
return new MultikickerManaCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCastSuffixMessage(int position) {
|
||||
return (position > 0 ? " and ":"") + " with " + getActivateCount() + (getActivateCount() > 1? " times":" time") + " multikicker";
|
||||
}
|
||||
}
|
|
@ -28,10 +28,13 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.abilities.costs.mana.MultikickerManaCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.keyword.MultikickerAbility;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -44,14 +47,23 @@ public class MultikickerCount implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public int calculate(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
if (ability instanceof MultikickerAbility) {
|
||||
return ((MultikickerAbility)ability).getActivateCount();
|
||||
}
|
||||
int count = 0;
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
KickerAbility kickerAbility = null;
|
||||
for (Ability ability: card.getAbilities()) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
kickerAbility = (KickerAbility) ability;
|
||||
}
|
||||
}
|
||||
if (kickerAbility != null) {
|
||||
for (OptionalAdditionalCost cost: kickerAbility.getKickerCosts()) {
|
||||
count = cost.getActivateCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility implements
|
|||
|
||||
public AffinityForArtifactsAbility() {
|
||||
super(Constants.Zone.OUTSIDE, new AffinityEffect(filter));
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
public AffinityForArtifactsAbility(final AffinityForArtifactsAbility ability) {
|
||||
|
|
|
@ -28,89 +28,123 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import mage.Constants;
|
||||
import mage.Constants.Zone;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class KickerAbility extends StaticAbility<KickerAbility> {
|
||||
public class KickerAbility extends StaticAbility<KickerAbility> implements OptionalAdditionalSourceCosts {
|
||||
|
||||
protected boolean kicked = false;
|
||||
protected boolean replaces = false;
|
||||
protected List<OptionalAdditionalCost> kickerCosts = new LinkedList<OptionalAdditionalCost>();
|
||||
|
||||
public KickerAbility(Effect effect, boolean replaces) {
|
||||
super(Zone.STACK, effect);
|
||||
this.replaces = replaces;
|
||||
public KickerAbility(OptionalAdditionalCost kickerCost) {
|
||||
super(Zone.STACK, null);
|
||||
kickerCosts.add(kickerCost);
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
public KickerAbility(final KickerAbility ability) {
|
||||
super(ability);
|
||||
this.kicked = ability.kicked;
|
||||
this.replaces = ability.replaces;
|
||||
super(ability);
|
||||
this.kickerCosts = ability.kickerCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KickerAbility copy() {
|
||||
return new KickerAbility(this);
|
||||
return new KickerAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activate(Game game, boolean noMana) {
|
||||
Player player = game.getPlayer(this.getControllerId());
|
||||
|
||||
String message = getKickerText(false) + "?";
|
||||
Card card = game.getCard(sourceId);
|
||||
// replace by card name or just plain "this"
|
||||
String text = card == null ? "this" : card.getName();
|
||||
message = message.replace("{this}", text).replace("{source}", text);
|
||||
if (player.chooseUse(getEffects().get(0).getOutcome(), message, game)) {
|
||||
int bookmark = game.bookmarkState();
|
||||
if (super.activate(game, noMana)) {
|
||||
game.removeBookmark(bookmark);
|
||||
kicked = true;
|
||||
}
|
||||
else {
|
||||
game.restoreState(bookmark);
|
||||
kicked = false;
|
||||
}
|
||||
return kicked;
|
||||
public void resetKicker() {
|
||||
for (OptionalAdditionalCost cost: kickerCosts) {
|
||||
cost.reset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isKicked() {
|
||||
return kicked;
|
||||
public List<OptionalAdditionalCost> getKickerCosts () {
|
||||
return kickerCosts;
|
||||
}
|
||||
|
||||
public boolean isReplaces() {
|
||||
return replaces;
|
||||
public void addKickerManaCost(OptionalAdditionalCost kickerCost) {
|
||||
kickerCosts.add(kickerCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOptionalAdditionalCosts(Ability ability, Game game) {
|
||||
if (ability instanceof SpellAbility) {
|
||||
Player player = game.getPlayer(controllerId);
|
||||
if (player != null) {
|
||||
this.resetKicker();
|
||||
for (OptionalAdditionalCost kickerCost: kickerCosts) {
|
||||
boolean again = true;
|
||||
while (again) {
|
||||
String times = "";
|
||||
if (kickerCost.isRepeatable()) {
|
||||
int activated = kickerCost.getActivateCount();
|
||||
times = Integer.toString(activated + 1) + (activated == 0 ? " time ":" times ");
|
||||
}
|
||||
if (player.chooseUse(Constants.Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", game)) {
|
||||
kickerCost.activate();
|
||||
for (Iterator it = ((Costs) kickerCost).iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
|
||||
again = kickerCost.isRepeatable();
|
||||
} else {
|
||||
again = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return getKickerText(true);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int numberKicker = 0;
|
||||
String remarkText = "";
|
||||
for (OptionalAdditionalCost kickerCost: kickerCosts) {
|
||||
if (numberKicker == 0) {
|
||||
sb.append(kickerCost.getText(false));
|
||||
remarkText = kickerCost.getReminderText();
|
||||
} else {
|
||||
sb.append(" and/or ").append(kickerCost.getText(true));
|
||||
}
|
||||
++numberKicker;
|
||||
}
|
||||
if (numberKicker == 1) {
|
||||
sb.append(" ").append(remarkText);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getKickerText(boolean withRemainder) {
|
||||
@Override
|
||||
public String getCastMessageSuffix() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Kicker - ");
|
||||
if (manaCosts.size() > 0) {
|
||||
sb.append(manaCosts.getText());
|
||||
if (costs.size() > 0)
|
||||
sb.append(",");
|
||||
int position = 0;
|
||||
for (OptionalAdditionalCost cost : kickerCosts) {
|
||||
if (cost.isActivated()) {
|
||||
sb.append(cost.getCastSuffixMessage(position));
|
||||
++position;
|
||||
}
|
||||
}
|
||||
if (costs.size() > 0)
|
||||
sb.append(costs.getText());
|
||||
sb.append(":").append(modes.getText());
|
||||
if (replaces)
|
||||
sb.append(" instead");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.common.EmptyEffect;
|
||||
import mage.abilities.costs.mana.KickerManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
|
@ -42,11 +42,14 @@ public class MultikickerAbility extends KickerAbility {
|
|||
int activateCount;
|
||||
|
||||
public MultikickerAbility(Effect effect, boolean replaces) {
|
||||
super(effect, replaces);
|
||||
super(new KickerManaCost("{1}"));
|
||||
// super(effect, replaces);
|
||||
|
||||
}
|
||||
|
||||
public MultikickerAbility(ManaCosts manaCosts) {
|
||||
super(new EmptyEffect(""), false);
|
||||
super((KickerManaCost) manaCosts);
|
||||
//super(new EmptyEffect(""), false);
|
||||
this.addManaCost(manaCosts);
|
||||
}
|
||||
|
||||
|
@ -70,8 +73,9 @@ public class MultikickerAbility extends KickerAbility {
|
|||
break;
|
||||
activateCount++;
|
||||
}
|
||||
kicked = activateCount > 0;
|
||||
return kicked;
|
||||
// kicked = activateCount > 0;
|
||||
// return kicked;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,24 +87,24 @@ public class MultikickerAbility extends KickerAbility {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKickerText(boolean withRemainder) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Multikicker ");
|
||||
if (manaCosts.size() > 0) {
|
||||
sb.append(manaCosts.getText());
|
||||
if (costs.size() > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
if (costs.size() > 0) {
|
||||
sb.append(costs.getText());
|
||||
}
|
||||
if (withRemainder) {
|
||||
sb.append(" (You may pay an additional ").append(manaCosts.getText()).append(" any number of times as you cast this spell.)");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
// @Override
|
||||
// public String getKickerText(boolean withRemainder) {
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append("Multikicker ");
|
||||
// if (manaCosts.size() > 0) {
|
||||
// sb.append(manaCosts.getText());
|
||||
// if (costs.size() > 0) {
|
||||
// sb.append(",");
|
||||
// }
|
||||
// }
|
||||
// if (costs.size() > 0) {
|
||||
// sb.append(costs.getText());
|
||||
// }
|
||||
// if (withRemainder) {
|
||||
// sb.append(" (You may pay an additional ").append(manaCosts.getText()).append(" any number of times as you cast this spell.)");
|
||||
// }
|
||||
// return sb.toString();
|
||||
// }
|
||||
|
||||
public int getActivateCount() {
|
||||
return activateCount;
|
||||
|
|
|
@ -447,9 +447,6 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
@Deprecated
|
||||
public void addAbility(Ability ability, MageObject attachedTo) {
|
||||
if (ability instanceof StaticAbility) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
return;
|
||||
}
|
||||
for (Mode mode: ability.getModes().values()) {
|
||||
for (Effect effect: mode.getEffects()) {
|
||||
if (effect instanceof ContinuousEffect) {
|
||||
|
@ -465,9 +462,6 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
|
||||
public void addAbility(Ability ability, UUID sourceId, MageObject attachedTo) {
|
||||
if (ability instanceof StaticAbility) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
return;
|
||||
}
|
||||
for (Mode mode: ability.getModes().values()) {
|
||||
for (Effect effect: mode.getEffects()) {
|
||||
if (effect instanceof ContinuousEffect) {
|
||||
|
|
|
@ -43,7 +43,6 @@ import mage.abilities.costs.mana.ManaCost;
|
|||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.PostResolveEffect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
|
@ -88,12 +87,7 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
if (card.getCardType().contains(CardType.INSTANT) || card.getCardType().contains(CardType.SORCERY)) {
|
||||
if (ability.getTargets().stillLegal(ability, game)) {
|
||||
updateOptionalCosts();
|
||||
boolean replaced = resolveKicker(game);
|
||||
if (!replaced)
|
||||
result = ability.resolve(game);
|
||||
else
|
||||
result = true;
|
||||
|
||||
result = ability.resolve(game);
|
||||
if (!copiedSpell) {
|
||||
for (Effect effect : ability.getEffects()) {
|
||||
if (effect instanceof PostResolveEffect) {
|
||||
|
@ -126,8 +120,6 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
return false;
|
||||
} else {
|
||||
updateOptionalCosts();
|
||||
|
||||
resolveKicker(game);
|
||||
result = card.putOntoBattlefield(game, Zone.HAND, ability.getId(), controllerId);
|
||||
return result;
|
||||
}
|
||||
|
@ -157,18 +149,6 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean resolveKicker(Game game) {
|
||||
boolean replaced = false;
|
||||
for (KickerAbility kicker: card.getAbilities().getKickerAbilities()) {
|
||||
if (kicker.isKicked()) {
|
||||
if (kicker.isReplaces()) {
|
||||
replaced = true;
|
||||
}
|
||||
kicker.resolve(game);
|
||||
}
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose new targets for the spell
|
||||
|
|
|
@ -507,11 +507,6 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
|
||||
SpellAbility spellAbility = game.getStack().getSpell(ability.getId()).getSpellAbility();
|
||||
if (spellAbility.activate(game, noMana)) {
|
||||
for (KickerAbility kicker: card.getAbilities().getKickerAbilities()) {
|
||||
if (kicker.getCosts().canPay(ability.getSourceId(), playerId, game) && kicker.canChooseTarget(game)) {
|
||||
kicker.activate(game, false);
|
||||
}
|
||||
}
|
||||
GameEvent event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spellAbility.getId(), spellAbility.getSourceId(), playerId);
|
||||
event.setZone(fromZone);
|
||||
game.fireEvent(event);
|
||||
|
|
Loading…
Reference in a new issue