diff --git a/Mage.Sets/src/mage/cards/e/Excavator.java b/Mage.Sets/src/mage/cards/e/Excavator.java index 132cf6f9f72d..c005e47247f4 100644 --- a/Mage.Sets/src/mage/cards/e/Excavator.java +++ b/Mage.Sets/src/mage/cards/e/Excavator.java @@ -1,7 +1,6 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -17,9 +16,10 @@ import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author Plopman diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java index e64d4a83a172..101514f51e45 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java @@ -78,7 +78,7 @@ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId * For storing additional info upon selecting permanents to sacrifice */ protected void addSacrificeTarget(Game game, Permanent permanent) { - permanents.add(permanent.copy()); + permanents.add(permanent.saveImmutableCopy(game)); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java index f464355448e3..92b19464735a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java @@ -85,9 +85,9 @@ public boolean apply(Game game, Ability source) { discard(); return false; } - // As long as the permanent is still in the short living LKI continue to copy to get triggered abilities to TriggeredAbilities for dies events. + // Don't need to continue applying if permanent is in LKI permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, source.getStackMomentSourceZCC()); - if (permanent == null) { + if (permanent != null) { discard(); return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 76afce20dce6..284467c33994 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -98,7 +98,7 @@ public boolean apply(Game game, Ability source) { } else { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { - permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo()); + permanent = game.getPermanent(equipment.getAttachedTo()); } else { permanent = null; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java index 2f05691c6dd4..6295322ffcac 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java @@ -79,7 +79,7 @@ public GainAbilityControlledEffect copy() { public boolean apply(Game game, Ability source) { if (getAffectedObjectsSet()) { for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost - Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper) + Permanent perm = it.next().getPermanent(game); if (perm != null) { for (Ability abilityToAdd : ability) { perm.addAbility(abilityToAdd, source.getSourceId(), game); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 62a2b2f17cd1..e9fb8892042f 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -73,6 +73,7 @@ import mage.target.TargetPlayer; import mage.util.*; import mage.util.functions.CopyApplier; +import mage.util.immutableWrappers.ImmutablePermanent; import mage.watchers.Watcher; import mage.watchers.common.*; import org.apache.log4j.Logger; @@ -2106,7 +2107,11 @@ public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, U // if it was no copy of copy take the target itself if (newBluePrint == null) { - newBluePrint = copyFromPermanent.copy(); + if (copyFromPermanent instanceof ImmutablePermanent) { + newBluePrint = ((ImmutablePermanent) copyFromPermanent).getResetPermanent(this); + } else { + newBluePrint = copyFromPermanent.copy(); + } // reset to original characteristics newBluePrint.reset(this); @@ -3678,8 +3683,12 @@ public boolean checkShortLivingLKI(UUID objectId, Zone zone) { public void rememberLKI(Zone zone, MageObject object) { UUID objectId = object.getId(); if (object instanceof Permanent || object instanceof StackObject) { - MageObject copy = object.copy(); - + MageObject copy; + if (object instanceof Permanent) { + copy = ((Permanent) object).saveImmutableCopy(this); + } else { + copy = object.copy(); + } Map lkiMap = lki.computeIfAbsent(zone, k -> new HashMap<>()); lkiMap.put(objectId, copy); diff --git a/Mage/src/main/java/mage/game/permanent/ImmutablePermanentCard.java b/Mage/src/main/java/mage/game/permanent/ImmutablePermanentCard.java new file mode 100644 index 000000000000..3c86b49bcc4b --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/ImmutablePermanentCard.java @@ -0,0 +1,944 @@ +package mage.game.permanent; + + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.cards.Card; +import mage.constants.*; +import mage.counters.Counter; +import mage.game.CardState; +import mage.game.Game; +import mage.game.MageObjectAttribute; +import mage.game.events.ZoneChangeEvent; +import mage.util.immutableWrappers.ImmutablePermanent; +import mage.watchers.Watcher; + +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +/** + * Represents an immutable permanent with dynamic values from game state. All modification attempts throw {@link UnsupportedOperationException}. + * Primarily used to save refences in Last Known Instance map. Use this if you need to save a snapshot of a permanent to reference later. + * + * @author Jmlundeen + */ +public class ImmutablePermanentCard extends PermanentCard implements ImmutablePermanent { + + protected ImmutablePermanentCard(PermanentCard permanent, MageObjectAttribute mageObjectAttribute, CardState cardState) { + super(permanent); + if (mageObjectAttribute != null) { + this.color = mageObjectAttribute.getColor(); + this.subtype = mageObjectAttribute.getSubtype(); + this.cardType = mageObjectAttribute.getCardType(); + this.supertype = mageObjectAttribute.getSuperType(); + } + if (cardState.hasLostAllAbilities()) { + abilities.clear(); + } else { + abilities.addAll(cardState.getAbilities()); + } + } + + protected ImmutablePermanentCard(ImmutablePermanentCard permanent) { + super(permanent); + } + + @Override + public PermanentCard getResetPermanent(Game game) { + PermanentCard card = new PermanentCard(this); + card.reset(game); + return card; + } + + @Override + public void reset(Game game) { + throwImmutableError(); + } + + @Override + public ImmutablePermanentCard copy() { + return new ImmutablePermanentCard(this); + } + + @Override + public boolean turnFaceUp(Ability source, Game game, UUID playerId) { + return throwImmutableError(); + } + + @Override + public boolean turnFaceDown(Ability source, Game game, UUID playerId) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, boolean isEffect) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects, boolean isEffect) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects, boolean isEffect, int maxCounters) { + return throwImmutableError(); + } + + @Override + public void removeCounters(String counterName, int amount, Ability source, Game game) { + throwImmutableError(); + } + + @Override + public int removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public int removeCounters(Counter counter, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int removeCounters(Counter counter, Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(String counterName, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(String counterName, Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public void setZone(Zone zone, Game game) { + throwImmutableError(); + } + + @Override + public void setSpellAbility(SpellAbility ability) { + throwImmutableError(); + } + + @Override + public boolean addAttachment(UUID permanentId, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean removeAttachment(UUID permanentId, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public void setName(String name) { + throwImmutableError(); + } + + @Override + public void setStartingLoyalty(int startingLoyalty) { + throwImmutableError(); + } + + @Override + public void setStartingDefense(int startingDefense) { + throwImmutableError(); + } + + @Override + public void setExpansionSetCode(String expansionSetCode) { + throwImmutableError(); + } + + @Override + public void setUsesVariousArt(boolean usesVariousArt) { + throwImmutableError(); + } + + @Override + public void setCardNumber(String cardNumber) { + throwImmutableError(); + } + + @Override + public void setImageFileName(String imageFileName) { + throwImmutableError(); + } + + @Override + public void setImageNumber(Integer imageNumber) { + throwImmutableError(); + } + + @Override + public void setManaCost(ManaCosts costs) { + throwImmutableError(); + } + + @Override + public void setCopy(boolean isCopy, MageObject copyFrom) { + throwImmutableError(); + } + + @Override + public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { + throwImmutableError(); + } + + @Override + public void setZoneChangeCounter(int value, Game game) { + throwImmutableError(); + } + + @Override + public void addSuperType(SuperType superType) { + throwImmutableError(); + } + + @Override + public void addSuperType(Game game, SuperType superType) { + throwImmutableError(); + } + + @Override + public void removeAllSuperTypes() { + throwImmutableError(); + } + + @Override + public void removeAllSuperTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeSuperType(SuperType superType) { + throwImmutableError(); + } + + @Override + public void removeSuperType(Game game, SuperType superType) { + throwImmutableError(); + } + + @Override + public void addCardType(CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void addCardType(Game game, CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void removeCardType(CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void removeCardType(Game game, CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void removeAllCardTypes() { + throwImmutableError(); + } + + @Override + public void removeAllCardTypes(Game game) { + throwImmutableError(); + } + + @Override + public void addSubType(Game game, Collection subTypes) { + throwImmutableError(); + } + + @Override + public void addSubType(SubType... subTypes) { + throwImmutableError(); + } + + @Override + public void addSubType(Game game, SubType... subTypes) { + throwImmutableError(); + } + + @Override + public void copySubTypesFrom(Game game, MageObject mageObject) { + throwImmutableError(); + } + + @Override + public void copySubTypesFrom(Game game, MageObject mageObject, SubTypeSet subTypeSet) { + throwImmutableError(); + } + + @Override + public void removeAllSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeAllSubTypes(Game game, SubTypeSet subTypeSet) { + throwImmutableError(); + } + + @Override + public void retainAllArtifactSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void retainAllEnchantmentSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void retainAllLandSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeAllCreatureTypes() { + throwImmutableError(); + } + + @Override + public void removeAllCreatureTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeSubType(Game game, SubType subType) { + throwImmutableError(); + } + + @Override + public void setIsAllCreatureTypes(boolean value) { + throwImmutableError(); + } + + @Override + public void setIsAllCreatureTypes(Game game, boolean value) { + throwImmutableError(); + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + throwImmutableError(); + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + throwImmutableError(); + } + + @Override + public void removePTCDA() { + throwImmutableError(); + } + + @Override + public void setControllerId(UUID controllerId) { + throwImmutableError(); + } + + @Override + public void setOriginalControllerId(UUID originalControllerId) { + throwImmutableError(); + } + + @Override + public void addInfo(String key, String value, Game game) { + throwImmutableError(); + } + + @Override + public void looseAllAbilities(Game game) { + throwImmutableError(); + } + + @Override + public void addAbility(Ability ability) { + throwImmutableError(); + } + + @Override + protected void addAbility(Ability ability, Watcher watcher) { + throwImmutableError(); + } + + @Override + public void replaceSpellAbility(SpellAbility newAbility) { + throwImmutableError(); + } + + @Override + public void setOwnerId(UUID ownerId) { + throwImmutableError(); + } + + @Override + public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag) { + return throwImmutableError(); + } + + @Override + public Ability addAbility(Ability ability, UUID sourceId, Game game) { + throwImmutableError(); + return null; + } + + @Override + public Ability addAbility(Ability ability, UUID sourceId, Game game, boolean fromExistingObject) { + throwImmutableError(); + return null; + } + + @Override + public void removeAllAbilities(UUID sourceId, Game game) { + throwImmutableError(); + } + + @Override + public void removeAbility(Ability abilityToRemove, UUID sourceId, Game game) { + throwImmutableError(); + } + + @Override + public void removeAbilities(List abilitiesToRemove, UUID sourceId, Game game) { + throwImmutableError(); + } + + @Override + public void beginningOfTurn(Game game) { + throwImmutableError(); + } + + @Override + public void endOfTurn(Game game) { + throwImmutableError(); + } + + @Override + public void incrementLoyaltyActivationsAvailable() { + throwImmutableError(); + } + + @Override + public void incrementLoyaltyActivationsAvailable(int max) { + throwImmutableError(); + } + + @Override + public void setLoyaltyActivationsAvailable(int setActivations) { + throwImmutableError(); + } + + @Override + public void addLoyaltyUsed() { + throwImmutableError(); + } + + @Override + public void setLegendRuleApplies(boolean legendRuleApplies) { + throwImmutableError(); + } + + @Override + public void setTapped(boolean tapped) { + throwImmutableError(); + } + + @Override + public boolean untap(Game game) { + return throwImmutableError(); + } + + @Override + public boolean tap(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean tap(boolean forCombat, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public void setFaceDown(boolean value, Game game) { + throwImmutableError(); + } + + @Override + public boolean flip(Game game) { + return throwImmutableError(); + } + + @Override + public boolean transform(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean transform(Ability source, Game game, boolean ignoreDayNight) { + return throwImmutableError(); + } + + @Override + public boolean phaseIn(Game game) { + return throwImmutableError(); + } + + @Override + public boolean phaseIn(Game game, boolean onlyDirect) { + return throwImmutableError(); + } + + @Override + public boolean phaseOut(Game game) { + return throwImmutableError(); + } + + @Override + public boolean phaseOut(Game game, boolean indirectPhase) { + return throwImmutableError(); + } + + @Override + public void removeSummoningSickness() { + throwImmutableError(); + } + + @Override + public void resetControl() { + throwImmutableError(); + } + + @Override + public boolean changeControllerId(UUID newControllerId, Game game, Ability source) { + return throwImmutableError(); + } + + @Override + public void removeUncontrolledRingBearer(Game game) { + throwImmutableError(); + } + + @Override + public boolean checkControlChanged(Game game) { + return throwImmutableError(); + } + + @Override + public void addConnectedCard(String key, UUID connectedCard) { + throwImmutableError(); + } + + @Override + public void clearConnectedCards(String key) { + throwImmutableError(); + } + + @Override + public void unattach(Game game) { + throwImmutableError(); + } + + @Override + public void attachTo(UUID attachToObjectId, Ability source, Game game) { + throwImmutableError(); + } + + @Override + public int damage(int damage, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damage(int damage, UUID attackerId, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable) { + throwImmutableError(); + return 0; + } + + @Override + public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable, List appliedEffects) { + throwImmutableError(); + return 0; + } + + @Override + public void markLifelink(int damage) { + throwImmutableError(); + } + + @Override + public int markDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat) { + throwImmutableError(); + return 0; + } + + @Override + public int applyDamage(Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damageWithExcess(int damage, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damageWithExcess(int damage, UUID attackerId, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public void removeAllDamage(Game game) { + throwImmutableError(); + } + + @Override + public boolean entersBattlefield(Ability source, Game game, Zone fromZone, boolean fireEvent) { + return throwImmutableError(); + } + + @Override + public boolean canBeTargetedBy(MageObject sourceObject, UUID sourceControllerId, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean destroy(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean destroy(Ability source, Game game, boolean noRegen) { + return throwImmutableError(); + } + + @Override + public boolean sacrifice(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean regenerate(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public void addPower(int power) { + throwImmutableError(); + } + + @Override + public void addToughness(int toughness) { + throwImmutableError(); + } + + @Override + public void setAttacking(boolean attacking) { + throwImmutableError(); + } + + @Override + public void setBlocking(int blocking) { + throwImmutableError(); + } + + @Override + public void setMaxBlocks(int maxBlocks) { + throwImmutableError(); + } + + @Override + public void setMinBlockedBy(int minBlockedBy) { + throwImmutableError(); + } + + @Override + public void setMaxBlockedBy(int maxBlockedBy) { + throwImmutableError(); + } + + @Override + public boolean removeFromCombat(Game game) { + return throwImmutableError(); + } + + @Override + public boolean removeFromCombat(Game game, boolean withEvent) { + return throwImmutableError(); + } + + @Override + public boolean imprint(UUID imprintedCard, Game game) { + return throwImmutableError(); + } + + @Override + public boolean clearImprinted(Game game) { + return throwImmutableError(); + } + + @Override + public void setTransformed(boolean value) { + throwImmutableError(); + } + + @Override + public void setMonstrous(boolean value) { + throwImmutableError(); + } + + @Override + public void setRenowned(boolean value) { + throwImmutableError(); + } + + @Override + public void setSuspected(boolean value, Game game, Ability source) { + throwImmutableError(); + } + + @Override + public void setRingBearer(Game game, boolean value) { + throwImmutableError(); + } + + @Override + public boolean setClassLevel(int classLevel) { + return throwImmutableError(); + } + + @Override + public void addGoadingPlayer(UUID playerId) { + throwImmutableError(); + } + + @Override + public void chooseProtector(Game game, Ability source) { + throwImmutableError(); + } + + @Override + public void setProtectorId(UUID protectorId) { + throwImmutableError(); + } + + @Override + public void setCanBeSacrificed(boolean canBeSacrificed) { + throwImmutableError(); + } + + @Override + public void setPairedCard(MageObjectReference pairedCard) { + throwImmutableError(); + } + + @Override + public void clearPairedCard() { + throwImmutableError(); + } + + @Override + public void addBandedCard(UUID bandedCard) { + throwImmutableError(); + } + + @Override + public void removeBandedCard(UUID bandedCard) { + throwImmutableError(); + } + + @Override + public void clearBandedCards() { + throwImmutableError(); + } + + @Override + public void setManifested(boolean value) { + throwImmutableError(); + } + + @Override + public void setCloaked(boolean value) { + throwImmutableError(); + } + + @Override + public void setMorphed(boolean value) { + throwImmutableError(); + } + + @Override + public void setDisguised(boolean value) { + throwImmutableError(); + } + + @Override + public void assignNewId() { + throwImmutableError(); + } + + @Override + public void setRarity(Rarity rarity) { + throwImmutableError(); + } + + @Override + public void setFlipCard(boolean flipCard) { + throwImmutableError(); + } + + @Override + public void setFlipCardName(String flipCardName) { + throwImmutableError(); + } + + @Override + public void setSecondCardFace(Card card) { + throwImmutableError(); + } + + @Override + public void setPrototyped(boolean prototyped) { + throwImmutableError(); + } + + @Override + public boolean solve(Game game, Ability source) { + return throwImmutableError(); + } + + @Override + public boolean fight(Permanent fightTarget, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) { + return throwImmutableError(); + } + + @Override + public void setCreateOrder(int createOrder) { + throwImmutableError(); + } + + @Override + public void switchPowerToughness() { + throwImmutableError(); + } + + @Override + public void detachAllAttachments(Game game) { + throwImmutableError(); + } + + @Override + public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { + return throwImmutableError(); + } + + @Override + public boolean moveToExile(UUID exileId, String name, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped, boolean faceDown) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped, boolean faceDown, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean removeFromZone(Game game, Zone fromZone, Ability source) { + return throwImmutableError(); + } + + @Override + public void applyEnterWithCounters(Permanent permanent, Ability source, Game game) { + throwImmutableError(); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/ImmutablePermanentToken.java b/Mage/src/main/java/mage/game/permanent/ImmutablePermanentToken.java new file mode 100644 index 000000000000..2b1e40a9edb9 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/ImmutablePermanentToken.java @@ -0,0 +1,944 @@ +package mage.game.permanent; + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.cards.Card; +import mage.constants.*; +import mage.counters.Counter; +import mage.game.CardState; +import mage.game.Game; +import mage.game.MageObjectAttribute; +import mage.game.events.ZoneChangeEvent; +import mage.util.immutableWrappers.ImmutablePermanent; +import mage.watchers.Watcher; + +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +/** + * Represents an immutable permanent token with dynamic values from game state. All modification attempts throw {@link UnsupportedOperationException}. + * Primarily used to save refences in Last Known Instance map. Use this if you need to save a snapshot of a permanent to reference later. + * + * @author Jmlundeen + */ +public class ImmutablePermanentToken extends PermanentToken implements ImmutablePermanent { + + protected ImmutablePermanentToken(PermanentToken permanent, MageObjectAttribute mageObjectAttribute, CardState cardState) { + super(permanent); + if (mageObjectAttribute != null) { + this.color = mageObjectAttribute.getColor(); + this.subtype = mageObjectAttribute.getSubtype(); + this.cardType = mageObjectAttribute.getCardType(); + this.supertype = mageObjectAttribute.getSuperType(); + } + if (cardState.hasLostAllAbilities()) { + abilities.clear(); + } else { + abilities.addAll(cardState.getAbilities()); + } + } + + protected ImmutablePermanentToken(ImmutablePermanentToken permanentToken) { + super(permanentToken); + } + + @Override + public PermanentToken getResetPermanent(Game game) { + PermanentToken card = new PermanentToken(this); + card.reset(game); + return card; + } + + @Override + public void reset(Game game) { + throwImmutableError(); + } + + @Override + public ImmutablePermanentToken copy() { + return new ImmutablePermanentToken(this); + } + + @Override + public boolean turnFaceUp(Ability source, Game game, UUID playerId) { + return throwImmutableError(); + } + + @Override + public boolean turnFaceDown(Ability source, Game game, UUID playerId) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, boolean isEffect) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects, boolean isEffect) { + return throwImmutableError(); + } + + @Override + public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects, boolean isEffect, int maxCounters) { + return throwImmutableError(); + } + + @Override + public void removeCounters(String counterName, int amount, Ability source, Game game) { + throwImmutableError(); + } + + @Override + public int removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public int removeCounters(Counter counter, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int removeCounters(Counter counter, Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(String counterName, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int removeAllCounters(String counterName, Ability source, Game game, boolean isDamage) { + throwImmutableError(); + return 0; + } + + @Override + public void setZone(Zone zone, Game game) { + throwImmutableError(); + } + + @Override + public void setSpellAbility(SpellAbility ability) { + throwImmutableError(); + } + + @Override + public boolean addAttachment(UUID permanentId, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean removeAttachment(UUID permanentId, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public void setName(String name) { + throwImmutableError(); + } + + @Override + public void setStartingLoyalty(int startingLoyalty) { + throwImmutableError(); + } + + @Override + public void setStartingDefense(int startingDefense) { + throwImmutableError(); + } + + @Override + public void setExpansionSetCode(String expansionSetCode) { + throwImmutableError(); + } + + @Override + public void setUsesVariousArt(boolean usesVariousArt) { + throwImmutableError(); + } + + @Override + public void setCardNumber(String cardNumber) { + throwImmutableError(); + } + + @Override + public void setImageFileName(String imageFileName) { + throwImmutableError(); + } + + @Override + public void setImageNumber(Integer imageNumber) { + throwImmutableError(); + } + + @Override + public void setManaCost(ManaCosts costs) { + throwImmutableError(); + } + + @Override + public void setCopy(boolean isCopy, MageObject copyFrom) { + throwImmutableError(); + } + + @Override + public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { + throwImmutableError(); + } + + @Override + public void setZoneChangeCounter(int value, Game game) { + throwImmutableError(); + } + + @Override + public void addSuperType(SuperType superType) { + throwImmutableError(); + } + + @Override + public void addSuperType(Game game, SuperType superType) { + throwImmutableError(); + } + + @Override + public void removeAllSuperTypes() { + throwImmutableError(); + } + + @Override + public void removeAllSuperTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeSuperType(SuperType superType) { + throwImmutableError(); + } + + @Override + public void removeSuperType(Game game, SuperType superType) { + throwImmutableError(); + } + + @Override + public void addCardType(CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void addCardType(Game game, CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void removeCardType(CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void removeCardType(Game game, CardType... cardTypes) { + throwImmutableError(); + } + + @Override + public void removeAllCardTypes() { + throwImmutableError(); + } + + @Override + public void removeAllCardTypes(Game game) { + throwImmutableError(); + } + + @Override + public void addSubType(Game game, Collection subTypes) { + throwImmutableError(); + } + + @Override + public void addSubType(SubType... subTypes) { + throwImmutableError(); + } + + @Override + public void addSubType(Game game, SubType... subTypes) { + throwImmutableError(); + } + + @Override + public void copySubTypesFrom(Game game, MageObject mageObject) { + throwImmutableError(); + } + + @Override + public void copySubTypesFrom(Game game, MageObject mageObject, SubTypeSet subTypeSet) { + throwImmutableError(); + } + + @Override + public void removeAllSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeAllSubTypes(Game game, SubTypeSet subTypeSet) { + throwImmutableError(); + } + + @Override + public void retainAllArtifactSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void retainAllEnchantmentSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void retainAllLandSubTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeAllCreatureTypes() { + throwImmutableError(); + } + + @Override + public void removeAllCreatureTypes(Game game) { + throwImmutableError(); + } + + @Override + public void removeSubType(Game game, SubType subType) { + throwImmutableError(); + } + + @Override + public void setIsAllCreatureTypes(boolean value) { + throwImmutableError(); + } + + @Override + public void setIsAllCreatureTypes(Game game, boolean value) { + throwImmutableError(); + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + throwImmutableError(); + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + throwImmutableError(); + } + + @Override + public void removePTCDA() { + throwImmutableError(); + } + + @Override + public void setControllerId(UUID controllerId) { + throwImmutableError(); + } + + @Override + public void setOriginalControllerId(UUID originalControllerId) { + throwImmutableError(); + } + + @Override + public void addInfo(String key, String value, Game game) { + throwImmutableError(); + } + + @Override + public void looseAllAbilities(Game game) { + throwImmutableError(); + } + + @Override + public void addAbility(Ability ability) { + throwImmutableError(); + } + + @Override + protected void addAbility(Ability ability, Watcher watcher) { + throwImmutableError(); + } + + @Override + public void replaceSpellAbility(SpellAbility newAbility) { + throwImmutableError(); + } + + @Override + public void setOwnerId(UUID ownerId) { + throwImmutableError(); + } + + @Override + public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag) { + return throwImmutableError(); + } + + @Override + public Ability addAbility(Ability ability, UUID sourceId, Game game) { + throwImmutableError(); + return null; + } + + @Override + public Ability addAbility(Ability ability, UUID sourceId, Game game, boolean fromExistingObject) { + throwImmutableError(); + return null; + } + + @Override + public void removeAllAbilities(UUID sourceId, Game game) { + throwImmutableError(); + } + + @Override + public void removeAbility(Ability abilityToRemove, UUID sourceId, Game game) { + throwImmutableError(); + } + + @Override + public void removeAbilities(List abilitiesToRemove, UUID sourceId, Game game) { + throwImmutableError(); + } + + @Override + public void beginningOfTurn(Game game) { + throwImmutableError(); + } + + @Override + public void endOfTurn(Game game) { + throwImmutableError(); + } + + @Override + public void incrementLoyaltyActivationsAvailable() { + throwImmutableError(); + } + + @Override + public void incrementLoyaltyActivationsAvailable(int max) { + throwImmutableError(); + } + + @Override + public void setLoyaltyActivationsAvailable(int setActivations) { + throwImmutableError(); + } + + @Override + public void addLoyaltyUsed() { + throwImmutableError(); + } + + @Override + public void setLegendRuleApplies(boolean legendRuleApplies) { + throwImmutableError(); + } + + @Override + public void setTapped(boolean tapped) { + throwImmutableError(); + } + + @Override + public boolean untap(Game game) { + return throwImmutableError(); + } + + @Override + public boolean tap(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean tap(boolean forCombat, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public void setFaceDown(boolean value, Game game) { + throwImmutableError(); + } + + @Override + public boolean flip(Game game) { + return throwImmutableError(); + } + + @Override + public boolean transform(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean transform(Ability source, Game game, boolean ignoreDayNight) { + return throwImmutableError(); + } + + @Override + public boolean phaseIn(Game game) { + return throwImmutableError(); + } + + @Override + public boolean phaseIn(Game game, boolean onlyDirect) { + return throwImmutableError(); + } + + @Override + public boolean phaseOut(Game game) { + return throwImmutableError(); + } + + @Override + public boolean phaseOut(Game game, boolean indirectPhase) { + return throwImmutableError(); + } + + @Override + public void removeSummoningSickness() { + throwImmutableError(); + } + + @Override + public void resetControl() { + throwImmutableError(); + } + + @Override + public boolean changeControllerId(UUID newControllerId, Game game, Ability source) { + return throwImmutableError(); + } + + @Override + public void removeUncontrolledRingBearer(Game game) { + throwImmutableError(); + } + + @Override + public boolean checkControlChanged(Game game) { + return throwImmutableError(); + } + + @Override + public void addConnectedCard(String key, UUID connectedCard) { + throwImmutableError(); + } + + @Override + public void clearConnectedCards(String key) { + throwImmutableError(); + } + + @Override + public void unattach(Game game) { + throwImmutableError(); + } + + @Override + public void attachTo(UUID attachToObjectId, Ability source, Game game) { + throwImmutableError(); + } + + @Override + public int damage(int damage, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damage(int damage, UUID attackerId, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable) { + throwImmutableError(); + return 0; + } + + @Override + public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable, List appliedEffects) { + throwImmutableError(); + return 0; + } + + @Override + public void markLifelink(int damage) { + throwImmutableError(); + } + + @Override + public int markDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat) { + throwImmutableError(); + return 0; + } + + @Override + public int applyDamage(Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damageWithExcess(int damage, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public int damageWithExcess(int damage, UUID attackerId, Ability source, Game game) { + throwImmutableError(); + return 0; + } + + @Override + public void removeAllDamage(Game game) { + throwImmutableError(); + } + + @Override + public boolean entersBattlefield(Ability source, Game game, Zone fromZone, boolean fireEvent) { + return throwImmutableError(); + } + + @Override + public boolean canBeTargetedBy(MageObject sourceObject, UUID sourceControllerId, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean destroy(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean destroy(Ability source, Game game, boolean noRegen) { + return throwImmutableError(); + } + + @Override + public boolean sacrifice(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean regenerate(Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public void addPower(int power) { + throwImmutableError(); + } + + @Override + public void addToughness(int toughness) { + throwImmutableError(); + } + + @Override + public void setAttacking(boolean attacking) { + throwImmutableError(); + } + + @Override + public void setBlocking(int blocking) { + throwImmutableError(); + } + + @Override + public void setMaxBlocks(int maxBlocks) { + throwImmutableError(); + } + + @Override + public void setMinBlockedBy(int minBlockedBy) { + throwImmutableError(); + } + + @Override + public void setMaxBlockedBy(int maxBlockedBy) { + throwImmutableError(); + } + + @Override + public boolean removeFromCombat(Game game) { + return throwImmutableError(); + } + + @Override + public boolean removeFromCombat(Game game, boolean withEvent) { + return throwImmutableError(); + } + + @Override + public boolean imprint(UUID imprintedCard, Game game) { + return throwImmutableError(); + } + + @Override + public boolean clearImprinted(Game game) { + return throwImmutableError(); + } + + @Override + public void setTransformed(boolean value) { + throwImmutableError(); + } + + @Override + public void setMonstrous(boolean value) { + throwImmutableError(); + } + + @Override + public void setRenowned(boolean value) { + throwImmutableError(); + } + + @Override + public void setSuspected(boolean value, Game game, Ability source) { + throwImmutableError(); + } + + @Override + public void setRingBearer(Game game, boolean value) { + throwImmutableError(); + } + + @Override + public boolean setClassLevel(int classLevel) { + return throwImmutableError(); + } + + @Override + public void addGoadingPlayer(UUID playerId) { + throwImmutableError(); + } + + @Override + public void chooseProtector(Game game, Ability source) { + throwImmutableError(); + } + + @Override + public void setProtectorId(UUID protectorId) { + throwImmutableError(); + } + + @Override + public void setCanBeSacrificed(boolean canBeSacrificed) { + throwImmutableError(); + } + + @Override + public void setPairedCard(MageObjectReference pairedCard) { + throwImmutableError(); + } + + @Override + public void clearPairedCard() { + throwImmutableError(); + } + + @Override + public void addBandedCard(UUID bandedCard) { + throwImmutableError(); + } + + @Override + public void removeBandedCard(UUID bandedCard) { + throwImmutableError(); + } + + @Override + public void clearBandedCards() { + throwImmutableError(); + } + + @Override + public void setManifested(boolean value) { + throwImmutableError(); + } + + @Override + public void setCloaked(boolean value) { + throwImmutableError(); + } + + @Override + public void setMorphed(boolean value) { + throwImmutableError(); + } + + @Override + public void setDisguised(boolean value) { + throwImmutableError(); + } + + @Override + public void assignNewId() { + throwImmutableError(); + } + + @Override + public void setRarity(Rarity rarity) { + throwImmutableError(); + } + + @Override + public void setFlipCard(boolean flipCard) { + throwImmutableError(); + } + + @Override + public void setFlipCardName(String flipCardName) { + throwImmutableError(); + } + + @Override + public void setSecondCardFace(Card card) { + throwImmutableError(); + } + + @Override + public void setPrototyped(boolean prototyped) { + throwImmutableError(); + } + + @Override + public boolean solve(Game game, Ability source) { + return throwImmutableError(); + } + + @Override + public boolean fight(Permanent fightTarget, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) { + return throwImmutableError(); + } + + @Override + public void setCreateOrder(int createOrder) { + throwImmutableError(); + } + + @Override + public void switchPowerToughness() { + throwImmutableError(); + } + + @Override + public void detachAllAttachments(Game game) { + throwImmutableError(); + } + + @Override + public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { + return throwImmutableError(); + } + + @Override + public boolean moveToExile(UUID exileId, String name, Ability source, Game game) { + return throwImmutableError(); + } + + @Override + public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped, boolean faceDown) { + return throwImmutableError(); + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped, boolean faceDown, List appliedEffects) { + return throwImmutableError(); + } + + @Override + public boolean removeFromZone(Game game, Zone fromZone, Ability source) { + return throwImmutableError(); + } + + @Override + public void applyEnterWithCounters(Permanent permanent, Ability source, Game game) { + throwImmutableError(); + } + +} diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 73075f8f6631..81dd383c8b05 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -350,8 +350,6 @@ default int damageWithExcess(int damage, UUID attackerId, Ability source, Game g * Removes this permanent from combat * * @param game - * @param withEvent true if removed from combat by an effect (default) - * false if removed because it left the battlefield * @return true if permanent was attacking or blocking */ boolean removeFromCombat(Game game); @@ -492,4 +490,6 @@ default void switchPowerToughness() { this.getPower().setBoostedValue(this.getToughness().getValue()); this.getToughness().setBoostedValue(power); } + + Permanent saveImmutableCopy(Game game); } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java index b7ef957b1008..9ff48c65e2a1 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java @@ -111,6 +111,14 @@ public void reset(Game game) { super.reset(game); } + @Override + public ImmutablePermanentCard saveImmutableCopy(Game game) { + return new ImmutablePermanentCard(this, + game.getState().getMageObjectAttribute(getId()), + game.getState().getCardState(getId()) + ); + } + protected void copyFromCard(final Card card, final Game game) { // TODO: must research - is it copy all fields or something miss this.name = card.getName(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 6a74a11ad232..fdc632f8bdb8 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -2102,4 +2102,5 @@ public boolean moveToExile(UUID exileId, String name, Ability source, Game game, } return successfullyMoved; } + } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java index 36825c296a08..d892f7b4a044 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java @@ -155,4 +155,10 @@ public boolean isTransformable() { public MageObject getOtherFace() { return this.transformed ? token : this.token.getBackFace(); } + + @Override + public PermanentToken saveImmutableCopy(Game game) { + return new ImmutablePermanentToken(this, game.getState().getMageObjectAttribute(getId()), + game.getState().getCardState(getId())); + } } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index ef0b43ea920a..bd89849b1764 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -4954,7 +4954,7 @@ public boolean moveCards(Set cards, Zone toZone, Ability source, // 303.4g. If an Aura is entering the battlefield and there is no legal object or player for it to enchant, // the Aura remains in its current zone, unless that zone is the stack. In that case, the Aura is put into // its owner's graveyard instead of entering the battlefield. If the Aura is a token, it isn't created. - if (card.hasSubtype(SubType.AURA, game) && !(source instanceof BestowAbility)) { + if (card.hasSubtype(SubType.AURA, game) && !(source instanceof BestowAbility) && !((source instanceof SpellAbility) && ((SpellAbility)source).getSpellAbilityCastMode().equals(SpellAbilityCastMode.BESTOW))) { SpellAbility auraSpellAbility; if (source instanceof SpellAbility && card.getAbilities(game).contains(source)) { // cast aura - use source ability diff --git a/Mage/src/main/java/mage/util/immutableWrappers/ImmutableObject.java b/Mage/src/main/java/mage/util/immutableWrappers/ImmutableObject.java new file mode 100644 index 000000000000..913b10eed02f --- /dev/null +++ b/Mage/src/main/java/mage/util/immutableWrappers/ImmutableObject.java @@ -0,0 +1,8 @@ +package mage.util.immutableWrappers; + +public interface ImmutableObject { + default boolean throwImmutableError() { + throw new UnsupportedOperationException("This object is immutable"); + } + +} diff --git a/Mage/src/main/java/mage/util/immutableWrappers/ImmutablePermanent.java b/Mage/src/main/java/mage/util/immutableWrappers/ImmutablePermanent.java new file mode 100644 index 000000000000..263b7d584f55 --- /dev/null +++ b/Mage/src/main/java/mage/util/immutableWrappers/ImmutablePermanent.java @@ -0,0 +1,16 @@ +package mage.util.immutableWrappers; + +import mage.game.Game; +import mage.game.permanent.Permanent; + +public interface ImmutablePermanent extends ImmutableObject { + + /** + * Returns a copy of the permanent with its values reset. Used by {@link mage.game.GameImpl#copyPermanent} + * @param game + * @return copy of permanent with values reset + */ + default Permanent getResetPermanent(Game game) { + throw new UnsupportedOperationException("Unsupported method for this object"); + } +}