diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/BriselaVoiceOfNightmares.java b/Mage.Sets/src/mage/sets/eldritchmoon/BriselaVoiceOfNightmares.java index 845a5117f88..b2c7d088e0e 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/BriselaVoiceOfNightmares.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/BriselaVoiceOfNightmares.java @@ -37,7 +37,7 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.cards.CardImpl; +import mage.cards.MeldCard; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -52,7 +52,7 @@ import mage.game.stack.Spell; * * @author LevelX2 */ -public class BriselaVoiceOfNightmares extends CardImpl { +public class BriselaVoiceOfNightmares extends MeldCard { public BriselaVoiceOfNightmares(UUID ownerId) { super(ownerId, 15, "Brisela, Voice of Nightmares", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, ""); @@ -67,12 +67,16 @@ public class BriselaVoiceOfNightmares extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // First strike this.addAbility(FirstStrikeAbility.getInstance()); + // Vigilance this.addAbility(VigilanceAbility.getInstance()); + // Lifelink this.addAbility(LifelinkAbility.getInstance()); + // Your opponents can't cast spells with converted mana cost 3 or less. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BriselaVoiceOfNightmaresCantCastEffect())); } diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/ChitteringHost.java b/Mage.Sets/src/mage/sets/eldritchmoon/ChitteringHost.java index 91568ac4327..cd3d19cad18 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/ChitteringHost.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/ChitteringHost.java @@ -70,6 +70,7 @@ public class ChitteringHost extends MeldCard { Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); effect = new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true); effect.setText("and gain menace until end of turn"); + ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/GiselaTheBrokenBlade.java b/Mage.Sets/src/mage/sets/eldritchmoon/GiselaTheBrokenBlade.java index 27f383d64e3..82b8ded1808 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/GiselaTheBrokenBlade.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/GiselaTheBrokenBlade.java @@ -29,15 +29,17 @@ package mage.sets.eldritchmoon; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.MeldCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.MeldEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.Zone; +import mage.constants.TargetController; /** * @@ -56,12 +58,19 @@ public class GiselaTheBrokenBlade extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // First strike this.addAbility(FirstStrikeAbility.getInstance()); + // Lifelink this.addAbility(LifelinkAbility.getInstance()); + // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Meld ability not implemeted yet."))); + this.addAbility(new ConditionalTriggeredAbility( + new BeginningOfEndStepTriggeredAbility(new MeldEffect("Bruna, the Fading Light", new BriselaVoiceOfNightmares(ownerId)), TargetController.YOU, false), + new MeldCondition("Bruna, the Fading Light"), + "At the beginning of combat on your turn, if you both own and control {this} and a creature named Bruna, the Fading Light, exile them, " + + "then meld them into Brisela, Voice of Nightmares.")); } public GiselaTheBrokenBlade(final GiselaTheBrokenBlade card) { diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/GrafRats.java b/Mage.Sets/src/mage/sets/eldritchmoon/GrafRats.java index 20036dcba9a..60a0baab3d3 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/GrafRats.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/GrafRats.java @@ -52,6 +52,7 @@ public class GrafRats extends CardImpl { this.toughness = new MageInt(1); // At the beginning of combat on your turn, if you both own and control Graf Rats and a creature named Midnight Scavengers, exile them, then meld them into Chittering Host. + this.addAbility(new ConditionalTriggeredAbility( new BeginningOfCombatTriggeredAbility(new MeldEffect("Midnight Scavengers", new ChitteringHost(ownerId)), TargetController.YOU, false), new MeldCondition("Midnight Scavengers"), diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/HanweirBattlements.java b/Mage.Sets/src/mage/sets/eldritchmoon/HanweirBattlements.java index 337c31dd47b..443c12fb732 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/HanweirBattlements.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/HanweirBattlements.java @@ -30,10 +30,11 @@ package mage.sets.eldritchmoon; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MeldCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.MeldEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.mana.ColorlessManaAbility; @@ -63,7 +64,11 @@ public class HanweirBattlements extends CardImpl { this.addAbility(ability); // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Meld ability not implemeted yet."))); + ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new MeldEffect("Hanweir Garrison", new HanweirTheWrithingTownship(ownerId)), + new ManaCostsImpl("{3}{R}{R}"), new MeldCondition("Hanweir Garrison"), + "{3}{R}{R}, {T}: If you both own and control {this} and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township."); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); } public HanweirBattlements(final HanweirBattlements card) { diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/HanweirTheWrithingTownship.java b/Mage.Sets/src/mage/sets/eldritchmoon/HanweirTheWrithingTownship.java index b741af6ed98..01220e96499 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/HanweirTheWrithingTownship.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/HanweirTheWrithingTownship.java @@ -33,7 +33,7 @@ import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.CardImpl; +import mage.cards.MeldCard; import mage.constants.CardType; import mage.constants.Rarity; @@ -41,7 +41,7 @@ import mage.constants.Rarity; * * @author LevelX2 */ -public class HanweirTheWrithingTownship extends CardImpl { +public class HanweirTheWrithingTownship extends MeldCard { public HanweirTheWrithingTownship(UUID ownerId) { super(ownerId, 130, "Hanweir, the Writhing Township", Rarity.RARE, new CardType[]{CardType.CREATURE}, ""); @@ -56,8 +56,10 @@ public class HanweirTheWrithingTownship extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); + // Haste this.addAbility(HasteAbility.getInstance()); + // Whenever Hanweir, the Writhing Township attacks, put two 3/2 colorless Eldrazi Horror creature tokens onto the battlefield tapped and attacking. this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new EldraziHorrorToken(), 2, true, true), false)); } diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java index de50f932334..17de3db867e 100644 --- a/Mage/src/main/java/mage/cards/MeldCard.java +++ b/Mage/src/main/java/mage/cards/MeldCard.java @@ -1,360 +1,373 @@ -/* - * 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.cards; - -import java.util.ArrayList; -import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; -import static mage.constants.Zone.EXILED; -import static mage.constants.Zone.GRAVEYARD; -import static mage.constants.Zone.HAND; -import static mage.constants.Zone.LIBRARY; -import mage.counters.Counter; -import mage.game.Game; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.PermanentMeld; -import mage.players.Player; - -/** - * - * @author emerald000 - */ -public abstract class MeldCard extends CardImpl { - - protected Card topHalfCard; - protected Card bottomHalfCard; - protected int topLastZoneChangeCounter; - protected int bottomLastZoneChangeCounter; - protected boolean isMelded; - - public MeldCard(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) { - super(ownerId, cardNumber, name, rarity, cardTypes, costs); - } - - public MeldCard(MeldCard card) { - super(card); - this.topHalfCard = card.topHalfCard; - this.bottomHalfCard = card.bottomHalfCard; - this.topLastZoneChangeCounter = card.topLastZoneChangeCounter; - this.bottomLastZoneChangeCounter = card.bottomLastZoneChangeCounter; - this.isMelded = card.isMelded; - } - - public void setMelded(boolean isMelded) { - this.isMelded = isMelded; - } - - public boolean isMelded() { - return isMelded; - } - - public Card getTopHalfCard() { - return topHalfCard; - } - - public void setTopHalfCard(Card topHalfCard, Game game) { - this.topHalfCard = topHalfCard; - this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); - } - - public int getTopLastZoneChangeCounter() { - return topLastZoneChangeCounter; - } - - public void setTopLastZoneChangeCounter(int topLastZoneChangeCounter) { - this.topLastZoneChangeCounter = topLastZoneChangeCounter; - } - - public Card getBottomHalfCard() { - return bottomHalfCard; - } - - public void setbottomHalfCard(Card bottomHalfCard, Game game) { - this.bottomHalfCard = bottomHalfCard; - this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); - } - - public int getBottomLastZoneChangeCounter() { - return bottomLastZoneChangeCounter; - } - - public void setBottomLastZoneChangeCounter(int bottomLastZoneChangeCounter) { - this.bottomLastZoneChangeCounter = bottomLastZoneChangeCounter; - } - - @Override - public void assignNewId() { - super.assignNewId(); - topHalfCard.assignNewId(); - bottomHalfCard.assignNewId(); - } - - @Override - public void setCopy(boolean isCopy) { - super.setCopy(isCopy); - topHalfCard.setCopy(isCopy); - bottomHalfCard.setCopy(isCopy); - } - - @Override - public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList appliedEffects) { - if (this.isMelded()) { - // Initial move to battlefield - if (toZone == Zone.BATTLEFIELD) { - return this.putOntoBattlefield(game, Zone.EXILED, sourceId, this.getOwnerId(), false, false, appliedEffects); - } // Move when melded from the battlefield to elsewhere - else { - ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, toZone, appliedEffects); - if (!game.replaceEvent(event)) { - updateZoneChangeCounter(game); - switch (event.getToZone()) { - case GRAVEYARD: - game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true); - game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true); - break; - case HAND: - game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard); - game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard); - break; - case EXILED: - game.getExile().getPermanentExile().add(topHalfCard); - game.getExile().getPermanentExile().add(bottomHalfCard); - break; - case LIBRARY: - Player controller = game.getPlayer(this.getOwnerId()); - if (controller != null) { - CardsImpl cardsToMove = new CardsImpl(); - cardsToMove.add(topHalfCard); - cardsToMove.add(bottomHalfCard); - if (flag) { - controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true); - } else { - controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true); - } - } - break; - default: - return false; - } - this.setMelded(false); - game.setZone(topHalfCard.getId(), event.getToZone()); - game.setZone(bottomHalfCard.getId(), event.getToZone()); - this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); - this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); - game.addSimultaneousEvent(event); - return true; - } else { - return false; - } - } - } else { - // Try to move the former meld cards after it has already left the battlefield. - // If the meld parts didn't move from that zone, move them instead of the meld card. - // Reset the local zcc so the meld card lose track of them. - boolean returnValue = false; - if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { - topHalfCard.moveToZone(toZone, sourceId, game, flag, appliedEffects); - topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); - returnValue = true; - } - if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { - bottomHalfCard.moveToZone(toZone, sourceId, game, flag, appliedEffects); - bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); - returnValue = true; - } - return returnValue; - } - } - - @Override - public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList appliedEffects) { - if (this.isMelded()) { - // Move when melded from the battlefield to exile - ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, Zone.EXILED, appliedEffects); - if (!game.replaceEvent(event)) { - updateZoneChangeCounter(game); - switch (event.getToZone()) { - case GRAVEYARD: - game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true); - game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true); - break; - case HAND: - game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard); - game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard); - break; - case EXILED: - if (exileId == null) { - game.getExile().getPermanentExile().add(topHalfCard); - game.getExile().getPermanentExile().add(bottomHalfCard); - } else { - game.getExile().createZone(exileId, name).add(topHalfCard); - game.getExile().getExileZone(exileId).add(bottomHalfCard); - } - break; - case LIBRARY: - Player controller = game.getPlayer(this.getOwnerId()); - if (controller != null) { - CardsImpl cardsToMove = new CardsImpl(); - cardsToMove.add(topHalfCard); - cardsToMove.add(bottomHalfCard); - if (event.getFlag()) { - controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true); - } else { - controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true); - } - } - break; - default: - return false; - } - this.setMelded(false); - game.setZone(topHalfCard.getId(), event.getToZone()); - game.setZone(bottomHalfCard.getId(), event.getToZone()); - this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); - this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); - game.addSimultaneousEvent(event); - return true; - } else { - return false; - } - } else { - // Try to move the former meld cards after it has already left the battlefield. - // If the meld parts didn't move from that zone, move them instead of the meld card. - // Reset the local zcc so the meld card lose track of them. - boolean returnValue = false; - if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { - topHalfCard.moveToExile(exileId, name, sourceId, game, appliedEffects); - topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); - returnValue = true; - } - if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { - bottomHalfCard.moveToExile(exileId, name, sourceId, game, appliedEffects); - bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); - returnValue = true; - } - return returnValue; - } - } - - @Override - public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown, ArrayList appliedEffects) { - // Initial move to battlefield - if (this.isMelded()) { - ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, controllerId, Zone.EXILED, Zone.BATTLEFIELD, appliedEffects); - if (!game.replaceEvent(event) && event.getToZone() == Zone.BATTLEFIELD) { - updateZoneChangeCounter(game); - PermanentMeld permanent = new PermanentMeld(this, event.getPlayerId(), game); // controller can be replaced (e.g. Gather Specimens) - game.addPermanent(permanent); - game.setZone(objectId, Zone.BATTLEFIELD); - game.setScopeRelevant(true); - game.applyEffects(); - boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); - game.setScopeRelevant(false); - game.applyEffects(); - if (entered) { - if (event.getFlag()) { - permanent.setTapped(true); - } - event.setTarget(permanent); - } else { - return false; - } - game.setZone(objectId, event.getToZone()); - game.addSimultaneousEvent(event); - game.getExile().removeCard(this.topHalfCard, game); - game.getExile().removeCard(this.bottomHalfCard, game); - return true; - } else { - this.setMelded(false); - return false; - } - } else { - // Try to move the former meld cards after it has already left the battlefield. - // If the meld parts didn't move from that zone, move them instead of the meld card. - // Reset the local zcc so the meld card lose track of them. - boolean returnValue = false; - if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { - topHalfCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, tapped, appliedEffects); - topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); - returnValue = true; - } - if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { - bottomHalfCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, tapped, appliedEffects); - bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); - returnValue = true; - } - return returnValue; - } - } - - @Override - public void setOwnerId(UUID ownerId) { - super.setOwnerId(ownerId); - abilities.setControllerId(ownerId); - } - - @Override - public int getConvertedManaCost() { - if (this.isCopy()) { - return 0; - } else { - return (this.topHalfCard != null ? this.topHalfCard.getConvertedManaCost() : 0) - + (this.bottomHalfCard != null ? this.bottomHalfCard.getConvertedManaCost() : 0); - } - } - - @Override - public void addCounters(Counter counter, Game game, ArrayList appliedEffects) { - if (this.isMelded()) { - super.addCounters(counter, game, appliedEffects); - } else { - if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { - topHalfCard.addCounters(counter, game, appliedEffects); - } - if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { - bottomHalfCard.addCounters(counter, game, appliedEffects); - } - } - } - - @Override - public void addCounters(String name, int amount, Game game, ArrayList appliedEffects) { - if (this.isMelded()) { - super.addCounters(name, amount, game, appliedEffects); - } else { - if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { - topHalfCard.addCounters(name, amount, game, appliedEffects); - } - if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { - bottomHalfCard.addCounters(name, amount, game, appliedEffects); - } - } - } -} +/* + * 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.cards; + +import java.util.ArrayList; +import java.util.UUID; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import static mage.constants.Zone.EXILED; +import static mage.constants.Zone.GRAVEYARD; +import static mage.constants.Zone.HAND; +import static mage.constants.Zone.LIBRARY; +import mage.counters.Counter; +import mage.game.Game; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.PermanentMeld; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public abstract class MeldCard extends CardImpl { + + protected Card topHalfCard; + protected Card bottomHalfCard; + protected int topLastZoneChangeCounter; + protected int bottomLastZoneChangeCounter; + protected boolean isMelded; + + public MeldCard(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) { + super(ownerId, cardNumber, name, rarity, cardTypes, costs); + } + + public MeldCard(MeldCard card) { + super(card); + this.topHalfCard = card.topHalfCard; + this.bottomHalfCard = card.bottomHalfCard; + this.topLastZoneChangeCounter = card.topLastZoneChangeCounter; + this.bottomLastZoneChangeCounter = card.bottomLastZoneChangeCounter; + this.isMelded = card.isMelded; + } + + public void setMelded(boolean isMelded) { + this.isMelded = isMelded; + } + + public boolean isMelded() { + return isMelded; + } + + public Card getTopHalfCard() { + return topHalfCard; + } + + public void setTopHalfCard(Card topHalfCard, Game game) { + this.topHalfCard = topHalfCard; + this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); + } + + public int getTopLastZoneChangeCounter() { + return topLastZoneChangeCounter; + } + + public void setTopLastZoneChangeCounter(int topLastZoneChangeCounter) { + this.topLastZoneChangeCounter = topLastZoneChangeCounter; + } + + public Card getBottomHalfCard() { + return bottomHalfCard; + } + + public void setbottomHalfCard(Card bottomHalfCard, Game game) { + this.bottomHalfCard = bottomHalfCard; + this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); + } + + public int getBottomLastZoneChangeCounter() { + return bottomLastZoneChangeCounter; + } + + public void setBottomLastZoneChangeCounter(int bottomLastZoneChangeCounter) { + this.bottomLastZoneChangeCounter = bottomLastZoneChangeCounter; + } + + @Override + public void assignNewId() { + super.assignNewId(); + topHalfCard.assignNewId(); + bottomHalfCard.assignNewId(); + } + + @Override + public void setCopy(boolean isCopy) { + super.setCopy(isCopy); + topHalfCard.setCopy(isCopy); + bottomHalfCard.setCopy(isCopy); + } + + @Override + public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList appliedEffects) { + if (this.isMelded()) { + // Initial move to battlefield + if (toZone == Zone.BATTLEFIELD) { + return this.putOntoBattlefield(game, Zone.EXILED, sourceId, this.getOwnerId(), false, false, appliedEffects); + } + // Move when melded from the battlefield to elsewhere + else { + ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, toZone, appliedEffects); + if (!game.replaceEvent(event)) { + updateZoneChangeCounter(game); + switch (event.getToZone()) { + case GRAVEYARD: + game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true); + game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true); + break; + case HAND: + game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard); + game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard); + break; + case EXILED: + game.getExile().getPermanentExile().add(topHalfCard); + game.getExile().getPermanentExile().add(bottomHalfCard); + break; + case LIBRARY: + Player controller = game.getPlayer(this.getOwnerId()); + if (controller != null) { + CardsImpl cardsToMove = new CardsImpl(); + cardsToMove.add(topHalfCard); + cardsToMove.add(bottomHalfCard); + if (flag) { + controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true); + } + else { + controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true); + } + } + break; + default: + return false; + } + this.setMelded(false); + game.setZone(topHalfCard.getId(), event.getToZone()); + game.setZone(bottomHalfCard.getId(), event.getToZone()); + this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); + this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); + game.addSimultaneousEvent(event); + return true; + } + else { + return false; + } + } + } + else { + // Try to move the former meld cards after it has already left the battlefield. + // If the meld parts didn't move from that zone, move them instead of the meld card. + // Reset the local zcc so the meld card lose track of them. + boolean returnValue = false; + if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { + topHalfCard.moveToZone(toZone, sourceId, game, flag, appliedEffects); + topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); + returnValue = true; + } + if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { + bottomHalfCard.moveToZone(toZone, sourceId, game, flag, appliedEffects); + bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); + returnValue = true; + } + return returnValue; + } + } + + @Override + public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList appliedEffects) { + if (this.isMelded()) { + // Move when melded from the battlefield to exile + ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, Zone.EXILED, appliedEffects); + if (!game.replaceEvent(event)) { + updateZoneChangeCounter(game); + switch (event.getToZone()) { + case GRAVEYARD: + game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true); + game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true); + break; + case HAND: + game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard); + game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard); + break; + case EXILED: + if (exileId == null) { + game.getExile().getPermanentExile().add(topHalfCard); + game.getExile().getPermanentExile().add(bottomHalfCard); + } + else { + game.getExile().createZone(exileId, name).add(topHalfCard); + game.getExile().getExileZone(exileId).add(bottomHalfCard); + } + break; + case LIBRARY: + Player controller = game.getPlayer(this.getOwnerId()); + if (controller != null) { + CardsImpl cardsToMove = new CardsImpl(); + cardsToMove.add(topHalfCard); + cardsToMove.add(bottomHalfCard); + if (event.getFlag()) { + controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true); + } + else { + controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true); + } + } + break; + default: + return false; + } + this.setMelded(false); + game.setZone(topHalfCard.getId(), event.getToZone()); + game.setZone(bottomHalfCard.getId(), event.getToZone()); + this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); + this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); + game.addSimultaneousEvent(event); + return true; + } + else { + return false; + } + } + else { + // Try to move the former meld cards after it has already left the battlefield. + // If the meld parts didn't move from that zone, move them instead of the meld card. + // Reset the local zcc so the meld card lose track of them. + boolean returnValue = false; + if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { + topHalfCard.moveToExile(exileId, name, sourceId, game, appliedEffects); + topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); + returnValue = true; + } + if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { + bottomHalfCard.moveToExile(exileId, name, sourceId, game, appliedEffects); + bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); + returnValue = true; + } + return returnValue; + } + } + + @Override + public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown, ArrayList appliedEffects) { + // Initial move to battlefield + if (this.isMelded()) { + ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, controllerId, Zone.EXILED, Zone.BATTLEFIELD, appliedEffects); + if (!game.replaceEvent(event) && event.getToZone() == Zone.BATTLEFIELD) { + updateZoneChangeCounter(game); + PermanentMeld permanent = new PermanentMeld(this, event.getPlayerId(), game); // controller can be replaced (e.g. Gather Specimens) + game.addPermanent(permanent); + game.setZone(objectId, Zone.BATTLEFIELD); + game.setScopeRelevant(true); + game.applyEffects(); + boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); + game.setScopeRelevant(false); + game.applyEffects(); + if (entered) { + if (event.getFlag()) { + permanent.setTapped(true); + } + event.setTarget(permanent); + } + else { + return false; + } + game.setZone(objectId, event.getToZone()); + game.addSimultaneousEvent(event); + game.getExile().removeCard(this.topHalfCard, game); + game.getExile().removeCard(this.bottomHalfCard, game); + return true; + } + else { + this.setMelded(false); + return false; + } + } + else { + // Try to move the former meld cards after it has already left the battlefield. + // If the meld parts didn't move from that zone, move them instead of the meld card. + // Reset the local zcc so the meld card lose track of them. + boolean returnValue = false; + if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { + topHalfCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, tapped, appliedEffects); + topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game); + returnValue = true; + } + if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { + bottomHalfCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, tapped, appliedEffects); + bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game); + returnValue = true; + } + return returnValue; + } + } + + @Override + public void setOwnerId(UUID ownerId) { + super.setOwnerId(ownerId); + abilities.setControllerId(ownerId); + } + + @Override + public int getConvertedManaCost() { + if (this.isCopy()) { + return 0; + } + else { + return this.topHalfCard.getConvertedManaCost() + this.bottomHalfCard.getConvertedManaCost(); + } + } + + @Override + public void addCounters(Counter counter, Game game, ArrayList appliedEffects) { + if (this.isMelded()) { + super.addCounters(counter, game, appliedEffects); + } + else { + if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { + topHalfCard.addCounters(counter, game, appliedEffects); + } + if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { + bottomHalfCard.addCounters(counter, game, appliedEffects); + } + } + } + + @Override + public void addCounters(String name, int amount, Game game, ArrayList appliedEffects) { + if (this.isMelded()) { + super.addCounters(name, amount, game, appliedEffects); + } + else { + if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) { + topHalfCard.addCounters(name, amount, game, appliedEffects); + } + if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) { + bottomHalfCard.addCounters(name, amount, game, appliedEffects); + } + } + } +}