Cost increasing effects - refactor, removed redundant custom effects (related to #6684 and #6698);

This commit is contained in:
Oleg Agafonov 2020-06-30 08:27:29 +04:00
parent b885fffd9d
commit 09bc2575d8
23 changed files with 442 additions and 658 deletions

View file

@ -1,25 +1,20 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetOpponent;
import mage.util.CardUtil;
import java.util.UUID;
@ -39,7 +34,10 @@ public final class AccursedWitch extends CardImpl {
this.secondSideCardClazz = mage.cards.i.InfectiousCurse.class;
// Spells your opponents cast that target Accursed Witch cost {1} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AccursedWitchSpellsCostReductionEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(-1, new FilterCard("Spells"), TargetController.OPPONENT))
);
// When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent.
this.addAbility(new TransformAbility());
Ability ability = new DiesSourceTriggeredAbility(new AccursedWitchReturnTransformedEffect());
@ -93,45 +91,3 @@ class AccursedWitchReturnTransformedEffect extends OneShotEffect {
return true;
}
}
class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl {
AccursedWitchSpellsCostReductionEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.REDUCE_COST);
this.staticText = "Spells your opponents cast that target {this} cost {1} less to cast";
}
private AccursedWitchSpellsCostReductionEffect(AccursedWitchSpellsCostReductionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
CardUtil.reduceCost(abilityToModify, 1);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility) || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
return false;
}
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
Permanent permanent = game.getPermanent(targetUUID);
if (permanent != null && permanent.getId().equals(source.getSourceId())) {
return true;
}
}
}
}
return false;
}
@Override
public AccursedWitchSpellsCostReductionEffect copy() {
return new AccursedWitchSpellsCostReductionEffect(this);
}
}

View file

