Skip to content

Commit 921b350

Browse files
authored
CostRemoveAnyCounter: cleanup description (Card-Forge#9997)
* CostRemoveAnyCounter: cleanup description * refactor mapMultiToInt into flatMap
1 parent d20cd52 commit 921b350

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+108
-68
lines changed

forge-ai/src/main/java/forge/ai/AiCostDecision.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,12 @@ public PaymentDecision visit(CostRemoveAnyCounter cost) {
589589
return null;
590590
}
591591

592-
CardCollectionView typeList = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source, ability);
592+
CardCollectionView typeList;
593+
if (cost.payCostFromSource()) {
594+
typeList = new CardCollection(ability.getHostCard());
595+
} else {
596+
typeList = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source, ability);
597+
}
593598
// only cards with counters are of interest
594599
typeList = CardLists.filter(typeList, CardPredicates.hasCounters());
595600

forge-core/src/main/java/forge/util/Lang.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,10 @@ public String getNickName(final String name) {
221221
}
222222

223223
public String buildValidDesc(Collection<String> valid, boolean multiple) {
224-
return joinHomogenous(valid.stream().map(s -> formatValidDesc(s)).collect(Collectors.toList()), null, multiple ? "and/or" : "or");
224+
return buildValidDesc(valid, multiple ? "and/or" : "or");
225+
}
226+
public String buildValidDesc(Collection<String> valid, String join) {
227+
return joinHomogenous(valid.stream().map(s -> formatValidDesc(s)).collect(Collectors.toList()), null, join);
225228
}
226229

