Remove lockedIn parameter from BoostTargetEffect. Fixes #9329 (#9334)

This commit is contained in:
Alex W. Jackson 2022-08-02 02:40:59 -04:00 committed by GitHub
parent 1a3d5923de
commit 7233061ae3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
133 changed files with 590 additions and 676 deletions

View file

@ -9,6 +9,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* @author TheElk801
@ -69,6 +70,6 @@ public class AttacksAloneControlledTriggeredAbility extends TriggeredAbilityImpl
@Override
public String getTriggerPhrase() {
return "Whenever " + filter.getMessage() + " attacks alone, ";
return "Whenever " + CardUtil.addArticle(filter.getMessage()) + " attacks alone, ";
}
}

View file

@ -18,4 +18,12 @@ public interface DynamicValue extends Serializable, Copyable<DynamicValue> {
DynamicValue copy();
String getMessage();
/**
*
* @return A positive value if the result of calculate() is usually positive, and a negative value if it is usually negative.
*/
default int getSign() {
return 1;
}
}

View file

@ -45,4 +45,9 @@ public class IntPlusDynamicValue implements DynamicValue {
public String getMessage() {
return value.getMessage();
}
@Override
public int getSign() {
return value.getSign();
}
}

View file

@ -48,4 +48,9 @@ public class LimitedDynamicValue implements DynamicValue {
public String getMessage() {
return value.getMessage();
}
@Override
public int getSign() {
return value.getSign();
}
}

View file