@ -82,9 +82,9 @@ class AnimarCostReductionEffect extends CostModificationEffectImpl {
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (abilityToModify.isControlledBy(source.getControllerId())) {
Card spell = ((SpellAbility) abilityToModify).getCharacteristics(game);
if (spell != null) {
return spell.isCreature();
Card card = ((SpellAbility) abilityToModify).getCharacteristics(game);
if (card != null) {
return card.isCreature();
}
}
}

View file

@ -1,17 +1,14 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.cards.Card;
import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.util.CardUtil;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.common.FilterInstantOrSorceryCard;
import java.util.UUID;
/**
* @author noxx
@ -22,7 +19,9 @@ public final class ArcaneMelee extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}");
// Instant and sorcery spells cost {2} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArcaneMeleeCostReductionEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostReductionAllEffect(new FilterInstantOrSorceryCard("Instant and sorcery spells"), 2))
);
}
public ArcaneMelee(final ArcaneMelee card) {
@ -34,39 +33,3 @@ public final class ArcaneMelee extends CardImpl {
return new ArcaneMelee(this);
}
}
class ArcaneMeleeCostReductionEffect extends CostModificationEffectImpl {
ArcaneMeleeCostReductionEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
staticText = "Instant and sorcery spells cost {2} less to cast";
}
ArcaneMeleeCostReductionEffect(ArcaneMeleeCostReductionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, 2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
Card sourceCard = game.getCard((abilityToModify).getSourceId());
if (sourceCard != null && (sourceCard.isInstant() || sourceCard.isSorcery())) {
return true;
}
}
return false;
}
@Override
public ArcaneMeleeCostReductionEffect copy() {
return new ArcaneMeleeCostReductionEffect(this);
}
}

View file

@ -1,33 +1,41 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class AvatarOfFury extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("artifact, creature, or enchantment");
static {
filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(),
CardType.CREATURE.getPredicate(),
CardType.ENCHANTMENT.getPredicate()));
}
public AvatarOfFury(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}{R}");
this.subtype.add(SubType.AVATAR);
@ -36,9 +44,14 @@ public final class AvatarOfFury extends CardImpl {
this.toughness = new MageInt(6);
// If an opponent controls seven or more lands, Avatar of Fury costs {6} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfFuryAdjustingCostsEffect()));
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, AvatarOfFuryCondition.instance)
.setText("if an opponent controls seven or more lands, Avatar of Fury costs {6} less to cast"))
.addHint(new ConditionHint(AvatarOfFuryCondition.instance, "Opponent controls seven or more lands"))
);
// Flying
this.addAbility(FlyingAbility.getInstance());
// {R}: Avatar of Fury gets +1/+0 until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
}
@ -53,39 +66,22 @@ public final class AvatarOfFury extends CardImpl {
}
}
class AvatarOfFuryAdjustingCostsEffect extends CostModificationEffectImpl {
enum AvatarOfFuryCondition implements Condition {
AvatarOfFuryAdjustingCostsEffect() {
super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST);
staticText = "If an opponent controls seven or more lands, {this} costs {6} less to cast";
}
AvatarOfFuryAdjustingCostsEffect(AvatarOfFuryAdjustingCostsEffect effect) {
super(effect);
}
instance;
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
CardUtil.reduceCost(abilityToModify, 6);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify.getSourceId().equals(source.getSourceId())
&& (abilityToModify instanceof SpellAbility)) {
for (UUID playerId : game.getOpponents(abilityToModify.getControllerId())) {
if (game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game) > 6) {
return true;
}
public boolean apply(Game game, Ability source) {
for (UUID playerId : game.getOpponents(source.getControllerId())) {
if (game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game) > 6) {
return true;
}
}
return false;
}
@Override
public AvatarOfFuryAdjustingCostsEffect copy() {
return new AvatarOfFuryAdjustingCostsEffect(this);
public String toString() {
return "an opponent controls seven or more lands";
}
}

View file

@ -1,22 +1,24 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.effects.common.combat.CanBlockAdditionalCreatureEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author Plopman
*/
public final class AvatarOfHope extends CardImpl {
@ -29,9 +31,14 @@ public final class AvatarOfHope extends CardImpl {
this.toughness = new MageInt(9);
// If you have 3 or less life, Avatar of Hope costs {6} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfHopeAdjustingCostsEffect()));
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, AvatarOfHopeCondition.instance)
.setText("if you have 3 or less life, Avatar of Hope costs {6} less to cast"))
.addHint(new ConditionHint(AvatarOfHopeCondition.instance))
);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Avatar of Hope can block any number of creatures.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanBlockAdditionalCreatureEffect(0)));
}
@ -46,39 +53,21 @@ public final class AvatarOfHope extends CardImpl {
}
}
class AvatarOfHopeAdjustingCostsEffect extends CostModificationEffectImpl {
enum AvatarOfHopeCondition implements Condition {
AvatarOfHopeAdjustingCostsEffect() {
super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST);
staticText = "If you have 3 or less life, {this} costs {6} less to cast";
}
AvatarOfHopeAdjustingCostsEffect(AvatarOfHopeAdjustingCostsEffect effect) {
super(effect);
}
instance;
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
CardUtil.reduceCost(abilityToModify, 6);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify.getSourceId().equals(source.getSourceId())
&& (abilityToModify instanceof SpellAbility)) {
Player player = game.getPlayer(abilityToModify.getControllerId());
if (player != null && player.getLife() < 4) {
return true;
}
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null && player.getLife() <= 3) {
return true;
}
return false;
}
@Override
public AvatarOfHopeAdjustingCostsEffect copy() {
return new AvatarOfHopeAdjustingCostsEffect(this);
public String toString() {
return "you have 3 or less life";
}
}

View file