227230
public String formatValidDesc(String valid) {

forge-game/src/main/java/forge/game/cost/CostPart.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public final String getTypeDescription() {
106106
return this.typeDescription;
107107
}
108108

109-
public final String getDescriptiveType() {
109+
public String getDescriptiveType() {
110110
String typeDesc = this.getTypeDescription();
111111
if (typeDesc == null) {
112112
String typeS = this.getType();

forge-game/src/main/java/forge/game/cost/CostRemoveAnyCounter.java

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,25 @@
1717
*/
1818
package forge.game.cost;
1919

20+
import forge.card.CardType;
2021
import forge.game.GameEntity;
2122
import forge.game.ability.AbilityUtils;
2223
import forge.game.card.Card;
24+
import forge.game.card.CardCollection;
2325
import forge.game.card.CardCollectionView;
2426
import forge.game.card.CardLists;
2527
import forge.game.card.CounterType;
2628
import forge.game.player.Player;
2729
import forge.game.spellability.SpellAbility;
2830
import forge.game.zone.ZoneType;
31+
import forge.util.Lang;
2932

33+
import java.util.Arrays;
34+
import java.util.Collection;
3035
import java.util.Map;
3136
import java.util.Map.Entry;
3237
import java.util.Optional;
38+
import java.util.stream.Collectors;
3339

3440
/**
3541
* The Class CostRemoveAnyCounter.
@@ -65,24 +71,19 @@ public CostRemoveAnyCounter(final String amount, final CounterType counter, fina
6571
public Integer getMaxAmountX(final SpellAbility ability, final Player payer, final boolean effect) {
6672
final Card source = ability.getHostCard();
6773

68-
CardCollectionView validCards = CardLists.getValidCards(payer.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), payer, source, ability);
69-
int allCounters = 0;
70-
for (Card c : validCards) {
71-
if (this.counter != null) {
72-
if (!c.canRemoveCounters(this.counter)) {
73-
continue;
74-
}
75-
allCounters += c.getCounters(this.counter);
76-
} else {
77-
for (Map.Entry<CounterType, Integer> entry : c.getCounters().entrySet()) {
78-
if (!c.canRemoveCounters(entry.getKey())) {
79-
continue;
80-
}
81-
allCounters += entry.getValue();
82-
}
83-
}
74+
CardCollectionView validCards;
75+
if (payCostFromSource()) {
76+
validCards = new CardCollection(source);
77+
} else {
78+
validCards = CardLists.getValidCards(payer.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), payer, source, ability);
8479
}
85-
return allCounters;
80+
if (this.counter != null) {
81+
return validCards.stream().mapToInt(c -> c.canRemoveCounters(this.counter) ? c.getCounters(this.counter) : 0).sum();
82+
}
83+
// use flatMap instead of mapMulti for Android 13 and below
84+
//https://developer.android.com/reference/java/util/stream/Stream#mapMulti
85+
return validCards.stream().flatMap(c -> c.getCounters().entrySet().stream().filter(e -> c.canRemoveCounters(e.getKey())))
86+
.collect(Collectors.summingInt(e -> e.getValue()));
8687
}
8788

8889
/*
@@ -107,16 +108,24 @@ public final String toString() {
107108
final StringBuilder sb = new StringBuilder();
108109

109110
final String counters = this.counter == null ? "counter" : this.counter.getName().toLowerCase() + " counter";
110-
final String desc = this.getTypeDescription() == null ? this.getType() : this.getTypeDescription();
111+
boolean multiple = !"1".equals(getAmount());
111112

112113
sb.append("Remove ");
113114
if (oneOrMore) {
114-
sb.append("one or more ").append(counters).append("s");
115+
sb.append("one or more ").append(Lang.getPlural(counters));
115116
} else {
116117
sb.append(Cost.convertAmountTypeToWords(this.convertAmount(), this.getAmount(), counters));
117-
sb.append(this.getAmount().equals("1") ? "" : "s");
118118
}
119-
sb.append(" from ").append(desc).append(" you control");
119+
sb.append(" from ");
120+
if (payCostFromSource()) { // TODO use THISTYPE
121+
sb.append(getDescriptiveType(multiple));
122+
} else {
123+
if (multiple) {
124+
sb.append(" among ");
125+
}
126+
sb.append(getDescriptiveType(multiple));
127+
sb.append(" you control");
128+
}
120129

121130
return sb.toString();
122131
}
@@ -138,8 +147,26 @@ public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ab
138147
return true;
139148
}
140149

150+
151+
public String getDescriptiveType(boolean multiple) {
152+
String typeDesc = this.getTypeDescription();
153+
if (typeDesc == null) {
154+
if (payCostFromSource()) {
155+
return getType();
156+
}
157+
Collection<String> types = Arrays.asList(getType().split(";"));
158+
if (multiple)
159+
types = types.stream().map(CardType::getPluralType).collect(Collectors.toList());
160+
typeDesc = Lang.getInstance().buildValidDesc(types, multiple);
161+
}
162+
if (!multiple && !typeDesc.startsWith("an")) { // skip adding to "another"
163+
typeDesc = (Lang.startsWithVowel(typeDesc) ? "an " : "a ") + typeDesc;
164+
}
165+
return typeDesc;
166+
}
167+
168+
@Override
141169
public <T> T accept(ICostVisitor<T> visitor) {
142170
return visitor.visit(this);
143171
}
144-
145172
}

forge-gui/res/cardsfolder/b/brambleback_brute.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ ManaCost:2 R
33
Types:Creature Giant Warrior
44
PT:4/5
55
K:etbCounter:M1M1:2
6-
A:AB$ Pump | Cost$ 1 R RemoveAnyCounter<1/Any/Card.Self/this creature> | ValidTgts$ Creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | SorcerySpeed$ True | SpellDescription$ Target creature can't block this turn. Activate only as a sorcery.
6+
A:AB$ Pump | Cost$ 1 R RemoveAnyCounter<1/Any/CARDNAME/this creature> | ValidTgts$ Creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | SorcerySpeed$ True | SpellDescription$ Target creature can't block this turn. Activate only as a sorcery.
77
Oracle:This creature enters with two -1/-1 counters on it.\n{1}{R}, Remove a counter from this creature: Target creature can't block this turn. Activate only as a sorcery.

forge-gui/res/cardsfolder/b/burdened_stoneback.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ ManaCost:1 W
33
Types:Creature Giant Warrior
44
PT:4/4
55
K:etbCounter:M1M1:2
6-
A:AB$ Pump | Cost$ 1 W RemoveAnyCounter<1/Any/Card.Self/this creature> | ValidTgts$ Creature | KW$ Indestructible | SorcerySpeed$ True | SpellDescription$ Target creature gains indestructible until end of turn. Activate only as a sorcery. (Damage and effects that say "destroy" don't destroy it. If its toughness is 0 or less, it still dies.)
6+
A:AB$ Pump | Cost$ 1 W RemoveAnyCounter<1/Any/CARDNAME/this creature> | ValidTgts$ Creature | KW$ Indestructible | SorcerySpeed$ True | SpellDescription$ Target creature gains indestructible until end of turn. Activate only as a sorcery. (Damage and effects that say "destroy" don't destroy it. If its toughness is 0 or less, it still dies.)
77
Oracle:This creature enters with two -1/-1 counters on it.\n{1}{W}, Remove a counter from this creature: Target creature gains indestructible until end of turn. Activate only as a sorcery. (Damage and effects that say "destroy" don't destroy it. If its toughness is 0 or less, it still dies.)

forge-gui/res/cardsfolder/c/chisei_heart_of_oceans.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Types:Legendary Creature Spirit
44
PT:4/4
55
K:Flying
66
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of your upkeep, sacrifice CARDNAME unless you remove a counter from a permanent you control.
7-
SVar:TrigSac:DB$ Sacrifice | UnlessPayer$ You | UnlessCost$ RemoveAnyCounter<1/Any/Permanent.YouCtrl/a permanent you control>
7+
SVar:TrigSac:DB$ Sacrifice | UnlessPayer$ You | UnlessCost$ RemoveAnyCounter<1/Any/Permanent>
88
SVar:NeedsToPlay:Creature.YouCtrl+HasCounters
99
SVar:AIRemoveCounterCostPriority:ANY
1010
DeckNeeds:Ability$Counters

forge-gui/res/cardsfolder/d/dawnhand_dissident.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ Types:Creature Elf Warlock
44
PT:1/2
55
A:AB$ Surveil | Cost$ T Blight<1> | Amount$ 1 | SpellDescription$ Surveil 1.
66
A:AB$ ChangeZone | Cost$ T Blight<2> | Origin$ Graveyard | Destination$ Exile | TgtPrompt$ Choose target card in a graveyard | ValidTgts$ Card | SpellDescription$ Exile target card from a graveyard.
7-
S:Mode$ Continuous | Affected$ Creature.YouOwn+ExiledWithSource | Condition$ PlayerTurn | MayPlay$ True | EffectZone$ Battlefield | AffectedZone$ Exile | RaiseCost$ RemoveAnyCounter<3/Any/Creature.YouCtrl/among creatures you control> | Description$ During your turn, you may cast creature spells from among cards you own exiled with this creature by removing three counters from among creatures you control in addition to paying their other costs.
7+
S:Mode$ Continuous | Affected$ Creature.YouOwn+ExiledWithSource | Condition$ PlayerTurn | MayPlay$ True | EffectZone$ Battlefield | AffectedZone$ Exile | RaiseCost$ RemoveAnyCounter<3/Any/Creature> | Description$ During your turn, you may cast creature spells from among cards you own exiled with this creature by removing three counters from among creatures you control in addition to paying their other costs.
88
Oracle:{T}, Blight 1: Surveil 1.\n{T}, Blight 2: Exile target card from a graveyard.\nDuring your turn, you may cast creature spells from among cards you own exiled with this creature by removing three counters from among creatures you control in addition to paying their other costs.

forge-gui/res/cardsfolder/d/duchess_wayward_tavernkeep.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ Types:Legendary Creature Human Citizen
44
PT:4/3
55
T:Mode$ DamageDone | ValidSource$ Creature.YouCtrl | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Hunters for Hire — Whenever a creature you control deals combat damage to a player, put a quest counter on it.
66
SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ QUEST | CounterNum$ 1
7-
A:AB$ Token | Cost$ 1 RemoveAnyCounter<1/QUEST/Permanent.YouCtrl/among permanents you control> | TokenScript$ c_a_junk_sac_exileplay | SpellDescription$ Create a Junk token. (It's an artifact with "{T}, Sacrifice this artifact: Exile the top card of your library. You may play that card this turn. Activate only as a sorcery.")
7+
A:AB$ Token | Cost$ 1 RemoveAnyCounter<1/QUEST/Permanent> | TokenScript$ c_a_junk_sac_exileplay | SpellDescription$ Create a Junk token. (It's an artifact with "{T}, Sacrifice this artifact: Exile the top card of your library. You may play that card this turn. Activate only as a sorcery.")
88
DeckHas:Ability$Counters|Token & Type$Junk|Artifact
99
Oracle:Hunters for Hire — Whenever a creature you control deals combat damage to a player, put a quest counter on it.\n{1}, Remove a quest counter from a permanent you control: Create a Junk token. (It's an artifact with "{T}, Sacrifice this artifact: Exile the top card of your library. You may play that card this turn. Activate only as a sorcery.")

forge-gui/res/cardsfolder/f/fain_the_broker.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ManaCost:2 B
33
Types:Legendary Creature Human Warlock
44
PT:3/3
55
A:AB$ PutCounter | Cost$ T Sac<1/Creature> | ValidTgts$ Creature | CounterType$ P1P1 | CounterNum$ 2 | SpellDescription$ Put two +1/+1 counters on target creature.
6-
A:AB$ Token | Cost$ T RemoveAnyCounter<1/Any/Creature.YouCtrl/a creature you control> | TokenScript$ c_a_treasure_sac | SpellDescription$ Create a Treasure token.
6+
A:AB$ Token | Cost$ T RemoveAnyCounter<1/Any/Creature> | TokenScript$ c_a_treasure_sac | SpellDescription$ Create a Treasure token.
77
A:AB$ Token | Cost$ T Sac<1/Artifact> | TokenScript$ wb_2_1_inkling_flying | SpellDescription$ Create a 2/1 white and black Inkling creature token with flying.
88
A:AB$ Untap | Cost$ 3 B | SpellDescription$ Untap CARDNAME.
99
DeckHas:Ability$Sacrifice|Counters|Token

0 commit comments

Comments
 (0)