mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 05:22:02 -08:00
* Gain abilities - fixed wrong order effects with changeling ability (all creature type effect, #6147);
This commit is contained in:
parent
1b4145e5b8
commit
b6d76a7c02
16 changed files with 220 additions and 32 deletions
|
|
@ -17,6 +17,7 @@ import mage.cards.FrameStyle;
|
|||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.SubTypeList;
|
||||
|
||||
|
|
@ -251,8 +252,14 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
if (value.getSubTypeSet() != SubTypeSet.CreatureType) {
|
||||
return false;
|
||||
}
|
||||
// as it is creature subtype, then check the existence of Changeling
|
||||
return abilities.contains(ChangelingAbility.getInstance()) || isAllCreatureTypes();
|
||||
// as it is a creature subtype, then check the existence of Changeling
|
||||
Abilities<Ability> checkList;
|
||||
if (this instanceof Permanent) {
|
||||
checkList = ((Permanent) this).getAbilities(game);
|
||||
} else {
|
||||
checkList = abilities;
|
||||
}
|
||||
return checkList.contains(ChangelingAbility.getInstance()) || isAllCreatureTypes();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,14 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
return super.getDependencyTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<DependencyType> getDependedToTypes() {
|
||||
if (effect != null) {
|
||||
return effect.getDependedToTypes();
|
||||
}
|
||||
return super.getDependedToTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer) {
|
||||
if (effect != null) {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ public interface ContinuousEffect extends Effect {
|
|||
|
||||
void setDependedToType(DependencyType dependencyType);
|
||||
|
||||
EnumSet<DependencyType> getDependedToTypes();
|
||||
|
||||
void addDependedToType(DependencyType dependencyType);
|
||||
|
||||
void setStartingControllerAndTurnNum(Game game, UUID startingController, UUID activePlayerId);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,17 @@ package mage.abilities.effects;
|
|||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.CompoundAbility;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.DomainValue;
|
||||
import mage.abilities.dynamicvalue.common.SignInversionDynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.keyword.ChangelingAbility;
|
||||
import mage.constants.*;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
|
|
@ -319,6 +324,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
return dependencyTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<DependencyType> getDependedToTypes() {
|
||||
return dependendToTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDependencyType(DependencyType dependencyType) {
|
||||
dependencyTypes.add(dependencyType);
|
||||
|
|
@ -341,4 +351,48 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-generates dependencies on different effects (what's apply first and what's apply second)
|
||||
*/
|
||||
public void generateGainAbilityDependencies(Ability abilityToGain, Filter filterToSearch) {
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
this.generateGainAbilityDependenciesFromAbility(abilityToGain);
|
||||
this.generateGainAbilityDependenciesFromFilter(filterToSearch);
|
||||
}
|
||||
|
||||
public void generateGainAbilityDependencies(CompoundAbility abilityToGain, Filter filterToSearch) {
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
this.generateGainAbilityDependenciesFromAbility(abilityToGain);
|
||||
this.generateGainAbilityDependenciesFromFilter(filterToSearch);
|
||||
}
|
||||
|
||||
private void generateGainAbilityDependenciesFromAbility(CompoundAbility compoundAbility) {
|
||||
if (compoundAbility == null) return;
|
||||
for (Ability ability : compoundAbility) {
|
||||
generateGainAbilityDependenciesFromAbility(ability);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateGainAbilityDependenciesFromAbility(Ability ability) {
|
||||
if (ability == null) return;
|
||||
|
||||
// 1. "Is all type" ability (changeling)
|
||||
// make dependency
|
||||
if (ability instanceof ChangelingAbility) {
|
||||
this.addDependencyType(DependencyType.AddingCreatureType);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateGainAbilityDependenciesFromFilter(Filter filter) {
|
||||
if (filter == null) return;
|
||||
|
||||
// 1. "Is all type" ability (changeling)
|
||||
// wait dependency
|
||||
// extraPredicates from some filters is player related, you don't need it here
|
||||
List<Predicate> list = new ArrayList<>();
|
||||
Predicates.collectAllComponents(filter.getPredicates(), list);
|
||||
if (list.stream().anyMatch(p -> p instanceof SubType.SubTypePredicate)) {
|
||||
this.addDependedToType(DependencyType.AddingCreatureType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
|
|||
this.ability.newId();
|
||||
this.filter = filter;
|
||||
this.excludeSource = excludeSource;
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
|
||||
this.generateGainAbilityDependencies(ability, filter);
|
||||
}
|
||||
|
||||
public GainAbilityAllEffect(final GainAbilityAllEffect effect) {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,7 @@ package mage.abilities.effects.common.continuous;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.AttachmentType;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
|
@ -50,7 +45,8 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl {
|
|||
} else {
|
||||
this.staticText = rule;
|
||||
}
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
|
||||
this.generateGainAbilityDependencies(ability, null);
|
||||
}
|
||||
|
||||
public GainAbilityAttachedEffect(final GainAbilityAttachedEffect effect) {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,9 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
|
|||
this.ability = ability;
|
||||
this.filter = filter;
|
||||
this.excludeSource = excludeSource;
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
setText();
|
||||
|
||||
this.generateGainAbilityDependencies(ability, filter);
|
||||
}
|
||||
|
||||
public GainAbilityControlledEffect(final GainAbilityControlledEffect effect) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import mage.MageObjectReference;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -48,7 +47,8 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
|
|||
if (noStaticText) {
|
||||
staticText = null;
|
||||
}
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
|
||||
this.generateGainAbilityDependencies(ability, null);
|
||||
}
|
||||
|
||||
public GainAbilitySourceEffect(final GainAbilitySourceEffect effect) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
|
|||
this.ability = ability;
|
||||
staticText = rule;
|
||||
this.onCard = onCard;
|
||||
this.addDependencyType(DependencyType.AddingAbility);
|
||||
|
||||
this.generateGainAbilityDependencies(ability, null);
|
||||
}
|
||||
|
||||
public GainAbilityTargetEffect(final GainAbilityTargetEffect effect) {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
|
||||
package mage.constants;
|
||||
|
||||
/**
|
||||
* Dependency types are a part of a workaround solution to handle dependencies
|
||||
* of continuous effects.
|
||||
*
|
||||
* <p>
|
||||
* All continuous effects can:
|
||||
* addDependencyType -- make dependency (effect makes some changes)
|
||||
* addDependedToType -- wait another dependency (effect must wait until all ather effects finished)
|
||||
* <p>
|
||||
* http://magiccards.info/rule/613-interaction-of-continuous-effects.html
|
||||
*
|
||||
* https://github.com/magefree/mage/issues/1259
|
||||
*
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum DependencyType {
|
||||
AuraAddingRemoving,
|
||||
ArtifactAddingRemoving,
|
||||
AddingAbility,
|
||||
AddingCreatureType,
|
||||
BecomeForest,
|
||||
BecomeIsland,
|
||||
BecomeMountain,
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ public class FilterCard extends FilterObject<Card> {
|
|||
|
||||
public Set<Card> filter(Set<Card> cards, Game game) {
|
||||
return cards.stream().filter(card -> match(card, game)).collect(Collectors.toSet());
|
||||
|
||||
}
|
||||
|
||||
public boolean hasPredicates() {
|
||||
|
|
|
|||
|
|
@ -71,5 +71,4 @@ public class FilterPermanent extends FilterObject<Permanent> implements FilterIn
|
|||
public FilterPermanent copy() {
|
||||
return new FilterPermanent(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,5 +56,4 @@ public class FilterPlayer extends FilterImpl<Player> {
|
|||
public FilterPlayer copy() {
|
||||
return new FilterPlayer(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package mage.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.filter.predicate.ObjectPlayer;
|
||||
import mage.filter.predicate.ObjectPlayerPredicate;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
|
|
@ -11,6 +8,10 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.StackObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
|
|
@ -51,5 +52,4 @@ public class FilterStackObject extends FilterObject<StackObject> {
|
|||
public FilterStackObject copy() {
|
||||
return new FilterStackObject(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ public final class Predicates {
|
|||
|
||||
/**
|
||||
* Returns a predicate that evaluates to {@code true} if the given predicate evaluates to {@code false}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param predicate
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> not(Predicate<T> predicate) {
|
||||
return new NotPredicate<T>(predicate);
|
||||
|
|
@ -34,9 +35,10 @@ public final class Predicates {
|
|||
* components are evaluated in order, and evaluation will be "short-circuited" as soon as a false predicate is
|
||||
* found. It defensively copies the iterable passed in, so future changes to it won't alter the behavior of this
|
||||
* predicate. If {@code components} is empty, the returned predicate will always evaluate to {@code true}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param components
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> and(Iterable<? extends Predicate<? super T>> components) {
|
||||
return new AndPredicate<T>(defensiveCopy(components));
|
||||
|
|
@ -47,9 +49,10 @@ public final class Predicates {
|
|||
* components are evaluated in order, and evaluation will be "short-circuited" as soon as a false predicate is
|
||||
* found. It defensively copies the array passed in, so future changes to it won't alter the behavior of this
|
||||
* predicate. If {@code components} is empty, the returned predicate will always evaluate to {@code true}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param components
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> and(Predicate<? super T>... components) {
|
||||
return new AndPredicate<T>(defensiveCopy(components));
|
||||
|
|
@ -59,10 +62,11 @@ public final class Predicates {
|
|||
* Returns a predicate that evaluates to {@code true} if both of its components evaluate to {@code true}. The
|
||||
* components are evaluated in order, and evaluation will be "short-circuited" as soon as a false predicate is
|
||||
* found.
|
||||
*
|
||||
* @param <T>
|
||||
* @param first
|
||||
* @param second
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> and(Predicate<? super T> first, Predicate<? super T> second) {
|
||||
return new AndPredicate<T>(Predicates.<T>asList(checkNotNull(first), checkNotNull(second)));
|
||||
|
|
@ -73,9 +77,10 @@ public final class Predicates {
|
|||
* components are evaluated in order, and evaluation will be "short-circuited" as soon as a true predicate is found.
|
||||
* It defensively copies the iterable passed in, so future changes to it won't alter the behavior of this predicate.
|
||||
* If {@code components} is empty, the returned predicate will always evaluate to {@code true}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param components
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> or(Iterable<? extends Predicate<? super T>> components) {
|
||||
return new OrPredicate<T>(defensiveCopy(components));
|
||||
|
|
@ -86,9 +91,10 @@ public final class Predicates {
|
|||
* components are evaluated in order, and evaluation will be "short-circuited" as soon as a true predicate is found.
|
||||
* It defensively copies the array passed in, so future changes to it won't alter the behavior of this predicate. If
|
||||
* {@code components} is empty, the returned predicate will always evaluate to {@code true}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param components
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> or(Predicate<? super T>... components) {
|
||||
return new OrPredicate<T>(defensiveCopy(components));
|
||||
|
|
@ -97,10 +103,11 @@ public final class Predicates {
|
|||
/**
|
||||
* Returns a predicate that evaluates to {@code true} if either of its components evaluates to {@code true}. The
|
||||
* components are evaluated in order, and evaluation will be "short-circuited" as soon as a true predicate is found.
|
||||
*
|
||||
* @param <T>
|
||||
* @param first
|
||||
* @param second
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> or(Predicate<? super T> first, Predicate<? super T> second) {
|
||||
return new OrPredicate<T>(Predicates.<T>asList(first, second));
|
||||
|
|
@ -126,6 +133,7 @@ public final class Predicates {
|
|||
public String toString() {
|
||||
return "Not(" + predicate.toString() + ')';
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
|
|
@ -150,6 +158,7 @@ public final class Predicates {
|
|||
public String toString() {
|
||||
return "And(" + commaJoin(components) + ')';
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +182,7 @@ public final class Predicates {
|
|||
public String toString() {
|
||||
return "Or(" + commaJoin(components) + ')';
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
|
|
@ -215,4 +225,25 @@ public final class Predicates {
|
|||
sb.deleteCharAt(sb.length() - 1);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect real predicates for searching some data (see dependency effect code)
|
||||
*/
|
||||
public static void collectAllComponents(Predicate predicate, List<Predicate> res) {
|
||||
if (predicate instanceof NotPredicate) {
|
||||
res.add(((NotPredicate) predicate).predicate);
|
||||
} else if (predicate instanceof AndPredicate) {
|
||||
collectAllComponents(((AndPredicate) predicate).components, res);
|
||||
} else if (predicate instanceof OrPredicate) {
|
||||
collectAllComponents(((OrPredicate) predicate).components, res);
|
||||
} else {
|
||||
res.add(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
public static void collectAllComponents(List<Predicate> predicates, List<Predicate> res) {
|
||||
predicates.forEach(p -> {
|
||||
collectAllComponents(p, res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue