mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 21:42:07 -08:00
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:
parent
fdcf2c616b
commit
a307e5934f
32 changed files with 291 additions and 197 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue