* 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

@ -5,17 +5,13 @@ import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.Mana;
import mage.abilities.costs.*;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.*;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
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.mana.ActivatedManaAbilityImpl;
import mage.cards.Card;
@ -25,7 +21,6 @@ import mage.game.Game;
import mage.game.command.Emblem;
import mage.game.command.Plane;
import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.game.stack.StackAbility;
@ -952,7 +947,12 @@ public abstract class AbilityImpl implements Ability {
return false;
}
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
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
return otherAbilities != null && otherAbilities.contains(this);

View file

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

View file

@ -1,6 +1,12 @@
package mage.cards;
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.MageObjectImpl;
import mage.Mana;
@ -26,13 +32,6 @@ import mage.util.SubTypeList;
import mage.watchers.Watcher;
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 {
private static final long serialVersionUID = 1L;
@ -240,19 +239,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
@Override
public List<String> getRules(Game game) {
try {
List<String> rules = getRules();
List<String> rules = getAbilities(game).getRules(getName());
if (game != null) {
// debug state
CardState cardState = game.getState().getCardState(objectId);
if (cardState != null) {
for (String data : cardState.getInfo().values()) {
rules.add(data);
}
for (Ability ability : cardState.getAbilities()) {
rules.add(ability.getRule());
}
for (String data : game.getState().getCardState(objectId).getInfo().values()) {
rules.add(data);
}
// ability hints
List<String> abilityHints = new ArrayList<>();
if (HintUtils.ABILITY_HINTS_ENABLE) {
@ -267,7 +259,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
}
// restrict hints only for permanents, not cards
// total hints
if (!abilityHints.isEmpty()) {
rules.add(HintUtils.HINT_START_MARK);
@ -301,21 +292,31 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
*/
@Override
public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(objectId);
if (otherAbilities == null || otherAbilities.isEmpty()) {
CardState cardState = game.getState().getCardState(this.getId());
if (!cardState.hasLostAllAbilities() && (cardState.getAbilities() == null || cardState.getAbilities().isEmpty())) {
return abilities;
}
Abilities<Ability> all = new AbilitiesImpl<>();
all.addAll(abilities);
all.addAll(otherAbilities);
if (!cardState.hasLostAllAbilities()) {
all.addAll(abilities);
}
all.addAll(cardState.getAbilities());
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
*
* @param ability
*/
@Override
public void addAbility(Ability ability) {
ability.setSourceId(this.getId());
abilities.add(ability);

View file

@ -18,6 +18,7 @@ public class CardState implements Serializable {
protected Map<String, String> info;
protected Counters counters;
protected Abilities<Ability> abilities;
protected boolean lostAllAbilities;
private static final Map<String, String> emptyInfo = new HashMap<>();
private static final Abilities<Ability> emptyAbilities = new AbilitiesImpl<>();
@ -39,6 +40,7 @@ public class CardState implements Serializable {
abilities.add(ability.copy());
}
}
this.lostAllAbilities = state.lostAllAbilities;
}
public CardState copy() {
@ -90,20 +92,29 @@ public class CardState implements Serializable {
abilities.addAll(ability.getSubAbilities());
}
/**
* Called from applyEffects reset, to reset all layered effects
*/
public void clearAbilities() {
if (abilities != null) {
// for (Ability ability: abilities) { // Causes problems if temporary (gained) continuous effects are removed
// ability.setSourceId(null);
// ability.setControllerId(null);
// }
abilities = null;
}
setLostAllAbilities(false);
}
public void clear() {
counters.clear();
info = null;
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;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import mage.MageObject;
import mage.abilities.*;
import mage.abilities.effects.ContinuousEffect;
@ -34,10 +37,6 @@ import mage.util.ThreadLocalStringBuilder;
import mage.watchers.Watcher;
import mage.watchers.Watchers;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
* <p>
@ -602,7 +601,6 @@ public class GameState implements Serializable, Copyable<GameState> {
// public void addMessage(String message) {
// this.messages.add(message);
// }
/**
* Returns a list of all players of the game ignoring range or if a player
* 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
* returning the other players in turn order.
* <p>
* Not safe for continuous effects, see rule 800.4k (effects must work until end of turn even after player leaves)
* Use Player.InRange() to find active players list at the start of the turn
* Not safe for continuous effects, see rule 800.4k (effects must work until
* end of turn even after player leaves) Use Player.InRange() to find active
* players list at the start of the turn
*
* @param playerId
* @param game
@ -775,7 +774,7 @@ public class GameState implements Serializable, Copyable<GameState> {
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
Set<Card> movedCards = 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();
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
UUID targetId = castEvent.getTargetId();
@ -1008,7 +1007,7 @@ public class GameState implements Serializable, Copyable<GameState> {
* @param attachedTo
* @param ability
* @param copyAbility copies non MageSingleton abilities before adding to
* state
* state
*/
public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) {
Ability newAbility;

View file

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