Improved some source related filters in effects:

* Fixed that some cards ignore range of influence or source related filters;
* Improved ChosenSubtypePredicate to work with gain abilities;
This commit is contained in:
Oleg Agafonov 2020-12-25 19:05:49 +04:00
parent fdcf2c616b
commit a307e5934f
32 changed files with 291 additions and 197 deletions

View file

@ -68,6 +68,6 @@ enum ArcticFoxesCondition implements Condition {
if (defenderId == null) {
return false;
}
return game.getBattlefield().contains(filter, defenderId, 1, game);
return game.getBattlefield().contains(filter, source.getSourceId(), defenderId, game, 1);
}
}

View file

@ -1,8 +1,5 @@
package mage.cards.c;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
@ -11,7 +8,6 @@ import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import static mage.cards.c.CityInABottle.getArabianNightsNamePredicates;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
@ -23,11 +19,15 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static mage.cards.c.CityInABottle.getArabianNightsNamePredicates;
/**
*
* @author emerald000
*/
public final class CityInABottle extends CardImpl {
@ -158,7 +158,7 @@ class CityInABottleStateTriggeredAbility extends StateTriggeredAbility {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return game.getBattlefield().contains(filter, this.getControllerId(), game, 1);
return game.getBattlefield().contains(filter, this.getSourceId(), this.getControllerId(), game, 1);
}
@Override

View file

@ -1,7 +1,5 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -18,14 +16,15 @@ import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class ConcertedEffort extends CardImpl {
public ConcertedEffort(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}");
// At the beginning of each upkeep, creatures you control gain flying until end of turn if a creature you control has flying. The same is true for fear, first strike, double strike, landwalk, protection, trample, and vigilance.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConcertedEffortEffect(), TargetController.ANY, false));
@ -81,22 +80,22 @@ class ConcertedEffortEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterFlying, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Fear
if (game.getBattlefield().contains(filterFear, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterFear, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FearAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterFirstStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterDoubleStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
@ -119,12 +118,12 @@ class ConcertedEffortEffect extends OneShotEffect {
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterTrample, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterVigilance, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
return true;

View file

@ -1,7 +1,5 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.common.DestroyTargetEffect;
@ -12,27 +10,30 @@ import mage.constants.ComparisonType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import java.util.UUID;
/**
*
* @author cg5
*/
public final class CullingScales extends CardImpl {
private static final FilterPermanent filterNonlandPermanentWithLowestCmc = new FilterNonlandPermanent(
"nonland permanent with the lowest converted mana cost (<i>If two or more permanents are tied for lowest cost, target any one of them.</i>)"
"nonland permanent with the lowest converted mana cost (<i>If two or more permanents are tied for lowest cost, target any one of them.</i>)"
);
static {
filterNonlandPermanentWithLowestCmc.add(new HasLowestCMCAmongstNonlandPermanentsPredicate());
}
public CullingScales(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// At the beginning of your upkeep, destroy target nonland permanent with the lowest converted mana cost.
Ability ability = new BeginningOfUpkeepTriggeredAbility(new DestroyTargetEffect(), TargetController.YOU, false);
@ -48,16 +49,16 @@ public final class CullingScales extends CardImpl {
public CullingScales copy() {
return new CullingScales(this);
}
}
class HasLowestCMCAmongstNonlandPermanentsPredicate implements Predicate<Permanent> {
class HasLowestCMCAmongstNonlandPermanentsPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<Permanent>> {
@Override
public boolean apply(Permanent input, Game game) {
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
FilterPermanent filter = new FilterNonlandPermanent();
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, input.getConvertedManaCost()));
return !game.getBattlefield().contains(filter, 1, game);
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, input.getObject().getConvertedManaCost()));
return !game.getBattlefield().contains(filter, input.getSourceId(), input.getPlayerId(), game, 1);
}
}

View file

@ -84,7 +84,7 @@ class DoomForetoldEffect extends OneShotEffect {
}
FilterPermanent filter2 = filter.copy();
filter2.add(new ControllerIdPredicate(player.getId()));
if (game.getBattlefield().contains(filter2, 1, game)) {
if (game.getBattlefield().contains(filter2, source, game, 1)) {
TargetPermanent target = new TargetPermanent(filter2);
target.setNotTarget(true);
if (player.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) {

View file

@ -1,7 +1,5 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
@ -24,14 +22,15 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class HedronAlignment extends CardImpl {
public HedronAlignment(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
// Hexproof
this.addAbility(HexproofAbility.getInstance());
@ -85,7 +84,7 @@ class HedronAlignmentEffect extends OneShotEffect {
Cards cardsToReveal = new CardsImpl();
controller.revealCards(sourceObject.getIdName(), cardsToReveal, game);
// Check battlefield
if (!game.getBattlefield().contains(filterPermanent, source.getControllerId(), game, 1)) {
if (!game.getBattlefield().contains(filterPermanent, source, game, 1)) {
return true;
}
if (controller.getHand().getCards(filterCard, source.getSourceId(), controller.getId(), game).isEmpty()) {

View file

@ -1,7 +1,5 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
@ -17,12 +15,7 @@ import mage.abilities.keyword.CumulativeUpkeepAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledLandPermanent;
import mage.filter.common.FilterLandPermanent;
@ -34,13 +27,15 @@ import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class HeraldOfLeshrac extends CardImpl {
private static final FilterPermanent filter = new FilterControlledLandPermanent("land you control but don't own");
static {
filter.add(TargetController.NOT_YOU.getOwnerPredicate());
}
@ -78,6 +73,7 @@ public final class HeraldOfLeshrac extends CardImpl {
class HeraldOfLeshracCumulativeCost extends CostImpl {
private static final FilterPermanent filter = new FilterLandPermanent("land you don't control");
static {
filter.add(TargetController.NOT_YOU.getControllerPredicate());
}
@ -105,7 +101,7 @@ class HeraldOfLeshracCumulativeCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return game.getBattlefield().contains(filter, controllerId, game, 1);
return game.getBattlefield().contains(filter, source.getSourceId(), controllerId, game, 1);
}
@Override

View file

@ -103,62 +103,62 @@ class MajesticMyriarchEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFlying, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), source);
}
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFirstStrike, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDoubleStrike, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source);
}
// Deathtouch
if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDeathtouch, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn), source);
}
// Haste
if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHaste, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), source);
}
// Hexproof
if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHexproof, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.EndOfTurn), source);
}
// Indestructible
if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterIndestructible, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), source);
}
// Lifelink
if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterLifelink, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), source);
}
// Menace
if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterMenace, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn), source);
}
// Reach
if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterReach, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(ReachAbility.getInstance(), Duration.EndOfTurn), source);
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterTrample, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterVigilance, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn), source);
}
return true;

View file

@ -1,4 +1,3 @@
package mage.cards.o;
import mage.MageInt;
@ -94,67 +93,67 @@ class OdricLunarchMarshalEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFirstStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFlying, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Deathtouch
if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDeathtouch, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDoubleStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Haste
if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHaste, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Hexproof
if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHexproof, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Indestructible
if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterIndestructible, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Lifelink
if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterLifelink, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Menace
if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterMenace, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Reach
if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterReach, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Skulk
if (game.getBattlefield().contains(filterSkulk, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterSkulk, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(new SkulkAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterTrample, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterVigilance, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
return true;

View file

@ -1,7 +1,5 @@
package mage.cards.r;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
@ -16,14 +14,15 @@ import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class RemorselessPunishment extends CardImpl {
public RemorselessPunishment(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
// Target opponent loses 5 life unless that player discards two cards or sacrifices a creature or planeswalker. Repeat this process once.
getSpellAbility().addEffect(new RemorselessPunishmentEffect());
@ -80,7 +79,7 @@ class RemorselessPunishmentEffect extends OneShotEffect {
return;
}
}
if (game.getBattlefield().contains(filter, opponent.getId(), 1, game)) {
if (game.getBattlefield().containsControlled(filter, source.getSourceId(), opponent.getId(), game, 1)) {
if (opponent.chooseUse(outcome, "Choose your " + iteration + " punishment.", null, "Sacrifice a creature or planeswalker", "Lose 5 life", source, game)) {
TargetPermanent target = new TargetPermanent(1, 1, filter, true);
if (target.choose(Outcome.Sacrifice, opponent.getId(), source.getId(), game)) {

View file

@ -1,7 +1,5 @@
package mage.cards.s;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -11,8 +9,8 @@ import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
@ -22,8 +20,9 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class ShelteringAncient extends CardImpl {
@ -82,7 +81,7 @@ class ShelteringAncientCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return game.getBattlefield().contains(filter, source.getSourceId(), game, 1);
return game.getBattlefield().contains(filter, source, game, 1);
}
@Override

View file

@ -1,7 +1,5 @@
package mage.cards.t;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
@ -18,8 +16,9 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author spjspj
*/
public final class TheCheeseStandsAlone extends CardImpl {
@ -47,6 +46,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp
private static final FilterControlledPermanent filter = new FilterControlledPermanent();
private boolean wonAlready = false;
static {
filter.add(new NamePredicate("The Cheese Stands Alone"));
}
@ -57,7 +57,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp
}
public CheeseStandsAloneContinuousEffect(final CheeseStandsAloneContinuousEffect effect) {
super(effect);
super(effect);
}
@Override
@ -72,7 +72,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp
if (controller.getHand().isEmpty()) {
int numberPerms = new PermanentsOnBattlefieldCount(new FilterControlledPermanent()).calculate(game, source, this);
if (numberPerms == 1) {
if (game.getBattlefield().contains(filter, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filter, source, game, 1)) {
if (!wonAlready) {
wonAlready = true;
controller.won(game);

View file

@ -1,7 +1,5 @@
package mage.cards.t;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility;
@ -31,8 +29,9 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import java.util.UUID;
/**
*
* @author LoneFox
*/
public final class TidalInfluence extends CardImpl {
@ -44,26 +43,26 @@ public final class TidalInfluence extends CardImpl {
}
public TidalInfluence(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
// Cast Tidal Influence only if no permanents named Tidal Influence are on the battlefield.
this.getSpellAbility().addCost(new TidalInfluenceCost());
// Tidal Influence enters the battlefield with a tide counter on it.
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()),
"with a tide counter on it."));
"with a tide counter on it."));
// At the beginning of your upkeep, put a tide counter on Tidal Influence.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()),
TargetController.YOU, false));
TargetController.YOU, false));
// As long as there is exactly one tide counter on Tidal Influence, all blue creatures get -2/-0.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 1, 1),
"As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0.")));
new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 1, 1),
"As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0.")));
// As long as there are exactly three tide counters on Tidal Influence, all blue creatures get +2/+0.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostAllEffect(2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 3, 3),
"As long as there are exactly three tide counter on {this}, all blue creatures get +2/-0.")));
new BoostAllEffect(2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 3, 3),
"As long as there are exactly three tide counter on {this}, all blue creatures get +2/-0.")));
// Whenever there are four tide counters on Tidal Influence, remove all tide counters from it.
this.addAbility(new TidalInfluenceTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.TIDE)));
}
@ -97,7 +96,7 @@ class TidalInfluenceCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return !game.getBattlefield().contains(filter, 1, game);
return !game.getBattlefield().contains(filter, source, game, 1);
}
@Override

View file

@ -1,13 +1,13 @@
package mage.cards.t;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
import mage.abilities.effects.common.ChooseLandTypeEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
@ -15,24 +15,24 @@ import mage.abilities.keyword.EnchantAbility;
import mage.abilities.keyword.LandwalkAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class TravelersCloak extends CardImpl {
public TravelersCloak(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
this.subtype.add(SubType.AURA);
// Enchant creature
@ -41,18 +41,17 @@ public final class TravelersCloak extends CardImpl {
this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// As Traveler's Cloak enters the battlefield, choose a land type.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseLandTypeEffect(Outcome.AddAbility)));
// When Traveler's Cloak enters the battlefield, draw a card.
this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
// Enchanted creature has landwalk of the chosen type.
FilterLandPermanent filter = new FilterLandPermanent("Landwalk of the chosen type");
filter.add(ChosenSubtypePredicate.instance);
Ability landwalkAbility = new LandwalkAbility(filter);
Effect effect = new GainAbilityAttachedEffect(landwalkAbility, AttachmentType.AURA);
filter.add(TravelersCloakChosenSubtypePredicate.instance);
Effect effect = new TravelersCloakGainAbilityAttachedEffect(filter);
effect.setText("Enchanted creature has landwalk of the chosen type");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
}
@ -66,3 +65,35 @@ public final class TravelersCloak extends CardImpl {
return new TravelersCloak(this);
}
}
class TravelersCloakGainAbilityAttachedEffect extends GainAbilityAttachedEffect {
public TravelersCloakGainAbilityAttachedEffect(FilterLandPermanent filter) {
super(new LandwalkAbility(filter), AttachmentType.AURA);
}
@Override
public void afterGain(Game game, Ability source, Permanent permanent, Ability addedAbility) {
super.afterGain(game, source, permanent, addedAbility);
// ChooseLandTypeEffect keep settings in original source, but we must transfer it to real permanent
Object val = game.getState().getValue(source.getSourceId() + "_type");
game.getState().setValue(permanent.getId() + "_landwalk_type", val);
}
}
enum TravelersCloakChosenSubtypePredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageObject>> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(input.getSourceId(), game, "_landwalk_type");
return input.getObject().hasSubtype(subType, game);
}
@Override
public String toString() {
return "Chosen subtype";
}
}

