Costs Tag Tracking part 4: Convoke (#11446)

* Switch Convoke to using costs tag system

* Add Convoke copy/clone tests

* update author name on sufficiently changed files

* Remove now-unused CONVOKED event
This commit is contained in:
ssk97 2023-11-22 13:31:56 -08:00 committed by GitHub
parent 31f028d41e
commit 2cc9957753
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 141 additions and 103 deletions

View file

@ -1,27 +1,26 @@
package mage.abilities.dynamicvalue.common;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.keyword.ConvokeAbility;
import mage.game.Game;
import mage.watchers.common.ConvokeWatcher;
import mage.util.CardUtil;
import java.util.HashSet;
/**
* @author TheElk801
* @author notgreat
*/
public enum ConvokedSourceCount implements DynamicValue {
PERMANENT(-1),
SPELL(0);
private final int offset;
instance;
ConvokedSourceCount(int offset) {
this.offset = offset;
ConvokedSourceCount() {
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return ConvokeWatcher.getConvokingCreatures(new MageObjectReference(game.getObject(sourceAbility), game, offset), game).size();
return CardUtil.getSourceCostsTag(game, sourceAbility, ConvokeAbility.convokingCreaturesKey, new HashSet<>(0)).size();
}
@Override

View file

@ -1,5 +1,6 @@
package mage.abilities.keyword;
import mage.MageObjectReference;
import mage.Mana;
import mage.ObjectColor;
import mage.abilities.Ability;
@ -30,12 +31,9 @@ import mage.players.ManaPool;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.watchers.common.ConvokeWatcher;
import mage.util.CardUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.*;
/**
* 502.46. Convoke
@ -71,6 +69,7 @@ import java.util.UUID;
*/
public class ConvokeAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
public static String convokingCreaturesKey = "convokingCreatures";
private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent();
static {
@ -80,7 +79,6 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
public ConvokeAbility() {
super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities
this.setRuleAtTheTop(true);
this.addWatcher(new ConvokeWatcher());
this.addHint(new ValueHint("Untapped creatures you control", new PermanentsOnBattlefieldCount(filterUntapped)));
}
@ -265,7 +263,9 @@ class ConvokeEffect extends OneShotEffect {
manaPool.unlockManaType(ManaType.COLORLESS);
manaName = "colorless";
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CONVOKED, perm.getId(), source, source.getControllerId()));
HashSet<MageObjectReference> set = CardUtil.getSourceCostsTag(game, spell.getSpellAbility(), ConvokeAbility.convokingCreaturesKey, new HashSet<>());
set.add(new MageObjectReference(perm, game));
spell.getSpellAbility().setCostsTag(ConvokeAbility.convokingCreaturesKey, set);
game.informPlayers("Convoke: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay one " + manaName + " mana");
// can't use mana abilities after that (convoke cost must be payed after mana abilities only)

View file

@ -1,28 +1,23 @@
package mage.filter.predicate.permanent;
import mage.MageObjectReference;
import mage.abilities.keyword.ConvokeAbility;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.watchers.common.ConvokeWatcher;
import mage.util.CardUtil;
import java.util.HashSet;
/**
* @author TheElk801
* @author notgreat
*/
public enum ConvokedSourcePredicate implements ObjectSourcePlayerPredicate<Permanent> {
PERMANENT(-1),
SPELL(0);
private final int offset;
ConvokedSourcePredicate(int offset) {
this.offset = offset;
}
instance;
@Override
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
return ConvokeWatcher.checkConvoke(
new MageObjectReference(input.getSource(), offset), input.getObject(), game
);
HashSet<MageObjectReference> set = CardUtil.getSourceCostsTag(game, input.getSource(), ConvokeAbility.convokingCreaturesKey, new HashSet<>(0));
return set.contains(new MageObjectReference(input.getObject(), game));
}
}

View file

@ -86,12 +86,6 @@ public class GameEvent implements Serializable {
MADNESS_CARD_EXILED,
INVESTIGATED, // playerId is the player who investigated
KICKED,
/* CONVOKED
targetId id of the creature that was taped to convoke the sourceId
sourceId sourceId of the convoked spell
playerId controller of the convoked spell
*/
CONVOKED,
/* DISCARD_CARD
flag event is result of effect (1) or result of cost (0)
*/

View file

@ -1,58 +0,0 @@
package mage.watchers.common;
import mage.MageObjectReference;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
import java.util.*;
/**
* @author LevelX2
*/
public class ConvokeWatcher extends Watcher {
private final Map<MageObjectReference, Set<MageObjectReference>> convokingCreatures = new HashMap<>();
public ConvokeWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.CONVOKED) {
return;
}
Spell spell = game.getSpell(event.getSourceId());
Permanent tappedCreature = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (spell == null || tappedCreature == null) {
return;
}
convokingCreatures
.computeIfAbsent(new MageObjectReference(spell.getSourceId(), game), x -> new HashSet<>())
.add(new MageObjectReference(tappedCreature, game));
}
public static Set<MageObjectReference> getConvokingCreatures(MageObjectReference mor, Game game) {
return game
.getState()
.getWatcher(ConvokeWatcher.class)
.convokingCreatures
.getOrDefault(mor, Collections.emptySet());
}
public static boolean checkConvoke(MageObjectReference mor, Permanent permanent, Game game) {
return getConvokingCreatures(mor, game)
.stream()
.anyMatch(m -> m.refersTo(permanent, game));
}
@Override
public void reset() {
super.reset();
convokingCreatures.clear();
}
}