diff --git a/Mage.Sets/src/mage/cards/w/WildMagicSorcerer.java b/Mage.Sets/src/mage/cards/w/WildMagicSorcerer.java index 342d8745f57..7b993b29414 100644 --- a/Mage.Sets/src/mage/cards/w/WildMagicSorcerer.java +++ b/Mage.Sets/src/mage/cards/w/WildMagicSorcerer.java @@ -2,9 +2,7 @@ package mage.cards.w; import mage.MageInt; import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.CascadeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,12 +11,14 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; -import mage.players.Player; import mage.watchers.Watcher; - import java.util.HashMap; import java.util.Map; import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.players.Player; /** * @author TheElk801 @@ -34,7 +34,9 @@ public final class WildMagicSorcerer extends CardImpl { this.toughness = new MageInt(3); // The first spell you cast from exile each turn has cascade. - this.addAbility(new SimpleStaticAbility(new WildMagicSorcererEffect()), new WildMagicSorcererWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect()), + new WildMagicSorcererWatcher()); } private WildMagicSorcerer(final WildMagicSorcerer card) { @@ -47,38 +49,60 @@ public final class WildMagicSorcerer extends CardImpl { } } -class WildMagicSorcererEffect extends ContinuousEffectImpl { +class WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect extends ContinuousEffectImpl { private final Ability cascadeAbility = new CascadeAbility(); - public WildMagicSorcererEffect() { + public WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - staticText = "the first spell you cast from exile each turn has cascade"; + staticText = "The first spell you cast from exile each turn has cascade"; } - public WildMagicSorcererEffect(final WildMagicSorcererEffect effect) { + public WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect(final WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect effect) { super(effect); } @Override - public WildMagicSorcererEffect copy() { - return new WildMagicSorcererEffect(this); + public WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect copy() { + return new WildMagicSorcererGainCascadeFirstSpellCastFromExileEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + if (controller != null) { + for (StackObject stackObject : game.getStack()) { + // only spells cast, so no copies of spells + if ((stackObject instanceof Spell) + && !stackObject.isCopy() + && stackObject.isControlledBy(source.getControllerId())) { + Spell spell = (Spell) stackObject; + WildMagicSorcererWatcher watcher = game.getState().getWatcher(WildMagicSorcererWatcher.class); + if (watcher != null + && FirstSpellCastFromExileEachTurnCondition.instance.apply(game, source)) { + game.getState().addOtherAbility(spell.getCard(), cascadeAbility); + } + } + } + return true; + } + return false; + } +} + +enum FirstSpellCastFromExileEachTurnCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + if (game.getStack().isEmpty()) { return false; } - for (StackObject stackObject : game.getStack()) { - // only spells cast, so no copies of spells - if (stackObject.isControlledBy(source.getControllerId()) - && WildMagicSorcererWatcher.checkSpell(stackObject, game)) { - game.getState().addOtherAbility(((Spell) stackObject).getCard(), cascadeAbility); - } - } - return true; + WildMagicSorcererWatcher watcher = game.getState().getWatcher(WildMagicSorcererWatcher.class); + StackObject so = game.getStack().getFirst(); + return so != null + && watcher != null + && WildMagicSorcererWatcher.checkSpell(so, game); } } @@ -92,10 +116,11 @@ class WildMagicSorcererWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getZone() == Zone.EXILED) { + if (event.getType() != GameEvent.EventType.CAST_SPELL + || event.getZone() != Zone.EXILED) { return; } - Spell spell = game.getSpell(event.getTargetId()); + Spell spell = game.getSpell(event.getSourceId()); if (spell == null) { return; } @@ -109,7 +134,8 @@ class WildMagicSorcererWatcher extends Watcher { } static boolean checkSpell(StackObject stackObject, Game game) { - if (stackObject.isCopy() || !(stackObject instanceof Spell)) { + if (stackObject.isCopy() + || !(stackObject instanceof Spell)) { return false; } WildMagicSorcererWatcher watcher = game.getState().getWatcher(WildMagicSorcererWatcher.class); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a031840e8b0..8cb50fc1b39 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -637,9 +637,9 @@ public abstract class PlayerImpl implements Player, Serializable { && this.hasOpponent(sourceControllerId, game) && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null && abilities.stream() - .filter(HexproofBaseAbility.class::isInstance) - .map(HexproofBaseAbility.class::cast) - .anyMatch(ability -> ability.checkObject(source, game))) { + .filter(HexproofBaseAbility.class::isInstance) + .map(HexproofBaseAbility.class::cast) + .anyMatch(ability -> ability.checkObject(source, game))) { return false; } @@ -679,7 +679,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.informPlayers(getLogName() + " discards down to " + this.maxHandSize + (this.maxHandSize == 1 - ? " hand card" : " hand cards")); + ? " hand card" : " hand cards")); } discard(hand.size() - this.maxHandSize, false, false, null, game); } @@ -1147,7 +1147,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param originalAbility * @param game - * @param noMana cast it without paying mana costs + * @param noMana cast it without paying mana costs * @param approvingObject which object approved the cast * @return */ @@ -1209,6 +1209,7 @@ public abstract class PlayerImpl implements Player, Serializable { GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, spell.getSpellAbility().getId(), spell.getSpellAbility(), playerId, approvingObject); + event.setZone(fromZone); // why wasn't this set before?? game.fireEvent(event); if (spell.activate(game, noMana)) { event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, @@ -2892,7 +2893,7 @@ public abstract class PlayerImpl implements Player, Serializable { * @return */ private Object rollDieInner(Outcome outcome, Game game, Ability source, RollDieType rollDieType, - int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int rollsAmount) { + int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int rollsAmount) { if (rollsAmount == 1) { return rollDieInnerWithReplacement(game, source, rollDieType, sidesAmount, chaosSidesAmount, planarSidesAmount); } @@ -2988,8 +2989,8 @@ public abstract class PlayerImpl implements Player, Serializable { * @param outcome * @param source * @param game - * @param sidesAmount number of sides the dice has - * @param rollsAmount number of tries to roll the dice + * @param sidesAmount number of sides the dice has + * @param rollsAmount number of tries to roll the dice * @param ignoreLowestAmount remove the lowest rolls from the results * @return the number that the player rolled */ @@ -3007,17 +3008,18 @@ public abstract class PlayerImpl implements Player, Serializable { * @param outcome * @param source * @param game - * @param rollDieType die type to roll, e.g. planar or numerical - * @param sidesAmount sides per die - * @param chaosSidesAmount for planar die: chaos sides - * @param planarSidesAmount for planar die: planar sides - * @param rollsAmount rolls - * @param ignoreLowestAmount for numerical die: ignore multiple rolls with the lowest values + * @param rollDieType die type to roll, e.g. planar or numerical + * @param sidesAmount sides per die + * @param chaosSidesAmount for planar die: chaos sides + * @param planarSidesAmount for planar die: planar sides + * @param rollsAmount rolls + * @param ignoreLowestAmount for numerical die: ignore multiple rolls with + * the lowest values * @return */ private List rollDiceInner(Outcome outcome, Ability source, Game game, RollDieType rollDieType, - int sidesAmount, int chaosSidesAmount, int planarSidesAmount, - int rollsAmount, int ignoreLowestAmount) { + int sidesAmount, int chaosSidesAmount, int planarSidesAmount, + int rollsAmount, int ignoreLowestAmount) { RollDiceEvent rollDiceEvent = new RollDiceEvent(source, rollDieType, sidesAmount, rollsAmount); if (ignoreLowestAmount > 0) { rollDiceEvent.incIgnoreLowestAmount(ignoreLowestAmount); @@ -3029,7 +3031,6 @@ public abstract class PlayerImpl implements Player, Serializable { // player rolls one or more dice to trigger. However, any effect that refers to a numerical // result of a die roll, including ones that compare the results of that roll to other rolls // or to a given number, ignores the rolling of the planar die. See rule 901, “Planechase.” - // ROLL MULTIPLE dies // results amount can be less than a rolls amount (example: The Big Idea allows rolling 2x instead 1x) List dieResults = new ArrayList<>(); @@ -3178,10 +3179,10 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param source * @param game - * @param chaosSidesAmount The number of chaos sides the planar die - * currently has (normally 1 but can be 5) + * @param chaosSidesAmount The number of chaos sides the planar die + * currently has (normally 1 but can be 5) * @param planarSidesAmount The number of chaos sides the planar die - * currently has (normally 1) + * currently has (normally 1) * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll * or BlankRoll */ @@ -3239,7 +3240,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (Card card : getHand().getCards(game)) { Abilities manaAbilities = card.getAbilities(game).getAvailableActivatedManaAbilities(Zone.HAND, playerId, game); - for (Iterator it = manaAbilities.iterator(); it.hasNext(); ) { + for (Iterator it = manaAbilities.iterator(); it.hasNext();) { ActivatedManaAbilityImpl ability = it.next(); Abilities noTapAbilities = new AbilitiesImpl<>(ability); if (ability.getManaCosts().isEmpty() && !ability.isPoolDependant()) { @@ -3256,7 +3257,7 @@ public abstract class PlayerImpl implements Player, Serializable { boolean useLater = false; // sources with mana costs or mana pool dependency Abilities manaAbilities = permanent.getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, playerId, game); // returns ability only if canActivate is true - for (Iterator it = manaAbilities.iterator(); it.hasNext(); ) { + for (Iterator it = manaAbilities.iterator(); it.hasNext();) { ActivatedManaAbilityImpl ability = it.next(); if (canUse == null) { canUse = permanent.canUseActivatedAbilities(game); @@ -3298,7 +3299,7 @@ public abstract class PlayerImpl implements Player, Serializable { boolean usePoolDependantAbilities = false; // use such abilities later than other if possible because it can maximize mana production while (anAbilityWasUsed && !sourceWithCosts.isEmpty()) { anAbilityWasUsed = false; - for (Iterator> iterator = sourceWithCosts.iterator(); iterator.hasNext(); ) { + for (Iterator> iterator = sourceWithCosts.iterator(); iterator.hasNext();) { Abilities manaAbilities = iterator.next(); if (usePoolDependantAbilities || !manaAbilities.hasPoolDependantAbilities()) { boolean used; @@ -3334,7 +3335,7 @@ public abstract class PlayerImpl implements Player, Serializable { * and cleared thereafter * * @param netManaAvailable the net mana produced by the triggered mana - * abaility + * abaility */ @Override public void addAvailableTriggeredMana(List netManaAvailable @@ -3416,7 +3417,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * @param ability * @param availableMana if null, it won't be checked if enough mana is - * available + * available * @param sourceObject * @param game * @return @@ -3805,7 +3806,6 @@ public abstract class PlayerImpl implements Player, Serializable { // spell/hand abilities (play from all zones) // need permitingObject or canPlayCardsFromGraveyard - // zone's abilities (play from specific zone) // no need in permitingObject if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) { @@ -3849,10 +3849,10 @@ public abstract class PlayerImpl implements Player, Serializable { * currently cast/activate with his available resources * * @param game - * @param hidden also from hidden objects (e.g. turned face down cards ?) - * @param fromZone of objects from which zone (ALL = from all zones) + * @param hidden also from hidden objects (e.g. turned face down cards ?) + * @param fromZone of objects from which zone (ALL = from all zones) * @param hideDuplicatedAbilities if equal abilities exist return only the - * first instance + * first instance * @return */ public List getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) { @@ -4434,7 +4434,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return moveCards(cards, toZone, source, game, false, false, false, null); } @@ -4590,7 +4590,7 @@ public abstract class PlayerImpl implements Player, Serializable { // identify cards from one owner Cards cards = new CardsImpl(); UUID ownerId = null; - for (Iterator it = allCards.iterator(); it.hasNext(); ) { + for (Iterator it = allCards.iterator(); it.hasNext();) { Card card = it.next(); if (cards.isEmpty()) { ownerId = card.getOwnerId(); @@ -4768,7 +4768,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' ' + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH) - + ' ' : "") + "to the exile zone" + CardUtil.getSourceLogName(game, source, card.getId())); + + ' ' : "") + "to the exile zone" + CardUtil.getSourceLogName(game, source, card.getId())); } }