View file

@ -1,9 +1,5 @@
package mage.cards.w;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
@ -25,8 +21,12 @@ import mage.target.TargetPermanent;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class WeightOfConscience extends CardImpl {
@ -84,8 +84,8 @@ class WeightOfConscienceEffect extends OneShotEffect {
// It was not blinked, use the standard method
enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
}
if (controller != null
&& enchantment != null
if (controller != null
&& enchantment != null
&& enchantment.getAttachedTo() != null) {
Permanent creature = game.getPermanent(enchantment.getAttachedTo());
if (creature != null) {
@ -119,10 +119,10 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
if (player != null) {
// Choosing first target
if (this.getTargets().isEmpty()) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
for (SubType subtype : permanent.getSubtype(game)) {
if (subtype.getSubTypeSet() == SubTypeSet.CreatureType) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), sourceControllerId, game, 2)) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), sourceId, sourceControllerId, game, 2)) {
possibleTargets.add(permanent.getId());
}
}
@ -133,7 +133,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
UUID firstTargetId = this.getTargets().get(0);
Permanent firstTargetCreature = game.getPermanent(firstTargetId);
if (firstTargetCreature != null) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
if (!permanent.getId().equals(firstTargetId) && firstTargetCreature.shareCreatureTypes(permanent, game)) {
possibleTargets.add(permanent.getId());
}
@ -146,8 +146,8 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
@Override
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
for (Permanent permanent1 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent2 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent1 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
for (Permanent permanent2 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
if (!Objects.equals(permanent1, permanent2) && permanent1.shareCreatureTypes(permanent2, game)) {
return true;
}
@ -162,10 +162,10 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
Permanent targetPermanent = game.getPermanent(id);
if (targetPermanent != null) {
if (this.getTargets().isEmpty()) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, source.getControllerId(), game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, source.getControllerId(), source.getSourceId(), game)) {
for (SubType subtype : permanent.getSubtype(game)) {
if (subtype.getSubTypeSet() == SubTypeSet.CreatureType) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), source.getControllerId(), game, 2)) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), source, game, 2)) {
return true;
}
}
@ -173,9 +173,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
}
} else {
Permanent firstTarget = game.getPermanent(this.getTargets().get(0));
if (firstTarget != null && firstTarget.shareCreatureTypes(targetPermanent, game)) {
return true;
}
return firstTarget != null && firstTarget.shareCreatureTypes(targetPermanent, game);
}
}
}

View file

@ -69,7 +69,7 @@ public final class Wirecat extends CardImpl {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getId().equals(source.getSourceId())) {
return game.getBattlefield().contains(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, 1, game);
return game.getBattlefield().contains(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source, game, 1);
}
return false;
}

View file

@ -1,4 +1,3 @@
package org.mage.test.cards.conditional;
import mage.constants.PhaseStep;
@ -7,7 +6,6 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class LegendarySorceryTest extends CardTestPlayerBase {
@ -55,10 +53,13 @@ public class LegendarySorceryTest extends CardTestPlayerBase {
// Flying, first strike, vigilance, trample, haste, protection from black and from red
addCard(Zone.BATTLEFIELD, playerB, "Akroma, Angel of Wrath", 1); // Legendary
// can't cast cause you don't have a legendary creature (only opponent have)
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Urza's Ruinous Blast");
//setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
//assertAllCommandsUsed();
assertGraveyardCount(playerA, "Urza's Ruinous Blast", 0);

View file

@ -0,0 +1,54 @@
package org.mage.test.cards.single.inv;
import mage.abilities.keyword.LandwalkAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class TravelersCloakTest extends CardTestPlayerBase {
@Test
public void test_MustHaveLandWalkOfTheChosenType() {
// Enchant creature
// As Traveler's Cloak enters the battlefield, choose a land type.
// When Traveler's Cloak enters the battlefield, draw a card.
// Enchanted creature has landwalk of the chosen type.
addCard(Zone.HAND, playerA, "Traveler's Cloak"); // {2}{U}
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
//
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 1);
// cast and assign landwalk ability to creature
checkAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", LandwalkAbility.class, false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Traveler's Cloak", "Grizzly Bears");
setChoice(playerA, "Swamp"); // land type for landwalk
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", LandwalkAbility.class, true);
// check that it can't be blocked
attack(1, playerA, "Grizzly Bears");
runCode("check blocking", 1, PhaseStep.DECLARE_BLOCKERS, playerB, (info, player, game) -> {
Permanent blocker = game.getBattlefield().getAllActivePermanents()
.stream()
.filter(p -> p.getName().equals("Kitesail Corsair"))
.findFirst()
.get();
Assert.assertFalse("Grizzly Bears must be protected from blocking by Kitesail Corsair",
game.getCombat().getGroups().get(0).canBlock(blocker, game));
});
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
}

View file

@ -9,7 +9,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
public class ConspicuousSnoopTest extends CardTestPlayerBase {
@Test
public void testTopCardLibraryRevealed(){
public void test_TopCardLibraryRevealed() {
// Play with the top card of your library revealed.
addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop");
addCard(Zone.LIBRARY, playerA, "Swamp");
@ -23,43 +23,49 @@ public class ConspicuousSnoopTest extends CardTestPlayerBase {
}
@Test
public void castGoblinSpellsFromLibrary(){
public void test_castGoblinSpellsFromLibrary() {
skipInitShuffling();
removeAllCardsFromLibrary(playerA);
// You may cast Goblin spells from the top of your library.
addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop");
addCard(Zone.LIBRARY, playerA, "Goblin Lackey");
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
// Whenever Goblin Lackey deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield.
addCard(Zone.LIBRARY, playerA, "Atog", 1); // second from top
addCard(Zone.LIBRARY, playerA, "Goblin Lackey", 1); // first from top
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 * 2);
// Whenever Goblin Lackey deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield.
checkPlayableAbility("can play goblin", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Goblin Lackey", true);
checkPlayableAbility("can't play non goblin before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Atog", false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Lackey");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkPlayableAbility("can't play non goblin after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Atog", false);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Goblin Lackey", 1);
assertPermanentCount(playerA, "Goblin Lackey", 1);
}
@Test
public void hasActivatedAbilities(){
public void test_hasActivatedAbilities() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
// Play with the top card of your library revealed.
// You may cast Goblin spells from the top of your library.
// As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.
addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop");
// {R}: Goblin Balloon Brigade gains flying until end of turn.
addCard(Zone.LIBRARY, playerA, "Goblin Balloon Brigade");
addCard(Zone.LIBRARY, playerA, "Goblin Balloon Brigade");
skipInitShuffling();
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertAllCommandsUsed();
assertAbilityCount(playerA, "Conspicuous Snoop", ActivatedAbility.class, 3); // (2 X casts + gains flying )
}
}

View file

@ -2,11 +2,7 @@ package mage.abilities.common;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.game.Game;
@ -65,7 +61,7 @@ class LegendarySpellAbilityCheckEffect extends ContinuousRuleModifyingEffectImpl
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getSourceId().equals(source.getSourceId())
&& !game.getBattlefield().contains(filter, event.getPlayerId(), 1, game);
&& !game.getBattlefield().containsControlled(filter, source, game, 1);
}
@Override

View file

@ -22,7 +22,7 @@ public enum MetalcraftCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
return game.getBattlefield().contains(filter, source.getControllerId(), 3, game);
return game.getBattlefield().containsControlled(filter, source, game, 3);
}
@Override

View file

@ -1,15 +1,15 @@
package mage.abilities.costs.common;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import java.util.UUID;
import mage.abilities.costs.Cost;
public class ControlPermanentCost extends CostImpl {
private FilterControlledPermanent filter;
private final FilterControlledPermanent filter;
public ControlPermanentCost(FilterControlledPermanent filter) {
this.filter = filter.copy();
@ -23,7 +23,7 @@ public class ControlPermanentCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return game.getBattlefield().contains(filter, controllerId, 1, game);
return game.getBattlefield().containsControlled(filter, source.getSourceId(), controllerId, game, 1);
}
@Override

View file

@ -1,7 +1,5 @@
package mage.abilities.effects.common;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
@ -14,6 +12,8 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author LevelX2
*/
@ -56,15 +56,19 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
return new ChooseCreatureTypeEffect(this);
}
public static SubType getChosenCreatureType(UUID objectId, Game game) {
return getChosenCreatureType(objectId, game, "_type");
}
/**
*
* @param objectId sourceId the effect was exeuted under
* @param objectId sourceId the effect was exeuted under
* @param game
* @param typePostfix special postfix if you want to store multiple choices from different effects
* @return
*/
public static SubType getChosenCreatureType(UUID objectId, Game game) {
public static SubType getChosenCreatureType(UUID objectId, Game game, String typePostfix) {
SubType creatureType = null;
Object savedCreatureType = game.getState().getValue(objectId + "_type");
Object savedCreatureType = game.getState().getValue(objectId + typePostfix);
if (savedCreatureType != null) {
creatureType = SubType.byDescription(savedCreatureType.toString());
}

View file

@ -96,10 +96,23 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl {
}
if (permanent != null) {
permanent.addAbility(ability, source.getSourceId(), game);
afterGain(game, source, permanent, ability);
}
return true;
}
/**
* Calls after ability gain. Override it to apply additional data (example: transfer ability's settings from original to destination source)
*
* @param game
* @param source
* @param permanent
* @param addedAbility
*/
public void afterGain(Game game, Ability source, Permanent permanent, Ability addedAbility) {
//
}
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append(attachmentType.verb());

View file

@ -61,7 +61,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
&& playerId.equals(source.getControllerId())
&& cardToCheck.isOwnedBy(source.getControllerId())
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
&& filter.match(cardToCheck, game)) {
&& filter.match(cardToCheck, source.getSourceId(), source.getControllerId(), game)) {
Player player = game.getPlayer(cardToCheck.getOwnerId());
UUID needCardID = player.getLibrary().getFromTop(game) == null ? null : player.getLibrary().getFromTop(game).getId();

View file

@ -67,7 +67,7 @@ public class AmassEffect extends OneShotEffect {
if (player == null) {
return false;
}
if (!game.getBattlefield().contains(filter, source.getControllerId(), 1, game)) {
if (!game.getBattlefield().containsControlled(filter, source, game, 1)) {
new CreateTokenEffect(new ZombieArmyToken()).apply(game, source);
}
Target target = new TargetPermanent(filter);

View file

@ -104,7 +104,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
@Override
public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && game.getBattlefield().contains(filterUntapped, controller.getId(), 1, game)) {
if (controller != null && game.getBattlefield().containsControlled(filterUntapped, source, game, 1)) {
if (source.getAbilityType() == AbilityType.SPELL) {
SpecialAction specialAction = new ConvokeSpecialAction(unpaid, this);
specialAction.setControllerId(source.getControllerId());

View file

@ -14,6 +14,12 @@ import mage.game.permanent.Permanent;
*/
public class LandwalkAbility extends EvasionAbility {
/**
* Don't use source related filters here (example: landwalk for user selected land type).
* If you want it then use workaround from Traveler's Cloak to transfer settings after gain
*
* @param filter
*/
public LandwalkAbility(FilterLandPermanent filter) {
this(filter, true);
}
@ -39,7 +45,6 @@ public class LandwalkAbility extends EvasionAbility {
}
return ruleText;
}
}
class LandwalkEffect extends RestrictionEffect {
@ -59,7 +64,7 @@ class LandwalkEffect extends RestrictionEffect {
@Override
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) {
if (game.getBattlefield().contains(filter, blocker.getControllerId(), 1, game)
if (game.getBattlefield().contains(filter, source.getSourceId(), blocker.getControllerId(), game, 1)
&& null == game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_LANDWALK, source, blocker.getControllerId(), game)) {
switch (filter.getMessage()) {
case "plains":
@ -74,7 +79,6 @@ class LandwalkEffect extends RestrictionEffect {
return null != game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_FORESTWALK, source, blocker.getControllerId(), game);
default:
return false;
}
}
return true;

View file

@ -1,4 +1,3 @@
package mage.filter.predicate.mageobject;
import mage.MageObject;
@ -9,6 +8,9 @@ import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
/**
* Warning, chosen type assign to original source ability, but after gain you will see another sourceId,
* see Traveler's Cloak for workaround to trasfer settings
*
* @author LoneFox
*/
public enum ChosenSubtypePredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageObject>> {

View file

@ -2276,7 +2276,7 @@ public abstract class GameImpl implements Game, Serializable {
filterLegendName.add(SuperType.LEGENDARY.getPredicate());
filterLegendName.add(new NamePredicate(legend.getName()));
filterLegendName.add(new ControllerIdPredicate(legend.getControllerId()));
if (getBattlefield().contains(filterLegendName, legend.getControllerId(), this, 2)) {
if (getBattlefield().contains(filterLegendName, null, legend.getControllerId(), this, 2)) {
if (!replaceEvent(GameEvent.getEvent(GameEvent.EventType.DESTROY_PERMANENT_BY_LEGENDARY_RULE, legend.getId(), legend.getControllerId()))) {
Player controller = this.getPlayer(legend.getControllerId());
if (controller != null) {

View file

@ -50,7 +50,7 @@ class GideonOfTheTrialsCantLoseEffect extends ContinuousRuleModifyingEffectImpl
public boolean applies(GameEvent event, Ability source, Game game) {
if ((event.getType() == GameEvent.EventType.WINS && game.getOpponents(source.getControllerId()).contains(event.getPlayerId()))
|| (event.getType() == GameEvent.EventType.LOSES && event.getPlayerId().equals(source.getControllerId()))) {
return game.getBattlefield().contains(filter, source.getControllerId(), 1, game);
return game.getBattlefield().containsControlled(filter, source, game, 1);
}
return false;
}

View file

@ -1,5 +1,6 @@
package mage.game.permanent;
import mage.abilities.Ability;
import mage.abilities.keyword.PhasingAbility;
import mage.constants.CardType;
import mage.constants.RangeOfInfluence;
@ -92,21 +93,8 @@ public class Battlefield implements Serializable {
}
}
/**
* Returns true if the battlefield contains at least 1 {@link Permanent}
* that matches the filter. This method ignores the range of influence.
*
* @param filter
* @param num
* @param game
* @return boolean
*/
public boolean contains(FilterPermanent filter, int num, Game game) {
return field.values()
.stream()
.filter(permanent -> filter.match(permanent, game)
&& permanent.isPhasedIn()).count() >= num;
public boolean containsControlled(FilterPermanent filter, Ability source, Game game, int num) {
return containsControlled(filter, source.getSourceId(), source.getControllerId(), game, num);
}
/**
@ -115,43 +103,49 @@ public class Battlefield implements Serializable {
* ignores the range of influence.
*
* @param filter
* @param controllerId
* @param sourceId
* @param controllerId controller and source can be different (from different players)
* @param num
* @param game
* @return boolean
*/
public boolean contains(FilterPermanent filter, UUID controllerId, int num, Game game) {
public boolean containsControlled(FilterPermanent filter, UUID sourceId, UUID controllerId, Game game, int num) {
return field.values()
.stream()
.filter(permanent -> permanent.isControlledBy(controllerId)
&& filter.match(permanent, game)
&& filter.match(permanent, sourceId, controllerId, game)
&& permanent.isPhasedIn())
.count() >= num;
}
public boolean contains(FilterPermanent filter, Ability source, Game game, int num) {
return contains(filter, source.getSourceId(), source.getControllerId(), game, num);
}
/**
* Returns true if the battlefield contains num or more {@link Permanent}
* that is within the range of influence of the specified player id and that
* matches the supplied filter.
*
* @param filter
* @param sourceId can be null for default SBA checks like legendary rule
* @param sourcePlayerId
* @param game
* @param num
* @return boolean
*/
public boolean contains(FilterPermanent filter, UUID sourcePlayerId, Game game, int num) {
public boolean contains(FilterPermanent filter, UUID sourceId, UUID sourcePlayerId, Game game, int num) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return field.values().stream()
.filter(permanent -> filter.match(permanent, null, sourcePlayerId, game)
.filter(permanent -> filter.match(permanent, sourceId, sourcePlayerId, game)
&& permanent.isPhasedIn()).count() >= num;
} else {
List<UUID> range = game.getState().getPlayersInRange(sourcePlayerId, game);
return field.values().stream()
.filter(permanent -> range.contains(permanent.getControllerId())
&& filter.match(permanent, null, sourcePlayerId, game)
&& filter.match(permanent, sourceId, sourcePlayerId, game)
&& permanent.isPhasedIn())
.count() >= num;
}