diff --git a/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java b/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java new file mode 100644 index 0000000000..b26ed1b207 --- /dev/null +++ b/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java @@ -0,0 +1,215 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.gatecrash; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Outcome; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.filter.FilterCard; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; + +/** + * Gatecrash FAQ (01.2013) + * + * If Bane Alley Broker's first ability resolves when you have no cards in your hand, + * you'll draw a card and then exile it. You won't have the opportunity to cast that + * card (or do anything else with it) before exiling it. + * + * Due to a recent rules change, once you are allowed to look at a face-down card in + * exile, you are allowed to look at that card as long as it's exiled. If you no longer + * control Bane Alley Broker when its last ability resolves, you can continue to look + * at the relevant cards in exile to choose one to return. + * + * Bane Alley Broker's second and third abilities apply to cards exiled with that + * specific Bane Alley Broker, not any other creature named Bane Alley Broker. + * You should keep cards exiled by different Bane Alley Brokers separate. + * + * If Bane Alley Broker leaves the battlefield, the cards exiled with it will be + * exiled indefinitely. If it later returns to the battlefield, it will be a new + * object with no connection to the cards exiled with it in its previous existence. + * You won't be able to use the "new" Bane Alley Broker to return cards exiled with + * the "old" one. + * + * Even if not all players can look at the exiled cards, each card's owner is still + * known. It is advisable to keep cards owned by different players in distinct piles + * in case another player gains control of Bane Alley Broker and exiles one or more + * cards with it. + * + * @author LevelX2 + */ +public class BaneAlleyBroker extends CardImpl { + + public BaneAlleyBroker(UUID ownerId) { + super(ownerId, 145, "Bane Alley Broker", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + this.expansionSetCode = "GTC"; + this.subtype.add("Human"); + this.subtype.add("Rogue"); + + this.color.setBlue(true); + this.color.setBlack(true); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // {tap}: Draw a card, then exile a card from your hand face down. + // You may look at cards exiled with Bane Alley Broker. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BaneAlleyBrokerDrawExileEffect(), new TapSourceCost())); + + + + // {U}{B}, {tap}: Return a card exiled with Bane Alley Broker to its owner's hand. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{U}{B}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInBaneAlleyBrokerExile(this.getId())); + this.addAbility(ability); + + } + + public BaneAlleyBroker(final BaneAlleyBroker card) { + super(card); + } + + @Override + public BaneAlleyBroker copy() { + return new BaneAlleyBroker(this); + } +} + +class BaneAlleyBrokerDrawExileEffect extends OneShotEffect { + + public BaneAlleyBrokerDrawExileEffect() { + super(Outcome.DrawCard); + staticText = "Draw a card, then exile a card from your hand face down.
You may look at cards exiled with Bane Alley Broker"; + } + + public BaneAlleyBrokerDrawExileEffect(final BaneAlleyBrokerDrawExileEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.drawCards(1, game); + Target target = new TargetCardInHand(new FilterCard("card to exile")); + target.setRequired(true); + if (player.chooseTarget(outcome, target, source, game)) { + Card card = game.getCard(target.getFirstTarget()); + Card sourceCard = game.getCard(source.getSourceId()); + if (card != null && sourceCard != null) { + card.setFaceDown(true); + UUID exileId = UUID.fromString(new StringBuilder(sourceCard.getId().toString()).append(sourceCard.getZoneChangeCounter()).toString()); + return card.moveToExile(exileId, new StringBuilder(sourceCard.getName()).append("(").append(sourceCard.getZoneChangeCounter()).append(")").toString(), source.getSourceId(), game); + } + } + } + return false; + } + + @Override + public BaneAlleyBrokerDrawExileEffect copy() { + return new BaneAlleyBrokerDrawExileEffect(this); + } +} + +class TargetCardInBaneAlleyBrokerExile extends TargetCard { + + public TargetCardInBaneAlleyBrokerExile(UUID CardId) { + super(1, 1, Zone.EXILED, new FilterCard("card exiled with Bane Alley Broker")); + } + + public TargetCardInBaneAlleyBrokerExile(final TargetCardInBaneAlleyBrokerExile target) { + super(target); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet(); + Card sourceCard = game.getCard(sourceId); + if (sourceCard != null) { + UUID exileId = UUID.fromString(new StringBuilder(sourceCard.getId().toString()).append(sourceCard.getZoneChangeCounter()).toString()); + ExileZone exile = game.getExile().getExileZone(exileId); + if (exile != null && exile.size() > 0) { + possibleTargets.addAll(exile); + } + } + return possibleTargets; + } + + @Override + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + Card sourceCard = game.getCard(sourceId); + if (sourceCard != null) { + UUID exileId = UUID.fromString(new StringBuilder(sourceCard.getId().toString()).append(sourceCard.getZoneChangeCounter()).toString()); + ExileZone exile = game.getExile().getExileZone(exileId); + if (exile != null && exile.size() > 0) { + return true; + } + } + return false; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + Card card = game.getCard(id); + if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED) { + ExileZone exile = null; + Card sourceCard = game.getCard(source.getSourceId()); + if (sourceCard != null) { + UUID exileId = UUID.fromString(new StringBuilder(sourceCard.getId().toString()).append(sourceCard.getZoneChangeCounter()).toString()); + exile = game.getExile().getExileZone(exileId); + } + if (exile != null && exile.contains(id)) { + return filter.match(card, source.getControllerId(), game); + } + } + return false; + } + + @Override + public TargetCardInBaneAlleyBrokerExile copy() { + return new TargetCardInBaneAlleyBrokerExile(this); + } +} diff --git a/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java b/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java new file mode 100644 index 0000000000..c5ad11e0ad --- /dev/null +++ b/Mage.Sets/src/mage/sets/gatecrash/CartelAristocrat.java @@ -0,0 +1,125 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.gatecrash; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Outcome; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continious.GainAbilitySourceEffect; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.choices.ChoiceColor; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +/** + * Gatecrash FAQ (01.2013) + * You choose the color when the ability resolves. + * + * @author LevelX2 + */ +public class CartelAristocrat extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("another creature"); + + static { + filter.add(new AnotherPredicate()); + } + public CartelAristocrat(UUID ownerId) { + super(ownerId, 150, "Cartel Aristocrat", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{W}{B}"); + this.expansionSetCode = "GTC"; + this.subtype.add("Human"); + this.subtype.add("Advisor"); + + this.color.setBlack(true); + this.color.setWhite(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Sacrifice another creature: Cartel Aristocrat gains protection from the color of your choice until end of turn. + Ability ability = new SimpleActivatedAbility( + Zone.BATTLEFIELD, new CartelAristocratEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + } + + public CartelAristocrat(final CartelAristocrat card) { + super(card); + } + + @Override + public CartelAristocrat copy() { + return new CartelAristocrat(this); + } +} +class CartelAristocratEffect extends OneShotEffect { + + public CartelAristocratEffect() { + super(Outcome.Protect); + this.staticText = "{this} gains protection from the color of your choice until end of turn"; + } + + public CartelAristocratEffect(final CartelAristocratEffect effect) { + super(effect); + } + + @Override + public CartelAristocratEffect copy() { + return new CartelAristocratEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ChoiceColor choice = new ChoiceColor(); + choice.setMessage("Choose color to get protection from"); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && controller.choose(outcome, choice, game)) { + FilterCard protectionFilter = new FilterCard(); + protectionFilter.add(new ColorPredicate(choice.getColor())); + protectionFilter.setMessage(choice.getChoice().toLowerCase()); + ContinuousEffect effect = new GainAbilitySourceEffect(new ProtectionAbility(protectionFilter), Duration.EndOfTurn); + game.addEffect(effect, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/gatecrash/UnexpectedResults.java b/Mage.Sets/src/mage/sets/gatecrash/UnexpectedResults.java new file mode 100644 index 0000000000..8b7d2bc94e --- /dev/null +++ b/Mage.Sets/src/mage/sets/gatecrash/UnexpectedResults.java @@ -0,0 +1,137 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.gatecrash; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Outcome; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardsImpl; +import mage.game.Game; +import mage.players.Player; + +/** + * Gatecrash FAQ 01/2013 + * + * If you reveal a nonland card, you may cast it during the resolution of Unexpected Results. + * Ignore timing restrictions based on the card's type. Other timing restrictions, such as + * "Cast [this card] only during combat," must be followed. + * + * If you can't cast the card (perhaps because there are no legal targets), or if you choose + * not to, the card will remain on top of the library. + * + * If you cast a spell "without paying its mana cost," you can't pay alternative costs such + * as overload costs. You can pay additional costs such as kicker costs. If the card has mandatory + * additional costs, you must pay those. + * + * If the card has X Mana in its mana cost, you must choose 0 as its value. + * + * If you reveal a land card, Unexpected Results will be returned to your hand only if you put + * that land card onto the battlefield. If you don't, Unexpected Results will be put into its + * owner's graveyard. + * + * If you reveal a land card and put that card onto the battlefield, Unexpected Results will + * be put into its owner's hand directly from the stack. It won't be put into any graveyard. + * + * @author LevelX2 + */ +public class UnexpectedResults extends CardImpl { + + public UnexpectedResults(UUID ownerId) { + super(ownerId, 203, "Unexpected Results", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{G}{U}"); + this.expansionSetCode = "GTC"; + + this.color.setBlue(true); + this.color.setGreen(true); + + // Shuffle your library, then reveal the top card. If it's a nonland card, you may cast it without paying its mana cost. If it's a land card, you may put it onto the battlefield and return Unexpected Results to its owner's hand. + this.getSpellAbility().addEffect(new UnexpectedResultEffect()); + + } + + public UnexpectedResults(final UnexpectedResults card) { + super(card); + } + + @Override + public UnexpectedResults copy() { + return new UnexpectedResults(this); + } +} + +class UnexpectedResultEffect extends OneShotEffect { + + public UnexpectedResultEffect() { + super(Outcome.PlayForFree); + this.staticText = "Shuffle your library, then reveal the top card. If it's a nonland card, you may cast it without paying its mana cost. If it's a land card, you may put it onto the battlefield and return Unexpected Results to its owner's hand"; + } + + public UnexpectedResultEffect(final UnexpectedResultEffect effect) { + super(effect); + } + + @Override + public UnexpectedResultEffect copy() { + return new UnexpectedResultEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card sourceCard = game.getCard(source.getSourceId()); + if (controller == null || sourceCard == null) { + return false; + } + if (controller.getLibrary().size() > 0) { + controller.getLibrary().shuffle(); + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + controller.revealCards(sourceCard.getName(), new CardsImpl(card), game); + if (card.getCardType().contains(CardType.LAND)) { + String message = "Put " + card.getName() + " onto the battlefield?"; + if (controller.chooseUse(Outcome.PutLandInPlay, message, game)) { + card.putOntoBattlefield(game, Zone.LIBRARY, source.getId(), source.getControllerId()); + return sourceCard.moveToZone(Zone.HAND, source.getId(), game, false); + } + } else { + if (controller.chooseUse(outcome, new StringBuilder("Cast ").append(card.getName()).append(" without paying its mana cost?").toString(), game)) { + return controller.cast(card.getSpellAbility(), game, true); + } + } + return true; + } + return false; + } +}