From d283b03e6938e051cc36ced7b7713d6956c242bf Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Tue, 8 Mar 2022 07:32:44 +0100 Subject: [PATCH] CounterPutEffect: make counterType support , seperator --- .../ability/effects/CountersPutEffect.java | 396 ++++++++++-------- forge-gui/res/cardsfolder/d/dismantle.txt | 6 +- .../res/cardsfolder/i/invoke_the_ancients.txt | 8 +- .../v/vivien_monsters_advocate.txt | 7 +- 4 files changed, 225 insertions(+), 192 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index 6717189e36f..11de95a10b9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -45,8 +45,9 @@ 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); - //skip the StringBuilder if no targets are chosen ("up to" scenario) + 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 targetCards = SpellAbilityEffect.getTargetCards(spellAbility); if (targetCards.size() == 0) { @@ -60,7 +61,7 @@ public class CountersPutEffect extends SpellAbilityEffect { boolean divAsChoose = spellAbility.isDividedAsYouChoose(); if (divAsChoose) { stringBuilder.append("Distribute "); - } else if (spellAbility.hasParam("DividedRandomly")){ + } else if (spellAbility.hasParam("DividedRandomly")) { stringBuilder.append("Randomly distribute "); } else { stringBuilder.append("Put "); @@ -70,37 +71,33 @@ 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 + // if use targeting we show all targets and corresponding counters } else if (spellAbility.usesTargeting()) { final List targetCards = SpellAbilityEffect.getTargetCards(spellAbility); - for(int i = 0; i < targetCards.size(); i++) { + for (int i = 0; i < targetCards.size(); i++) { Card targetCard = targetCards.get(i); stringBuilder.append(targetCard); Integer v = spellAbility.getDividedValue(targetCard); if (v != null) // fix null counter stack description stringBuilder.append(" (").append(v).append(v == 1 ? " counter)" : " counters)"); - if(i == targetCards.size() - 2) { + 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 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; } @@ -223,58 +223,83 @@ public class CountersPutEffect extends SpellAbilityEffect { return; } Map randomMap = Maps.newHashMap(); - for (int i=0; i choices = Lists.newArrayList(); - // get types of counters - for (CounterType ct : obj.getCounters().keySet()) { - if (obj.canReceiveCounters(ct)) { - choices.add(ct); + } 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) { + Card tgtCard = (Card) obj; + gameCard = game.getCardState(tgtCard, null); + // gameCard is LKI in that case, the card is not in game anymore + // or the timestamp did change + // this should check Self too + if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) { + continue; } } - if (eachExistingCounter) { - for (CounterType ct : choices) { - if (obj instanceof Player) { - ((Player) obj).addCounter(ct, counterAmount, placer, table); + if (existingCounter) { + final List choices = Lists.newArrayList(); + // get types of counters + for (CounterType ct : obj.getCounters().keySet()) { + if (obj.canReceiveCounters(ct)) { + choices.add(ct); } - if (obj instanceof Card) { - gameCard.addCounter(ct, counterAmount, placer, table); + } + + if (eachExistingCounter) { + for (CounterType ct : choices) { + if (obj instanceof Player) { + ((Player) obj).addCounter(ct, counterAmount, placer, table); + } + if (obj instanceof Card) { + gameCard.addCounter(ct, counterAmount, placer, table); + } + } + continue; + } + + if (choices.isEmpty()) { + continue; + } else if (choices.size() == 1) { + counterType = choices.get(0); + } else { + Map 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 (sa.hasParam("EachFromSource")) { + for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) { + for (Entry cti : c.getCounters().entrySet()) { + if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) { + gameCard.addCounter(cti.getKey(), cti.getValue(), placer, table); + } } } continue; } - - if (choices.isEmpty()) { - continue; - } else if (choices.size() == 1) { - counterType = choices.get(0); - } else { + if (sa.hasParam("CounterTypePerDefined")) { + List choices = Lists.newArrayList(); + for (String s : sa.getParam("CounterType").split(",")) { + choices.add(CounterType.getType(s)); + } Map params = Maps.newHashMap(); params.put("Target", obj); StringBuilder sb = new StringBuilder(); @@ -282,126 +307,128 @@ public class CountersPutEffect extends SpellAbilityEffect { sb.append(obj); counterType = pc.chooseCounterType(choices, sa, sb.toString(), params); } - } - if (sa.hasParam("EachFromSource")) { - for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) { - for (Entry cti : c.getCounters().entrySet()) { - if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) { - gameCard.addCounter(cti.getKey(), cti.getValue(), placer, table); + if (obj instanceof Card) { + 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); } - } - } - continue; - } - - if (obj instanceof Card) { - 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); - } - if (sa.hasParam("UpTo")) { - Map params = Maps.newHashMap(); - params.put("Target", obj); - params.put("CounterType", counterType); - counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params); - } - if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { - Map 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())), - 1, counterRemain, params); + if (sa.hasParam("UpTo")) { + Map params = Maps.newHashMap(); + params.put("Target", obj); + params.put("CounterType", counterType); + counterAmount = pc.chooseNumber(sa, + Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params); } - } - - // Adapt need extra logic - if (sa.hasParam("Adapt")) { - if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0 || StaticAbilityAdapt.anyWithAdapt(sa, gameCard))) { - continue; - } - } - - if (sa.hasParam("Tribute")) { - // make a copy to check if it would be on the battlefield - Card noTributeLKI = CardUtil.getLKICopy(gameCard); - // this check needs to check if this card would be on the battlefield - noTributeLKI.setLastKnownZone(activator.getZone(ZoneType.Battlefield)); - - // double freeze tracker, so it doesn't update view - game.getTracker().freeze(); - - CardCollection preList = new CardCollection(noTributeLKI); - game.getAction().checkStaticAbilities(false, Sets.newHashSet(noTributeLKI), preList); - - boolean abort = !noTributeLKI.canReceiveCounters(counterType); - - game.getAction().checkStaticAbilities(false); - // clear delayed changes, this check should not have updated the view - game.getTracker().clearDelayed(); - // need to unfreeze tracker - game.getTracker().unfreeze(); - - // check if it can recive the Tribute - if (abort) { - continue; + if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { + Map 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())), + 1, counterRemain, params); + } } - Map params = Maps.newHashMap(); - params.put("CounterType", counterType); - params.put("Amount", counterAmount); - params.put("Target", gameCard); + // Adapt need extra logic + if (sa.hasParam("Adapt")) { + if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0 + || StaticAbilityAdapt.anyWithAdapt(sa, gameCard))) { + continue; + } + } - 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 (sa.hasParam("Tribute")) { + // make a copy to check if it would be on the battlefield + Card noTributeLKI = CardUtil.getLKICopy(gameCard); + // this check needs to check if this card would be on the battlefield + noTributeLKI.setLastKnownZone(activator.getZone(ZoneType.Battlefield)); - if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) { - gameCard.setTributed(true); + // double freeze tracker, so it doesn't update view + game.getTracker().freeze(); + + CardCollection preList = new CardCollection(noTributeLKI); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(noTributeLKI), preList); + + boolean abort = !noTributeLKI.canReceiveCounters(counterType); + + game.getAction().checkStaticAbilities(false); + // clear delayed changes, this check should not have updated the view + game.getTracker().clearDelayed(); + // need to unfreeze tracker + game.getTracker().unfreeze(); + + // check if it can recive the Tribute + if (abort) { + continue; + } + + Map params = Maps.newHashMap(); + params.put("CounterType", counterType); + 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); + + if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) { + gameCard.setTributed(true); + } else { + continue; + } + } + + if (etbcounter) { + gameCard.addEtbCounter(counterType, counterAmount, placer); } else { - continue; + gameCard.addCounter(counterType, counterAmount, placer, table); + } + + if (sa.hasParam("Evolve")) { + game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard), + false); + } + if (sa.hasParam("Monstrosity")) { + gameCard.setMonstrous(true); + final Map runParams = AbilityKey.mapFromCard(gameCard); + runParams.put(AbilityKey.MonstrosityAmount, counterAmount); + game.getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false); + } + if (sa.hasParam("Renown")) { + gameCard.setRenowned(true); + game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, + AbilityKey.mapFromCard(gameCard), false); + } + if (sa.hasParam("Adapt")) { + game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), + false); + } + if (sa.hasParam("Training")) { + game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard), + false); + } + + game.updateLastStateForCard(gameCard); + if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { + counterRemain = counterRemain - counterAmount; } } - - if (etbcounter) { - gameCard.addEtbCounter(counterType, counterAmount, placer); - } else { - gameCard.addCounter(counterType, counterAmount, placer, table); - } - - if (sa.hasParam("Evolve")) { - game.getTriggerHandler().runTrigger(TriggerType.Evolved, AbilityKey.mapFromCard(gameCard), false); - } - if (sa.hasParam("Monstrosity")) { - gameCard.setMonstrous(true); - final Map runParams = AbilityKey.mapFromCard(gameCard); - runParams.put(AbilityKey.MonstrosityAmount, counterAmount); - game.getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false); - } - if (sa.hasParam("Renown")) { - gameCard.setRenowned(true); - game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, AbilityKey.mapFromCard(gameCard), false); - } - if (sa.hasParam("Adapt")) { - game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), false); - } - if (sa.hasParam("Training")) { - game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard), false); - } - - game.updateLastStateForCard(gameCard); - if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { - counterRemain = counterRemain - counterAmount; - } + } else if (obj instanceof Player) { + // Add Counters to players! + Player pl = (Player) obj; + pl.addCounter(counterType, counterAmount, placer, table); } - } else if (obj instanceof Player) { - // Add Counters to players! - Player pl = (Player) obj; - pl.addCounter(counterType, counterAmount, placer, table); } } } @@ -427,23 +454,33 @@ public class CountersPutEffect extends SpellAbilityEffect { if (sa.hasParam("TriggeredCounterMap")) { @SuppressWarnings("unchecked") - Map counterMap = (Map) sa.getTriggeringObject(AbilityKey.CounterMap); + Map counterMap = (Map) sa + .getTriggeringObject(AbilityKey.CounterMap); for (Map.Entry e : counterMap.entrySet()) { resolvePerType(sa, placer, e.getKey(), e.getValue(), table); } } else if (sa.hasParam("SharedKeywords")) { List keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & ")); - List zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone")); - String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[]{"Card"}; + List zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone")); + 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 choices = Lists.newArrayList(); + for (String s : sa.getParam("CounterType").split(",")) { + choices.add(CounterType.getType(s)); + } + Map 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++) { diff --git a/forge-gui/res/cardsfolder/d/dismantle.txt b/forge-gui/res/cardsfolder/d/dismantle.txt index b666a24d1b0..951a91b5a3b 100644 --- a/forge-gui/res/cardsfolder/d/dismantle.txt +++ b/forge-gui/res/cardsfolder/d/dismantle.txt @@ -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. diff --git a/forge-gui/res/cardsfolder/i/invoke_the_ancients.txt b/forge-gui/res/cardsfolder/i/invoke_the_ancients.txt index 2f5ff69f4fc..75bd80ed79e 100644 --- a/forge-gui/res/cardsfolder/i/invoke_the_ancients.txt +++ b/forge-gui/res/cardsfolder/i/invoke_the_ancients.txt @@ -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. diff --git a/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt b/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt index 1172795412e..745eec7f07a 100644 --- a/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt +++ b/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt @@ -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.