From 1e76b59f4e9b0b22074722b7b21c70ac0a00e788 Mon Sep 17 00:00:00 2001 From: Steven Knipe Date: Thu, 16 Nov 2023 14:39:55 -0800 Subject: [PATCH] Convert Bargain/Entwine/Squad to costs tag system --- .../condition/common/BargainedCondition.java | 14 +--- .../abilities/keyword/BargainAbility.java | 43 +----------- .../abilities/keyword/EntwineAbility.java | 24 ++----- .../mage/abilities/keyword/SquadAbility.java | 65 +++---------------- 4 files changed, 18 insertions(+), 128 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java index cda1156d377..80435d4b3cc 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java @@ -1,11 +1,10 @@ package mage.abilities.condition.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.abilities.keyword.BargainAbility; -import mage.cards.Card; import mage.game.Game; +import mage.util.CardUtil; /** * Checks if the spell was cast with the alternate Bargain cost @@ -18,16 +17,7 @@ public enum BargainedCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - // TODO: replace by Tag Cost Tracking. - MageObject sourceObject = source.getSourceObject(game); - if (sourceObject instanceof Card) { - for (Ability ability : ((Card) sourceObject).getAbilities(game)) { - if (ability instanceof BargainAbility) { - return ((BargainAbility) ability).wasBargained(game, source); - } - } - } - return false; + return CardUtil.checkSourceCostsTagExists(game, source, BargainAbility.BARGAIN_ACTIVATION_VALUE_KEY); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/BargainAbility.java b/Mage/src/main/java/mage/abilities/keyword/BargainAbility.java index 8c4db4df8cc..eb20487925d 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BargainAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BargainAbility.java @@ -1,7 +1,6 @@ package mage.abilities.keyword; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; @@ -16,7 +15,6 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.players.Player; -import mage.util.CardUtil; /** * Written before ruling was clarified. Feel free to put the ruling once it gets there. @@ -37,7 +35,7 @@ public class BargainAbility extends StaticAbility implements OptionalAdditionalS private static final String reminderText = "You may sacrifice an artifact, enchantment, or token as you cast this spell."; private final String rule; - private String activationKey; // TODO: replace by Tag Cost Tracking. + public static final String BARGAIN_ACTIVATION_VALUE_KEY = "bargainActivation"; protected OptionalAdditionalCost additionalCost; @@ -61,14 +59,12 @@ public class BargainAbility extends StaticAbility implements OptionalAdditionalS this.rule = additionalCost.getName() + ' ' + additionalCost.getReminderText(); this.setRuleAtTheTop(true); this.addHint(BargainCostWasPaidHint.instance); - this.activationKey = null; } private BargainAbility(final BargainAbility ability) { super(ability); this.rule = ability.rule; this.additionalCost = ability.additionalCost.copy(); - this.activationKey = ability.activationKey; } @Override @@ -80,7 +76,6 @@ public class BargainAbility extends StaticAbility implements OptionalAdditionalS if (additionalCost != null) { additionalCost.reset(); } - this.activationKey = null; } @Override @@ -104,7 +99,7 @@ public class BargainAbility extends StaticAbility implements OptionalAdditionalS for (Cost cost : ((Costs) additionalCost)) { ability.getCosts().add(cost.copy()); } - this.activationKey = getActivationKey(ability, game); + ability.setCostsTag(BARGAIN_ACTIVATION_VALUE_KEY, null); } @Override @@ -112,40 +107,6 @@ public class BargainAbility extends StaticAbility implements OptionalAdditionalS return additionalCost.getCastSuffixMessage(0); } - public boolean wasBargained(Game game, Ability source) { - return activationKey != null && getActivationKey(source, game).equalsIgnoreCase(activationKey); - } - - /** - * TODO: remove with Tag Cost Tracking. - * Return activation zcc key for searching spell's settings in source object - * - * @param source - * @param game - */ - public static String getActivationKey(Ability source, Game game) { - // Bargain activates in STACK zone so all zcc must be from "stack moment" - // Use cases: - // * resolving spell have same zcc (example: check kicker status in sorcery/instant); - // * copied spell have same zcc as source spell (see Spell.copySpell and zcc sync); - // * creature/token from resolved spell have +1 zcc after moved to battlefield (example: check kicker status in ETB triggers/effects); - - // find object info from the source ability (it can be a permanent or a spell on stack, on the moment of trigger/resolve) - MageObject sourceObject = source.getSourceObject(game); - Zone sourceObjectZone = game.getState().getZone(sourceObject.getId()); - int zcc = CardUtil.getActualSourceObjectZoneChangeCounter(game, source); - - // find "stack moment" zcc: - // * permanent cards enters from STACK to BATTLEFIELD (+1 zcc) - // * permanent tokens enters from OUTSIDE to BATTLEFIELD (+1 zcc, see prepare code in TokenImpl.putOntoBattlefieldHelper) - // * spells and copied spells resolves on STACK (zcc not changes) - if (sourceObjectZone != Zone.STACK) { - --zcc; - } - - return zcc + ""; - } - @Override public String getRule() { return rule; diff --git a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java index fb066dcd096..e90d8bcf79e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java @@ -9,10 +9,9 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import mage.util.CardUtil; -import java.util.HashSet; import java.util.Iterator; -import java.util.Set; /** * 702.40. Entwine @@ -32,9 +31,9 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM private static final String keywordText = "Entwine"; protected static final String reminderText = "You may {cost} in addition to any other costs to use all modes."; + protected static final String ENTWINE_ACTIVATION_VALUE_KEY = "entwineActivation"; protected OptionalAdditionalCost entwineCost; - protected Set activations = new HashSet<>(); // same logic as KickerAbility: activations per zoneChangeCounter public EntwineAbility(String manaString) { super(Zone.STACK, null); @@ -62,7 +61,6 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM if (ability.entwineCost != null) { this.entwineCost = ability.entwineCost.copy(); } - this.activations.addAll(ability.activations); } @Override @@ -97,7 +95,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM ability.addCost(cost.copy()); } } - activateEntwine(game, ability); + ability.setCostsTag(ENTWINE_ACTIVATION_VALUE_KEY, null); } } @@ -135,23 +133,9 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM if (entwineCost != null) { entwineCost.reset(); } - - String key = getActivationKey(source, game); - this.activations.remove(key); - } - - private void activateEntwine(Game game, Ability source) { - String key = getActivationKey(source, game); - this.activations.add(key); } public boolean costWasActivated(Ability ability, Game game) { - String key = getActivationKey(ability, game); - return this.activations.contains(key); - } - - private String getActivationKey(Ability source, Game game) { - // same logic as KickerAbility - return KickerAbility.getActivationKey(source, game); + return CardUtil.checkSourceCostsTagExists(game, ability, ENTWINE_ACTIVATION_VALUE_KEY); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/SquadAbility.java b/Mage/src/main/java/mage/abilities/keyword/SquadAbility.java index a640f797b5f..a2448e4b18b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SquadAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SquadAbility.java @@ -1,6 +1,5 @@ package mage.abilities.keyword; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; @@ -9,7 +8,6 @@ import mage.abilities.costs.*; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.CreateTokenCopySourceEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.constants.Zone; @@ -24,6 +22,7 @@ import mage.util.CardUtil; public class SquadAbility extends StaticAbility implements OptionalAdditionalSourceCosts { protected OptionalAdditionalCost cost; protected static final String SQUAD_KEYWORD = "Squad"; + protected static final String SQUAD_ACTIVATION_VALUE_KEY = "squadActivationCount"; protected static final String SQUAD_REMINDER = "You may pay an additional " + "{cost} any number of times as you cast this spell."; public SquadAbility() { @@ -34,7 +33,6 @@ public class SquadAbility extends StaticAbility implements OptionalAdditionalSou super(Zone.STACK, null); setSquadCost(cost); addSubAbility(new SquadTriggerAbility()); - //Note that I get subabilities list's position 0 to modify the zcc/count references } private SquadAbility(final SquadAbility ability) { @@ -47,7 +45,7 @@ public class SquadAbility extends StaticAbility implements OptionalAdditionalSou return new SquadAbility(this); } - public final void setSquadCost(Cost cost) { + private void setSquadCost(Cost cost) { OptionalAdditionalCost newCost = new OptionalAdditionalCostImpl( SQUAD_KEYWORD, SQUAD_REMINDER, cost); newCost.setRepeatable(true); @@ -59,28 +57,6 @@ public class SquadAbility extends StaticAbility implements OptionalAdditionalSou cost.reset(); } - protected static int get_zcc(Ability source, Game game) { - // Squad/Kicker activates in STACK zone so all zcc must be from "stack moment" - // Use cases: - // * resolving spell have same zcc (example: check kicker status in sorcery/instant); - // * copied spell have same zcc as source spell (see Spell.copySpell and zcc sync); - // * creature/token from resolved spell have +1 zcc after moved to battlefield (example: check kicker status in ETB triggers/effects); - - // find object info from the source ability (it can be a permanent or a spell on stack, on the moment of trigger/resolve) - MageObject sourceObject = source.getSourceObject(game); - Zone sourceObjectZone = game.getState().getZone(sourceObject.getId()); - int zcc = CardUtil.getActualSourceObjectZoneChangeCounter(game, source); - - // find "stack moment" zcc: - // * permanent cards enters from STACK to BATTLEFIELD (+1 zcc) - // * permanent tokens enters from OUTSIDE to BATTLEFIELD (+1 zcc, see prepare code in TokenImpl.putOntoBattlefieldHelper) - // * spells and copied spells resolves on STACK (zcc not changes) - if (sourceObjectZone != Zone.STACK) { - --zcc; - } - return zcc; - } - @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { if (!(ability instanceof SpellAbility)) { @@ -94,7 +70,7 @@ public class SquadAbility extends StaticAbility implements OptionalAdditionalSou boolean again = true; while (player.canRespond() && again) { String times = ""; - int activatedCount = getSquadCount(); + int activatedCount = cost.getActivateCount(); times = (activatedCount + 1) + (activatedCount == 0 ? " time " : " times "); // TODO: add AI support to find max number of possible activations (from available mana) // canPay checks only single mana available, not total mana usage @@ -111,10 +87,7 @@ public class SquadAbility extends StaticAbility implements OptionalAdditionalSou again = false; } } - SquadTriggerAbility squadETB = (SquadTriggerAbility)getSubAbilities().get(0); - squadETB.zcc = get_zcc(ability, game); - SquadEffectETB squadEffect = (SquadEffectETB)squadETB.getEffects().get(0); - squadEffect.activationCount = cost.getActivateCount(); + ability.setCostsTag(SQUAD_ACTIVATION_VALUE_KEY,cost.getActivateCount()); } @Override @@ -131,18 +104,8 @@ public class SquadAbility extends StaticAbility implements OptionalAdditionalSou cost.getText()+"any number of times. When this creature enters the battlefield, "+ "create that many tokens that are copies of it.)"; } - - /** - * Number of times squad cost was paid - * - * @return int activation count - */ - public int getSquadCount() { - return cost.getActivateCount(); - } } class SquadTriggerAbility extends EntersBattlefieldTriggeredAbility { - protected Integer zcc; public SquadTriggerAbility() { super(new SquadEffectETB()); this.setRuleVisible(false); @@ -150,7 +113,6 @@ class SquadTriggerAbility extends EntersBattlefieldTriggeredAbility { private SquadTriggerAbility(final SquadTriggerAbility ability) { super(ability); - this.zcc = ability.zcc; } @Override public SquadTriggerAbility copy() { @@ -159,21 +121,17 @@ class SquadTriggerAbility extends EntersBattlefieldTriggeredAbility { @Override public boolean checkInterveningIfClause(Game game) { - if (zcc != null && zcc == SquadAbility.get_zcc(this, game)){ - SquadEffectETB effect = (SquadEffectETB)getEffects().get(0); - return effect.activationCount > 0; - } - return false; + int squadCount = (int)CardUtil.getSourceCostsTag(game, this, SquadAbility.SQUAD_ACTIVATION_VALUE_KEY,0); + return (squadCount > 0); } @Override public String getRule() { return "Squad (When this creature enters the battlefield, if its squad cost was paid, " - + "create a token that’s a copy of it for each time its squad cost was paid.)"; + + "create a token that's a copy of it for each time its squad cost was paid.)"; } } class SquadEffectETB extends OneShotEffect { - protected Integer activationCount; SquadEffectETB() { super(Outcome.Benefit); @@ -181,7 +139,6 @@ class SquadEffectETB extends OneShotEffect { private SquadEffectETB(final SquadEffectETB effect) { super(effect); - this.activationCount = effect.activationCount; } @Override @@ -191,10 +148,8 @@ class SquadEffectETB extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (activationCount != null) { - CreateTokenCopySourceEffect effect = new CreateTokenCopySourceEffect(activationCount); - return effect.apply(game, source); - } - return true; + int squadCount = (int)CardUtil.getSourceCostsTag(game, source, SquadAbility.SQUAD_ACTIVATION_VALUE_KEY,0); + CreateTokenCopySourceEffect effect = new CreateTokenCopySourceEffect(squadCount); + return effect.apply(game, source); } }