1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-09 01:01:06 -09:00

* Soulbond - Reworked to two triggerd abilities (fixes ).

This commit is contained in:
LevelX2 2016-04-16 02:43:52 +02:00
parent 3b98d0714d
commit 1dbba3f7a9
32 changed files with 374 additions and 285 deletions

View file

@ -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;

View file

@ -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}"));

View file

@ -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)));

View file

@ -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)));

View file

@ -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)));

View file

@ -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

View file

@ -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}"));

View file

@ -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)));

View file

@ -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)));

View file

@ -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++;
}
}

View file

@ -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)));

View file

@ -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)));

View file

@ -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)));

View file

@ -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)));

View file

@ -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)));

View file

@ -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)));

View file

@ -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());

View file

@ -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}"));

View file

@ -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));

View file

@ -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)));

View file

@ -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)));

View file

@ -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)));

View file

@ -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()));
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;

View file

@ -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 its paired with; it or
* the creature its paired with stops being a creature; or it or the creature
* its 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 <i>(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.)</i>";
}
@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 <i>(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.)</i>";
}
@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<Permanent> {
@Override
public boolean apply(Permanent input, Game game) {
return input.getPairedCard() != null;
}
@Override
public String toString() {
return "Paired";
}
}

View file

@ -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);

View file

@ -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.

View file

@ -116,7 +116,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
protected HashSet<MageObjectReference> dealtDamageByThisTurn;
protected UUID attachedTo;
protected int attachedToZoneChangeCounter;
protected UUID pairedCard;
protected MageObjectReference pairedPermanent;
protected Counters counters;
protected List<Counter> 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

View file

@ -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();
}

View file

@ -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<Permanent> {
@Override
public boolean apply(Permanent input, Game game) {
return input.getPairedCard() != null;
}
@Override
public String toString() {
return "Paired";
}
}