From dbb9be670386c0ff4243b3c220b832a87b17e5c7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 3 Apr 2015 16:28:40 +0200 Subject: [PATCH] * Arashin Sovereign - Fixed that the player had no option to let the card go to the graveyard. --- .../mage/sets/alarareborn/SlaveOfBolas.java | 2 +- .../sets/avacynrestored/DeadeyeNavigator.java | 4 +- .../sets/avacynrestored/HarvesterOfSouls.java | 1 - .../sets/avacynrestored/ThatcherRevolt.java | 2 +- .../betrayersofkamigawa/GoryosVengeance.java | 2 +- .../KaijinOfTheVanishingTouch.java | 2 +- .../ShireiShizosCaretaker.java | 2 +- .../bornofthegods/FelhideSpiritbinder.java | 2 +- .../mage/sets/bornofthegods/SearingBlood.java | 2 +- .../HikariTwilightGuardian.java | 2 +- .../championsofkamigawa/InameLifeAspect.java | 2 +- .../sets/championsofkamigawa/JunkyoBell.java | 2 +- .../KikiJikiMirrorBreaker.java | 2 +- .../OtherworldlyJourney.java | 2 +- .../SosukeSonOfSeshiro.java | 2 +- .../TatsumasaTheDragonsFang.java | 6 +- .../championsofkamigawa/ThroughTheBreach.java | 2 +- .../mage/sets/coldsnap/AdarkarValkyrie.java | 2 +- .../mage/sets/commander/ScatteringStroke.java | 2 +- .../mage/sets/commander2013/Flickerform.java | 2 +- .../sets/commander2013/Reincarnation.java | 2 +- .../commander2013/RoonOfTheHiddenRealm.java | 2 +- .../commander2014/DarettiScrapSavant.java | 2 +- .../commander2014/FeldonOfTheThirdPath.java | 2 +- .../mage/sets/commander2014/WakeTheDead.java | 2 +- .../sets/conflux/BrackwaterElemental.java | 2 +- .../src/mage/sets/conflux/QuenchableFire.java | 2 +- .../sets/darkascension/BeguilerOfWills.java | 1 - .../mage/sets/darkascension/LoyalCathar.java | 2 +- .../src/mage/sets/darkascension/Seance.java | 2 +- .../darkascension/SuddenDisappearance.java | 2 +- .../mage/sets/dissension/RakdosGuildmage.java | 2 +- .../src/mage/sets/dragonsmaze/AEtherling.java | 2 +- .../sets/dragonsmaze/LegionsInitiative.java | 2 +- .../mage/sets/dragonsmaze/PlasmCapture.java | 2 +- .../sets/dragonsoftarkir/HedonistsTrove.java | 6 +- .../mage/sets/dragonsoftarkir/LivingLore.java | 6 +- .../sets/dragonsoftarkir/MirrorMockery.java | 2 +- .../sets/dragonsoftarkir/SwiftWarkite.java | 4 +- .../src/mage/sets/eventide/Flickerwisp.java | 2 +- .../sets/fatereforged/RallyTheAncestors.java | 2 +- .../mage/sets/fifthedition/TheWretched.java | 62 ++++++--- .../sets/futuresight/BridgeFromBelow.java | 30 ++--- .../src/mage/sets/gatecrash/Voidwalk.java | 2 +- .../sets/guildpact/GhostCouncilOfOrzhova.java | 2 +- .../src/mage/sets/guildpact/Ghostway.java | 4 +- Mage.Sets/src/mage/sets/iceage/IcyPrison.java | 2 +- .../src/mage/sets/iceage/Necropotence.java | 2 +- .../src/mage/sets/invasion/SpinalEmbrace.java | 2 +- .../mage/sets/journeyintonyx/BrainMaggot.java | 17 ++- .../src/mage/sets/journeyintonyx/Skybind.java | 2 +- .../mage/sets/journeyintonyx/Twinflame.java | 2 +- .../src/mage/sets/judgment/WormfangDrake.java | 2 +- .../khansoftarkir/AnafenzaTheForemost.java | 3 - .../sets/khansoftarkir/KheruLichLord.java | 2 +- .../khansoftarkir/MeanderingTowershell.java | 2 +- .../src/mage/sets/legends/Abomination.java | 2 +- .../src/mage/sets/legends/HazezonTamar.java | 2 +- .../src/mage/sets/limitedalpha/Berserk.java | 2 +- .../mage/sets/limitedalpha/Cockatrice.java | 2 +- .../sets/limitedalpha/ThicketBasilisk.java | 2 +- .../src/mage/sets/magic2010/DragonWhelp.java | 2 +- .../src/mage/sets/magic2010/StoneGiant.java | 2 +- .../mage/sets/magic2011/MystifyingMaze.java | 2 +- .../mage/sets/magic2011/PrimevalTitan.java | 12 +- .../mage/sets/magic2014/BanisherPriest.java | 4 +- .../src/mage/sets/magic2015/Phytotitan.java | 2 +- .../src/mage/sets/mirage/RockBasilisk.java | 2 +- .../src/mage/sets/mirage/ShallowGrave.java | 2 +- .../sets/mirrodinbesieged/GruesomeEncore.java | 2 +- .../sets/mirrodinbesieged/VirulentWound.java | 2 +- .../src/mage/sets/nemesis/ParallaxWave.java | 4 +- .../mage/sets/newphyrexia/KarnLiberated.java | 2 +- .../mage/sets/newphyrexia/MyrSuperion.java | 26 +--- .../sets/newphyrexia/PostmortemLunge.java | 2 +- .../mage/sets/newphyrexia/PraetorsGrasp.java | 2 +- .../src/mage/sets/onslaught/AstralSlide.java | 2 +- .../phyrexiavsthecoalition/HornetCannon.java | 2 +- .../mage/sets/planarchaos/FatalFrenzy.java | 2 +- .../sets/planeshift/SkyshipWeatherlight.java | 2 +- .../sets/returntoravnica/GraveBetrayal.java | 2 +- .../TrostaniSelesnyasVoice.java | 14 +- .../riseoftheeldrazi/ArrogantBloodlord.java | 32 ++--- .../sets/riseoftheeldrazi/SplinterTwin.java | 2 +- .../saviorsofkamigawa/FeralLightning.java | 2 +- .../FootstepsOfTheGoryo.java | 2 +- .../sets/saviorsofkamigawa/InameAsOne.java | 2 +- .../saviorsofkamigawa/WineOfBloodAndIron.java | 2 +- .../sets/scarsofmirrodin/ArgentSphinx.java | 2 +- .../scarsofmirrodin/GlimmerpointStag.java | 2 +- .../mage/sets/scarsofmirrodin/MimicVat.java | 2 +- .../sets/scarsofmirrodin/OgreGeargrabber.java | 2 +- .../scarsofmirrodin/VenserTheSojourner.java | 2 +- .../sets/shadowmoor/ElementalMastery.java | 2 +- .../mage/sets/shadowmoor/Giantbaiting.java | 2 +- .../mage/sets/shadowmoor/ImpromptuRaid.java | 2 +- .../mage/sets/shadowmoor/MistmeadowWitch.java | 2 +- .../mage/sets/shadowmoor/PuppeteerClique.java | 2 +- .../sets/shadowmoor/WickerWarcrawler.java | 2 +- .../sets/shardsofalara/GatherSpecimens.java | 20 +-- .../mage/sets/shardsofalara/Skeletonize.java | 2 +- .../sets/shardsofalara/TidehollowSculler.java | 4 +- .../mage/sets/stronghold/SacredGround.java | 7 +- .../src/mage/sets/tempest/CorpseDance.java | 2 +- .../src/mage/sets/tempest/OracleEnVec.java | 2 +- Mage.Sets/src/mage/sets/thedark/Venom.java | 2 +- .../sets/theros/AshiokNightmareWeaver.java | 2 +- .../mage/sets/theros/GiftOfImmortality.java | 2 +- .../mage/sets/theros/PyxisOfPandemonium.java | 4 +- .../sets/theros/RescueFromTheUnderworld.java | 2 +- .../mage/sets/theros/UnderworldCerberus.java | 8 +- .../src/mage/sets/theros/WhipOfErebos.java | 2 +- .../sets/timespiral/FlickeringSpirit.java | 63 +++++++++ .../sets/timespiral/MangaraOfCorondor.java | 2 +- .../mage/sets/timespiral/NorinTheWary.java | 2 +- .../sets/timespiral/SaffiEriksdotter.java | 2 +- .../src/mage/sets/torment/MesmericFiend.java | 10 +- .../mage/sets/urzasdestiny/AcademyRector.java | 2 +- .../src/mage/sets/urzasdestiny/Flicker.java | 56 ++++++++ .../src/mage/sets/urzaslegacy/MemoryJar.java | 2 +- .../src/mage/sets/urzassaga/SneakAttack.java | 2 +- .../sets/venservskoth/GalepowderMage.java | 2 +- .../mage/sets/vintagemasters/ManaDrain.java | 2 +- .../vintagemasters/MarchesaTheBlackRose.java | 2 +- .../sets/visions/UndiscoveredParadise.java | 2 +- .../mage/sets/worldwake/StoneIdolTrap.java | 2 +- .../mage/sets/zendikar/ElementalAppeal.java | 2 +- .../sets/zendikar/ZektarShrineExpedition.java | 2 +- .../abilities/enters/BanisherPriestTest.java | 2 +- .../abilities/enters/GatherSpecimensTest.java | 1 + .../abilities/flicker/CloudshiftTest.java | 37 ++++++ .../cards/abilities/keywords/UndyingTest.java | 1 + .../abilities/other/PastInFlamesTest.java | 5 +- .../cards/conditional/TheWretchedTest.java | 57 +++++--- .../cards/continuous/ElvishArchdruidTest.java | 5 +- .../BattlefieldTriggeredAbilitiesTest.java | 8 +- .../modification/CostModificationTest.java | 5 +- .../cards/replacement/DryadMilitantTest.java | 4 +- .../test/cards/single/FracturingGustTest.java | 4 + .../test/cards/single/ParallaxWaveTest.java | 4 + .../cards/triggers/JournexToNowhereTest.java | 4 +- .../test/cards/triggers/SoulWardenTest.java | 3 +- .../cards/triggers/dies/BrainMaggotTest.java | 2 +- .../triggers/dies/HarvesterOfSoulsTest.java | 9 +- .../test/lki/LastKnownInformationTest.java | 12 +- .../java/org/mage/test/player/TestPlayer.java | 2 +- Mage/src/mage/MageObjectReference.java | 13 +- Mage/src/mage/abilities/Ability.java | 16 ++- Mage/src/mage/abilities/AbilityImpl.java | 90 ++++++++++--- .../mage/abilities/TriggeredAbilities.java | 123 +++++++++--------- .../mage/abilities/TriggeredAbilityImpl.java | 2 +- .../common/ZoneChangeTriggeredAbility.java | 2 +- ...LeaveReturnExiledToBattlefieldAbility.java | 4 +- .../costs/common/ExileSourceCost.java | 35 +++-- .../abilities/costs/mana/GenericManaCost.java | 6 +- .../abilities/effects/ContinuousEffects.java | 14 +- .../effects/ReplacementEffectImpl.java | 5 + .../CreateDelayedTriggeredAbilityEffect.java | 2 +- .../effects/common/ExileSourceEffect.java | 46 ++++--- .../common/ExileTargetForSourceEffect.java | 2 +- .../ReturnFromExileForSourceEffect.java | 35 +++-- ...ttlefieldUnderYourControlSourceEffect.java | 22 ++-- ...ttlefieldUnderYourControlTargetEffect.java | 7 +- .../abilities/keyword/ChampionAbility.java | 2 +- .../mage/abilities/keyword/EchoAbility.java | 12 +- .../abilities/keyword/FlashbackAbility.java | 65 +++++---- .../abilities/keyword/UnearthAbility.java | 13 +- Mage/src/mage/game/Game.java | 2 +- Mage/src/mage/game/GameImpl.java | 39 +++--- Mage/src/mage/game/GameState.java | 1 + Mage/src/mage/game/stack/Spell.java | 1 + Mage/src/mage/game/stack/StackAbility.java | 16 ++- Mage/src/mage/players/ManaPool.java | 27 ++-- 173 files changed, 873 insertions(+), 506 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java create mode 100644 Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java diff --git a/Mage.Sets/src/mage/sets/alarareborn/SlaveOfBolas.java b/Mage.Sets/src/mage/sets/alarareborn/SlaveOfBolas.java index f4bbe5b34eb..414e9e9cee0 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/SlaveOfBolas.java +++ b/Mage.Sets/src/mage/sets/alarareborn/SlaveOfBolas.java @@ -101,7 +101,7 @@ class SlaveOfBolasEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java b/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java index 61722234ec2..4a5660fb5d8 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java @@ -63,8 +63,8 @@ public class DeadeyeNavigator extends CardImpl { this.addAbility(SoulbondAbility.getInstance()); // As long as Deadeye Navigator is paired with another creature, each of those creatures has "{1}{U}: Exile this creature, then return it to the battlefield under your control." - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(Zone.BATTLEFIELD), new ManaCostsImpl("{1}{U}")); - ability.addEffect(new ReturnToBattlefieldUnderYourControlSourceEffect(Zone.EXILED)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(true), new ManaCostsImpl("{1}{U}")); + ability.addEffect(new ReturnToBattlefieldUnderYourControlSourceEffect()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(ability, ruleText))); } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/HarvesterOfSouls.java b/Mage.Sets/src/mage/sets/avacynrestored/HarvesterOfSouls.java index 9400f612a6a..a31048b767a 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/HarvesterOfSouls.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/HarvesterOfSouls.java @@ -58,7 +58,6 @@ public class HarvesterOfSouls extends CardImpl { this.expansionSetCode = "AVR"; this.subtype.add("Demon"); - this.color.setBlack(true); this.power = new MageInt(5); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/ThatcherRevolt.java b/Mage.Sets/src/mage/sets/avacynrestored/ThatcherRevolt.java index f7159d030f5..2a5dfa521b6 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/ThatcherRevolt.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/ThatcherRevolt.java @@ -97,7 +97,7 @@ class ThatcherRevoltEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoryosVengeance.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoryosVengeance.java index 7c1c1f7b2e4..4818b700397 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoryosVengeance.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoryosVengeance.java @@ -125,7 +125,7 @@ class GoryosVengeanceEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/KaijinOfTheVanishingTouch.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/KaijinOfTheVanishingTouch.java index 0733b89f4a3..f52364f0fab 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/KaijinOfTheVanishingTouch.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/KaijinOfTheVanishingTouch.java @@ -94,7 +94,7 @@ class KaijinOfTheVanishingTouchEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new ReturnToHandTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/ShireiShizosCaretaker.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/ShireiShizosCaretaker.java index 2ef86d875ec..3f3d007453d 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/ShireiShizosCaretaker.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/ShireiShizosCaretaker.java @@ -151,7 +151,7 @@ class ShireiShizosCaretakerEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java b/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java index 69efc811108..39d174386f6 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/FelhideSpiritbinder.java @@ -115,7 +115,7 @@ class FelhideSpiritbinderEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java b/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java index 8f6456f0068..046b4a22a78 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java @@ -95,7 +95,7 @@ class SearingBloodEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new SearingBloodDelayedTriggeredAbility(source.getFirstTarget()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return new DamageTargetEffect(2).apply(game, source); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HikariTwilightGuardian.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HikariTwilightGuardian.java index 0a7b55abd5e..b4015f04766 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HikariTwilightGuardian.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HikariTwilightGuardian.java @@ -103,7 +103,7 @@ class HikariTwilightGuardianEffect extends OneShotEffect { new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/InameLifeAspect.java b/Mage.Sets/src/mage/sets/championsofkamigawa/InameLifeAspect.java index d7c6652c72d..036318169bc 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/InameLifeAspect.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/InameLifeAspect.java @@ -107,7 +107,7 @@ class InameLifeAspectEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); if (controller != null && sourceObject != null) { if (controller.chooseUse(outcome, "Exile " + sourceObject.getLogName() + " to return Spirit cards?", game)) { - new ExileSourceEffect(Zone.GRAVEYARD).apply(game, source); + new ExileSourceEffect().apply(game, source); return new ReturnToHandTargetEffect().apply(game, source); } return true; diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/JunkyoBell.java b/Mage.Sets/src/mage/sets/championsofkamigawa/JunkyoBell.java index 46ab66c5550..7e1c9efce0f 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/JunkyoBell.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/JunkyoBell.java @@ -103,7 +103,7 @@ private class JunkyoBellSacrificeEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java index aff7952cd47..476155135d9 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java @@ -131,7 +131,7 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/OtherworldlyJourney.java b/Mage.Sets/src/mage/sets/championsofkamigawa/OtherworldlyJourney.java index 1170a612573..d8a0daeb564 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/OtherworldlyJourney.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/OtherworldlyJourney.java @@ -99,7 +99,7 @@ class OtherworldlyJourneyEffect extends OneShotEffect { new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD, "return that card to the battlefield under its owner's control with a +1/+1 counter on it")); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); AddCountersTargetEffect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); delayedAbility.addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java b/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java index b411a75bee2..730d9837224 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java @@ -141,7 +141,7 @@ class SosukeSonOfSeshiroEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/TatsumasaTheDragonsFang.java b/Mage.Sets/src/mage/sets/championsofkamigawa/TatsumasaTheDragonsFang.java index 5dcf3b6c15a..419f09538d8 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/TatsumasaTheDragonsFang.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/TatsumasaTheDragonsFang.java @@ -70,7 +70,7 @@ public class TatsumasaTheDragonsFang extends CardImpl { // {6}, Exile Tatsumasa, the Dragon's Fang: Put a 5/5 blue Dragon Spirit creature token with flying onto the battlefield. Return Tatsumasa to the battlefield under its owner's control when that token dies. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TatsumaTheDragonsFangEffect(), new GenericManaCost(6)); - ability.addCost(new ExileSourceCost()); + ability.addCost(new ExileSourceCost(true)); this.addAbility(ability); // Equip {3} @@ -111,7 +111,7 @@ class TatsumaTheDragonsFangEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new TatsumaTheDragonsFangTriggeredAbility(fixedTarget); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; @@ -123,7 +123,7 @@ class TatsumaTheDragonsFangTriggeredAbility extends DelayedTriggeredAbility { protected FixedTarget fixedTarget; public TatsumaTheDragonsFangTriggeredAbility(FixedTarget fixedTarget) { - super(new ReturnToBattlefieldUnderYourControlSourceEffect(Zone.EXILED), Duration.OneUse); + super(new ReturnToBattlefieldUnderYourControlSourceEffect(), Duration.OneUse); this.fixedTarget = fixedTarget; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/ThroughTheBreach.java b/Mage.Sets/src/mage/sets/championsofkamigawa/ThroughTheBreach.java index e018dc5d95e..b634a884d25 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/ThroughTheBreach.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/ThroughTheBreach.java @@ -119,7 +119,7 @@ class ThroughTheBreachEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/coldsnap/AdarkarValkyrie.java b/Mage.Sets/src/mage/sets/coldsnap/AdarkarValkyrie.java index 086fdaf9e0d..49699d1ad64 100644 --- a/Mage.Sets/src/mage/sets/coldsnap/AdarkarValkyrie.java +++ b/Mage.Sets/src/mage/sets/coldsnap/AdarkarValkyrie.java @@ -115,7 +115,7 @@ class AdarkarValkyrieEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(new FixedTarget(this.getTargetPointer().getFirst(game, source))); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return false; } diff --git a/Mage.Sets/src/mage/sets/commander/ScatteringStroke.java b/Mage.Sets/src/mage/sets/commander/ScatteringStroke.java index 8fcc62768e9..c49d63c7d9f 100644 --- a/Mage.Sets/src/mage/sets/commander/ScatteringStroke.java +++ b/Mage.Sets/src/mage/sets/commander/ScatteringStroke.java @@ -103,7 +103,7 @@ class ScatteringStrokeEffect extends OneShotEffect { new AtTheBeginOMainPhaseDelayedTriggeredAbility(effect, true, TargetController.YOU, AtTheBeginOMainPhaseDelayedTriggeredAbility.PhaseSelection.NEXT_MAIN); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/commander2013/Flickerform.java b/Mage.Sets/src/mage/sets/commander2013/Flickerform.java index 24fbf2b8c11..fa30c49048f 100644 --- a/Mage.Sets/src/mage/sets/commander2013/Flickerform.java +++ b/Mage.Sets/src/mage/sets/commander2013/Flickerform.java @@ -136,7 +136,7 @@ class FlickerformEffect extends OneShotEffect { new FlickerformReturnEffect(enchantedCreature.getId(), exileZoneId)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/commander2013/Reincarnation.java b/Mage.Sets/src/mage/sets/commander2013/Reincarnation.java index 3be9ee8d14d..45dde8d09ee 100644 --- a/Mage.Sets/src/mage/sets/commander2013/Reincarnation.java +++ b/Mage.Sets/src/mage/sets/commander2013/Reincarnation.java @@ -98,7 +98,7 @@ class ReincarnationEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new ReincarnationDelayedTriggeredAbility(targetPointer.getFirst(game, source)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/commander2013/RoonOfTheHiddenRealm.java b/Mage.Sets/src/mage/sets/commander2013/RoonOfTheHiddenRealm.java index eeb543c56c7..d885e3a6723 100644 --- a/Mage.Sets/src/mage/sets/commander2013/RoonOfTheHiddenRealm.java +++ b/Mage.Sets/src/mage/sets/commander2013/RoonOfTheHiddenRealm.java @@ -129,7 +129,7 @@ class RoonOfTheHiddenRealmEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(card.getOwnerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/commander2014/DarettiScrapSavant.java b/Mage.Sets/src/mage/sets/commander2014/DarettiScrapSavant.java index 79fcd510229..b236165e2e1 100644 --- a/Mage.Sets/src/mage/sets/commander2014/DarettiScrapSavant.java +++ b/Mage.Sets/src/mage/sets/commander2014/DarettiScrapSavant.java @@ -250,7 +250,7 @@ class DarettiScrapSavantEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java b/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java index 7bdf557c6a7..0ab3b15dffc 100644 --- a/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java +++ b/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java @@ -121,7 +121,7 @@ class FeldonOfTheThirdPathEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/commander2014/WakeTheDead.java b/Mage.Sets/src/mage/sets/commander2014/WakeTheDead.java index 46f3a3a6df3..fc82a68cec7 100644 --- a/Mage.Sets/src/mage/sets/commander2014/WakeTheDead.java +++ b/Mage.Sets/src/mage/sets/commander2014/WakeTheDead.java @@ -165,7 +165,7 @@ class WakeTheDeadReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEff DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } diff --git a/Mage.Sets/src/mage/sets/conflux/BrackwaterElemental.java b/Mage.Sets/src/mage/sets/conflux/BrackwaterElemental.java index b27632f5025..8ee1ac65238 100644 --- a/Mage.Sets/src/mage/sets/conflux/BrackwaterElemental.java +++ b/Mage.Sets/src/mage/sets/conflux/BrackwaterElemental.java @@ -101,7 +101,7 @@ class BrackwaterElementalSacrificeEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return false; diff --git a/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java b/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java index 381ff57eae5..9b7f9cdaf37 100644 --- a/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java +++ b/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java @@ -96,7 +96,7 @@ class QuenchableFireEffect extends OneShotEffect { QuenchableFireDelayedTriggeredAbility delayedAbility = new QuenchableFireDelayedTriggeredAbility(); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getTargets().addAll(source.getTargets()); game.addDelayedTriggeredAbility(delayedAbility); diff --git a/Mage.Sets/src/mage/sets/darkascension/BeguilerOfWills.java b/Mage.Sets/src/mage/sets/darkascension/BeguilerOfWills.java index 07bed451c65..93f3b073836 100644 --- a/Mage.Sets/src/mage/sets/darkascension/BeguilerOfWills.java +++ b/Mage.Sets/src/mage/sets/darkascension/BeguilerOfWills.java @@ -56,7 +56,6 @@ public class BeguilerOfWills extends CardImpl { this.subtype.add("Human"); this.subtype.add("Wizard"); - this.color.setBlue(true); this.power = new MageInt(1); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/sets/darkascension/LoyalCathar.java b/Mage.Sets/src/mage/sets/darkascension/LoyalCathar.java index c47d7ce736c..08e41e3fe4b 100644 --- a/Mage.Sets/src/mage/sets/darkascension/LoyalCathar.java +++ b/Mage.Sets/src/mage/sets/darkascension/LoyalCathar.java @@ -100,7 +100,7 @@ class LoyalCatharEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnLoyalCatharEffect(source.getSourceId())); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/darkascension/Seance.java b/Mage.Sets/src/mage/sets/darkascension/Seance.java index fc9387fb38c..6e1d5378385 100644 --- a/Mage.Sets/src/mage/sets/darkascension/Seance.java +++ b/Mage.Sets/src/mage/sets/darkascension/Seance.java @@ -112,7 +112,7 @@ class SeanceEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } diff --git a/Mage.Sets/src/mage/sets/darkascension/SuddenDisappearance.java b/Mage.Sets/src/mage/sets/darkascension/SuddenDisappearance.java index 15b62eaac19..6d3b2d87091 100644 --- a/Mage.Sets/src/mage/sets/darkascension/SuddenDisappearance.java +++ b/Mage.Sets/src/mage/sets/darkascension/SuddenDisappearance.java @@ -97,7 +97,7 @@ class SuddenDisappearanceEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } diff --git a/Mage.Sets/src/mage/sets/dissension/RakdosGuildmage.java b/Mage.Sets/src/mage/sets/dissension/RakdosGuildmage.java index 635baf4fb83..b4a6247fbea 100644 --- a/Mage.Sets/src/mage/sets/dissension/RakdosGuildmage.java +++ b/Mage.Sets/src/mage/sets/dissension/RakdosGuildmage.java @@ -112,7 +112,7 @@ class RakdosGuildmageEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java b/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java index 542e122c00e..9fa3b9f5a36 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java @@ -107,7 +107,7 @@ class AEherlingRemovingEffect extends OneShotEffect { new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/LegionsInitiative.java b/Mage.Sets/src/mage/sets/dragonsmaze/LegionsInitiative.java index f7727cb7e4f..b1152ff5089 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/LegionsInitiative.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/LegionsInitiative.java @@ -133,7 +133,7 @@ class LegionsInitiativeExileEffect extends OneShotEffect { AtTheBeginOfCombatDelayedTriggeredAbility delayedAbility = new AtTheBeginOfCombatDelayedTriggeredAbility(new LegionsInitiativeReturnFromExileEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java b/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java index bbe41eb33cc..3708609e3e6 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/PlasmCapture.java @@ -101,7 +101,7 @@ class PlasmCaptureCounterEffect extends OneShotEffect { new AtTheBeginOMainPhaseDelayedTriggeredAbility(new PlasmCaptureManaEffect(mana), false, TargetController.YOU, PhaseSelection.NEXT_PRECOMBAT_MAIN); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/HedonistsTrove.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/HedonistsTrove.java index b79f31b4026..145f8035f3c 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/HedonistsTrove.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/HedonistsTrove.java @@ -101,7 +101,7 @@ class HedonistsTroveExileEffect extends OneShotEffect { Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); MageObject sourceObject = source.getSourceObject(game); if (controller != null && targetPlayer != null && sourceObject != null) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); ArrayList graveyard = new ArrayList<>(targetPlayer.getGraveyard()); for (UUID cardId : graveyard) { Card card = game.getCard(cardId); @@ -141,7 +141,7 @@ class HedonistsTrovePlayLandEffect extends AsThoughEffectImpl { Card card = game.getCard(objectId); MageObject sourceObject = source.getSourceObject(game); if (card != null && card.getCardType().contains(CardType.LAND) && sourceObject != null) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId != null) { ExileZone exileZone = game.getState().getExile().getExileZone(exileId); return exileZone != null && exileZone.contains(objectId); @@ -182,7 +182,7 @@ class HedonistsTroveCastNonlandCardsEffect extends AsThoughEffectImpl { Card card = game.getCard(objectId); MageObject sourceObject = source.getSourceObject(game); if (card != null && !card.getCardType().contains(CardType.LAND) && sourceObject != null) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId != null) { ExileZone exileZone = game.getState().getExile().getExileZone(exileId); if (exileZone != null && exileZone.contains(objectId)) { diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java index 28972e6eaa3..48c370f786c 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java @@ -110,7 +110,7 @@ class LivingLoreExileEffect extends OneShotEffect { if (sourceObject != null && controller != null){ TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard")); if (controller.chooseTarget(outcome, target, source, game)) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); Card card = controller.getGraveyard().get(target.getFirstTarget(), game); if (card != null) { controller.moveCardToExileWithInfo(card, exileId, sourceObject.getName(), source.getSourceId(), game, Zone.GRAVEYARD); @@ -147,7 +147,7 @@ class LivingLoreSetPowerToughnessSourceEffect extends ContinuousEffectImpl { discard(); return false; } - UUID exileId = CardUtil.getObjectExileZoneId(game, mageObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone == null) { @@ -192,7 +192,7 @@ class LivingLoreSacrificeEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null && mageObject != null && new MageObjectReference(permanent, game).refersTo(mageObject, game)) { if (permanent.sacrifice(source.getSourceId(), game)) { - UUID exileId = CardUtil.getObjectExileZoneId(game, mageObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); Card exiledCard = null; diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java index 424eb9d4640..a2bc51c15ee 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java @@ -120,7 +120,7 @@ class MirrorMockeryEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java index c462496c451..26ba71120ca 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java @@ -133,7 +133,7 @@ class SwiftWarkiteEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect2); delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } @@ -154,7 +154,7 @@ class SwiftWarkiteEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect2); delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/eventide/Flickerwisp.java b/Mage.Sets/src/mage/sets/eventide/Flickerwisp.java index 024ac1d53dd..5d335ca96cd 100644 --- a/Mage.Sets/src/mage/sets/eventide/Flickerwisp.java +++ b/Mage.Sets/src/mage/sets/eventide/Flickerwisp.java @@ -107,7 +107,7 @@ class FlickerwispEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD, false)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/fatereforged/RallyTheAncestors.java b/Mage.Sets/src/mage/sets/fatereforged/RallyTheAncestors.java index 485566f5625..bd820c79eb1 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/RallyTheAncestors.java +++ b/Mage.Sets/src/mage/sets/fatereforged/RallyTheAncestors.java @@ -107,7 +107,7 @@ class RallyTheAncestorsEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/fifthedition/TheWretched.java b/Mage.Sets/src/mage/sets/fifthedition/TheWretched.java index 23a5fdaead6..954925a745b 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/TheWretched.java +++ b/Mage.Sets/src/mage/sets/fifthedition/TheWretched.java @@ -44,6 +44,7 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; +import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -53,13 +54,18 @@ import mage.watchers.common.BlockedAttackerWatcher; * * @author jeffwadsworth * -5/1/2009 The ability grants you control of all creatures that are blocking it as the ability resolves. This will include any creatures that were put onto the battlefield blocking it. -5/1/2009 Any blocking creatures that regenerated during combat will have been removed from combat. Since such creatures are no longer in combat, they cannot be blocking The Wretched, which means you won't be able to gain control of them. -5/1/2009 If The Wretched itself regenerated during combat, then it will have been removed from combat. Since it is no longer in combat, there cannot be any creatures blocking it, which means you won't be able to gain control of any creatures. -10/1/2009 The Wretched's ability triggers only if it's still on the battlefield when the end of combat step begins (after the combat damage step). For example, if it's blocked by a 7/7 creature and is destroyed, its ability won't trigger at all. +5/1/2009 The ability grants you control of all creatures that are blocking it as the ability resolves. This will include +* any creatures that were put onto the battlefield blocking it. +5/1/2009 Any blocking creatures that regenerated during combat will have been removed from combat. Since such creatures +* are no longer in combat, they cannot be blocking The Wretched, which means you won't be able to gain control of them. +5/1/2009 If The Wretched itself regenerated during combat, then it will have been removed from combat. Since it is no longer +* in combat, there cannot be any creatures blocking it, which means you won't be able to gain control of any creatures. +10/1/2009 The Wretched's ability triggers only if it's still on the battlefield when the end of combat step begins (after the +* combat damage step). For example, if it's blocked by a 7/7 creature and is destroyed, its ability won't trigger at all. 10/1/2009 If The Wretched leaves the battlefield, you no longer control it, so the duration of its control-change effect ends. 10/1/2009 If you lose control of The Wretched before its ability resolves, you won't gain control of the creatures blocking it at all. -10/1/2009 Once the ability resolves, it doesn't care whether the permanents you gained control of remain creatures, only that they remain on the battlefield. +10/1/2009 Once the ability resolves, it doesn't care whether the permanents you gained control of remain creatures, only that +* they remain on the battlefield. */ public class TheWretched extends CardImpl { @@ -100,10 +106,15 @@ class EndOfAnyCombatTriggeredAbility extends TriggeredAbilityImpl { public EndOfAnyCombatTriggeredAbility copy() { return new EndOfAnyCombatTriggeredAbility(this); } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.END_COMBAT_STEP_PRE; + } @Override public boolean checkTrigger(GameEvent event, Game game) { - return (event.getType() == GameEvent.EventType.END_COMBAT_STEP_PRE); + return true; } @Override @@ -124,29 +135,44 @@ class TheWretchedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent theWretched = game.getPermanent(source.getSourceId()); + Permanent theWretched = (Permanent) source.getSourceObjectIfItStillExists(game); if (theWretched == null) { return false; } - if (theWretched.isRemovedFromCombat()) { + if (theWretched.isRemovedFromCombat() || !theWretched.isAttacking()) { return false; } if (!new SourceOnBattlefieldControlUnchangedCondition().apply(game, source)) { return false; } - BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get("BlockedAttackerWatcher"); - if (watcher != null) { - for (Permanent creature : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game)) { - if (watcher.creatureHasBlockedAttacker(theWretched, creature, game) - && !creature.isRemovedFromCombat()) { - ContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom, source.getControllerId()), new SourceOnBattlefieldControlUnchangedCondition(), "test"); - effect.setTargetPointer(new FixedTarget(creature.getId())); - game.addEffect(effect, source); + + for (CombatGroup combatGroup :game.getCombat().getGroups()) { + if (combatGroup.getAttackers().contains(source.getSourceId())) { + for(UUID creatureId: combatGroup.getBlockers()) { + Permanent blocker = game.getPermanent(creatureId); + if (blocker != null && blocker.getBlocking() > 0) { + ContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom, source.getControllerId()), new SourceOnBattlefieldControlUnchangedCondition(), ""); + effect.setTargetPointer(new FixedTarget(blocker.getId())); + game.addEffect(effect, source); + + } } } - return true; } - return false; + return true; +// BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get("BlockedAttackerWatcher"); +// if (watcher != null) { +// for (Permanent creature : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game)) { +// if (watcher.creatureHasBlockedAttacker(theWretched, creature, game) +// && !creature.isRemovedFromCombat()) { +// ContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom, source.getControllerId()), new SourceOnBattlefieldControlUnchangedCondition(), "test"); +// effect.setTargetPointer(new FixedTarget(creature.getId())); +// game.addEffect(effect, source); +// } +// } +// return true; +// } +// return false; } @Override diff --git a/Mage.Sets/src/mage/sets/futuresight/BridgeFromBelow.java b/Mage.Sets/src/mage/sets/futuresight/BridgeFromBelow.java index 35daa721e3f..0e8b36bb95c 100644 --- a/Mage.Sets/src/mage/sets/futuresight/BridgeFromBelow.java +++ b/Mage.Sets/src/mage/sets/futuresight/BridgeFromBelow.java @@ -69,8 +69,6 @@ public class BridgeFromBelow extends CardImpl { super(ownerId, 81, "Bridge from Below", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{B}{B}{B}"); this.expansionSetCode = "FUT"; - this.color.setBlack(true); - // Whenever a nontoken creature is put into your graveyard from the battlefield, if Bridge from Below is in your graveyard, put a 2/2 black Zombie creature token onto the battlefield. this.addAbility(new BridgeFromBelowAbility(new CreateTokenEffect(new ZombieToken()), filter1)); // When a creature is put into an opponent's graveyard from the battlefield, if Bridge from Below is in your graveyard, exile Bridge from Below. @@ -106,15 +104,18 @@ class BridgeFromBelowAbility extends TriggeredAbilityImpl { return new BridgeFromBelowAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() == Zone.GRAVEYARD) { - Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (permanent != null && filter.match(permanent, sourceId, controllerId, game)) { - return true; - } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() == Zone.GRAVEYARD) { + Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); + if (permanent != null && filter.match(permanent, sourceId, controllerId, game)) { + return true; } } return false; @@ -123,16 +124,11 @@ class BridgeFromBelowAbility extends TriggeredAbilityImpl { @Override public boolean checkInterveningIfClause(Game game) { Player controller = game.getPlayer(this.getControllerId()); - if(controller != null && controller.getGraveyard().contains(this.getSourceId())) { - return true; - } - return false; + return controller != null && controller.getGraveyard().contains(this.getSourceId()); } - - - + @Override public String getRule() { - return filter.getMessage() +", if Bridge from Below is in your graveyard, " + super.getRule(); + return filter.getMessage() +", if {this} is in your graveyard, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/sets/gatecrash/Voidwalk.java b/Mage.Sets/src/mage/sets/gatecrash/Voidwalk.java index ffa817321ad..4fb09d45941 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/Voidwalk.java +++ b/Mage.Sets/src/mage/sets/gatecrash/Voidwalk.java @@ -98,7 +98,7 @@ class VoidwalkEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(card.getOwnerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/guildpact/GhostCouncilOfOrzhova.java b/Mage.Sets/src/mage/sets/guildpact/GhostCouncilOfOrzhova.java index e6953319c89..3aeacc406de 100644 --- a/Mage.Sets/src/mage/sets/guildpact/GhostCouncilOfOrzhova.java +++ b/Mage.Sets/src/mage/sets/guildpact/GhostCouncilOfOrzhova.java @@ -140,7 +140,7 @@ class GhostCouncilOfOrzhovaRemovingEffect extends OneShotEffect { new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java index ed6267976c2..7570d3f1325 100644 --- a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java +++ b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java @@ -95,7 +95,7 @@ class GhostwayEffect extends OneShotEffect { MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { int numberCreatures = 0; - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (creature != null) { controller.moveCardToExileWithInfo(creature, exileId,sourceObject.getLogName(), source.getSourceId(), game, Zone.BATTLEFIELD); @@ -107,7 +107,7 @@ class GhostwayEffect extends OneShotEffect { new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/iceage/IcyPrison.java b/Mage.Sets/src/mage/sets/iceage/IcyPrison.java index 7f948ad3f0c..a93871eba4a 100644 --- a/Mage.Sets/src/mage/sets/iceage/IcyPrison.java +++ b/Mage.Sets/src/mage/sets/iceage/IcyPrison.java @@ -66,7 +66,7 @@ public class IcyPrison extends CardImpl { new DoUnlessAnyPlayerPaysEffect(new SacrificeSourceEffect(), new GenericManaCost(3)), TargetController.YOU, false)); // When Icy Prison leaves the battlefield, return the exiled card to the battlefield under its owner's control. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD, false, true), false)); } diff --git a/Mage.Sets/src/mage/sets/iceage/Necropotence.java b/Mage.Sets/src/mage/sets/iceage/Necropotence.java index febd9fb8608..1b76b90ba7b 100644 --- a/Mage.Sets/src/mage/sets/iceage/Necropotence.java +++ b/Mage.Sets/src/mage/sets/iceage/Necropotence.java @@ -144,7 +144,7 @@ class NecropotenceEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToHandeffect, TargetController.YOU); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/invasion/SpinalEmbrace.java b/Mage.Sets/src/mage/sets/invasion/SpinalEmbrace.java index 666508a757b..1d7d6d44f73 100644 --- a/Mage.Sets/src/mage/sets/invasion/SpinalEmbrace.java +++ b/Mage.Sets/src/mage/sets/invasion/SpinalEmbrace.java @@ -149,7 +149,7 @@ class SpinalEmbraceAddDelayedEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java b/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java index 61880236b1d..a1d27c47b5b 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java @@ -30,6 +30,7 @@ package mage.sets.journeyintonyx; import java.util.LinkedList; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -49,6 +50,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; @@ -117,7 +119,7 @@ class BrainMaggotExileEffect extends OneShotEffect { Card card = opponent.getHand().get(target.getFirstTarget(), game); // If source permanent leaves the battlefield before its triggered ability resolves, the target card won't be exiled. if (card != null && game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { - controller.moveCardToExileWithInfo(card, CardUtil.getObjectExileZoneId(game, sourcePermanent), sourcePermanent.getName(), source.getSourceId(), game, Zone.HAND); + controller.moveCardToExileWithInfo(card, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), sourcePermanent.getName(), source.getSourceId(), game, Zone.HAND); } } } @@ -151,9 +153,14 @@ class BrainMaggotReturnExiledCreatureAbility extends DelayedTriggeredAbility { return new BrainMaggotReturnExiledCreatureAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId())) { + if (event.getTargetId().equals(this.getSourceId())) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.getFromZone() == Zone.BATTLEFIELD) { return true; @@ -182,8 +189,10 @@ class BrainMaggotReturnExiledCreatureEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exile = game.getExile().getExileZone(CardUtil.getObjectExileZoneId(game, source.getSourceObject(game))); + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject != null && controller != null) { + int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() -1; + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (exile != null && sourcePermanent != null) { LinkedList cards = new LinkedList<>(exile); diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/Skybind.java b/Mage.Sets/src/mage/sets/journeyintonyx/Skybind.java index cb260afb96a..93ba4ddb917 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/Skybind.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/Skybind.java @@ -100,7 +100,7 @@ class SkybindEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java b/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java index 9878bca7aa5..1b1556ddb82 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java @@ -112,7 +112,7 @@ class TwinflameCopyEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/judgment/WormfangDrake.java b/Mage.Sets/src/mage/sets/judgment/WormfangDrake.java index 6b95f6d498f..71aa6c63f46 100644 --- a/Mage.Sets/src/mage/sets/judgment/WormfangDrake.java +++ b/Mage.Sets/src/mage/sets/judgment/WormfangDrake.java @@ -128,7 +128,7 @@ class WormfangDrakeExileCost extends CostImpl { MageObject sourceObject = ability.getSourceObject(game); if (controller != null && sourceObject != null) { if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, ability.getSourceId(), ability.getSourceObjectZoneChangeCounter()); for (UUID targetId: targets.get(0).getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent == null) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java b/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java index a46a4e33e44..e8d8845684a 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java @@ -72,9 +72,6 @@ public class AnafenzaTheForemost extends CardImpl { this.subtype.add("Human"); this.subtype.add("Soldier"); - this.color.setGreen(true); - this.color.setBlack(true); - this.color.setWhite(true); this.power = new MageInt(4); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/KheruLichLord.java b/Mage.Sets/src/mage/sets/khansoftarkir/KheruLichLord.java index f1c932fd2de..7c18472e6fd 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/KheruLichLord.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/KheruLichLord.java @@ -141,7 +141,7 @@ class KheruLichLordEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); KheruLichLordReplacementEffect replacementEffect = new KheruLichLordReplacementEffect(); diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/MeanderingTowershell.java b/Mage.Sets/src/mage/sets/khansoftarkir/MeanderingTowershell.java index 7b131ddf079..c0d05537b91 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/MeanderingTowershell.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/MeanderingTowershell.java @@ -118,7 +118,7 @@ class MeanderingTowershellEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtBeginningNextDeclareAttackersStepNextTurnDelayedTriggeredAbility(); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/legends/Abomination.java b/Mage.Sets/src/mage/sets/legends/Abomination.java index 746db1da820..cc5af6221dd 100644 --- a/Mage.Sets/src/mage/sets/legends/Abomination.java +++ b/Mage.Sets/src/mage/sets/legends/Abomination.java @@ -154,7 +154,7 @@ class AbominationEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(permanent.getId()); delayedAbility.setControllerId(event.getControllerId()); - delayedAbility.setSourceObject(event.getSourceObject(game)); + delayedAbility.setSourceObject(event.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/legends/HazezonTamar.java b/Mage.Sets/src/mage/sets/legends/HazezonTamar.java index a45ff2e38f4..11a0a7b4234 100644 --- a/Mage.Sets/src/mage/sets/legends/HazezonTamar.java +++ b/Mage.Sets/src/mage/sets/legends/HazezonTamar.java @@ -117,7 +117,7 @@ class HazezonTamarEntersEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java b/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java index 14606f15d4c..904cd5be44c 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java @@ -174,7 +174,7 @@ class BerserkDestroyEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Cockatrice.java b/Mage.Sets/src/mage/sets/limitedalpha/Cockatrice.java index 0d3facd1af0..066dc7eaa22 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/Cockatrice.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/Cockatrice.java @@ -136,7 +136,7 @@ class CockatriceEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/limitedalpha/ThicketBasilisk.java b/Mage.Sets/src/mage/sets/limitedalpha/ThicketBasilisk.java index 630133e654e..aaab3b720d1 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/ThicketBasilisk.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/ThicketBasilisk.java @@ -133,7 +133,7 @@ class ThicketBasiliskEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java b/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java index 883eb2b13c1..b637bdd578c 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java +++ b/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java @@ -103,7 +103,7 @@ class DragonWhelpEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new DragonWhelpDelayedEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } amount++; diff --git a/Mage.Sets/src/mage/sets/magic2010/StoneGiant.java b/Mage.Sets/src/mage/sets/magic2010/StoneGiant.java index 889d19d223c..62d8025e734 100644 --- a/Mage.Sets/src/mage/sets/magic2010/StoneGiant.java +++ b/Mage.Sets/src/mage/sets/magic2010/StoneGiant.java @@ -134,7 +134,7 @@ class StoneGiantEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/magic2011/MystifyingMaze.java b/Mage.Sets/src/mage/sets/magic2011/MystifyingMaze.java index fb994eb692c..cf47f9b2bb2 100644 --- a/Mage.Sets/src/mage/sets/magic2011/MystifyingMaze.java +++ b/Mage.Sets/src/mage/sets/magic2011/MystifyingMaze.java @@ -101,7 +101,7 @@ class MystifyingMazeEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD, true)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/magic2011/PrimevalTitan.java b/Mage.Sets/src/mage/sets/magic2011/PrimevalTitan.java index 3f7bd8a1f7c..60e79d7a2f1 100644 --- a/Mage.Sets/src/mage/sets/magic2011/PrimevalTitan.java +++ b/Mage.Sets/src/mage/sets/magic2011/PrimevalTitan.java @@ -54,7 +54,7 @@ public class PrimevalTitan extends CardImpl { super(ownerId, 192, "Primeval Titan", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); this.expansionSetCode = "M11"; this.subtype.add("Giant"); - this.color.setGreen(true); + this.power = new MageInt(6); this.toughness = new MageInt(6); @@ -90,15 +90,17 @@ class PrimevalTitanAbility extends TriggeredAbilityImpl { return new PrimevalTitanAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.ATTACKER_DECLARED || event.getType() == EventType.ENTERS_THE_BATTLEFIELD; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId())) { return true; } - if (event.getType() == EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId()) ) { - return true; - } - return false; + return event.getType() == EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId()); } @Override diff --git a/Mage.Sets/src/mage/sets/magic2014/BanisherPriest.java b/Mage.Sets/src/mage/sets/magic2014/BanisherPriest.java index 17b5cbcdbb3..527bac746a0 100644 --- a/Mage.Sets/src/mage/sets/magic2014/BanisherPriest.java +++ b/Mage.Sets/src/mage/sets/magic2014/BanisherPriest.java @@ -103,11 +103,11 @@ class BanisherPriestExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); // If Banisher Priest leaves the battlefield before its triggered ability resolves, // the target creature won't be exiled. if (permanent != null) { - return new ExileTargetEffect(CardUtil.getObjectExileZoneId(game, permanent), permanent.getLogName()).apply(game, source); + return new ExileTargetEffect(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getLogName()).apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/sets/magic2015/Phytotitan.java b/Mage.Sets/src/mage/sets/magic2015/Phytotitan.java index e8aef60a630..54098ff3478 100644 --- a/Mage.Sets/src/mage/sets/magic2015/Phytotitan.java +++ b/Mage.Sets/src/mage/sets/magic2015/Phytotitan.java @@ -94,7 +94,7 @@ class PhytotitanEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/mirage/RockBasilisk.java b/Mage.Sets/src/mage/sets/mirage/RockBasilisk.java index 2da903f01e0..fcf917cd3d3 100644 --- a/Mage.Sets/src/mage/sets/mirage/RockBasilisk.java +++ b/Mage.Sets/src/mage/sets/mirage/RockBasilisk.java @@ -135,7 +135,7 @@ class RockBasiliskEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java b/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java index f48674a193c..08189134019 100644 --- a/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java +++ b/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java @@ -113,7 +113,7 @@ class ShallowGraveEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/GruesomeEncore.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/GruesomeEncore.java index d7f56e40889..7ce29211155 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/GruesomeEncore.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/GruesomeEncore.java @@ -106,7 +106,7 @@ class GruesomeEncoreEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/VirulentWound.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/VirulentWound.java index a05353ec267..b5382f5910b 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/VirulentWound.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/VirulentWound.java @@ -97,7 +97,7 @@ class VirulentWoundEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new VirulentWoundDelayedTriggeredAbility(source.getFirstTarget()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/nemesis/ParallaxWave.java b/Mage.Sets/src/mage/sets/nemesis/ParallaxWave.java index 2bf3c7107d1..3e902629d87 100644 --- a/Mage.Sets/src/mage/sets/nemesis/ParallaxWave.java +++ b/Mage.Sets/src/mage/sets/nemesis/ParallaxWave.java @@ -45,6 +45,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.game.ExileZone; import mage.game.Game; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; @@ -104,7 +105,8 @@ class ParallaxWaveEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null) { - UUID exileZoneId = CardUtil.getObjectExileZoneId(game, sourceObject); + int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() -1; + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter); if (exileZoneId != null) { ExileZone exileZone = game.getExile().getExileZone(exileZoneId); if (exileZone != null) { diff --git a/Mage.Sets/src/mage/sets/newphyrexia/KarnLiberated.java b/Mage.Sets/src/mage/sets/newphyrexia/KarnLiberated.java index 261fd6cf29b..65334e81479 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/KarnLiberated.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/KarnLiberated.java @@ -149,7 +149,7 @@ class KarnLiberatedEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new KarnLiberatedDelayedTriggeredAbility(exileId); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); game.start(null); return true; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java index 364e0f62388..e7f2738eac5 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java @@ -30,6 +30,8 @@ package mage.sets.newphyrexia; import java.util.UUID; import mage.MageInt; import mage.abilities.StaticAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; @@ -53,7 +55,7 @@ public class MyrSuperion extends CardImpl { this.toughness = new MageInt(6); // Spend only mana produced by creatures to cast Myr Superion. - this.addAbility(new MyrSuperionStaticAbility()); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Spend only mana produced by creatures to cast {this}"))); this.getSpellAbility().getManaCostsToPay().setSourceFilter(filter); this.getSpellAbility().getManaCosts().setSourceFilter(filter); } @@ -67,25 +69,3 @@ public class MyrSuperion extends CardImpl { return new MyrSuperion(this); } } - -class MyrSuperionStaticAbility extends StaticAbility { - - public MyrSuperionStaticAbility() { - super(Zone.STACK, null); - } - - public MyrSuperionStaticAbility(MyrSuperionStaticAbility ability) { - super(ability); - } - - @Override - public MyrSuperionStaticAbility copy() { - return new MyrSuperionStaticAbility(this); - } - - @Override - public String getRule() { - return "Spend only mana produced by creatures to cast {this}."; - } - -} diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PostmortemLunge.java b/Mage.Sets/src/mage/sets/newphyrexia/PostmortemLunge.java index c7274afd37b..d7dc520a4a1 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PostmortemLunge.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PostmortemLunge.java @@ -118,7 +118,7 @@ class PostmortemLungeEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java index ca58c5fd1cd..a9015ce1700 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java @@ -103,7 +103,7 @@ class PraetorsGraspEffect extends OneShotEffect { if (controller.searchLibrary(target, game, opponent.getId())) { UUID targetId = target.getFirstTarget(); Card card = opponent.getLibrary().getCard(targetId, game); - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (card != null && exileId != null) { game.informPlayers(controller.getName() + " moves the searched card face down to exile"); card.moveToExile(exileId, sourceObject.getName(), source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java b/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java index 35423169e24..7995162ebe7 100644 --- a/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java +++ b/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java @@ -97,7 +97,7 @@ class AstralSlideEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/phyrexiavsthecoalition/HornetCannon.java b/Mage.Sets/src/mage/sets/phyrexiavsthecoalition/HornetCannon.java index 58d7ae71780..921db990ae4 100644 --- a/Mage.Sets/src/mage/sets/phyrexiavsthecoalition/HornetCannon.java +++ b/Mage.Sets/src/mage/sets/phyrexiavsthecoalition/HornetCannon.java @@ -98,7 +98,7 @@ class HornetCannonEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(destroyEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/planarchaos/FatalFrenzy.java b/Mage.Sets/src/mage/sets/planarchaos/FatalFrenzy.java index d2ff2f038cf..0e50c4eb3ce 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/FatalFrenzy.java +++ b/Mage.Sets/src/mage/sets/planarchaos/FatalFrenzy.java @@ -100,7 +100,7 @@ class FatalFrenzyEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/planeshift/SkyshipWeatherlight.java b/Mage.Sets/src/mage/sets/planeshift/SkyshipWeatherlight.java index ac5662ad6ba..077bd3a69b9 100644 --- a/Mage.Sets/src/mage/sets/planeshift/SkyshipWeatherlight.java +++ b/Mage.Sets/src/mage/sets/planeshift/SkyshipWeatherlight.java @@ -132,7 +132,7 @@ class SkyshipWeatherlightEffect2 extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - ExileZone exZone = game.getExile().getExileZone(CardUtil.getObjectExileZoneId(game, sourceObject)); + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); if (exZone != null) { Card card = exZone.getRandom(game); controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.EXILED); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java index c8f82a8e597..cd1acf681db 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java @@ -114,7 +114,7 @@ class GraveBetrayalTriggeredAbility extends TriggeredAbilityImpl { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(this.getSourceId()); delayedAbility.setControllerId(this.getControllerId()); - delayedAbility.setSourceObject(this.getSourceObject(game)); + delayedAbility.setSourceObject(this.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/TrostaniSelesnyasVoice.java b/Mage.Sets/src/mage/sets/returntoravnica/TrostaniSelesnyasVoice.java index a30212e4292..4a89055ee60 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/TrostaniSelesnyasVoice.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/TrostaniSelesnyasVoice.java @@ -28,6 +28,8 @@ package mage.sets.returntoravnica; +import java.util.UUID; +import static javax.xml.bind.JAXBIntrospector.getValue; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -47,8 +49,6 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import java.util.UUID; - /** * * @author LevelX2 @@ -60,8 +60,7 @@ public class TrostaniSelesnyasVoice extends CardImpl { this.expansionSetCode = "RTR"; this.supertype.add("Legendary"); this.subtype.add("Dryad"); - this.color.setGreen(true); - this.color.setWhite(true); + this.power = new MageInt(2); this.toughness = new MageInt(5); @@ -102,6 +101,7 @@ class TrostaniSelesnyasVoiceTriggeredAbility extends TriggeredAbilityImpl { && permanent.getControllerId().equals(this.controllerId) && event.getTargetId() != this.getSourceId()) { Effect effect = this.getEffects().get(0); + // life is determined during resolution so it has to be retrieved there (e.g. Giant Growth before resolution) effect.setValue("lifeSource", event.getTargetId()); effect.setValue("zoneChangeCounter", permanent.getZoneChangeCounter(game)); return true; @@ -112,7 +112,7 @@ class TrostaniSelesnyasVoiceTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever another creature enters the battlefield under your control, you gain life equal to that creature's toughness."; + return "Whenever another creature enters the battlefield under your control, " + super.getRule(); } @Override @@ -125,7 +125,7 @@ class TrostaniSelesnyasVoiceEffect extends OneShotEffect { public TrostaniSelesnyasVoiceEffect() { super(Outcome.GainLife); - staticText = "you gain life equal to its toughness"; + staticText = "you gain life equal to that creature's toughness"; } public TrostaniSelesnyasVoiceEffect(final TrostaniSelesnyasVoiceEffect effect) { @@ -155,4 +155,4 @@ class TrostaniSelesnyasVoiceEffect extends OneShotEffect { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ArrogantBloodlord.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ArrogantBloodlord.java index 8cc722bb30b..568f7bba137 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ArrogantBloodlord.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ArrogantBloodlord.java @@ -89,22 +89,24 @@ class ArrogantBloodlordTriggeredAbility extends TriggeredAbilityImpl { return new ArrogantBloodlordTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { - Permanent blocker = game.getPermanent(event.getSourceId()); - Permanent blocked = game.getPermanent(event.getTargetId()); - Permanent arrogantBloodlord = game.getPermanent(sourceId); - if (blocker != null && blocker != arrogantBloodlord - && blocker.getPower().getValue() < 2 - && blocked == arrogantBloodlord) { - return true; - } - if (blocker != null && blocker == arrogantBloodlord - && game.getPermanent(event.getTargetId()).getPower().getValue() < 2) { - return true; - } - return false; + Permanent blocker = game.getPermanent(event.getSourceId()); + Permanent blocked = game.getPermanent(event.getTargetId()); + Permanent arrogantBloodlord = game.getPermanent(sourceId); + if (blocker != null && blocker != arrogantBloodlord + && blocker.getPower().getValue() < 2 + && blocked == arrogantBloodlord) { + return true; + } + if (blocker != null && blocker == arrogantBloodlord + && game.getPermanent(event.getTargetId()).getPower().getValue() < 2) { + return true; } return false; } @@ -133,7 +135,7 @@ class ArrogantBloodlordEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(source.getSourceId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java index dd965111302..935ebb8b7b1 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java @@ -119,7 +119,7 @@ class SplinterTwinEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/FeralLightning.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/FeralLightning.java index 0ba4d846ea4..9b97fb0cf52 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/FeralLightning.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/FeralLightning.java @@ -101,7 +101,7 @@ class FeralLightningEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/FootstepsOfTheGoryo.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/FootstepsOfTheGoryo.java index 75e00fea63d..7b95bd8050c 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/FootstepsOfTheGoryo.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/FootstepsOfTheGoryo.java @@ -110,7 +110,7 @@ class FootstepsOfTheGoryoEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java index 79a257de5d2..e7246a95664 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java @@ -121,7 +121,7 @@ class InameAsOneEffect extends OneShotEffect { // exile it, not that it actually gets to the exile zone. This is similar to how destroying a creature // (with, for example, Rest in Peace) doesn't necessarily ensure that creature will end up in the graveyard; // it just so happens that the action of exiling something and the exile zone both use the same word: "exile". - new ExileSourceEffect(Zone.GRAVEYARD).apply(game, source); + new ExileSourceEffect().apply(game, source); return new ReturnFromGraveyardToBattlefieldTargetEffect().apply(game, source); } return true; diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/WineOfBloodAndIron.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/WineOfBloodAndIron.java index 97f21d2197c..2a831a03a06 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/WineOfBloodAndIron.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/WineOfBloodAndIron.java @@ -100,7 +100,7 @@ class WineOfBloodAndIronEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java index c92a61e319d..dd93aadc947 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java @@ -99,7 +99,7 @@ class ArgentSphinxEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GlimmerpointStag.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GlimmerpointStag.java index a04acf096cf..d50ba2057c9 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GlimmerpointStag.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GlimmerpointStag.java @@ -100,7 +100,7 @@ class GlimmerpointStagEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java index 8ef9e3b1290..47c8fb0af19 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java @@ -212,7 +212,7 @@ class MimicVatCreateTokenEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/OgreGeargrabber.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/OgreGeargrabber.java index 247252a0f61..14f91f126d4 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/OgreGeargrabber.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/OgreGeargrabber.java @@ -111,7 +111,7 @@ class OgreGeargrabberEffect1 extends OneShotEffect { OgreGeargrabberDelayedTriggeredAbility delayedAbility = new OgreGeargrabberDelayedTriggeredAbility(equipmentId); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); Permanent equipment = game.getPermanent(equipmentId); if (equipment != null) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java index b432a0c9ca7..9100da777fe 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java @@ -136,7 +136,7 @@ class VenserTheSojournerEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ElementalMastery.java b/Mage.Sets/src/mage/sets/shadowmoor/ElementalMastery.java index 5840b30eac5..f09c9b30ed4 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ElementalMastery.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ElementalMastery.java @@ -119,7 +119,7 @@ class ElementalMasteryEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java b/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java index 8a291f6b3e9..43ca95f1ffa 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java @@ -97,7 +97,7 @@ class GiantbaitingEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ImpromptuRaid.java b/Mage.Sets/src/mage/sets/shadowmoor/ImpromptuRaid.java index a2d7cde7e7a..583e2c8601c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ImpromptuRaid.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ImpromptuRaid.java @@ -125,7 +125,7 @@ class ImpromptuRaidEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java b/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java index cd282e9c85a..a471b42244e 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java @@ -99,7 +99,7 @@ class MistmeadowWitchEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/PuppeteerClique.java b/Mage.Sets/src/mage/sets/shadowmoor/PuppeteerClique.java index 1f1452805e4..c9853a17c9e 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/PuppeteerClique.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/PuppeteerClique.java @@ -130,7 +130,7 @@ class PuppeteerCliqueEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect, TargetController.YOU); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); result = true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/WickerWarcrawler.java b/Mage.Sets/src/mage/sets/shadowmoor/WickerWarcrawler.java index ccb2fc01d94..4ea92e72079 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/WickerWarcrawler.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/WickerWarcrawler.java @@ -89,7 +89,7 @@ class WickerWarcrawlerEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance())); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(source.getSourceId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/shardsofalara/GatherSpecimens.java b/Mage.Sets/src/mage/sets/shardsofalara/GatherSpecimens.java index 8ceda7ed95a..34e541b2602 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/GatherSpecimens.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/GatherSpecimens.java @@ -90,15 +90,6 @@ class GatherSpecimensReplacementEffect extends ReplacementEffectImpl { return true; } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - event.setPlayerId(controller.getId()); - } - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ZONE_CHANGE || event.getType() == GameEvent.EventType.CREATE_TOKEN; @@ -107,7 +98,7 @@ class GatherSpecimensReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && ((ZoneChangeEvent) event).getToZone() == Zone.BATTLEFIELD) { + && ((ZoneChangeEvent) event).getToZone().match(Zone.BATTLEFIELD)) { Card card = game.getCard(event.getTargetId()); if (card.getCardType().contains(CardType.CREATURE)) { // TODO: Bestow Card cast as Enchantment probably not handled correctly Player controller = game.getPlayer(source.getControllerId()); @@ -124,4 +115,13 @@ class GatherSpecimensReplacementEffect extends ReplacementEffectImpl { } return false; } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + event.setPlayerId(controller.getId()); + } + return false; + } } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java b/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java index 3dfa27e93ef..5ff6e3656fb 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java @@ -103,7 +103,7 @@ class SkeletonizeEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new SkeletonizeDelayedTriggeredAbility(); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/TidehollowSculler.java b/Mage.Sets/src/mage/sets/shardsofalara/TidehollowSculler.java index c290aba0449..38ea11a0514 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/TidehollowSculler.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/TidehollowSculler.java @@ -44,6 +44,7 @@ import mage.filter.common.FilterNonlandCard; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; @@ -149,7 +150,8 @@ class TidehollowScullerLeaveEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (controller != null && sourceObject != null) { - ExileZone exZone = game.getExile().getExileZone(CardUtil.getObjectExileZoneId(game, sourceObject)); + int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() -1; + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); if (exZone != null) { for (Card card : exZone.getCards(game)) { if (card != null) { diff --git a/Mage.Sets/src/mage/sets/stronghold/SacredGround.java b/Mage.Sets/src/mage/sets/stronghold/SacredGround.java index f7ab63d0106..08846966fe8 100644 --- a/Mage.Sets/src/mage/sets/stronghold/SacredGround.java +++ b/Mage.Sets/src/mage/sets/stronghold/SacredGround.java @@ -92,11 +92,8 @@ class SacredGroundTriggeredAbility extends TriggeredAbilityImpl { if (Zone.BATTLEFIELD.equals(zce.getFromZone()) && Zone.GRAVEYARD.equals(zce.getToZone())) { Permanent targetPermanent = zce.getTarget(); if (targetPermanent.getCardType().contains(CardType.LAND) && targetPermanent.getControllerId().equals(getControllerId())) { - MageObject mageObject = game.getShortLivingLKI(event.getSourceId(), Zone.STACK); - if (mageObject instanceof StackObject) { - getEffects().get(0).setTargetPointer(new FixedTarget(targetPermanent.getId())); - return true; - } + getEffects().get(0).setTargetPointer(new FixedTarget(targetPermanent.getId())); + return true; } } } diff --git a/Mage.Sets/src/mage/sets/tempest/CorpseDance.java b/Mage.Sets/src/mage/sets/tempest/CorpseDance.java index e9c9bf61c55..3476d2477c3 100644 --- a/Mage.Sets/src/mage/sets/tempest/CorpseDance.java +++ b/Mage.Sets/src/mage/sets/tempest/CorpseDance.java @@ -116,7 +116,7 @@ class CorpseDanceEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/tempest/OracleEnVec.java b/Mage.Sets/src/mage/sets/tempest/OracleEnVec.java index 2c525b626de..f7c0de990ea 100644 --- a/Mage.Sets/src/mage/sets/tempest/OracleEnVec.java +++ b/Mage.Sets/src/mage/sets/tempest/OracleEnVec.java @@ -126,7 +126,7 @@ class OracleEnVecEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new OracleEnVecDelayedTriggeredAbility(game.getTurnNum(), target.getTargets()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/thedark/Venom.java b/Mage.Sets/src/mage/sets/thedark/Venom.java index 316dceafcd6..eb2c763a830 100644 --- a/Mage.Sets/src/mage/sets/thedark/Venom.java +++ b/Mage.Sets/src/mage/sets/thedark/Venom.java @@ -144,7 +144,7 @@ class VenomEffect extends OneShotEffect { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/theros/AshiokNightmareWeaver.java b/Mage.Sets/src/mage/sets/theros/AshiokNightmareWeaver.java index cf8ebdd6657..28efbbfeef6 100644 --- a/Mage.Sets/src/mage/sets/theros/AshiokNightmareWeaver.java +++ b/Mage.Sets/src/mage/sets/theros/AshiokNightmareWeaver.java @@ -265,7 +265,7 @@ class AshiokNightmareWeaverExileAllEffect extends OneShotEffect { if (sourceObject == null || controller == null) { return false; } - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId == null) { return false; } diff --git a/Mage.Sets/src/mage/sets/theros/GiftOfImmortality.java b/Mage.Sets/src/mage/sets/theros/GiftOfImmortality.java index 7e99744ca43..29fab265fd6 100644 --- a/Mage.Sets/src/mage/sets/theros/GiftOfImmortality.java +++ b/Mage.Sets/src/mage/sets/theros/GiftOfImmortality.java @@ -113,7 +113,7 @@ class GiftOfImmortalityEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java b/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java index 1daa6936996..bd019836222 100644 --- a/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java +++ b/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java @@ -115,7 +115,7 @@ class PyxisOfPandemoniumExileEffect extends OneShotEffect { if (player != null) { if (player.getLibrary().size() > 0) { Card card = player.getLibrary().getFromTop(game); - String exileKey = playerId.toString() + CardUtil.getObjectExileZoneId(game, sourceObject).toString(); + String exileKey = playerId.toString() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()).toString(); UUID exileId = exileIds.get(exileKey); if (exileId == null) { exileId = UUID.randomUUID(); @@ -167,7 +167,7 @@ class PyxisOfPandemoniumPutOntoBattlefieldEffect extends OneShotEffect { for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { - String exileKey = playerId.toString() + CardUtil.getObjectExileZoneId(game, sourceObject).toString(); + String exileKey = playerId.toString() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()).toString(); UUID exileId = exileIds.get(exileKey); if (exileId != null) { ExileZone exileZone = game.getState().getExile().getExileZone(exileId); diff --git a/Mage.Sets/src/mage/sets/theros/RescueFromTheUnderworld.java b/Mage.Sets/src/mage/sets/theros/RescueFromTheUnderworld.java index 1e50ea9d0c5..3640058e174 100644 --- a/Mage.Sets/src/mage/sets/theros/RescueFromTheUnderworld.java +++ b/Mage.Sets/src/mage/sets/theros/RescueFromTheUnderworld.java @@ -146,7 +146,7 @@ class RescueFromTheUnderworldCreateDelayedTriggeredAbilityEffect extends OneShot DelayedTriggeredAbility delayedAbility = (DelayedTriggeredAbility) ability.copy(); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getTargets().addAll(source.getTargets()); for(Effect effect : delayedAbility.getEffects()) { effect.getTargetPointer().init(game, source); diff --git a/Mage.Sets/src/mage/sets/theros/UnderworldCerberus.java b/Mage.Sets/src/mage/sets/theros/UnderworldCerberus.java index 9e50047530d..4d43827ea89 100644 --- a/Mage.Sets/src/mage/sets/theros/UnderworldCerberus.java +++ b/Mage.Sets/src/mage/sets/theros/UnderworldCerberus.java @@ -59,15 +59,15 @@ public class UnderworldCerberus extends CardImpl { this.expansionSetCode = "THS"; this.subtype.add("Hound"); - this.color.setRed(true); - this.color.setBlack(true); this.power = new MageInt(6); this.toughness = new MageInt(6); // Underworld Cerberus can't be blocked except by three or more creatures. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedByOneEffect(3))); + // Cards in graveyards can't be the targets of spells or abilities. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UnderworldCerberusEffect())); + // When Underworld Cerberus dies, exile it and each player returns all creature cards from his or her graveyard to his or her hand. Ability ability = new DiesTriggeredAbility(new ExileSourceEffect()); ability.addEffect(new ReturnToHandFromGraveyardAllEffect(new FilterCreatureCard("creature cards"))); @@ -113,10 +113,10 @@ class UnderworldCerberusEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { Card targetCard = game.getCard(event.getTargetId()); - StackObject stackObject = (StackObject) game.getStack().getStackObject(event.getSourceId()); + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); if (targetCard != null && stackObject != null) { Zone zone = game.getState().getZone(targetCard.getId()); - if (zone != null && (zone == Zone.GRAVEYARD)) { + if (zone != null && zone == Zone.GRAVEYARD) { return true; } } diff --git a/Mage.Sets/src/mage/sets/theros/WhipOfErebos.java b/Mage.Sets/src/mage/sets/theros/WhipOfErebos.java index 97b1633f236..ca4d377c130 100644 --- a/Mage.Sets/src/mage/sets/theros/WhipOfErebos.java +++ b/Mage.Sets/src/mage/sets/theros/WhipOfErebos.java @@ -127,7 +127,7 @@ class WhipOfErebosEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java b/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java new file mode 100644 index 00000000000..240355f9bd1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java @@ -0,0 +1,63 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.timespiral; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LevelX2 + */ +public class FlickeringSpirit extends CardImpl { + + public FlickeringSpirit(UUID ownerId) { + super(ownerId, 17, "Flickering Spirit", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.expansionSetCode = "TSP"; + this.subtype.add("Spirit"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // {3}{W}: Exile Flickering Spirit, then return it to the battlefield under its owner's control. + } + + public FlickeringSpirit(final FlickeringSpirit card) { + super(card); + } + + @Override + public FlickeringSpirit copy() { + return new FlickeringSpirit(this); + } +} diff --git a/Mage.Sets/src/mage/sets/timespiral/MangaraOfCorondor.java b/Mage.Sets/src/mage/sets/timespiral/MangaraOfCorondor.java index 1f531d8f736..a6bdfb8da5f 100644 --- a/Mage.Sets/src/mage/sets/timespiral/MangaraOfCorondor.java +++ b/Mage.Sets/src/mage/sets/timespiral/MangaraOfCorondor.java @@ -58,7 +58,7 @@ public class MangaraOfCorondor extends CardImpl { this.toughness = new MageInt(1); // {tap}: Exile Mangara of Corondor and target permanent. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(Zone.BATTLEFIELD), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(), new TapSourceCost()); ability.addEffect(new ExileTargetEffect()); ability.addTarget(new TargetPermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java b/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java index d1b899ae047..bd37b15f168 100644 --- a/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java +++ b/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java @@ -132,7 +132,7 @@ class NorinTheWaryRemovingEffect extends OneShotEffect { new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/timespiral/SaffiEriksdotter.java b/Mage.Sets/src/mage/sets/timespiral/SaffiEriksdotter.java index 00640ca0ebf..472967811e1 100644 --- a/Mage.Sets/src/mage/sets/timespiral/SaffiEriksdotter.java +++ b/Mage.Sets/src/mage/sets/timespiral/SaffiEriksdotter.java @@ -105,7 +105,7 @@ class SaffiEriksdotterEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new SaffiEriksdotterDelayedTriggeredAbility(new FixedTarget(this.getTargetPointer().getFirst(game, source))); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return false; } diff --git a/Mage.Sets/src/mage/sets/torment/MesmericFiend.java b/Mage.Sets/src/mage/sets/torment/MesmericFiend.java index 646116480a0..b420a60c9f7 100644 --- a/Mage.Sets/src/mage/sets/torment/MesmericFiend.java +++ b/Mage.Sets/src/mage/sets/torment/MesmericFiend.java @@ -29,6 +29,7 @@ package mage.sets.torment; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; @@ -43,6 +44,7 @@ import mage.filter.common.FilterNonlandCard; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; @@ -110,7 +112,7 @@ class MesmericFiendExileEffect extends OneShotEffect { if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) { Card card = opponent.getHand().get(target.getFirstTarget(), game); if (card != null) { - controller.moveCardToExileWithInfo(card, CardUtil.getObjectExileZoneId(game, sourcePermanent), sourcePermanent.getName(), source.getSourceId(), game, Zone.HAND); + controller.moveCardToExileWithInfo(card, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), sourcePermanent.getName(), source.getSourceId(), game, Zone.HAND); } } @@ -141,8 +143,10 @@ class MesmericFiendLeaveEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exZone = game.getExile().getExileZone(CardUtil.getObjectExileZoneId(game, source.getSourceObject(game))); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject !=null) { + int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() -1; + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); if (exZone != null) { for (Card card : exZone.getCards(game)) { if (card != null) { diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/AcademyRector.java b/Mage.Sets/src/mage/sets/urzasdestiny/AcademyRector.java index 232f7be4a9c..6713f455a08 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/AcademyRector.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/AcademyRector.java @@ -102,7 +102,7 @@ class AcademyRectorEffect extends OneShotEffect { MageObject sourceObject = game.getObject(source.getSourceId()); if (controller != null && sourceObject != null) { if (controller.chooseUse(outcome, "Exile " + sourceObject.getLogName() + " to return Spirit card?", game)) { - new ExileSourceEffect(Zone.GRAVEYARD).apply(game, source); + new ExileSourceEffect().apply(game, source); TargetCardInLibrary target = new TargetCardInLibrary(filter); target.setNotTarget(true); controller.searchLibrary(target, game); diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java b/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java new file mode 100644 index 00000000000..b690ecf5f90 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.urzasdestiny; + +import java.util.UUID; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LevelX2 + */ +public class Flicker extends CardImpl { + + public Flicker(UUID ownerId) { + super(ownerId, 9, "Flicker", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{W}"); + this.expansionSetCode = "UDS"; + + // Exile target nontoken permanent, then return it to the battlefield under its owner's control. + } + + public Flicker(final Flicker card) { + super(card); + } + + @Override + public Flicker copy() { + return new Flicker(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzaslegacy/MemoryJar.java b/Mage.Sets/src/mage/sets/urzaslegacy/MemoryJar.java index 1387ec50842..d91b6ad1630 100644 --- a/Mage.Sets/src/mage/sets/urzaslegacy/MemoryJar.java +++ b/Mage.Sets/src/mage/sets/urzaslegacy/MemoryJar.java @@ -129,7 +129,7 @@ class MemoryJarEffect extends OneShotEffect { delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/SneakAttack.java b/Mage.Sets/src/mage/sets/urzassaga/SneakAttack.java index e55fc37dd5c..7c92c489747 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/SneakAttack.java +++ b/Mage.Sets/src/mage/sets/urzassaga/SneakAttack.java @@ -116,7 +116,7 @@ class SneakAttackEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/venservskoth/GalepowderMage.java b/Mage.Sets/src/mage/sets/venservskoth/GalepowderMage.java index ef46c296413..3b966ba4232 100644 --- a/Mage.Sets/src/mage/sets/venservskoth/GalepowderMage.java +++ b/Mage.Sets/src/mage/sets/venservskoth/GalepowderMage.java @@ -120,7 +120,7 @@ class GalepowderMageEffect extends OneShotEffect { AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(card.getOwnerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); } } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java b/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java index 7afd9d22e0c..1561ea5d217 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/ManaDrain.java @@ -101,7 +101,7 @@ class ManaDrainCounterEffect extends OneShotEffect { new AtTheBeginOMainPhaseDelayedTriggeredAbility(effect, false, TargetController.YOU, PhaseSelection.NEXT_MAIN); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/MarchesaTheBlackRose.java b/Mage.Sets/src/mage/sets/vintagemasters/MarchesaTheBlackRose.java index 5974ac06525..8dcff4bfb95 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/MarchesaTheBlackRose.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/MarchesaTheBlackRose.java @@ -160,7 +160,7 @@ class MarchesaTheBlackRoseEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId())); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/visions/UndiscoveredParadise.java b/Mage.Sets/src/mage/sets/visions/UndiscoveredParadise.java index edfc0a3ec67..f82c2cbeaca 100644 --- a/Mage.Sets/src/mage/sets/visions/UndiscoveredParadise.java +++ b/Mage.Sets/src/mage/sets/visions/UndiscoveredParadise.java @@ -86,7 +86,7 @@ class UndiscoveredParadiseEffect extends OneShotEffect { AtBeginningOfUntapDelayedTriggeredAbility delayedAbility = new AtBeginningOfUntapDelayedTriggeredAbility(new ReturnToHandSourceEffect(true)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; } diff --git a/Mage.Sets/src/mage/sets/worldwake/StoneIdolTrap.java b/Mage.Sets/src/mage/sets/worldwake/StoneIdolTrap.java index c597a5d4851..bc6f9e86912 100644 --- a/Mage.Sets/src/mage/sets/worldwake/StoneIdolTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/StoneIdolTrap.java @@ -143,7 +143,7 @@ class StoneIdolTrapEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect, TargetController.YOU); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/zendikar/ElementalAppeal.java b/Mage.Sets/src/mage/sets/zendikar/ElementalAppeal.java index e907ebb2bae..fb98e221fa7 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ElementalAppeal.java +++ b/Mage.Sets/src/mage/sets/zendikar/ElementalAppeal.java @@ -109,7 +109,7 @@ class ElementalAppealEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Sets/src/mage/sets/zendikar/ZektarShrineExpedition.java b/Mage.Sets/src/mage/sets/zendikar/ZektarShrineExpedition.java index 2da29ce3a05..c26d30ab296 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ZektarShrineExpedition.java +++ b/Mage.Sets/src/mage/sets/zendikar/ZektarShrineExpedition.java @@ -108,7 +108,7 @@ class ZektarShrineExpeditionEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(delayedAbility); return true; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java index 6f8421fa7b6..133d1724b49 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BanisherPriestTest.java @@ -63,7 +63,7 @@ public class BanisherPriestTest extends CardTestPlayerBase { /** - * Check if the returning target did not trigger the die Event of + * Check that the returning target did not trigger the dies Event of * the dying Banisher Priest */ @Test diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/GatherSpecimensTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/GatherSpecimensTest.java index 4c3dcf71a8e..5ce39d021f3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/GatherSpecimensTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/GatherSpecimensTest.java @@ -101,6 +101,7 @@ public class GatherSpecimensTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); addCard(Zone.LIBRARY, playerB, "Plains", 10); + // If a creature would enter the battlefield under an opponent's control this turn, it enters the battlefield under your control instead. castSpell(1, PhaseStep.UPKEEP, playerA, "Gather Specimens"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oblivion Ring"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/flicker/CloudshiftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/flicker/CloudshiftTest.java index 7afac0cc4bd..348dea38ad6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/flicker/CloudshiftTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/flicker/CloudshiftTest.java @@ -19,6 +19,9 @@ public class CloudshiftTest extends CardTestPlayerBase { /** * Tests that casting Cloudshift makes targeting spell fizzling + * + * Cloudshift + * Exile target creature you control, then return that card to the battlefield under your control. */ @Test public void testSpellFizzle() { @@ -229,4 +232,38 @@ public class CloudshiftTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); } + /** + * Test that if I cast cloudshift and it goes to the stack and another instant spell exiles the + * target creature as response, cloudshift does not bring back that creature from exile because it's + * a complete other object (400.7). + * 400.7g allows Cloudshift to bring it back only if it was exiled by cloudshift itself. + * + */ + @Test + public void testReturnIfExiledByAnotherSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.HAND, playerA, "Cloudshift"); + + + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + // Exile target creature. Its controller gains life equal to its power. + addCard(Zone.HAND, playerB, "Swords to Plowshares"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Swords to Plowshares", "Silvercoat Lion", "Cloudshift"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Cloudshift", 1); + assertGraveyardCount(playerB, "Swords to Plowshares", 1); + + assertLife(playerA, 22); + assertLife(playerB, 20); + + assertPermanentCount(playerA,"Silvercoat Lion", 0); + assertExileCount("Silvercoat Lion", 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index 1ab3383cd8a..eca34b84cd3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -164,6 +164,7 @@ public class UndyingTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); // Destroy all creatures. They can't be regenerated. addCard(Zone.HAND, playerB, "Wrath of God"); + // Anafenza, the Foremost // Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control. // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java index a0325e26810..6d4b9fb5f48 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java @@ -61,11 +61,12 @@ public class PastInFlamesTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - assertExileCount("Lightning Bolt", 1); - assertGraveyardCount(playerA, "Lightning Bolt", 0); assertLife(playerB, 17); assertLife(playerA, 20); + assertExileCount("Lightning Bolt", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 0); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java index ec920551d07..7f8c446225a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java @@ -18,14 +18,15 @@ public class TheWretchedTest extends CardTestPlayerBase { @Test public void testGainControl_One_NoRegenThusNothingIsRemovedFromCombat() { + // At end of combat, gain control of all creatures blocking The Wretched for as long as you control The Wretched. addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration - attack(2, playerA, "The Wretched"); - block(2, playerB, "Wall of Pine Needles", "The Wretched"); - block(2, playerB, "Living Wall", "The Wretched"); - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + attack(3, playerA, "The Wretched"); + block(3, playerB, "Wall of Pine Needles", "The Wretched"); + block(3, playerB, "Living Wall", "The Wretched"); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); assertPermanentCount(playerA, "The Wretched", 1); @@ -37,21 +38,28 @@ public class TheWretchedTest extends CardTestPlayerBase { public void testGainControl_One_RegenWhichRemovesBlockerFromCombat() { addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); + addCard(Zone.BATTLEFIELD, playerA, "Bad Moon"); // +1/+1 for black creatures + + addCard(Zone.BATTLEFIELD, playerB, "Forest"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration - attack(2, playerA, "The Wretched"); - block(2, playerB, "Wall of Pine Needles", "The Wretched"); - block(2, playerB, "Living Wall", "The Wretched"); + // The Wretched + // Creature — Demon - Demon 2/5 + attack(3, playerA, "The Wretched"); + block(3, playerB, "Wall of Pine Needles", "The Wretched"); + block(3, playerB, "Living Wall", "The Wretched"); - activateAbility(2, PhaseStep.DECLARE_BLOCKERS, playerB, "{G}: Regenerate {this}."); + activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerB, "{G}: Regenerate {this}."); // Wall of Pine Needles - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); assertPermanentCount(playerA, "The Wretched", 1); assertPermanentCount(playerA, "Living Wall", 1); + assertPermanentCount(playerB, "Wall of Pine Needles", 1); + } @@ -59,16 +67,19 @@ public class TheWretchedTest extends CardTestPlayerBase { public void testLoseControlOfTheWretched() { addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); + addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration + addCard(Zone.BATTLEFIELD, playerB, "Island", 4); + addCard(Zone.HAND, playerB, "Control Magic"); - attack(2, playerA, "The Wretched"); - block(2, playerB, "Wall of Pine Needles", "The Wretched"); - block(2, playerB, "Living Wall", "The Wretched"); + attack(3, playerA, "The Wretched"); + block(3, playerB, "Wall of Pine Needles", "The Wretched"); + block(3, playerB, "Living Wall", "The Wretched"); - castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Control Magic", "The Wretched"); + castSpell(4, PhaseStep.POSTCOMBAT_MAIN, playerB, "Control Magic", "The Wretched"); - setStopAt(2, PhaseStep.END_TURN); + setStopAt(4, PhaseStep.END_TURN); execute(); assertPermanentCount(playerB, "The Wretched", 1); @@ -80,20 +91,24 @@ public class TheWretchedTest extends CardTestPlayerBase { public void testRegenTheWretchedThusRemovingFromCombat() { addCard(Zone.BATTLEFIELD, playerA, "The Wretched"); + addCard(Zone.HAND, playerA, "Regenerate"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration - addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration + addCard(Zone.BATTLEFIELD, playerB, "Wall of Spears"); // 3/2 - attack(2, playerA, "The Wretched"); - block(2, playerB, "Wall of Pine Needles", "The Wretched"); - block(2, playerB, "Living Wall", "The Wretched"); - castSpell(2, PhaseStep.DECLARE_BLOCKERS, playerA, "Regenerate", "The Wretched"); + attack(3, playerA, "The Wretched"); + block(3, playerB, "Wall of Pine Needles", "The Wretched"); + block(3, playerB, "Wall of Spears", "The Wretched"); - setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + castSpell(3, PhaseStep.DECLARE_BLOCKERS, playerA, "Regenerate", "The Wretched"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); assertPermanentCount(playerA, "The Wretched", 1); assertPermanentCount(playerB, "Wall of Pine Needles", 1); - assertPermanentCount(playerB, "Living Wall", 1); + assertPermanentCount(playerB, "Wall of Spears", 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ElvishArchdruidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ElvishArchdruidTest.java index daddf046635..0372d6acc57 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ElvishArchdruidTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ElvishArchdruidTest.java @@ -49,11 +49,12 @@ public class ElvishArchdruidTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); addCard(Zone.BATTLEFIELD, playerA, "Elvish Archdruid", 1); addCard(Zone.BATTLEFIELD, playerA, "Nettle Sentinel", 1); + // Pyroclasm deals 2 damage to each creature. addCard(Zone.HAND, playerA, "Pyroclasm"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Pyroclasm"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyroclasm"); - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); // has to be the same phase as the cast spell to see if Nettle Sentinel dies in this phase execute(); assertGraveyardCount(playerA, "Pyroclasm", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/BattlefieldTriggeredAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/BattlefieldTriggeredAbilitiesTest.java index 6a6359941b2..d9340ed3756 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/BattlefieldTriggeredAbilitiesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/BattlefieldTriggeredAbilitiesTest.java @@ -15,10 +15,16 @@ public class BattlefieldTriggeredAbilitiesTest extends CardTestPlayerBase { @Test public void testBeguilerofWillsAndPrimevalTitan() { + + // Whenever Primeval Titan enters the battlefield or attacks, you may search your library for up to two land cards, + // put them onto the battlefield tapped, then shuffle your library. addCard(Zone.BATTLEFIELD, playerA, "Primeval Titan"); addCard(Zone.LIBRARY, playerA, "Mountain", 10); + // {T}: Gain control of target creature with power less than or equal to the number of creatures you control. addCard(Zone.BATTLEFIELD, playerB, "Beguiler of Wills"); + + // Whenever Arrogant Bloodlord blocks or becomes blocked by a creature with power 1 or less, destroy Arrogant Bloodlord at end of combat. addCard(Zone.BATTLEFIELD, playerB, "Arrogant Bloodlord", 5); addCard(Zone.LIBRARY, playerB, "Mountain", 10); @@ -34,7 +40,7 @@ public class BattlefieldTriggeredAbilitiesTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Beguiler of Wills", 1); assertPermanentCount(playerB, "Arrogant Bloodlord", 5); assertPermanentCount(playerB, "Primeval Titan", 1); - + // lands weren't added to playerA assertPermanentCount(playerA, "Mountain", 0); // but to playerB instead diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java index 0e5db1f8e02..c0022e21951 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java @@ -58,12 +58,15 @@ public class CostModificationTest extends CardTestPlayerBase { @Test public void testCostReductionWithManaSourceRestrictionWorking() { + // Artifact spells you cast cost {1} less to cast addCard(Zone.BATTLEFIELD, playerA, "Etherium Sculptor"); addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves"); + // Myr Superion {2} + // Spend only mana produced by creatures to cast Myr Superion. addCard(Zone.HAND, playerA, "Myr Superion"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {G} to your mana pool."); + activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {G} to your mana pool."); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Myr Superion"); setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DryadMilitantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DryadMilitantTest.java index 451d18381c7..7e696f8d92d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DryadMilitantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DryadMilitantTest.java @@ -79,7 +79,7 @@ public class DryadMilitantTest extends CardTestPlayerBase { } /** - * Tests if Dryad Militant dies by damage spell, the + * Tests if Dryad Militant dies by destroy spell, the * spell don't get exiled */ @Test @@ -97,7 +97,7 @@ public class DryadMilitantTest extends CardTestPlayerBase { assertLife(playerB, 20); assertHandCount(playerA, "Terminate", 0); - assertGraveyardCount(playerB, "Terminate", 1); + assertGraveyardCount(playerA, "Terminate", 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java index a3e7c4bbd73..3c91554cf73 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/FracturingGustTest.java @@ -44,8 +44,12 @@ public class FracturingGustTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); addCard(Zone.HAND, playerA, "Fracturing Gust", 1); + // Players can't gain life. + // At the beginning of your upkeep, Witch Hunt deals 4 damage to you. + // At the beginning of your end step, target opponent chosen at random gains control of Witch Hunt. addCard(Zone.BATTLEFIELD, playerB, "Witch Hunt", 1); + // Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way. castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fracturing Gust"); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java index 9e0cf706314..e9afcbd6d7b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ParallaxWaveTest.java @@ -60,8 +60,12 @@ public class ParallaxWaveTest extends CardTestPlayerBase { */ @Test public void testFirstExileHandlingOfItself() { + // Each other non-Aura enchantment is a creature with power and toughness each equal to its converted mana cost. It's still an enchantment. addCard(Zone.BATTLEFIELD, playerA, "Opalescence"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + // Fading 5 (This enchantment enters the battlefield with five fade counters on it. At the beginning of your upkeep, remove a fade counter from it. If you can't, sacrifice it.) + // Remove a fade counter from Parallax Wave: Exile target creature. + // When Parallax Wave leaves the battlefield, each player returns to the battlefield all cards he or she owns exiled with Parallax Wave. addCard(Zone.HAND, playerA, "Parallax Wave"); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java index ddb20debb0e..9199ba892e2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java @@ -114,9 +114,9 @@ public class JournexToNowhereTest extends CardTestPlayerBase { /* Journey is played and targets the creature as it enters the battlefield. - The Journey will be returned to hand before the ability reolves. + The Journey will be returned to hand before the ability resolves. The Journey will be played again targeting another creature. - The journey will be disencahnted later, so only the second creatufre has to return to battlefield. + The Journey will be disenchanted later, so only the second creature has to return to battlefield. */ @Test diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SoulWardenTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SoulWardenTest.java index 8efda10359b..df49fb232f7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SoulWardenTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SoulWardenTest.java @@ -21,10 +21,11 @@ public class SoulWardenTest extends CardTestPlayerBase { public void testDisabledEffectOnChangeZone() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.HAND, playerA, "Lightning Bolt"); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.HAND, playerA, "Elite Vanguard", 2); addCard(Zone.BATTLEFIELD, playerA, "Soul Warden", 1); + // Creature 2/1 castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Vanguard"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Soul Warden"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Elite Vanguard"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java index 3c7a4a40ea0..e8056826a70 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java @@ -88,7 +88,7 @@ public class BrainMaggotTest extends CardTestPlayerBase { public void testCardFromHandWillBeExiledAndReturnMesmericFiend() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); addCard(Zone.HAND, playerA, "Mesmeric Fiend", 2); - + // Sacrifice a creature: Put a +1/+1 counter on Bloodflow Connoisseur. addCard(Zone.HAND, playerB, "Bloodflow Connoisseur", 1); addCard(Zone.HAND, playerB, "Lightning Bolt", 1); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/HarvesterOfSoulsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/HarvesterOfSoulsTest.java index 750a0b7c824..7f1e00812f1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/HarvesterOfSoulsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/HarvesterOfSoulsTest.java @@ -21,14 +21,19 @@ public class HarvesterOfSoulsTest extends CardTestPlayerBase { public void testDisabledEffectOnChangeZone() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - addCard(Zone.HAND, playerA, "Day of Judgment", 1); + addCard(Zone.HAND, playerA, "Day of Judgment", 1); addCard(Zone.HAND, playerA, "Thatcher Revolt", 1); + // Whenever another nontoken creature dies, you may draw a card. addCard(Zone.BATTLEFIELD, playerA, "Harvester of Souls", 1); + addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm", 1); + // Whenever Arrogant Bloodlord blocks or becomes blocked by a creature with power 1 or less, destroy Arrogant Bloodlord at end of combat. addCard(Zone.BATTLEFIELD, playerB, "Arrogant Bloodlord", 1); - + + // Put three 1/1 red Human creature tokens with haste onto the battlefield. Sacrifice those tokens at the beginning of the next end step. castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thatcher Revolt"); + // Destroy all creatures. castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Day of Judgment"); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java b/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java index bef9fb7f94a..26249229be3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/lki/LastKnownInformationTest.java @@ -33,10 +33,12 @@ public class LastKnownInformationTest extends CardTestPlayerBase { // Creature - Elf Scout // 2/2 // Persist - addCard(Zone.BATTLEFIELD, playerA, "Safehold Elite"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - addCard(Zone.HAND, playerA, "Murder Investigation",2); + // {1}{W} + // Enchant creature you control + // When enchanted creature dies, put X 1/1 white Soldier creature tokens onto the battlefield, where X is its power. + addCard(Zone.HAND, playerA, "Murder Investigation",1); addCard(Zone.HAND, playerB, "Lightning Bolt",2); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); @@ -45,16 +47,18 @@ public class LastKnownInformationTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Safehold Elite"); // choose triggered ability order - playerA.addChoice("When enchanted creature dies, put X 1/1 red and white Soldier creature token with haste onto the battlefield, where X is its power."); + playerA.addChoice("When enchanted creature dies"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Safehold Elite", "When enchanted creature dies, put X 1/1 red and white Soldier creature token with haste onto the battlefield, where X is its power"); setStopAt(1, PhaseStep.END_TURN); execute(); - assertActionCount(playerB, 0); + assertGraveyardCount(playerB, "Lightning Bolt", 2); assertPermanentCount(playerA, "Safehold Elite", 0); // because enchanted Safehold Elite's P/T was 2/2, Murder Investigation has to put 2 Soldier onto the battlefield assertPermanentCount(playerA, "Soldier", 2); + + assertActionCount(playerB, 0); } diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 7d447091f68..1fb9cff865a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -436,7 +436,7 @@ public class TestPlayer extends ComputerPlayer { public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { if (!choices.isEmpty()) { for(TriggeredAbility ability :abilities) { - if (choices.get(0).equals(ability.toString())) { + if (ability.toString().startsWith(choices.get(0))) { choices.remove(0); return ability; } diff --git a/Mage/src/mage/MageObjectReference.java b/Mage/src/mage/MageObjectReference.java index 8d5237807b3..7edac3720b3 100644 --- a/Mage/src/mage/MageObjectReference.java +++ b/Mage/src/mage/MageObjectReference.java @@ -32,7 +32,6 @@ import java.util.UUID; import mage.cards.Card; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.stack.Spell; /** * A object reference that takes zone changes into account. @@ -65,10 +64,11 @@ public class MageObjectReference implements Comparable { public MageObjectReference(UUID sourceId, Game game) { this.sourceId = sourceId; MageObject mageObject = game.getObject(sourceId); - if (mageObject != null) + if (mageObject != null) { this.zoneChangeCounter = mageObject.getZoneChangeCounter(game); - else - this.zoneChangeCounter = 0; + } else { + throw new IllegalArgumentException("The provided sourceId is not connected to an object in the game"); + } } public UUID getSourceId() { @@ -109,7 +109,10 @@ public class MageObjectReference implements Comparable { } public boolean refersTo(MageObject mageObject, Game game) { - return mageObject.getId().equals(sourceId) && this.zoneChangeCounter == mageObject.getZoneChangeCounter(game); + if (mageObject != null) { + return mageObject.getId().equals(sourceId) && this.zoneChangeCounter == mageObject.getZoneChangeCounter(game); + } + return false; } public Permanent getPermanent(Game game) { diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index 44a9fc1336b..8216042b1e8 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -499,8 +499,9 @@ public interface Ability extends Controllable, Serializable { * an ability was activated. * * @param mageObject + * @param game */ - void setSourceObject(MageObject mageObject); + void setSourceObject(MageObject mageObject, Game game); /** * Returns the object that actually existed while a ability triggerd or @@ -511,4 +512,17 @@ public interface Ability extends Controllable, Serializable { * @return */ MageObject getSourceObject(Game game); + + int getSourceObjectZoneChangeCounter(); + + /** + * Returns the object that actually existed while a ability triggerd or + * an ability was activated only if it has not changed zone meanwhile. + * If not set yet, the current object will be retrieved from the game. + * + * @param game + * @return + */ + + MageObject getSourceObjectIfItStillExists(Game game); } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 02c4c47192f..947974e88d2 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.MageObject; +import mage.MageObjectReference; import mage.Mana; import mage.abilities.costs.AdjustingSourceCosts; import mage.abilities.costs.AlternativeCost; @@ -109,6 +110,7 @@ public abstract class AbilityImpl implements Ability { protected boolean activated = false; protected boolean worksFaceDown = false; protected MageObject sourceObject; + protected int sourceObjectZoneChangeCounter; protected List watchers = null; protected List subAbilities = null; @@ -160,6 +162,7 @@ public abstract class AbilityImpl implements Ability { this.worksFaceDown = ability.worksFaceDown; this.abilityWord = ability.abilityWord; this.sourceObject = ability.sourceObject; + this.sourceObjectZoneChangeCounter = ability.sourceObjectZoneChangeCounter; } @Override @@ -213,6 +216,13 @@ public abstract class AbilityImpl implements Ability { else { game.addEffect((ContinuousEffect) effect, this); } + /** + * game.applyEffects() has to be done at least for every effect that moves cards/permanent between zones, + * so Static effects work as intened if dependant from the moved objects zone it is in + * Otherwise for example were static abilities with replacement effects deactivated to late + * Example: {@link org.mage.test.cards.replacement.DryadMilitantTest#testDiesByDestroy testDiesByDestroy} + */ +// game.applyEffects(); // some effects must be applied before next effect is resolved, because effect is dependend. if (effect.applyEffectsAfter()) { game.applyEffects(); @@ -236,6 +246,8 @@ public abstract class AbilityImpl implements Ability { return false; } + getSourceObject(game); + /* 20130201 - 601.2b * If the player wishes to splice any cards onto the spell (see rule 702.45), he * or she reveals those cards in his or her hand. @@ -243,13 +255,13 @@ public abstract class AbilityImpl implements Ability { if (this.abilityType.equals(AbilityType.SPELL)) { game.getContinuousEffects().applySpliceEffects(this, game); } - - // TODO: Because all (non targeted) choices have to be done during resolution - // this has to be removed, if all using effects are changed - sourceObject = this.getSourceObject(game); + + if (sourceObject != null) { sourceObject.adjustChoices(this, game); } + // TODO: Because all (non targeted) choices have to be done during resolution + // this has to be removed, if all using effects are changed for (UUID modeId :this.getModes().getSelectedModes()) { this.getModes().setMode(this.getModes().get(modeId)); if (getChoices().size() > 0 && getChoices().choose(game, this) == false) { @@ -660,16 +672,18 @@ public abstract class AbilityImpl implements Ability { @Override public List getWatchers() { - if (watchers != null) + if (watchers != null) { return watchers; - else + } else { return emptyWatchers; + } } @Override public void addWatcher(Watcher watcher) { - if (watchers == null) + if (watchers == null) { watchers = new ArrayList<>(); + } watcher.setSourceId(this.sourceId); watcher.setControllerId(this.controllerId); watchers.add(watcher); @@ -677,16 +691,18 @@ public abstract class AbilityImpl implements Ability { @Override public List getSubAbilities() { - if (subAbilities != null) + if (subAbilities != null) { return subAbilities; - else + } else { return emptyAbilities; + } } @Override public void addSubAbility(Ability ability) { - if (subAbilities == null) + if (subAbilities == null) { subAbilities = new ArrayList<>(); + } ability.setSourceId(this.sourceId); ability.setControllerId(this.controllerId); subAbilities.add(ability); @@ -842,8 +858,15 @@ public abstract class AbilityImpl implements Ability { return false; } + /** + * + * @param game + * @param source + * @param checkShortLivingLKI if the object was in the needed zone as the effect that's currently applied started, the check returns true + * @return + */ @Override - public boolean isInUseableZone(Game game, MageObject source, boolean checkLKI) { + public boolean isInUseableZone(Game game, MageObject source, boolean checkShortLivingLKI) { if (zone.equals(Zone.COMMAND)) { if (this.getSourceId() == null) { // commander effects return true; @@ -855,10 +878,13 @@ public abstract class AbilityImpl implements Ability { } } - // try LKI first - if (checkLKI) { - MageObject lkiTest = game.getShortLivingLKI(getSourceId(), zone); - if (lkiTest != null) { + // try LKI first (was the object with the id in the needed zone before) + if (checkShortLivingLKI) { + if (game.getShortLivingLKI(getSourceId(), zone)) { + return true; + } + } else { + if (game.getLastKnownInformation(getSourceId(), zone) != null) { return true; } } @@ -1097,16 +1123,38 @@ public abstract class AbilityImpl implements Ability { @Override public MageObject getSourceObject(Game game) { - if (sourceObject != null) { - return sourceObject; - } else { - return game.getObject(sourceId); + if (sourceObject == null) { + setSourceObject(null, game); } + return sourceObject; } @Override - public void setSourceObject(MageObject sourceObject) { - this.sourceObject = sourceObject; + public MageObject getSourceObjectIfItStillExists(Game game) { + MageObject currentObject = game.getObject(getSourceId()); + if (currentObject != null) { + MageObjectReference mor = new MageObjectReference(currentObject, game); + if (mor.getZoneChangeCounter() == getSourceObjectZoneChangeCounter()) { + // source object has meanwhile not changed zone + return sourceObject; + } + } + return null; + } + + @Override + public int getSourceObjectZoneChangeCounter() { + return sourceObjectZoneChangeCounter; + } + + @Override + public void setSourceObject(MageObject sourceObject, Game game) { + if (sourceObject == null) { + this.sourceObject = game.getObject(sourceId); + } else { + this.sourceObject = sourceObject; + } + this.sourceObjectZoneChangeCounter = game.getState().getZoneChangeCounter(sourceId); } diff --git a/Mage/src/mage/abilities/TriggeredAbilities.java b/Mage/src/mage/abilities/TriggeredAbilities.java index 705efd3737a..d2ed7faf5ab 100644 --- a/Mage/src/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/mage/abilities/TriggeredAbilities.java @@ -1,32 +1,31 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.abilities; import java.util.ArrayList; @@ -38,6 +37,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import mage.MageObject; +import mage.cards.Card; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -45,17 +45,18 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; /** -* -* @author BetaSteward_at_googlemail.com -*/ + * + * @author BetaSteward_at_googlemail.com + */ public class TriggeredAbilities extends ConcurrentHashMap { private final Map> sources = new HashMap<>(); - public TriggeredAbilities() {} + public TriggeredAbilities() { + } public TriggeredAbilities(final TriggeredAbilities abilities) { - for (Map.Entry entry: abilities.entrySet()) { + for (Map.Entry entry : abilities.entrySet()) { this.put(entry.getKey(), entry.getValue().copy()); } for (Map.Entry> entry : abilities.sources.entrySet()) { @@ -70,29 +71,34 @@ public class TriggeredAbilities extends ConcurrentHashMap uuidList = new LinkedList<>(); uuidList.add(sourceId); @@ -145,18 +150,18 @@ public class TriggeredAbilities extends ConcurrentHashMap keysToRemove = new ArrayList<>(); - for (String key: this.keySet()) { - if(key.endsWith(sourceId.toString())) { + for (String key : this.keySet()) { + if (key.endsWith(sourceId.toString())) { keysToRemove.add(key); } } - for(String key: keysToRemove) { + for (String key : keysToRemove) { remove(key); } } public void removeAllGainedAbilities() { - for(String key: sources.keySet()) { + for (String key : sources.keySet()) { this.remove(key); } sources.clear(); @@ -166,4 +171,4 @@ public class TriggeredAbilities extends ConcurrentHashMap abilities = layeredEffects.getAbility(effect.getId()); for (Ability ability: abilities) { // If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect) - if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) { + if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, true)) { layerEffects.add(effect); break; } @@ -332,7 +333,7 @@ public class ContinuousEffects implements Serializable { if(auraReplacementEffect.checksEventType(event, game) && auraReplacementEffect.applies(event, null, game)){ replaceEffects.put(auraReplacementEffect, null); } - boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); + // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects for (ReplacementEffect effect: replacementEffects) { if (!effect.checksEventType(event, game)) { @@ -346,10 +347,11 @@ public class ContinuousEffects implements Serializable { HashSet abilities = replacementEffects.getAbility(effect.getId()); HashSet applicableAbilities = new HashSet<>(); for (Ability ability : abilities) { - if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, checkLKI)) { + // for replacment effects of static abilities do not use LKI to check if to apply + if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, true)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) { - if (checkAbilityStillExists(ability, effect, event, game)) { + if (checkAbilityStillExists(ability, effect, event, game)) { // TODO: This is really needed??? if (effect.applies(event, ability, game)) { applicableAbilities.add(ability); } @@ -374,7 +376,7 @@ public class ContinuousEffects implements Serializable { HashSet abilities = preventionEffects.getAbility(effect.getId()); HashSet applicableAbilities = new HashSet<>(); for (Ability ability : abilities) { - if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) { + if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, true)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { if (effect.applies(event, ability, game)) { applicableAbilities.add(ability); @@ -658,7 +660,7 @@ public class ContinuousEffects implements Serializable { continue; } for (Ability sourceAbility : continuousRuleModifyingEffects.getAbility(effect.getId())) { - if (!(sourceAbility instanceof StaticAbility) || sourceAbility.isInUseableZone(game, null, false)) { + if (!(sourceAbility instanceof StaticAbility) || sourceAbility.isInUseableZone(game, null, true)) { if (checkAbilityStillExists(sourceAbility, effect, event, game)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { effect.setValue("targetAbility", targetAbility); diff --git a/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java b/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java index 2aaba09f902..1243f4a3f3b 100644 --- a/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java +++ b/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java @@ -74,6 +74,11 @@ public abstract class ReplacementEffectImpl extends ContinuousEffectImpl impleme return selfScope; } + @Override + public boolean apply(Game game, Ability source) { + throw new UnsupportedOperationException("Not used for replacemnt effect."); + } + @Override public boolean checksEventType(GameEvent event, Game game) { return true; diff --git a/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java b/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java index 08627a8d600..de8085730a6 100644 --- a/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java +++ b/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java @@ -70,7 +70,7 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = ability.copy(); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); + delayedAbility.setSourceObject(source.getSourceObject(game), game); if (this.copyTargets) { if (source.getTargets().isEmpty()) { for(Effect effect : delayedAbility.getEffects()) { diff --git a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java index 1f047fdfca8..e77d8337908 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java @@ -28,14 +28,15 @@ package mage.abilities.effects.common; +import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -43,21 +44,26 @@ import mage.players.Player; */ public class ExileSourceEffect extends OneShotEffect { - private Zone onlyfromZone; + private boolean toUniqueExileZone; public ExileSourceEffect() { - this(Zone.ALL); + this(false); } - public ExileSourceEffect(Zone onlyFromZone) { + /** + * + * @param toUniqueExileZone moves the card to a source object dependant unique exile zone, so another + * effect of the same source object (e.g. Deadeye Navigator) can identify the card + */ + public ExileSourceEffect(boolean toUniqueExileZone) { super(Outcome.Exile); staticText = "Exile {this}"; - this.onlyfromZone = onlyFromZone; + this.toUniqueExileZone = toUniqueExileZone; } public ExileSourceEffect(final ExileSourceEffect effect) { super(effect); - this.onlyfromZone = effect.onlyfromZone; + this.toUniqueExileZone = effect.toUniqueExileZone; } @Override @@ -66,22 +72,22 @@ public class ExileSourceEffect extends OneShotEffect { } @Override - public boolean apply(Game game, Ability source) { - Zone zone = game.getState().getZone(source.getSourceId()); - if (!zone.match(onlyfromZone)) { - return false; - } - Permanent permanent = game.getPermanent(source.getSourceId()); + public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (permanent != null) { - return controller.moveCardToExileWithInfo(permanent, null, null, source.getSourceId(), game, zone); - } else { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - return controller.moveCardToExileWithInfo(card, null, null, source.getSourceId(), game, zone); + if (controller != null) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (sourceObject instanceof Card) { + UUID exileZoneId = null; + String exileZoneName = ""; + if (toUniqueExileZone) { + exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + exileZoneName = sourceObject.getLogName(); + } + Card sourceCard = (Card) sourceObject; + return controller.moveCardToExileWithInfo(sourceCard, exileZoneId, exileZoneName, source.getSourceId(), game, game.getState().getZone(sourceCard.getId())); } + return true; } return false; } - } diff --git a/Mage/src/mage/abilities/effects/common/ExileTargetForSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileTargetForSourceEffect.java index f4da07a7af7..de28d429d22 100644 --- a/Mage/src/mage/abilities/effects/common/ExileTargetForSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileTargetForSourceEffect.java @@ -69,7 +69,7 @@ public class ExileTargetForSourceEffect extends OneShotEffect { MageObject sourceObject = source.getSourceObject(game); if (controller != null && sourceObject != null) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (permanent != null) { return controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getLogName(), source.getSourceId(), game, Zone.BATTLEFIELD); } else { diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java index 9b452d94fef..657569add16 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java @@ -50,24 +50,41 @@ import mage.util.CardUtil; */ public class ReturnFromExileForSourceEffect extends OneShotEffect { - private Zone zone; + private Zone returnToZone; private boolean tapped; + private boolean previousZone; + /** + * + * @param zone Zone the card should return to + */ public ReturnFromExileForSourceEffect(Zone zone) { this(zone, false); } public ReturnFromExileForSourceEffect(Zone zone, boolean tapped) { + this(zone, tapped, true); + } + + /** + * + * @param zone + * @param tapped + * @param previousZone if this is used from a dies leave battlefield or destroyed trigger, the exile zone is based on previous zone of the object + */ + public ReturnFromExileForSourceEffect(Zone zone, boolean tapped, boolean previousZone) { super(Outcome.PutCardInPlay); - this.zone = zone; + this.returnToZone = zone; this.tapped = tapped; + this.previousZone = previousZone; setText(); } public ReturnFromExileForSourceEffect(final ReturnFromExileForSourceEffect effect) { super(effect); - this.zone = effect.zone; + this.returnToZone = effect.returnToZone; this.tapped = effect.tapped; + this.previousZone = effect.previousZone; } @Override @@ -78,9 +95,9 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + if (controller != null) { + int zoneChangeCounter = source.getSourceObjectZoneChangeCounter() - (previousZone ? 1:0); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter); ExileZone exile = game.getExile().getExileZone(exileId); if (exile != null) { // null is valid if source left battlefield before enters the battlefield effect resolved LinkedList cards = new LinkedList<>(exile); @@ -89,8 +106,8 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { if (card == null) { return false; } - game.informPlayers(controller.getName() + " moves " + card.getLogName() + " from exile to " + zone.toString().toLowerCase()); - card.moveToZone(zone, source.getSourceId(), game, tapped); + game.informPlayers(controller.getName() + " moves " + card.getLogName() + " from exile to " + returnToZone.toString().toLowerCase()); + card.moveToZone(returnToZone, source.getSourceId(), game, tapped); } exile.clear(); } @@ -102,7 +119,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { private void setText() { StringBuilder sb = new StringBuilder(); sb.append("return the exiled cards "); - switch(zone) { + switch(returnToZone) { case BATTLEFIELD: sb.append("to the battlefield under its owner's control"); if (tapped) { diff --git a/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java index 0a31c031ecc..b786237bc6a 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java @@ -28,13 +28,16 @@ package mage.abilities.effects.common; -import mage.MageObjectReference; +import java.util.UUID; +import mage.MageObject; import mage.constants.Outcome; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.Zone; +import mage.game.ExileZone; import mage.game.Game; +import mage.util.CardUtil; /** * @@ -42,16 +45,13 @@ import mage.game.Game; */ public class ReturnToBattlefieldUnderYourControlSourceEffect extends OneShotEffect { - private final Zone onlyFromZone; - public ReturnToBattlefieldUnderYourControlSourceEffect(Zone fromZone) { + public ReturnToBattlefieldUnderYourControlSourceEffect() { super(Outcome.Benefit); - this.onlyFromZone = fromZone; staticText = "return that card to the battlefield under your control"; } public ReturnToBattlefieldUnderYourControlSourceEffect(final ReturnToBattlefieldUnderYourControlSourceEffect effect) { super(effect); - this.onlyFromZone = effect.onlyFromZone; } @Override @@ -61,11 +61,11 @@ public class ReturnToBattlefieldUnderYourControlSourceEffect extends OneShotEffe @Override public boolean apply(Game game, Ability source) { - MageObjectReference mor = new MageObjectReference(source.getSourceObject(game), game); - Card card = game.getCard(source.getSourceId()); - if (card != null && game.getState().getZone(source.getSourceId()) == onlyFromZone && mor.getZoneChangeCounter() == card.getZoneChangeCounter(game) + 1) { - Zone currentZone = game.getState().getZone(card.getId()); - if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), source.getControllerId())) { + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ExileZone exileZone = game.getExile().getExileZone(exileZoneId); + if (exileZone != null && exileZone.contains(source.getSourceId())) { + Card card = game.getCard(source.getSourceId()); + if (card != null && card.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), source.getControllerId())) { return true; } } diff --git a/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java index 560e8c1c776..600df26f809 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java @@ -53,7 +53,7 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe /** * - * @param fromExileZone - the card will only be retunred if it's still in the sour obect specific exile zone + * @param fromExileZone - the card will only be returned if it's still in the sour obect specific exile zone */ public ReturnToBattlefieldUnderYourControlTargetEffect(boolean fromExileZone) { super(Outcome.Benefit); @@ -74,11 +74,10 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { + if (controller != null) { Card card = null; if (fromExileZone) { - UUID exilZoneId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exilZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exilZoneId != null) { card = game.getExile().getExileZone(exilZoneId).get(getTargetPointer().getFirst(game, source), game); } diff --git a/Mage/src/mage/abilities/keyword/ChampionAbility.java b/Mage/src/mage/abilities/keyword/ChampionAbility.java index 47853817d5f..de9b8ce83f7 100644 --- a/Mage/src/mage/abilities/keyword/ChampionAbility.java +++ b/Mage/src/mage/abilities/keyword/ChampionAbility.java @@ -155,7 +155,7 @@ class ChampionExileCost extends CostImpl { MageObject sourceObject = ability.getSourceObject(game); if (controller != null && sourceObject != null) { if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) { - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + UUID exileId = CardUtil.getExileZoneId(game, ability.getSourceId(), ability.getSourceObjectZoneChangeCounter()); for (UUID targetId: targets.get(0).getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent == null) { diff --git a/Mage/src/mage/abilities/keyword/EchoAbility.java b/Mage/src/mage/abilities/keyword/EchoAbility.java index d00226a0e45..6b1d318678c 100644 --- a/Mage/src/mage/abilities/keyword/EchoAbility.java +++ b/Mage/src/mage/abilities/keyword/EchoAbility.java @@ -29,7 +29,6 @@ package mage.abilities.keyword; import java.util.UUID; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; @@ -53,7 +52,7 @@ public class EchoAbility extends TriggeredAbilityImpl { protected UUID lastController; protected boolean echoPaid; - protected Costs echoCosts = new CostsImpl(); + protected Costs echoCosts = new CostsImpl<>(); private boolean manaEcho = true; public EchoAbility(String manaString) { @@ -94,6 +93,7 @@ public class EchoAbility extends TriggeredAbilityImpl { // reset the echo paid state back, if creature enteres the battlefield if (event.getType().equals(GameEvent.EventType.ENTERS_THE_BATTLEFIELD) && event.getTargetId().equals(this.getSourceId())) { + this.echoPaid = false; } if (event.getType().equals(GameEvent.EventType.UPKEEP_STEP_PRE)) { @@ -144,15 +144,17 @@ class EchoEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObjectReference mor = new MageObjectReference(source.getSourceId(), game); - if (controller != null && mor.refersTo(source.getSourceObject(game), game)) { + if (controller != null && source.getSourceObjectIfItStillExists(game) != null) { if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", game)) { cost.clearPaid(); if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { return true; } } - mor.getPermanent(game).sacrifice(source.getSourceId(), game); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } return true; } return false; diff --git a/Mage/src/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/mage/abilities/keyword/FlashbackAbility.java index 66eb5866f57..ed391773859 100644 --- a/Mage/src/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/mage/abilities/keyword/FlashbackAbility.java @@ -35,10 +35,13 @@ import mage.abilities.costs.Cost; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.cards.Card; import mage.cards.SplitCard; +import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.TimingRule; @@ -74,7 +77,6 @@ public class FlashbackAbility extends SpellAbility { this.timing = timingRule; this.usesStack = false; this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; - this.addEffect(new CreateDelayedTriggeredAbilityEffect(new FlashbackTriggeredAbility())); } public FlashbackAbility(final FlashbackAbility ability) { @@ -211,48 +213,59 @@ class FlashbackEffect extends OneShotEffect { } game.informPlayers(new StringBuilder(controller.getName()).append(" flashbacks ").append(card.getName()).toString()); spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells - return controller.cast(spellAbility, game, true); + if (controller.cast(spellAbility, game, true)) { + game.addEffect(new FlashbackReplacementEffect(), source); + return true; + } + return false; } } return false; } } -class FlashbackTriggeredAbility extends DelayedTriggeredAbility { +class FlashbackReplacementEffect extends ReplacementEffectImpl { - public FlashbackTriggeredAbility() { - super(new ExileSourceEffect()); - usesStack = false; + public FlashbackReplacementEffect() { + super(Duration.OneUse, Outcome.Exile); + staticText = "(If the flashback cost was paid, exile this card instead of putting it anywhere else any time it would leave the stack)"; } - public FlashbackTriggeredAbility(final FlashbackTriggeredAbility ability) { - super(ability); + public FlashbackReplacementEffect(final FlashbackReplacementEffect effect) { + super(effect); } @Override - public FlashbackTriggeredAbility copy() { - return new FlashbackTriggeredAbility(this); + public FlashbackReplacementEffect copy() { + return new FlashbackReplacementEffect(this); } @Override - public boolean checkEventType(GameEvent event, Game game) { + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card card = game.getCard(event.getTargetId()); + if (card != null) { + return controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, game.getState().getZone(card.getId())); + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ZONE_CHANGE; } @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.STACK) { - return true; - } - } - return false; + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId().equals(source.getSourceId()) + && ((ZoneChangeEvent)event).getFromZone() == Zone.STACK + && ((ZoneChangeEvent)event).getToZone() != Zone.EXILED; } - - @Override - public String getRule() { - return "(If the flashback cost was paid, exile this card instead of putting it anywhere else any time it would leave the stack)"; - } - -} +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/UnearthAbility.java b/Mage/src/mage/abilities/keyword/UnearthAbility.java index f094c79bbe8..19e9b4724b0 100644 --- a/Mage/src/mage/abilities/keyword/UnearthAbility.java +++ b/Mage/src/mage/abilities/keyword/UnearthAbility.java @@ -124,7 +124,7 @@ class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility { class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { public UnearthLeavesBattlefieldEffect() { - super(Duration.WhileOnBattlefield, Outcome.Exile); + super(Duration.OneUse, Outcome.Exile); staticText = "When {this} leaves the battlefield, exile it"; } @@ -144,7 +144,7 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) { + if (source.getSourceObjectIfItStillExists(game) != null) { ZoneChangeEvent zEvent = (ZoneChangeEvent)event; if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED) { return true; @@ -153,14 +153,9 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { return false; } - @Override - public boolean apply(Game game, Ability source) { - ExileSourceEffect effect = new ExileSourceEffect(); - return effect.apply(game, source); - } - @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return apply(game, source); + new ExileSourceEffect().apply(game, source); + return true; } } diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 7e3f9f06a0c..6694959ddf8 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -142,7 +142,7 @@ public interface Game extends MageItem, Serializable { void setSimulation(boolean simulation); MageObject getLastKnownInformation(UUID objectId, Zone zone); MageObject getLastKnownInformation(UUID objectId, Zone zone, int zoneChangeCounter); - MageObject getShortLivingLKI(UUID objectId, Zone zone); + boolean getShortLivingLKI(UUID objectId, Zone zone); void rememberLKI(UUID objectId, Zone zone, MageObject object); void resetLKI(); void resetShortLivingLKI(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index c0a358a2670..50a3f2967bd 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -159,9 +159,11 @@ public abstract class GameImpl implements Game, Serializable { protected transient PlayerQueryEventSource playerQueryEventSource = new PlayerQueryEventSource(); protected Map gameCards = new HashMap<>(); + protected Map> lki = new EnumMap<>(Zone.class); protected Map> lkiExtended = new HashMap<>(); - protected Map> shortLivingLKI = new EnumMap<>(Zone.class); + // Used to check if an object was moved by the current effect in resolution (so Wrath like effect can be handled correctly) + protected Map> shortLivingLKI = new EnumMap<>(Zone.class); protected GameState state; private transient Stack savedStates = new Stack<>(); @@ -1131,6 +1133,9 @@ public abstract class GameImpl implements Game, Serializable { // 603.3. Once an ability has triggered, its controller puts it on the stack as an object that’s not a card the next time a player would receive priority checkStateAndTriggered(); applyEffects(); + if (state.getStack().isEmpty()) { + resetLKI(); + } saveState(false); if (isPaused() || gameOver(null)) { return; @@ -1158,7 +1163,7 @@ public abstract class GameImpl implements Game, Serializable { state.getPlayers().resetPassed(); fireUpdatePlayersEvent(); state.getRevealed().reset(); - resetShortLivingLKI(); + resetShortLivingLKI(); break; } else { resetLKI(); @@ -2195,15 +2200,12 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public MageObject getShortLivingLKI(UUID objectId, Zone zone) { - Map shortLivingLkiMap = shortLivingLKI.get(zone); - if (shortLivingLkiMap != null) { - MageObject object = shortLivingLkiMap.get(objectId); - if (object != null) { - return object.copy(); - } + public boolean getShortLivingLKI(UUID objectId, Zone zone) { + Set idSet = shortLivingLKI.get(zone); + if (idSet != null) { + return idSet.contains(objectId); } - return null; + return false; } /** @@ -2226,16 +2228,15 @@ public abstract class GameImpl implements Game, Serializable { newMap.put(objectId, copy); lki.put(zone, newMap); } - - Map shortLivingLkiMap = shortLivingLKI.get(zone); - if (shortLivingLkiMap != null) { - shortLivingLkiMap.put(objectId, copy); - } else { - HashMap newMap = new HashMap<>(); - newMap.put(objectId, copy); - shortLivingLKI.put(zone, newMap); + // remembers if a object was in a zone during the resolution of an effect + // e.g. Wrath destroys all and you the question is is the replacement effect to apply because the source was also moved by the same effect + // because it ahppens all at the same time the replcaement effect has still to be applied + Set idSet = shortLivingLKI.get(zone); + if (idSet == null) { + idSet = new HashSet<>(); + shortLivingLKI.put(zone, idSet); } - + idSet.add(objectId); if (object instanceof Permanent) { Map lkiExtendedMap = lkiExtended.get(objectId); if (lkiExtendedMap != null) { diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index b7ee51ce1dd..5240c08e1b1 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -394,6 +394,7 @@ public class GameState implements Serializable, Copyable { } public void applyEffects(Game game) { + game.resetShortLivingLKI(); for (Player player: players.values()) { player.reset(); } diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index d99f2b0891c..bb93cb5e0ab 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -198,6 +198,7 @@ public class Spell implements StackObject, Card { result |= spellAbility.resolve(game); } } + game.resetShortLivingLKI(); index++; } } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 4fa68ac39a9..8ffbf916c54 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -499,12 +499,22 @@ public class StackAbility implements StackObject, Ability { @Override public MageObject getSourceObject(Game game) { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported."); } @Override - public void setSourceObject(MageObject sourceObject) { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + public MageObject getSourceObjectIfItStillExists(Game game) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public int getSourceObjectZoneChangeCounter() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void setSourceObject(MageObject sourceObject, Game game) { + throw new UnsupportedOperationException("Not supported."); } @Override diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index 588fa5bc96f..ea8c3002efc 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -111,20 +111,23 @@ public class ManaPool implements Serializable { lockManaType(); // pay only one mana if mana payment is set to manually return true; } - MageObject sourceObject = ability.getSourceObject(game); for (ManaPoolItem mana : manaItems) { - if (filter == null || filter.match(sourceObject, game)) { - boolean spendAnyMana = spendAnyMana(ability, game); - if (mana.get(manaType) > 0 || (spendAnyMana && mana.count() > 0)) { - game.fireEvent(new GameEvent(GameEvent.EventType.MANA_PAYED, ability.getId(), mana.getSourceId(), ability.getControllerId(), 0, mana.getFlag())); - if (spendAnyMana) { - mana.removeAny(); - } else { - mana.remove(manaType); - } - lockManaType(); // pay only one mana if mana payment is set to manually - return true; + if (filter != null) { + MageObject sourceObject = game.getObject(mana.getSourceId()); + if (!filter.match(sourceObject, game)) { + continue; } + } + boolean spendAnyMana = spendAnyMana(ability, game); + if (mana.get(manaType) > 0 || (spendAnyMana && mana.count() > 0)) { + game.fireEvent(new GameEvent(GameEvent.EventType.MANA_PAYED, ability.getId(), mana.getSourceId(), ability.getControllerId(), 0, mana.getFlag())); + if (spendAnyMana) { + mana.removeAny(); + } else { + mana.remove(manaType); + } + lockManaType(); // pay only one mana if mana payment is set to manually + return true; } } return false;