/* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ package mage.abilities.keyword; 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; /** * 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 EntersBattlefieldTriggeredAbility { public SoulbondAbility() { super(new SoulboundEntersSelfEffect(), true); this.addSubAbility(new SoulbondEntersOtherAbility()); } public SoulbondAbility(SoulbondAbility 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 boolean self = false; boolean other = false; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(getControllerId())) { if (permanent.isCreature()) { 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 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.isCreature()) { 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.isCreature()) { Player controller = game.getPlayer(permanent.getControllerId()); if (controller != null) { Permanent enteringPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (enteringPermanent != null && enteringPermanent.isCreature() && 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"; } }