From 78612ddc91653bc53c614eda62b20a83bfdf08f1 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 18 Feb 2024 15:05:05 +0400 Subject: [PATCH] multiple changes: * refactor: improved target pointer init code and logic, added docs and runtime checks; * game: fixed miss or wrong init calls in some continuous effects; * game: fixed wrong usage of target pointers (miss copy code, miss npe checks); --- Mage.Sets/src/mage/cards/a/AuriokReplica.java | 2 +- .../src/mage/cards/b/BackdraftHellkite.java | 6 +- .../src/mage/cards/b/BeaconOfDestiny.java | 2 +- Mage.Sets/src/mage/cards/b/Besmirch.java | 10 +- Mage.Sets/src/mage/cards/b/BoneMask.java | 2 +- .../src/mage/cards/c/ChoArrimAlchemist.java | 1 + .../src/mage/cards/c/CommandersPlate.java | 4 +- .../src/mage/cards/c/ConvincingMirage.java | 5 + Mage.Sets/src/mage/cards/c/CorpseDance.java | 6 +- .../src/mage/cards/c/CosmiumConfluence.java | 6 +- Mage.Sets/src/mage/cards/d/DarkSphere.java | 1 + .../src/mage/cards/d/DeflectingPalm.java | 2 +- .../src/mage/cards/d/DesperateGambit.java | 12 +- .../src/mage/cards/d/DisplacedDinosaurs.java | 8 +- .../mage/cards/e/ElrondOfTheWhiteCouncil.java | 8 +- .../src/mage/cards/e/ElsewhereFlask.java | 5 + Mage.Sets/src/mage/cards/e/EyeForAnEye.java | 2 +- Mage.Sets/src/mage/cards/f/Fecundity.java | 6 +- .../src/mage/cards/g/GeneralsRegalia.java | 2 +- Mage.Sets/src/mage/cards/g/GiantOyster.java | 37 ++-- Mage.Sets/src/mage/cards/g/GiftOfFangs.java | 3 +- .../src/mage/cards/g/GoblinPsychopath.java | 9 +- Mage.Sets/src/mage/cards/g/GoreVassal.java | 4 +- Mage.Sets/src/mage/cards/g/GuardDogs.java | 3 +- Mage.Sets/src/mage/cards/h/HarmsWay.java | 2 +- .../src/mage/cards/h/HazduhrTheAbbot.java | 1 + Mage.Sets/src/mage/cards/h/HealingGrace.java | 1 + Mage.Sets/src/mage/cards/h/HisokasGuard.java | 1 + .../src/mage/cards/h/HoardSmelterDragon.java | 1 + Mage.Sets/src/mage/cards/h/HuntingWilds.java | 6 +- .../src/mage/cards/i/IllicitAuction.java | 3 +- .../src/mage/cards/i/ImpulsiveManeuvers.java | 2 +- Mage.Sets/src/mage/cards/j/JadeMonolith.java | 1 + Mage.Sets/src/mage/cards/j/Juxtapose.java | 1 + Mage.Sets/src/mage/cards/k/KheruLichLord.java | 12 +- .../src/mage/cards/k/KillerInstinct.java | 6 +- .../src/mage/cards/l/LegionLoyalist.java | 1 + .../src/mage/cards/m/MournersShield.java | 1 + .../src/mage/cards/n/NacatlWarPride.java | 4 +- .../src/mage/cards/n/NarciFableSinger.java | 2 +- Mage.Sets/src/mage/cards/n/NewBlood.java | 4 +- Mage.Sets/src/mage/cards/n/Nightcreep.java | 4 +- Mage.Sets/src/mage/cards/n/NovaPentacle.java | 2 +- .../src/mage/cards/n/NoyanDarRoilShaper.java | 6 +- .../mage/cards/o/OpalEyeKondasYojimbo.java | 2 +- .../src/mage/cards/o/OraclesAttendants.java | 1 + Mage.Sets/src/mage/cards/p/Penance.java | 2 +- .../src/mage/cards/p/PhantasmalTerrain.java | 5 + .../src/mage/cards/p/PilgrimOfJustice.java | 1 + .../src/mage/cards/p/PilgrimOfVirtue.java | 1 + Mage.Sets/src/mage/cards/p/Portcullis.java | 7 +- .../src/mage/cards/p/PrismaticCircle.java | 2 +- .../src/mage/cards/p/ProgenitorsIcon.java | 1 + .../src/mage/cards/p/ProtectiveSphere.java | 2 +- Mage.Sets/src/mage/cards/p/PushPull.java | 6 +- Mage.Sets/src/mage/cards/q/Quicken.java | 1 + Mage.Sets/src/mage/cards/r/Realmwright.java | 5 + .../src/mage/cards/r/RefractionTrap.java | 2 +- Mage.Sets/src/mage/cards/r/ReverseDamage.java | 1 + .../src/mage/cards/r/RideTheAvalanche.java | 1 + Mage.Sets/src/mage/cards/r/Runesword.java | 1 + .../src/mage/cards/s/SamiteMinistration.java | 1 + .../src/mage/cards/s/SavageSummoning.java | 3 + .../mage/cards/s/ScholarOfTheLostTrove.java | 3 +- Mage.Sets/src/mage/cards/s/ScoutsWarning.java | 1 + Mage.Sets/src/mage/cards/s/ShallowGrave.java | 6 +- Mage.Sets/src/mage/cards/s/ShamanEnKor.java | 1 + .../src/mage/cards/s/ShieldmageAdvocate.java | 1 + Mage.Sets/src/mage/cards/s/ShiningShoal.java | 1 + .../src/mage/cards/s/ShowOfDominance.java | 6 +- Mage.Sets/src/mage/cards/s/StoryCircle.java | 2 +- .../mage/cards/t/TadeasJuniperAscendant.java | 2 +- .../src/mage/cards/t/TenthDistrictHero.java | 1 + Mage.Sets/src/mage/cards/t/Terraformer.java | 5 + .../mage/cards/t/TezzeretCruelMachinist.java | 2 +- .../src/mage/cards/t/TheTombOfAclazotz.java | 4 +- Mage.Sets/src/mage/cards/t/ThranPortal.java | 5 + Mage.Sets/src/mage/cards/t/TideShaper.java | 3 +- .../src/mage/cards/t/TreasureNabber.java | 7 - .../src/mage/cards/w/WakeToSlaughter.java | 6 +- .../src/mage/cards/w/WelcomeToTheFold.java | 3 +- .../AttacksWithCreaturesTriggeredAbility.java | 3 +- .../costs/common/ExileFromGraveCost.java | 3 +- .../abilities/effects/ContinuousEffect.java | 5 + .../effects/ContinuousEffectImpl.java | 6 +- .../mage/abilities/effects/EffectImpl.java | 15 ++ .../mage/abilities/effects/OneShotEffect.java | 13 ++ .../common/CreateTokenCopyTargetEffect.java | 3 +- .../effects/common/DetainAllEffect.java | 3 +- ...urnBattlefieldNextEndStepTargetEffect.java | 3 +- .../common/ExileTopXMayPlayUntilEffect.java | 3 + ...tAllDamageFromChosenSourceToYouEffect.java | 1 + .../common/PreventDamageBySourceEffect.java | 1 + ...tDamageFromChosenSourceToTargetEffect.java | 1 + ...NextDamageFromChosenSourceToYouEffect.java | 1 + ...ToHandChosenControlledPermanentEffect.java | 2 +- .../common/SacrificeControllerEffect.java | 2 +- .../PlayFromNotOwnHandZoneTargetEffect.java | 5 +- .../continuous/BecomesColorSourceEffect.java | 4 +- .../continuous/BecomesColorTargetEffect.java | 5 +- .../continuous/BoostEquippedEffect.java | 2 +- .../ExchangeControlTargetEffect.java | 2 + .../continuous/GainAbilityAttachedEffect.java | 2 +- .../GainAbilityWithAttachmentEffect.java | 2 +- .../effects/keyword/BolsterEffect.java | 6 +- .../mage/abilities/keyword/AwakenAbility.java | 6 +- .../mage/abilities/keyword/MyriadAbility.java | 2 +- .../command/emblems/DackFaydenEmblem.java | 6 +- .../main/java/mage/game/events/GameEvent.java | 5 + .../targetpointer/EachTargetPointer.java | 28 +-- .../target/targetpointer/FixedTarget.java | 40 ++-- .../target/targetpointer/FixedTargets.java | 106 ++++------- .../targetpointer/NthTargetPointer.java | 178 +++++++++--------- .../target/targetpointer/TargetPointer.java | 19 +- .../targetpointer/TargetPointerImpl.java | 27 +++ 115 files changed, 466 insertions(+), 355 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AuriokReplica.java b/Mage.Sets/src/mage/cards/a/AuriokReplica.java index b05a266d472..8ff9dcaeb0f 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokReplica.java +++ b/Mage.Sets/src/mage/cards/a/AuriokReplica.java @@ -64,8 +64,8 @@ class AuriokReplicaEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java b/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java index 712d08a1044..cffd0f25c49 100644 --- a/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java +++ b/Mage.Sets/src/mage/cards/b/BackdraftHellkite.java @@ -14,6 +14,7 @@ import mage.constants.*; import mage.game.Game; import mage.players.Player; +import java.util.Objects; import java.util.UUID; /** @@ -74,8 +75,9 @@ class BackdraftHellkiteEffect extends ContinuousEffectImpl { } player.getGraveyard() .stream() - .map((cardId) -> game.getCard(cardId)) - .filter(card1 -> card1.isInstantOrSorcery(game)) + .map(game::getCard) + .filter(Objects::nonNull) + .filter(card -> card.isInstantOrSorcery(game)) .forEachOrdered(card -> affectedObjectList.add(new MageObjectReference(card, game))); } diff --git a/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java b/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java index 3e4feefb4ee..36c5a8cf87e 100644 --- a/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java +++ b/Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java @@ -71,8 +71,8 @@ class BeaconOfDestinyEffect extends RedirectionEffect { @Override public void init(Ability source, Game game) { - this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/b/Besmirch.java b/Mage.Sets/src/mage/cards/b/Besmirch.java index aa377139f52..83f68a4e623 100644 --- a/Mage.Sets/src/mage/cards/b/Besmirch.java +++ b/Mage.Sets/src/mage/cards/b/Besmirch.java @@ -61,22 +61,22 @@ class BesmirchEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { if (game.getPermanent(source.getFirstTarget()) != null) { - TargetPointer target = new FixedTarget(source.getFirstTarget(), game); + TargetPointer blueprintTarget = new FixedTarget(source.getFirstTarget(), game); // gain control game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn) - .setTargetPointer(target), source); + .setTargetPointer(blueprintTarget.copy()), source); // haste game.addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn - ).setTargetPointer(target), source); + ).setTargetPointer(blueprintTarget.copy()), source); // goad - game.addEffect(new GoadTargetEffect().setTargetPointer(target), source); + game.addEffect(new GoadTargetEffect().setTargetPointer(blueprintTarget.copy()), source); // untap - new UntapTargetEffect().setTargetPointer(target).apply(game, source); + new UntapTargetEffect().setTargetPointer(blueprintTarget.copy()).apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/b/BoneMask.java b/Mage.Sets/src/mage/cards/b/BoneMask.java index 6353ac9ac9d..574a6c78eaf 100644 --- a/Mage.Sets/src/mage/cards/b/BoneMask.java +++ b/Mage.Sets/src/mage/cards/b/BoneMask.java @@ -68,8 +68,8 @@ class BoneMaskEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java b/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java index 6f4666ba7f8..206174cb155 100644 --- a/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java +++ b/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java @@ -74,6 +74,7 @@ class ChoArrimAlchemistEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/c/CommandersPlate.java b/Mage.Sets/src/mage/cards/c/CommandersPlate.java index 7fe92ff32fd..7a4a0bae432 100644 --- a/Mage.Sets/src/mage/cards/c/CommandersPlate.java +++ b/Mage.Sets/src/mage/cards/c/CommandersPlate.java @@ -86,15 +86,17 @@ class CommandersPlateEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { - super.init(source, game); if (!affectedObjectsSet) { return; } Permanent equipment = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (equipment == null || equipment.getAttachedTo() == null) { + discard(); return; } this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game)); + + super.init(source, game); // must call at the end due target pointer setup } @Override diff --git a/Mage.Sets/src/mage/cards/c/ConvincingMirage.java b/Mage.Sets/src/mage/cards/c/ConvincingMirage.java index f9b9a6b0f5b..c496bf77be8 100644 --- a/Mage.Sets/src/mage/cards/c/ConvincingMirage.java +++ b/Mage.Sets/src/mage/cards/c/ConvincingMirage.java @@ -71,6 +71,11 @@ class ConvincingMirageContinousEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + discard(); + return; + } + switch (choice) { case FOREST: dependencyTypes.add(DependencyType.BecomeForest); diff --git a/Mage.Sets/src/mage/cards/c/CorpseDance.java b/Mage.Sets/src/mage/cards/c/CorpseDance.java index d7f130ef6f9..6a62a0c8fe5 100644 --- a/Mage.Sets/src/mage/cards/c/CorpseDance.java +++ b/Mage.Sets/src/mage/cards/c/CorpseDance.java @@ -79,14 +79,14 @@ class CorpseDanceEffect extends OneShotEffect { if (controller.moveCards(lastCreatureCard, Zone.BATTLEFIELD, source, game)) { Permanent creature = game.getPermanent(lastCreatureCard.getId()); if (creature != null) { - FixedTarget fixedTarget = new FixedTarget(creature, game); + FixedTarget blueprintTarget = new FixedTarget(creature, game); // Gains Haste ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - hasteEffect.setTargetPointer(fixedTarget); + hasteEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(hasteEffect, source); // Exile it at end of turn ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(fixedTarget); + exileEffect.setTargetPointer(blueprintTarget.copy()); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); game.addDelayedTriggeredAbility(delayedAbility, source); } diff --git a/Mage.Sets/src/mage/cards/c/CosmiumConfluence.java b/Mage.Sets/src/mage/cards/c/CosmiumConfluence.java index 6222e1ca1fc..bc287fb4f98 100644 --- a/Mage.Sets/src/mage/cards/c/CosmiumConfluence.java +++ b/Mage.Sets/src/mage/cards/c/CosmiumConfluence.java @@ -104,16 +104,16 @@ class CosmiumConfluenceEffect extends OneShotEffect { } controller.choose(outcome, target, source, game); - FixedTarget fixedTarget = new FixedTarget(target.getFirstTarget(), game); + FixedTarget blueprintTarget = new FixedTarget(target.getFirstTarget(), game); new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)) - .setTargetPointer(fixedTarget) + .setTargetPointer(blueprintTarget.copy()) .apply(game, source); ContinuousEffect effect = new BecomesCreatureTargetEffect( new CreatureToken(0, 0, "0/0 Elemental creature with haste", SubType.ELEMENTAL) .withAbility(HasteAbility.getInstance()), false, true, Duration.Custom ); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DarkSphere.java b/Mage.Sets/src/mage/cards/d/DarkSphere.java index 2a93aa89600..c90d347b09f 100644 --- a/Mage.Sets/src/mage/cards/d/DarkSphere.java +++ b/Mage.Sets/src/mage/cards/d/DarkSphere.java @@ -70,6 +70,7 @@ class DarkSpherePreventionEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/d/DeflectingPalm.java b/Mage.Sets/src/mage/cards/d/DeflectingPalm.java index dfe9f6a0db2..7189cbe5329 100644 --- a/Mage.Sets/src/mage/cards/d/DeflectingPalm.java +++ b/Mage.Sets/src/mage/cards/d/DeflectingPalm.java @@ -62,8 +62,8 @@ class DeflectingPalmEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DesperateGambit.java b/Mage.Sets/src/mage/cards/d/DesperateGambit.java index 59c3a44d300..1931ccdc0e3 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateGambit.java +++ b/Mage.Sets/src/mage/cards/d/DesperateGambit.java @@ -65,12 +65,16 @@ class DesperateGambitEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.Benefit, source.getControllerId(), source.getSourceId(), source, game); + super.init(source, game); + Player you = game.getPlayer(source.getControllerId()); - if(you != null) { - wonFlip = you.flipCoin(source, game, true); - super.init(source, game); + if (you == null) { + discard(); + return; } + + this.target.choose(Outcome.Benefit, source.getControllerId(), source.getSourceId(), source, game); + this.wonFlip = you.flipCoin(source, game, true); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DisplacedDinosaurs.java b/Mage.Sets/src/mage/cards/d/DisplacedDinosaurs.java index f2cfdaf5300..83185aba85d 100644 --- a/Mage.Sets/src/mage/cards/d/DisplacedDinosaurs.java +++ b/Mage.Sets/src/mage/cards/d/DisplacedDinosaurs.java @@ -78,15 +78,15 @@ class DisplacedDinosaursEntersBattlefieldEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent historic = ((EntersTheBattlefieldEvent) event).getTarget(); if (historic != null) { - TargetPointer historicTarget = new FixedTarget(historic.getId(), historic.getZoneChangeCounter(game) + 1); + TargetPointer blueprintTarget = new FixedTarget(historic.getId(), historic.getZoneChangeCounter(game) + 1); ContinuousEffect creatureEffect = new AddCardTypeTargetEffect(Duration.Custom, CardType.CREATURE); - creatureEffect.setTargetPointer(historicTarget); + creatureEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(creatureEffect, source); ContinuousEffect dinosaurEffect = new AddCardSubTypeTargetEffect(SubType.DINOSAUR, Duration.Custom); - dinosaurEffect.setTargetPointer(historicTarget); + dinosaurEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(dinosaurEffect, source); ContinuousEffect sevenSevenEffect = new SetBasePowerToughnessTargetEffect(7, 7, Duration.Custom); - sevenSevenEffect.setTargetPointer(historicTarget); + sevenSevenEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(sevenSevenEffect, source); } return false; diff --git a/Mage.Sets/src/mage/cards/e/ElrondOfTheWhiteCouncil.java b/Mage.Sets/src/mage/cards/e/ElrondOfTheWhiteCouncil.java index f921f460dd1..942bc6fcf44 100644 --- a/Mage.Sets/src/mage/cards/e/ElrondOfTheWhiteCouncil.java +++ b/Mage.Sets/src/mage/cards/e/ElrondOfTheWhiteCouncil.java @@ -1,6 +1,7 @@ package mage.cards.e; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -23,6 +24,7 @@ import mage.target.targetpointer.FixedTargets; import mage.target.targetpointer.TargetPointer; import mage.util.CardUtil; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -127,16 +129,16 @@ class ElrondOfWhiteCouncilEffect extends OneShotEffect { } // You gain control of each creature chosen this way, and they gain "This creature can't attack its owner." - TargetPointer pointer = new FixedTargets(chosenCreatures.stream().collect(Collectors.toList()), game); + TargetPointer blueprintTarget = new FixedTargets(new ArrayList<>(chosenCreatures), game); game.addEffect(new GainControlTargetEffect( Duration.WhileOnBattlefield - ).setTargetPointer(pointer), source); + ).setTargetPointer(blueprintTarget.copy()), source); game.addEffect(new GainAbilityTargetEffect( new SimpleStaticAbility(new CantAttackItsOwnerEffect()), Duration.WhileOnBattlefield - ).setTargetPointer(pointer), source); + ).setTargetPointer(blueprintTarget.copy()), source); // Need to process the control change. game.getState().processAction(game); diff --git a/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java b/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java index 706aacafef4..970ad2a9d33 100644 --- a/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java +++ b/Mage.Sets/src/mage/cards/e/ElsewhereFlask.java @@ -95,6 +95,11 @@ class ElsewhereFlaskContinuousEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_ElsewhereFlask")); + if (choice == null) { + discard(); + return; + } + switch (choice) { case FOREST: dependencyTypes.add(DependencyType.BecomeForest); diff --git a/Mage.Sets/src/mage/cards/e/EyeForAnEye.java b/Mage.Sets/src/mage/cards/e/EyeForAnEye.java index 191592d95a5..e3cac0359e9 100644 --- a/Mage.Sets/src/mage/cards/e/EyeForAnEye.java +++ b/Mage.Sets/src/mage/cards/e/EyeForAnEye.java @@ -59,8 +59,8 @@ class EyeForAnEyeEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { - this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/f/Fecundity.java b/Mage.Sets/src/mage/cards/f/Fecundity.java index 97a9283deb8..92455435f31 100644 --- a/Mage.Sets/src/mage/cards/f/Fecundity.java +++ b/Mage.Sets/src/mage/cards/f/Fecundity.java @@ -54,9 +54,9 @@ class FecundityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer() - // Card can be moved again (e.g. commander replacement) so we need the row id from fixed target to check - .getFixedTarget(game, source).getTarget(), Zone.BATTLEFIELD); + // card can be moved again (e.g. commander replacement) so we need the row id from fixed target to check + // TODO: bugged with commander replacement effects? + Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); if (permanent != null) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null) { diff --git a/Mage.Sets/src/mage/cards/g/GeneralsRegalia.java b/Mage.Sets/src/mage/cards/g/GeneralsRegalia.java index 8c1ddd3d577..faceffa370e 100644 --- a/Mage.Sets/src/mage/cards/g/GeneralsRegalia.java +++ b/Mage.Sets/src/mage/cards/g/GeneralsRegalia.java @@ -67,8 +67,8 @@ class GeneralsRegaliaEffect extends RedirectionEffect { @Override public void init(Ability source, Game game) { - this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GiantOyster.java b/Mage.Sets/src/mage/cards/g/GiantOyster.java index 830af87196f..3d4a7c74d48 100644 --- a/Mage.Sets/src/mage/cards/g/GiantOyster.java +++ b/Mage.Sets/src/mage/cards/g/GiantOyster.java @@ -1,6 +1,5 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -28,8 +27,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author noahg */ public final class GiantOyster extends CardImpl { @@ -103,23 +103,24 @@ class GiantOysterCreateDelayedTriggerEffects extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent oyster = game.getPermanent(source.getSourceId()); - Permanent tappedCreature = game.getPermanent(source.getFirstTarget()); - if (oyster != null && tappedCreature != null) { - Effect addCountersEffect = new AddCountersTargetEffect(CounterType.M1M1.createInstance(1)); - addCountersEffect.setTargetPointer(getTargetPointer().getFixedTarget(game, source)); - DelayedTriggeredAbility drawStepAbility = new AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(addCountersEffect, Duration.Custom, false); - drawStepAbility.setControllerId(source.getControllerId()); - UUID drawStepAbilityUUID = game.addDelayedTriggeredAbility(drawStepAbility, source); - - DelayedTriggeredAbility leaveUntapDelayedTriggeredAbility = new GiantOysterLeaveUntapDelayedTriggeredAbility(drawStepAbilityUUID); - leaveUntapDelayedTriggeredAbility.getEffects().get(0).setTargetPointer(new FixedTarget(tappedCreature, game)); - game.addDelayedTriggeredAbility(leaveUntapDelayedTriggeredAbility, source); - return true; - } + Permanent oyster = game.getPermanent(source.getSourceId()); + Permanent tappedCreature = game.getPermanent(source.getFirstTarget()); + FixedTarget fixedTarget = getTargetPointer().getFirstAsFixedTarget(game, source); + if (controller == null || oyster == null || tappedCreature == null || fixedTarget == null) { + return false; } - return false; + + Effect addCountersEffect = new AddCountersTargetEffect(CounterType.M1M1.createInstance(1)); + addCountersEffect.setTargetPointer(fixedTarget); + DelayedTriggeredAbility drawStepAbility = new AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(addCountersEffect, Duration.Custom, false); + drawStepAbility.setControllerId(source.getControllerId()); + UUID drawStepAbilityUUID = game.addDelayedTriggeredAbility(drawStepAbility, source); + + DelayedTriggeredAbility leaveUntapDelayedTriggeredAbility = new GiantOysterLeaveUntapDelayedTriggeredAbility(drawStepAbilityUUID); + leaveUntapDelayedTriggeredAbility.getEffects().get(0).setTargetPointer(new FixedTarget(tappedCreature, game)); + game.addDelayedTriggeredAbility(leaveUntapDelayedTriggeredAbility, source); + return true; + } } diff --git a/Mage.Sets/src/mage/cards/g/GiftOfFangs.java b/Mage.Sets/src/mage/cards/g/GiftOfFangs.java index a4b00861c14..d65fce68fb2 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfFangs.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfFangs.java @@ -66,7 +66,6 @@ class GiftOfFangsEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { - super.init(source, game); if (affectedObjectsSet) { // Added boosts of activated or triggered abilities exist independent from the source they are created by // so a continuous effect for the permanent itself with the attachment is created @@ -75,6 +74,8 @@ class GiftOfFangsEffect extends ContinuousEffectImpl { this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game.getState().getZoneChangeCounter(equipment.getAttachedTo()))); } } + + super.init(source, game); // must call at the end due target pointer setup } @Override diff --git a/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java b/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java index 295f0b23ba3..fbade67a840 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java +++ b/Mage.Sets/src/mage/cards/g/GoblinPsychopath.java @@ -59,8 +59,15 @@ class GoblinPsychopathEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { - this.wonFlip = game.getPlayer(source.getControllerId()).flipCoin(source, game, true); super.init(source, game); + + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + discard(); + return; + } + + this.wonFlip = controller.flipCoin(source, game, true); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GoreVassal.java b/Mage.Sets/src/mage/cards/g/GoreVassal.java index 8b6dce02238..83d42417e7d 100644 --- a/Mage.Sets/src/mage/cards/g/GoreVassal.java +++ b/Mage.Sets/src/mage/cards/g/GoreVassal.java @@ -69,12 +69,12 @@ class GoreVassalEffect extends RegenerateTargetEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); + Permanent creature = game.getPermanent(source.getFirstTarget()); if (creature == null || creature.getToughness().getValue() < 1) { this.discard(); - return; } - super.init(source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GuardDogs.java b/Mage.Sets/src/mage/cards/g/GuardDogs.java index aeb52ddc335..c8cc0430beb 100644 --- a/Mage.Sets/src/mage/cards/g/GuardDogs.java +++ b/Mage.Sets/src/mage/cards/g/GuardDogs.java @@ -67,10 +67,11 @@ class GuardDogsEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); + this.controlledTarget = new TargetControlledPermanent(); this.controlledTarget.withNotTarget(true); this.controlledTarget.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - super.init(source, game); } diff --git a/Mage.Sets/src/mage/cards/h/HarmsWay.java b/Mage.Sets/src/mage/cards/h/HarmsWay.java index b26b1b6c7ac..b5cb7f2ab7a 100644 --- a/Mage.Sets/src/mage/cards/h/HarmsWay.java +++ b/Mage.Sets/src/mage/cards/h/HarmsWay.java @@ -63,8 +63,8 @@ class HarmsWayPreventDamageTargetEffect extends RedirectionEffect { @Override public void init(Ability source, Game game) { - this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/h/HazduhrTheAbbot.java b/Mage.Sets/src/mage/cards/h/HazduhrTheAbbot.java index 29ac0100ea0..8a7f2053daf 100644 --- a/Mage.Sets/src/mage/cards/h/HazduhrTheAbbot.java +++ b/Mage.Sets/src/mage/cards/h/HazduhrTheAbbot.java @@ -76,6 +76,7 @@ class HazduhrTheAbbotRedirectDamageEffect extends RedirectionEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); amountToRedirect = source.getManaCostsToPay().getX(); } diff --git a/Mage.Sets/src/mage/cards/h/HealingGrace.java b/Mage.Sets/src/mage/cards/h/HealingGrace.java index d9520df8041..7a98ba94c80 100644 --- a/Mage.Sets/src/mage/cards/h/HealingGrace.java +++ b/Mage.Sets/src/mage/cards/h/HealingGrace.java @@ -62,6 +62,7 @@ class HealingGraceEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); // be sure to note the target source's zcc, etc, if able. if (targetSource.getFirstTarget() != null) { diff --git a/Mage.Sets/src/mage/cards/h/HisokasGuard.java b/Mage.Sets/src/mage/cards/h/HisokasGuard.java index bfb076c050e..7f3689bec67 100644 --- a/Mage.Sets/src/mage/cards/h/HisokasGuard.java +++ b/Mage.Sets/src/mage/cards/h/HisokasGuard.java @@ -79,6 +79,7 @@ class HisokasGuardGainAbilityTargetEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); // remember the guarded creature Permanent guardedCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); Permanent hisokasGuard = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java b/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java index 194323c2d57..eb98f3e095a 100644 --- a/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java +++ b/Mage.Sets/src/mage/cards/h/HoardSmelterDragon.java @@ -78,6 +78,7 @@ class HoardSmelterEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); Card targeted = game.getCard(source.getFirstTarget()); if (targeted != null) { costValue = targeted.getManaValue(); diff --git a/Mage.Sets/src/mage/cards/h/HuntingWilds.java b/Mage.Sets/src/mage/cards/h/HuntingWilds.java index 150e9505747..57a85580afd 100644 --- a/Mage.Sets/src/mage/cards/h/HuntingWilds.java +++ b/Mage.Sets/src/mage/cards/h/HuntingWilds.java @@ -82,13 +82,13 @@ class HuntingWildsEffect extends OneShotEffect { if (sourceEffect instanceof SearchLibraryPutInPlayEffect) { Cards foundCards = new CardsImpl(((SearchLibraryPutInPlayEffect) sourceEffect).getTargets()); if (!foundCards.isEmpty()) { - FixedTargets fixedTargets = new FixedTargets(foundCards, game); + FixedTargets blueprintTarget = new FixedTargets(foundCards, game); UntapTargetEffect untapEffect = new UntapTargetEffect(); - untapEffect.setTargetPointer(fixedTargets); + untapEffect.setTargetPointer(blueprintTarget.copy()); untapEffect.apply(game, source); BecomesCreatureTargetEffect becomesCreatureEffect = new BecomesCreatureTargetEffect(new HuntingWildsToken(), false, true, Duration.Custom); - becomesCreatureEffect.setTargetPointer(fixedTargets); + becomesCreatureEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(becomesCreatureEffect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/i/IllicitAuction.java b/Mage.Sets/src/mage/cards/i/IllicitAuction.java index c3983d0d7b2..454476ca0c8 100644 --- a/Mage.Sets/src/mage/cards/i/IllicitAuction.java +++ b/Mage.Sets/src/mage/cards/i/IllicitAuction.java @@ -64,6 +64,8 @@ class IllicitAuctionEffect extends GainControlTargetEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (controller != null && targetCreature != null) { @@ -108,7 +110,6 @@ class IllicitAuctionEffect extends GainControlTargetEffect { winner.loseLife(highBid, game, source, false); super.controllingPlayerId = winner.getId(); } - super.init(source, game); } } diff --git a/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java b/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java index ba4bf49e6ca..0d1effc5658 100644 --- a/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java +++ b/Mage.Sets/src/mage/cards/i/ImpulsiveManeuvers.java @@ -57,8 +57,8 @@ class ImpulsiveManeuversEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.wonFlip = game.getPlayer(source.getControllerId()).flipCoin(source, game, true); super.init(source, game); + this.wonFlip = game.getPlayer(source.getControllerId()).flipCoin(source, game, true); } @Override diff --git a/Mage.Sets/src/mage/cards/j/JadeMonolith.java b/Mage.Sets/src/mage/cards/j/JadeMonolith.java index 5eba05dcebe..2f992ce9092 100644 --- a/Mage.Sets/src/mage/cards/j/JadeMonolith.java +++ b/Mage.Sets/src/mage/cards/j/JadeMonolith.java @@ -70,6 +70,7 @@ class JadeMonolithRedirectionEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/j/Juxtapose.java b/Mage.Sets/src/mage/cards/j/Juxtapose.java index 1167d2e339d..a7b0f829e62 100644 --- a/Mage.Sets/src/mage/cards/j/Juxtapose.java +++ b/Mage.Sets/src/mage/cards/j/Juxtapose.java @@ -75,6 +75,7 @@ class JuxtaposeEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); Player you = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/k/KheruLichLord.java b/Mage.Sets/src/mage/cards/k/KheruLichLord.java index 0b2814030c9..7f16c65aa70 100644 --- a/Mage.Sets/src/mage/cards/k/KheruLichLord.java +++ b/Mage.Sets/src/mage/cards/k/KheruLichLord.java @@ -81,26 +81,26 @@ class KheruLichLordEffect extends OneShotEffect { controller.moveCards(card, Zone.BATTLEFIELD, source, game); Permanent permanent = game.getPermanent(card.getId()); if (permanent != null) { - FixedTarget fixedTarget = new FixedTarget(permanent, game); + FixedTarget blueprintTarget = new FixedTarget(permanent, game); ContinuousEffect effect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(fixedTarget); + exileEffect.setTargetPointer(blueprintTarget.copy()); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); game.addDelayedTriggeredAbility(delayedAbility, source); ReplacementEffect replacementEffect = new LeaveBattlefieldExileTargetReplacementEffect("that card"); - replacementEffect.setTargetPointer(fixedTarget); + replacementEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(replacementEffect, source); } } diff --git a/Mage.Sets/src/mage/cards/k/KillerInstinct.java b/Mage.Sets/src/mage/cards/k/KillerInstinct.java index 97d19d07766..5b349790c49 100644 --- a/Mage.Sets/src/mage/cards/k/KillerInstinct.java +++ b/Mage.Sets/src/mage/cards/k/KillerInstinct.java @@ -85,12 +85,12 @@ class KillerInstinctEffect extends OneShotEffect { if (card.isCreature(game) && player.moveCards(card, Zone.BATTLEFIELD, source, game)) { Permanent permanent = game.getPermanent(card.getId()); if (permanent != null) { - FixedTarget ft = new FixedTarget(permanent, game); + FixedTarget blueprintTarget = new FixedTarget(permanent, game); ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(ft); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); Effect sacrificeEffect = new SacrificeTargetEffect("Sacrifice it at the beginning of the next end step", source.getControllerId()); - sacrificeEffect.setTargetPointer(ft); + sacrificeEffect.setTargetPointer(blueprintTarget.copy()); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source); } return true; diff --git a/Mage.Sets/src/mage/cards/l/LegionLoyalist.java b/Mage.Sets/src/mage/cards/l/LegionLoyalist.java index 6cd4fd68ec9..9f7b8851995 100644 --- a/Mage.Sets/src/mage/cards/l/LegionLoyalist.java +++ b/Mage.Sets/src/mage/cards/l/LegionLoyalist.java @@ -69,6 +69,7 @@ class LegionLoyalistCantBeBlockedByTokensEffect extends RestrictionEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); affectedObjectsSet = true; for (Permanent perm : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURES, source.getControllerId(), source, game)) { affectedObjectList.add(new MageObjectReference(perm, game)); diff --git a/Mage.Sets/src/mage/cards/m/MournersShield.java b/Mage.Sets/src/mage/cards/m/MournersShield.java index e6a7e6bf7bd..20902ca4436 100644 --- a/Mage.Sets/src/mage/cards/m/MournersShield.java +++ b/Mage.Sets/src/mage/cards/m/MournersShield.java @@ -123,6 +123,7 @@ class MournersShieldEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); ObjectColor colorsAmongImprinted = new ObjectColor(); Permanent sourceObject = game.getPermanent(source.getSourceId()); ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source.getSourceId())); diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java index ab4ce40056d..b9b9b59e0a0 100644 --- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java +++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java @@ -108,9 +108,9 @@ class NacatlWarPrideEffect extends OneShotEffect { copies.addAll(effect.getAddedPermanents()); if (!copies.isEmpty()) { - FixedTargets fixedTargets = new FixedTargets(copies, game); + FixedTargets blueprintTarget = new FixedTargets(copies, game); ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(fixedTargets).setText("exile the tokens"); + exileEffect.setTargetPointer(blueprintTarget.copy()).setText("exile the tokens"); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); return true; } diff --git a/Mage.Sets/src/mage/cards/n/NarciFableSinger.java b/Mage.Sets/src/mage/cards/n/NarciFableSinger.java index b395cc96f69..809a1a595bf 100644 --- a/Mage.Sets/src/mage/cards/n/NarciFableSinger.java +++ b/Mage.Sets/src/mage/cards/n/NarciFableSinger.java @@ -79,7 +79,7 @@ class NarciFableSingerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - FixedTarget fixedTarget = targetPointer.getFixedTarget(game, source); + FixedTarget fixedTarget = targetPointer.getFirstAsFixedTarget(game, source); if (fixedTarget == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/n/NewBlood.java b/Mage.Sets/src/mage/cards/n/NewBlood.java index a9e47aad75e..2f0092bf567 100644 --- a/Mage.Sets/src/mage/cards/n/NewBlood.java +++ b/Mage.Sets/src/mage/cards/n/NewBlood.java @@ -110,6 +110,8 @@ class ChangeCreatureTypeTargetEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return; @@ -126,8 +128,6 @@ class ChangeCreatureTypeTargetEffect extends ContinuousEffectImpl { game.informPlayers(controller.getLogName() + " has chosen the creature type: " + fromSubType.toString()); } } - - super.init(source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/n/Nightcreep.java b/Mage.Sets/src/mage/cards/n/Nightcreep.java index b0be53899c6..518fce9de01 100644 --- a/Mage.Sets/src/mage/cards/n/Nightcreep.java +++ b/Mage.Sets/src/mage/cards/n/Nightcreep.java @@ -54,9 +54,9 @@ class NightcreepLandEffect extends BecomesBasicLandTargetEffect { @Override public void init(Ability source, Game game) { - super.init(source, game); List targets = new ArrayList<>(game.getBattlefield().getActivePermanents(StaticFilters.FILTER_LAND, source.getControllerId(), source, game)); this.setTargetPointer(new FixedTargets(targets, game)); + super.init(source, game); // must call at the end due target pointer setup } @Override @@ -78,9 +78,9 @@ class NightcreepCreatureEffect extends BecomesColorTargetEffect { @Override public void init(Ability source, Game game) { - super.init(source, game); List targets = new ArrayList<>(game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source, game)); this.setTargetPointer(new FixedTargets(targets, game)); + super.init(source, game); // must call at the end due target pointer setup } @Override diff --git a/Mage.Sets/src/mage/cards/n/NovaPentacle.java b/Mage.Sets/src/mage/cards/n/NovaPentacle.java index 6637de77084..7ab6107cfa0 100644 --- a/Mage.Sets/src/mage/cards/n/NovaPentacle.java +++ b/Mage.Sets/src/mage/cards/n/NovaPentacle.java @@ -69,8 +69,8 @@ class NovaPentacleEffect extends RedirectionEffect { @Override public void init(Ability source, Game game) { - this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java b/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java index 5d9efd51085..4eb59533a7b 100644 --- a/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java +++ b/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java @@ -89,12 +89,12 @@ class NoyanDarEffect extends OneShotEffect { targetId = target.getFirstTarget(); } if (targetId != null) { - FixedTarget fixedTarget = new FixedTarget(targetId, game); + FixedTarget blueprintTarget = new FixedTarget(targetId, game); ContinuousEffect continuousEffect = new BecomesCreatureTargetEffect(new AwakenElementalToken(), false, true, Duration.EndOfGame); - continuousEffect.setTargetPointer(fixedTarget); + continuousEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(continuousEffect, source); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); return effect.apply(game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java b/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java index 8a7780b1d9b..296c5d7342f 100644 --- a/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java +++ b/Mage.Sets/src/mage/cards/o/OpalEyeKondasYojimbo.java @@ -77,8 +77,8 @@ class OpalEyeKondasYojimboRedirectionEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/o/OraclesAttendants.java b/Mage.Sets/src/mage/cards/o/OraclesAttendants.java index 34af66ff2b6..1e190ad2016 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesAttendants.java +++ b/Mage.Sets/src/mage/cards/o/OraclesAttendants.java @@ -74,6 +74,7 @@ class OraclesAttendantsReplacementEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/p/Penance.java b/Mage.Sets/src/mage/cards/p/Penance.java index e28674f1951..5f44789f2ae 100644 --- a/Mage.Sets/src/mage/cards/p/Penance.java +++ b/Mage.Sets/src/mage/cards/p/Penance.java @@ -63,8 +63,8 @@ class PenanceEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java index 04729f4ed4e..65008b845e7 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java @@ -69,6 +69,11 @@ public final class PhantasmalTerrain extends CardImpl { public void init(Ability source, Game game) { super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + discard(); + return; + } + switch (choice) { case FOREST: dependencyTypes.add(DependencyType.BecomeForest); diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java b/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java index 56651fbd1cc..521347adba6 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java @@ -82,6 +82,7 @@ class PilgrimOfJusticeEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java b/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java index a4d8a6ef104..f7fa244cd89 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java @@ -82,6 +82,7 @@ class PilgrimOfVirtueEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/p/Portcullis.java b/Mage.Sets/src/mage/cards/p/Portcullis.java index 7497944d0f0..60b3fb061ab 100644 --- a/Mage.Sets/src/mage/cards/p/Portcullis.java +++ b/Mage.Sets/src/mage/cards/p/Portcullis.java @@ -97,10 +97,9 @@ class PortcullisExileEffect extends OneShotEffect { && controller != null) { UUID exileZoneId = CardUtil.getExileZoneId(game, creatureToExile.getId(), creatureToExile.getZoneChangeCounter(game)); controller.moveCardsToExile(creatureToExile, source, game, true, exileZoneId, portcullis.getName()); - FixedTarget fixedTarget = new FixedTarget(portcullis, game); Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); returnEffect.setTargetPointer(new FixedTarget(creatureToExile.getId(), game.getState().getZoneChangeCounter(creatureToExile.getId()))); - DelayedTriggeredAbility delayedAbility = new PortcullisReturnToBattlefieldTriggeredAbility(fixedTarget, returnEffect); + DelayedTriggeredAbility delayedAbility = new PortcullisReturnToBattlefieldTriggeredAbility(new FixedTarget(portcullis, game), returnEffect); game.addDelayedTriggeredAbility(delayedAbility, source); } return true; @@ -109,7 +108,7 @@ class PortcullisExileEffect extends OneShotEffect { class PortcullisReturnToBattlefieldTriggeredAbility extends DelayedTriggeredAbility { - protected FixedTarget fixedTarget; + protected final FixedTarget fixedTarget; public PortcullisReturnToBattlefieldTriggeredAbility(FixedTarget fixedTarget, Effect effect) { super(effect, Duration.OneUse); @@ -118,7 +117,7 @@ class PortcullisReturnToBattlefieldTriggeredAbility extends DelayedTriggeredAbil private PortcullisReturnToBattlefieldTriggeredAbility(final PortcullisReturnToBattlefieldTriggeredAbility ability) { super(ability); - this.fixedTarget = ability.fixedTarget; + this.fixedTarget = ability.fixedTarget.copy(); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PrismaticCircle.java b/Mage.Sets/src/mage/cards/p/PrismaticCircle.java index 5a0a12f0598..91be2dabb3c 100644 --- a/Mage.Sets/src/mage/cards/p/PrismaticCircle.java +++ b/Mage.Sets/src/mage/cards/p/PrismaticCircle.java @@ -58,9 +58,9 @@ class PrismaticCircleEffect extends PreventNextDamageFromChosenSourceToYouEffect @Override public void init(Ability source, Game game) { + super.init(source, game); FilterObject filter = targetSource.getFilter(); filter.add(new ColorPredicate((ObjectColor) game.getState().getValue(source.getSourceId() + "_color"))); - super.init(source, game); } private PrismaticCircleEffect(final PrismaticCircleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java b/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java index 56823eb3f4d..762037cfc4a 100644 --- a/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java +++ b/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java @@ -64,6 +64,7 @@ class ProgenitorsIconAsThoughEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game); ProgenitorsIconWatcher.addPlayer(source.getControllerId(), subType, game); } diff --git a/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java b/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java index 9d4c9e49ca6..215d66ae3d5 100644 --- a/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java +++ b/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java @@ -75,6 +75,7 @@ class ProtectiveSphereEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); target.withNotTarget(true); target.setRequired(false); Player controller = game.getPlayer(source.getControllerId()); @@ -89,7 +90,6 @@ class ProtectiveSphereEffect extends PreventionEffectImpl { + source.getManaCostsToPay().getUsedManaToPay()), game); } this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - super.init(source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PushPull.java b/Mage.Sets/src/mage/cards/p/PushPull.java index 2e617425ea0..7c829f6960f 100644 --- a/Mage.Sets/src/mage/cards/p/PushPull.java +++ b/Mage.Sets/src/mage/cards/p/PushPull.java @@ -109,14 +109,14 @@ class PullEffect extends OneShotEffect { } permanents.forEach(permanent -> { - FixedTarget target = new FixedTarget(permanent, game); + FixedTarget blueprintTarget = new FixedTarget(permanent, game); ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance()); - effect.setTargetPointer(target); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice " + permanent.getLogName(), controller.getId()); - sacrificeEffect.setTargetPointer(target); + sacrificeEffect.setTargetPointer(blueprintTarget.copy()); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source); }); diff --git a/Mage.Sets/src/mage/cards/q/Quicken.java b/Mage.Sets/src/mage/cards/q/Quicken.java index 830cc8c9cc4..240c18fbdc1 100644 --- a/Mage.Sets/src/mage/cards/q/Quicken.java +++ b/Mage.Sets/src/mage/cards/q/Quicken.java @@ -64,6 +64,7 @@ class QuickenAsThoughEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); quickenWatcher = game.getState().getWatcher(QuickenWatcher.class); Card card = game.getCard(source.getSourceId()); if (quickenWatcher != null && card != null) { diff --git a/Mage.Sets/src/mage/cards/r/Realmwright.java b/Mage.Sets/src/mage/cards/r/Realmwright.java index becf404e95d..3e1d18ad70e 100644 --- a/Mage.Sets/src/mage/cards/r/Realmwright.java +++ b/Mage.Sets/src/mage/cards/r/Realmwright.java @@ -66,6 +66,11 @@ class RealmwrightEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + discard(); + return; + } + switch (choice) { case PLAINS: dependencyTypes.add(DependencyType.BecomePlains); diff --git a/Mage.Sets/src/mage/cards/r/RefractionTrap.java b/Mage.Sets/src/mage/cards/r/RefractionTrap.java index 96e96113562..2c9c52c290b 100644 --- a/Mage.Sets/src/mage/cards/r/RefractionTrap.java +++ b/Mage.Sets/src/mage/cards/r/RefractionTrap.java @@ -106,8 +106,8 @@ class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/r/ReverseDamage.java b/Mage.Sets/src/mage/cards/r/ReverseDamage.java index 5f8d332a596..24ad5920abe 100644 --- a/Mage.Sets/src/mage/cards/r/ReverseDamage.java +++ b/Mage.Sets/src/mage/cards/r/ReverseDamage.java @@ -61,6 +61,7 @@ class ReverseDamageEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/r/RideTheAvalanche.java b/Mage.Sets/src/mage/cards/r/RideTheAvalanche.java index b27922c05b8..c7d4b60948c 100644 --- a/Mage.Sets/src/mage/cards/r/RideTheAvalanche.java +++ b/Mage.Sets/src/mage/cards/r/RideTheAvalanche.java @@ -57,6 +57,7 @@ class RideTheAvalancheAsThoughEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); RideTheAvalancheWatcher.addPlayer(source.getControllerId(), game); } diff --git a/Mage.Sets/src/mage/cards/r/Runesword.java b/Mage.Sets/src/mage/cards/r/Runesword.java index 4206caf3ad9..c171002797f 100644 --- a/Mage.Sets/src/mage/cards/r/Runesword.java +++ b/Mage.Sets/src/mage/cards/r/Runesword.java @@ -120,6 +120,7 @@ class RuneswordCantBeRegeneratedEffect extends ContinuousRuleModifyingEffectImpl } public void init(Ability source, Game game) { + super.init(source, game); targetCreatureId = getTargetPointer().getFirst(game, source); } diff --git a/Mage.Sets/src/mage/cards/s/SamiteMinistration.java b/Mage.Sets/src/mage/cards/s/SamiteMinistration.java index 3a15cf149e7..461c693749e 100644 --- a/Mage.Sets/src/mage/cards/s/SamiteMinistration.java +++ b/Mage.Sets/src/mage/cards/s/SamiteMinistration.java @@ -62,6 +62,7 @@ class SamiteMinistrationEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/s/SavageSummoning.java b/Mage.Sets/src/mage/cards/s/SavageSummoning.java index e10ba2dd44b..ac293c3135a 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSummoning.java +++ b/Mage.Sets/src/mage/cards/s/SavageSummoning.java @@ -72,6 +72,7 @@ class SavageSummoningAsThoughEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); watcher = game.getState().getWatcher(SavageSummoningWatcher.class, source.getControllerId()); Card card = game.getCard(source.getSourceId()); if (watcher != null && card != null) { @@ -188,6 +189,7 @@ class SavageSummoningCantCounterEffect extends ContinuousRuleModifyingEffectImpl @Override public void init(Ability source, Game game) { + super.init(source, game); watcher = game.getState().getWatcher(SavageSummoningWatcher.class, source.getControllerId()); Card card = game.getCard(source.getSourceId()); if (watcher == null || card == null) { @@ -244,6 +246,7 @@ class SavageSummoningEntersBattlefieldEffect extends ReplacementEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); watcher = game.getState().getWatcher(SavageSummoningWatcher.class, source.getControllerId()); Card card = game.getCard(source.getSourceId()); if (watcher == null || card == null) { diff --git a/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java b/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java index 8f473cbc245..aadfea87a73 100644 --- a/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java +++ b/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java @@ -95,7 +95,6 @@ class ScholarOfTheLostTroveEffect extends OneShotEffect { if (!controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + '?', source, game)) { return true; } - FixedTarget fixedTarget = new FixedTarget(card, game); game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), game, true, new ApprovingObject(source, game)); @@ -104,7 +103,7 @@ class ScholarOfTheLostTroveEffect extends OneShotEffect { return true; } ContinuousEffect effect = new ThatSpellGraveyardExileReplacementEffect(true); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead"); game.addEffect(effect, source); return true; diff --git a/Mage.Sets/src/mage/cards/s/ScoutsWarning.java b/Mage.Sets/src/mage/cards/s/ScoutsWarning.java index 348ae3a4fa8..0d5f4d90b6b 100644 --- a/Mage.Sets/src/mage/cards/s/ScoutsWarning.java +++ b/Mage.Sets/src/mage/cards/s/ScoutsWarning.java @@ -63,6 +63,7 @@ class ScoutsWarningAsThoughEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); ScoutsWarningWatcher watcher = game.getState().getWatcher(ScoutsWarningWatcher.class, source.getControllerId()); Card card = game.getCard(source.getSourceId()); if (watcher != null && card != null) { diff --git a/Mage.Sets/src/mage/cards/s/ShallowGrave.java b/Mage.Sets/src/mage/cards/s/ShallowGrave.java index ac282a59052..1d316598ec0 100644 --- a/Mage.Sets/src/mage/cards/s/ShallowGrave.java +++ b/Mage.Sets/src/mage/cards/s/ShallowGrave.java @@ -76,14 +76,14 @@ class ShallowGraveEffect extends OneShotEffect { if (controller.moveCards(lastCreatureCard, Zone.BATTLEFIELD, source, game)) { Permanent returnedCreature = game.getPermanent(lastCreatureCard.getId()); if (returnedCreature != null) { - FixedTarget fixedTarget = new FixedTarget(returnedCreature, game); + FixedTarget blueprintTarget = new FixedTarget(returnedCreature, game); // Gains Haste ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - hasteEffect.setTargetPointer(fixedTarget); + hasteEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(hasteEffect, source); // Exile it at end of turn ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(fixedTarget); + exileEffect.setTargetPointer(blueprintTarget.copy()); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); game.addDelayedTriggeredAbility(delayedAbility, source); } diff --git a/Mage.Sets/src/mage/cards/s/ShamanEnKor.java b/Mage.Sets/src/mage/cards/s/ShamanEnKor.java index e20a23c458b..1d3bfe3eee6 100644 --- a/Mage.Sets/src/mage/cards/s/ShamanEnKor.java +++ b/Mage.Sets/src/mage/cards/s/ShamanEnKor.java @@ -83,6 +83,7 @@ class ShamanEnKorRedirectFromTargetEffect extends RedirectionEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); Player player = game.getPlayer(source.getControllerId()); if (player != null) { TargetSource target = new TargetSource(); diff --git a/Mage.Sets/src/mage/cards/s/ShieldmageAdvocate.java b/Mage.Sets/src/mage/cards/s/ShieldmageAdvocate.java index 11602fecb74..7911fdf3547 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldmageAdvocate.java +++ b/Mage.Sets/src/mage/cards/s/ShieldmageAdvocate.java @@ -82,6 +82,7 @@ class ShieldmageAdvocateEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/s/ShiningShoal.java b/Mage.Sets/src/mage/cards/s/ShiningShoal.java index cd11a7df768..10f1f3aca49 100644 --- a/Mage.Sets/src/mage/cards/s/ShiningShoal.java +++ b/Mage.Sets/src/mage/cards/s/ShiningShoal.java @@ -86,6 +86,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT @Override public void init(Ability source, Game game) { + super.init(source, game); amountToRedirect = dynamicAmount.calculate(game, source, this); } diff --git a/Mage.Sets/src/mage/cards/s/ShowOfDominance.java b/Mage.Sets/src/mage/cards/s/ShowOfDominance.java index 593aee6aa9e..0ec001fb5a4 100644 --- a/Mage.Sets/src/mage/cards/s/ShowOfDominance.java +++ b/Mage.Sets/src/mage/cards/s/ShowOfDominance.java @@ -90,14 +90,14 @@ class ShowOfDominanceEffect extends OneShotEffect { } } if (selectedCreature != null) { - FixedTarget target = new FixedTarget(selectedCreature.getId(), game); + FixedTarget blueprintTarget = new FixedTarget(selectedCreature.getId(), game); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(4)); - effect.setTargetPointer(target); + effect.setTargetPointer(blueprintTarget.copy()); effect.apply(game, source); ContinuousEffect continuousEffect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); - continuousEffect.setTargetPointer(target); + continuousEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(continuousEffect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/StoryCircle.java b/Mage.Sets/src/mage/cards/s/StoryCircle.java index f0c1c7d4097..3156d8a9f75 100644 --- a/Mage.Sets/src/mage/cards/s/StoryCircle.java +++ b/Mage.Sets/src/mage/cards/s/StoryCircle.java @@ -54,9 +54,9 @@ class StoryCircleEffect extends PreventNextDamageFromChosenSourceToYouEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); FilterObject filter = targetSource.getFilter(); filter.add(new ColorPredicate((ObjectColor) game.getState().getValue(source.getSourceId() + "_color"))); - super.init(source, game); } private StoryCircleEffect(final StoryCircleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TadeasJuniperAscendant.java b/Mage.Sets/src/mage/cards/t/TadeasJuniperAscendant.java index 2794e7fc06e..c57b511bc90 100644 --- a/Mage.Sets/src/mage/cards/t/TadeasJuniperAscendant.java +++ b/Mage.Sets/src/mage/cards/t/TadeasJuniperAscendant.java @@ -110,7 +110,7 @@ class TadeasJuniperAscendantEvasionEffect extends RestrictionEffect { TadeasJuniperAscendantEvasionEffect(TargetPointer targetPointer) { super(Duration.EndOfCombat); - this.targetPointer = targetPointer; + this.setTargetPointer(targetPointer); staticText = "and can't be blocked by creatures with greater power"; } diff --git a/Mage.Sets/src/mage/cards/t/TenthDistrictHero.java b/Mage.Sets/src/mage/cards/t/TenthDistrictHero.java index a5e4a8a6205..bc6d8af9fe0 100644 --- a/Mage.Sets/src/mage/cards/t/TenthDistrictHero.java +++ b/Mage.Sets/src/mage/cards/t/TenthDistrictHero.java @@ -83,6 +83,7 @@ class TenthDistrictHeroEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); Permanent permanent = source.getSourcePermanentIfItStillExists(game); if (permanent == null || !permanent.hasSubtype(SubType.DETECTIVE, game)) { discard(); diff --git a/Mage.Sets/src/mage/cards/t/Terraformer.java b/Mage.Sets/src/mage/cards/t/Terraformer.java index a10fd01ad7c..49d3c09ee84 100644 --- a/Mage.Sets/src/mage/cards/t/Terraformer.java +++ b/Mage.Sets/src/mage/cards/t/Terraformer.java @@ -98,6 +98,11 @@ class TerraformerContinuousEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_Terraformer")); + if (choice == null) { + discard(); + return; + } + switch (choice) { case FOREST: dependencyTypes.add(DependencyType.BecomeForest); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java index e7b935cee74..35bf1fe400c 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java @@ -97,7 +97,7 @@ class TezzeretCruelMachinistEffect extends OneShotEffect { .getCards(game) .stream() .map(card -> new MageObjectReference(card, game, 1)) - .collect(Collectors.toSet()), game + .collect(Collectors.toList()) )), source); player.moveCards( cardsToMove.getCards(game), Zone.BATTLEFIELD, source, game, diff --git a/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java b/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java index aabc0feaad5..0bd5b7b9af3 100644 --- a/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java +++ b/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java @@ -248,8 +248,8 @@ class AddCardSubTypeEnteringTargetEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Spell spell = game.getSpell(targetPointer.getFixedTarget(game, source).getTarget()); - MageObject target = game.getObject(targetPointer.getFixedTarget(game, source).getTarget()); + Spell spell = game.getSpell(targetPointer.getFirst(game, source)); + MageObject target = game.getObject(targetPointer.getFirst(game, source)); if (spell != null) { card = spell.getCard(); } diff --git a/Mage.Sets/src/mage/cards/t/ThranPortal.java b/Mage.Sets/src/mage/cards/t/ThranPortal.java index 68c74ea50e9..e901ca3a132 100644 --- a/Mage.Sets/src/mage/cards/t/ThranPortal.java +++ b/Mage.Sets/src/mage/cards/t/ThranPortal.java @@ -94,6 +94,11 @@ class ThranPortalManaAbilityContinousEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + discard(); + return; + } + switch (choice) { case FOREST: dependencyTypes.add(DependencyType.BecomeForest); diff --git a/Mage.Sets/src/mage/cards/t/TideShaper.java b/Mage.Sets/src/mage/cards/t/TideShaper.java index 53c7db5bb56..5d49565b16b 100644 --- a/Mage.Sets/src/mage/cards/t/TideShaper.java +++ b/Mage.Sets/src/mage/cards/t/TideShaper.java @@ -92,11 +92,10 @@ class TideShaperEffect extends BecomesBasicLandTargetEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); if (source.getSourcePermanentIfItStillExists(game) == null) { discard(); - return; } - super.init(source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/t/TreasureNabber.java b/Mage.Sets/src/mage/cards/t/TreasureNabber.java index 435627c416d..5a051a50980 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureNabber.java +++ b/Mage.Sets/src/mage/cards/t/TreasureNabber.java @@ -89,8 +89,6 @@ class TreasureNabberAbility extends TriggeredAbilityImpl { class TreasureNabberEffect extends ContinuousEffectImpl { - protected FixedTargets fixedTargets; - TreasureNabberEffect() { super(Duration.UntilEndOfYourNextTurn, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); this.staticText = "gain control of that artifact until the end of your next turn"; @@ -98,7 +96,6 @@ class TreasureNabberEffect extends ContinuousEffectImpl { private TreasureNabberEffect(final TreasureNabberEffect effect) { super(effect); - this.fixedTargets = effect.fixedTargets; } @Override @@ -116,8 +113,4 @@ class TreasureNabberEffect extends ContinuousEffectImpl { } return false; } - - public void setTargets(List targetedPermanents, Game game) { - this.fixedTargets = new FixedTargets(targetedPermanents, game); - } } diff --git a/Mage.Sets/src/mage/cards/w/WakeToSlaughter.java b/Mage.Sets/src/mage/cards/w/WakeToSlaughter.java index 8b84c6a0707..2fa47047c53 100644 --- a/Mage.Sets/src/mage/cards/w/WakeToSlaughter.java +++ b/Mage.Sets/src/mage/cards/w/WakeToSlaughter.java @@ -106,13 +106,13 @@ class WakeToSlaughterEffect extends OneShotEffect { } else { player.moveCards(card, Zone.BATTLEFIELD, source, game); - FixedTarget fixedTarget = new FixedTarget(card, game); + FixedTarget blueprintTarget = new FixedTarget(card, game); ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfGame); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); game.addEffect(effect, source); ExileTargetEffect exileEffect = new ExileTargetEffect(null, null, Zone.BATTLEFIELD); - exileEffect.setTargetPointer(fixedTarget); + exileEffect.setTargetPointer(blueprintTarget.copy()); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); game.addDelayedTriggeredAbility(delayedAbility, source); } diff --git a/Mage.Sets/src/mage/cards/w/WelcomeToTheFold.java b/Mage.Sets/src/mage/cards/w/WelcomeToTheFold.java index 80c058f4c48..6908b9bf6ce 100644 --- a/Mage.Sets/src/mage/cards/w/WelcomeToTheFold.java +++ b/Mage.Sets/src/mage/cards/w/WelcomeToTheFold.java @@ -61,6 +61,7 @@ class WelcomeToTheFoldEffect extends GainControlTargetEffect { @Override public void init(Ability source, Game game) { + super.init(source, game); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int maxToughness = 2; @@ -71,10 +72,8 @@ class WelcomeToTheFoldEffect extends GainControlTargetEffect { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null && permanent.getToughness().getValue() > maxToughness) { this.discard(); - return; } } - super.init(source, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java index 8bdb2645a64..6fea411b8e4 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java @@ -11,6 +11,7 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -91,7 +92,7 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { } getEffects().setValue(VALUEKEY_NUMBER_ATTACKERS, attackers.size()); if (setTargetPointer) { - getEffects().setTargetPointer(new FixedTargets(attackers, game)); + getEffects().setTargetPointer(new FixedTargets(new ArrayList<>(attackers), game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java index 8690b000414..0a518e03f9f 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java @@ -18,6 +18,7 @@ import mage.util.CardUtil; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; /** * @author nantuko @@ -95,7 +96,7 @@ public class ExileFromGraveCost extends CostImpl { CardUtil.getSourceName(game, source) ); if (setTargetPointer) { - source.getEffects().setTargetPointer(new FixedTargets(cardsToExile, game)); + source.getEffects().setTargetPointer(new FixedTargets(cardsToExile.getCards(game), game)); } paid = true; } diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java index 1fd59290f7b..c1456292c37 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java @@ -39,6 +39,11 @@ public interface ContinuousEffect extends Effect { boolean isInactive(Ability source, Game game); + /** + * Init ability data like ZCC or targets on first check in game cycle (ApplyEffects) + *

+ * Warning, if you setup target pointer in init then must call super.init at the end (after all choices) + */ void init(Ability source, Game game); void init(Ability source, Game game, UUID activePlayerId); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java index 015028a6ac2..e5078964d22 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java @@ -3,7 +3,6 @@ package mage.abilities.effects; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.CompoundAbility; -import mage.abilities.MageSingleton; import mage.abilities.keyword.ChangelingAbility; import mage.constants.*; import mage.filter.Filter; @@ -154,6 +153,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu this.discarded = true; } + @Override + public final void initNewTargetPointer() { + // continuous effect uses init code, so do nothing here + } + @Override public void init(Ability source, Game game) { init(source, game, game.getActivePlayerId()); diff --git a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java index b3abfdd1e2c..a594b6535fa 100644 --- a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java @@ -21,6 +21,8 @@ public abstract class EffectImpl implements Effect { protected EffectType effectType; // read related docs about static and dynamic targets in ContinuousEffectImpl.affectedObjectsSet + // warning, do not change it directly, use setTargetPointer instead + // TODO: make it private and replace all usage to getTargetPointer protected TargetPointer targetPointer = new FirstTargetPointer(); protected String staticText = ""; @@ -30,6 +32,8 @@ public abstract class EffectImpl implements Effect { public EffectImpl(Outcome outcome) { this.id = UUID.randomUUID(); this.outcome = outcome; + + initNewTargetPointer(); } protected EffectImpl(final EffectImpl effect) { @@ -48,6 +52,11 @@ public abstract class EffectImpl implements Effect { } } + /** + * Init target pointer by default (see TargetPointer for details) + */ + abstract public void initNewTargetPointer(); + @Override public UUID getId() { return id; @@ -81,7 +90,13 @@ public abstract class EffectImpl implements Effect { @Override public Effect setTargetPointer(TargetPointer targetPointer) { + if (targetPointer == null) { + // first target pointer is default + throw new IllegalArgumentException("Wrong code usage: target pointer can't be set to null: " + this); + } + this.targetPointer = targetPointer; + initNewTargetPointer(); return this; } diff --git a/Mage/src/main/java/mage/abilities/effects/OneShotEffect.java b/Mage/src/main/java/mage/abilities/effects/OneShotEffect.java index 927c5941f14..8cbe2fd5aff 100644 --- a/Mage/src/main/java/mage/abilities/effects/OneShotEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/OneShotEffect.java @@ -4,6 +4,7 @@ package mage.abilities.effects; import mage.constants.EffectType; import mage.constants.Outcome; +import mage.target.targetpointer.TargetPointer; /** * @author BetaSteward_at_googlemail.com @@ -15,6 +16,12 @@ public abstract class OneShotEffect extends EffectImpl { this.effectType = EffectType.ONESHOT; } + @Override + public final void initNewTargetPointer() { + // one short effects don't use init logic + this.targetPointer.setInitialized(); + } + protected OneShotEffect(final OneShotEffect effect) { super(effect); } @@ -24,4 +31,10 @@ public abstract class OneShotEffect extends EffectImpl { super.setText(staticText); return this; } + + @Override + public Effect setTargetPointer(TargetPointer targetPointer) { + super.setTargetPointer(targetPointer); + return this; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 44c1530d9be..b5ec85ad1d1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -25,6 +25,7 @@ import mage.util.functions.CopyTokenFunction; import mage.util.functions.EmptyCopyApplier; import java.util.*; +import java.util.stream.Collectors; /** * @author LevelX2 @@ -418,7 +419,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } else { effect = new SacrificeTargetEffect("sacrifice the token copies", source.getControllerId()); } - effect.setTargetPointer(new FixedTargets(addedTokenPermanents, game)); + effect.setTargetPointer(new FixedTargets(new ArrayList<>(addedTokenPermanents), game)); DelayedTriggeredAbility exileAbility; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java index 323e3577ca4..6f5127e1754 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java @@ -46,8 +46,7 @@ public class DetainAllEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers("Detained permanent: " + permanent.getName()); } - FixedTarget fixedTarget = new FixedTarget(permanent, game); - detainedObjects.add(fixedTarget); + detainedObjects.add(new FixedTarget(permanent, game)); } game.addEffect(new DetainAllRestrictionEffect(detainedObjects), source); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileReturnBattlefieldNextEndStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileReturnBattlefieldNextEndStepTargetEffect.java index d1513b2090f..9ea93adf090 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileReturnBattlefieldNextEndStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileReturnBattlefieldNextEndStepTargetEffect.java @@ -13,6 +13,7 @@ import mage.players.Player; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; @@ -74,7 +75,7 @@ public class ExileReturnBattlefieldNextEndStepTargetEffect extends OneShotEffect Effect effect = yourControl ? new ReturnToBattlefieldUnderYourControlTargetEffect(exiledOnly) : new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, exiledOnly); - effect.setTargetPointer(new FixedTargets(new CardsImpl(toExile), game)); + effect.setTargetPointer(new FixedTargets(toExile, game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEffect.java index c94d2b5f89e..31f2aec2472 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEffect.java @@ -1,5 +1,6 @@ package mage.abilities.effects.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; @@ -14,7 +15,9 @@ import mage.players.Player; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.ArrayList; import java.util.Set; +import java.util.stream.Collectors; public class ExileTopXMayPlayUntilEffect extends OneShotEffect { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java index e8ae3485aff..967cb6ccc63 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java @@ -42,6 +42,7 @@ public class PreventAllDamageFromChosenSourceToYouEffect extends PreventionEffec @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); // be sure to note the target source's zcc, etc, if able. if (targetSource.getFirstTarget() != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java index a2faf91e017..2f254477516 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java @@ -47,6 +47,7 @@ public class PreventDamageBySourceEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java index 789a675fbc4..f3f876bb433 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java @@ -43,6 +43,7 @@ public class PreventNextDamageFromChosenSourceToTargetEffect extends PreventionE @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java index b8468ab444a..03ff1e1396a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java @@ -43,6 +43,7 @@ public class PreventNextDamageFromChosenSourceToYouEffect extends PreventionEffe @Override public void init(Ability source, Game game) { + super.init(source, game); this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java index 1f7e6ae523e..5ded6ad025f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java @@ -27,7 +27,7 @@ public class ReturnToHandChosenControlledPermanentEffect extends ReturnToHandCho @Override public boolean apply(Game game, Ability source) { - this.targetPointer = new FixedTarget(source.getControllerId()); + this.setTargetPointer(new FixedTarget(source.getControllerId())); return super.apply(game, source); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeControllerEffect.java index 7f10dc889ab..3a475337074 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeControllerEffect.java @@ -31,7 +31,7 @@ public class SacrificeControllerEffect extends SacrificeEffect { @Override public boolean apply(Game game, Ability source) { - this.targetPointer = new FixedTarget(source.getControllerId()); + this.setTargetPointer(new FixedTarget(source.getControllerId())); return super.apply(game, source); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java index 81b044ef6aa..61b88863b46 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java @@ -13,10 +13,7 @@ import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; /** diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorSourceEffect.java index 2bd064558e1..671f2901d4e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorSourceEffect.java @@ -71,8 +71,11 @@ public class BecomesColorSourceEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { + discard(); return; } if (setColor == null) { @@ -86,7 +89,6 @@ public class BecomesColorSourceEffect extends ContinuousEffectImpl { game.informPlayers(controller.getLogName() + " has chosen the color: " + setColor.toString()); } } - super.init(source, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java index ab4f389b6a8..00461f7d8e8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java @@ -50,8 +50,11 @@ public class BecomesColorTargetEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); + Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { + discard(); return; } if (setColor == null) { @@ -65,8 +68,6 @@ public class BecomesColorTargetEffect extends ContinuousEffectImpl { game.informPlayers(controller.getLogName() + " has chosen the color: " + setColor.toString()); } } - - super.init(source, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java index 713b5aaf15c..b4f342fa4d0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java @@ -65,7 +65,7 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game)); } } - super.init(source, game); // inits the target pointer so call it after setting the targetPointer + super.init(source, game); // must call at the end due target pointer setup } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java index 1df94bc0e22..7181b5d5b87 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java @@ -73,6 +73,8 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { + super.init(source, game); + Permanent permanent1 = null; Permanent permanent2 = null; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index d07c2b053d1..88ba35a258b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -77,13 +77,13 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { - super.init(source, game); if (affectedObjectsSet) { Permanent equipment = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game.getState().getZoneChangeCounter(equipment.getAttachedTo()))); } } + super.init(source, game); // must call at the end due target pointer setup } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java index 8cfe2280bbd..13b1ea96674 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java @@ -60,13 +60,13 @@ public class GainAbilityWithAttachmentEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { - super.init(source, game); if (affectedObjectsSet) { Permanent equipment = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game.getState().getZoneChangeCounter(equipment.getAttachedTo()))); } } + super.init(source, game); // must call at the end due target pointer setup } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java index 008f9393378..ff4fdd8e102 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/BolsterEffect.java @@ -94,12 +94,12 @@ public class BolsterEffect extends OneShotEffect { return false; } Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(amount.calculate(game, source, this))); - FixedTarget fixedTarget = new FixedTarget(selectedCreature, game); - effect.setTargetPointer(fixedTarget); + FixedTarget blueprintTarget = new FixedTarget(selectedCreature, game); + effect.setTargetPointer(blueprintTarget.copy()); effect.apply(game, source); if (!additionalEffects.isEmpty()) { for (Effect additionalEffect : additionalEffects) { - additionalEffect.setTargetPointer(fixedTarget); + additionalEffect.setTargetPointer(blueprintTarget.copy()); if (additionalEffect instanceof OneShotEffect) { additionalEffect.apply(game, source); } else { diff --git a/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java b/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java index 88094d29882..84a151a1c53 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java @@ -104,12 +104,12 @@ class AwakenEffect extends OneShotEffect { } } if (targetId != null) { - FixedTarget fixedTarget = new FixedTarget(targetId, game); + FixedTarget blueprintTarget = new FixedTarget(targetId, game); ContinuousEffect continuousEffect = new BecomesCreatureTargetEffect(new AwakenElementalToken(), false, true, Duration.Custom); - continuousEffect.setTargetPointer(fixedTarget); + continuousEffect.setTargetPointer(blueprintTarget.copy()); game.addEffect(continuousEffect, source); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(awakenValue)); - effect.setTargetPointer(fixedTarget); + effect.setTargetPointer(blueprintTarget.copy()); effect.apply(game, source); } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java b/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java index fe3d3b6f35c..18f9f20d736 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java @@ -89,7 +89,7 @@ class MyriadEffect extends OneShotEffect { } if (!tokens.isEmpty()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTargets(tokens, game)); + exileEffect.setTargetPointer(new FixedTargets(new ArrayList<>(tokens), game)); game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); } return true; diff --git a/Mage/src/main/java/mage/game/command/emblems/DackFaydenEmblem.java b/Mage/src/main/java/mage/game/command/emblems/DackFaydenEmblem.java index 03b8936e70b..f56faea246d 100644 --- a/Mage/src/main/java/mage/game/command/emblems/DackFaydenEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/DackFaydenEmblem.java @@ -118,7 +118,7 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl { class DackFaydenEmblemEffect extends ContinuousEffectImpl { - protected FixedTargets fixedTargets; + protected FixedTargets fixedTargets = new FixedTargets(new ArrayList<>()); DackFaydenEmblemEffect() { super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); @@ -127,7 +127,7 @@ class DackFaydenEmblemEffect extends ContinuousEffectImpl { DackFaydenEmblemEffect(final DackFaydenEmblemEffect effect) { super(effect); - this.fixedTargets = effect.fixedTargets; + this.fixedTargets = effect.fixedTargets.copy(); } @Override @@ -147,6 +147,6 @@ class DackFaydenEmblemEffect extends ContinuousEffectImpl { } public void setTargets(List targetedPermanents, Game game) { - this.fixedTargets = new FixedTargets(targetedPermanents, game); + this.fixedTargets = new FixedTargets(new ArrayList<>(targetedPermanents), game); } } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 89e6df93bdf..2db01f66da4 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -812,4 +812,9 @@ public class GameEvent implements Serializable { protected void setSourceId(UUID sourceId) { this.sourceId = sourceId; } + + @Override + public String toString() { + return this.type.toString(); + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java index 69863e89027..a9e90993042 100644 --- a/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/EachTargetPointer.java @@ -15,11 +15,7 @@ import java.util.stream.Collectors; public class EachTargetPointer extends TargetPointerImpl { - private Map zoneChangeCounter = new HashMap<>(); - - public static EachTargetPointer getInstance() { - return new EachTargetPointer(); - } + private final Map zoneChangeCounter = new HashMap<>(); public EachTargetPointer() { super(); @@ -27,15 +23,16 @@ public class EachTargetPointer extends TargetPointerImpl { protected EachTargetPointer(final EachTargetPointer targetPointer) { super(targetPointer); - - this.zoneChangeCounter = new HashMap<>(); - for (Map.Entry entry : targetPointer.zoneChangeCounter.entrySet()) { - this.zoneChangeCounter.put(entry.getKey(), entry.getValue()); - } + this.zoneChangeCounter.putAll(targetPointer.zoneChangeCounter); } @Override public void init(Game game, Ability source) { + if (isInitialized()) { + return; + } + this.setInitialized(); + if (!source.getTargets().isEmpty()) { for (UUID target : source .getTargets() @@ -99,17 +96,6 @@ public class EachTargetPointer extends TargetPointerImpl { return new EachTargetPointer(this); } - @Override - public FixedTarget getFixedTarget(Game game, Ability source) { - this.init(game, source); - UUID firstId = getFirst(game, source); - if (firstId != null) { - return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); - } - return null; - - } - @Override public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { UUID targetId = source.getFirstTarget(); diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java index 70416666fed..a9004ba9aa2 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java @@ -16,10 +16,11 @@ import java.util.UUID; public class FixedTarget extends TargetPointerImpl { private final UUID targetId; - private int zoneChangeCounter; - private boolean initialized; + private int zoneChangeCounter = 0; /** + * Dynamic ZCC (not recommended) + *

* Use this best only to target to a player or spells on the stack. Try to * avoid this method to set the target to a specific card or permanent if * possible. Because the zoneChangeCounter is not set immediately, it can be @@ -32,7 +33,6 @@ public class FixedTarget extends TargetPointerImpl { public FixedTarget(UUID target) { super(); this.targetId = target; - this.initialized = false; } public FixedTarget(MageObjectReference mor) { @@ -40,7 +40,9 @@ public class FixedTarget extends TargetPointerImpl { } /** - * Target counter is immediatly initialised with current zoneChangeCounter + * Static ZCC + *

+ * Target counter is immediately initialised with current zoneChangeCounter * value from the GameState Sets fixed the currect zoneChangeCounter * * @param card used to get the objectId @@ -50,10 +52,13 @@ public class FixedTarget extends TargetPointerImpl { super(); this.targetId = card.getId(); this.zoneChangeCounter = card.getZoneChangeCounter(game); - this.initialized = true; + + this.setInitialized(); // no need dynamic init } /** + * Static ZCC + *

* Target counter is immediately initialized with current zoneChangeCounter * value from the given permanent * @@ -65,6 +70,8 @@ public class FixedTarget extends TargetPointerImpl { } /** + * Static ZCC + *

* Use this if you already want to fix the target object to the known zone * now (otherwise the zone will be set if the ability triggers or not at * all) If not initialized, the object of the current zone then will be @@ -76,11 +83,14 @@ public class FixedTarget extends TargetPointerImpl { public FixedTarget(UUID targetId, int zoneChangeCounter) { super(); this.targetId = targetId; - this.initialized = true; this.zoneChangeCounter = zoneChangeCounter; + + this.setInitialized(); // no need dynamic init } /** + * Static ZCC + *

* Use this to set the target to exactly the zone the target is currently in * * @param targetId @@ -89,8 +99,9 @@ public class FixedTarget extends TargetPointerImpl { public FixedTarget(UUID targetId, Game game) { super(); this.targetId = targetId; - this.initialized = true; this.zoneChangeCounter = game.getState().getZoneChangeCounter(targetId); + + this.setInitialized(); // no need dynamic init } protected FixedTarget(final FixedTarget targetPointer) { @@ -98,15 +109,16 @@ public class FixedTarget extends TargetPointerImpl { this.targetId = targetPointer.targetId; this.zoneChangeCounter = targetPointer.zoneChangeCounter; - this.initialized = targetPointer.initialized; } @Override public void init(Game game, Ability source) { - if (!initialized) { - initialized = true; - this.zoneChangeCounter = game.getState().getZoneChangeCounter(targetId); + if (isInitialized()) { + return; } + setInitialized(); + + this.zoneChangeCounter = game.getState().getZoneChangeCounter(targetId); } /** @@ -161,12 +173,6 @@ public class FixedTarget extends TargetPointerImpl { return zoneChangeCounter; } - @Override - public FixedTarget getFixedTarget(Game game, Ability source) { - init(game, source); - return this; - } - @Override public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { init(game, source); diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index 78d26932f87..af3d80dbb31 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -14,80 +14,61 @@ import java.util.*; import java.util.stream.Collectors; /** + * Targets list with static ZCC + * * @author LevelX2 */ public class FixedTargets extends TargetPointerImpl { final ArrayList targets = new ArrayList<>(); - final ArrayList targetsNotInitialized = new ArrayList<>(); - private boolean initialized; - - public FixedTargets(UUID targetId) { - super(); - - targetsNotInitialized.add(targetId); - this.initialized = false; + public FixedTargets(List objects, Game game) { + this(objects + .stream() + .map(o -> new MageObjectReference(o.getId(), game)) + .collect(Collectors.toList())); } - public FixedTargets(Cards cards, Game game) { - super(); - if (cards != null) { - for (UUID targetId : cards) { - MageObjectReference mor = new MageObjectReference(targetId, game); - targets.add(mor); - } - } - this.initialized = true; + public FixedTargets(Set objects, Game game) { + this(objects + .stream() + .map(o -> new MageObjectReference(o.getId(), game)) + .collect(Collectors.toList())); + } + + public FixedTargets(Cards objects, Game game) { + this(objects.getCards(game) + .stream() + .map(o -> new MageObjectReference(o.getId(), game)) + .collect(Collectors.toList())); } public FixedTargets(Token token, Game game) { - this(token.getLastAddedTokenIds().stream().map(game::getPermanent).collect(Collectors.toList()), game); + this(token.getLastAddedTokenIds() + .stream() + .map(game::getPermanent) + .collect(Collectors.toList()), game); } - public FixedTargets(List permanents, Game game) { + public FixedTargets(List morList) { super(); - - for (Permanent permanent : permanents) { - MageObjectReference mor = new MageObjectReference(permanent.getId(), permanent.getZoneChangeCounter(game), game); - targets.add(mor); - } - this.initialized = true; + targets.addAll(morList); + this.setInitialized(); // no need dynamic init } - public FixedTargets(Set cards, Game game) { - super(); - - for (Card card : cards) { - MageObjectReference mor = new MageObjectReference(card.getId(), card.getZoneChangeCounter(game), game); - targets.add(mor); - } - this.initialized = true; - } - - public FixedTargets(Collection morSet, Game game) { - super(); - - targets.addAll(morSet); - this.initialized = true; - } - - private FixedTargets(final FixedTargets targetPointer) { - super(targetPointer); - - this.targets.addAll(targetPointer.targets); - this.targetsNotInitialized.addAll(targetPointer.targetsNotInitialized); - this.initialized = targetPointer.initialized; + private FixedTargets(final FixedTargets pointer) { + super(pointer); + this.targets.addAll(pointer.targets); } @Override public void init(Game game, Ability source) { - if (!initialized) { - initialized = true; - for (UUID targetId : targetsNotInitialized) { - targets.add(new MageObjectReference(targetId, game.getState().getZoneChangeCounter(targetId), game)); - } + if (isInitialized()) { + return; } + + // impossible use case + throw new IllegalArgumentException("Wrong code usage: FixedTargets support only static ZCC, you can't get here"); } @Override @@ -118,23 +99,6 @@ public class FixedTargets extends TargetPointerImpl { return new FixedTargets(this); } - /** - * Returns a fixed target for (and only) the first taget - * - * @param game - * @param source - * @return - */ - @Override - public FixedTarget getFixedTarget(Game game, Ability source) { - this.init(game, source); - UUID firstId = getFirst(game, source); - if (firstId != null) { - return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); - } - return null; - } - @Override public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { UUID targetId = null; @@ -143,8 +107,6 @@ public class FixedTargets extends TargetPointerImpl { MageObjectReference mor = targets.get(0); targetId = mor.getSourceId(); zoneChangeCounter = mor.getZoneChangeCounter(); - } else if (!targetsNotInitialized.isEmpty()) { - targetId = targetsNotInitialized.get(0); } if (targetId != null) { Permanent permanent = game.getPermanent(targetId); diff --git a/Mage/src/main/java/mage/target/targetpointer/NthTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/NthTargetPointer.java index a4f2726e404..88d2ea32320 100644 --- a/Mage/src/main/java/mage/target/targetpointer/NthTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/NthTargetPointer.java @@ -1,6 +1,6 @@ package mage.target.targetpointer; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.cards.Card; import mage.constants.Zone; @@ -15,154 +15,148 @@ import java.util.*; */ public abstract class NthTargetPointer extends TargetPointerImpl { - private static final Map emptyZoneChangeCounter = Collections.unmodifiableMap(new HashMap<>(0)); private static final List emptyTargets = Collections.unmodifiableList(new ArrayList<>(0)); - private Map zoneChangeCounter; - private final int targetNumber; + // TODO: rework to list of MageObjectReference instead zcc + private final Map zoneChangeCounter = new HashMap<>(); + private final int targetIndex; // zero-based target numbers (1 -> 0, 2 -> 1, 3 -> 2, etc) public NthTargetPointer(int targetNumber) { super(); - this.targetNumber = targetNumber; + this.targetIndex = targetNumber - 1; } protected NthTargetPointer(final NthTargetPointer nthTargetPointer) { super(nthTargetPointer); - this.targetNumber = nthTargetPointer.targetNumber; - - if (nthTargetPointer.zoneChangeCounter != null) { - this.zoneChangeCounter = new HashMap<>(nthTargetPointer.zoneChangeCounter.size()); - for (Map.Entry entry : nthTargetPointer.zoneChangeCounter.entrySet()) { - addToZoneChangeCounter(entry.getKey(), entry.getValue()); - } - } + this.targetIndex = nthTargetPointer.targetIndex; + this.zoneChangeCounter.putAll(nthTargetPointer.zoneChangeCounter); } @Override public void init(Game game, Ability source) { - if (source.getTargets().size() < targetNumber) { + if (isInitialized()) { + return; + } + this.setInitialized(); + + if (source.getTargets().size() <= this.targetIndex) { + wrongTargetsUsage(source); return; } - for (UUID target : source.getTargets().get(targetIndex()).getTargets()) { + for (UUID target : source.getTargets().get(this.targetIndex).getTargets()) { Card card = game.getCard(target); if (card != null) { - addToZoneChangeCounter(target, card.getZoneChangeCounter(game)); + this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); } } } + private void wrongTargetsUsage(Ability source) { + if (this.targetIndex > 0) { + // first target pointer is default, so must be ignored + throw new IllegalStateException("Wrong code usage: source ability miss targets setup for target pointer - " + + this.getClass().getSimpleName() + " - " + source.getClass().getSimpleName() + " - " + source); + } + } + @Override public List getTargets(Game game, Ability source) { - if (source.getTargets().size() < targetNumber) { + // can be used before effect's init (example: checking spell targets on stack before resolve like HeroicAbility) + + if (source.getTargets().size() <= this.targetIndex) { + wrongTargetsUsage(source); return emptyTargets; } - List targetIds = source.getTargets().get(targetIndex()).getTargets(); - List finalTargetIds = new ArrayList<>(targetIds.size()); - - for (UUID targetId : targetIds) { - Card card = game.getCard(targetId); - if (card != null - && getZoneChangeCounter().containsKey(targetId) - && card.getZoneChangeCounter(game) != getZoneChangeCounter().get(targetId)) { - // But no longer if new permanent is already on the battlefield - Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent == null || permanent.getZoneChangeCounter(game) != getZoneChangeCounter().get(targetId)) { - continue; - } + List res = new ArrayList<>(); + for (UUID targetId : source.getTargets().get(this.targetIndex).getTargets()) { + if (!isOutdatedTarget(game, targetId)) { + res.add(targetId); } - - finalTargetIds.add(targetId); } - return finalTargetIds; + return res; + } + + private boolean isOutdatedTarget(Game game, UUID targetId) { + int needZcc = this.zoneChangeCounter.getOrDefault(targetId, 0); + if (needZcc == 0) { + // any zcc (target not init yet here) + return false; + } + + // card + Card card = game.getCard(targetId); + if (card != null && card.getZoneChangeCounter(game) == needZcc) { + return false; + } + + // permanent + Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); + if (permanent != null && permanent.getZoneChangeCounter(game) == needZcc) { + return false; + } + + // TODO: if no bug reports with die triggers and new code then remove it, 2024-02-18 + // if you catch bugs then add code like if permanent.getZoneChangeCounter(game) == needZcc + 1 then return false + // old comments: + // Because if dies trigger has to trigger as permanent has already moved zone, we have to check if target + // was on the battlefield immed. before, but no longer if new permanent is already on the battlefield + + // outdated + return true; } @Override public UUID getFirst(Game game, Ability source) { - if (source.getTargets().size() < targetNumber) { + if (source.getTargets().size() <= this.targetIndex) { + wrongTargetsUsage(source); return null; } - UUID targetId = source.getTargets().get(targetIndex()).getFirstTarget(); - if (getZoneChangeCounter().containsKey(targetId)) { - Card card = game.getCard(targetId); - if (card != null && getZoneChangeCounter().containsKey(targetId) - && card.getZoneChangeCounter(game) != getZoneChangeCounter().get(targetId)) { - - // Because if dies trigger has to trigger as permanent has already moved zone, we have to check if target was on the battlefield immed. before - // but no longer if new permanent is already on the battlefield - Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent == null || permanent.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { - return null; - } - } + UUID targetId = source.getTargets().get(this.targetIndex).getFirstTarget(); + if (isOutdatedTarget(game, targetId)) { + return null; } + return targetId; - - } - - @Override - public FixedTarget getFixedTarget(Game game, Ability source) { - this.init(game, source); - UUID firstId = getFirst(game, source); - if (firstId != null) { - return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); - } - - return null; } @Override public Permanent getFirstTargetPermanentOrLKI(Game game, Ability source) { - if (source.getTargets().size() < targetNumber) { + if (source.getTargets().size() < this.targetIndex) { + wrongTargetsUsage(source); return null; } + UUID targetId = source.getTargets().get(this.targetIndex).getFirstTarget(); - Permanent permanent; - UUID targetId = source.getTargets().get(targetIndex()).getFirstTarget(); - - if (getZoneChangeCounter().containsKey(targetId)) { - permanent = game.getPermanent(targetId); - if (permanent != null && permanent.getZoneChangeCounter(game) == getZoneChangeCounter().get(targetId)) { - return permanent; - } - MageObject mageObject = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD, getZoneChangeCounter().get(targetId)); - if (mageObject instanceof Permanent) { - return (Permanent) mageObject; - } - + if (this.zoneChangeCounter.containsKey(targetId)) { + // need static zcc + MageObjectReference needRef = new MageObjectReference(targetId, this.zoneChangeCounter.getOrDefault(targetId, 0), game); + return game.getPermanentOrLKIBattlefield(needRef); } else { - permanent = game.getPermanent(targetId); + // need any zcc + // TODO: must research, is it used at all?! Init code must fill all static zcc data before go here + Permanent permanent = game.getPermanent(targetId); if (permanent == null) { permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); } + return permanent; } - return permanent; } @Override public String describeTargets(Targets targets, String defaultDescription) { - return targets.size() < targetNumber ? defaultDescription : targets.get(targetIndex()).getDescription(); + if (targets.size() <= this.targetIndex) { + // TODO: need research, is it used for non setup targets ?! + return defaultDescription; + } else { + return targets.get(this.targetIndex).getDescription(); + } } @Override public boolean isPlural(Targets targets) { - return targets.size() > targetIndex() && targets.get(targetIndex()).getMaxNumberOfTargets() > 1; - } - - private int targetIndex() { - return targetNumber - 1; - } - - private Map getZoneChangeCounter() { - return zoneChangeCounter != null ? zoneChangeCounter : emptyZoneChangeCounter; - } - - private void addToZoneChangeCounter(UUID key, Integer value) { - if (zoneChangeCounter == null) { - zoneChangeCounter = new HashMap<>(); - } - getZoneChangeCounter().put(key, value); + return targets.size() > this.targetIndex && targets.get(this.targetIndex).getMaxNumberOfTargets() > 1; } } diff --git a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java index 52609e4b828..da9a5446350 100644 --- a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java @@ -12,15 +12,30 @@ import java.util.UUID; public interface TargetPointer extends Serializable, Copyable { + /** + * Init dynamic targets (must save current targets zcc to fizzle it later on outdated targets) + * - one shot effects: no needs to init + * - continues effects: must use init logic + */ void init(Game game, Ability source); + boolean isInitialized(); + + void setInitialized(); + List getTargets(Game game, Ability source); + /** + * Return first actual target id (null on outdated targets) + */ UUID getFirst(Game game, Ability source); - TargetPointer copy(); + /** + * Return first actual target data (null on outdated targets) + */ + FixedTarget getFirstAsFixedTarget(Game game, Ability source); - FixedTarget getFixedTarget(Game game, Ability source); + TargetPointer copy(); /** * Retrieves the permanent according the first targetId and diff --git a/Mage/src/main/java/mage/target/targetpointer/TargetPointerImpl.java b/Mage/src/main/java/mage/target/targetpointer/TargetPointerImpl.java index 87ee02cc782..9d991af23e9 100644 --- a/Mage/src/main/java/mage/target/targetpointer/TargetPointerImpl.java +++ b/Mage/src/main/java/mage/target/targetpointer/TargetPointerImpl.java @@ -1,7 +1,11 @@ package mage.target.targetpointer; +import mage.abilities.Ability; +import mage.game.Game; + import java.util.HashMap; import java.util.Map; +import java.util.UUID; /** * @author JayDi85 @@ -11,6 +15,8 @@ public abstract class TargetPointerImpl implements TargetPointer { // Store custom data here. Use it to keep unique values for ability instances on stack (example: Gruul Ragebeast) private Map data; + private boolean initialized = false; + public TargetPointerImpl() { super(); } @@ -21,6 +27,17 @@ public abstract class TargetPointerImpl implements TargetPointer { this.data = new HashMap<>(); this.data.putAll(targetPointer.data); } + this.initialized = targetPointer.initialized; + } + + @Override + public boolean isInitialized() { + return this.initialized; + } + + @Override + public void setInitialized() { + this.initialized = true; } @Override @@ -39,4 +56,14 @@ public abstract class TargetPointerImpl implements TargetPointer { data.put(key, value); return this; } + + @Override + public final FixedTarget getFirstAsFixedTarget(Game game, Ability source) { + UUID firstId = this.getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + + return null; + } }