@ -1,20 +1,17 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.target.Target;
import mage.util.CardUtil;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import java.util.Collection;
import java.util.UUID;
/**
@ -33,7 +30,9 @@ public final class BorealElemental extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// Spells your opponents cast that target Boreal Elemental cost {2} more to cast.
this.addAbility(new SimpleStaticAbility(new BorealElementalCostIncreaseEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
);
}
private BorealElemental(final BorealElemental card) {
@ -45,46 +44,3 @@ public final class BorealElemental extends CardImpl {
return new BorealElemental(this);
}
}
class BorealElementalCostIncreaseEffect extends CostModificationEffectImpl {
BorealElementalCostIncreaseEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
}
private BorealElementalCostIncreaseEffect(BorealElementalCostIncreaseEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility)
|| !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
return false;
}
return abilityToModify
.getModes()
.getSelectedModes()
.stream()
.map(uuid -> abilityToModify.getModes().get(uuid))
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(Target::getTargets)
.flatMap(Collection::stream)
.anyMatch(uuid -> uuid.equals(source.getSourceId()));
}
@Override
public BorealElementalCostIncreaseEffect copy() {
return new BorealElementalCostIncreaseEffect(this);
}
}

View file

@ -2,23 +2,18 @@ package mage.cards.c;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.DevotionCount;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.effects.common.continuous.SetPowerSourceEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.Collection;
import java.util.UUID;
/**
@ -51,11 +46,12 @@ public final class CallapheBelovedOfTheSea extends CardImpl {
).addHint(DevotionCount.U.getHint()));
// Creatures and enchantments you control have "Spells your opponents cast that target this permanent cost {1} more to cast".
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
new SimpleStaticAbility(
new CallapheBelovedOfTheSeaEffect()
), Duration.WhileOnBattlefield, filter)
.withForceQuotes()
Ability gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(1, new FilterCard("Spells"), TargetController.OPPONENT)
.withTargetName("this permanent")
);
this.addAbility(new SimpleStaticAbility(
new GainAbilityControlledEffect(gainAbility, Duration.WhileOnBattlefield, filter).withForceQuotes()
));
}
@ -68,45 +64,3 @@ public final class CallapheBelovedOfTheSea extends CardImpl {
return new CallapheBelovedOfTheSea(this);
}
}
class CallapheBelovedOfTheSeaEffect extends CostModificationEffectImpl {
CallapheBelovedOfTheSeaEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target this permanent cost {1} more to cast";
}
private CallapheBelovedOfTheSeaEffect(CallapheBelovedOfTheSeaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -1);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility)
|| !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
return false;
}
return abilityToModify
.getModes()
.getSelectedModes()
.stream()
.map(uuid -> abilityToModify.getModes().get(uuid))
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(Target::getTargets)
.flatMap(Collection::stream)
.anyMatch(uuid -> uuid.equals(source.getSourceId()));
}
@Override
public CallapheBelovedOfTheSeaEffect copy() {
return new CallapheBelovedOfTheSeaEffect(this);
}
}

View file

@ -1,24 +1,21 @@
package mage.cards.e;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.abilities.keyword.LifelinkAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.target.Target;
import mage.util.CardUtil;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class ElderwoodScion extends CardImpl {
@ -32,12 +29,19 @@ public final class ElderwoodScion extends CardImpl {
// Trample
this.addAbility(TrampleAbility.getInstance());
// Lifelink
this.addAbility(LifelinkAbility.getInstance());
// Spells you cast that target Elderwood Scion cost {2} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElderwoodScionCostReductionEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(-2, new FilterCard("Spells"), TargetController.YOU))
);
// Spells your opponents cast that target Elderwood Scion cost {2} more to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElderwoodScionCostReductionEffect2()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
);
}
public ElderwoodScion(final ElderwoodScion card) {
@ -49,92 +53,3 @@ public final class ElderwoodScion extends CardImpl {
return new ElderwoodScion(this);
}
}
class ElderwoodScionCostReductionEffect extends CostModificationEffectImpl {
private static final String effectText = "Spells you cast that target {this} cost {2} less to cast";
ElderwoodScionCostReductionEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
staticText = effectText;
}
ElderwoodScionCostReductionEffect(ElderwoodScionCostReductionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, 2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (abilityToModify.isControlledBy(source.getControllerId())) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getSourceId())) {
return true;
}
}
}
}
}
}
return false;
}
@Override
public ElderwoodScionCostReductionEffect copy() {
return new ElderwoodScionCostReductionEffect(this);
}
}
class ElderwoodScionCostReductionEffect2 extends CostModificationEffectImpl {
private static final String effectText = "Spells your opponents cast that target {this} cost {2} more to cast";
ElderwoodScionCostReductionEffect2() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = effectText;
}
ElderwoodScionCostReductionEffect2(ElderwoodScionCostReductionEffect2 effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.increaseCost(spellAbility, 2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
for (Target target : abilityToModify.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getSourceId())) {
return true;
}
}
}
}
}
return false;
}
@Override
public ElderwoodScionCostReductionEffect2 copy() {
return new ElderwoodScionCostReductionEffect2(this);
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.g;
import java.util.UUID;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.keyword.StormAbility;
import mage.cards.CardImpl;
@ -9,19 +7,20 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetAnyTarget;
import java.util.UUID;
/**
*
* @author Plopman
*/
public final class Grapeshot extends CardImpl {
public Grapeshot(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{R}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}");
// Grapeshot deals 1 damage to any target.
this.getSpellAbility().addTarget(new TargetAnyTarget());
this.getSpellAbility().addEffect(new DamageTargetEffect(1));
// Storm
this.addAbility(new StormAbility());
}

View file

@ -2,17 +2,16 @@ package mage.cards.i;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -20,7 +19,6 @@ import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
import mage.watchers.Watcher;
import java.util.UUID;
@ -53,8 +51,9 @@ public final class IcefallRegent extends CardImpl {
this.addAbility(ability, new IcefallRegentWatcher());
// Spells your opponents cast that target Icefall Regent cost {2} more to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IcefallRegentCostIncreaseEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
);
}
public IcefallRegent(final IcefallRegent card) {
@ -157,48 +156,3 @@ class IcefallRegentWatcher extends Watcher {
//don't reset condition each turn - only when this leaves the battlefield
}
}
class IcefallRegentCostIncreaseEffect extends CostModificationEffectImpl {
IcefallRegentCostIncreaseEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
}
private IcefallRegentCostIncreaseEffect(IcefallRegentCostIncreaseEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getSourceId())) {
return true;
}
}
}
}
}
}
return false;
}
@Override
public IcefallRegentCostIncreaseEffect copy() {
return new IcefallRegentCostIncreaseEffect(this);
}
}

View file

@ -2,24 +2,19 @@ package mage.cards.j;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.Collection;
import java.util.UUID;
/**
@ -46,10 +41,12 @@ public final class JubilantSkybonder extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// Creatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast."
ContinuousEffect effect = new GainAbilityAllEffect(
new SimpleStaticAbility(new JubilantSkybonderEffect()),
Duration.WhileOnBattlefield, filter
).withForceQuotes();
Ability gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)
.withTargetName("this creature")
);
ContinuousEffect effect = new GainAbilityAllEffect(gainAbility, Duration.WhileOnBattlefield, filter).withForceQuotes();
effect.setDependedToType(DependencyType.AddingAbility);
this.addAbility(new SimpleStaticAbility(effect));
}
@ -63,45 +60,3 @@ public final class JubilantSkybonder extends CardImpl {
return new JubilantSkybonder(this);
}
}
class JubilantSkybonderEffect extends CostModificationEffectImpl {
JubilantSkybonderEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target this creature cost {2} more to cast";
}
private JubilantSkybonderEffect(JubilantSkybonderEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility)
|| !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
return false;
}
return abilityToModify
.getModes()
.getSelectedModes()
.stream()
.map(abilityToModify.getModes()::get)
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(Target::getTargets)
.flatMap(Collection::stream)
.anyMatch(source.getSourceId()::equals);
}
@Override
public JubilantSkybonderEffect copy() {
return new JubilantSkybonderEffect(this);
}
}

View file

@ -2,22 +2,18 @@ package mage.cards.p;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.permanent.token.PursuedWhaleToken;
import mage.game.permanent.token.Token;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.Collection;
import java.util.UUID;
/**
@ -36,7 +32,9 @@ public final class PursuedWhale extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new PursuedWhaleTokenEffect()));
// Spells your opponents cast that target Pursued Whale cost {3} more to cast.
this.addAbility(new SimpleStaticAbility(new PursuedWhaleCostIncreaseEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(3, new FilterCard("Spells"), TargetController.OPPONENT))
);
}
private PursuedWhale(final PursuedWhale card) {
@ -76,45 +74,3 @@ class PursuedWhaleTokenEffect extends OneShotEffect {
return true;
}
}
class PursuedWhaleCostIncreaseEffect extends CostModificationEffectImpl {
PursuedWhaleCostIncreaseEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target {this} cost {3} more to cast";
}
private PursuedWhaleCostIncreaseEffect(PursuedWhaleCostIncreaseEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -3);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility)
|| !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
return false;
}
return abilityToModify
.getModes()
.getSelectedModes()
.stream()
.map(uuid -> abilityToModify.getModes().get(uuid))
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(Target::getTargets)
.flatMap(Collection::stream)
.anyMatch(uuid -> uuid.equals(source.getSourceId()));
}
@Override
public PursuedWhaleCostIncreaseEffect copy() {
return new PursuedWhaleCostIncreaseEffect(this);
}
}

View file

@ -1,20 +1,17 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
import mage.util.CardUtil;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import java.util.UUID;
@ -37,7 +34,9 @@ public final class SphinxOfNewPrahv extends CardImpl {
this.addAbility(VigilanceAbility.getInstance());
// Spells your opponents cast that target Sphinx of New Prahv cost {2} more to cast.
this.addAbility(new SimpleStaticAbility(new SphinxOfNewPrahvCostIncreaseEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
);
}
private SphinxOfNewPrahv(final SphinxOfNewPrahv card) {
@ -49,49 +48,3 @@ public final class SphinxOfNewPrahv extends CardImpl {
return new SphinxOfNewPrahv(this);
}
}
class SphinxOfNewPrahvCostIncreaseEffect extends CostModificationEffectImpl {
SphinxOfNewPrahvCostIncreaseEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
}
private SphinxOfNewPrahvCostIncreaseEffect(SphinxOfNewPrahvCostIncreaseEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null
|| !(abilityToModify instanceof SpellAbility)
|| !controller.hasOpponent(abilityToModify.getControllerId(), game)) {
return false;
}
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getSourceId())) {
return true;
}
}
}
}
return false;
}
@Override
public SphinxOfNewPrahvCostIncreaseEffect copy() {
return new SphinxOfNewPrahvCostIncreaseEffect(this);
}
}

View file

@ -1,23 +1,17 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.CardsInControllerHandCount;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.continuous.SetPowerSourceEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.target.Target;
import mage.util.CardUtil;
import mage.filter.FilterCard;
import java.util.Collection;
import java.util.UUID;
/**
@ -43,7 +37,9 @@ public final class SyrElenoraTheDiscerning extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)));
// Spells your opponents cast that target Syr Elenora cost {2} more to cast.
this.addAbility(new SimpleStaticAbility(new SyrElenoraTheDiscerningCostIncreaseEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
);
}
private SyrElenoraTheDiscerning(final SyrElenoraTheDiscerning card) {
@ -55,46 +51,3 @@ public final class SyrElenoraTheDiscerning extends CardImpl {
return new SyrElenoraTheDiscerning(this);
}
}
class SyrElenoraTheDiscerningCostIncreaseEffect extends CostModificationEffectImpl {
SyrElenoraTheDiscerningCostIncreaseEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
}
private SyrElenoraTheDiscerningCostIncreaseEffect(SyrElenoraTheDiscerningCostIncreaseEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
CardUtil.adjustCost(spellAbility, -2);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility)
|| !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
return false;
}
return abilityToModify
.getModes()
.getSelectedModes()
.stream()
.map(uuid -> abilityToModify.getModes().get(uuid))
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(Target::getTargets)
.flatMap(Collection::stream)
.anyMatch(uuid -> uuid.equals(source.getSourceId()));
}
@Override
public SyrElenoraTheDiscerningCostIncreaseEffect copy() {
return new SyrElenoraTheDiscerningCostIncreaseEffect(this);
}
}

View file

@ -7,10 +7,7 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* also tests cost reduction effects
*
* @author BetaSteward
* @author BetaSteward, JayDi85
*/
public class CostModificationTest extends CardTestPlayerBase {
@ -71,7 +68,7 @@ public class CostModificationTest extends CardTestPlayerBase {
/**
* Test that cost reduction also works with mana source restriction Myr
* Superion Spend only mana produced by creatures to cast Myr Superion
*
* <p>
* Etherium Sculptor {1}{U} Artifact Creature - Vedalken Artificer 1/2
* Artifact spells you cast cost {1} less to cast.
*/
@ -143,7 +140,7 @@ public class CostModificationTest extends CardTestPlayerBase {
}
/*
* Reported bug: Grand Arbiter Augustin IV makes moth spells you cast and your opponent cast {1} more. Should only affect opponent's spells costs.
* Reported bug: Grand Arbiter Augustin IV makes moth spells you cast and your opponent cast {1} more. Should only affect opponent's spells costs.
*/
@Test
public void testArbiterIncreasingCostBothPlayers() {
@ -244,6 +241,106 @@ public class CostModificationTest extends CardTestPlayerBase {
assertCounterCount(playerA, "Animar, Soul of Elements", CounterType.P1P1, 2);
assertTappedCount("Plains", true, 3);
}
@Test
public void test_ThatTargetSourceEffect_AccursedWitch_CanPlayWithReduction() {
// creature 4/2
// Spells your opponents cast that target Accursed Witch cost {1} less to cast.
addCard(Zone.BATTLEFIELD, playerB, "Accursed Witch");
//
// {1}{R} SORCERY
// Grapeshot deals 1 damage to any target.
addCard(Zone.HAND, playerA, "Grapeshot");
addCard(Zone.HAND, playerA, "Mountain", 1); // play to add mana
checkPlayableAbility("0 mana, can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false);
// add 1 mana, can cast by target
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain");
checkPlayableAbility("1 mana, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true);
// cast with target
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grapeshot", "Accursed Witch");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertGraveyardCount(playerA, "Grapeshot", 1);
}
@Test
public void test_ThatTargetSourceEffect_AccursedWitch_CantPlayOnProtection() {
// creature 4/2
// Spells your opponents cast that target Accursed Witch cost {1} less to cast.
addCard(Zone.BATTLEFIELD, playerB, "Accursed Witch");
//
// {1}{R} SORCERY
// Grapeshot deals 1 damage to any target.
addCard(Zone.HAND, playerA, "Grapeshot");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
//
// Artifact Equipment
// Equip {2}
// Equipped creature gets +2/+2 and has protection from red and from white.
addCard(Zone.BATTLEFIELD, playerB, "Sword of War and Peace");
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
// 1 mana, can cast by target
checkPlayableAbility("1 mana, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true);
// add protection from red
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Accursed Witch");
// can't cast cause can't target to red
checkPlayableAbility("can't cast cause protection", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false);
setStrictChooseMode(true);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
@Test
public void test_ThatTargetSourceEffect_BorealElemental() {
// use case: cost increase for getPlayable works only for no other targets available
// so if you can targets another target then allows to cast (don't apply cost increase)
// creature 3/4
// Spells your opponents cast that target Boreal Elemental cost {2} more to cast.
addCard(Zone.BATTLEFIELD, playerB, "Boreal Elemental");
//
// {R} instant
// Engulfing Flames deals 1 damage to target creature. It can't be regenerated this turn.
addCard(Zone.HAND, playerA, "Engulfing Flames");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
//
addCard(Zone.HAND, playerA, "Grizzly Bears"); // {1}{G}
addCard(Zone.HAND, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
// no second target, so must cost increase
checkPlayableAbility("one target, can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Engulfing Flames", false);
// prepare second target
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkPlayableAbility("two targets, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Engulfing Flames", true);
// try to cast (only one target possible to targets/play)
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Engulfing Flames");
//addTarget(playerA, "Boreal Elemental"); // you can't target Boreal Elemental cause it will increase cost
addTarget(playerA, "Grizzly Bears");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertGraveyardCount(playerA, "Engulfing Flames", 1);
}
}

View file

@ -1181,6 +1181,8 @@ public class TestPlayer implements Player {
}
if (mustHave && !founded) {
printStart("Available mana for " + computerPlayer.getName());
printMana(game, computerPlayer.getManaAvailable(game));
printStart(action.getActionName());
printAbilities(game, computerPlayer.getPlayable(game, true));
printEnd();
@ -1188,6 +1190,8 @@ public class TestPlayer implements Player {
}
if (!mustHave && founded) {
printStart("Available mana for " + computerPlayer.getName());
printMana(game, computerPlayer.getManaAvailable(game));
printStart(action.getActionName());
printAbilities(game, computerPlayer.getPlayable(game, true));
printEnd();

View file

@ -769,7 +769,7 @@ public abstract class AbilityImpl implements Ability {
if (ruleStart.length() > 1) {
String end = ruleStart.substring(ruleStart.length() - 2).trim();
if (end.isEmpty() || end.equals(":") || end.equals(".")) {
rule = ruleStart + Character.toUpperCase(text.charAt(0)) + text.substring(1);
rule = ruleStart + CardUtil.getTextWithFirstCharUpperCase(text);
} else {
rule = ruleStart + text;
}

View file

@ -4,6 +4,7 @@ import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.constants.Outcome;
import mage.target.targetpointer.TargetPointer;
import mage.util.CardUtil;
import java.util.ArrayList;
@ -31,7 +32,7 @@ public class Effects extends ArrayList<Effect> {
public String getTextStartingUpperCase(Mode mode) {
String text = getText(mode);
if (text.length() > 3) {
return Character.toUpperCase(text.charAt(0)) + text.substring(1);
return CardUtil.getTextWithFirstCharUpperCase(text);
}
return text;
}

View file

@ -0,0 +1,166 @@
package mage.abilities.effects.common.cost;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author JayDi85
*/
public class SpellsCostModificationThatTargetSourceEffect extends CostModificationEffectImpl {
private final FilterCard spellFilter;
private final int modificationAmount;
private String targetName = "{this}";
private TargetController targetController;
public SpellsCostModificationThatTargetSourceEffect(int modificationAmount, FilterCard spellFilter, TargetController targetController) {
super(Duration.WhileOnBattlefield, Outcome.Neutral, modificationAmount < 0 ? CostModificationType.REDUCE_COST : CostModificationType.INCREASE_COST);
this.spellFilter = spellFilter;
this.modificationAmount = modificationAmount;
this.targetController = targetController;
setText();
}
private void setText() {
// example: Spells your opponents cast that target Accursed Witch cost {1} less to cast.
StringBuilder sb = new StringBuilder();
sb.append(this.spellFilter.getMessage());
switch (this.targetController) {
case ANY:
break;
case YOU:
sb.append(" you");
break;
case OPPONENT:
sb.append(" your opponents");
break;
default:
throw new IllegalArgumentException("Unsupported target controller " + this.targetController);
}
sb.append(" cast that target ").append(this.targetName);
if (this.modificationAmount < 0) {
sb.append(" cost {").append(-1 * this.modificationAmount).append("} less to cast");
} else {
sb.append(" cost {").append(this.modificationAmount).append("} more to cast");
}
this.staticText = sb.toString();
}
private SpellsCostModificationThatTargetSourceEffect(SpellsCostModificationThatTargetSourceEffect effect) {
super(effect);
this.spellFilter = effect.spellFilter;
this.modificationAmount = effect.modificationAmount;
this.targetName = effect.targetName;
this.targetController = effect.targetController;
}
@Override
public SpellsCostModificationThatTargetSourceEffect copy() {
return new SpellsCostModificationThatTargetSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
if (this.modificationAmount >= 0) {
CardUtil.increaseCost(abilityToModify, this.modificationAmount);
} else {
CardUtil.reduceCost(abilityToModify, -1 * this.modificationAmount);
}
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (!(abilityToModify instanceof SpellAbility)) {
return false;
}
Player sourceController = game.getPlayer(source.getControllerId());
Player abilityController = game.getPlayer(abilityToModify.getControllerId());
if (sourceController == null || abilityController == null) {
return false;
}
switch (this.targetController) {
case ANY:
break;
case YOU:
if (!sourceController.getId().equals(abilityController.getId())) {
return false;
}
break;
case OPPONENT:
if (!sourceController.hasOpponent(abilityController.getId(), game)) {
return false;
}
break;
default:
return false;
}
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
if (spell != null && this.spellFilter.match(spell, game)) {
// real cast with put on stack
Set<UUID> allTargets = getAllSelectedTargets(abilityToModify, source, game);
return allTargets.contains(source.getSourceId());
} else {
// get playable and other staff without put on stack
// used at least for flashback ability because Flashback ability doesn't use stack
Set<UUID> allTargets = getAllPossibleTargets(abilityToModify, source, game);
switch (this.getModificationType()) {
case REDUCE_COST:
// reduce all the time
return allTargets.contains(source.getSourceId());
case INCREASE_COST:
// increase if can't target another (e.g. user can choose another target without cost increase)
return allTargets.contains(source.getSourceId()) && allTargets.size() <= 1;
}
}
return false;
}
private Set<UUID> getAllSelectedTargets(Ability abilityToModify, Ability source, Game game) {
return abilityToModify.getModes().getSelectedModes()
.stream()
.map(abilityToModify.getModes()::get)
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(Target::getTargets)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}
private Set<UUID> getAllPossibleTargets(Ability abilityToModify, Ability source, Game game) {
return abilityToModify.getModes().values()
.stream()
.map(Mode::getTargets)
.flatMap(Collection::stream)
.map(t -> t.possibleTargets(abilityToModify.getSourceId(), abilityToModify.getControllerId(), game))
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}
public SpellsCostModificationThatTargetSourceEffect withTargetName(String targetName) {
this.targetName = targetName;
setText();
return this;
}
}

View file

@ -1,8 +1,5 @@
package mage.abilities.effects.common.cost;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
@ -17,8 +14,11 @@ import mage.game.stack.Spell;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
@ -125,8 +125,10 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
if (abilityToModify instanceof SpellAbility) {
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
if (spell != null) {
// real cast with put on stack
return this.filter.match(spell, game) && selectedByRuntimeData(spell, source, game);
} else {
// get playable and other staff without put on stack
// used at least for flashback ability because Flashback ability doesn't use stack
Card sourceCard = game.getCard(abilityToModify.getSourceId());
return sourceCard != null && this.filter.match(sourceCard, game) && selectedByRuntimeData(sourceCard, source, game);

View file

@ -1,7 +1,5 @@
package mage.abilities.effects.common.cost;
import java.util.LinkedHashSet;
import java.util.Set;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Ability;
@ -19,8 +17,10 @@ import mage.game.stack.Spell;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.LinkedHashSet;
import java.util.Set;
/**
*
* @author North
*/
public class SpellsCostReductionControllerEffect extends CostModificationEffectImpl {
@ -116,9 +116,11 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI
if (abilityToModify.isControlledBy(source.getControllerId())) {
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
if (spell != null) {
// real cast with put on stack
return this.filter.match(spell, source.getSourceId(), source.getControllerId(), game);
} else {
// used at least for flashback ability because Flashback ability doesn't use stack or for getPlayables where spell is not cast yet
// get playable and other staff without put on stack
// used at least for flashback ability because Flashback ability doesn't use stack
Card sourceCard = game.getCard(abilityToModify.getSourceId());
return sourceCard != null && this.filter.match(sourceCard, source.getSourceId(), source.getControllerId(), game);
}

View file

@ -3,6 +3,7 @@ package mage.abilities.hint;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.util.CardUtil;
import java.awt.*;
@ -18,15 +19,19 @@ public class ConditionHint implements Hint {
private Color falseColor;
private Boolean useIcons;
public ConditionHint(Condition condition) {
this(condition, condition.toString());
}
public ConditionHint(Condition condition, String textWithIcons) {
this(condition, textWithIcons, null, textWithIcons, null, true);
}
public ConditionHint(Condition condition, String trueText, Color trueColor, String falseText, Color falseColor, Boolean useIcons) {
this.condition = condition;
this.trueText = trueText;
this.trueText = CardUtil.getTextWithFirstCharUpperCase(trueText);
this.trueColor = trueColor;
this.falseText = falseText;
this.falseText = CardUtil.getTextWithFirstCharUpperCase(falseText);
this.falseColor = falseColor;
this.useIcons = useIcons;
}

View file

@ -800,4 +800,12 @@ public final class CardUtil {
return object.getAbilities();
}
}
public static String getTextWithFirstCharUpperCase(String text) {
if (text != null && text.length() >= 1) {
return Character.toUpperCase(text.charAt(0)) + text.substring(1);
} else {
return text;
}
}
}