diff --git a/Mage.Sets/src/mage/cards/l/LibraryOfLeng.java b/Mage.Sets/src/mage/cards/l/LibraryOfLeng.java index f2ec1576193..181472deff6 100644 --- a/Mage.Sets/src/mage/cards/l/LibraryOfLeng.java +++ b/Mage.Sets/src/mage/cards/l/LibraryOfLeng.java @@ -1,17 +1,11 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -22,14 +16,15 @@ import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LibraryOfLeng extends CardImpl { public LibraryOfLeng(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // You have no maximum hand size. Effect effect = new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.WhileOnBattlefield, MaximumHandSizeControllerEffect.HandSizeModification.SET); @@ -79,14 +74,15 @@ class LibraryOfLengEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.DISCARD_CARD) { + // rules: + // You can’t use the Library of Leng ability to place a discarded card on top of your library when you discard a card as a cost, + // because costs aren’t effects. (2004-10-04) + if (event.getType() == EventType.DISCARD_CARD && event.getFlag()) { return event.getPlayerId().equals(source.getControllerId()); } if (event.getType() == EventType.ZONE_CHANGE) { if (event.getTargetId().equals(cardId) && game.getState().getZoneChangeCounter(event.getTargetId()) == zoneChangeCounter) { - if (((ZoneChangeEvent) event).getFromZone() == Zone.HAND && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) { - return true; - } + return ((ZoneChangeEvent) event).getFromZone() == Zone.HAND && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; } } return false; diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 837c21ac81f..71ea031c5e3 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -21,6 +21,7 @@ public class GameEvent implements Serializable { // flags: // for counters: event is result of effect (+1 from planeswalkers is cost, not effect) // for combat damage: event is preventable damage + // for discard: event is result of effect (1) or result of cost (0) protected boolean flag; protected String data; protected Zone zone; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index ca8cae8daf3..f8c1e404737 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -792,20 +792,23 @@ public abstract class PlayerImpl implements Player, Serializable { about the discarded card, that cost payment is illegal; the game returns to the moment before the cost was paid (see rule 717, "Handling Illegal Actions"). */ - if (card != null - && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId), source)) { - // write info to game log first so game log infos from triggered or replacement effects follow in the game log - if (!game.isSimulation()) { - game.informPlayers(getLogName() + " discards " + card.getLogName()); + if (card != null) { + GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId); + gameEvent.setFlag(source != null); // event from effect or from cost (source == null) + if (!game.replaceEvent(gameEvent, source)) { + // write info to game log first so game log infos from triggered or replacement effects follow in the game log + if (!game.isSimulation()) { + game.informPlayers(getLogName() + " discards " + card.getLogName()); + } + /* If a card is discarded while Rest in Peace is on the battlefield, abilities that function + * when a card is discarded (such as madness) still work, even though that card never reaches + * a graveyard. In addition, spells or abilities that check the characteristics of a discarded + * card (such as Chandra Ablaze's first ability) can find that card in exile. */ + card.moveToZone(Zone.GRAVEYARD, source == null ? null : source.getSourceId(), game, false); + // So discard is also successful if card is moved to another zone by replacement effect! + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId)); + return true; } - /* If a card is discarded while Rest in Peace is on the battlefield, abilities that function - * when a card is discarded (such as madness) still work, even though that card never reaches - * a graveyard. In addition, spells or abilities that check the characteristics of a discarded - * card (such as Chandra Ablaze's first ability) can find that card in exile. */ - card.moveToZone(Zone.GRAVEYARD, source == null ? null : source.getSourceId(), game, false); - // So discard is also successful if card is moved to another zone by replacement effect! - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId)); - return true; } return false; } @@ -1014,7 +1017,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) { + public void setCastSourceIdWithAlternateMana(UUID + sourceId, ManaCosts manaCosts, Costs costs) { castSourceIdWithAlternateMana = sourceId; castSourceIdManaCosts = manaCosts; castSourceIdCosts = costs; @@ -1046,7 +1050,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference) { + public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference + reference) { if (card == null) { return false; } @@ -1063,7 +1068,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference permittingObject) { + public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference + permittingObject) { if (game == null || originalAbility == null) { return false; } @@ -1426,7 +1432,8 @@ public abstract class PlayerImpl implements Player, Serializable { // Get the usable activated abilities for a *single card object*, that is, either a card or half of a split card. // Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities // as candidates. - private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities candidateAbilites, LinkedHashMap output) { + private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game + game, Abilities candidateAbilites, LinkedHashMap output) { boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game); ManaOptions availableMana = null; // ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly @@ -1496,7 +1503,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { + public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game + game) { LinkedHashMap useable = new LinkedHashMap<>(); if (object instanceof StackAbility) { // It may not be possible to activate abilities of stack abilities return useable; @@ -1515,7 +1523,8 @@ public abstract class PlayerImpl implements Player, Serializable { } // Adds special abilities that are given to non permanents by continuous effects - private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game game, Map useable) { + private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game + game, Map useable) { Abilities otherAbilities = game.getState().getActivatedOtherAbilities(object.getId(), zone); if (otherAbilities != null) { boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game); @@ -1561,7 +1570,8 @@ public abstract class PlayerImpl implements Player, Serializable { } } - protected LinkedHashMap getUseableManaAbilities(MageObject object, Zone zone, Game game) { + protected LinkedHashMap getUseableManaAbilities(MageObject object, Zone + zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game); for (ActivatedManaAbilityImpl ability : object.getAbilities().getActivatedManaAbilities(zone)) { @@ -1813,7 +1823,9 @@ public abstract class PlayerImpl implements Player, Serializable { } } - private List getPermanentsThatCanBeUntapped(Game game, List canBeUntapped, RestrictionUntapNotMoreThanEffect handledEffect, Map>, Integer> notMoreThanEffectsUsage) { + private List getPermanentsThatCanBeUntapped(Game + game, List canBeUntapped, RestrictionUntapNotMoreThanEffect + handledEffect, Map>, Integer> notMoreThanEffectsUsage) { List leftForUntap = new ArrayList<>(); // select permanents that can still be untapped for (Permanent permanent : canBeUntapped) { @@ -1996,12 +2008,14 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List appliedEffects) { + public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List< + UUID> appliedEffects) { return doDamage(damage, sourceId, game, combatDamage, preventable, appliedEffects); } @SuppressWarnings({"null", "ConstantConditions"}) - private int doDamage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List appliedEffects) { + private int doDamage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List< + UUID> appliedEffects) { if (damage > 0) { if (canDamage(game.getObject(sourceId), game)) { GameEvent event = new DamagePlayerEvent(playerId, sourceId, playerId, damage, preventable, combatDamage); @@ -2503,7 +2517,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId, boolean triggerEvents) { + public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId, + boolean triggerEvents) { //20091005 - 701.14c Library searchedLibrary = null; String searchInfo = null; @@ -2745,7 +2760,8 @@ public abstract class PlayerImpl implements Player, Serializable { * or NilRoll */ @Override - public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, int numberPlanarSides) { + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, + int numberPlanarSides) { int result = RandomUtil.nextInt(9) + 1; PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL; if (numberChaosSides + numberPlanarSides > 9) { @@ -2965,7 +2981,8 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game game) { + protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game + game) { if (sourceObject != null && !(sourceObject instanceof Permanent)) { for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) { // if cast for noMana no Alternative costs are allowed @@ -3029,7 +3046,8 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) { + protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability + ability, Game game) { if (!(sourceObject instanceof Permanent)) { Ability sourceAbility = null; for (Ability landAbility : sourceObject.getAbilities()) { @@ -3064,7 +3082,8 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - private void getPlayableFromGraveyardCard(Game game, Card card, Abilities candidateAbilities, ManaOptions availableMana, List output) { + private void getPlayableFromGraveyardCard(Game game, Card + card, Abilities candidateAbilities, ManaOptions availableMana, List output) { MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game); for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) { boolean possible = false;