From 1dbba3f7a9318698bbea0c6b9738c150b77ce8b9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 16 Apr 2016 02:43:52 +0200 Subject: [PATCH] * Soulbond - Reworked to two triggerd abilities (fixes #1882). --- Mage.Common/src/mage/view/CardView.java | 48 ++-- .../sets/avacynrestored/DeadeyeNavigator.java | 2 +- .../sets/avacynrestored/DiregrafEscort.java | 2 +- .../sets/avacynrestored/DruidsFamiliar.java | 2 +- .../sets/avacynrestored/ElgaudShieldmate.java | 2 +- .../avacynrestored/FloweringLumberknot.java | 30 +- .../avacynrestored/GalvanicAlchemist.java | 2 +- .../sets/avacynrestored/GeistTrappers.java | 2 +- .../sets/avacynrestored/HanweirLancer.java | 2 +- .../sets/avacynrestored/JointAssault.java | 23 +- .../sets/avacynrestored/LightningMauler.java | 2 +- .../sets/avacynrestored/NearheathPilgrim.java | 2 +- .../avacynrestored/NightshadePeddler.java | 2 +- .../sets/avacynrestored/PathbreakerWurm.java | 2 +- .../avacynrestored/SilverbladePaladin.java | 2 +- .../avacynrestored/SpectralGateguards.java | 2 +- .../mage/sets/avacynrestored/SternMentor.java | 2 +- .../mage/sets/avacynrestored/Stonewright.java | 2 +- .../sets/avacynrestored/TandemLookout.java | 2 +- .../sets/avacynrestored/TrustedForcemage.java | 2 +- .../mage/sets/avacynrestored/Wingcrafter.java | 2 +- .../avacynrestored/WolfirSilverheart.java | 2 +- .../mage/sets/magic2012/PhantasmalBear.java | 5 +- .../keywords/SoulbondKeywordTest.java | 43 +-- .../common/continuous/BoostPairedEffect.java | 9 +- .../continuous/GainAbilityPairedEffect.java | 26 +- .../abilities/keyword/SoulbondAbility.java | 266 ++++++++++++++++-- Mage/src/main/java/mage/game/GameImpl.java | 6 +- .../java/mage/game/permanent/Permanent.java | 4 +- .../mage/game/permanent/PermanentImpl.java | 14 +- .../main/java/mage/players/PlayerImpl.java | 2 +- .../mage/watchers/common/SoulbondWatcher.java | 145 ---------- 32 files changed, 374 insertions(+), 285 deletions(-) delete mode 100644 Mage/src/main/java/mage/watchers/common/SoulbondWatcher.java diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 5be823cbe31..885d75593fe 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -186,16 +186,14 @@ public class CardView extends SimpleCardView { this.rules.add("You may cast this card as a 2/2 face-down creature, with no text," + " no name, no subtypes, and no mana cost by paying {3} rather than paying its mana cost."); return; + } else if (card instanceof Permanent) { + this.power = Integer.toString(card.getPower().getValue()); + this.toughness = Integer.toString(card.getToughness().getValue()); + this.cardTypes = card.getCardType(); + this.faceDown = ((Permanent) card).isFaceDown(game); } else { - if (card instanceof Permanent) { - this.power = Integer.toString(card.getPower().getValue()); - this.toughness = Integer.toString(card.getToughness().getValue()); - this.cardTypes = card.getCardType(); - this.faceDown = ((Permanent) card).isFaceDown(game); - } else { - // this.hideInfo = true; - return; - } + // this.hideInfo = true; + return; } } @@ -203,18 +201,16 @@ public class CardView extends SimpleCardView { if (card.isSplitCard()) { splitCard = (SplitCard) card; rotate = true; - } else { - if (card instanceof Spell) { - switch (((Spell) card).getSpellAbility().getSpellAbilityType()) { - case SPLIT_FUSED: - splitCard = (SplitCard) ((Spell) card).getCard(); - rotate = true; - break; - case SPLIT_LEFT: - case SPLIT_RIGHT: - rotate = true; - break; - } + } else if (card instanceof Spell) { + switch (((Spell) card).getSpellAbility().getSpellAbilityType()) { + case SPLIT_FUSED: + splitCard = (SplitCard) ((Spell) card).getCard(); + rotate = true; + break; + case SPLIT_LEFT: + case SPLIT_RIGHT: + rotate = true; + break; } } if (splitCard != null) { @@ -241,7 +237,7 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.PERMANENT; Permanent permanent = (Permanent) card; this.loyalty = Integer.toString(permanent.getCounters().getCount(CounterType.LOYALTY)); - this.pairedCard = permanent.getPairedCard(); + this.pairedCard = permanent.getPairedCard() != null ? permanent.getPairedCard().getSourceId() : null; if (!permanent.getControllerId().equals(permanent.getOwnerId())) { controlledByOwner = false; } @@ -421,12 +417,10 @@ public class CardView extends SimpleCardView { if (card != null) { if (card instanceof Permanent) { this.mageObjectType = MageObjectType.PERMANENT; + } else if (card.isCopy()) { + this.mageObjectType = MageObjectType.COPY_CARD; } else { - if (card.isCopy()) { - this.mageObjectType = MageObjectType.COPY_CARD; - } else { - this.mageObjectType = MageObjectType.CARD; - } + this.mageObjectType = MageObjectType.CARD; } if (card instanceof PermanentToken) { this.mageObjectType = MageObjectType.TOKEN; diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java b/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java index 88addcc3e72..f5b882ffb0c 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DeadeyeNavigator.java @@ -58,7 +58,7 @@ public class DeadeyeNavigator extends CardImpl { this.toughness = new MageInt(5); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Deadeye Navigator is paired with another creature, each of those creatures has "{1}{U}: Exile this creature, then return it to the battlefield under your control." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(true), new ManaCostsImpl("{1}{U}")); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DiregrafEscort.java b/Mage.Sets/src/mage/sets/avacynrestored/DiregrafEscort.java index 8f3f2a08be5..15e7377ee4b 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DiregrafEscort.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DiregrafEscort.java @@ -64,7 +64,7 @@ public class DiregrafEscort extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Diregraf Escort is paired with another creature, both creatures have protection from Zombies. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(new ProtectionAbility(filter), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DruidsFamiliar.java b/Mage.Sets/src/mage/sets/avacynrestored/DruidsFamiliar.java index 026de8d4fbc..1e1588e616f 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DruidsFamiliar.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DruidsFamiliar.java @@ -55,7 +55,7 @@ public class DruidsFamiliar extends CardImpl { this.toughness = new MageInt(2); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Druid's Familiar is paired with another creature, each of those creatures gets +2/+2. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostPairedEffect(2, 2, ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/ElgaudShieldmate.java b/Mage.Sets/src/mage/sets/avacynrestored/ElgaudShieldmate.java index 65af29ca5c7..3ec6a556266 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/ElgaudShieldmate.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/ElgaudShieldmate.java @@ -55,7 +55,7 @@ public class ElgaudShieldmate extends CardImpl { this.toughness = new MageInt(3); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Elgaud Shieldmate is paired with another creature, both creatures have hexproof. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(HexproofAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/FloweringLumberknot.java b/Mage.Sets/src/mage/sets/avacynrestored/FloweringLumberknot.java index 397163a3caf..2a6a99d2d61 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/FloweringLumberknot.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/FloweringLumberknot.java @@ -27,21 +27,20 @@ */ package mage.sets.avacynrestored; -import mage.constants.CardType; -import mage.constants.Rarity; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.RestrictionEffect; import mage.abilities.keyword.SoulbondAbility; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.UUID; - /** * @author noxx */ @@ -83,17 +82,26 @@ class FloweringLumberknotEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { if (permanent.getId().equals(source.getSourceId())) { - if (permanent.getPairedCard() == null) { - return true; // not paired => can't attack or block + if (permanent.getPairedCard() != null) { + Permanent paired = permanent.getPairedCard().getPermanent(game); + if (paired != null) { + boolean found = false; + for (Ability ability : paired.getAbilities(game)) { + if (ability instanceof SoulbondAbility) { + found = true; + break; + } + } + if (found) { + return false;// paired => can attack or block + } + } } - Permanent paired = game.getPermanent(permanent.getPairedCard()); - if (paired != null && paired.getAbilities().contains(SoulbondAbility.getInstance())) { - return false; // paired => can attack or block - } - // can't attack or block otherwise + // can't attack or block return true; } return false; + } @Override diff --git a/Mage.Sets/src/mage/sets/avacynrestored/GalvanicAlchemist.java b/Mage.Sets/src/mage/sets/avacynrestored/GalvanicAlchemist.java index d29e9b8226d..5155169cc51 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/GalvanicAlchemist.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/GalvanicAlchemist.java @@ -58,7 +58,7 @@ public class GalvanicAlchemist extends CardImpl { this.toughness = new MageInt(4); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Galvanic Alchemist is paired with another creature, each of those creatures has "{2}{U}: Untap this creature." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new ManaCostsImpl("{2}{U}")); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/GeistTrappers.java b/Mage.Sets/src/mage/sets/avacynrestored/GeistTrappers.java index b9548a39b42..03643b1748e 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/GeistTrappers.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/GeistTrappers.java @@ -55,7 +55,7 @@ public class GeistTrappers extends CardImpl { this.toughness = new MageInt(5); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Geist Trappers is paired with another creature, both creatures have reach. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(ReachAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/HanweirLancer.java b/Mage.Sets/src/mage/sets/avacynrestored/HanweirLancer.java index 6afd6a9aba1..d7c41a478bf 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/HanweirLancer.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/HanweirLancer.java @@ -55,7 +55,7 @@ public class HanweirLancer extends CardImpl { this.toughness = new MageInt(2); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Hanweir Lancer is paired with another creature, both creatures have first strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(FirstStrikeAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/JointAssault.java b/Mage.Sets/src/mage/sets/avacynrestored/JointAssault.java index c625f1f5f24..701cb39e7ff 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/JointAssault.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/JointAssault.java @@ -27,21 +27,21 @@ */ package mage.sets.avacynrestored; -import mage.constants.CardType; -import mage.constants.Rarity; +import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; +import mage.constants.Rarity; import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import java.util.UUID; - /** * @author noxx */ @@ -51,7 +51,6 @@ public class JointAssault extends CardImpl { super(ownerId, 183, "Joint Assault", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "AVR"; - // Target creature gets +2/+2 until end of turn. If it's paired with a creature, that creature also gets +2/+2 until end of turn. this.getSpellAbility().addEffect(new JointAssaultBoostTargetEffect(2, 2, Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -69,9 +68,9 @@ public class JointAssault extends CardImpl { class JointAssaultBoostTargetEffect extends ContinuousEffectImpl { - private int power; - private int toughness; - private UUID paired; + private final int power; + private final int toughness; + private MageObjectReference paired; public JointAssaultBoostTargetEffect(int power, int toughness, Duration duration) { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); @@ -116,10 +115,10 @@ class JointAssaultBoostTargetEffect extends ContinuousEffectImpl { } if (this.paired != null) { - Permanent paired = game.getPermanent(this.paired); - if (paired != null) { - paired.addPower(power); - paired.addToughness(toughness); + Permanent pairedPermanent = this.paired.getPermanent(game); + if (pairedPermanent != null) { + pairedPermanent.addPower(power); + pairedPermanent.addToughness(toughness); affectedTargets++; } } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/LightningMauler.java b/Mage.Sets/src/mage/sets/avacynrestored/LightningMauler.java index 4cd01414af0..470d291dabe 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/LightningMauler.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/LightningMauler.java @@ -57,7 +57,7 @@ public class LightningMauler extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Lightning Mauler is paired with another creature, both creatures have haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(HasteAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/NearheathPilgrim.java b/Mage.Sets/src/mage/sets/avacynrestored/NearheathPilgrim.java index 05f5c75bd02..cb2461a1011 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/NearheathPilgrim.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/NearheathPilgrim.java @@ -55,7 +55,7 @@ public class NearheathPilgrim extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Nearheath Pilgrim is paired with another creature, both creatures have lifelink. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(LifelinkAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/NightshadePeddler.java b/Mage.Sets/src/mage/sets/avacynrestored/NightshadePeddler.java index 92b146acf25..9209cf8bf91 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/NightshadePeddler.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/NightshadePeddler.java @@ -55,7 +55,7 @@ public class NightshadePeddler extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Nightshade Peddler is paired with another creature, both creatures have deathtouch. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(DeathtouchAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/PathbreakerWurm.java b/Mage.Sets/src/mage/sets/avacynrestored/PathbreakerWurm.java index 9efa9431ae5..5a6c1b5c9cb 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/PathbreakerWurm.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/PathbreakerWurm.java @@ -54,7 +54,7 @@ public class PathbreakerWurm extends CardImpl { this.toughness = new MageInt(4); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Pathbreaker Wurm is paired with another creature, both creatures have trample. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(TrampleAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/SilverbladePaladin.java b/Mage.Sets/src/mage/sets/avacynrestored/SilverbladePaladin.java index e37cfe7e501..0a3e36f8652 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/SilverbladePaladin.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/SilverbladePaladin.java @@ -55,7 +55,7 @@ public class SilverbladePaladin extends CardImpl { this.toughness = new MageInt(2); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Silverblade Paladin is paired with another creature, both creatures have double strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(DoubleStrikeAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/SpectralGateguards.java b/Mage.Sets/src/mage/sets/avacynrestored/SpectralGateguards.java index 77efc0a7e49..e1b0e2d1eb4 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/SpectralGateguards.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/SpectralGateguards.java @@ -55,7 +55,7 @@ public class SpectralGateguards extends CardImpl { this.toughness = new MageInt(5); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Spectral Gateguards is paired with another creature, both creatures have vigilance. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(VigilanceAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/SternMentor.java b/Mage.Sets/src/mage/sets/avacynrestored/SternMentor.java index feb58ede09a..4e1006f01de 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/SternMentor.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/SternMentor.java @@ -59,7 +59,7 @@ public class SternMentor extends CardImpl { this.toughness = new MageInt(2); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Stern Mentor is paired with another creature, each of those creatures has "{T}: Target player puts the top two cards of his or her library into his or her graveyard." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(2), new TapSourceCost()); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/Stonewright.java b/Mage.Sets/src/mage/sets/avacynrestored/Stonewright.java index cfb262d43fc..ab0116c728e 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/Stonewright.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/Stonewright.java @@ -59,7 +59,7 @@ public class Stonewright extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Stonewright is paired with another creature, each of those creatures has "{R}: This creature gets +1/+0 until end of turn." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/TandemLookout.java b/Mage.Sets/src/mage/sets/avacynrestored/TandemLookout.java index a2cec673f68..4d9f26a803f 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/TandemLookout.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/TandemLookout.java @@ -57,7 +57,7 @@ public class TandemLookout extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Tandem Lookout is paired with another creature, each of those creatures has "Whenever this creature deals damage to an opponent, draw a card." Ability ability = new DealsDamageToOpponentTriggeredAbility(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/TrustedForcemage.java b/Mage.Sets/src/mage/sets/avacynrestored/TrustedForcemage.java index d7f87bea1bd..fb67db8533f 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/TrustedForcemage.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/TrustedForcemage.java @@ -54,7 +54,7 @@ public class TrustedForcemage extends CardImpl { this.toughness = new MageInt(2); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Trusted Forcemage is paired with another creature, each of those creatures gets +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostPairedEffect(1, 1, ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/Wingcrafter.java b/Mage.Sets/src/mage/sets/avacynrestored/Wingcrafter.java index 35cba6a297b..11104a704dc 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/Wingcrafter.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/Wingcrafter.java @@ -57,7 +57,7 @@ public class Wingcrafter extends CardImpl { this.toughness = new MageInt(1); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Wingcrafter is paired with another creature, both creatures have flying. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(FlyingAbility.getInstance(), ruleText))); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/WolfirSilverheart.java b/Mage.Sets/src/mage/sets/avacynrestored/WolfirSilverheart.java index 76e751edc66..63f706ce094 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/WolfirSilverheart.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/WolfirSilverheart.java @@ -54,7 +54,7 @@ public class WolfirSilverheart extends CardImpl { this.toughness = new MageInt(4); // Soulbond - this.addAbility(SoulbondAbility.getInstance()); + this.addAbility(new SoulbondAbility()); // As long as Wolfir Silverheart is paired with another creature, each of those creatures gets +4/+4. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostPairedEffect(4, 4, ruleText))); diff --git a/Mage.Sets/src/mage/sets/magic2012/PhantasmalBear.java b/Mage.Sets/src/mage/sets/magic2012/PhantasmalBear.java index d8b971a44f0..d26375c8063 100644 --- a/Mage.Sets/src/mage/sets/magic2012/PhantasmalBear.java +++ b/Mage.Sets/src/mage/sets/magic2012/PhantasmalBear.java @@ -28,12 +28,12 @@ package mage.sets.magic2012; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.common.BecomesTargetTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; /** * @@ -50,6 +50,7 @@ public class PhantasmalBear extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); + // When Phantasmal Bear becomes the target of a spell or ability, sacrifice it. this.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect())); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SoulbondKeywordTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SoulbondKeywordTest.java index 94afd71874a..f1f1d9c0717 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SoulbondKeywordTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SoulbondKeywordTest.java @@ -9,7 +9,6 @@ import mage.constants.Zone; import mage.filter.Filter; import mage.game.permanent.Permanent; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -318,9 +317,12 @@ public class SoulbondKeywordTest extends CardTestPlayerBase { */ @Test public void testRebondOnNextCreature() { - addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + // When Phantasmal Bear becomes the target of a spell or ability, sacrifice it. addCard(Zone.HAND, playerA, "Phantasmal Bear"); + // Soulbond + // As long as Trusted Forcemage is paired with another creature, each of those creatures gets +1/+1. addCard(Zone.HAND, playerA, "Trusted Forcemage"); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // 2/1 addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); @@ -329,10 +331,10 @@ public class SoulbondKeywordTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Lightning Bolt", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Elite Vanguard"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Bear"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Elite Vanguard"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phantasmal Bear"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStopAt(1, PhaseStep.END_TURN); execute(); assertPermanentCount(playerA, "Elite Vanguard", 0); @@ -347,7 +349,9 @@ public class SoulbondKeywordTest extends CardTestPlayerBase { */ @Test public void testGrantingAbility() { - addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // 2/1 + // Soulbond + // As long as Nearheath Pilgrim is paired with another creature, both creatures have lifelink. addCard(Zone.HAND, playerA, "Nearheath Pilgrim"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); @@ -391,42 +395,39 @@ public class SoulbondKeywordTest extends CardTestPlayerBase { Assert.assertEquals(trustedForcemange.getPairedCard(), null); Assert.assertEquals(eliteVanguard.getPairedCard(), null); } - + /* * Reported bug: Soulbond should use the stack, but unable to use instant speed removal since no trigger occurs - */ + */ @Test - @Ignore // Soulbond does not currently use the stack, so this test will fail until then public void testRespondToSoulboundWithRemoval() { - + // When Palinchron enters the battlefield, untap up to seven lands. // {2}{U}{U}: Return Palinchron to its owner's hand. addCard(Zone.BATTLEFIELD, playerA, "Palinchron"); // 4/5 flying - + // Soulbond // As long as Deadeye Navigator is paired with another creature, each of those creatures has "{1}{U}: Exile this creature, then return it to the battlefield under your control." addCard(Zone.HAND, playerA, "Deadeye Navigator"); // 5/5 addCard(Zone.BATTLEFIELD, playerA, "Island", 8); - addCard(Zone.BATTLEFIELD, playerB, "Doom Blade", 1); // {1}{B} instant: Destroy target non-black creature + addCard(Zone.HAND, playerB, "Doom Blade", 1); // {1}{B} instant: Destroy target non-black creature addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deadeye Navigator"); setChoice(playerA, "Yes"); - addTarget(playerA, "Palinchron"); + setChoice(playerA, "Palinchron"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Deadeye Navigator"); - - // Deadeye's ability should not be usable since was Destroyed before Soulbond trigger resolved + + // Deadeye's ability should not be usable since was destroyed before Soulbond trigger resolved activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}:"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - + Permanent palinchron = getPermanent("Palinchron", playerA); - Permanent deadeye = getPermanent("Deadeye Navigator", playerA); Assert.assertEquals(null, palinchron.getPairedCard()); // should not be paired - Assert.assertEquals(null, deadeye.getPairedCard()); // should not be paired - assertGraveyardCount(playerA, "Deadeye Navigator", 1); - assertGraveyardCount(playerB, "Doom Blade", 1); + assertGraveyardCount(playerA, "Deadeye Navigator", 1); + assertGraveyardCount(playerB, "Doom Blade", 1); assertPermanentCount(playerA, "Palinchron", 1); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostPairedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostPairedEffect.java index b9651c6e985..8cad96f84c7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostPairedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostPairedEffect.java @@ -24,16 +24,15 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.abilities.effects.common.continuous; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -68,7 +67,7 @@ public class BoostPairedEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null && permanent.getPairedCard() != null) { - Permanent paired = game.getPermanent(permanent.getPairedCard()); + Permanent paired = permanent.getPairedCard().getPermanent(game); if (paired != null) { permanent.addPower(power); permanent.addToughness(toughness); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java index 686b7f0a525..ff651cb4138 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,20 +20,20 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common.continuous; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -66,11 +66,15 @@ public class GainAbilityPairedEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null && permanent.getPairedCard() != null) { - Permanent paired = game.getPermanent(permanent.getPairedCard()); - if (paired != null) { - permanent.addAbility(ability, game); + Permanent paired = permanent.getPairedCard().getPermanent(game); + if (paired != null && paired.getPairedCard() != null && paired.getPairedCard().equals(new MageObjectReference(permanent, game))) { + permanent.addAbility(ability, source.getSourceId(), game); paired.addAbility(ability, source.getSourceId(), game, false); return true; + + } else { + // No longer the same cards as orininally paired. + permanent.setPairedCard(null); } } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java b/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java index f8150ded958..1029ffc903a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SoulbondAbility.java @@ -24,32 +24,71 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.abilities.keyword; -import java.io.ObjectStreamException; -import mage.abilities.MageSingleton; -import mage.abilities.StaticAbility; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; /** - * @author noxx + * 702.94. Soulbond + * + * 702.94a Soulbond is a keyword that represents two triggered abilities. + * “Soulbond” means “When this creature enters the battlefield, if you control + * both this creature and another creature and both are unpaired, you may pair + * this creature with another unpaired creature you control for as long as both + * remain creatures on the battlefield under your control” and “Whenever another + * creature enters the battlefield under your control, if you control both that + * creature and this one and both are unpaired, you may pair that creature with + * this creature for as long as both remain creatures on the battlefield under + * your control.” + * + * 702.94b A creature becomes “paired” with another as the result of a soulbond + * ability. Abilities may refer to a paired creature, the creature another + * creature is paired with, or whether a creature is paired. An “unpaired” + * creature is one that is not paired. + * + * 702.94c When the soulbond ability resolves, if either object that would be + * paired is no longer a creature, no longer on the battlefield, or no longer + * under the control of the player who controls the soulbond ability, neither + * object becomes paired. + * + * 702.94d A creature can be paired with only one other creature. + * + * 702.94e A paired creature becomes unpaired if any of the following occur: + * another player gains control of it or the creature it’s paired with; it or + * the creature it’s paired with stops being a creature; or it or the creature + * it’s paired with leaves the battlefield. + * + * @author LevelX2 */ -public class SoulbondAbility extends StaticAbility implements MageSingleton { +public class SoulbondAbility extends EntersBattlefieldTriggeredAbility { - private static final SoulbondAbility fINSTANCE = new SoulbondAbility(); - - private Object readResolve() throws ObjectStreamException { - return fINSTANCE; + public SoulbondAbility() { + super(new SoulboundEntersSelfEffect(), true); + this.addSubAbility(new SoulbondEntersOtherAbility()); } - public static SoulbondAbility getInstance() { - return fINSTANCE; - } - - private SoulbondAbility() { - super(Zone.BATTLEFIELD, null); + public SoulbondAbility(SoulbondAbility ability) { + super(ability); } @Override @@ -57,9 +96,200 @@ public class SoulbondAbility extends StaticAbility implements MageSingleton { return "Soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)"; } + @Override + public boolean checkInterveningIfClause(Game game) { + // if you control both this creature and another creature and both are unpaired + boolean self = false; + boolean other = false; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(getControllerId())) { + if (permanent.getCardType().contains(CardType.CREATURE)) { + if (permanent.getId().equals(getSourceId())) { + if (permanent.getControllerId().equals(getControllerId())) { + self = true; + if (other) { + return true; + } + } else { + return false; + } + } else if (permanent.getPairedCard() == null) { + other = true; + if (self) { + return true; + } + } + } + } + return false; + } + @Override public SoulbondAbility copy() { - return fINSTANCE; + return new SoulbondAbility(this); + + } + +} +// When this creature enters the battlefield, if you control both this creature and another creature and both are unpaired, you may pair +// this creature with another unpaired creature you control for as long as both remain creatures on the battlefield under your control + +class SoulboundEntersSelfEffect extends OneShotEffect { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another not paired creature you control"); + + static { + filter.add(new AnotherPredicate()); + filter.add(Predicates.not(new PairedPredicate())); + } + + public SoulboundEntersSelfEffect() { + super(Outcome.Benefit); + } + + public SoulboundEntersSelfEffect(final SoulboundEntersSelfEffect effect) { + super(effect); + } + + @Override + public SoulboundEntersSelfEffect copy() { + return new SoulboundEntersSelfEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && permanent.getCardType().contains(CardType.CREATURE)) { + Player controller = game.getPlayer(permanent.getControllerId()); + if (controller != null) { + TargetControlledPermanent target = new TargetControlledPermanent(filter); + target.setNotTarget(true); + if (target.canChoose(permanent.getId(), controller.getId(), game)) { + if (controller.choose(Outcome.Benefit, target, permanent.getId(), game)) { + Permanent chosen = game.getPermanent(target.getFirstTarget()); + if (chosen != null) { + chosen.setPairedCard(new MageObjectReference(permanent, game)); + permanent.setPairedCard(new MageObjectReference(chosen, game)); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " soulbonds " + permanent.getLogName() + " with " + chosen.getLogName()); + } + } + } + } + + } + return true; + } + return false; + } +} + +/** + * “Whenever another creature enters the battlefield under your control, if you + * control both that creature and this one and both are unpaired, you may pair + * that creature with this creature for as long as both remain creatures on the + * battlefield under your control.” + * + */ +class SoulbondEntersOtherAbility extends EntersBattlefieldAllTriggeredAbility { + + private final static FilterCreaturePermanent soulbondFilter = new FilterCreaturePermanent(); + + static { + soulbondFilter.add(Predicates.not(new PairedPredicate())); + soulbondFilter.add(new ControllerPredicate(TargetController.YOU)); + soulbondFilter.add(new AnotherPredicate()); + } + + public SoulbondEntersOtherAbility() { + super(Zone.BATTLEFIELD, new SoulboundEntersOtherEffect(), soulbondFilter, true, SetTargetPointer.PERMANENT, ""); + setRuleVisible(false); + } + + public SoulbondEntersOtherAbility(SoulbondEntersOtherAbility ability) { + super(ability); + } + + @Override + public String getRule() { + return "Soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)"; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + // if you control both this creature and another creature and both are unpaired + if (game.getBattlefield().countAll(filter, getControllerId(), game) > 0) { + Permanent sourcePermanent = game.getPermanent(getSourceId()); + if (sourcePermanent != null && sourcePermanent.getControllerId().equals(getControllerId()) && sourcePermanent.getPairedCard() == null) { + return true; + } + } + return false; + } + + @Override + public SoulbondEntersOtherAbility copy() { + return new SoulbondEntersOtherAbility(this); + } + +} + +class SoulboundEntersOtherEffect extends OneShotEffect { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another not paired creature you control"); + + static { + filter.add(new AnotherPredicate()); + filter.add(Predicates.not(new PairedPredicate())); + } + + public SoulboundEntersOtherEffect() { + super(Outcome.Benefit); + } + + public SoulboundEntersOtherEffect(final SoulboundEntersOtherEffect effect) { + super(effect); + } + + @Override + public SoulboundEntersOtherEffect copy() { + return new SoulboundEntersOtherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && permanent.getPairedCard() == null + && permanent.getCardType().contains(CardType.CREATURE)) { + Player controller = game.getPlayer(permanent.getControllerId()); + if (controller != null) { + Permanent enteringPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (enteringPermanent != null && enteringPermanent.getCardType().contains(CardType.CREATURE) && enteringPermanent.getPairedCard() == null) { + enteringPermanent.setPairedCard(new MageObjectReference(permanent, game)); + permanent.setPairedCard(new MageObjectReference(enteringPermanent, game)); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " soulbonds " + permanent.getLogName() + " with " + enteringPermanent.getLogName()); + } + } + } + + return true; + } + + return false; + } + +} + +class PairedPredicate implements Predicate { + + @Override + public boolean apply(Permanent input, Game game) { + return input.getPairedCard() != null; + } + + @Override + public String toString() { + return "Paired"; } } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index defd8d18293..4ba9f91b1e8 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -131,7 +131,6 @@ import mage.watchers.common.DamageDoneWatcher; import mage.watchers.common.MorbidWatcher; import mage.watchers.common.PlayerDamagedBySourceWatcher; import mage.watchers.common.PlayerLostLifeWatcher; -import mage.watchers.common.SoulbondWatcher; import org.apache.log4j.Logger; public abstract class GameImpl implements Game, Serializable { @@ -1007,7 +1006,6 @@ public abstract class GameImpl implements Game, Serializable { } watchers.add(new MorbidWatcher()); watchers.add(new CastSpellLastTurnWatcher()); - watchers.add(new SoulbondWatcher()); watchers.add(new PlayerLostLifeWatcher()); watchers.add(new BlockedAttackerWatcher()); watchers.add(new DamageDoneWatcher()); @@ -1687,7 +1685,7 @@ public abstract class GameImpl implements Game, Serializable { if (perm.getPairedCard() != null) { //702.93e.: ...another player gains control // ...or the creature it's paired with leaves the battlefield. - Permanent paired = getPermanent(perm.getPairedCard()); + Permanent paired = perm.getPairedCard().getPermanent(this); if (paired == null || !perm.getControllerId().equals(paired.getControllerId()) || paired.getPairedCard() == null) { perm.setPairedCard(null); if (paired != null) { @@ -1698,7 +1696,7 @@ public abstract class GameImpl implements Game, Serializable { } } else if (perm.getPairedCard() != null) { //702.93e.: ...stops being a creature - Permanent paired = getPermanent(perm.getPairedCard()); + Permanent paired = perm.getPairedCard().getPermanent(this); perm.setPairedCard(null); if (paired != null) { paired.setPairedCard(null); diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 3e0429d6acf..a7497bb4c57 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -327,14 +327,14 @@ public interface Permanent extends Card, Controllable { * * @param pairedCard */ - void setPairedCard(UUID pairedCard); + void setPairedCard(MageObjectReference pairedCard); /** * Gets paired card. Can return null. * * @return */ - UUID getPairedCard(); + MageObjectReference getPairedCard(); /** * Makes permanent paired with no other permanent. diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 7e5de9d2bc5..16a1726137c 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -116,7 +116,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected HashSet dealtDamageByThisTurn; protected UUID attachedTo; protected int attachedToZoneChangeCounter; - protected UUID pairedCard; + protected MageObjectReference pairedPermanent; protected Counters counters; protected List markedDamage; protected int timesLoyaltyUsed = 0; @@ -179,7 +179,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.transformed = permanent.transformed; this.monstrous = permanent.monstrous; this.renowned = permanent.renowned; - this.pairedCard = permanent.pairedCard; + this.pairedPermanent = permanent.pairedPermanent; this.timesLoyaltyUsed = permanent.timesLoyaltyUsed; this.morphed = permanent.morphed; @@ -1318,18 +1318,18 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } @Override - public void setPairedCard(UUID pairedCard) { - this.pairedCard = pairedCard; + public void setPairedCard(MageObjectReference pairedCard) { + this.pairedPermanent = pairedCard; } @Override - public UUID getPairedCard() { - return pairedCard; + public MageObjectReference getPairedCard() { + return pairedPermanent; } @Override public void clearPairedCard() { - this.pairedCard = null; + this.pairedPermanent = null; } @Override diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index dccd865a1a1..69d7ec91c29 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -839,7 +839,7 @@ public abstract class PlayerImpl implements Player, Serializable { } if (permanent.getPairedCard() != null) { - Permanent pairedCard = game.getPermanent(permanent.getPairedCard()); + Permanent pairedCard = permanent.getPairedCard().getPermanent(game); if (pairedCard != null) { pairedCard.clearPairedCard(); } diff --git a/Mage/src/main/java/mage/watchers/common/SoulbondWatcher.java b/Mage/src/main/java/mage/watchers/common/SoulbondWatcher.java deleted file mode 100644 index ffe63d93f6b..00000000000 --- a/Mage/src/main/java/mage/watchers/common/SoulbondWatcher.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.watchers.common; - -import mage.abilities.keyword.SoulbondAbility; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.WatcherScope; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicate; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetControlledPermanent; -import mage.watchers.Watcher; - -/** - * Reacts on various events to pair or unpair creatures on the battlefield. - * - * @author noxx - */ -public class SoulbondWatcher extends Watcher { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another not paired creature you control"); - - static { - filter.add(new AnotherPredicate()); - filter.add(Predicates.not(new PairedPredicate())); - } - - public SoulbondWatcher() { - super("SoulbondWatcher", WatcherScope.GAME); - } - - public SoulbondWatcher(final SoulbondWatcher watcher) { - super(watcher); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getCardType().contains(CardType.CREATURE)) { - if (permanent.getAbilities().contains(SoulbondAbility.getInstance())) { - Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null) { - Cards cards = new CardsImpl(); - cards.add(permanent); - controller.lookAtCards("Soulbond", cards, game); - if (controller.chooseUse(Outcome.Benefit, "Use Soulbond?", null, game)) { - TargetControlledPermanent target = new TargetControlledPermanent(filter); - target.setNotTarget(true); - if (target.canChoose(permanent.getId(), controller.getId(), game)) { - if (controller.choose(Outcome.Benefit, target, permanent.getId(), game)) { - Permanent chosen = game.getPermanent(target.getFirstTarget()); - if (chosen != null) { - chosen.setPairedCard(permanent.getId()); - permanent.setPairedCard(chosen.getId()); - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(" souldbonds ").append(permanent.getLogName()).append(" with ").append(chosen.getName()).toString()); - } - } - } - } - } - } - } - - // if still unpaired - if (permanent.getPairedCard() == null) { - // try to find creature with Soulbond and unpaired - Player controller = null; - for (Permanent chosen : game.getBattlefield().getActivePermanents(filter, permanent.getControllerId(), permanent.getId(), game)) { - if (!chosen.getId().equals(permanent.getId()) && chosen.getAbilities().contains(SoulbondAbility.getInstance()) && chosen.getPairedCard() == null) { - if (controller == null) { - controller = game.getPlayer(permanent.getControllerId()); - } - if (controller != null) { - Cards cards = new CardsImpl(); - cards.add(chosen); - controller.lookAtCards("Soulbond", cards, game); - if (controller.chooseUse(Outcome.Benefit, "Use Soulbond for recent " + permanent.getLogName() + "?", SoulbondAbility.getInstance(), game)) { - chosen.setPairedCard(permanent.getId()); - permanent.setPairedCard(chosen.getId()); - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(" souldbonds ").append(permanent.getLogName()).append(" with ").append(chosen.getName()).toString()); - } - break; - } - } - } - } - } - } - } - } - - @Override - public SoulbondWatcher copy() { - return new SoulbondWatcher(this); - } -} - -class PairedPredicate implements Predicate { - - @Override - public boolean apply(Permanent input, Game game) { - return input.getPairedCard() != null; - } - - @Override - public String toString() { - return "Paired"; - } -}