CounterPutEffect: make counterType support , seperator

This commit is contained in:
Hans Mackowiak
2022-03-08 07:32:44 +01:00
parent 0ea1795ef3
commit d283b03e69
4 changed files with 225 additions and 192 deletions

View File

@@ -45,7 +45,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
final StringBuilder stringBuilder = new StringBuilder();
final Card card = spellAbility.getHostCard();
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"),
spellAbility);
// skip the StringBuilder if no targets are chosen ("up to" scenario)
if (spellAbility.usesTargeting()) {
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
@@ -70,21 +71,18 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
final String typeName = CounterType.getType(spellAbility.getParam("CounterType")).getName().toLowerCase();
stringBuilder.append(Lang.nounWithNumeralExceptOne(amount,
typeName + " counter"));
stringBuilder.append(divAsChoose || spellAbility.hasParam("DividedRandomly")
? " among " : " on ");
stringBuilder.append(Lang.nounWithNumeralExceptOne(amount, typeName + " counter"));
stringBuilder.append(divAsChoose || spellAbility.hasParam("DividedRandomly") ? " among " : " on ");
// special handling for multiple Defined
if (spellAbility.hasParam("Defined") && spellAbility.getParam("Defined").contains(" & ")) {
String[] def = spellAbility.getParam("Defined").split(" & ");
for (int i = 0; i < def.length; i++) {
stringBuilder.append(AbilityUtils.getDefinedEntities(card, def[i],
spellAbility).toString().replaceAll("[\\[\\]]",""));
stringBuilder.append(AbilityUtils.getDefinedEntities(card, def[i], spellAbility).toString()
.replaceAll("[\\[\\]]", ""));
if (i + 1 < def.length) {
stringBuilder.append(" and ");
stringBuilder.append(Lang.nounWithNumeralExceptOne(amount,
typeName + " counter")).append(" on ");
stringBuilder.append(Lang.nounWithNumeralExceptOne(amount, typeName + " counter")).append(" on ");
}
}
// if use targeting we show all targets and corresponding counters
@@ -99,8 +97,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (i == targetCards.size() - 2) {
stringBuilder.append(" and ");
}
else if(i + 1 < targetCards.size()) {
} else if (i + 1 < targetCards.size()) {
stringBuilder.append(", ");
}
}
@@ -124,7 +121,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
return stringBuilder.toString();
}
protected void resolvePerType(SpellAbility sa, final Player placer, CounterType counterType, int counterAmount, GameEntityCounterTable table) {
protected void resolvePerType(SpellAbility sa, final Player placer, CounterType counterType, int counterAmount,
GameEntityCounterTable table) {
final Card card = sa.getHostCard();
final Game game = card.getGame();
final Player activator = sa.getActivatingPlayer();
@@ -139,12 +137,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
int divrem = 0;
if (sa.hasParam("Bolster")) {
CardCollection creatsYouCtrl = activator.getCreaturesInPlay();
CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense));
CardCollection leastToughness = new CardCollection(
Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense));
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
Iterables.addAll(tgtObjects, activator.getController().chooseCardsForEffect(leastToughness, sa, Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false, params));
Iterables.addAll(tgtObjects, activator.getController().chooseCardsForEffect(leastToughness, sa,
Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false, params));
} else if (sa.hasParam("Choices")) {
ZoneType choiceZone = ZoneType.Battlefield;
if (sa.hasParam("ChoiceZone")) {
@@ -159,17 +159,17 @@ public class CountersPutEffect extends SpellAbilityEffect {
chooser = choosers.get(0);
}
int n = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("ChoiceAmount",
"1"), sa);
int m = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("MinChoiceAmount",
sa.getParamOrDefault("ChoiceAmount", "1")), sa);
int n = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("ChoiceAmount", "1"), sa);
int m = AbilityUtils.calculateAmount(card,
sa.getParamOrDefault("MinChoiceAmount", sa.getParamOrDefault("ChoiceAmount", "1")), sa);
// no choices allowed
if (n <= 0) {
return;
}
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"), activator, card, sa);
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"),
activator, card, sa);
String title = Localizer.getInstance().getMessage("lblChooseaCard") + " ";
if (sa.hasParam("ChoiceTitle")) {
@@ -198,8 +198,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
}
if (sa.hasParam("Optional") && !pc.confirmAction
(sa, null, Localizer.getInstance().getMessage("lblDoYouWantPutCounter"))) {
if (sa.hasParam("Optional")
&& !pc.confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantPutCounter"))) {
return;
}
@@ -235,7 +235,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
}
game.fireEvent(new GameEventRandomLog(logOutput(randomMap, card)));
} else for (final GameEntity obj : tgtObjects) {
} else {
for (final GameEntity obj : tgtObjects) {
// check if the object is still in game or if it was moved
Card gameCard = null;
if (obj instanceof Card) {
@@ -294,35 +295,53 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
continue;
}
if (sa.hasParam("CounterTypePerDefined")) {
List<CounterType> choices = Lists.newArrayList();
for (String s : sa.getParam("CounterType").split(",")) {
choices.add(CounterType.getType(s));
}
Map<String, Object> params = Maps.newHashMap();
params.put("Target", obj);
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblSelectCounterTypeAddTo") + " ");
sb.append(obj);
counterType = pc.chooseCounterType(choices, sa, sb.toString(), params);
}
if (obj instanceof Card) {
counterAmount = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(gameCard) : counterAmount;
counterAmount = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(gameCard)
: counterAmount;
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
if (max != -1) {
counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), 0);
counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount),
0);
}
if (sa.hasParam("UpTo")) {
Map<String, Object> params = Maps.newHashMap();
params.put("Target", obj);
params.put("CounterType", counterType);
counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params);
counterAmount = pc.chooseNumber(sa,
Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params);
}
if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
Map<String, Object> params = Maps.newHashMap();
params.put("Target", obj);
params.put("CounterType", counterType);
divrem++;
if (divrem == tgtObjects.size() || counterRemain == 1) { counterAmount = counterRemain; }
else {
counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage
("lblHowManyCountersThis", CardTranslation.getTranslatedName(gameCard.getName())),
if (divrem == tgtObjects.size() || counterRemain == 1) {
counterAmount = counterRemain;
} else {
counterAmount = pc.chooseNumber(sa,
Localizer.getInstance().getMessage("lblHowManyCountersThis",
CardTranslation.getTranslatedName(gameCard.getName())),
1, counterRemain, params);
}
}
// Adapt need extra logic
if (sa.hasParam("Adapt")) {
if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0 || StaticAbilityAdapt.anyWithAdapt(sa, gameCard))) {
if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0
|| StaticAbilityAdapt.anyWithAdapt(sa, gameCard))) {
continue;
}
}
@@ -357,8 +376,11 @@ public class CountersPutEffect extends SpellAbilityEffect {
params.put("Amount", counterAmount);
params.put("Target", gameCard);
String message = Localizer.getInstance().getMessage("lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount), CardTranslation.getTranslatedName(gameCard.getName()));
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseAnOpponent"), params);
String message = Localizer.getInstance().getMessage(
"lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount),
CardTranslation.getTranslatedName(gameCard.getName()));
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa,
Localizer.getInstance().getMessage("lblChooseAnOpponent"), params);
if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) {
gameCard.setTributed(true);
@@ -374,7 +396,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
if (sa.hasParam("Evolve")) {
game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard), false);
game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard),
false);
}
if (sa.hasParam("Monstrosity")) {
gameCard.setMonstrous(true);
@@ -384,13 +407,16 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
if (sa.hasParam("Renown")) {
gameCard.setRenowned(true);
game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, AbilityKey.mapFromCard(gameCard), false);
game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned,
AbilityKey.mapFromCard(gameCard), false);
}
if (sa.hasParam("Adapt")) {
game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), false);
game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard),
false);
}
if (sa.hasParam("Training")) {
game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard), false);
game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard),
false);
}
game.updateLastStateForCard(gameCard);
@@ -405,6 +431,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
}
}
}
@Override
public void resolve(SpellAbility sa) {
@@ -427,23 +454,33 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (sa.hasParam("TriggeredCounterMap")) {
@SuppressWarnings("unchecked")
Map<CounterType, Integer> counterMap = (Map<CounterType, Integer>) sa.getTriggeringObject(AbilityKey.CounterMap);
Map<CounterType, Integer> counterMap = (Map<CounterType, Integer>) sa
.getTriggeringObject(AbilityKey.CounterMap);
for (Map.Entry<CounterType, Integer> e : counterMap.entrySet()) {
resolvePerType(sa, placer, e.getKey(), e.getValue(), table);
}
} else if (sa.hasParam("SharedKeywords")) {
List<String> keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & "));
List<ZoneType> zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone"));
String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[]{"Card"};
String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",")
: new String[] { "Card" };
keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, card, sa);
for (String k : keywords) {
resolvePerType(sa, placer, CounterType.getType(k), counterAmount, table);
}
} else {
CounterType counterType = null;
if (!sa.hasParam("EachExistingCounter") && !sa.hasParam("EachFromSource")) {
if (!sa.hasParam("EachExistingCounter") && !sa.hasParam("EachFromSource")
&& !sa.hasParam("CounterTypePerDefined")) {
try {
counterType = CounterType.getType(sa.getParam("CounterType"));
List<CounterType> choices = Lists.newArrayList();
for (String s : sa.getParam("CounterType").split(",")) {
choices.add(CounterType.getType(s));
}
Map<String, Object> params = Maps.newHashMap();
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblSelectCounterTypeAddTo"));
counterType = placer.getController().chooseCounterType(choices, sa, sb.toString(), params);
} catch (Exception e) {
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
return;
@@ -473,19 +510,24 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
}
protected void addRemovePhaseTrigger(final Card host, final SpellAbility sa, String phase, GameEntity tgt, CounterType ct, int added) {
protected void addRemovePhaseTrigger(final Card host, final SpellAbility sa, String phase, GameEntity tgt,
CounterType ct, int added) {
boolean intrinsic = sa.isIntrinsic();
StringBuilder delTrig = new StringBuilder("Mode$ Phase | Phase$ ");
delTrig.append(phase);
delTrig.append(" | TriggerDescription$ For each ").append(ct.getName()).append(" counter you put on a creature this way, remove a ").append(ct.getName()).append(" counter from that creature at the beginning of the next");
delTrig.append(" | TriggerDescription$ For each ").append(ct.getName())
.append(" counter you put on a creature this way, remove a ").append(ct.getName())
.append(" counter from that creature at the beginning of the next");
if ("Cleanup".equals(phase)) {
delTrig.append("cleanup step");
} else if ("End of Turn".equals(phase)) {
delTrig.append("next end step");
}
String trigSA = new StringBuilder("DB$ RemoveCounter | Defined$ DelayTriggerRemembered | CounterNum$ 1 | CounterType$ ").append(ct).toString();
String trigSA = new StringBuilder(
"DB$ RemoveCounter | Defined$ DelayTriggerRemembered | CounterNum$ 1 | CounterType$ ").append(ct)
.toString();
// these trigger are one per counter
for (int i = 0; i < added; i++) {

View File

@@ -1,9 +1,7 @@
Name:Dismantle
ManaCost:2 R
Types:Sorcery
A:SP$ Destroy | Cost$ 2 R | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SubAbility$ DBChoice | SpellDescription$ Destroy target artifact. If that artifact had counters on it, put that many +1/+1 counters or charge counters on an artifact you control.
SVar:DBChoice:DB$ GenericChoice | Choices$ DBPutP1P1,DBPutCharge | ConditionDefined$ Targeted | ConditionPresent$ Card.HasCounters | ConditionCompare$ GE1 | StackDescription$ put that many +1/+1 counters or charge counters on an artifact you control.
SVar:DBPutP1P1:DB$ PutCounter | Choices$ Artifact.YouCtrl | CounterType$ P1P1 | CounterNum$ X | SpellDescription$ +1/+1
SVar:DBPutCharge:DB$ PutCounter | Choices$ Artifact.YouCtrl | CounterType$ CHARGE | CounterNum$ X | SpellDescription$ Charge
A:SP$ Destroy | Cost$ 2 R | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SubAbility$ DBPutCounter | SpellDescription$ Destroy target artifact. If that artifact had counters on it, put that many +1/+1 counters or charge counters on an artifact you control.
SVar:DBPutCounter:DB$ PutCounter | Choices$ Artifact.YouCtrl | CounterType$ P1P1,CHARGE | CounterNum$ X | CounterTypePerDefined$ True | ConditionDefined$ Targeted | ConditionPresent$ Card.HasCounters | ConditionCompare$ GE1 | StackDescription$ put that many +1/+1 counters or charge counters on an artifact you control.
SVar:X:TargetedLKI$CardCounters.ALL
Oracle:Destroy target artifact. If that artifact had counters on it, put that many +1/+1 counters or charge counters on an artifact you control.

View File

@@ -1,12 +1,8 @@
Name:Invoke the Ancients
ManaCost:1 G G G G
Types:Sorcery
A:SP$ Token | TokenAmount$ 2 | TokenScript$ g_4_5_spirit | RememberTokens$ True | SubAbility$ DBRepeatEach | SpellDescription$ Create two 4/5 green Spirit creature tokens.
SVar:DBRepeatEach:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Card.IsRemembered | RepeatSubAbility$ CounterChoice | SubAbility$ DBCleanup | SpellDescription$ For each of them, put your choice of a vigilance counter, a reach counter, or a trample counter on it.
SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Vigilance,Reach,Trample
SVar:Vigilance:DB$ PutCounter | Defined$ Imprinted | CounterType$ Vigilance | CounterNum$ 1 | SpellDescription$ Vigilance
SVar:Reach:DB$ PutCounter | Defined$ Imprinted | CounterType$ Reach | CounterNum$ 1 | SpellDescription$ Reach
SVar:Trample:DB$ PutCounter | Defined$ Imprinted | CounterType$ Trample | CounterNum$ 1 | SpellDescription$ Trample
A:SP$ Token | TokenAmount$ 2 | TokenScript$ g_4_5_spirit | RememberTokens$ True | SubAbility$ DBCounter | SpellDescription$ Create two 4/5 green Spirit creature tokens.
SVar:DBCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ Vigilance,Reach,Trample | CounterNum$ 1 | CounterTypePerDefined$ True | SubAbility$ DBCleanup | SpellDescription$ For each of them, put your choice of a vigilance counter, a reach counter, or a trample counter on it.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
DeckHas:Ability$Token|Counters & Type$Spirit & Keyword$Vigilance|Reach|Trample
Oracle:Create two 4/5 green Spirit creature tokens. For each of them, put your choice of a vigilance counter, a reach counter, or a trample counter on it.

View File

@@ -5,11 +5,8 @@ Loyalty:3
S:Mode$ Continuous | Affected$ Card.TopLibrary+YouCtrl | AffectedZone$ Library | MayLookAt$ You | Description$ You may look at the top card of your library any time.
S:Mode$ Continuous | Affected$ Creature.TopLibrary+YouCtrl | MayPlay$ True | AffectedZone$ Library | Description$ You may cast creature spells from the top of your library.
SVar:NonStackingEffect:True
A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ g_3_3_beast | TokenOwner$ You | LegacyImage$ g 3 3 beast iko | RememberTokens$ True | SubAbility$ DBGenericChoice | SpellDescription$ Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.
SVar:DBGenericChoice:DB$ GenericChoice | Defined$ You | Choices$ Vigilance,Reach,Trample | AILogic$ Always
SVar:Reach:DB$ PutCounter | Defined$ Remembered | CounterType$ Reach | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Reach
SVar:Trample:DB$ PutCounter | Defined$ Remembered | CounterType$ Trample | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Trample
SVar:Vigilance:DB$ PutCounter | Defined$ Remembered | CounterType$ Vigilance | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Vigilance
A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ g_3_3_beast | TokenOwner$ You | RememberTokens$ True | SubAbility$ DBCounter | SpellDescription$ Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.
SVar:DBCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ Vigilance,Reach,Trample | CounterNum$ 1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ Effect | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | Ultimate$ True | Triggers$ TrigSearch | AILogic$ WillCastCreature | SpellDescription$ When you cast your next creature spell this turn, search your library for a creature card with lesser mana value, put it onto the battlefield, then shuffle.
SVar:TrigSearch:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | OneOff$ True | TriggerZones$ Command | Execute$ DBSearch | TriggerDescription$ When you cast your next creature spell this turn, search your library for a creature card with lesser mana value, put it onto the battlefield, then shuffle.