* Fixed problems of Yixlid Jailer that removed abilities from cards in graveyard permanently (fixes #1147).

This commit is contained in:
LevelX2 2020-01-03 15:23:52 +01:00
parent 893bcbb01f
commit 8854871c15
28 changed files with 248 additions and 180 deletions

View file

@ -1,6 +1,8 @@
package mage.view; package mage.view;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
import java.util.*;
import java.util.stream.Collectors;
import mage.MageObject; import mage.MageObject;
import mage.ObjectColor; import mage.ObjectColor;
import mage.abilities.Abilities; import mage.abilities.Abilities;
@ -26,9 +28,6 @@ import mage.target.Target;
import mage.target.Targets; import mage.target.Targets;
import mage.util.SubTypeList; import mage.util.SubTypeList;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -211,8 +210,8 @@ public class CardView extends SimpleCardView {
* @param card * @param card
* @param game * @param game
* @param controlled is the card view created for the card controller - used * @param controlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for * for morph / face down cards to know which player may see information for
* the card * the card
*/ */
public CardView(Card card, Game game, boolean controlled) { public CardView(Card card, Game game, boolean controlled) {
this(card, game, controlled, false, false); this(card, game, controlled, false, false);
@ -238,12 +237,12 @@ public class CardView extends SimpleCardView {
/** /**
* @param card * @param card
* @param game * @param game
* @param controlled is the card view created for the card controller - used * @param controlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for * for morph / face down cards to know which player may see information for
* the card * the card
* @param showFaceDownCard if true and the card is not on the battlefield, * @param showFaceDownCard if true and the card is not on the battlefield,
* also a face down card is shown in the view, face down cards will be shown * also a face down card is shown in the view, face down cards will be shown
* @param storeZone if true the card zone will be set in the zone attribute. * @param storeZone if true the card zone will be set in the zone attribute.
*/ */
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) { public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor()); super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor());
@ -436,7 +435,7 @@ public class CardView extends SimpleCardView {
artRect = ArtRect.SPLIT_FUSED; artRect = ArtRect.SPLIT_FUSED;
} else if (spell.getCard() != null) { } else if (spell.getCard() != null) {
SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard(); SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard();
Abilities<Ability> aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(); Abilities<Ability> aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(game);
if (aftermathHalfAbilities.stream().anyMatch(ability -> ability instanceof AftermathAbility)) { if (aftermathHalfAbilities.stream().anyMatch(ability -> ability instanceof AftermathAbility)) {
if (ty == SpellAbilityType.SPLIT_RIGHT) { if (ty == SpellAbilityType.SPLIT_RIGHT) {
artRect = ArtRect.AFTERMATH_BOTTOM; artRect = ArtRect.AFTERMATH_BOTTOM;
@ -470,7 +469,7 @@ public class CardView extends SimpleCardView {
this.startingLoyalty = "" + card.getStartingLoyalty(); this.startingLoyalty = "" + card.getStartingLoyalty();
} }
public CardView(MageObject object) { public CardView(MageObject object, Game game) {
super(object.getId(), "", "0", false, "", true, ""); super(object.getId(), "", "0", false, "", true, "");
this.originalCard = null; this.originalCard = null;

View file

@ -1,5 +1,6 @@
package mage.view; package mage.view;
import java.util.*;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
@ -14,8 +15,6 @@ import mage.game.permanent.PermanentToken;
import mage.target.targetpointer.TargetPointer; import mage.target.targetpointer.TargetPointer;
import mage.util.GameLog; import mage.util.GameLog;
import java.util.*;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -102,7 +101,7 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
} else if (isCard) { } else if (isCard) {
sourceCardView = new CardView((Card) sourceObject); sourceCardView = new CardView((Card) sourceObject);
} else { } else {
sourceCardView = new CardView(sourceObject); sourceCardView = new CardView(sourceObject, game);
} }
abilityView = new AbilityView(ability, sourceObject.getName(), sourceCardView); abilityView = new AbilityView(ability, sourceObject.getName(), sourceCardView);
} }

View file

@ -2,6 +2,11 @@ package mage.view;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.abilities.costs.Cost; import mage.abilities.costs.Cost;
import mage.cards.Card; import mage.cards.Card;
@ -25,12 +30,6 @@ import mage.players.Player;
import mage.watchers.common.CastSpellLastTurnWatcher; import mage.watchers.common.CastSpellLastTurnWatcher;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -121,7 +120,7 @@ public class GameView implements Serializable {
} else if (object instanceof Designation) { } else if (object instanceof Designation) {
Designation designation = (Designation) game.getObject(object.getId()); Designation designation = (Designation) game.getObject(object.getId());
if (designation != null) { if (designation != null) {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, designation.getName(), new CardView(designation))); stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, designation.getName(), new CardView(designation, game)));
} else { } else {
LOGGER.fatal("Designation object not found: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString()); LOGGER.fatal("Designation object not found: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());
} }
@ -129,7 +128,7 @@ public class GameView implements Serializable {
} else if (object instanceof StackAbility) { } else if (object instanceof StackAbility) {
StackAbility stackAbility = ((StackAbility) object); StackAbility stackAbility = ((StackAbility) object);
stackAbility.newId(); stackAbility.newId();
stack.put(stackObject.getId(), new CardView(stackObject)); stack.put(stackObject.getId(), new CardView(stackObject, game));
checkPaid(stackObject.getId(), ((StackAbility) stackObject)); checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else { } else {
LOGGER.fatal("Object can't be cast to StackAbility: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString()); LOGGER.fatal("Object can't be cast to StackAbility: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());

View file

@ -1,5 +1,8 @@
package mage.cards.a; package mage.cards.a;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ReplacementEffectImpl;
@ -18,11 +21,6 @@ import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.watchers.Watcher; import mage.watchers.Watcher;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
/** /**
* @author jeffwadsworth * @author jeffwadsworth
*/ */
@ -114,7 +112,7 @@ class AbandonedSarcophagusReplacementEffect extends ReplacementEffectImpl {
|| !card.isOwnedBy(controller.getId())) { || !card.isOwnedBy(controller.getId())) {
return false; return false;
} }
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof CyclingAbility) { if (ability instanceof CyclingAbility) {
cardHasCycling = true; cardHasCycling = true;
} }

View file

@ -1,6 +1,7 @@
package mage.cards.c; package mage.cards.c;
import java.util.Iterator;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.ContinuousEffectImpl;
@ -18,10 +19,6 @@ import mage.game.stack.Spell;
import mage.game.stack.StackObject; import mage.game.stack.StackObject;
import mage.players.Player; import mage.players.Player;
import java.util.Iterator;
import java.util.UUID;
/** /**
* @author magenoxx_at_gmail.com * @author magenoxx_at_gmail.com
*/ */
@ -34,7 +31,7 @@ public final class CastThroughTime extends CardImpl {
} }
public CastThroughTime(UUID ownerId, CardSetInfo setInfo) { public CastThroughTime(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{U}{U}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{U}{U}");
// Instant and sorcery spells you control have rebound. // Instant and sorcery spells you control have rebound.
// (Exile the spell as it resolves if you cast it from your hand. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.) // (Exile the spell as it resolves if you cast it from your hand. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.)
@ -73,7 +70,7 @@ class GainReboundEffect extends ContinuousEffectImpl {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && permanent != null) { if (player != null && permanent != null) {
for (Card card : player.getHand().getCards(CastThroughTime.filter, game)) { for (Card card : player.getHand().getCards(CastThroughTime.filter, game)) {
addReboundAbility(card, source, game); addReboundAbility(card, game);
} }
for (Iterator<StackObject> iterator = game.getStack().iterator(); iterator.hasNext();) { for (Iterator<StackObject> iterator = game.getStack().iterator(); iterator.hasNext();) {
StackObject stackObject = iterator.next(); StackObject stackObject = iterator.next();
@ -81,7 +78,7 @@ class GainReboundEffect extends ContinuousEffectImpl {
Spell spell = (Spell) stackObject; Spell spell = (Spell) stackObject;
Card card = spell.getCard(); Card card = spell.getCard();
if (card != null) { if (card != null) {
addReboundAbility(card, source, game); addReboundAbility(card, game);
} }
} }
@ -91,9 +88,9 @@ class GainReboundEffect extends ContinuousEffectImpl {
return false; return false;
} }
private void addReboundAbility(Card card, Ability source, Game game) { private void addReboundAbility(Card card, Game game) {
if (CastThroughTime.filter.match(card, game)) { if (CastThroughTime.filter.match(card, game)) {
boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof ReboundAbility); boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility);
if (!found) { if (!found) {
Ability ability = new ReboundAbility(); Ability ability = new ReboundAbility();
game.getState().addOtherAbility(card, ability); game.getState().addOtherAbility(card, ability);

View file

@ -1,4 +1,3 @@
package mage.cards.d; package mage.cards.d;
import java.util.UUID; import java.util.UUID;
@ -108,7 +107,7 @@ class DarkImpostorContinuousEffect extends ContinuousEffectImpl {
for (UUID imprintedId : perm.getImprinted()) { for (UUID imprintedId : perm.getImprinted()) {
Card card = game.getCard(imprintedId); Card card = game.getCard(imprintedId);
if (card != null) { if (card != null) {
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof ActivatedAbility) { if (ability instanceof ActivatedAbility) {
perm.addAbility(ability.copy(), source.getSourceId(), game); perm.addAbility(ability.copy(), source.getSourceId(), game);
} }

View file

@ -1,4 +1,3 @@
package mage.cards.d; package mage.cards.d;
import java.util.UUID; import java.util.UUID;
@ -28,8 +27,7 @@ import mage.target.TargetSpell;
public final class Delay extends CardImpl { public final class Delay extends CardImpl {
public Delay(UUID ownerId, CardSetInfo setInfo) { public Delay(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
// Counter target spell. If the spell is countered this way, exile it with three time counters on it instead of putting it into its owner's graveyard. If it doesn't have suspend, it gains suspend. // Counter target spell. If the spell is countered this way, exile it with three time counters on it instead of putting it into its owner's graveyard. If it doesn't have suspend, it gains suspend.
this.getSpellAbility().addEffect(new DelayEffect()); this.getSpellAbility().addEffect(new DelayEffect());
@ -71,7 +69,7 @@ class DelayEffect extends OneShotEffect {
effect.setTargetPointer(targetPointer); effect.setTargetPointer(targetPointer);
Card card = game.getCard(spell.getSourceId()); Card card = game.getCard(spell.getSourceId());
if (card != null && effect.apply(game, source) && game.getState().getZone(card.getId()) == Zone.EXILED) { if (card != null && effect.apply(game, source) && game.getState().getZone(card.getId()) == Zone.EXILED) {
boolean hasSuspend = card.getAbilities().containsClass(SuspendAbility.class); boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class);
UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game);
if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getLogName(), source.getSourceId(), game, Zone.HAND, true)) { if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getLogName(), source.getSourceId(), game, Zone.HAND, true)) {
card.addCounters(CounterType.TIME.createInstance(3), source, game); card.addCounters(CounterType.TIME.createInstance(3), source, game);

View file

@ -1,4 +1,3 @@
package mage.cards.f; package mage.cards.f;
import java.util.UUID; import java.util.UUID;
@ -15,8 +14,8 @@ import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate;
@ -36,7 +35,7 @@ public final class Filth extends CardImpl {
} }
public Filth(UUID ownerId, CardSetInfo setInfo) { public Filth(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.subtype.add(SubType.INCARNATION); this.subtype.add(SubType.INCARNATION);
this.power = new MageInt(2); this.power = new MageInt(2);
@ -47,7 +46,7 @@ public final class Filth extends CardImpl {
// As long as Filth is in your graveyard and you control a Swamp, creatures you control have swampwalk. // As long as Filth is in your graveyard and you control a Swamp, creatures you control have swampwalk.
ContinuousEffect effect = new GainAbilityControlledEffect(new SwampwalkAbility(), ContinuousEffect effect = new GainAbilityControlledEffect(new SwampwalkAbility(),
Duration.WhileOnBattlefield, new FilterCreaturePermanent()); Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES);
ConditionalContinuousEffect filthEffect = new ConditionalContinuousEffect(effect, ConditionalContinuousEffect filthEffect = new ConditionalContinuousEffect(effect,
new PermanentsOnTheBattlefieldCondition(filter), ruleText); new PermanentsOnTheBattlefieldCondition(filter), ruleText);
this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, filthEffect)); this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, filthEffect));

View file

@ -1,4 +1,3 @@
package mage.cards.h; package mage.cards.h;
import java.util.UUID; import java.util.UUID;
@ -16,11 +15,11 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType; import mage.constants.AsThoughEffectType;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer; import mage.constants.Layer;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
@ -39,7 +38,7 @@ public final class HavengulLich extends CardImpl {
private static final FilterCard filter = new FilterCreatureCard("creature card in a graveyard"); private static final FilterCard filter = new FilterCreatureCard("creature card in a graveyard");
public HavengulLich(UUID ownerId, CardSetInfo setInfo) { public HavengulLich(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}");
this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.WIZARD); this.subtype.add(SubType.WIZARD);
@ -188,7 +187,7 @@ class HavengulLichEffect extends ContinuousEffectImpl {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
Card card = game.getCard(cardId); Card card = game.getCard(cardId);
if (permanent != null && card != null) { if (permanent != null && card != null) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) { for (ActivatedAbility ability : card.getAbilities(game).getActivatedAbilities(Zone.BATTLEFIELD)) {
permanent.addAbility(ability, source.getSourceId(), game); permanent.addAbility(ability, source.getSourceId(), game);
} }
} }

View file

@ -1,5 +1,8 @@
package mage.cards.j; package mage.cards.j;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObjectReference; import mage.MageObjectReference;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -20,10 +23,6 @@ import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInHand;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/** /**
* @author LevelX2 * @author LevelX2
*/ */
@ -89,7 +88,7 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect {
if (card == null) { if (card == null) {
return false; return false;
} }
boolean hasSuspend = card.getAbilities().containsClass(SuspendAbility.class); boolean hasSuspend = card.getAbilities(game).containsClass(SuspendAbility.class);
UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game);
if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.HAND, true)) { if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.HAND, true)) {
@ -104,4 +103,3 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect {
return false; return false;
} }
} }

View file

@ -1,4 +1,3 @@
package mage.cards.m; package mage.cards.m;
import java.util.Objects; import java.util.Objects;
@ -14,11 +13,11 @@ import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer; import mage.constants.Layer;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.counters.CounterType; import mage.counters.CounterType;
@ -140,7 +139,7 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
} }
for (Card card : game.getExile().getAllCards(game)) { for (Card card : game.getExile().getAllCards(game)) {
if (filter.match(card, game) && Objects.equals(card.getOwnerId(), perm.getControllerId())) { if (filter.match(card, game) && Objects.equals(card.getOwnerId(), perm.getControllerId())) {
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof ActivatedAbility) { if (ability instanceof ActivatedAbility) {
ActivatedAbility copyAbility = (ActivatedAbility) ability.copy(); ActivatedAbility copyAbility = (ActivatedAbility) ability.copy();
copyAbility.setMaxActivationsPerTurn(1); copyAbility.setMaxActivationsPerTurn(1);

View file

@ -1,4 +1,3 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID; import java.util.UUID;
@ -59,7 +58,7 @@ class MemoryCrystalSpellsCostReductionEffect extends CostModificationEffectImpl
Card card = game.getCard(abilityToModify.getSourceId()); Card card = game.getCard(abilityToModify.getSourceId());
if (card != null) { if (card != null) {
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof BuybackAbility) { if (ability instanceof BuybackAbility) {
if (ability.isActivated()) { if (ability.isActivated()) {
int amountToReduce = ((BuybackAbility) ability).reduceCost(2); int amountToReduce = ((BuybackAbility) ability).reduceCost(2);

View file

@ -1,5 +1,3 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID; import java.util.UUID;
@ -15,11 +13,11 @@ import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer; import mage.constants.Layer;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterArtifactCard;
import mage.game.Game; import mage.game.Game;
@ -33,7 +31,7 @@ import mage.target.common.TargetCardInGraveyard;
public final class MyrWelder extends CardImpl { public final class MyrWelder extends CardImpl {
public MyrWelder(UUID ownerId, CardSetInfo setInfo) { public MyrWelder(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
this.subtype.add(SubType.MYR); this.subtype.add(SubType.MYR);
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
@ -104,12 +102,12 @@ class MyrWelderContinuousEffect extends ContinuousEffectImpl {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent perm = game.getPermanent(source.getSourceId()); Permanent perm = game.getPermanent(source.getSourceId());
if (perm != null) { if (perm != null) {
for (UUID imprintedId: perm.getImprinted()) { for (UUID imprintedId : perm.getImprinted()) {
Card card = game.getCard(imprintedId); Card card = game.getCard(imprintedId);
if (card != null) { if (card != null) {
for (Ability ability: card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof ActivatedAbility) { if (ability instanceof ActivatedAbility) {
perm.addAbility(ability, game); perm.addAbility(ability, source.getId(), game);
} }
} }
} }

View file

@ -1,4 +1,3 @@
package mage.cards.n; package mage.cards.n;
import java.util.UUID; import java.util.UUID;
@ -18,11 +17,11 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.cards.CardsImpl; import mage.cards.CardsImpl;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer; import mage.constants.Layer;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
@ -171,7 +170,7 @@ class NarsetTranscendentGainReboundEffect extends ContinuousEffectImpl {
if (spell != null) { if (spell != null) {
Card card = spell.getCard(); Card card = spell.getCard();
if (card != null) { if (card != null) {
addReboundAbility(card, source, game); addReboundAbility(card, game);
} }
} else { } else {
discard(); discard();
@ -181,9 +180,9 @@ class NarsetTranscendentGainReboundEffect extends ContinuousEffectImpl {
return false; return false;
} }
private void addReboundAbility(Card card, Ability source, Game game) { private void addReboundAbility(Card card, Game game) {
boolean found = false; boolean found = false;
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof ReboundAbility) { if (ability instanceof ReboundAbility) {
found = true; found = true;
break; break;

View file

@ -1,11 +1,9 @@
package mage.cards.n; package mage.cards.n;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.ActivatedAbility; import mage.abilities.ActivatedAbility;
@ -66,9 +64,9 @@ public final class NecroticOoze extends CardImpl {
if (player != null) { if (player != null) {
for (Card card : player.getGraveyard().getCards(game)) { for (Card card : player.getGraveyard().getCards(game)) {
if (card.isCreature()) { if (card.isCreature()) {
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (ability instanceof ActivatedAbility) { if (ability instanceof ActivatedAbility) {
perm.addAbility(ability, game); perm.addAbility(ability, source.getSourceId(), game);
} }
} }
} }

View file

@ -1,5 +1,8 @@
package mage.cards.p; package mage.cards.p;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.SpellAbility; import mage.abilities.SpellAbility;
@ -23,10 +26,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/** /**
* @author TheElk801 * @author TheElk801
*/ */
@ -76,6 +75,7 @@ class PolukranosUnchainedEffect extends OneShotEffect {
PolukranosUnchainedEffect() { PolukranosUnchainedEffect() {
super(Outcome.BoostCreature); super(Outcome.BoostCreature);
staticText = "with six +1/+1 counters on it. It escapes with twelve +1/+1 counters on it instead";
} }
private PolukranosUnchainedEffect(final PolukranosUnchainedEffect effect) { private PolukranosUnchainedEffect(final PolukranosUnchainedEffect effect) {

View file

@ -1,4 +1,3 @@
package mage.cards.s; package mage.cards.s;
import java.util.UUID; import java.util.UUID;
@ -13,11 +12,11 @@ import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer; import mage.constants.Layer;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -30,7 +29,7 @@ import mage.players.Player;
public final class SkillBorrower extends CardImpl { public final class SkillBorrower extends CardImpl {
public SkillBorrower(UUID ownerId, CardSetInfo setInfo) { public SkillBorrower(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{U}");
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD); this.subtype.add(SubType.WIZARD);
@ -53,7 +52,6 @@ public final class SkillBorrower extends CardImpl {
} }
} }
class SkillBorrowerAbility extends StaticAbility { class SkillBorrowerAbility extends StaticAbility {
public SkillBorrowerAbility() { public SkillBorrowerAbility() {
@ -71,7 +69,7 @@ class SkillBorrowerAbility extends StaticAbility {
@Override @Override
public String getRule() { public String getRule() {
return "As long as the top card of your library is an artifact or creature card, Skill Borrower has all activated abilities of that card"; return "As long as the top card of your library is an artifact or creature card, {this} has all activated abilities of that card";
} }
} }
@ -79,14 +77,13 @@ class SkillBorrowerEffect extends ContinuousEffectImpl {
public SkillBorrowerEffect() { public SkillBorrowerEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
staticText = "As long as the top card of your library is an artifact or creature card, Skill Borrower has all activated abilities of that card"; staticText = "As long as the top card of your library is an artifact or creature card, {this} has all activated abilities of that card";
} }
public SkillBorrowerEffect(final SkillBorrowerEffect effect) { public SkillBorrowerEffect(final SkillBorrowerEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public SkillBorrowerEffect copy() { public SkillBorrowerEffect copy() {
return new SkillBorrowerEffect(this); return new SkillBorrowerEffect(this);
@ -95,13 +92,13 @@ class SkillBorrowerEffect extends ContinuousEffectImpl {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
if(player != null){ if (player != null) {
Card card = player.getLibrary().getFromTop(game); Card card = player.getLibrary().getFromTop(game);
if(card != null && (card.isCreature() || card.isArtifact())){ if (card != null && (card.isCreature() || card.isArtifact())) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if(permanent != null){ if (permanent != null) {
for(Ability ability : card.getAbilities()){ for (Ability ability : card.getAbilities(game)) {
if(ability instanceof ActivatedAbility){ if (ability instanceof ActivatedAbility) {
permanent.addAbility(ability, source.getSourceId(), game); permanent.addAbility(ability, source.getSourceId(), game);
} }
} }

View file

@ -1,5 +1,8 @@
package mage.cards.s; package mage.cards.s;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObjectReference; import mage.MageObjectReference;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -16,10 +19,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/** /**
* @author LevelX2 * @author LevelX2
*/ */
@ -91,7 +90,7 @@ class SoulflayerEffect extends ContinuousEffectImpl implements SourceEffect {
if (!card.isCreature()) { if (!card.isCreature()) {
continue; continue;
} }
for (Ability cardAbility : card.getAbilities()) { for (Ability cardAbility : card.getAbilities(game)) {
if (cardAbility instanceof FlyingAbility) { if (cardAbility instanceof FlyingAbility) {
abilitiesToAdd.add(FlyingAbility.getInstance()); abilitiesToAdd.add(FlyingAbility.getInstance());
} }

View file

@ -1,5 +1,6 @@
package mage.cards.t; package mage.cards.t;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility; import mage.abilities.DelayedTriggeredAbility;
@ -26,8 +27,6 @@ import mage.players.Player;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import mage.watchers.common.AttackedThisTurnWatcher; import mage.watchers.common.AttackedThisTurnWatcher;
import java.util.UUID;
/** /**
* @author spjspj * @author spjspj
*/ */
@ -153,7 +152,7 @@ class TaigamOjutaiMasterGainReboundEffect extends ContinuousEffectImpl {
} }
private void addReboundAbility(Card card, Ability source, Game game) { private void addReboundAbility(Card card, Ability source, Game game) {
boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof ReboundAbility); boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility);
if (!found) { if (!found) {
Ability ability = new ReboundAbility(); Ability ability = new ReboundAbility();
game.getState().addOtherAbility(card, ability); game.getState().addOtherAbility(card, ability);

View file

@ -1,5 +1,6 @@
package mage.cards.v; package mage.cards.v;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
@ -14,8 +15,6 @@ import mage.constants.*;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import java.util.UUID;
/** /**
* @author ImperatorPrime * @author ImperatorPrime
*/ */
@ -98,7 +97,7 @@ class VolrathsShapeshifterEffect extends ContinuousEffectImpl {
} }
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities(game)) {
if (!permanent.getAbilities().contains(ability)) { if (!permanent.getAbilities().contains(ability)) {
permanent.addAbility(ability, source.getSourceId(), game); permanent.addAbility(ability, source.getSourceId(), game);
} }

View file

@ -1,9 +1,7 @@
package mage.cards.y; package mage.cards.y;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Abilities;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.ContinuousEffectImpl;
@ -21,7 +19,7 @@ import mage.players.Player;
public final class YixlidJailer extends CardImpl { public final class YixlidJailer extends CardImpl {
public YixlidJailer(UUID ownerId, CardSetInfo setInfo) { public YixlidJailer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.WIZARD); this.subtype.add(SubType.WIZARD);
@ -67,14 +65,7 @@ public final class YixlidJailer extends CardImpl {
if (player != null) { if (player != null) {
for (Card card : player.getGraveyard().getCards(game)) { for (Card card : player.getGraveyard().getCards(game)) {
if (card != null) { if (card != null) {
card.getAbilities(game).clear(); // Will the abilities ever come back???? card.looseAllAbilities(game);
// TODO: Fix that (LevelX2)
// game.getContinuousEffects().removeGainedEffectsForSource(card.getId());
// game.getState().resetTriggersForSourceId(card.getId());
Abilities abilities = game.getState().getAllOtherAbilities(card.getId());
if (abilities != null) {
abilities.clear();
}
} }
} }
} }

View file

@ -0,0 +1,89 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.test.cards.continuous;
import mage.abilities.keyword.SwampwalkAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class LoosingAbilitiesTest extends CardTestPlayerBase {
@Test
public void GivingSwampwalkFromGraveyard() {
// Swampwalk
// As long as Filth is in your graveyard and you control a Swamp, creatures you control have swampwalk.
addCard(Zone.GRAVEYARD, playerB, "Filth"); // Creature
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerB, "Silvercoat Lion", 1);
assertAbility(playerB, "Silvercoat Lion", new SwampwalkAbility(), true);
}
/**
* The Card in the graveyard should have no Swampwalk if Yixlid Jailer
* effect was added later
*/
@Test
public void testYixlidJailerRemovesAbilities() {
// Cards in graveyards lose all abilities.
addCard(Zone.HAND, playerA, "Yixlid Jailer"); // Creature 2/1 - {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Swampwalk
// As long as Filth is in your graveyard and you control a Swamp, creatures you control have swampwalk.
addCard(Zone.GRAVEYARD, playerB, "Filth");
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yixlid Jailer");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerB, "Silvercoat Lion", 1);
assertAbility(playerB, "Silvercoat Lion", new SwampwalkAbility(), false);
}
// https://github.com/magefree/mage/issues/1147
@Test
public void testYixlidJailerAbilitiesComeBack() {
// Cards in graveyards lose all abilities.
addCard(Zone.HAND, playerA, "Yixlid Jailer"); // Creature 2/1 - {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
// Gravecrawler cant block.
// You may cast Gravecrawler from your graveyard as long as you control a Zombie.
addCard(Zone.GRAVEYARD, playerB, "Gravecrawler");
addCard(Zone.HAND, playerB, "Lightning Bolt", 2);
addCard(Zone.BATTLEFIELD, playerB, "Walking Corpse"); // Creature 2/2 - Zombie
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yixlid Jailer");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Yixlid Jailer");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Gravecrawler");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Yixlid Jailer", 1);
assertGraveyardCount(playerB, "Lightning Bolt", 1);
assertPermanentCount(playerB, "Gravecrawler", 1);
}
}

View file

@ -5,17 +5,13 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.Mana;
import mage.abilities.costs.*; import mage.abilities.costs.*;
import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.*; import mage.abilities.costs.mana.*;
import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects; import mage.abilities.effects.Effects;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ManaEffect;
import mage.abilities.effects.mana.DynamicManaEffect;
import mage.abilities.hint.Hint; import mage.abilities.hint.Hint;
import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.Card; import mage.cards.Card;
@ -25,7 +21,6 @@ import mage.game.Game;
import mage.game.command.Emblem; import mage.game.command.Emblem;
import mage.game.command.Plane; import mage.game.command.Plane;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.game.stack.StackAbility; import mage.game.stack.StackAbility;
@ -952,7 +947,12 @@ public abstract class AbilityImpl implements Ability {
return false; return false;
} }
return ((Permanent) object).isPhasedIn(); return ((Permanent) object).isPhasedIn();
} else if (!object.getAbilities().contains(this)) { } else if (object instanceof Card) {
if (!((Card) object).getAbilities(game).contains(this)) {
return false;
}
return true;
} else if (!object.getAbilities().contains(this)) { // not sure which object it can still be
// check if it's an ability that is temporary gained to a card // check if it's an ability that is temporary gained to a card
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId()); Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
return otherAbilities != null && otherAbilities.contains(this); return otherAbilities != null && otherAbilities.contains(this);

View file

@ -1,5 +1,7 @@
package mage.cards; package mage.cards;
import java.util.List;
import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.Mana; import mage.Mana;
import mage.abilities.Abilities; import mage.abilities.Abilities;
@ -14,9 +16,6 @@ import mage.game.Game;
import mage.game.GameState; import mage.game.GameState;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import java.util.List;
import java.util.UUID;
public interface Card extends MageObject { public interface Card extends MageObject {
UUID getOwnerId(); UUID getOwnerId();
@ -77,15 +76,15 @@ public interface Card extends MageObject {
* @param zone * @param zone
* @param sourceId * @param sourceId
* @param game * @param game
* @param flag If zone * @param flag If zone
* <ul> * <ul>
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on * <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
* bottom</li></ul></li> * bottom</li></ul></li>
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false - * <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
* untapped</li></ul></li> * untapped</li></ul></li>
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from * <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
* Battlefield</li></ul></li> * Battlefield</li></ul></li>
* </ul> * </ul>
* @return true if card was moved to zone * @return true if card was moved to zone
*/ */
boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag); boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag);
@ -95,8 +94,8 @@ public interface Card extends MageObject {
/** /**
* Moves the card to an exile zone * Moves the card to an exile zone
* *
* @param exileId set to null for generic exile zone * @param exileId set to null for generic exile zone
* @param name used for exile zone with the specified exileId * @param name used for exile zone with the specified exileId
* @param sourceId * @param sourceId
* @param game * @param game
* @return true if card was moved to zone * @return true if card was moved to zone
@ -132,6 +131,8 @@ public interface Card extends MageObject {
void addAbility(Ability ability); void addAbility(Ability ability);
void looseAllAbilities(Game game);
boolean addCounters(Counter counter, Ability source, Game game); boolean addCounters(Counter counter, Ability source, Game game);
boolean addCounters(Counter counter, Ability source, Game game, boolean isEffect); boolean addCounters(Counter counter, Ability source, Game game, boolean isEffect);
@ -148,8 +149,8 @@ public interface Card extends MageObject {
Card copy(); Card copy();
/** /**
* @return The main card of a split half card or adventure spell card, otherwise the card itself is * @return The main card of a split half card or adventure spell card,
* returned * otherwise the card itself is returned
*/ */
Card getMainCard(); Card getMainCard();

View file

@ -1,6 +1,12 @@
package mage.cards; package mage.cards;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectImpl; import mage.MageObjectImpl;
import mage.Mana; import mage.Mana;
@ -26,13 +32,6 @@ import mage.util.SubTypeList;
import mage.watchers.Watcher; import mage.watchers.Watcher;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public abstract class CardImpl extends MageObjectImpl implements Card { public abstract class CardImpl extends MageObjectImpl implements Card {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -240,19 +239,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
@Override @Override
public List<String> getRules(Game game) { public List<String> getRules(Game game) {
try { try {
List<String> rules = getRules(); List<String> rules = getAbilities(game).getRules(getName());
if (game != null) { if (game != null) {
// debug state // debug state
CardState cardState = game.getState().getCardState(objectId); for (String data : game.getState().getCardState(objectId).getInfo().values()) {
if (cardState != null) { rules.add(data);
for (String data : cardState.getInfo().values()) {
rules.add(data);
}
for (Ability ability : cardState.getAbilities()) {
rules.add(ability.getRule());
}
} }
// ability hints // ability hints
List<String> abilityHints = new ArrayList<>(); List<String> abilityHints = new ArrayList<>();
if (HintUtils.ABILITY_HINTS_ENABLE) { if (HintUtils.ABILITY_HINTS_ENABLE) {
@ -267,7 +259,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
} }
// restrict hints only for permanents, not cards // restrict hints only for permanents, not cards
// total hints // total hints
if (!abilityHints.isEmpty()) { if (!abilityHints.isEmpty()) {
rules.add(HintUtils.HINT_START_MARK); rules.add(HintUtils.HINT_START_MARK);
@ -301,21 +292,31 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
*/ */
@Override @Override
public Abilities<Ability> getAbilities(Game game) { public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(objectId); CardState cardState = game.getState().getCardState(this.getId());
if (otherAbilities == null || otherAbilities.isEmpty()) { if (!cardState.hasLostAllAbilities() && (cardState.getAbilities() == null || cardState.getAbilities().isEmpty())) {
return abilities; return abilities;
} }
Abilities<Ability> all = new AbilitiesImpl<>(); Abilities<Ability> all = new AbilitiesImpl<>();
all.addAll(abilities); if (!cardState.hasLostAllAbilities()) {
all.addAll(otherAbilities); all.addAll(abilities);
}
all.addAll(cardState.getAbilities());
return all; return all;
} }
@Override
public void looseAllAbilities(Game game) {
CardState cardState = game.getState().getCardState(this.getId());
cardState.setLostAllAbilities(true);
cardState.getAbilities().clear();
}
/** /**
* Public in order to support adding abilities to SplitCardHalf's * Public in order to support adding abilities to SplitCardHalf's
* *
* @param ability * @param ability
*/ */
@Override
public void addAbility(Ability ability) { public void addAbility(Ability ability) {
ability.setSourceId(this.getId()); ability.setSourceId(this.getId());
abilities.add(ability); abilities.add(ability);

View file

@ -18,6 +18,7 @@ public class CardState implements Serializable {
protected Map<String, String> info; protected Map<String, String> info;
protected Counters counters; protected Counters counters;
protected Abilities<Ability> abilities; protected Abilities<Ability> abilities;
protected boolean lostAllAbilities;
private static final Map<String, String> emptyInfo = new HashMap<>(); private static final Map<String, String> emptyInfo = new HashMap<>();
private static final Abilities<Ability> emptyAbilities = new AbilitiesImpl<>(); private static final Abilities<Ability> emptyAbilities = new AbilitiesImpl<>();
@ -39,6 +40,7 @@ public class CardState implements Serializable {
abilities.add(ability.copy()); abilities.add(ability.copy());
} }
} }
this.lostAllAbilities = state.lostAllAbilities;
} }
public CardState copy() { public CardState copy() {
@ -90,20 +92,29 @@ public class CardState implements Serializable {
abilities.addAll(ability.getSubAbilities()); abilities.addAll(ability.getSubAbilities());
} }
/**
* Called from applyEffects reset, to reset all layered effects
*/
public void clearAbilities() { public void clearAbilities() {
if (abilities != null) { if (abilities != null) {
// for (Ability ability: abilities) { // Causes problems if temporary (gained) continuous effects are removed
// ability.setSourceId(null);
// ability.setControllerId(null);
// }
abilities = null; abilities = null;
} }
setLostAllAbilities(false);
} }
public void clear() { public void clear() {
counters.clear(); counters.clear();
info = null; info = null;
clearAbilities(); clearAbilities();
lostAllAbilities = false;
}
public boolean hasLostAllAbilities() {
return lostAllAbilities;
}
public void setLostAllAbilities(boolean lostAllAbilities) {
this.lostAllAbilities = lostAllAbilities;
} }
} }

View file

@ -1,5 +1,8 @@
package mage.game; package mage.game;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import mage.MageObject; import mage.MageObject;
import mage.abilities.*; import mage.abilities.*;
import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffect;
@ -34,10 +37,6 @@ import mage.util.ThreadLocalStringBuilder;
import mage.watchers.Watcher; import mage.watchers.Watcher;
import mage.watchers.Watchers; import mage.watchers.Watchers;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
* <p> * <p>
@ -602,7 +601,6 @@ public class GameState implements Serializable, Copyable<GameState> {
// public void addMessage(String message) { // public void addMessage(String message) {
// this.messages.add(message); // this.messages.add(message);
// } // }
/** /**
* Returns a list of all players of the game ignoring range or if a player * Returns a list of all players of the game ignoring range or if a player
* has lost or left the game. * has lost or left the game.
@ -636,8 +634,9 @@ public class GameState implements Serializable, Copyable<GameState> {
* also setting the playerId to the first/current player of the list. Also * also setting the playerId to the first/current player of the list. Also
* returning the other players in turn order. * returning the other players in turn order.
* <p> * <p>
* Not safe for continuous effects, see rule 800.4k (effects must work until end of turn even after player leaves) * Not safe for continuous effects, see rule 800.4k (effects must work until
* Use Player.InRange() to find active players list at the start of the turn * end of turn even after player leaves) Use Player.InRange() to find active
* players list at the start of the turn
* *
* @param playerId * @param playerId
* @param game * @param game
@ -775,7 +774,7 @@ public class GameState implements Serializable, Copyable<GameState> {
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) { for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
Set<Card> movedCards = new LinkedHashSet<>(); Set<Card> movedCards = new LinkedHashSet<>();
Set<PermanentToken> movedTokens = new LinkedHashSet<>(); Set<PermanentToken> movedTokens = new LinkedHashSet<>();
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext(); ) { for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
GameEvent event = it.next(); GameEvent event = it.next();
ZoneChangeEvent castEvent = (ZoneChangeEvent) event; ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
UUID targetId = castEvent.getTargetId(); UUID targetId = castEvent.getTargetId();
@ -1008,7 +1007,7 @@ public class GameState implements Serializable, Copyable<GameState> {
* @param attachedTo * @param attachedTo
* @param ability * @param ability
* @param copyAbility copies non MageSingleton abilities before adding to * @param copyAbility copies non MageSingleton abilities before adding to
* state * state
*/ */
public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) { public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) {
Ability newAbility; Ability newAbility;

View file

@ -1,5 +1,6 @@
package mage.game.stack; package mage.game.stack;
import java.util.*;
import mage.MageInt; import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.Mana; import mage.Mana;
@ -31,8 +32,6 @@ import mage.players.Player;
import mage.util.GameLog; import mage.util.GameLog;
import mage.util.SubTypeList; import mage.util.SubTypeList;
import java.util.*;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -1045,4 +1044,9 @@ public class Spell extends StackObjImpl implements Card {
return commandedBy; return commandedBy;
} }
@Override
public void looseAllAbilities(Game game) {
throw new UnsupportedOperationException("Spells should not loose all abilities. Check if this operation is correct.");
}
} }