mirror of
https://github.com/correl/mage.git
synced 2024-11-27 03:00:11 +00:00
fix ActivatedAbilityUsedThisTurnWatcher ClassCastException
Chronatog Totem and Groundling Pouncer were breaking when trying to play spells with convoke: java.lang.ClassCastException: mage.game.stack.Spell cannot be cast to mage.game.stack.StackAbility mage.sets.timespiral.ActivatedAbilityUsedThisTurnWatcher.watch(ChronatogTotem.java:148) mage.watchers.Watchers.watch(Watchers.java:63) mage.game.GameState.handleEvent(GameState.java:665) mage.game.GameImpl.fireEvent(GameImpl.java:2286) mage.players.PlayerImpl.specialAction(PlayerImpl.java:1094) mage.players.PlayerImpl.activateAbility(PlayerImpl.java:1133) mage.player.human.HumanPlayer.activateAbility(HumanPlayer.java:1219) mage.player.human.HumanPlayer.specialManaAction(HumanPlayer.java:1209) mage.player.human.HumanPlayer.playManaHandling(HumanPlayer.java:791) mage.player.human.HumanPlayer.playMana(HumanPlayer.java:769) mage.abilities.costs.mana.ManaCostsImpl.pay(ManaCostsImpl.java:135) mage.abilities.AbilityImpl.activate(AbilityImpl.java:394) mage.game.stack.Spell.activate(Spell.java:128) mage.players.PlayerImpl.cast(PlayerImpl.java:969) (...)
This commit is contained in:
parent
8af7526acc
commit
cad96e1927
2 changed files with 110 additions and 135 deletions
|
@ -27,32 +27,28 @@
|
||||||
*/
|
*/
|
||||||
package mage.sets.eventide;
|
package mage.sets.eventide;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.condition.common.OpponentControlsPermanentCondition;
|
||||||
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.decorator.ConditionalActivatedAbility;
|
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.Effects;
|
||||||
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.constants.AbilityType;
|
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.EffectType;
|
||||||
import mage.constants.Rarity;
|
import mage.constants.Rarity;
|
||||||
import mage.constants.WatcherScope;
|
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
import mage.game.stack.StackAbility;
|
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.watchers.Watcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -61,7 +57,11 @@ import mage.watchers.Watcher;
|
||||||
*/
|
*/
|
||||||
public class GroundlingPouncer extends CardImpl {
|
public class GroundlingPouncer extends CardImpl {
|
||||||
|
|
||||||
private String rule = "{this} gets +1/+3 and gains flying until end of turn. Activate this ability only once each turn and only if an opponent controls a creature with flying.";
|
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(new AbilityPredicate(FlyingAbility.class));
|
||||||
|
}
|
||||||
|
|
||||||
public GroundlingPouncer(UUID ownerId) {
|
public GroundlingPouncer(UUID ownerId) {
|
||||||
super(ownerId, 154, "Groundling Pouncer", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G/U}");
|
super(ownerId, 154, "Groundling Pouncer", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G/U}");
|
||||||
|
@ -72,12 +72,14 @@ public class GroundlingPouncer extends CardImpl {
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// {GU}: Groundling Pouncer gets +1/+3 and gains flying until end of turn. Activate this ability only once each turn and only if an opponent controls a creature with flying.
|
// {GU}: Groundling Pouncer gets +1/+3 and gains flying until end of turn. Activate this ability only once each turn and only if an opponent controls a creature with flying.
|
||||||
Condition condition = new GroundingPouncerCondition();
|
Ability ability = new GroundlingPouncerAbility(
|
||||||
Effect effect = new BoostSourceEffect(1, 3, Duration.EndOfTurn);
|
Zone.BATTLEFIELD,
|
||||||
Effect effect2 = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, false, true);
|
new BoostSourceEffect(1, 3, Duration.EndOfTurn),
|
||||||
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{G/U}"), condition, rule);
|
new ManaCostsImpl("{G/U}"),
|
||||||
ability.addEffect(effect2);
|
new OpponentControlsPermanentCondition(filter),
|
||||||
this.addAbility(ability, new ActivatedAbilityUsedThisTurnWatcher());
|
"{G/U}: {this} gets +1/+3 and gains flying until end of turn. Activate this ability only once each turn and only if an opponent controls a creature with flying.");
|
||||||
|
ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, false, true));
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,69 +93,48 @@ public class GroundlingPouncer extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroundingPouncerCondition implements Condition {
|
class GroundlingPouncerAbility extends LimitedTimesPerTurnActivatedAbility {
|
||||||
|
|
||||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
private static final Effects emptyEffects = new Effects();
|
||||||
|
|
||||||
static {
|
private final Condition condition;
|
||||||
filter.add(new AbilityPredicate(FlyingAbility.class));
|
private final String ruleText;
|
||||||
|
|
||||||
|
public GroundlingPouncerAbility(Zone zone, Effect effect, Cost cost, Condition condition, String rule) {
|
||||||
|
super(zone, effect, cost);
|
||||||
|
this.condition = condition;
|
||||||
|
this.ruleText = rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroundlingPouncerAbility(GroundlingPouncerAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
this.condition = ability.condition;
|
||||||
|
this.ruleText = ability.ruleText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public Effects getEffects(Game game, EffectType effectType) {
|
||||||
ActivatedAbilityUsedThisTurnWatcher watcher = (ActivatedAbilityUsedThisTurnWatcher) game.getState().getWatchers().get("ActivatedAbilityUsedThisTurn");
|
if (!condition.apply(game, this)) {
|
||||||
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
|
return emptyEffects;
|
||||||
if (game.getBattlefield().countAll(filter, opponentId, game) > 0 && !watcher.getActivatedThisTurn().contains(source.getSourceId())) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return super.getEffects(game, effectType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canActivate(UUID playerId, Game game) {
|
||||||
|
if (!condition.apply(game, this)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return super.canActivate(playerId, game);
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "once each turn and only if an opponent controls a flying creature";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ActivatedAbilityUsedThisTurnWatcher extends Watcher {
|
|
||||||
|
|
||||||
public Set<UUID> activatedThisTurn = new HashSet<>();
|
|
||||||
|
|
||||||
public ActivatedAbilityUsedThisTurnWatcher() {
|
|
||||||
super("ActivatedAbilityUsedThisTurn", WatcherScope.GAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActivatedAbilityUsedThisTurnWatcher(final ActivatedAbilityUsedThisTurnWatcher watcher) {
|
|
||||||
super(watcher);
|
|
||||||
this.activatedThisTurn.addAll(watcher.activatedThisTurn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void watch(GameEvent event, Game game) {
|
public GroundlingPouncerAbility copy() {
|
||||||
if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) {
|
return new GroundlingPouncerAbility(this);
|
||||||
StackObject stackObject = game.getStack().getStackObject(event.getTargetId());
|
|
||||||
if (stackObject != null) {
|
|
||||||
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getTargetId());
|
|
||||||
if (stackAbility != null && stackAbility.getAbilityType() == AbilityType.ACTIVATED) {
|
|
||||||
this.activatedThisTurn.add(stackAbility.getOriginalId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<UUID> getActivatedThisTurn() {
|
|
||||||
return this.activatedThisTurn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActivatedAbilityUsedThisTurnWatcher copy() {
|
public String getRule() {
|
||||||
return new ActivatedAbilityUsedThisTurnWatcher(this);
|
return ruleText;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
super.reset();
|
|
||||||
this.activatedThisTurn.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,35 +27,29 @@
|
||||||
*/
|
*/
|
||||||
package mage.sets.timespiral;
|
package mage.sets.timespiral;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.decorator.ConditionalActivatedAbility;
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.Effects;
|
||||||
import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect;
|
import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect;
|
||||||
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
||||||
import mage.abilities.effects.common.turn.SkipNextTurnSourceEffect;
|
import mage.abilities.effects.common.turn.SkipNextTurnSourceEffect;
|
||||||
import mage.abilities.mana.BlueManaAbility;
|
import mage.abilities.mana.BlueManaAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.constants.AbilityType;
|
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.EffectType;
|
||||||
import mage.constants.Rarity;
|
import mage.constants.Rarity;
|
||||||
import mage.constants.WatcherScope;
|
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
import mage.game.events.GameEvent.EventType;
|
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.game.permanent.token.Token;
|
import mage.game.permanent.token.Token;
|
||||||
import mage.game.stack.StackAbility;
|
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.watchers.Watcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -74,14 +68,13 @@ public class ChronatogTotem extends CardImpl {
|
||||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new ChronatogTotemToken(), "", Duration.EndOfTurn), new ManaCostsImpl<>("{1}{U}")));
|
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new ChronatogTotemToken(), "", Duration.EndOfTurn), new ManaCostsImpl<>("{1}{U}")));
|
||||||
|
|
||||||
// {0}: Chronatog Totem gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if Chronatog Totem is a creature.
|
// {0}: Chronatog Totem gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if Chronatog Totem is a creature.
|
||||||
Ability ability = new ConditionalActivatedAbility(
|
Ability ability = new ChronatogTotemAbility(
|
||||||
Zone.BATTLEFIELD,
|
Zone.BATTLEFIELD,
|
||||||
new BoostSourceEffect(3, 3, Duration.EndOfTurn),
|
new BoostSourceEffect(3, 3, Duration.EndOfTurn),
|
||||||
new ManaCostsImpl<>("{0}"),
|
new ManaCostsImpl<>("{0}"),
|
||||||
new ChronatogTotemCondition(),
|
new ChronatogTotemCondition());
|
||||||
"{0}: {this} gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if {this} is a creature");
|
|
||||||
ability.addEffect(new SkipNextTurnSourceEffect());
|
ability.addEffect(new SkipNextTurnSourceEffect());
|
||||||
this.addAbility(ability, new ActivatedAbilityUsedThisTurnWatcher());
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChronatogTotem(final ChronatogTotem card) {
|
public ChronatogTotem(final ChronatogTotem card) {
|
||||||
|
@ -94,6 +87,52 @@ public class ChronatogTotem extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
|
||||||
|
|
||||||
|
private static final Effects emptyEffects = new Effects();
|
||||||
|
|
||||||
|
private final Condition condition;
|
||||||
|
|
||||||
|
public ChronatogTotemAbility(Zone zone, Effect effect, Cost cost, Condition condition) {
|
||||||
|
super(zone, effect, cost);
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChronatogTotemAbility(ChronatogTotemAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
this.condition = ability.condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Effects getEffects(Game game, EffectType effectType) {
|
||||||
|
if (!condition.apply(game, this)) {
|
||||||
|
return emptyEffects;
|
||||||
|
}
|
||||||
|
return super.getEffects(game, effectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canActivate(UUID playerId, Game game) {
|
||||||
|
if (!condition.apply(game, this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.canActivate(playerId, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChronatogTotemAbility copy() {
|
||||||
|
return new ChronatogTotemAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRule() {
|
||||||
|
StringBuilder sb = new StringBuilder(super.getRule());
|
||||||
|
sb.deleteCharAt(sb.length() - 1); // remove last '.'
|
||||||
|
sb.append(" and only if ").append(condition.toString()).append(".");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ChronatogTotemToken extends Token {
|
class ChronatogTotemToken extends Token {
|
||||||
|
|
||||||
ChronatogTotemToken() {
|
ChronatogTotemToken() {
|
||||||
|
@ -111,60 +150,15 @@ class ChronatogTotemCondition implements Condition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
ActivatedAbilityUsedThisTurnWatcher watcher = (ActivatedAbilityUsedThisTurnWatcher) game.getState().getWatchers().get("ActivatedAbilityUsedThisTurn");
|
|
||||||
if (!watcher.getActivatedThisTurn().contains(source.getOriginalId())) {
|
|
||||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
return permanent.getCardType().contains(CardType.CREATURE);
|
return permanent.getCardType().contains(CardType.CREATURE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "once each turn and only if an opponent controls a flying creature";
|
return "{this} is a creature";
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ActivatedAbilityUsedThisTurnWatcher extends Watcher {
|
|
||||||
|
|
||||||
public Set<UUID> activatedThisTurn = new HashSet<>(0);
|
|
||||||
|
|
||||||
ActivatedAbilityUsedThisTurnWatcher() {
|
|
||||||
super("ActivatedAbilityUsedThisTurn", WatcherScope.GAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
ActivatedAbilityUsedThisTurnWatcher(final ActivatedAbilityUsedThisTurnWatcher watcher) {
|
|
||||||
super(watcher);
|
|
||||||
this.activatedThisTurn.addAll(watcher.activatedThisTurn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void watch(GameEvent event, Game game) {
|
|
||||||
if (event.getType() == EventType.ACTIVATED_ABILITY) {
|
|
||||||
StackObject stackObject = game.getStack().getStackObject(event.getTargetId());
|
|
||||||
if (stackObject != null) {
|
|
||||||
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getTargetId());
|
|
||||||
if (stackAbility != null && stackAbility.getAbilityType() == AbilityType.ACTIVATED) {
|
|
||||||
this.activatedThisTurn.add(stackAbility.getOriginalId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<UUID> getActivatedThisTurn() {
|
|
||||||
return Collections.unmodifiableSet(this.activatedThisTurn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActivatedAbilityUsedThisTurnWatcher copy() {
|
|
||||||
return new ActivatedAbilityUsedThisTurnWatcher(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
super.reset();
|
|
||||||
this.activatedThisTurn.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue