Fix #9649 and clean up counter effect text generation

This commit is contained in:
Alex W. Jackson 2022-10-14 00:41:02 -04:00
parent 5ec2cd0378
commit 332db3aecb
17 changed files with 117 additions and 374 deletions

View file

@ -46,6 +46,10 @@ class DustOfMomentsEffect extends OneShotEffect {
private static final FilterPermanentOrSuspendedCard filter = new FilterPermanentOrSuspendedCard();
private final boolean remove;
static {
filter.getPermanentFilter().add(CounterType.TIME.getPredicate());
}
DustOfMomentsEffect(boolean remove) {
super(Outcome.Benefit);
this.remove = remove;

View file

@ -180,7 +180,6 @@ public class NestOfScarabsTest extends CardTestPlayerBase {
}
/*
Reported bug: Nest of Scarabs not triggering off wither damage dealt by creatures such as Sickle Ripper
*/
@Test
@ -204,4 +203,48 @@ public class NestOfScarabsTest extends CardTestPlayerBase {
assertCounterCount(playerB, wOmens, CounterType.M1M1, 2);
assertPermanentCount(playerA, "Insect Token", 2);
}
/*
https://github.com/magefree/mage/issues/9649
*/
@Test
public void scarabs_ETBWithCountersTriggers() {
String hatchling = "Noxious Hatchling"; // ETB with four -1/-1 counters
addCard(Zone.BATTLEFIELD, playerA, nestScarabs);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.HAND, playerA, hatchling);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hatchling);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nestScarabs, 1);
assertPermanentCount(playerA, hatchling, 1);
assertPermanentCount(playerA, "Insect Token", 4);
}
@Test
public void scarabs_OpponentETBWithCountersNoTriggers() {
String hatchling = "Noxious Hatchling"; // ETB with four -1/-1 counters
addCard(Zone.BATTLEFIELD, playerB, nestScarabs);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.HAND, playerA, hatchling);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hatchling);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerB, nestScarabs, 1);
assertPermanentCount(playerA, hatchling, 1);
assertPermanentCount(playerA, "Insect Token", 0);
assertPermanentCount(playerB, "Insect Token", 0);
}
}

View file

@ -74,6 +74,9 @@ public class PutCounterOnCreatureTriggeredAbility extends TriggeredAbilityImpl {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId());
}
if (permanent == null || !filter.match(permanent, controllerId, this, game)) {
return false;
}

View file

@ -11,7 +11,6 @@ import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import mage.util.RandomUtil;
import java.util.Iterator;
@ -27,19 +26,15 @@ public class RemoveCountersSourceCost extends CostImpl {
private final String name;
public RemoveCountersSourceCost() {
this((Counter) null);
this.amount = 1;
this.name = "";
this.text = "remove a counter from {this}";
}
public RemoveCountersSourceCost(Counter counter) {
this.amount = counter != null ? counter.getCount() : 1;
this.name = counter != null ? counter.getName() : "";
this.text = new StringBuilder("remove ")
.append((amount == 1 ? CounterType.findArticle(name) : CardUtil.numberToText(amount)))
.append(name.isEmpty() ? "" : (' ' + name))
.append(" counter")
.append((amount != 1 ? "s" : ""))
.append(" from {this}").toString();
this.amount = counter.getCount();
this.name = counter.getName();
this.text = "remove " + counter.getDescription() + " from {this}";
}
private RemoveCountersSourceCost(RemoveCountersSourceCost cost) {

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common.counter;
import mage.MageObject;
@ -6,14 +5,10 @@ import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.Locale;
/**
* @author North
@ -27,7 +22,7 @@ public class AddCountersAllEffect extends OneShotEffect {
super(Outcome.Benefit);
this.counter = counter;
this.filter = filter;
setText();
staticText = "put " + counter.getDescription() + " on each " + filter.getMessage();
}
public AddCountersAllEffect(final AddCountersAllEffect effect) {
@ -45,7 +40,7 @@ public class AddCountersAllEffect extends OneShotEffect {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) {
permanent.addCounters(counter.copy(), source.getControllerId(), source, game);
if (!game.isSimulation()) {
game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " puts " + counter.getCount() + ' ' + counter.getName().toLowerCase(Locale.ENGLISH)
game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " puts " + counter.getCount() + ' ' + counter.getName()
+ " counter on " + permanent.getLogName());
}
}
@ -55,18 +50,6 @@ public class AddCountersAllEffect extends OneShotEffect {
return false;
}
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append("put ");
if (counter.getCount() > 1) {
sb.append(CardUtil.numberToText(counter.getCount(), "a")).append(' ').append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counters on each ");
} else {
sb.append(CounterType.findArticle(counter.getName())).append(' ').append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counter on each ");
}
sb.append(filter.getMessage());
staticText = sb.toString();
}
@Override
public AddCountersAllEffect copy() {
return new AddCountersAllEffect(this);

View file

@ -6,13 +6,10 @@ import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
import java.util.Locale;
/**
* @author LevelX2
*/
@ -20,7 +17,6 @@ public class AddCountersAttachedEffect extends OneShotEffect {
private Counter counter;
private DynamicValue amount;
private String textEnchanted;
public AddCountersAttachedEffect(Counter counter, String textEnchanted) {
this(counter, StaticValue.get(1), textEnchanted);
@ -35,8 +31,7 @@ public class AddCountersAttachedEffect extends OneShotEffect {
super(Outcome.Benefit);
this.counter = counter.copy();
this.amount = amount;
this.textEnchanted = textEnchanted;
setText();
staticText = CardUtil.getAddRemoveCountersText(amount, counter, textEnchanted, true);
}
public AddCountersAttachedEffect(final AddCountersAttachedEffect effect) {
@ -45,7 +40,6 @@ public class AddCountersAttachedEffect extends OneShotEffect {
this.counter = effect.counter.copy();
}
this.amount = effect.amount;
this.textEnchanted = effect.textEnchanted;
}
@Override
@ -67,38 +61,8 @@ public class AddCountersAttachedEffect extends OneShotEffect {
return false;
}
private void setText() {
if (!staticText.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder();
// put a +1/+1 counter on it
sb.append("put ");
boolean plural = true;
if (amount.toString().equals("X")) {
sb.append("X ");
} else if (counter.getCount() > 1) {
sb.append(CardUtil.numberToText(counter.getCount())).append(' ');
} else {
sb.append(CounterType.findArticle(counter.getName())).append(' ');
plural = false;
}
sb.append(counter.getName().toLowerCase(Locale.ENGLISH));
if (plural) {
sb.append(" counters on ");
} else {
sb.append(" counter on ");
}
sb.append(textEnchanted);
if (!amount.getMessage().isEmpty()) {
sb.append(" for each ").append(amount.getMessage());
}
staticText = sb.toString();
}
@Override
public AddCountersAttachedEffect copy() {
return new AddCountersAttachedEffect(this);
}
}

View file

@ -1,11 +1,9 @@
package mage.abilities.effects.common.counter;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -30,7 +28,7 @@ public class AddCountersControllerEffect extends OneShotEffect {
super(Outcome.Benefit);
this.counter = counter.copy();
this.enchantedEquipped = enchantedEquipped;
setText();
staticText = "its controller gets " + counter.getDescription();
}
public AddCountersControllerEffect(final AddCountersControllerEffect effect) {
@ -66,16 +64,6 @@ public class AddCountersControllerEffect extends OneShotEffect {
return false;
}
private void setText() {
if (counter.getCount() > 1) {
StringBuilder sb = new StringBuilder();
sb.append("its controller gets ").append(Integer.toString(counter.getCount())).append(' ').append(counter.getName()).append(" counters");
staticText = sb.toString();
} else {
staticText = "its controller gets " + CounterType.findArticle(counter.getName()) + " " + counter.getName() + " counter";
}
}
@Override
public AddCountersControllerEffect copy() {
return new AddCountersControllerEffect(this);

View file

@ -8,7 +8,6 @@ import mage.cards.Card;
import mage.constants.AbilityType;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -16,7 +15,6 @@ import mage.util.CardUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
/**
@ -54,7 +52,7 @@ public class AddCountersSourceEffect extends OneShotEffect {
this.informPlayers = informPlayers;
this.amount = amount;
this.putOnCard = putOnCard;
setText();
staticText = CardUtil.getAddRemoveCountersText(amount, counter, "{this}", true);
}
public AddCountersSourceEffect(final AddCountersSourceEffect effect) {
@ -95,7 +93,7 @@ public class AddCountersSourceEffect extends OneShotEffect {
if (informPlayers && !game.isSimulation()) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
game.informPlayers(player.getLogName() + " puts " + newCounter.getCount() + ' ' + newCounter.getName().toLowerCase(Locale.ENGLISH) + " counter on " + card.getLogName());
game.informPlayers(player.getLogName() + " puts " + newCounter.getCount() + ' ' + newCounter.getName() + " counter on " + card.getLogName());
}
}
return true;
@ -124,7 +122,7 @@ public class AddCountersSourceEffect extends OneShotEffect {
int amountAdded = permanent.getCounters(game).getCount(newCounter.getName()) - before;
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
game.informPlayers(player.getLogName() + " puts " + amountAdded + ' ' + newCounter.getName().toLowerCase(Locale.ENGLISH) + " counter on " + permanent.getLogName());
game.informPlayers(player.getLogName() + " puts " + amountAdded + ' ' + newCounter.getName() + " counter on " + permanent.getLogName());
}
}
}
@ -133,32 +131,8 @@ public class AddCountersSourceEffect extends OneShotEffect {
return true;
}
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append("put ");
boolean plural = true;
if (counter.getCount() > 1) {
sb.append(CardUtil.numberToText(counter.getCount())).append(' ');
} else if (amount.toString().equals("X") && amount.getMessage().isEmpty()) {
sb.append("X ");
} else {
sb.append(CounterType.findArticle(counter.getName())).append(' ');
plural = false;
}
sb.append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counter");
if (plural) {
sb.append('s');
}
sb.append(" on {this}");
if (!amount.getMessage().isEmpty()) {
sb.append(" for each ").append(amount.getMessage());
}
staticText = sb.toString();
}
@Override
public AddCountersSourceEffect copy() {
return new AddCountersSourceEffect(this);
}
}

View file

@ -13,10 +13,8 @@ import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.Locale;
import java.util.UUID;
/**
@ -79,17 +77,17 @@ public class AddCountersTargetEffect extends OneShotEffect {
permanent.addCounters(newCounter, source.getControllerId(), source, game);
affectedTargets++;
game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " puts "
+ newCounter.getCount() + ' ' + newCounter.getName().toLowerCase(Locale.ENGLISH) + " counters on " + permanent.getLogName());
+ newCounter.getCount() + ' ' + newCounter.getName() + " counters on " + permanent.getLogName());
} else if (player != null) {
player.addCounters(newCounter, source.getControllerId(), source, game);
affectedTargets++;
game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " puts "
+ newCounter.getCount() + ' ' + newCounter.getName().toLowerCase(Locale.ENGLISH) + " counters on " + player.getLogName());
+ newCounter.getCount() + ' ' + newCounter.getName() + " counters on " + player.getLogName());
} else if (card != null) {
card.addCounters(newCounter, source.getControllerId(), source, game);
affectedTargets++;
game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " puts "
+ newCounter.getCount() + ' ' + newCounter.getName().toLowerCase(Locale.ENGLISH) + " counters on " + card.getLogName());
+ newCounter.getCount() + ' ' + newCounter.getName() + " counters on " + card.getLogName());
}
}
return affectedTargets > 0;
@ -102,51 +100,11 @@ public class AddCountersTargetEffect extends OneShotEffect {
if (!staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder();
sb.append("put ");
String counterName = counter.getName().toLowerCase(Locale.ENGLISH);
if (counter.getCount() > 1) {
sb.append(CardUtil.numberToText(counter.getCount())).append(' ');
} else {
sb.append(CounterType.findArticle(counterName)).append(' ');
}
sb.append(counterName).append(" counter");
if (counter.getCount() > 1) {
sb.append('s');
}
sb.append(" on ");
Target target = mode.getTargets().getEffectTarget(this.targetPointer);
if (target != null) {
if (target.getNumberOfTargets() == 0) {
if (target.getMaxNumberOfTargets() > 1) {
sb.append("each of ");
}
sb.append("up to ");
}
if (target.getMaxNumberOfTargets() > 1 || target.getNumberOfTargets() == 0) {
sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets()))
.append(" target ").append(target.getTargetName());
} else {
if (!target.getTargetName().startsWith("another")) {
sb.append("target ");
}
sb.append(target.getTargetName());
}
} else {
sb.append("that creature");
}
if (!amount.getMessage().isEmpty()) {
sb.append(" for each ").append(amount.getMessage());
}
return sb.toString();
return CardUtil.getAddRemoveCountersText(amount, counter, getTargetPointer().describeTargets(mode.getTargets(), "that creature"), true);
}
@Override
public AddCountersTargetEffect copy() {
return new AddCountersTargetEffect(this);
}
}

View file

@ -1,101 +0,0 @@
package mage.abilities.effects.common.counter;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.filter.Filter;
import mage.game.Game;
import mage.players.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* @author Gal Lerman
*/
public class AddRemoveAllTimeSuspentCountersEffect extends OneShotEffect {
private final Counter counter;
private final Filter<Card> filter;
private final boolean removeCounter;
private final String actionStr;
public AddRemoveAllTimeSuspentCountersEffect(Counter counter, Filter<Card> filter, boolean removeCounter) {
super(Outcome.Benefit);
this.counter = counter;
this.filter = filter;
this.removeCounter = removeCounter;
actionStr = removeCounter ? " removes " : " puts ";
setText();
}
public AddRemoveAllTimeSuspentCountersEffect(final AddRemoveAllTimeSuspentCountersEffect effect) {
super(effect);
this.counter = effect.counter.copy();
this.filter = effect.filter.copy();
this.removeCounter = effect.removeCounter;
this.actionStr = effect.actionStr;
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source);
if (controller != null && sourceObject != null) {
if (counter != null) {
List<Card> permanents = new ArrayList<>(game.getBattlefield().getAllActivePermanents());
execute(game, controller, sourceObject, source, permanents, removeCounter);
final List<Card> exiledCards = game.getExile().getAllCards(game);
execute(game, controller, sourceObject, source, exiledCards, removeCounter);
}
return true;
}
return false;
}
private void execute(final Game game, final Player controller, final MageObject sourceObject, Ability source, final List<Card> cards, final boolean removeCounter) {
for (Card card : cards) {
if (filter.match(card, game)) {
final String counterName = counter.getName();
if (removeCounter) {
final Counter existingCounterOfSameType = card.getCounters(game).get(counterName);
final int countersToRemove = Math.min(existingCounterOfSameType.getCount(), counter.getCount());
final Counter modifiedCounter = new Counter(counterName, countersToRemove);
card.removeCounters(modifiedCounter, source, game);
} else {
card.addCounters(counter, source.getControllerId(), source, game);
}
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(sourceObject.getName()).append(": ")
.append(controller.getLogName()).append(actionStr)
.append(counter.getCount()).append(' ').append(counterName.toLowerCase(Locale.ENGLISH))
.append(" counter on ").append(card.getName()).toString());
}
}
}
}
private void setText() {
StringBuilder sb = new StringBuilder();
final String actionsStr2 = removeCounter ? "remove " : " put ";
sb.append(actionsStr2);
if (counter.getCount() > 1) {
sb.append(Integer.toString(counter.getCount())).append(' ').append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counters on each ");
} else {
sb.append(CounterType.findArticle(counter.getName())).append(' ').append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counter on each ");
}
sb.append(filter.getMessage());
staticText = sb.toString();
}
@Override
public AddRemoveAllTimeSuspentCountersEffect copy() {
return new AddRemoveAllTimeSuspentCountersEffect(this);
}
}

View file

@ -5,10 +5,8 @@ import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
/**
* @author Loki
@ -20,7 +18,7 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
public RemoveCounterSourceEffect(Counter counter) {
super(Outcome.UnboostCreature);
this.counter = counter;
setText();
staticText = "remove " + counter.getDescription() + " from {this}";
}
public RemoveCounterSourceEffect(RemoveCounterSourceEffect effect) {
@ -63,14 +61,4 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
public RemoveCounterSourceEffect copy() {
return new RemoveCounterSourceEffect(this);
}
private void setText() {
if (counter.getCount() > 1) {
StringBuilder sb = new StringBuilder();
sb.append("remove ").append(CardUtil.numberToText(counter.getCount())).append(' ').append(counter.getName()).append(" counters from {this}");
staticText = sb.toString();
} else {
staticText = "remove " + CounterType.findArticle(counter.getName()) + " " + counter.getName() + " counter from {this}";
}
}
}

View file

@ -8,11 +8,9 @@ import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
@ -41,7 +39,7 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent p = game.getPermanent(targetPointer.getFirst(game, source));
Permanent p = game.getPermanent(getTargetPointer().getFirst(game, source));
if (p != null) {
Counter toRemove = (counter == null ? selectCounterType(game, source, p) : counter);
if (toRemove != null && p.getCounters(game).getCount(toRemove.getName()) >= toRemove.getCount()) {
@ -52,7 +50,7 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
}
}
} else {
Card c = game.getCard(targetPointer.getFirst(game, source));
Card c = game.getCard(getTargetPointer().getFirst(game, source));
if (c != null && counter != null && c.getCounters(game).getCount(counter.getName()) >= counter.getCount()) {
c.removeCounters(counter.getName(), counter.getCount(), source, game);
if (!game.isSimulation()) {
@ -106,15 +104,9 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
String text = "remove ";
if (counter == null) {
text += "a counter";
} else {
text += CardUtil.numberToText(counter.getCount(), CounterType.findArticle(counter.getName())) + ' ' + counter.getName();
text += counter.getCount() > 1 ? " counters" : " counter";
}
text += " from target " + (mode.getTargets().isEmpty() ? " object" : mode.getTargets().get(0).getTargetName());
return text;
return "remove "
+ (counter == null ? "a counter" : counter.getDescription())
+ " from "
+ getTargetPointer().describeTargets(mode.getTargets(), "that creature");
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common.counter;
import mage.abilities.Ability;
@ -7,13 +6,10 @@ import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
import java.util.Locale;
/**
* @author noahg
*/
@ -21,7 +17,6 @@ public class RemoveCountersAttachedEffect extends OneShotEffect {
private Counter counter;
private DynamicValue amount;
private String textEnchanted;
public RemoveCountersAttachedEffect(Counter counter, String textEnchanted) {
this(counter, StaticValue.get(0), textEnchanted);
@ -36,8 +31,7 @@ public class RemoveCountersAttachedEffect extends OneShotEffect {
super(Outcome.UnboostCreature);
this.counter = counter.copy();
this.amount = amount;
this.textEnchanted = textEnchanted;
setText();
this.staticText = CardUtil.getAddRemoveCountersText(amount, counter, textEnchanted, false);
}
public RemoveCountersAttachedEffect(final RemoveCountersAttachedEffect effect) {
@ -46,7 +40,6 @@ public class RemoveCountersAttachedEffect extends OneShotEffect {
this.counter = effect.counter.copy();
}
this.amount = effect.amount;
this.textEnchanted = effect.textEnchanted;
}
@Override
@ -64,27 +57,8 @@ public class RemoveCountersAttachedEffect extends OneShotEffect {
return false;
}
private void setText() {
StringBuilder sb = new StringBuilder();
// put a +1/+1 counter on it
sb.append("remove ");
if (counter.getCount() > 1) {
sb.append(CardUtil.numberToText(counter.getCount())).append(' ');
sb.append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counters from ");
} else {
sb.append(CounterType.findArticle(counter.getName())).append(' ');
sb.append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counter from ");
}
sb.append(textEnchanted);
if (!amount.getMessage().isEmpty()) {
sb.append(" for each ").append(amount.getMessage());
}
staticText = sb.toString();
}
@Override
public RemoveCountersAttachedEffect copy() {
return new RemoveCountersAttachedEffect(this);
}
}

View file

@ -1,6 +1,7 @@
package mage.counters;
import mage.util.CardUtil;
import java.io.Serializable;
import org.apache.log4j.Logger;
@ -56,20 +57,6 @@ public class Counter implements Serializable {
count += amount;
}
/**
* Decreases the {@code count} by one. Will not allow the count to be less
* than 0. If an attempt is made to make the count be less than zero, the
* call will be logged.
*/
public void decrease() {
if (count > 0) {
count--;
} else {
logger.warn("An attempt was made to set the counter '" + name
+ "' to less than 0. Setting to 0.");
}
}
/**
* Decreases the {@code count} by the passed in {@code amount}. Will not
* allow the count to be less than 0. If an attempt is made to make the
@ -105,6 +92,15 @@ public class Counter implements Serializable {
return count;
}
/**
* Returns a full description of this {@link Counter}, e.g. "a +1/+1 counter" or "two -1/-1 counters"
*
* @return a full description of this {@link Counter}
*/
public String getDescription() {
return CardUtil.numberToText(Math.max(count, 1), CounterType.findArticle(name)) + ' ' + name + (count > 1 ? " counters" : " counter");
}
/**
* Returns a deep copy of this object.
*

View file

@ -5,6 +5,9 @@ import mage.cards.Card;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import java.util.HashMap;
import java.util.Map;
/**
* Enum for counters, names and instances.
*
@ -205,6 +208,14 @@ public enum CounterType {
private final String article;
private final CounterPredicate predicate;
private static final Map<String, CounterType> counterNameMap = new HashMap<>();
static {
for (CounterType counter : CounterType.values()) {
counterNameMap.put(counter.name, counter);
}
}
CounterType(String name) {
this(name, "aeiou".contains("" + name.charAt(0)) ? "an" : "a");
}
@ -301,12 +312,7 @@ public enum CounterType {
}
public static CounterType findByName(String name) {
for (CounterType counterType : values()) {
if (counterType.getName().equals(name)) {
return counterType;
}
}
return null;
return counterNameMap.get(name);
}
public static String findArticle(String name) {

View file

@ -5,7 +5,6 @@ import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.targetpointer.*;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
@ -17,8 +16,6 @@ import java.util.stream.Collectors;
*/
public class Targets extends ArrayList<Target> {
private static final Logger logger = Logger.getLogger(Targets.class);
public Targets(Target... targets) {
for (Target target : targets) {
this.add(target);
@ -147,42 +144,6 @@ public class Targets extends ArrayList<Target> {
return null;
}
public Target getEffectTarget(TargetPointer targetPointer) {
boolean proccessed = false;
if (targetPointer instanceof FirstTargetPointer) {
proccessed = true;
if (this.size() > 0) {
return this.get(0);
}
}
if (targetPointer instanceof SecondTargetPointer) {
proccessed = true;
if (this.size() > 1) {
return this.get(1);
}
}
if (targetPointer instanceof ThirdTargetPointer) {
proccessed = true;
if (this.size() > 2) {
return this.get(2);
}
}
if (targetPointer instanceof FixedTarget || targetPointer instanceof FixedTargets) {
// fixed target = direct ID, you can't find target type and description
proccessed = true;
}
if (!proccessed) {
logger.error("Unknown target pointer " + (targetPointer != null ? targetPointer : "null"), new Throwable());
}
return null;
}
public Targets copy() {
return new Targets(this);
}

View file

@ -870,10 +870,10 @@ public final class CardUtil {
String p = power.toString();
String t = toughness.toString();
if (!p.startsWith("-")) {
p = (t.startsWith("-") && p.equals("0") ? "-" : "+") + p;
p = t.startsWith("-") && p.equals("0") ? "-0" : "+" + p;
}
if (!t.startsWith("-")) {
t = (p.startsWith("-") && t.equals("0") ? "-" : "+") + t;
t = p.startsWith("-") && t.equals("0") ? "-0" : "+" + t;
}
return p + "/" + t;
}
@ -889,7 +889,7 @@ public final class CardUtil {
if (duration != Duration.EndOfGame) {
String d = duration.toString();
if (!d.isEmpty()) {
sb.append(" ").append(d);
sb.append(' ').append(d);
}
}
String message = power.getMessage();
@ -912,6 +912,21 @@ public final class CardUtil {
return Outcome.BoostCreature;
}
public static String getAddRemoveCountersText(DynamicValue amount, Counter counter, String description, boolean add) {
StringBuilder sb = new StringBuilder(add ? "put " : "remove ");
boolean xValue = amount.toString().equals("X");
if (xValue) {
sb.append("X ").append(counter.getName()).append(" counters");
} else {
sb.append(counter.getDescription());
}
sb.append(add ? " on " : " from ").append(description);
if (!amount.getMessage().isEmpty()) {
sb.append(xValue ? ", where X is " : " for each ").append(amount.getMessage());
}
return sb.toString();
}
public static boolean isFusedPartAbility(Ability ability, Game game) {
// TODO: does it work fine with copies of spells on stack?
if (!(ability instanceof SpellAbility)) {