@ -49,4 +49,9 @@ public class MultipliedValue implements DynamicValue {
public String getMessage() {
return value.getMessage();
}
@Override
public int getSign() {
return multiplier * value.getSign();
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.dynamicvalue.common;
import java.util.List;
@ -16,18 +15,18 @@ import mage.game.permanent.Permanent;
*/
public class AuraAttachedCount implements DynamicValue {
private Integer amount;
private final int multiplier;
public AuraAttachedCount() {
this(1);
}
public AuraAttachedCount(Integer amount) {
this.amount = amount;
public AuraAttachedCount(int multiplier) {
this.multiplier = multiplier;
}
public AuraAttachedCount(final AuraAttachedCount dynamicValue) {
this.amount = dynamicValue.amount;
this.multiplier = dynamicValue.multiplier;
}
@Override
@ -44,7 +43,7 @@ public class AuraAttachedCount implements DynamicValue {
}
}
return amount * count;
return multiplier * count;
}
@Override
@ -54,14 +53,16 @@ public class AuraAttachedCount implements DynamicValue {
@Override
public String toString() {
if (amount != null) {
return amount.toString();
}
return "";
return Integer.toString(multiplier);
}
@Override
public String getMessage() {
return "Aura attached to it";
}
@Override
public int getSign() {
return multiplier;
}
}

View file

@ -23,7 +23,7 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
private final String message;
CardTypesInGraveyardCount(String message) {
this.message = message;
this.message = "the number of card types among cards in " + message;
}
@Override
@ -44,12 +44,12 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
@Override
public String toString() {
return "1";
return "X";
}
@Override
public String getMessage() {
return "the number of card types in " + message;
return message;
}
private final Stream<Card> getStream(Game game, Ability ability) {

View file

@ -8,13 +8,15 @@ import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
* @author North
*/
public class CardsInControllerGraveyardCount implements DynamicValue {
private final FilterCard filter;
private final Integer amount;
private final Integer multiplier;
public CardsInControllerGraveyardCount() {
this(StaticFilters.FILTER_CARD, 1);
@ -24,25 +26,28 @@ public class CardsInControllerGraveyardCount implements DynamicValue {
this(filter, 1);
}
public CardsInControllerGraveyardCount(FilterCard filter, Integer amount) {
public CardsInControllerGraveyardCount(FilterCard filter, Integer multiplier) {
this.filter = filter;
this.amount = amount;
this.multiplier = multiplier;
}
public CardsInControllerGraveyardCount(final CardsInControllerGraveyardCount dynamicValue) {
this.filter = dynamicValue.filter;
this.amount = dynamicValue.amount;
this.multiplier = dynamicValue.multiplier;
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
Player player = game.getPlayer(sourceAbility.getControllerId());
if (player != null) {
return amount * player.getGraveyard().count(
filter, sourceAbility.getControllerId(), sourceAbility, game
);
UUID playerId = sourceAbility.getControllerId();
Player player = game.getPlayer(playerId);
if (player == null) {
return 0;
}
return 0;
int value = player.getGraveyard().count(filter, playerId, sourceAbility, game);
if (multiplier != null) {
value *= multiplier;
}
return value;
}
@Override
@ -52,11 +57,16 @@ public class CardsInControllerGraveyardCount implements DynamicValue {
@Override
public String toString() {
return amount.toString();
return multiplier == null ? "X" : multiplier.toString();
}
@Override
public String getMessage() {
return filter.getMessage() + " in your graveyard";
return (multiplier == null ? "the number of " : "") + filter.getMessage() + " in your graveyard";
}
@Override
public int getSign() {
return multiplier == null ? 1 : multiplier;
}
}

View file

@ -45,7 +45,7 @@ public enum ControllerGotLifeCount implements DynamicValue {
@Override
public String getMessage() {
return "the amount of life you've gained this turn";
return "the amount of life you gained this turn";
}
public static Hint getHint() {

View file

@ -41,11 +41,26 @@ public enum DevotionCount implements DynamicValue {
GU(ColoredManaSymbol.G, ColoredManaSymbol.U);
private final ArrayList<ColoredManaSymbol> devotionColors = new ArrayList<>();
private final String message;
private final String reminder;
private final Hint hint;
DevotionCount(ColoredManaSymbol... devotionColor) {
this.devotionColors.addAll(Arrays.asList(devotionColor));
this.hint = new ValueHint(this.getMessage().replace("your d", "D"), this);
this.message = "your devotion to "
+ String.join(" and ", devotionColors.stream()
.map(ColoredManaSymbol::getColorName)
.collect(Collectors.toList()));
this.reminder = "<i>(Each "
+ String.join(" and ", devotionColors.stream()
.map(s -> "{" + s + "}")
.collect(Collectors.toList()))
+ " in the mana costs of permanents you control counts toward "
+ this.message
+ ".)</i>";
this.hint = new ValueHint(this.message.replace("your d", "D"), this);
}
@Override
@ -84,12 +99,11 @@ public enum DevotionCount implements DynamicValue {
@Override
public String getMessage() {
return "your devotion to " + String.join(
" and ",
devotionColors.stream()
.map(ColoredManaSymbol::getColorName)
.collect(Collectors.toList())
);
return message;
}
public String getReminder() {
return reminder;
}
public Hint getHint() {

View file

@ -61,10 +61,6 @@ public enum DomainValue implements DynamicValue {
return "1";
}
public int getAmount() {
return 1;
}
@Override
public String getMessage() {
return "basic land type among lands " + (this == TARGET ? "they control" : "you control");

View file

@ -1,4 +1,3 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
@ -17,18 +16,18 @@ import java.util.UUID;
*/
public class EquipmentAttachedCount implements DynamicValue {
private Integer amount;
private final int multiplier;
public EquipmentAttachedCount() {
this(1);
}
public EquipmentAttachedCount(Integer amount) {
this.amount = amount;
public EquipmentAttachedCount(int multiplier) {
this.multiplier = multiplier;
}
public EquipmentAttachedCount(final EquipmentAttachedCount dynamicValue) {
this.amount = dynamicValue.amount;
this.multiplier = dynamicValue.multiplier;
}
@Override
@ -44,7 +43,7 @@ public class EquipmentAttachedCount implements DynamicValue {
}
}
}
return amount * count;
return multiplier * count;
}
@Override
@ -54,14 +53,16 @@ public class EquipmentAttachedCount implements DynamicValue {
@Override
public String toString() {
if (amount != null) {
return amount.toString();
}
return "";
return Integer.toString(multiplier);
}
@Override
public String getMessage() {
return "Equipment attached to it";
}
@Override
public int getSign() {
return multiplier;
}
}

View file

@ -47,6 +47,11 @@ public class HalfValue implements DynamicValue {
return sb.toString();
}
@Override
public int getSign() {
return value.getSign();
}
@Override
public String toString() {
return "half ";

View file

@ -51,7 +51,7 @@ public class HighestManaValueCount implements DynamicValue {
@Override
public String getMessage() {
return "the highest mana value among permanents you control";
return "the highest mana value among " + filter.getMessage() + " you control";
}
@Override

View file

@ -1,4 +1,3 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
@ -13,8 +12,8 @@ import mage.game.Game;
*/
public class PermanentsOnBattlefieldCount implements DynamicValue {
private FilterPermanent filter;
private Integer multiplier;
private final FilterPermanent filter;
private final Integer multiplier;
public PermanentsOnBattlefieldCount() {
this(new FilterPermanent(), 1);
@ -55,14 +54,16 @@ public class PermanentsOnBattlefieldCount implements DynamicValue {
@Override
public String toString() {
if (multiplier != null) {
return multiplier.toString();
}
return "X";
return multiplier == null ? "X" : multiplier.toString();
}
@Override
public String getMessage() {
return multiplier == null ? "the number of " + filter.getMessage() : filter.getMessage();
}
@Override
public int getSign() {
return multiplier == null ? 1 : multiplier;
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.dynamicvalue.common;
import java.util.UUID;
@ -14,8 +13,8 @@ import mage.game.Game;
*/
public class PermanentsTargetOpponentControlsCount implements DynamicValue {
private FilterPermanent filter;
private Integer multiplier;
private final FilterPermanent filter;
private final Integer multiplier;
public PermanentsTargetOpponentControlsCount() {
this(new FilterPermanent(), 1);
@ -43,24 +42,28 @@ public class PermanentsTargetOpponentControlsCount implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
UUID targetOpponentId = effect.getTargetPointer().getFirst(game, sourceAbility);
if (targetOpponentId != null) {
int value = game.getBattlefield().countAll(filter, targetOpponentId, game);
return multiplier * value;
} else {
if (targetOpponentId == null) {
return 0;
}
int value = game.getBattlefield().countAll(filter, targetOpponentId, game);
if (multiplier != null) {
value *= multiplier;
}
return value;
}
@Override
public String toString() {
if (multiplier != null) {
return multiplier.toString();
}
return "X";
return multiplier == null ? "X" : multiplier.toString();
}
@Override
public String getMessage() {
return filter.getMessage() + " target opponent controls";
return (multiplier == null ? "the number of " : "") + filter.getMessage() + " target opponent controls";
}
@Override
public int getSign() {
return multiplier == null ? 1 : multiplier;
}
}

View file

@ -25,12 +25,12 @@ public enum RevealTargetFromHandCostCount implements DynamicValue {
@Override
public String getMessage() {
return "number of revealed cards";
return "";
}
@Override
public RevealTargetFromHandCostCount copy() {
return RevealTargetFromHandCostCount.instance;
return instance;
}
@Override

View file

@ -48,4 +48,9 @@ public class SignInversionDynamicValue implements DynamicValue {
public String getMessage() {
return value.getMessage();
}
@Override
public int getSign() {
return -1 * value.getSign();
}
}

View file

@ -45,6 +45,11 @@ public class StaticValue implements DynamicValue {
return "";
}
@Override
public int getSign() {
return value;
}
public int getValue() {
return value;
}

View file

@ -10,12 +10,17 @@ import mage.game.Game;
*
* @author LevelX2
*/
public class SweepNumber implements DynamicValue {
public enum SweepNumber implements DynamicValue {
// FOREST("Forest");
// ISLAND("Island");
PLAINS("Plains"),
MOUNTAIN("Mountain"),
SWAMP("Swamp");
private final String sweepSubtype;
private final String message;
public SweepNumber(String sweepSubtype) {
this.sweepSubtype = sweepSubtype;
SweepNumber(String landType) {
this.message = landType + " returned this way";
}
@Override
@ -27,16 +32,16 @@ public class SweepNumber implements DynamicValue {
@Override
public SweepNumber copy() {
return new SweepNumber(sweepSubtype);
return this;
}
@Override
public String toString() {
return "X";
return "1";
}
@Override
public String getMessage() {
return "the number of " + sweepSubtype + (sweepSubtype.endsWith("s") ? "" : "s") + " returned this way";
return message;
}
}

View file

@ -293,20 +293,6 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return sublayer;
}
protected static boolean isCanKill(DynamicValue toughness) {
if (toughness instanceof StaticValue) {
return toughness.calculate(null, null, null) < 0;
}
if (toughness instanceof SignInversionDynamicValue) {
// count this class as used for "-{something_positive}"
return true;
}
if (toughness instanceof DomainValue) {
return ((DomainValue) toughness).getAmount() < 0;
}
return false;
}
@Override
public List<MageObjectReference> getAffectedObjects() {
return affectedObjectList;

View file

@ -49,7 +49,7 @@ public class BoostAllEffect extends ContinuousEffectImpl {
}
public BoostAllEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource, String rule) {
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, isCanKill(toughness) ? Outcome.UnboostCreature : Outcome.BoostCreature);
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, CardUtil.getBoostOutcome(power, toughness));
this.power = power;
this.toughness = toughness;
this.filter = filter;

View file

@ -37,7 +37,7 @@ public class BoostEnchantedEffect extends ContinuousEffectImpl {
}
public BoostEnchantedEffect(DynamicValue power, DynamicValue toughness, Duration duration) {
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, isCanKill(toughness) ? Outcome.UnboostCreature : Outcome.BoostCreature);
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, CardUtil.getBoostOutcome(power, toughness));
this.power = power;
this.toughness = toughness;
this.staticText = "enchanted creature gets " + CardUtil.getBoostText(power, toughness, duration);

View file

@ -24,39 +24,30 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
private DynamicValue power;
private DynamicValue toughness;
private final boolean lockedIn;
public BoostTargetEffect(int power, int toughness) {
this(power, toughness, Duration.EndOfTurn);
}
public BoostTargetEffect(int power, int toughness, Duration duration) {
this(StaticValue.get(power), StaticValue.get(toughness), duration, false);
}
public BoostTargetEffect(DynamicValue power, DynamicValue toughness, Duration duration) {
this(power, toughness, duration, false);
this(StaticValue.get(power), StaticValue.get(toughness), duration);
}
/**
* @param power
* @param toughness
* @param duration
* @param lockedIn if true, power and toughness will be calculated only
* once, when the ability resolves
*/
public BoostTargetEffect(DynamicValue power, DynamicValue toughness, Duration duration, boolean lockedIn) {
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, isCanKill(toughness) ? Outcome.UnboostCreature : Outcome.BoostCreature);
public BoostTargetEffect(DynamicValue power, DynamicValue toughness, Duration duration) {
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, CardUtil.getBoostOutcome(power, toughness));
this.power = power;
this.toughness = toughness;
this.lockedIn = lockedIn;
}
public BoostTargetEffect(final BoostTargetEffect effect) {
super(effect);
this.power = effect.power.copy();
this.toughness = effect.toughness.copy();
this.lockedIn = effect.lockedIn;
}
@Override
@ -67,7 +58,7 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (lockedIn) {
if (affectedObjectsSet) {
power = StaticValue.get(power.calculate(game, source, this));
toughness = StaticValue.get(toughness.calculate(game, source, this));
}
@ -92,24 +83,25 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
if (mode == null || mode.getTargets().isEmpty()) {
return "no target";
}
Target target = mode.getTargets().get(0);
StringBuilder sb = new StringBuilder();
if (target.getMaxNumberOfTargets() > 1) {
if (target.getNumberOfTargets() < target.getMaxNumberOfTargets()) {
sb.append("up to ");
}
sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target ").append(target.getTargetName()).append(" get ");
if (mode == null || mode.getTargets().isEmpty()) {
sb.append("it gets ");
} else {
if (target.getNumberOfTargets() < target.getMaxNumberOfTargets()) {
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' ');
Target target = mode.getTargets().get(0);
if (target.getMaxNumberOfTargets() > 1) {
if (target.getNumberOfTargets() < target.getMaxNumberOfTargets()) {
sb.append("up to ");
}
sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target ").append(target.getTargetName()).append(" get ");
} else {
if (target.getNumberOfTargets() < target.getMaxNumberOfTargets()) {
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' ');
}
if (!target.getTargetName().toLowerCase(Locale.ENGLISH).startsWith("another")) {
sb.append("target ");
}
sb.append(target.getTargetName()).append(" gets ");
}
if (!target.getTargetName().toLowerCase(Locale.ENGLISH).startsWith("another")) {
sb.append("target ");
}
sb.append(target.getTargetName()).append(" gets ");
}
sb.append(CardUtil.getBoostText(power, toughness, duration));
return sb.toString();

View file

@ -675,7 +675,7 @@ public final class StaticFilters {
FILTER_PERMANENT_ALL_SLIVERS.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_SAMURAI_OR_WARRIOR = new FilterControlledPermanent("a Samurai or Warrior you control");
public static final FilterControlledPermanent FILTER_CONTROLLED_SAMURAI_OR_WARRIOR = new FilterControlledPermanent("Samurai or Warrior you control");
static {
FILTER_CONTROLLED_SAMURAI_OR_WARRIOR.add(Predicates.or(

View file

@ -901,6 +901,16 @@ public final class CardUtil {
return sb.toString();
}
public static Outcome getBoostOutcome(DynamicValue power, DynamicValue toughness) {
if (toughness.getSign() < 0) {
return Outcome.Removal;
}
if (power.getSign() < 0) {
return Outcome.UnboostCreature;
}
return Outcome.BoostCreature;
}
public static boolean isSpliceAbility(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
return ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLICE;