From 3792f6d2d8fea0b00f6aefe8dd70d6b4f7ed629e Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 18 Sep 2015 11:38:42 +0300 Subject: [PATCH 01/29] Text fixes --- Mage.Sets/src/mage/sets/thedark/CityOfShadows.java | 2 +- Mage.Sets/src/mage/sets/thedark/DanceOfMany.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java b/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java index 8651daa4ad0..781370f3cec 100644 --- a/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java +++ b/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java @@ -60,7 +60,7 @@ public class CityOfShadows extends CardImpl { // {T}: Add {X} to your mana pool, where X is the number of storage counters on City of Shadows. ability = new DynamicManaAbility(Mana.ColorlessMana, new CountersCount(CounterType.STORAGE), - "{tap}: Add {X} to your mana pool, where X is the number of storage counters on {this}"); + "Add {X} to your mana pool, where X is the number of storage counters on {this}"); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java b/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java index de05affa1de..1b858cc40fa 100644 --- a/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java +++ b/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java @@ -84,7 +84,7 @@ public class DanceOfMany extends CardImpl { // When Dance of Many leaves the battlefield, exile the token. // When the token leaves the battlefield, sacrifice Dance of Many. Ability ability2 = new LeavesBattlefieldTriggeredAbility(new DanceOfManyExileTokenEffect(), false); - ability2.addEffect(new InfoEffect("When the token leaves the battlfield, sacrifice {this}")); + ability2.addEffect(new InfoEffect("When the token leaves the battlefield, sacrifice {this}")); this.addAbility(ability2); // At the beginning of your upkeep, sacrifice Dance of Many unless you pay {U}{U}. From 7ad041e35b30dcf13b4005744aea2a97aad687a8 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 18 Sep 2015 11:38:55 +0300 Subject: [PATCH 02/29] Implement cards: Drowned, Flood, Riptide, and Sunken City --- .../src/mage/sets/fifthedition/Flood.java | 52 +++++++++++++ .../src/mage/sets/fourthedition/Flood.java | 75 ++++++++++++++++++ .../mage/sets/fourthedition/SunkenCity.java | 54 +++++++++++++ .../mage/sets/mastersedition/SunkenCity.java | 76 +++++++++++++++++++ .../mage/sets/masterseditioniv/Drowned.java | 52 +++++++++++++ Mage.Sets/src/mage/sets/thedark/Drowned.java | 65 ++++++++++++++++ Mage.Sets/src/mage/sets/thedark/Flood.java | 54 +++++++++++++ Mage.Sets/src/mage/sets/thedark/Riptide.java | 67 ++++++++++++++++ .../src/mage/sets/thedark/SunkenCity.java | 54 +++++++++++++ 9 files changed, 549 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/fifthedition/Flood.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/Flood.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/SunkenCity.java create mode 100644 Mage.Sets/src/mage/sets/mastersedition/SunkenCity.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/Drowned.java create mode 100644 Mage.Sets/src/mage/sets/thedark/Drowned.java create mode 100644 Mage.Sets/src/mage/sets/thedark/Flood.java create mode 100644 Mage.Sets/src/mage/sets/thedark/Riptide.java create mode 100644 Mage.Sets/src/mage/sets/thedark/SunkenCity.java diff --git a/Mage.Sets/src/mage/sets/fifthedition/Flood.java b/Mage.Sets/src/mage/sets/fifthedition/Flood.java new file mode 100644 index 00000000000..cd715597cef --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/Flood.java @@ -0,0 +1,52 @@ +/* + * 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.fifthedition; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class Flood extends mage.sets.fourthedition.Flood { + + public Flood(UUID ownerId) { + super(ownerId); + this.cardNumber = 87; + this.expansionSetCode = "5ED"; + } + + public Flood(final Flood card) { + super(card); + } + + @Override + public Flood copy() { + return new Flood(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/Flood.java b/Mage.Sets/src/mage/sets/fourthedition/Flood.java new file mode 100644 index 00000000000..642a518bf89 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/Flood.java @@ -0,0 +1,75 @@ +/* + * 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.fourthedition; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class Flood extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature without flying"); + + static { + filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } + + public Flood(UUID ownerId) { + super(ownerId, 73, "Flood", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + this.expansionSetCode = "4ED"; + + // {U}{U}: Tap target creature without flying. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCostsImpl("{U}{U}")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public Flood(final Flood card) { + super(card); + } + + @Override + public Flood copy() { + return new Flood(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/SunkenCity.java b/Mage.Sets/src/mage/sets/fourthedition/SunkenCity.java new file mode 100644 index 00000000000..e5c8de8c8a8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/SunkenCity.java @@ -0,0 +1,54 @@ +/* + * 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.fourthedition; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class SunkenCity extends mage.sets.mastersedition.SunkenCity { + + public SunkenCity(UUID ownerId) { + super(ownerId); + this.cardNumber = 106; + this.expansionSetCode = "4ED"; + this.rarity = Rarity.COMMON; + } + + public SunkenCity(final SunkenCity card) { + super(card); + } + + @Override + public SunkenCity copy() { + return new SunkenCity(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mastersedition/SunkenCity.java b/Mage.Sets/src/mage/sets/mastersedition/SunkenCity.java new file mode 100644 index 00000000000..566daa28c39 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/SunkenCity.java @@ -0,0 +1,76 @@ +/* + * 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.mastersedition; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; + +/** + * + * @author LoneFox + */ +public class SunkenCity extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public SunkenCity(UUID ownerId) { + super(ownerId, 51, "Sunken City", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{U}{U}"); + this.expansionSetCode = "MED"; + + // At the beginning of your upkeep, sacrifice Sunken City unless you pay {U}{U}. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl("{U}{U}")), TargetController.YOU, false)); + // Blue creatures get +1/+1. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); + } + + public SunkenCity(final SunkenCity card) { + super(card); + } + + @Override + public SunkenCity copy() { + return new SunkenCity(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/Drowned.java b/Mage.Sets/src/mage/sets/masterseditioniv/Drowned.java new file mode 100644 index 00000000000..94839a3396c --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/Drowned.java @@ -0,0 +1,52 @@ +/* + * 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.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class Drowned extends mage.sets.thedark.Drowned { + + public Drowned(UUID ownerId) { + super(ownerId); + this.cardNumber = 47; + this.expansionSetCode = "ME4"; + } + + public Drowned(final Drowned card) { + super(card); + } + + @Override + public Drowned copy() { + return new Drowned(this); + } +} diff --git a/Mage.Sets/src/mage/sets/thedark/Drowned.java b/Mage.Sets/src/mage/sets/thedark/Drowned.java new file mode 100644 index 00000000000..ad60e1e8770 --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/Drowned.java @@ -0,0 +1,65 @@ +/* + * 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.thedark; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class Drowned extends CardImpl { + + public Drowned(UUID ownerId) { + super(ownerId, 23, "Drowned", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.expansionSetCode = "DRK"; + this.subtype.add("Zombie"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {B}: Regenerate Drowned. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{B}"))); + } + + public Drowned(final Drowned card) { + super(card); + } + + @Override + public Drowned copy() { + return new Drowned(this); + } +} diff --git a/Mage.Sets/src/mage/sets/thedark/Flood.java b/Mage.Sets/src/mage/sets/thedark/Flood.java new file mode 100644 index 00000000000..c1b651ec76c --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/Flood.java @@ -0,0 +1,54 @@ +/* + * 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.thedark; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class Flood extends mage.sets.fourthedition.Flood { + + public Flood(UUID ownerId) { + super(ownerId); + this.cardNumber = 26; + this.expansionSetCode = "DRK"; + this.rarity = Rarity.UNCOMMON; + } + + public Flood(final Flood card) { + super(card); + } + + @Override + public Flood copy() { + return new Flood(this); + } +} diff --git a/Mage.Sets/src/mage/sets/thedark/Riptide.java b/Mage.Sets/src/mage/sets/thedark/Riptide.java new file mode 100644 index 00000000000..e2fd424ed0f --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/Riptide.java @@ -0,0 +1,67 @@ +/* + * 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.thedark; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.effects.common.TapAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; + +/** + * + * @author LoneFox + */ +public class Riptide extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public Riptide(UUID ownerId) { + super(ownerId, 34, "Riptide", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}"); + this.expansionSetCode = "DRK"; + + // Tap all blue creatures. + this.getSpellAbility().addEffect(new TapAllEffect(filter)); + } + + public Riptide(final Riptide card) { + super(card); + } + + @Override + public Riptide copy() { + return new Riptide(this); + } +} diff --git a/Mage.Sets/src/mage/sets/thedark/SunkenCity.java b/Mage.Sets/src/mage/sets/thedark/SunkenCity.java new file mode 100644 index 00000000000..52d66ec2239 --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/SunkenCity.java @@ -0,0 +1,54 @@ +/* + * 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.thedark; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class SunkenCity extends mage.sets.mastersedition.SunkenCity { + + public SunkenCity(UUID ownerId) { + super(ownerId); + this.cardNumber = 35; + this.expansionSetCode = "DRK"; + this.rarity = Rarity.COMMON; + } + + public SunkenCity(final SunkenCity card) { + super(card); + } + + @Override + public SunkenCity copy() { + return new SunkenCity(this); + } +} From debbdba1c91466e034bd57cd4adc554cbd769541 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 18 Sep 2015 19:39:50 +0300 Subject: [PATCH 03/29] Implement cards: Barbed-Back Wurm, Gossamer Chains, Jungle Wurm, and Knight of Valor --- .../src/mage/sets/mirage/BarbedBackWurm.java | 77 ++++++++++++ .../src/mage/sets/mirage/JungleWurm.java | 119 ++++++++++++++++++ .../mage/sets/vintagemasters/JungleWurm.java | 52 ++++++++ .../src/mage/sets/visions/GossamerChains.java | 74 +++++++++++ .../src/mage/sets/visions/KnightOfValor.java | 78 ++++++++++++ 5 files changed, 400 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mirage/BarbedBackWurm.java create mode 100644 Mage.Sets/src/mage/sets/mirage/JungleWurm.java create mode 100644 Mage.Sets/src/mage/sets/vintagemasters/JungleWurm.java create mode 100644 Mage.Sets/src/mage/sets/visions/GossamerChains.java create mode 100644 Mage.Sets/src/mage/sets/visions/KnightOfValor.java diff --git a/Mage.Sets/src/mage/sets/mirage/BarbedBackWurm.java b/Mage.Sets/src/mage/sets/mirage/BarbedBackWurm.java new file mode 100644 index 00000000000..139d3f54ac7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/BarbedBackWurm.java @@ -0,0 +1,77 @@ +/* + * 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.mirage; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.BlockingAttackerIdPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class BarbedBackWurm extends CardImpl { + + public BarbedBackWurm(UUID ownerId) { + super(ownerId, 3, "Barbed-Back Wurm", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{4}{B}"); + this.expansionSetCode = "MIR"; + this.subtype.add("Wurm"); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // {B}: Target green creature blocking Barbed-Back Wurm gets -1/-1 until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-1, -1, Duration.EndOfTurn), new ManaCostsImpl("{B}")); + FilterCreaturePermanent filter = new FilterCreaturePermanent("green creature blocking {this}"); + filter.add(new ColorPredicate(ObjectColor.GREEN)); + filter.add(new BlockingAttackerIdPredicate(this.getId())); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public BarbedBackWurm(final BarbedBackWurm card) { + super(card); + } + + @Override + public BarbedBackWurm copy() { + return new BarbedBackWurm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirage/JungleWurm.java b/Mage.Sets/src/mage/sets/mirage/JungleWurm.java new file mode 100644 index 00000000000..0ad27034812 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/JungleWurm.java @@ -0,0 +1,119 @@ +/* + * 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.mirage; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; + +/** + * + * @author LoneFox + */ +public class JungleWurm extends CardImpl { + + public JungleWurm(UUID ownerId) { + super(ownerId, 122, "Jungle Wurm", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + this.expansionSetCode = "MIR"; + this.subtype.add("Wurm"); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Whenever Jungle Wurm becomes blocked, it gets -1/-1 until end of turn for each creature blocking it beyond the first. + this.addAbility(new JungleWurmAbility()); + } + + public JungleWurm(final JungleWurm card) { + super(card); + } + + @Override + public JungleWurm copy() { + return new JungleWurm(this); + } +} + +class JungleWurmAbility extends BecomesBlockedTriggeredAbility { + + public JungleWurmAbility() { + super(null, false); + JungleWurmValue value = new JungleWurmValue(); + this.addEffect(new BoostSourceEffect(value, value, Duration.EndOfTurn)); + } + + public JungleWurmAbility(final JungleWurmAbility ability) { + super(ability); + } + + @Override + public JungleWurmAbility copy() { + return new JungleWurmAbility(this); + } + + @Override + public String getRule() { + return "Whenever {this} becomes blocked, it gets -1/-1 until end of turn for each creature blocking it beyond the first."; + } +} + +class JungleWurmValue implements DynamicValue { + + @Override + public JungleWurmValue copy() { + return new JungleWurmValue(); + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int count = 0; + for(CombatGroup combatGroup : game.getCombat().getGroups()) { + if(combatGroup.getAttackers().contains(sourceAbility.getSourceId())) { + int blockers = combatGroup.getBlockers().size(); + return blockers > 1 ? -(blockers - 1) : 0; + } + } + return 0; + } + + @Override + public String getMessage() { + return "-1/-1 until end of turn for each creature blocking it beyond the first"; + } +} + diff --git a/Mage.Sets/src/mage/sets/vintagemasters/JungleWurm.java b/Mage.Sets/src/mage/sets/vintagemasters/JungleWurm.java new file mode 100644 index 00000000000..85d9d57a05e --- /dev/null +++ b/Mage.Sets/src/mage/sets/vintagemasters/JungleWurm.java @@ -0,0 +1,52 @@ +/* + * 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.vintagemasters; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class JungleWurm extends mage.sets.mirage.JungleWurm { + + public JungleWurm(UUID ownerId) { + super(ownerId); + this.cardNumber = 217; + this.expansionSetCode = "VMA"; + } + + public JungleWurm(final JungleWurm card) { + super(card); + } + + @Override + public JungleWurm copy() { + return new JungleWurm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/GossamerChains.java b/Mage.Sets/src/mage/sets/visions/GossamerChains.java new file mode 100644 index 00000000000..aa991ac8fd6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/GossamerChains.java @@ -0,0 +1,74 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ReturnToHandSourceCost; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.UnblockedPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class GossamerChains extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("unblocked creature"); + + static { + filter.add(new UnblockedPredicate()); + } + + public GossamerChains(UUID ownerId) { + super(ownerId, 106, "Gossamer Chains", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}{W}"); + this.expansionSetCode = "VIS"; + + // Return Gossamer Chains to its owner's hand: Prevent all combat damage that would be dealt by target unblocked creature this turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageByTargetEffect(Duration.EndOfTurn, true), new ReturnToHandSourceCost()); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public GossamerChains(final GossamerChains card) { + super(card); + } + + @Override + public GossamerChains copy() { + return new GossamerChains(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/KnightOfValor.java b/Mage.Sets/src/mage/sets/visions/KnightOfValor.java new file mode 100644 index 00000000000..1c12875167f --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/KnightOfValor.java @@ -0,0 +1,78 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.FlankingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.BlockingAttackerIdPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class KnightOfValor extends CardImpl { + + public KnightOfValor(UUID ownerId) { + super(ownerId, 111, "Knight of Valor", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); + this.expansionSetCode = "VIS"; + this.subtype.add("Human"); + this.subtype.add("Knight"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flanking + this.addAbility(new FlankingAbility()); + // {1}{W}: Each creature without flanking blocking Knight of Valor gets -1/-1 until end of turn. Activate this ability only once each turn. + FilterCreaturePermanent filter = new FilterCreaturePermanent("each creature without flanking blocking {this}"); + filter.add(Predicates.not(new AbilityPredicate(FlankingAbility.class))); + filter.add(new BlockingAttackerIdPredicate(this.getId())); + this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1, -1, Duration.EndOfTurn, filter, false), new ManaCostsImpl("{1}{W}"))); + } + + public KnightOfValor(final KnightOfValor card) { + super(card); + } + + @Override + public KnightOfValor copy() { + return new KnightOfValor(this); + } +} From 952a9db112a9ac8cf507b5504720df337473c0dd Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 19 Sep 2015 17:17:26 +0300 Subject: [PATCH 04/29] Replace some custom token classes with the standard ones --- .../sets/bornofthegods/RaisedByWolves.java | 16 +--------- .../commander2014/HallowedSpiritkeeper.java | 20 ++----------- .../sets/commander2014/SylvanOffering.java | 20 ++----------- .../mage/sets/eventide/PatrolSignaler.java | 19 ++---------- .../mage/sets/guildpact/WurmweaverCoil.java | 22 +++++--------- .../sets/magicorigins/GideonsPhalanx.java | 20 ++----------- .../sets/magicorigins/ThopterEngineer.java | 20 +++---------- .../src/mage/sets/onslaught/CentaurGlade.java | 14 +-------- .../sets/returntoravnica/CentaursHerald.java | 20 +++---------- .../sets/scarsofmirrodin/CarrionCall.java | 21 ++------------ .../src/mage/sets/theros/AkroanHorse.java | 17 ++--------- .../src/mage/sets/theros/EvangelOfHeliod.java | 18 ++---------- .../timespiral/SarpadianEmpiresVolVii.java | 12 +------- .../mage/sets/urzassaga/GoblinOffensive.java | 19 ++---------- .../sets/vintagemasters/ThopterSquadron.java | 29 +++++-------------- 15 files changed, 44 insertions(+), 243 deletions(-) diff --git a/Mage.Sets/src/mage/sets/bornofthegods/RaisedByWolves.java b/Mage.Sets/src/mage/sets/bornofthegods/RaisedByWolves.java index 9ae5c235150..d0f51973bd3 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/RaisedByWolves.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/RaisedByWolves.java @@ -28,7 +28,6 @@ package mage.sets.bornofthegods; import java.util.UUID; -import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -47,7 +46,7 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.WolfToken; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -92,16 +91,3 @@ public class RaisedByWolves extends CardImpl { return new RaisedByWolves(this); } } - -class WolfToken extends Token { - - public WolfToken() { - super("Wolf", "2/2 green Wolf creature token"); - this.setOriginalExpansionSetCode("BNG"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add("Wolf"); - power = new MageInt(2); - toughness = new MageInt(2); - } -} diff --git a/Mage.Sets/src/mage/sets/commander2014/HallowedSpiritkeeper.java b/Mage.Sets/src/mage/sets/commander2014/HallowedSpiritkeeper.java index b052260ed7e..e9fd02c7262 100644 --- a/Mage.Sets/src/mage/sets/commander2014/HallowedSpiritkeeper.java +++ b/Mage.Sets/src/mage/sets/commander2014/HallowedSpiritkeeper.java @@ -39,7 +39,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.filter.common.FilterCreatureCard; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.SpiritWhiteToken; /** * @@ -59,7 +59,7 @@ public class HallowedSpiritkeeper extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Hallowed Spiritkeeper dies, put X 1/1 white Spirit creature tokens with flying onto the battlefield, where X is the number of creature cards in your graveyard. - Effect effect = new CreateTokenEffect(new HallowedSpiritkeeperSpiritToken(), new CardsInControllerGraveyardCount(new FilterCreatureCard("creature cards"))); + Effect effect = new CreateTokenEffect(new SpiritWhiteToken(), new CardsInControllerGraveyardCount(new FilterCreatureCard("creature cards"))); effect.setText("put X 1/1 white Spirit creature tokens with flying onto the battlefield, where X is the number of creature cards in your graveyard"); this.addAbility(new DiesTriggeredAbility(effect, false)); @@ -74,19 +74,3 @@ public class HallowedSpiritkeeper extends CardImpl { return new HallowedSpiritkeeper(this); } } - -class HallowedSpiritkeeperSpiritToken extends Token { - - public HallowedSpiritkeeperSpiritToken() { - super("Spirit", "1/1 white Spirit creature token with flying"); - setOriginalExpansionSetCode("C14"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - subtype.add("Spirit"); - power = new MageInt(1); - toughness = new MageInt(1); - - addAbility(FlyingAbility.getInstance()); - - } -} diff --git a/Mage.Sets/src/mage/sets/commander2014/SylvanOffering.java b/Mage.Sets/src/mage/sets/commander2014/SylvanOffering.java index 069978000ba..fab6eea4bed 100644 --- a/Mage.Sets/src/mage/sets/commander2014/SylvanOffering.java +++ b/Mage.Sets/src/mage/sets/commander2014/SylvanOffering.java @@ -41,6 +41,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.game.Game; import mage.game.permanent.token.SpiritWhiteToken; +import mage.game.permanent.token.ElfToken; import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.Target; @@ -149,28 +150,13 @@ class SylvanOfferingEffect2 extends OneShotEffect { Player opponent = game.getPlayer(target.getFirstTarget()); if (opponent != null) { int xValue = source.getManaCostsToPay().getX(); - Effect effect = new CreateTokenTargetEffect(new SylvanOfferingElfWarriorToken(), xValue); + Effect effect = new CreateTokenTargetEffect(new ElfToken(), xValue); effect.setTargetPointer(new FixedTarget(opponent.getId())); effect.apply(game, source); - new CreateTokenEffect(new SylvanOfferingElfWarriorToken(), xValue).apply(game, source); + new CreateTokenEffect(new ElfToken(), xValue).apply(game, source); return true; } } return false; } } - -class SylvanOfferingElfWarriorToken extends Token { - - public SylvanOfferingElfWarriorToken() { - super("Elf Warrior", "1/1 green Elf Warrior creature token"); - setOriginalExpansionSetCode("C14"); - cardType.add(CardType.CREATURE); - subtype.add("Elf"); - subtype.add("Warrior"); - color.setGreen(true); - power = new MageInt(1); - toughness = new MageInt(1); - - } -} diff --git a/Mage.Sets/src/mage/sets/eventide/PatrolSignaler.java b/Mage.Sets/src/mage/sets/eventide/PatrolSignaler.java index d23a120320f..3f4c9e306f0 100644 --- a/Mage.Sets/src/mage/sets/eventide/PatrolSignaler.java +++ b/Mage.Sets/src/mage/sets/eventide/PatrolSignaler.java @@ -39,7 +39,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.KithkinToken; /** * @@ -57,10 +57,9 @@ public class PatrolSignaler extends CardImpl { this.toughness = new MageInt(1); // {1}{W}, {untap}: Put a 1/1 white Kithkin Soldier creature token onto the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new KithkinSoldierToken()), new ManaCostsImpl("{1}{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new KithkinToken()), new ManaCostsImpl("{1}{W}")); ability.addCost(new UntapSourceCost()); this.addAbility(ability); - } public PatrolSignaler(final PatrolSignaler card) { @@ -72,17 +71,3 @@ public class PatrolSignaler extends CardImpl { return new PatrolSignaler(this); } } - -class KithkinSoldierToken extends Token { - - public KithkinSoldierToken() { - super("Kithkin Soldier", "1/1 white Kithkin Soldier creature token"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - - subtype.add("Soldier"); - power = new MageInt(1); - toughness = new MageInt(1); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/guildpact/WurmweaverCoil.java b/Mage.Sets/src/mage/sets/guildpact/WurmweaverCoil.java index da5e93aa789..afc106c50d5 100644 --- a/Mage.Sets/src/mage/sets/guildpact/WurmweaverCoil.java +++ b/Mage.Sets/src/mage/sets/guildpact/WurmweaverCoil.java @@ -28,8 +28,6 @@ package mage.sets.guildpact; import java.util.UUID; - -import mage.constants.*; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; @@ -42,9 +40,14 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.WurmToken; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -71,7 +74,7 @@ public class WurmweaverCoil extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(6, 6, Duration.WhileOnBattlefield))); - Ability activatedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new WurmweaverCoilToken(), 1), new ManaCostsImpl("{G}{G}{G}")); + Ability activatedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new WurmToken(), 1), new ManaCostsImpl("{G}{G}{G}")); activatedAbility.addCost(new SacrificeSourceCost()); this.addAbility(activatedAbility); } @@ -85,14 +88,3 @@ public class WurmweaverCoil extends CardImpl { return new WurmweaverCoil(this); } } - -class WurmweaverCoilToken extends Token { - WurmweaverCoilToken() { - super("Wurm", "6/6 green Wurm creature token"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add("Wurm"); - power = new MageInt(6); - toughness = new MageInt(6); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magicorigins/GideonsPhalanx.java b/Mage.Sets/src/mage/sets/magicorigins/GideonsPhalanx.java index 58afd8bc82e..d091b330b4e 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/GideonsPhalanx.java +++ b/Mage.Sets/src/mage/sets/magicorigins/GideonsPhalanx.java @@ -28,7 +28,6 @@ package mage.sets.magicorigins; import java.util.UUID; -import mage.MageInt; import mage.ObjectColor; import mage.abilities.condition.common.SpellMasteryCondition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -37,13 +36,12 @@ import mage.abilities.effects.common.AddContinuousEffectToGame; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.IndestructibleAbility; -import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.KnightToken; /** * @@ -56,7 +54,7 @@ public class GideonsPhalanx extends CardImpl { this.expansionSetCode = "ORI"; // Put four 2/2 white Knight creature tokens with vigilance onto the battlefield. - this.getSpellAbility().addEffect(new CreateTokenEffect(new GideonsPhalanxKnightToken(), 4)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new KnightToken(), 4)); // Spell mastery - If there are two or more instant and/or sorcery cards in your graveyard, creatures you control gain indestructible until end of turn. Effect effect = new ConditionalOneShotEffect( @@ -75,17 +73,3 @@ public class GideonsPhalanx extends CardImpl { return new GideonsPhalanx(this); } } - -class GideonsPhalanxKnightToken extends Token { - - public GideonsPhalanxKnightToken() { - super("Knight", "2/2 white Knight creature tokens with vigilance"); - this.setOriginalExpansionSetCode("ORI"); - cardType.add(CardType.CREATURE); - color.setColor(ObjectColor.WHITE); - subtype.add("Knight"); - power = new MageInt(2); - toughness = new MageInt(2); - addAbility(VigilanceAbility.getInstance()); - } -} diff --git a/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java b/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java index dd0a4a1c202..a0cd37ed550 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java +++ b/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java @@ -42,7 +42,7 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.ThopterColorlessToken; /** * @@ -55,7 +55,7 @@ public class ThopterEngineer extends CardImpl { static { filter.add(new CardTypePredicate(CardType.ARTIFACT)); } - + public ThopterEngineer(UUID ownerId) { super(ownerId, 165, "Thopter Engineer", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.expansionSetCode = "ORI"; @@ -65,8 +65,8 @@ public class ThopterEngineer extends CardImpl { this.toughness = new MageInt(3); // When Thopter Engineer enters the battlefield, put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ThopterEngineerThopterToken(), 1))); - + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken(), 1))); + // Artifact creatures you control have haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, false))); } @@ -80,15 +80,3 @@ public class ThopterEngineer extends CardImpl { return new ThopterEngineer(this); } } - -class ThopterEngineerThopterToken extends Token { - ThopterEngineerThopterToken() { - super("Thopter", "a 1/1 colorless Thopter artifact creature token with flying"); - cardType.add(CardType.CREATURE); - cardType.add(CardType.ARTIFACT); - subtype.add("Thopter"); - power = new MageInt(1); - toughness = new MageInt(1); - this.addAbility(FlyingAbility.getInstance()); - } -} diff --git a/Mage.Sets/src/mage/sets/onslaught/CentaurGlade.java b/Mage.Sets/src/mage/sets/onslaught/CentaurGlade.java index 75c0807d8d2..83937e00c64 100644 --- a/Mage.Sets/src/mage/sets/onslaught/CentaurGlade.java +++ b/Mage.Sets/src/mage/sets/onslaught/CentaurGlade.java @@ -28,7 +28,6 @@ package mage.sets.onslaught; import java.util.UUID; -import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; @@ -36,7 +35,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.CentaurToken; /** * @@ -61,14 +60,3 @@ public class CentaurGlade extends CardImpl { return new CentaurGlade(this); } } - -class CentaurToken extends Token { - CentaurToken() { - super("Centaur", "3/3 green Centaur creature token"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add("Centaur"); - power = new MageInt(3); - toughness = new MageInt(3); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/returntoravnica/CentaursHerald.java b/Mage.Sets/src/mage/sets/returntoravnica/CentaursHerald.java index 92c32cd12fa..6d3819d0672 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/CentaursHerald.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/CentaursHerald.java @@ -29,8 +29,6 @@ package mage.sets.returntoravnica; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; @@ -39,8 +37,10 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.CentaurToken; /** * @@ -58,7 +58,7 @@ public class CentaursHerald extends CardImpl { this.toughness = new MageInt(1); // {2}{G}, Sacrifice Centaur's Herald: Put a 3/3 green Centaur creature token onto the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new CentaursHeraldToken()), new ManaCostsImpl("{2}{G}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new CentaurToken()), new ManaCostsImpl("{2}{G}")); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -71,16 +71,4 @@ public class CentaursHerald extends CardImpl { public CentaursHerald copy() { return new CentaursHerald(this); } - - private class CentaursHeraldToken extends Token { - - public CentaursHeraldToken() { - super("Centaur", "3/3 green Centaur creature token"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add("Centaur"); - power = new MageInt(3); - toughness = new MageInt(3); - } - } } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/CarrionCall.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/CarrionCall.java index 6e230d5b0a5..230f49814f1 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/CarrionCall.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/CarrionCall.java @@ -29,13 +29,12 @@ package mage.sets.scarsofmirrodin; import java.util.UUID; -import mage.MageInt; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.InsectInfectToken; /** * @@ -48,7 +47,7 @@ public class CarrionCall extends CardImpl { this.expansionSetCode = "SOM"; // Put two 1/1 green Insect creature tokens with infect onto the battlefield. (They deal damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.) - this.getSpellAbility().addEffect(new CreateTokenEffect(new CarrionCallInsectToken(), 2)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new InsectInfectToken(), 2)); } public CarrionCall (final CarrionCall card) { @@ -60,19 +59,3 @@ public class CarrionCall extends CardImpl { return new CarrionCall(this); } } - -class CarrionCallInsectToken extends Token { - - public CarrionCallInsectToken() { - super("Insect", "1/1 green Insect creature tokens with infect"); - setOriginalExpansionSetCode("SOM"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add("Insect"); - power = new MageInt(1); - toughness = new MageInt(1); - - addAbility(InfectAbility.getInstance()); - } -} - diff --git a/Mage.Sets/src/mage/sets/theros/AkroanHorse.java b/Mage.Sets/src/mage/sets/theros/AkroanHorse.java index 014fb58c23a..702daa268df 100644 --- a/Mage.Sets/src/mage/sets/theros/AkroanHorse.java +++ b/Mage.Sets/src/mage/sets/theros/AkroanHorse.java @@ -49,6 +49,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.token.SoldierToken; import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.Target; @@ -175,23 +176,9 @@ class AkroanHorseCreateTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (UUID opponentId: game.getOpponents(source.getControllerId())) { - Token token = new AkroanHorseSoldierToken(); + Token token = new SoldierToken(); token.putOntoBattlefield(1, game, source.getSourceId(), opponentId); } return true; } } - -class AkroanHorseSoldierToken extends Token { - - public AkroanHorseSoldierToken() { - super("Soldier", "1/1 white Soldier creature token"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - - subtype.add("Soldier"); - power = new MageInt(1); - toughness = new MageInt(1); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/theros/EvangelOfHeliod.java b/Mage.Sets/src/mage/sets/theros/EvangelOfHeliod.java index 21614846b7b..81b53460c64 100644 --- a/Mage.Sets/src/mage/sets/theros/EvangelOfHeliod.java +++ b/Mage.Sets/src/mage/sets/theros/EvangelOfHeliod.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; import mage.constants.Rarity; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.SoldierToken; /** * @@ -55,7 +55,7 @@ public class EvangelOfHeliod extends CardImpl { this.toughness = new MageInt(3); // When Evangel of Heliod enters the battlefield, put a number of 1/1 white Soldier creature tokens onto the battlefield equal to your devotion to white. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new EvangelOfHeliodSoldierToken(), new DevotionCount(ColoredManaSymbol.W)))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SoldierToken(), new DevotionCount(ColoredManaSymbol.W)))); } public EvangelOfHeliod(final EvangelOfHeliod card) { @@ -67,17 +67,3 @@ public class EvangelOfHeliod extends CardImpl { return new EvangelOfHeliod(this); } } - -class EvangelOfHeliodSoldierToken extends Token { - - public EvangelOfHeliodSoldierToken() { - super("Soldier", "1/1 white Soldier creature tokens"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - - subtype.add("Soldier"); - power = new MageInt(1); - toughness = new MageInt(1); - } - -} diff --git a/Mage.Sets/src/mage/sets/timespiral/SarpadianEmpiresVolVii.java b/Mage.Sets/src/mage/sets/timespiral/SarpadianEmpiresVolVii.java index 406595d7c1e..8b3f154f19d 100644 --- a/Mage.Sets/src/mage/sets/timespiral/SarpadianEmpiresVolVii.java +++ b/Mage.Sets/src/mage/sets/timespiral/SarpadianEmpiresVolVii.java @@ -43,6 +43,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.token.CitizenToken; import mage.game.permanent.token.GoblinToken; import mage.game.permanent.token.SaprolingToken; import mage.game.permanent.token.Token; @@ -154,17 +155,6 @@ class CreateSelectedTokenEffect extends OneShotEffect { } } -class CitizenToken extends Token { - public CitizenToken() { - super("Citizen", "1/1 white Citizen creature token"); - cardType.add(CardType.CREATURE); - subtype.add("Citizen"); - color.setWhite(true); - power = new MageInt(1); - toughness = new MageInt(1); - } -} - class CamaridToken extends Token { public CamaridToken() { super("Camarid", "1/1 blue Camarid creature token"); diff --git a/Mage.Sets/src/mage/sets/urzassaga/GoblinOffensive.java b/Mage.Sets/src/mage/sets/urzassaga/GoblinOffensive.java index 4d11aa29eec..a9937de242e 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/GoblinOffensive.java +++ b/Mage.Sets/src/mage/sets/urzassaga/GoblinOffensive.java @@ -28,13 +28,12 @@ package mage.sets.urzassaga; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.MageInt; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; -import mage.game.permanent.token.Token; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.game.permanent.token.GoblinToken; /** * @@ -46,7 +45,6 @@ public class GoblinOffensive extends CardImpl { super(ownerId, 192, "Goblin Offensive", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{X}{1}{R}{R}"); this.expansionSetCode = "USG"; - // Put X 1/1 red Goblin creature tokens onto the battlefield. this.getSpellAbility().addEffect(new CreateTokenEffect(new GoblinToken(), new ManacostVariableValue())); } @@ -60,14 +58,3 @@ public class GoblinOffensive extends CardImpl { return new GoblinOffensive(this); } } - -class GoblinToken extends Token { - public GoblinToken() { - super("Goblin", "1/1 red Goblin creature token"); - cardType.add(CardType.CREATURE); - color.setRed(true); - subtype.add("Goblin"); - power = new MageInt(1); - toughness = new MageInt(1); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java b/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java index c5748f4dcc3..24ec01d3920 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java @@ -48,7 +48,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.ThopterColorlessToken; import mage.target.common.TargetControlledPermanent; /** @@ -56,14 +56,14 @@ import mage.target.common.TargetControlledPermanent; * @author LevelX2 */ public class ThopterSquadron extends CardImpl { - + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another Thopter"); - + static { filter.add(new AnotherPredicate()); filter.add(new SubtypePredicate("Thopter")); } - + public ThopterSquadron(UUID ownerId) { super(ownerId, 286, "Thopter Squadron", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.expansionSetCode = "VMA"; @@ -75,16 +75,16 @@ public class ThopterSquadron extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Thopter Squadron enters the battlefield with three +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), "with three +1/+1 counters on it")); - + // {1}, Remove a +1/+1 counter from Thopter Squadron: Put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. Activate this ability only any time you could cast a sorcery. - Ability firstAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new ThopterSquadronThopterToken(), 1), new GenericManaCost(1)); + Ability firstAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new ThopterColorlessToken(), 1), new GenericManaCost(1)); firstAbility.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1))); this.addAbility(firstAbility); - + // {1}, Sacrifice another Thopter: Put a +1/+1 counter on Thopter Squadron. Activate this ability only any time you could cast a sorcery. Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true), new ManaCostsImpl("{1}")); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); - + } public ThopterSquadron(final ThopterSquadron card) { @@ -96,16 +96,3 @@ public class ThopterSquadron extends CardImpl { return new ThopterSquadron(this); } } - -class ThopterSquadronThopterToken extends Token { - public ThopterSquadronThopterToken() { - super("Thopter", "1/1 colorless Thopter artifact creature token with flying"); - cardType.add(CardType.ARTIFACT); - cardType.add(CardType.CREATURE); - subtype.add("Thopter"); - power = new MageInt(1); - toughness = new MageInt(1); - this.addAbility(FlyingAbility.getInstance()); - } - -} \ No newline at end of file From b43814a122eb8f22c62233f3bbb75d42e2e01fe5 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 19 Sep 2015 18:48:37 +0300 Subject: [PATCH 05/29] Fix type of the counters used by Vile Requiem. Implement cards: Great Whale, Hermetic Study, Midsummer Revel, and Torch Song --- .../src/mage/sets/urzassaga/GreatWhale.java | 63 +++++++++++++++ .../mage/sets/urzassaga/HermeticStudy.java | 81 +++++++++++++++++++ .../mage/sets/urzassaga/MidsummerRevel.java | 75 +++++++++++++++++ .../src/mage/sets/urzassaga/TorchSong.java | 75 +++++++++++++++++ .../src/mage/sets/urzassaga/VileRequiem.java | 6 +- Mage/src/mage/counters/CounterType.java | 1 - 6 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/urzassaga/GreatWhale.java create mode 100644 Mage.Sets/src/mage/sets/urzassaga/HermeticStudy.java create mode 100644 Mage.Sets/src/mage/sets/urzassaga/MidsummerRevel.java create mode 100644 Mage.Sets/src/mage/sets/urzassaga/TorchSong.java diff --git a/Mage.Sets/src/mage/sets/urzassaga/GreatWhale.java b/Mage.Sets/src/mage/sets/urzassaga/GreatWhale.java new file mode 100644 index 00000000000..e8b5b9f6c5f --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzassaga/GreatWhale.java @@ -0,0 +1,63 @@ +/* + * 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.urzassaga; + +import java.util.UUID; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.UntapLandsEffect; +import mage.cards.CardImpl; + +/** + * + * @author LoneFox + */ +public class GreatWhale extends CardImpl { + + public GreatWhale(UUID ownerId) { + super(ownerId, 77, "Great Whale", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + this.expansionSetCode = "USG"; + this.subtype.add("Whale"); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // When Great Whale enters the battlefield, untap up to seven lands. + this.addAbility(new EntersBattlefieldTriggeredAbility(new UntapLandsEffect(7))); + } + + public GreatWhale(final GreatWhale card) { + super(card); + } + + @Override + public GreatWhale copy() { + return new GreatWhale(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/HermeticStudy.java b/Mage.Sets/src/mage/sets/urzassaga/HermeticStudy.java new file mode 100644 index 00000000000..d04371b3e0c --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzassaga/HermeticStudy.java @@ -0,0 +1,81 @@ +/* + * 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.urzassaga; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreatureOrPlayer; +import mage.target.common.TargetCreaturePermanent; +/** + * + * @author LoneFox + */ +public class HermeticStudy extends CardImpl { + + public HermeticStudy(UUID ownerId) { + super(ownerId, 78, "Hermetic Study", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + this.expansionSetCode = "USG"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature has "{tap}: This creature deals 1 damage to target creature or player." + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability, AttachmentType.AURA, Duration.WhileOnBattlefield, + "Enchanted creature has \"{T}: This creature deals 1 damage to target creature or player.\""))); + } + + public HermeticStudy(final HermeticStudy card) { + super(card); + } + + @Override + public HermeticStudy copy() { + return new HermeticStudy(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/MidsummerRevel.java b/Mage.Sets/src/mage/sets/urzassaga/MidsummerRevel.java new file mode 100644 index 00000000000..4197bea2266 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzassaga/MidsummerRevel.java @@ -0,0 +1,75 @@ +/* + * 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.urzassaga; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.permanent.token.BeastToken; + +/** + * + * @author LoneFox + */ +public class MidsummerRevel extends CardImpl { + + public MidsummerRevel(UUID ownerId) { + super(ownerId, 268, "Midsummer Revel", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{G}"); + this.expansionSetCode = "USG"; + + // At the beginning of your upkeep, you may put a verse counter on Midsummer Revel. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), TargetController.YOU, true)); + // {G}, Sacrifice Midsummer Revel: Put X 3/3 green Beast creature tokens onto the battlefield, where X is the number of verse counters on Midsummer Revel. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new BeastToken(), + new CountersCount(CounterType.VERSE)), new ManaCostsImpl("{G}")); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + public MidsummerRevel(final MidsummerRevel card) { + super(card); + } + + @Override + public MidsummerRevel copy() { + return new MidsummerRevel(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/TorchSong.java b/Mage.Sets/src/mage/sets/urzassaga/TorchSong.java new file mode 100644 index 00000000000..edd10ecd569 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzassaga/TorchSong.java @@ -0,0 +1,75 @@ +/* + * 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.urzassaga; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author LoneFox + */ +public class TorchSong extends CardImpl { + + public TorchSong(UUID ownerId) { + super(ownerId, 222, "Torch Song", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + this.expansionSetCode = "USG"; + + // At the beginning of your upkeep, you may put a verse counter on Torch Song. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), TargetController.YOU, true)); + // {2}{R}, Sacrifice Torch Song: Torch Song deals X damage to target creature or player, where X is the number of verse counters on Torch Song. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new CountersCount(CounterType.VERSE)), new ManaCostsImpl("{2}{R}")); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(ability); + } + + public TorchSong(final TorchSong card) { + super(card); + } + + @Override + public TorchSong copy() { + return new TorchSong(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/VileRequiem.java b/Mage.Sets/src/mage/sets/urzassaga/VileRequiem.java index 0bb7130dc62..e41d1ff8d95 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/VileRequiem.java +++ b/Mage.Sets/src/mage/sets/urzassaga/VileRequiem.java @@ -71,11 +71,11 @@ public class VileRequiem extends CardImpl { // At the beginning of your upkeep, you may put a verse counter on Vile Requiem. this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.VILE.createInstance(), true ), TargetController.YOU, true)); + new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true ), TargetController.YOU, true)); // {1}{B}, Sacrifice Vile Requiem: Destroy up to X target nonblack creatures, where X is the number of verse counters on Vile Requiem. They can't be regenerated. Effect effect = new DestroyTargetEffect(true); effect.setText("Destroy up to X target nonblack creatures, where X is the number of verse counters on {this}. They can't be regenerated"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{B}")); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetCreaturePermanent(0,0,filter, false)); this.addAbility(ability); @@ -88,7 +88,7 @@ public class VileRequiem extends CardImpl { if (ability.getOriginalId().equals(originalId)) { Permanent sourcePermanent = game.getPermanent(ability.getSourceId()); if (sourcePermanent != null) { - int numberCounters = sourcePermanent.getCounters().getCount(CounterType.VILE); + int numberCounters = sourcePermanent.getCounters().getCount(CounterType.VERSE); ability.getTargets().clear(); FilterCreaturePermanent newFilter = filter.copy(); newFilter.setMessage(new StringBuilder("up to ").append(CardUtil.numberToText(numberCounters)).append(" target nonblack creatures").toString()); diff --git a/Mage/src/mage/counters/CounterType.java b/Mage/src/mage/counters/CounterType.java index 1651ac2f3d2..52ebd732211 100644 --- a/Mage/src/mage/counters/CounterType.java +++ b/Mage/src/mage/counters/CounterType.java @@ -94,7 +94,6 @@ public enum CounterType { TOWER("tower"), VELOCITY("velocity"), VERSE("verse"), - VILE("vile"), VITALITY("vitality"), WISH("wish"); From 35d4106311f9a7f514c9fb9f50ac81c12850731b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 08:43:04 +0200 Subject: [PATCH 06/29] * Tide Drifter and Ruination Guide - Fixed that they buffed themself. --- Mage.Sets/src/mage/sets/battleforzendikar/RuinationGuide.java | 4 +--- Mage.Sets/src/mage/sets/battleforzendikar/TideDrifter.java | 4 +--- .../effects/common/continuous/BoostControlledEffect.java | 3 ++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/RuinationGuide.java b/Mage.Sets/src/mage/sets/battleforzendikar/RuinationGuide.java index a30a353a99b..128a4ae5697 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/RuinationGuide.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/RuinationGuide.java @@ -40,7 +40,6 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; /** * @@ -51,7 +50,6 @@ public class RuinationGuide extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Other colorless creatures you control"); static { - filter.add(new AnotherPredicate()); filter.add(new ColorlessPredicate()); } @@ -68,7 +66,7 @@ public class RuinationGuide extends CardImpl { // Ingest this.addAbility(new IngestAbility()); // Other colorless creatures you control get +1/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, filter, true))); } public RuinationGuide(final RuinationGuide card) { diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/TideDrifter.java b/Mage.Sets/src/mage/sets/battleforzendikar/TideDrifter.java index aad5defd960..43dcbe4827d 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/TideDrifter.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/TideDrifter.java @@ -39,7 +39,6 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; -import mage.filter.predicate.permanent.AnotherPredicate; /** * @@ -50,7 +49,6 @@ public class TideDrifter extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Other colorless creatures you control"); static { - filter.add(new AnotherPredicate()); filter.add(new ColorlessPredicate()); } @@ -65,7 +63,7 @@ public class TideDrifter extends CardImpl { // Devoid this.addAbility(new DevoidAbility(this.color)); // Other colorless creatures you control get +0/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(0, 1, Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(0, 1, Duration.WhileOnBattlefield, filter, true))); } diff --git a/Mage/src/mage/abilities/effects/common/continuous/BoostControlledEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BoostControlledEffect.java index eb834b17d70..763e78028b5 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BoostControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BoostControlledEffect.java @@ -81,7 +81,8 @@ public class BoostControlledEffect extends ContinuousEffectImpl { * @param power * @param toughness * @param duration - * @param filter + * @param filter AnotherPredicate is not working, you need to use the + * excludeSource option * @param lockedIn if true, power and toughness will be calculated only * once, when the ability resolves * @param excludeSource From 84757f132c6aee1db748dbbd8a23d26642e5f1d3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 08:47:04 +0200 Subject: [PATCH 07/29] * Horribly Awry - Fixed that it also could target non creature spells. --- Mage.Sets/src/mage/sets/battleforzendikar/HorriblyAwry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/HorriblyAwry.java b/Mage.Sets/src/mage/sets/battleforzendikar/HorriblyAwry.java index 793c186d79e..40d9e1f3104 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/HorriblyAwry.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/HorriblyAwry.java @@ -36,7 +36,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.Filter; -import mage.filter.FilterSpell; +import mage.filter.common.FilterCreatureSpell; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.target.TargetSpell; @@ -46,7 +46,7 @@ import mage.target.TargetSpell; */ public class HorriblyAwry extends CardImpl { - private static final FilterSpell filter = new FilterSpell("spell with converted mana cost 4 or less"); + private static final FilterCreatureSpell filter = new FilterCreatureSpell("creature spell with converted mana cost 4 or less"); static { filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, 5)); From 5a8ca36916f5b56764f4e6d5eebae12125d5e756 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 09:25:25 +0200 Subject: [PATCH 08/29] * Ondu Rising - Fixed that the attacking creatures did not get lifelink. --- .../sets/battleforzendikar/OnduRising.java | 4 +- .../triggers/delayed/OnduRisingTest.java | 71 +++++++++++++++++++ Mage/src/mage/game/combat/Combat.java | 8 +-- Mage/src/mage/game/events/GameEvent.java | 6 ++ 4 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/OnduRisingTest.java diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/OnduRising.java b/Mage.Sets/src/mage/sets/battleforzendikar/OnduRising.java index d10af4706ce..5e8d6d4defa 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/OnduRising.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/OnduRising.java @@ -83,12 +83,12 @@ class OnduRisingTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DECLARE_ATTACKER; + return event.getType() == EventType.ATTACKER_DECLARED; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); + Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/OnduRisingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/OnduRisingTest.java new file mode 100644 index 00000000000..d6b92fe2fee --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/OnduRisingTest.java @@ -0,0 +1,71 @@ +/* + * 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 org.mage.test.cards.triggers.delayed; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class OnduRisingTest extends CardTestPlayerBase { + + @Test + public void testLiflinkGained() { + addCard(Zone.BATTLEFIELD, playerB, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + // Whenever a creature attacks this turn, it gains lifelink until end of turn. + // Awaken 4—{4}{W} + addCard(Zone.HAND, playerB, "Ondu Rising", 1); + + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {W}"); + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {W}"); + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {W}"); + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {W}"); + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {W}"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ondu Rising with awaken"); + + attack(2, playerB, "Silvercoat Lion"); + attack(2, playerB, "Mountain"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Ondu Rising", 1); + assertPowerToughness(playerB, "Mountain", 4, 4); + + assertLife(playerA, 14); + assertLife(playerB, 26); + } + +} diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index aeb50cd8800..d8d815e94e9 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -927,15 +927,15 @@ public class Combat implements Serializable, Copyable { } } - public boolean declareAttacker(UUID attackerId, UUID defenderId, UUID playerId, Game game) { - Permanent attacker = game.getPermanent(attackerId); + public boolean declareAttacker(UUID creatureId, UUID defenderId, UUID playerId, Game game) { + Permanent attacker = game.getPermanent(creatureId); if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId())) { if (!attacker.isTapped()) { attacker.tap(game); } } - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, attackerId, playerId))) { - return addAttackerToCombat(attackerId, defenderId, game); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) { + return addAttackerToCombat(creatureId, defenderId, game); } return false; } diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index fcabb52c61a..93d4cef0249 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -154,6 +154,12 @@ public class GameEvent implements Serializable { COUNTER, COUNTERED, DECLARING_ATTACKERS, DECLARED_ATTACKERS, + /* DECLARE_ATTACKER + REPLACE EVENT - can be used to replace attack declaration + targetId id of the defending player or planeswalker attacked + sourceId id of the attacking creature + playerId player defining the attacking creatures + */ DECLARE_ATTACKER, /* ATTACKER_DECLARED targetId id of the defending player or planeswalker attacked From 1d9cebfc5c8a49e21b60042c17ffef3cb1406976 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 09:27:41 +0200 Subject: [PATCH 09/29] * From Beyond - Fixed that the searched cards was not revealed. --- Mage.Sets/src/mage/sets/battleforzendikar/FromBeyond.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/FromBeyond.java b/Mage.Sets/src/mage/sets/battleforzendikar/FromBeyond.java index f01242399cd..048f5b9264b 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/FromBeyond.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/FromBeyond.java @@ -70,7 +70,7 @@ public class FromBeyond extends CardImpl { // {1}{G}, Sacrifice From Beyond: Search your library for an Eldrazi card, reveal it, put it into your hand, then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter)), + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), new ManaCostsImpl("{1}{G}")); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); From 24d555f41ccd4c7859aecae9cdd10ac71596e701 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 09:46:19 +0200 Subject: [PATCH 10/29] * Emira Shepherd - Fixed that the returned card could be returned to battlefield if the played land was a Plains. --- .../battleforzendikar/EmeriaShepherd.java | 46 +++++++++++++++++-- .../abilities/common/LandfallAbility.java | 6 +++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/EmeriaShepherd.java b/Mage.Sets/src/mage/sets/battleforzendikar/EmeriaShepherd.java index 663f22d37c6..b4322ab7f47 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/EmeriaShepherd.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/EmeriaShepherd.java @@ -31,14 +31,20 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.LandfallAbility; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; +import mage.cards.CardsImpl; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; /** @@ -62,8 +68,9 @@ public class EmeriaShepherd extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Landfall — Whenever a land enters the battlefield under your control, you may return target nonland permanent card from your graveyard to your hand. If that land is a Plains, you may return that nonland permanent card to the battlefield instead. - Ability ability = new LandfallAbility(new ReturnToHandTargetEffect(), true); + // Landfall — Whenever a land enters the battlefield under your control, you may return target nonland permanent card from your graveyard to your hand. + // If that land is a Plains, you may return that nonland permanent card to the battlefield instead. + Ability ability = new LandfallAbility(Zone.BATTLEFIELD, new EmeriaShepherdReturnToHandTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(new FilterPermanentCard(filter))); this.addAbility(ability); } @@ -77,3 +84,36 @@ public class EmeriaShepherd extends CardImpl { return new EmeriaShepherd(this); } } + +class EmeriaShepherdReturnToHandTargetEffect extends OneShotEffect { + + public EmeriaShepherdReturnToHandTargetEffect() { + super(Outcome.ReturnToHand); + staticText = "you may return target nonland permanent card from your graveyard to your hand. If that land is a Plains, you may return that nonland permanent card to the battlefield instead"; + } + + public EmeriaShepherdReturnToHandTargetEffect(final EmeriaShepherdReturnToHandTargetEffect effect) { + super(effect); + } + + @Override + public EmeriaShepherdReturnToHandTargetEffect copy() { + return new EmeriaShepherdReturnToHandTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent triggeringLand = ((LandfallAbility) source).getTriggeringPermanent(); + if (controller == null || triggeringLand == null) { + return false; + } + Zone toZone = Zone.HAND; + if (triggeringLand.getSubtype().contains("Plains") + && controller.chooseUse(Outcome.PutCardInPlay, "Put the card to battlefield instead?", source, game)) { + toZone = Zone.BATTLEFIELD; + } + return controller.moveCards(new CardsImpl(targetPointer.getTargets(game, source)), null, toZone, source, game); + } + +} diff --git a/Mage/src/mage/abilities/common/LandfallAbility.java b/Mage/src/mage/abilities/common/LandfallAbility.java index 84f3e3087e4..17560ca6926 100644 --- a/Mage/src/mage/abilities/common/LandfallAbility.java +++ b/Mage/src/mage/abilities/common/LandfallAbility.java @@ -44,6 +44,7 @@ import mage.target.targetpointer.FixedTarget; public class LandfallAbility extends TriggeredAbilityImpl { protected SetTargetPointer setTargetPointer; + protected Permanent triggeringLand; public LandfallAbility(Effect effect, boolean optional) { this(Zone.BATTLEFIELD, effect, optional); @@ -61,6 +62,7 @@ public class LandfallAbility extends TriggeredAbilityImpl { public LandfallAbility(final LandfallAbility ability) { super(ability); this.setTargetPointer = ability.setTargetPointer; + this.triggeringLand = ability.triggeringLand; } @Override @@ -74,6 +76,7 @@ public class LandfallAbility extends TriggeredAbilityImpl { if (permanent != null && permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId)) { + triggeringLand = permanent; if (setTargetPointer.equals(SetTargetPointer.PERMANENT)) { for (Effect effect : getEffects()) { effect.setTargetPointer(new FixedTarget(permanent, game)); @@ -94,4 +97,7 @@ public class LandfallAbility extends TriggeredAbilityImpl { return new LandfallAbility(this); } + public Permanent getTriggeringPermanent() { + return triggeringLand; + } } From 89a7170f58625b869afa801e654b04a3cb864264 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sun, 20 Sep 2015 11:04:23 +0300 Subject: [PATCH 11/29] Add PegasusToken and use it for existing cards. Implement card: Pegasus Refuge --- .../src/mage/sets/exodus/PegasusStampede.java | 18 +---- .../src/mage/sets/guildpact/StormHerd.java | 16 +---- .../src/mage/sets/mirage/SacredMesa.java | 23 ++----- .../src/mage/sets/tempest/PegasusRefuge.java | 66 +++++++++++++++++++ .../game/permanent/token/PegasusToken.java | 52 +++++++++++++++ 5 files changed, 125 insertions(+), 50 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/tempest/PegasusRefuge.java create mode 100644 Mage/src/mage/game/permanent/token/PegasusToken.java diff --git a/Mage.Sets/src/mage/sets/exodus/PegasusStampede.java b/Mage.Sets/src/mage/sets/exodus/PegasusStampede.java index 908aee2eb2e..7d9869a6633 100644 --- a/Mage.Sets/src/mage/sets/exodus/PegasusStampede.java +++ b/Mage.Sets/src/mage/sets/exodus/PegasusStampede.java @@ -32,12 +32,11 @@ import mage.MageInt; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.BuybackAbility; -import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.filter.common.FilterControlledLandPermanent; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.PegasusToken; import mage.target.common.TargetControlledPermanent; /** @@ -52,7 +51,7 @@ public class PegasusStampede extends CardImpl { // Buyback-Sacrifice a land. this.addAbility(new BuybackAbility(new SacrificeTargetCost(new TargetControlledPermanent(1,1, new FilterControlledLandPermanent(), true)))); - + // Put a 1/1 white Pegasus creature token with flying onto the battlefield. this.getSpellAbility().addEffect(new CreateTokenEffect(new PegasusToken())); } @@ -66,16 +65,3 @@ public class PegasusStampede extends CardImpl { return new PegasusStampede(this); } } - -class PegasusToken extends Token { - - PegasusToken() { - super("Pegasus", "1/1 white Pegasus creature token with flying"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - subtype.add("Pegasus"); - power = new MageInt(1); - toughness = new MageInt(1); - addAbility(FlyingAbility.getInstance()); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/guildpact/StormHerd.java b/Mage.Sets/src/mage/sets/guildpact/StormHerd.java index 0e623afe8bf..82a2fe9002e 100644 --- a/Mage.Sets/src/mage/sets/guildpact/StormHerd.java +++ b/Mage.Sets/src/mage/sets/guildpact/StormHerd.java @@ -31,11 +31,10 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.dynamicvalue.common.ControllerLifeCount; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.PegasusToken; /** * @@ -60,16 +59,3 @@ public class StormHerd extends CardImpl { return new StormHerd(this); } } - -class PegasusToken extends Token { - - public PegasusToken() { - super("Pegasus", "1/1 white Pegasus creature tokens with flying"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - subtype.add("Pegasus"); - power = new MageInt(1); - toughness = new MageInt(1); - addAbility(FlyingAbility.getInstance()); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/mirage/SacredMesa.java b/Mage.Sets/src/mage/sets/mirage/SacredMesa.java index 825bdbc7be6..1f44ac6bc21 100644 --- a/Mage.Sets/src/mage/sets/mirage/SacredMesa.java +++ b/Mage.Sets/src/mage/sets/mirage/SacredMesa.java @@ -35,7 +35,6 @@ import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; -import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; @@ -43,7 +42,7 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.PegasusToken; import mage.target.common.TargetControlledPermanent; /** @@ -51,7 +50,7 @@ import mage.target.common.TargetControlledPermanent; * @author emerald000 */ public class SacredMesa extends CardImpl { - + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Pegasus"); static { filter.add(new SubtypePredicate("Pegasus")); @@ -61,12 +60,11 @@ public class SacredMesa extends CardImpl { super(ownerId, 241, "Sacred Mesa", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); this.expansionSetCode = "MIR"; - // At the beginning of your upkeep, sacrifice Sacred Mesa unless you sacrifice a Pegasus. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new SacrificeTargetCost(new TargetControlledPermanent(filter))), TargetController.YOU, false)); - + // {1}{W}: Put a 1/1 white Pegasus creature token with flying onto the battlefield. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SacredMesaPegasusToken()), new ManaCostsImpl<>("{1}{W}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new PegasusToken()), new ManaCostsImpl<>("{1}{W}"))); } public SacredMesa(final SacredMesa card) { @@ -78,16 +76,3 @@ public class SacredMesa extends CardImpl { return new SacredMesa(this); } } - -class SacredMesaPegasusToken extends Token { - - SacredMesaPegasusToken() { - super("Pegasus", "1/1 white Pegasus creature token with flying"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - subtype.add("Pegasus"); - power = new MageInt(1); - toughness = new MageInt(1); - addAbility(FlyingAbility.getInstance()); - } -} diff --git a/Mage.Sets/src/mage/sets/tempest/PegasusRefuge.java b/Mage.Sets/src/mage/sets/tempest/PegasusRefuge.java new file mode 100644 index 00000000000..483a7e1925a --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/PegasusRefuge.java @@ -0,0 +1,66 @@ +/* + * 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.tempest; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.permanent.token.PegasusToken; + +/** + * + * @author LoneFox + */ +public class PegasusRefuge extends CardImpl { + + public PegasusRefuge(UUID ownerId) { + super(ownerId, 247, "Pegasus Refuge", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + this.expansionSetCode = "TMP"; + + // {2}, Discard a card: Put a 1/1 white Pegasus creature token with flying onto the battlefield. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new PegasusToken()), new ManaCostsImpl("{2}")); + ability.addCost(new DiscardCardCost()); + this.addAbility(ability); + } + + public PegasusRefuge(final PegasusRefuge card) { + super(card); + } + + @Override + public PegasusRefuge copy() { + return new PegasusRefuge(this); + } +} diff --git a/Mage/src/mage/game/permanent/token/PegasusToken.java b/Mage/src/mage/game/permanent/token/PegasusToken.java new file mode 100644 index 00000000000..12e90966ac0 --- /dev/null +++ b/Mage/src/mage/game/permanent/token/PegasusToken.java @@ -0,0 +1,52 @@ +/* +* 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 O +R +* 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.game.permanent.token; + +import mage.constants.CardType; +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; + +/** + * + * @author LoneFox + */ +public class PegasusToken extends Token { + + public PegasusToken() { + super("Pegasus", "1/1 white Pegasus creature token with flying"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add("Pegasus"); + power = new MageInt(1); + toughness = new MageInt(1); + addAbility(FlyingAbility.getInstance()); + setOriginalExpansionSetCode("C14"); + } +} From 391d247e7c7c8ca9f937fabb2c2ba38df5a1d207 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 10:31:14 +0200 Subject: [PATCH 12/29] * Brutal Expulsion - Fixed that also spell targets were handled correctly. --- .../sets/futuresight/VenserShaperSavant.java | 64 +----------------- .../abilities/activated/ReturnToHandTest.java | 66 ++++++++++++++----- .../base/impl/CardTestPlayerAPIImpl.java | 3 +- Mage/src/mage/players/PlayerImpl.java | 19 ++++++ 4 files changed, 71 insertions(+), 81 deletions(-) diff --git a/Mage.Sets/src/mage/sets/futuresight/VenserShaperSavant.java b/Mage.Sets/src/mage/sets/futuresight/VenserShaperSavant.java index 1045ba09c7c..ff25793eca5 100644 --- a/Mage.Sets/src/mage/sets/futuresight/VenserShaperSavant.java +++ b/Mage.Sets/src/mage/sets/futuresight/VenserShaperSavant.java @@ -31,18 +31,11 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlashAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.players.Player; import mage.target.Target; import mage.target.common.TargetSpellOrPermanent; @@ -65,7 +58,7 @@ public class VenserShaperSavant extends CardImpl { // Flash this.addAbility(FlashAbility.getInstance()); // When Venser, Shaper Savant enters the battlefield, return target spell or permanent to its owner's hand. - Ability ability = new EntersBattlefieldTriggeredAbility(new VenserShaperSavantEffect(), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(true), false); Target target = new TargetSpellOrPermanent(); ability.addTarget(target); this.addAbility(ability); @@ -80,56 +73,3 @@ public class VenserShaperSavant extends CardImpl { return new VenserShaperSavant(this); } } - -class VenserShaperSavantEffect extends OneShotEffect { - - public VenserShaperSavantEffect() { - super(Outcome.ReturnToHand); - this.staticText = "return target spell or permanent to its owner's hand"; - } - - public VenserShaperSavantEffect(final VenserShaperSavantEffect effect) { - super(effect); - } - - @Override - public VenserShaperSavantEffect copy() { - return new VenserShaperSavantEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (permanent != null) { - return controller.moveCards(permanent, null, Zone.HAND, source, game); - } - - /** - * 01.05.2007 If a spell is returned to its owner's hand, it's - * removed from the stack and thus will not resolve. The spell isn't - * countered; it just no longer exists. 01.05.2007 If a copy of a - * spell is returned to its owner's hand, it's moved there, then it - * will cease to exist as a state-based action. 01.05.2007 If - * Venser's enters-the-battlefield ability targets a spell cast with - * flashback, that spell will be exiled instead of returning to its - * owner's hand. - */ - Spell spell = game.getStack().getSpell(this.getTargetPointer().getFirst(game, source)); - if (spell != null) { - Card card = null; - if (!spell.isCopy()) { - card = spell.getCard(); - } - game.getStack().remove(spell); - if (card != null) { - controller.moveCards(card, null, Zone.HAND, source, game); - } - return true; - } - - } - return false; - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java index 9864c33cfdb..5e55cb4d37d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReturnToHandTest.java @@ -36,7 +36,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class ReturnToHandTest extends CardTestPlayerBase { /** @@ -47,23 +46,23 @@ public class ReturnToHandTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); // Bloodthirst 3 // Flying - // {R}{R}{R}: Return Skarrgan Firebird from your graveyard to your hand. Activate this ability only if an opponent was dealt damage this turn. + // {R}{R}{R}: Return Skarrgan Firebird from your graveyard to your hand. Activate this ability only if an opponent was dealt damage this turn. addCard(Zone.BATTLEFIELD, playerB, "Skarrgan Firebird"); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); addCard(Zone.HAND, playerB, "Bone Splinters"); - + // As an additional cost to cast Bone Splinters, sacrifice a creature. // Destroy target creature. castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Bone Splinters", "Pillarfield Ox"); setChoice(playerB, "Skarrgan Firebird"); - + attack(2, playerB, "Silvercoat Lion"); - + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{R}{R}{R}: Return"); setStopAt(2, PhaseStep.END_TURN); - + execute(); assertPermanentCount(playerA, "Skarrgan Firebird", 0); @@ -79,40 +78,71 @@ public class ReturnToHandTest extends CardTestPlayerBase { @Test public void VeilbornGhoulTest1() { // Veilborn Ghoul can't block. - // Whenever a Swamp enters the battlefield under your control, you may return Veilborn Ghoul from your graveyard to your hand. + // Whenever a Swamp enters the battlefield under your control, you may return Veilborn Ghoul from your graveyard to your hand. addCard(Zone.GRAVEYARD, playerA, "Veilborn Ghoul"); addCard(Zone.HAND, playerA, "Swamp"); - + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp"); setStopAt(1, PhaseStep.BEGIN_COMBAT); - + execute(); - assertPermanentCount(playerA, "Swamp", 1); + assertPermanentCount(playerA, "Swamp", 1); assertHandCount(playerA, "Veilborn Ghoul", 1); } + /** - * Return from graveyard to hand if you play a non swamp land but Urborg, Tomb of Yawgmoth is in play + * Return from graveyard to hand if you play a non swamp land but Urborg, + * Tomb of Yawgmoth is in play */ @Test public void VeilbornGhoulTest2() { // Veilborn Ghoul can't block. - // Whenever a Swamp enters the battlefield under your control, you may return Veilborn Ghoul from your graveyard to your hand. + // Whenever a Swamp enters the battlefield under your control, you may return Veilborn Ghoul from your graveyard to your hand. addCard(Zone.GRAVEYARD, playerA, "Veilborn Ghoul"); addCard(Zone.HAND, playerA, "Flood Plain"); - + // Each land is a Swamp in addition to its other land types. addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); - + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flood Plain"); setStopAt(1, PhaseStep.BEGIN_COMBAT); - + execute(); - assertPermanentCount(playerA, "Flood Plain", 1); + assertPermanentCount(playerA, "Flood Plain", 1); assertHandCount(playerA, "Veilborn Ghoul", 1); - } - + } + + /** + * Return a spell from stack to Hand + */ + @Test + public void BrutalExpulsionTest() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Devoid + // Choose one or both + // - Return target spell or creature to its owner's hand; + // or Brutal Expulsion deals 2 damage to target creature or planeswalker. If that permanent would be put into a graveyard this turn, exile it instead. + addCard(Zone.HAND, playerA, "Brutal Expulsion"); // {2}{U}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + addCard(Zone.HAND, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Pillarfield Ox"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Brutal Expulsion", "mode=1Pillarfield Ox^mode=2Silvercoat Lion", "Pillarfield Ox"); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertGraveyardCount(playerA, "Brutal Expulsion", 1); + assertExileCount("Silvercoat Lion", 1); + assertPermanentCount(playerB, "Pillarfield Ox", 0); + assertHandCount(playerB, "Pillarfield Ox", 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index f438ff6ed50..26e68f09440 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -935,7 +935,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param step * @param player * @param cardName - * @param targetName + * @param targetName for modal spells add the mode to the name e.g. + * "mode=2SilvercoatLion^mode3=PillarfieldOx" * @param spellOnStack */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) { diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 6a6f5efd5e9..0c0d8ebd7d1 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2934,6 +2934,18 @@ public abstract class PlayerImpl implements Player, Serializable { } } else { Card card = game.getCard(cardId); + if (card == null) { + Spell spell = game.getState().getStack().getSpell(cardId); + if (spell != null) { + if (!spell.isCopy()) { + card = spell.getCard(); + } else { + // If a spell is returned to its owner's hand, it's removed from the stack and thus will not resolve + game.getStack().remove(spell); + game.informPlayers(spell.getLogName() + " was removed from the stack"); + } + } + } if (card != null) { cardList.add(card); } @@ -2973,6 +2985,13 @@ public abstract class PlayerImpl implements Player, Serializable { case HAND: for (Card card : cards) { fromZone = game.getState().getZone(card.getId()); + if (fromZone == Zone.STACK) { + // If a spell is returned to its owner's hand, it's removed from the stack and thus will not resolve + Spell spell = game.getStack().getSpell(card.getId()); + if (spell != null) { + game.getStack().remove(spell); + } + } boolean hideCard = fromZone.equals(Zone.LIBRARY) || (card.isFaceDown(game) && !fromZone.equals(Zone.STACK) && !fromZone.equals(Zone.BATTLEFIELD)); if (moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard)) { From 2b1e66828b2d5db8983b1254e3ffb8929dc0333f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 10:33:21 +0200 Subject: [PATCH 13/29] * Infuse with the Elements - Fixed that the target gained haste instead of trample. --- .../mage/sets/battleforzendikar/InfuseWithTheElements.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/InfuseWithTheElements.java b/Mage.Sets/src/mage/sets/battleforzendikar/InfuseWithTheElements.java index 656f335ff61..080658ef9cb 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/InfuseWithTheElements.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/InfuseWithTheElements.java @@ -32,7 +32,7 @@ import mage.abilities.dynamicvalue.common.ColorsOfManaSpentToCastCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.constants.AbilityWord; import mage.constants.CardType; @@ -58,7 +58,7 @@ public class InfuseWithTheElements extends CardImpl { this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // That creature gains trample until end of turn. - effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); + effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("That creature gains trample until end of turn"); this.getSpellAbility().addEffect(effect); } From 258f903c483046fd6cb7f700a50b15949ca2ba13 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sun, 20 Sep 2015 11:45:41 +0300 Subject: [PATCH 14/29] Add Kor Soldier token and use it for existing cards. This also fixes a bug where Nomads' Assembly was creating regular Soldiers instead of Kor Soldiers. --- .../commander2014/NahiriTheLithomancer.java | 51 +++++++------------ .../sets/riseoftheeldrazi/NomadsAssembly.java | 4 +- .../mage/sets/zendikar/ConquerorsPledge.java | 16 +----- .../game/permanent/token/KorSoldierToken.java | 51 +++++++++++++++++++ 4 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 Mage/src/mage/game/permanent/token/KorSoldierToken.java diff --git a/Mage.Sets/src/mage/sets/commander2014/NahiriTheLithomancer.java b/Mage.Sets/src/mage/sets/commander2014/NahiriTheLithomancer.java index 826add7420e..c9f5dad4248 100644 --- a/Mage.Sets/src/mage/sets/commander2014/NahiriTheLithomancer.java +++ b/Mage.Sets/src/mage/sets/commander2014/NahiriTheLithomancer.java @@ -61,6 +61,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.token.KorSoldierToken; import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.Target; @@ -79,20 +80,20 @@ public class NahiriTheLithomancer extends CardImpl { this.expansionSetCode = "C14"; this.subtype.add("Nahiri"); - + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); // +2: Put a 1/1 white Kor Soldier creature token onto the battlefield. You may attach an Equipment you control to it. this.addAbility(new LoyaltyAbility(new NahiriTheLithomancerFirstAbilityEffect(), 2)); - + // -2: You may put an Equipment card from your hand or graveyard onto the battlefield. this.addAbility(new LoyaltyAbility(new NahiriTheLithomancerSecondAbilityEffect(), -2)); - + // -10: Put a colorless Equipment artifact token named Stoneforged Blade onto the battlefield. It has indestructible, "Equipped creature gets +5/+5 and has double strike," and equip {0}. Effect effect = new CreateTokenEffect(new NahiriTheLithomancerEquipmentToken()); effect.setText("Put a colorless Equipment artifact token named Stoneforged Blade onto the battlefield. It has indestructible, \"Equipped creature gets +5/+5 and has double strike,\" and equip {0}"); this.addAbility(new LoyaltyAbility(effect, -10)); - + // Nahiri, the Lithomancer can be your commander. this.addAbility(CanBeYourCommanderAbility.getInstance()); } @@ -108,31 +109,31 @@ public class NahiriTheLithomancer extends CardImpl { } class NahiriTheLithomancerFirstAbilityEffect extends OneShotEffect { - + private static final FilterControlledPermanent filter = new FilterControlledPermanent("an Equipment you control"); static { filter.add(new SubtypePredicate("Equipment")); } - + NahiriTheLithomancerFirstAbilityEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Put a 1/1 white Kor Soldier creature token onto the battlefield. You may attach an Equipment you control to it"; } - + NahiriTheLithomancerFirstAbilityEffect(final NahiriTheLithomancerFirstAbilityEffect effect) { super(effect); } - + @Override public NahiriTheLithomancerFirstAbilityEffect copy() { return new NahiriTheLithomancerFirstAbilityEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Token token = new NahiriTheLithomancerKorSoldierToken(); + Token token = new KorSoldierToken(); if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { Permanent tokenPermanent = game.getPermanent(token.getLastAddedToken()); if (tokenPermanent != null) { @@ -160,41 +161,27 @@ class NahiriTheLithomancerFirstAbilityEffect extends OneShotEffect { } } -class NahiriTheLithomancerKorSoldierToken extends Token { - - NahiriTheLithomancerKorSoldierToken() { - super("Kor Soldier", "1/1 white Kor Soldier creature token"); - setOriginalExpansionSetCode("C14"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - subtype.add("Kor"); - subtype.add("Soldier"); - power = new MageInt(1); - toughness = new MageInt(1); - } -} - class NahiriTheLithomancerSecondAbilityEffect extends OneShotEffect { - + private static final FilterCard filter = new FilterCard("an Equipment"); static { filter.add(new SubtypePredicate("Equipment")); } - + NahiriTheLithomancerSecondAbilityEffect() { super(Outcome.PutCardInPlay); this.staticText = "You may put an Equipment card from your hand or graveyard onto the battlefield"; } - + NahiriTheLithomancerSecondAbilityEffect(final NahiriTheLithomancerSecondAbilityEffect effect) { super(effect); } - + @Override public NahiriTheLithomancerSecondAbilityEffect copy() { return new NahiriTheLithomancerSecondAbilityEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -227,13 +214,13 @@ class NahiriTheLithomancerEquipmentToken extends Token { super("Stoneforged Blade", "colorless Equipment artifact token named Stoneforged Blade with indestructible, \"Equipped creature gets +5/+5 and has double strike,\" and equip {0}"); cardType.add(CardType.ARTIFACT); subtype.add("Equipment"); - + this.addAbility(IndestructibleAbility.getInstance()); - + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(5, 5)); ability.addEffect(new GainAbilityAttachedEffect(DoubleStrikeAbility.getInstance(), AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield, "and has double strike")); this.addAbility(ability); - + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(0))); } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/NomadsAssembly.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/NomadsAssembly.java index 7dd99c31499..12b1af89ef3 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/NomadsAssembly.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/NomadsAssembly.java @@ -35,7 +35,7 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.ReboundAbility; import mage.cards.CardImpl; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.permanent.token.SoldierToken; +import mage.game.permanent.token.KorSoldierToken; /** * @@ -50,7 +50,7 @@ public class NomadsAssembly extends CardImpl { this.expansionSetCode = "ROE"; - this.getSpellAbility().addEffect(new CreateTokenEffect(new SoldierToken(), new PermanentsOnBattlefieldCount(filter))); + this.getSpellAbility().addEffect(new CreateTokenEffect(new KorSoldierToken(), new PermanentsOnBattlefieldCount(filter))); this.addAbility(new ReboundAbility()); } diff --git a/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java b/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java index de37a7675b9..02fbd720725 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java +++ b/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java @@ -37,7 +37,7 @@ import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; -import mage.game.permanent.token.Token; +import mage.game.permanent.token.KorSoldierToken; /** * @@ -67,17 +67,3 @@ public class ConquerorsPledge extends CardImpl { } } - -class KorSoldierToken extends Token { - - public KorSoldierToken() { - super("Kor Soldier", "1/1 white Kor Soldier creature token"); - cardType.add(CardType.CREATURE); - color.setWhite(true); - subtype.add("Kor"); - subtype.add("Soldier"); - power = new MageInt(1); - toughness = new MageInt(1); - } - -} diff --git a/Mage/src/mage/game/permanent/token/KorSoldierToken.java b/Mage/src/mage/game/permanent/token/KorSoldierToken.java new file mode 100644 index 00000000000..8f6b41c93bd --- /dev/null +++ b/Mage/src/mage/game/permanent/token/KorSoldierToken.java @@ -0,0 +1,51 @@ +/* +* 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 O +R +* 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.game.permanent.token; + +import java.util.Arrays; +import mage.constants.CardType; +import mage.MageInt; + +/** + * + * @author LoneFox + */ +public class KorSoldierToken extends Token { + + public KorSoldierToken() { + super("Kor Soldier", "1/1 white Kor Soldier creature token"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add("Kor"); + subtype.add("Soldier"); + power = new MageInt(1); + toughness = new MageInt(1); + availableImageSetCodes.addAll(Arrays.asList("C14", "ZEN")); } +} From daa954c785a111a155a5a8f408dfbf959859f1eb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 18:10:34 +0200 Subject: [PATCH 15/29] * Elder Pine of Jukai - Fixed that it also triggered if other players cast Spirit or Arcane spells. --- .../src/mage/sets/saviorsofkamigawa/ElderPineOfJukai.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ElderPineOfJukai.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ElderPineOfJukai.java index 860c187ec2f..91e83fce440 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ElderPineOfJukai.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ElderPineOfJukai.java @@ -31,7 +31,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.SoulshiftAbility; import mage.cards.Card; @@ -61,7 +61,7 @@ public class ElderPineOfJukai extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, reveal the top three cards of your library. Put all land cards revealed this way into your hand and the rest on the bottom of your library in any order. - this.addAbility(new SpellCastAllTriggeredAbility(new ElderPineOfJukaiEffect(), new FilterSpiritOrArcaneCard(), false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ElderPineOfJukaiEffect(), new FilterSpiritOrArcaneCard(), false)); // Soulshift 2 this.addAbility(new SoulshiftAbility(2)); From b2ff7ac380c799b994f1a6f754f093674671506a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 19:13:18 +0200 Subject: [PATCH 16/29] * Monocolor hybrid mana - Fixed that the payment did not always try to pay the colored cost if possible. --- .../sets/battleforzendikar/OblivionSower.java | 4 +- .../mage/sets/bornofthegods/Mindreaver.java | 33 +++++----- .../sets/journeyintonyx/DictateOfErebos.java | 9 ++- .../sets/magic2014/ChandraPyromaster.java | 38 +++++------- .../mage/test/cards/mana/HybridManaTest.java | 62 +++++++++++++++++++ .../abilities/costs/mana/ManaCostsImpl.java | 32 ++++++++++ .../common/SacrificeOpponentsEffect.java | 17 ++--- 7 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java b/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java index bfc7d1b0917..80518ad1c23 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java @@ -106,7 +106,9 @@ class OblivionSowerEffect extends OneShotEffect { Cards exiledLands = new CardsImpl(); exiledLands.addAll(exiledCards.getCards(filter, source.getSourceId(), controller.getId(), game)); if (!exiledLands.isEmpty() && controller.chooseUse(outcome, "Put lands into play?", source, game)) { - FilterCard filterToPlay = new FilterCard("Lands owned by " + targetPlayer.getName() + " to put into play under your control"); + FilterCard filterToPlay = new FilterCard("land" + + (exiledLands.size() > 1 ? "s" : "") + " from exile owned by " + + targetPlayer.getName() + " to put into play under your control"); TargetCard targetCards = new TargetCard(0, exiledLands.size(), Zone.EXILED, filterToPlay); if (controller.chooseTarget(outcome, exiledLands, targetCards, source, game)) { controller.moveCards(new CardsImpl(targetCards.getTargets()), null, Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java b/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java index 231854d0afb..c5df6d378d0 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java @@ -58,8 +58,8 @@ import mage.target.TargetSpell; import mage.util.CardUtil; /** - import mage.constants.Outcome; -* + * import mage.constants.Outcome; + * * @author LevelX2 */ public class Mindreaver extends CardImpl { @@ -77,10 +77,10 @@ public class Mindreaver extends CardImpl { Ability ability = new HeroicAbility(new MindreaverExileEffect(), false); ability.addTarget(new TargetPlayer()); this.addAbility(ability); - - // {U}{U}, Sacrifice Mindreaver: Counter target spell with the same name as a card exiled with mindreaver. + + // {U}{U}, Sacrifice Mindreaver: Counter target spell with the same name as a card exiled with Mindreaver. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}{U}")); - FilterSpell filter = new FilterSpell("spell with the same name as a card exiled with mindreaver"); + FilterSpell filter = new FilterSpell("spell with the same name as a card exiled with {this}"); filter.add(new MindreaverNamePredicate(this.getId())); ability.addTarget(new TargetSpell(filter)); ability.addCost(new SacrificeSourceCost()); @@ -116,12 +116,13 @@ class MindreaverExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { UUID exileId = CardUtil.getCardExileZoneId(game, source); + MageObject sourceObject = source.getSourceObject(game); Player opponent = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (opponent != null) { + if (opponent != null && sourceObject != null) { for (int i = 0; i < 3; i++) { Card card = opponent.getLibrary().getFromTop(game); if (card != null) { - card.moveToExile(exileId, "Mindreaver", source.getSourceId(), game); + card.moveToExile(exileId, sourceObject.getIdName(), source.getSourceId(), game); } } } @@ -130,29 +131,29 @@ class MindreaverExileEffect extends OneShotEffect { } class MindreaverNamePredicate implements Predicate { - + private final UUID sourceId; - + public MindreaverNamePredicate(UUID sourceId) { this.sourceId = sourceId; } - + @Override public boolean apply(MageObject input, Game game) { Set cardNames = new HashSet(); UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null) { - for(Card card : exileZone.getCards(game)) { + for (Card card : exileZone.getCards(game)) { cardNames.add(card.getName()); } } - // If a player names a card, the player may name either half of a split card, but not both. + // If a player names a card, the player may name either half of a split card, but not both. // A split card has the chosen name if one of its two names matches the chosen name. if (input instanceof SplitCard) { - return cardNames.contains(((SplitCard)input).getLeftHalfCard().getName()) || cardNames.contains(((SplitCard)input).getRightHalfCard().getName()); - } else if (input instanceof Spell && ((Spell)input).getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)){ - SplitCard card = (SplitCard) ((Spell)input).getCard(); + return cardNames.contains(((SplitCard) input).getLeftHalfCard().getName()) || cardNames.contains(((SplitCard) input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { + SplitCard card = (SplitCard) ((Spell) input).getCard(); return cardNames.contains(card.getLeftHalfCard().getName()) || cardNames.contains(card.getRightHalfCard().getName()); } else { return cardNames.contains(input.getName()); @@ -163,4 +164,4 @@ class MindreaverNamePredicate implements Predicate { public String toString() { return "spell with the same name as a card exiled with {source}"; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java b/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java index 6fd46c4aaf0..82b8bc6e849 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java @@ -44,22 +44,21 @@ import mage.filter.predicate.permanent.ControllerPredicate; * @author LevelX2 */ public class DictateOfErebos extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); - + static { filter.add(new ControllerPredicate(TargetController.YOU)); } - + public DictateOfErebos(UUID ownerId) { super(ownerId, 65, "Dictate of Erebos", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); this.expansionSetCode = "JOU"; - // Flash this.addAbility(FlashAbility.getInstance()); // Whenever a creature you control dies, each opponent sacrifices a creature. - this.addAbility(new DiesCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")), false, filter)); } public DictateOfErebos(final DictateOfErebos card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java b/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java index 1ce15ed3de0..8c3d87f6aad 100644 --- a/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java +++ b/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java @@ -74,7 +74,6 @@ public class ChandraPyromaster extends CardImpl { this.expansionSetCode = "M14"; this.subtype.add("Chandra"); - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(4)), false)); // +1: Chandra, Pyromaster deals 1 damage to target player and 1 damage to up to one target creature that player controls. That creature can't block this turn. @@ -215,7 +214,7 @@ class ChandraPyromasterEffect2 extends OneShotEffect { Card card = library.removeFromTop(game); if (card != null) { controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName() + " ", source.getSourceId(), game, Zone.LIBRARY, true); - ContinuousEffect effect = new ChandraPyromasterCastFromExileEffect(); + ContinuousEffect effect = new ChandraPyromasterCastFromExileEffect(); effect.setTargetPointer(new FixedTarget(card.getId())); game.addEffect(effect, source); } @@ -273,36 +272,31 @@ class ChandraPyromasterEffect3 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you == null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } Cards cards = new CardsImpl(); - int max = Math.min(you.getLibrary().size(), 10); - for (int i = 0; i < max; i++) { - Card card = you.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToExile(source.getSourceId(), "Chandra Pyromaster", source.getSourceId(), game); - cards.add(card); - } - } + cards.addAll(controller.getLibrary().getTopCards(game, 10)); + controller.moveCardsToExile(cards.getCards(game), source, game, true, source.getSourceId(), sourceObject.getIdName()); if (cards.getCards(new FilterInstantOrSorceryCard(), game).size() > 0) { TargetCard target = new TargetCard(Zone.EXILED, new FilterInstantOrSorceryCard()); - if (you.chooseTarget(Outcome.PlayForFree, cards, target, source, game)) { + if (controller.chooseTarget(Outcome.PlayForFree, cards, target, source, game)) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { - Card copy1 = card.copy(); - Card copy2 = card.copy(); - Card copy3 = card.copy(); - if (copy1 != null && you.chooseUse(outcome, "Do you wish to cast copy 1 of " + card.getName(), source, game)) { - you.cast(copy1.getSpellAbility(), game, true); + if (controller.chooseUse(outcome, "Do you wish to cast copy 1 of " + card.getName(), source, game)) { + Card copy1 = card.copy(); + controller.cast(copy1.getSpellAbility(), game, true); } - if (copy2 != null && you.chooseUse(outcome, "Do you wish to cast copy 2 of " + card.getName(), source, game)) { - you.cast(copy2.getSpellAbility(), game, true); + if (controller.chooseUse(outcome, "Do you wish to cast copy 2 of " + card.getName(), source, game)) { + Card copy2 = card.copy(); + controller.cast(copy2.getSpellAbility(), game, true); } - if (copy3 != null && you.chooseUse(outcome, "Do you wish to cast copy 3 of " + card.getName(), source, game)) { - you.cast(copy3.getSpellAbility(), game, true); + if (controller.chooseUse(outcome, "Do you wish to cast copy 3 of " + card.getName(), source, game)) { + Card copy3 = card.copy(); + controller.cast(copy3.getSpellAbility(), game, true); } return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java new file mode 100644 index 00000000000..d39028ab764 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java @@ -0,0 +1,62 @@ +/* + * 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 org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class HybridManaTest extends CardTestPlayerBase { + + @Test + public void testCastReaperKingMonoHybrid() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // Other Scarecrow creatures you control get +1/+1. + // Whenever another Scarecrow enters the battlefield under your control, destroy target permanent. + addCard(Zone.HAND, playerA, "Reaper King", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reaper King"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Reaper King", 1); + + } + +} diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java index c6d7654244a..5c6cb083be8 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java @@ -228,30 +228,62 @@ public class ManaCostsImpl extends ArrayList implements M for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof ColoredManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof HybridManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } + // Mono Hybrid mana costs + // First try only to pay colored mana with the pool + for (ManaCost cost : this) { + if (!cost.isPaid() && cost instanceof MonoHybridManaCost) { + if (((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.W)) && pool.getWhite() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.B)) && pool.getBlack() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.R)) && pool.getRed() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.G)) && pool.getGreen() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.U)) && pool.getBlue() > 0)) { + cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } + } + } + } + // if colored didn't fit pay colorless with the mana for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof MonoHybridManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof SnowManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof GenericManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java index 6edd6c83449..54e94ab31b8 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common; import java.util.ArrayList; @@ -46,8 +45,7 @@ import mage.target.TargetPermanent; import mage.util.CardUtil; /** - * All opponents have to sacrifice [amount] permanents - * that match the [filter]. + * All opponents have to sacrifice [amount] permanents that match the [filter]. * * @author LevelX2 */ @@ -59,6 +57,7 @@ public class SacrificeOpponentsEffect extends OneShotEffect { public SacrificeOpponentsEffect(FilterPermanent filter) { this(1, filter); } + public SacrificeOpponentsEffect(int amount, FilterPermanent filter) { this(new StaticValue(amount), filter); } @@ -87,12 +86,14 @@ public class SacrificeOpponentsEffect extends OneShotEffect { filter.add(new ControllerPredicate(TargetController.YOU)); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); - if (player != null) { + if (player != null) { int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game)); - TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true); - if (target.canChoose(player.getId(), game)) { - player.chooseTarget(Outcome.Sacrifice, target, source, game); - perms.addAll(target.getTargets()); + if (numTargets > 0) { + TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true); + if (target.canChoose(player.getId(), game)) { + player.chooseTarget(Outcome.Sacrifice, target, source, game); + perms.addAll(target.getTargets()); + } } } } From 75bb39cae83bf5818a1ee35508bc99a517f981e7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 19:22:32 +0200 Subject: [PATCH 17/29] * Evolutionary Leap - Fixed that also if no creature card was in the libraray a card was moved to the controller hand. --- .../sets/magicorigins/EvolutionaryLeap.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magicorigins/EvolutionaryLeap.java b/Mage.Sets/src/mage/sets/magicorigins/EvolutionaryLeap.java index 60209bb3d7c..b5cd46c4577 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/EvolutionaryLeap.java +++ b/Mage.Sets/src/mage/sets/magicorigins/EvolutionaryLeap.java @@ -96,7 +96,7 @@ class EvolutionaryLeapEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && controller.getLibrary().size() > 0) { + if (controller != null && controller.getLibrary().size() > 0) { Cards cards = new CardsImpl(); Library library = controller.getLibrary(); Card card = null; @@ -108,22 +108,27 @@ class EvolutionaryLeapEffect extends OneShotEffect { } while (library.size() > 0 && card != null && !filter.match(card, game)); // reveal cards if (!cards.isEmpty()) { - controller.revealCards(sourceObject.getName(), cards, game); - } - // put creature card in hand - controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); - // remove it from revealed card list - cards.remove(card); - // Put the rest on the bottom of your library in a random order - while (cards.size() > 0) { - card = cards.getRandom(game); - if (card != null) { + controller.revealCards(sourceObject.getIdName(), cards, game); + if (filter.match(card, game)) { + // put creature card in hand + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); + // remove it from revealed card list cards.remove(card); - controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, false, false); } + // Put the rest on the bottom of your library in a random order + Cards randomOrder = new CardsImpl(); + while (cards.size() > 0) { + card = cards.getRandom(game); + if (card != null) { + cards.remove(card); + randomOrder.add(card); + controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, false, false); + } + } + controller.putCardsOnBottomOfLibrary(randomOrder, game, source, false); } return true; } return false; } -} \ No newline at end of file +} From ed15f0b86be9bc84dad9bf8da38fa0552f7e0a7e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 20:23:36 +0200 Subject: [PATCH 18/29] Fixed that not BFZ images were downloaded from mysticspoliers source. Fixed that Zendikar Expeditions images were not downloaded from mysticspoliers source. --- .../dl/sources/MythicspoilerComSource.java | 57 +++++++++++++------ .../src/main/resources/image.url.properties | 2 +- .../battleforzendikar/TitansPresence.java | 2 +- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java index 0728084c111..d366c4e4623 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java @@ -36,7 +36,10 @@ import java.net.Proxy; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.prefs.Preferences; import mage.client.MageFrame; import mage.remote.Connection; @@ -56,6 +59,7 @@ public class MythicspoilerComSource implements CardImageSource { private static CardImageSource instance; private static Map setsAliases; private static Map cardNameAliases; + private static Map> cardNameAliasesStart; private final Map> sets; public static CardImageSource getInstance() { @@ -71,8 +75,9 @@ public class MythicspoilerComSource implements CardImageSource { } public MythicspoilerComSource() { - sets = new HashMap<>(); + sets = new LinkedHashMap<>(); setsAliases = new HashMap<>(); + setsAliases.put("exp", "bfz"); cardNameAliases = new HashMap<>(); // set+wrong name from web side => correct card name cardNameAliases.put("MM2-otherwordlyjourney", "otherworldlyjourney"); @@ -81,12 +86,21 @@ public class MythicspoilerComSource implements CardImageSource { cardNameAliases.put("THS-soldierofpantheon", "soldierofthepantheon"); cardNameAliases.put("THS-vulpinegolaith", "vulpinegoliath"); cardNameAliases.put("ORI-kothopedhoarderofsouls", "kothophedsoulhoarder"); + cardNameAliases.put("BFZ-unisonstrike", "tandemtactics"); + cardNameAliases.put("BFZ-eldrazidevastator", "eldrazidevastator"); + cardNameAliases.put("BFZ-kozliekschanneler", "kozilekschanneler"); + + cardNameAliasesStart = new HashMap<>(); + HashSet names = new HashSet<>(); + names.add("eldrazidevastator.jpg"); + cardNameAliasesStart.put("BFZ", names); } private Map getSetLinks(String cardSet) { Map setLinks = new HashMap<>(); try { String setNames = setsAliases.get(cardSet.toLowerCase()); + Set aliasesStart = cardNameAliasesStart.get(cardSet); if (setNames == null) { setNames = cardSet.toLowerCase(); } @@ -119,33 +133,40 @@ public class MythicspoilerComSource implements CardImageSource { } Elements cardsImages = doc.select("img[src^=cards/]"); // starts with cards/ + for (String text : aliasesStart) { + cardsImages.addAll(doc.select("img[src^=" + text + "]")); + } if (cardsImages.isEmpty()) { break; } for (Element cardsImage : cardsImages) { String cardLink = cardsImage.attr("src"); + String cardName = null; if (cardLink.startsWith("cards/") && cardLink.endsWith(".jpg")) { - String cardName = cardLink.substring(6, cardLink.length() - 4); - if (cardName != null && !cardName.isEmpty()) { - if (cardNameAliases.containsKey(cardSet + "-" + cardName)) { - cardName = cardNameAliases.get(cardSet + "-" + cardName); - } - if (cardName.endsWith("1") || cardName.endsWith("2") || cardName.endsWith("3") || cardName.endsWith("4") || cardName.endsWith("5")) { - if (!cardName.startsWith("forest") - && !cardName.startsWith("swamp") - && !cardName.startsWith("mountain") - && !cardName.startsWith("island") - && !cardName.startsWith("plains")) { - cardName = cardName.substring(0, cardName.length() - 1); - } - } - setLinks.put(cardName, baseUrl + cardLink); - } + cardName = cardLink.substring(6, cardLink.length() - 4); + } else if (aliasesStart.contains(cardLink)) { + cardName = cardLink.substring(0, cardLink.length() - 4);; + } + if (cardName != null && !cardName.isEmpty()) { + if (cardNameAliases.containsKey(cardSet + "-" + cardName)) { + cardName = cardNameAliases.get(cardSet + "-" + cardName); + } + if (cardName.endsWith("1") || cardName.endsWith("2") || cardName.endsWith("3") || cardName.endsWith("4") || cardName.endsWith("5")) { + if (!cardName.startsWith("forest") + && !cardName.startsWith("swamp") + && !cardName.startsWith("mountain") + && !cardName.startsWith("island") + && !cardName.startsWith("plains")) { + cardName = cardName.substring(0, cardName.length() - 1); + } + } + setLinks.put(cardName, baseUrl + cardLink); } - } + } + } catch (IOException ex) { System.out.println("Exception when parsing the mythicspoiler page: " + ex.getMessage()); } diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index f0342705c71..fc6e7f06ec3 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -64,6 +64,6 @@ ddd=gvl unh=uh dde=pvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK,EXP,OGW +ignore.urls=TOK, OGW # sets ordered by release time (newest goes first) token.lookup.order=OGW,EXP,DDP,BFZ,FVD,FVE,FVL,FVR,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/TitansPresence.java b/Mage.Sets/src/mage/sets/battleforzendikar/TitansPresence.java index f19f583b71d..e9910dbcd65 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/TitansPresence.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/TitansPresence.java @@ -52,7 +52,7 @@ import mage.target.common.TargetCreaturePermanent; */ public class TitansPresence extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("a colorless creature card from your hand to reveal"); + private static final FilterCreatureCard filter = new FilterCreatureCard("a colorless creature card from your hand"); static { filter.add(new ColorlessPredicate()); From e94163a4c3a9cf229857b45eed3e210587bced49 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 21:10:02 +0200 Subject: [PATCH 19/29] Fixed that no Zendikar Expeditions lands were added to Battle for Zendikar boosters. I replaced every 20th basic land now by one random land of Zendikar Expeditions. --- .../src/mage/sets/BattleForZendikar.java | 24 +++++++++++++++++-- Mage/src/mage/cards/ExpansionSet.java | 14 ++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/BattleForZendikar.java b/Mage.Sets/src/mage/sets/BattleForZendikar.java index b6dc6ad2d77..f21179f9746 100644 --- a/Mage.Sets/src/mage/sets/BattleForZendikar.java +++ b/Mage.Sets/src/mage/sets/BattleForZendikar.java @@ -25,18 +25,21 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets; +import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.List; import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.constants.SetType; /** * * @author fireshoes */ - public class BattleForZendikar extends ExpansionSet { private static final BattleForZendikar fINSTANCE = new BattleForZendikar(); @@ -45,16 +48,33 @@ public class BattleForZendikar extends ExpansionSet { return fINSTANCE; } + List savedSpecialLand = new ArrayList<>(); + private BattleForZendikar() { super("Battle for Zendikar", "BFZ", "mage.sets.battleforzendikar", new GregorianCalendar(2015, 10, 2).getTime(), SetType.EXPANSION); this.blockName = "Battle for Zendikar"; this.hasBoosters = true; this.hasBasicLands = true; this.numBoosterLands = 1; + this.ratioBoosterSpecialLand = 20; // Approximately as rare as opening a foil mythic = 8 * 6 = ~every 48th booster includes one + // I set it to 20 to get it more often this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.numBoosterSpecial = 0; } + @Override + public List getSpecialLand() { + List specialLand = new ArrayList<>(); + if (savedSpecialLand.isEmpty()) { + CardCriteria criteria = new CardCriteria(); + criteria.setCodes("EXP"); + specialLand.addAll(CardRepository.instance.findCards(criteria)); + } + + specialLand.addAll(savedSpecialLand); + return specialLand; + } } diff --git a/Mage/src/mage/cards/ExpansionSet.java b/Mage/src/mage/cards/ExpansionSet.java index 23c5649fbe3..7758bb84a3f 100644 --- a/Mage/src/mage/cards/ExpansionSet.java +++ b/Mage/src/mage/cards/ExpansionSet.java @@ -56,7 +56,10 @@ public abstract class ExpansionSet implements Serializable { protected String blockName; protected boolean hasBoosters = false; protected int numBoosterSpecial; + protected int numBoosterLands; + protected int ratioBoosterSpecialLand = 0; // if > 0 basic lands are replaced with speical land in the ratio every X land is replaced by special land + protected int numBoosterCommon; protected int numBoosterUncommon; protected int numBoosterRare; @@ -150,9 +153,14 @@ public abstract class ExpansionSet implements Serializable { } if (numBoosterLands > 0) { + List specialLands = getSpecialLand(); List basicLands = getCardsByRarity(Rarity.LAND); for (int i = 0; i < numBoosterLands; i++) { - addToBooster(booster, basicLands); + if (ratioBoosterSpecialLand > 0 && rnd.nextInt(ratioBoosterSpecialLand) == 1 && specialLands != null) { + addToBooster(booster, specialLands); + } else { + addToBooster(booster, basicLands); + } } } List commons = getCardsByRarity(Rarity.COMMON); @@ -320,6 +328,10 @@ public abstract class ExpansionSet implements Serializable { return null; } + public List getSpecialLand() { + return null; + } + public void removeSavedCards() { savedCards.clear(); } From 322eae2ec69a3b31c8f7c5fc3216d30a009658e6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 00:31:42 +0200 Subject: [PATCH 20/29] Deck editor - renamed sort type "Color Detailed" to "Color Identity" and compare for the sort also with mana symbols in casting cost and rules. --- .../main/java/mage/client/cards/CardGrid.java | 96 +++++----- .../java/mage/client/cards/CardsList.java | 176 +++++++++--------- .../java/mage/client/constants/Constants.java | 79 ++++---- ...a => CardViewColorIdentityComparator.java} | 18 +- .../client/util/CardViewCostComparator.java | 13 +- Mage.Common/src/mage/utils/CardUtil.java | 38 ++++ 6 files changed, 233 insertions(+), 187 deletions(-) rename Mage.Client/src/main/java/mage/client/util/{CardViewColorDetailedComparator.java => CardViewColorIdentityComparator.java} (71%) diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java index a2369fe3149..9940628f998 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * 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. @@ -31,7 +31,6 @@ * * Created on 30-Mar-2010, 9:25:40 PM */ - package mage.client.cards; import java.awt.Component; @@ -54,6 +53,7 @@ import mage.client.plugins.impl.Plugins; import mage.client.util.Config; import mage.client.util.Event; import mage.client.util.Listener; +import mage.utils.CardUtil; import mage.view.CardView; import mage.view.CardsView; import org.mage.card.arcane.CardPanel; @@ -82,7 +82,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } public void clear() { - for(MouseListener ml: this.getMouseListeners()) { + for (MouseListener ml : this.getMouseListeners()) { this.removeMouseListener(ml); } this.clearCardEventListeners(); @@ -101,7 +101,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, this.bigCard = bigCard; this.gameId = gameId; if (merge) { - for (CardView card: showCards.values()) { + for (CardView card : showCards.values()) { if (!cards.containsKey(card.getId())) { addCard(card, bigCard, gameId, drawImage); } @@ -115,7 +115,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } } else { this.clearCards(); - for (CardView card: showCards.values()) { + for (CardView card : showCards.values()) { addCard(card, bigCard, gameId, drawImage); } } @@ -144,7 +144,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, int curRow = 0; if (cards.size() > 0) { Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight); - List sortedCards = new ArrayList(cards.values()); + List sortedCards = new ArrayList<>(cards.values()); switch (sortSetting.getSortBy()) { case NAME: Collections.sort(sortedCards, new CardNameComparator()); @@ -155,15 +155,16 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, case COLOR: Collections.sort(sortedCards, new CardColorComparator()); break; - case COLOR_DETAILED: - Collections.sort(sortedCards, new CardColorDetailedComparator()); + case COLOR_IDENTITY: + Collections.sort(sortedCards, new CardColorDetailedIdentity()); break; case CASTING_COST: Collections.sort(sortedCards, new CardCostComparator()); break; + } MageCard lastCard = null; - for (MageCard cardImg: sortedCards) { + for (MageCard cardImg : sortedCards) { if (sortSetting.isPilesToggle()) { if (lastCard == null) { lastCard = cardImg; @@ -187,8 +188,9 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, curRow = 0; } break; - case COLOR_DETAILED: - if (cardImg.getOriginal().getColor().hashCode() != lastCard.getOriginal().getColor().hashCode()) { + case COLOR_IDENTITY: + if (CardUtil.getColorIdentitySortValue(cardImg.getOriginal().getManaCost(), cardImg.getOriginal().getColor(), cardImg.getOriginal().getRules()) + != CardUtil.getColorIdentitySortValue(lastCard.getOriginal().getManaCost(), lastCard.getOriginal().getColor(), lastCard.getOriginal().getRules())) { curColumn++; curRow = 0; } @@ -206,8 +208,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, moveToFront(cardImg); curRow++; lastCard = cardImg; - } - else { + } else { rectangle.setLocation(curColumn * Config.dimensions.frameWidth, curRow * 20); cardImg.setBounds(rectangle); cardImg.setCardBounds(rectangle.x, rectangle.y, Config.dimensions.frameWidth, Config.dimensions.frameHeight); @@ -221,15 +222,15 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } } resizeArea(); - revalidate(); - repaint(); + revalidate(); + repaint(); } private void clearCards() { // remove possible mouse listeners, preventing gc - for (MageCard mageCard: cards.values()) { + for (MageCard mageCard : cards.values()) { if (mageCard instanceof CardPanel) { - ((CardPanel)mageCard).cleanUp(); + ((CardPanel) mageCard).cleanUp(); } } this.cards.clear(); @@ -237,7 +238,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } private void removeAllCardImg() { - for (Component comp: getComponents()) { + for (Component comp : getComponents()) { if (comp instanceof Card || comp instanceof MageCard) { remove(comp); } @@ -245,14 +246,14 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } private void removeCardImg(UUID cardId) { - for (Component comp: getComponents()) { + for (Component comp : getComponents()) { if (comp instanceof Card) { - if (((Card)comp).getCardId().equals(cardId)) { + if (((Card) comp).getCardId().equals(cardId)) { remove(comp); comp = null; } } else if (comp instanceof MageCard) { - if (((MageCard)comp).getOriginal().getId().equals(cardId)) { + if (((MageCard) comp).getOriginal().getId().equals(cardId)) { remove(comp); comp = null; } @@ -275,10 +276,10 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, cardEventSource.clearListeners(); } - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents @@ -296,10 +297,8 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, ); }// //GEN-END:initComponents - // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables - @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2 && !e.isConsumed()) { @@ -322,22 +321,26 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } @Override - public void mousePressed(MouseEvent e) {} + public void mousePressed(MouseEvent e) { + } @Override - public void mouseReleased(MouseEvent e) {} + public void mouseReleased(MouseEvent e) { + } @Override - public void mouseEntered(MouseEvent e) {} + public void mouseEntered(MouseEvent e) { + } @Override - public void mouseExited(MouseEvent e) {} + public void mouseExited(MouseEvent e) { + } private void resizeArea() { Dimension area = new Dimension(0, 0); Dimension size = getPreferredSize(); - for (Component comp: getComponents()) { + for (Component comp : getComponents()) { Rectangle r = comp.getBounds(); if (r.x + r.width > area.width) { area.width = r.x + r.width; @@ -348,13 +351,13 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } if (size.height != area.height || size.width != area.width) { setPreferredSize(area); - } + } } @Override public void refresh() { revalidate(); - repaint(); + repaint(); } @Override @@ -379,8 +382,7 @@ class CardRarityComparator implements Comparator { int val = o1.getOriginal().getRarity().compareTo(o2.getOriginal().getRarity()); if (val == 0) { return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } - else { + } else { return val; } } @@ -394,8 +396,7 @@ class CardCostComparator implements Comparator { int val = Integer.valueOf(o1.getOriginal().getConvertedManaCost()).compareTo(Integer.valueOf(o2.getOriginal().getConvertedManaCost())); if (val == 0) { return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } - else { + } else { return val; } } @@ -409,23 +410,22 @@ class CardColorComparator implements Comparator { int val = o1.getOriginal().getColor().compareTo(o2.getOriginal().getColor()); if (val == 0) { return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } - else { + } else { return val; } } } -class CardColorDetailedComparator implements Comparator { +class CardColorDetailedIdentity implements Comparator { @Override public int compare(MageCard o1, MageCard o2) { - int val = o1.getOriginal().getColor().hashCode() - o2.getOriginal().getColor().hashCode(); + int val = CardUtil.getColorIdentitySortValue(o1.getOriginal().getManaCost(), o1.getOriginal().getColor(), o1.getOriginal().getRules()) + - CardUtil.getColorIdentitySortValue(o2.getOriginal().getManaCost(), o2.getOriginal().getColor(), o2.getOriginal().getRules()); if (val == 0) { return o1.getOriginal().getName().compareTo(o2.getOriginal().getName()); - } - else { + } else { return val; } } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java index 489f18d08ef..e4bf5d2b67f 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java @@ -1,37 +1,36 @@ /* -* 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. -*/ + * 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. + */ /* * CardsList.java * * Created on Dec 18, 2009, 10:40:12 AM */ - package mage.client.cards; import java.awt.Color; @@ -59,7 +58,7 @@ import mage.client.constants.Constants.DeckEditorMode; import mage.client.constants.Constants.SortBy; import static mage.client.constants.Constants.SortBy.CASTING_COST; import static mage.client.constants.Constants.SortBy.COLOR; -import static mage.client.constants.Constants.SortBy.COLOR_DETAILED; +import static mage.client.constants.Constants.SortBy.COLOR_IDENTITY; import static mage.client.constants.Constants.SortBy.RARITY; import mage.client.deckeditor.SortSetting; import mage.client.deckeditor.table.TableModel; @@ -67,7 +66,7 @@ import mage.client.deckeditor.table.UpdateCountsCallback; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.CardViewColorComparator; -import mage.client.util.CardViewColorDetailedComparator; +import mage.client.util.CardViewColorIdentityComparator; import mage.client.util.CardViewCostComparator; import mage.client.util.CardViewNameComparator; import mage.client.util.CardViewRarityComparator; @@ -86,7 +85,7 @@ import org.mage.card.arcane.CardPanel; * @author BetaSteward_at_googlemail.com */ public class CardsList extends javax.swing.JPanel implements MouseListener, ICardGrid { - + protected CardEventSource cardEventSource = new CardEventSource(); private Dimension cardDimension; private CardsView cards; @@ -98,8 +97,10 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar private TableModel mainModel; private JTable mainTable; private ICardGrid currentView; - - /** Creates new form Cards */ + + /** + * Creates new form Cards + */ public CardsList() { initComponents(); makeTransparent(); @@ -115,22 +116,22 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar mainModel.removeTableModelListener(mainTable); mainModel.clear(); } - if(cardArea != null) { - for(MouseListener ml: cardArea.getMouseListeners()) { + if (cardArea != null) { + for (MouseListener ml : cardArea.getMouseListeners()) { cardArea.removeMouseListener(ml); } } - if(mainTable != null) { - for(MouseListener ml: mainTable.getMouseListeners()) { + if (mainTable != null) { + for (MouseListener ml : mainTable.getMouseListeners()) { mainTable.removeMouseListener(ml); } } if (currentView != null) { currentView.clearCardEventListeners(); } - for (Component comp :cardArea.getComponents()) { - if (comp instanceof CardPanel) { - ((CardPanel)comp).cleanUp(); + for (Component comp : cardArea.getComponents()) { + if (comp instanceof CardPanel) { + ((CardPanel) comp).cleanUp(); } } mageCards.clear(); @@ -208,20 +209,20 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar // activate spinner for card number change mainModel.setNumberEditable(true); TableColumnModel tcm = mainTable.getColumnModel(); - TableColumn tc = tcm.getColumn(0); + TableColumn tc = tcm.getColumn(0); tc.setMaxWidth(55); tc.setMinWidth(55); tc.setPreferredWidth(55); - tc.setCellEditor(new TableSpinnerEditor(this)); + tc.setCellEditor(new TableSpinnerEditor(this)); } } - + public void handleSetNumber(int number) { if (mainTable.getSelectedRowCount() == 1) { mainModel.setNumber(mainTable.getSelectedRow(), number); - } + } } - + public void handleDoubleClick() { if (mainTable.getSelectedRowCount() > 0) { int[] n = mainTable.getSelectedRows(); @@ -232,7 +233,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar } } } - + public void handleAltDoubleClick() { if (mainTable.getSelectedRowCount() > 0) { int[] n = mainTable.getSelectedRows(); @@ -243,7 +244,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar } } } - + public ICardGrid getMainModel() { return mainModel; } @@ -256,7 +257,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar return list; } - public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) { int selectedRow = -1; if (currentView.equals(mainModel)) { @@ -270,7 +270,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar chkPiles.setSelected(sortSetting.isPilesToggle()); currentView.loadCards(showCards, sortSetting, bigCard, gameId); if (selectedRow >= 0) { - selectedRow = Math.min(selectedRow, mainTable.getRowCount()-1); + selectedRow = Math.min(selectedRow, mainTable.getRowCount() - 1); if (selectedRow >= 0) { mainTable.setRowSelectionInterval(selectedRow, selectedRow); } @@ -281,7 +281,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar if (cards == null) { cards = new CardsView(); } - currentView.loadCards(cards, sortSetting, bigCard, gameId); + currentView.loadCards(cards, sortSetting, bigCard, gameId); } @Override @@ -295,22 +295,21 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar Comparator comparator = null; Map oldMageCards = mageCards; mageCards = new LinkedHashMap<>(); - + //Find card view - for(UUID uuid : cards.keySet()){ - if(oldMageCards.containsKey(uuid)){ + for (UUID uuid : cards.keySet()) { + if (oldMageCards.containsKey(uuid)) { mageCards.put(uuid, oldMageCards.get(uuid)); oldMageCards.remove(uuid); - } - else{ + } else { mageCards.put(uuid, addCard(cards.get(uuid), bigCard, gameId)); } } //Remove unused cards - for(MageCard card : oldMageCards.values()){ + for (MageCard card : oldMageCards.values()) { cardArea.remove(card); } - + if (cards != null && cards.size() > 0) { Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight); List sortedCards = new ArrayList<>(cards.values()); @@ -324,25 +323,25 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar case COLOR: comparator = new CardViewColorComparator(); break; - case COLOR_DETAILED: - comparator = new CardViewColorDetailedComparator(); + case COLOR_IDENTITY: + comparator = new CardViewColorIdentityComparator(); break; case CASTING_COST: comparator = new CardViewCostComparator(); break; } - if(comparator != null){ + if (comparator != null) { Collections.sort(sortedCards, new CardViewNameComparator()); Collections.sort(sortedCards, comparator); } CardView lastCard = null; - for (CardView card: sortedCards) { + for (CardView card : sortedCards) { if (sortSetting.isPilesToggle()) { if (lastCard == null) { lastCard = card; } - if(comparator != null){ - if(comparator.compare(card, lastCard) > 0){ + if (comparator != null) { + if (comparator.compare(card, lastCard) > 0) { curColumn++; maxRow = Math.max(maxRow, curRow); curRow = 0; @@ -350,7 +349,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar } rectangle.setLocation(curColumn * Config.dimensions.frameWidth, curRow * 20); setCardBounds(mageCards.get(card.getId()), rectangle); - + curRow++; lastCard = card; } else { @@ -368,21 +367,21 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar maxRow = Math.max(maxRow, curRow); maxColumn = Math.max(maxColumn, curColumn); updateCounts(); - cardArea.setPreferredSize(new Dimension((maxColumn+1) * Config.dimensions.frameWidth, Config.dimensions.frameHeight + maxRow*20)); + cardArea.setPreferredSize(new Dimension((maxColumn + 1) * Config.dimensions.frameWidth, Config.dimensions.frameHeight + maxRow * 20)); cardArea.revalidate(); this.revalidate(); this.repaint(); this.setVisible(true); } - - private void updateCounts(){ + + private void updateCounts() { int landCount = 0; int creatureCount = 0; int sorceryCount = 0; int instantCount = 0; int enchantmentCount = 0; - for (CardView card: cards.values()) { - if (card.getCardTypes().contains(CardType.LAND)) { + for (CardView card : cards.values()) { + if (card.getCardTypes().contains(CardType.LAND)) { landCount++; } if (card.getCardTypes().contains(CardType.CREATURE)) { @@ -398,7 +397,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar enchantmentCount++; } } - + int count = cards != null ? cards.size() : 0; this.lblCount.setText(Integer.toString(count)); this.lblCreatureCount.setText(Integer.toString(creatureCount)); @@ -418,7 +417,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar cardImg.addMouseListener(this); return cardImg; } - private void setCardBounds(MageCard card, Rectangle rectangle) { card.setBounds(rectangle); @@ -456,10 +454,10 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar mainModel.clearCardEventListeners(); } - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents @@ -678,9 +676,9 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar currentView = mainModel; panelCardArea.setViewportView(mainTable); cbSortBy.setEnabled(false); - chkPiles.setEnabled(false); + chkPiles.setEnabled(false); PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "listView"); - redrawCards(); + redrawCards(); }//GEN-LAST:event_jToggleListViewActionPerformed private void cbSortByActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSortByActionPerformed @@ -698,8 +696,8 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar panelCardArea.setViewportView(cardArea); cbSortBy.setEnabled(true); chkPiles.setEnabled(true); - PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView"); - redrawCards(); + PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView"); + redrawCards(); }//GEN-LAST:event_jToggleCardViewActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables @@ -725,28 +723,26 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar @Override public void mousePressed(MouseEvent e) { - if (e.getClickCount() >= 1 && !e.isConsumed()) { + if (e.getClickCount() >= 1 && !e.isConsumed()) { Object obj = e.getSource(); if (e.getClickCount() == 2) { e.consume(); if (obj instanceof Card) { if (e.isAltDown()) { - cardEventSource.altDoubleClick(((Card)obj).getOriginal(), "alt-double-click"); - } - else { - cardEventSource.doubleClick(((Card)obj).getOriginal(), "double-click"); + cardEventSource.altDoubleClick(((Card) obj).getOriginal(), "alt-double-click"); + } else { + cardEventSource.doubleClick(((Card) obj).getOriginal(), "double-click"); } } else if (obj instanceof MageCard) { if (e.isAltDown()) { - cardEventSource.altDoubleClick(((MageCard)obj).getOriginal(), "alt-double-click"); - } - else { - cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "double-click"); + cardEventSource.altDoubleClick(((MageCard) obj).getOriginal(), "alt-double-click"); + } else { + cardEventSource.doubleClick(((MageCard) obj).getOriginal(), "double-click"); } } } if (obj instanceof MageCard) { - checkMenu(e, ((MageCard)obj).getOriginal()); + checkMenu(e, ((MageCard) obj).getOriginal()); } else { checkMenu(e, null); } @@ -758,14 +754,14 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar if (!e.isConsumed()) { Object obj = e.getSource(); if (obj instanceof MageCard) { - checkMenu(e, ((MageCard)obj).getOriginal()); + checkMenu(e, ((MageCard) obj).getOriginal()); } else { checkMenu(e, null); } } } - private void checkMenu(MouseEvent Me, SimpleCardView card){ + private void checkMenu(MouseEvent Me, SimpleCardView card) { if (Me.isPopupTrigger()) { Me.consume(); cardEventSource.showPopupMenuEvent(card, Me.getComponent(), Me.getX(), Me.getY(), "show-popup-menu"); diff --git a/Mage.Client/src/main/java/mage/client/constants/Constants.java b/Mage.Client/src/main/java/mage/client/constants/Constants.java index 969a9654c67..1ea8c4a1e9d 100644 --- a/Mage.Client/src/main/java/mage/client/constants/Constants.java +++ b/Mage.Client/src/main/java/mage/client/constants/Constants.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.client.constants; import java.io.File; @@ -37,6 +36,7 @@ import javax.swing.border.Border; * @author BetaSteward_at_googlemail.com */ public final class Constants { + private Constants() { throw new AssertionError(); } @@ -69,7 +69,7 @@ public final class Constants { public static final int POWBOX_TEXT_MAX_LEFT = 212; public static final int DAMAGE_MAX_LEFT = 180; - public static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(2,2,2,2); + public static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); public static final double SCALE_FACTOR = 0.5; @@ -80,26 +80,29 @@ public final class Constants { public static final String RESOURCE_PATH_SET = IO.imageBaseDir + "sets" + File.separator; public static final String RESOURCE_PATH_SET_SMALL = RESOURCE_PATH_SET + File.separator + "small" + File.separator; public static final String BASE_SOUND_PATH = "sounds" + File.separator; - public static final String BASE_MUSICS_PATH = "music" + File.separator ; - + public static final String BASE_MUSICS_PATH = "music" + File.separator; + public interface IO { + String imageBaseDir = "plugins" + File.separator + "images" + File.separator; String IMAGE_PROPERTIES_FILE = "image.url.properties"; } public enum DeckEditorMode { + FREE_BUILDING, LIMITED_BUILDING, SIDEBOARDING } public enum SortBy { - CASTING_COST ("Casting Cost"), - RARITY ("Rarity"), - COLOR ("Color"), - COLOR_DETAILED ("Color Detailed"), - NAME ("Name"), - UNSORTED ("Unsorted"); + + CASTING_COST("Casting Cost"), + RARITY("Rarity"), + COLOR("Color"), + COLOR_IDENTITY("Color Identity"), + NAME("Name"), + UNSORTED("Unsorted"); private final String text; @@ -120,8 +123,8 @@ public final class Constants { return RARITY; case "Color": return COLOR; - case "Color Detailed": - return COLOR_DETAILED; + case "Color Identity": + return COLOR_IDENTITY; case "Name": return NAME; default: diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewColorDetailedComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java similarity index 71% rename from Mage.Client/src/main/java/mage/client/util/CardViewColorDetailedComparator.java rename to Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java index 179254b961c..3c205f9bb66 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewColorDetailedComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java @@ -25,21 +25,31 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.client.util; import java.util.Comparator; +import java.util.List; +import mage.ObjectColor; import mage.view.CardView; /** * * @author BetaSteward_at_googlemail.com */ -public class CardViewColorDetailedComparator implements Comparator { +public class CardViewColorIdentityComparator implements Comparator { @Override public int compare(CardView o1, CardView o2) { - return o1.getColor().hashCode() - o2.getColor().hashCode(); + return sortValue(o1.getManaCost(), o1.getColor()) - sortValue(o2.getManaCost(), o2.getColor()); } -} \ No newline at end of file + static public int sortValue(List manaCost, ObjectColor color) { + int hash = 3; + hash = 23 * hash + (color.isWhite() || manaCost.contains("{W}") ? 1 : 0); + hash = 23 * hash + (color.isBlue() || manaCost.contains("{U}") ? 1 : 0); + hash = 23 * hash + (color.isBlack() || manaCost.contains("{B}") ? 1 : 0); + hash = 23 * hash + (color.isRed() || manaCost.contains("{R}") ? 1 : 0); + hash = 23 * hash + (color.isGreen() || manaCost.contains("{G}") ? 1 : 0); + return hash; + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java index 22eae462b06..a77d685a489 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/CardViewCostComparator.java @@ -1,16 +1,16 @@ /* * Copyright 2011 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,12 +20,11 @@ * 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.client.util; import java.util.Comparator; @@ -39,7 +38,7 @@ public class CardViewCostComparator implements Comparator { @Override public int compare(CardView o1, CardView o2) { - return Integer.valueOf(o1.getConvertedManaCost()).compareTo(Integer.valueOf(o2.getConvertedManaCost())); + return Integer.valueOf(o1.getConvertedManaCost()).compareTo(o2.getConvertedManaCost()); } } diff --git a/Mage.Common/src/mage/utils/CardUtil.java b/Mage.Common/src/mage/utils/CardUtil.java index fb3331ed4be..f5c7c9c6a85 100644 --- a/Mage.Common/src/mage/utils/CardUtil.java +++ b/Mage.Common/src/mage/utils/CardUtil.java @@ -1,5 +1,7 @@ package mage.utils; +import java.util.List; +import mage.ObjectColor; import mage.cards.Card; import mage.cards.MagePermanent; import mage.constants.CardType; @@ -13,6 +15,12 @@ import mage.view.CardView; */ public class CardUtil { + private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*"; + private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*"; + private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*"; + private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*"; + private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*"; + public static boolean isCreature(CardView card) { return is(card, CardType.CREATURE); } @@ -48,4 +56,34 @@ public class CardUtil { public static boolean isLand(Card card) { return card.getCardType().contains(CardType.LAND); } + + public static int getColorIdentitySortValue(List manaCost, ObjectColor originalColor, List rules) { + ObjectColor color = new ObjectColor(originalColor); + for (String rule : rules) { + rule = rule.replaceAll("(?i)", ""); // Ignoring reminder text in italic + if (rule.matches(regexBlack)) { + color.setBlack(true); + } + if (rule.matches(regexBlue)) { + color.setBlue(true); + } + if (rule.matches(regexGreen)) { + color.setGreen(true); + } + if (rule.matches(regexRed)) { + color.setRed(true); + } + if (rule.matches(regexWhite)) { + color.setWhite(true); + } + } + + int hash = 3; + hash = 23 * hash + (color.isWhite() || manaCost.contains("{W}") ? 1 : 0); + hash = 23 * hash + (color.isBlue() || manaCost.contains("{U}") ? 1 : 0); + hash = 23 * hash + (color.isBlack() || manaCost.contains("{B}") ? 1 : 0); + hash = 23 * hash + (color.isRed() || manaCost.contains("{R}") ? 1 : 0); + hash = 23 * hash + (color.isGreen() || manaCost.contains("{G}") ? 1 : 0); + return hash; + } } From d0a6ae09b470cf5988409ceda192de4657b1f014 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 07:48:49 +0200 Subject: [PATCH 21/29] * Grip of Desolation - Fixed bugged card type definition. --- .../src/mage/sets/battleforzendikar/GripOfDesolation.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/GripOfDesolation.java b/Mage.Sets/src/mage/sets/battleforzendikar/GripOfDesolation.java index 25c3015a0ad..ab18f020a02 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/GripOfDesolation.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/GripOfDesolation.java @@ -46,9 +46,8 @@ import mage.target.targetpointer.SecondTargetPointer; public class GripOfDesolation extends CardImpl { public GripOfDesolation(UUID ownerId) { - super(ownerId, 94, "Grip of Desolation", Rarity.UNCOMMON, new CardType[]{}, "{4}{B}{B}"); + super(ownerId, 94, "Grip of Desolation", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{4}{B}{B}"); this.expansionSetCode = "BFZ"; - this.supertype.add("Intant"); // Devoid Ability ability = new DevoidAbility(this.color); From 9ab323a89c937c8d51f59b9696727eccfb302ec4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 08:11:58 +0200 Subject: [PATCH 22/29] Deck editor - renamed sort type "Color Detailed" to "Color Identity" and compare for the sort also with mana symbols in casting cost and rules. --- .../util/CardViewColorIdentityComparator.java | 16 +++------------- .../mage/cards/repository/CardRepository.java | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java index 3c205f9bb66..36be3025d11 100644 --- a/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/CardViewColorIdentityComparator.java @@ -28,8 +28,7 @@ package mage.client.util; import java.util.Comparator; -import java.util.List; -import mage.ObjectColor; +import mage.utils.CardUtil; import mage.view.CardView; /** @@ -40,16 +39,7 @@ public class CardViewColorIdentityComparator implements Comparator { @Override public int compare(CardView o1, CardView o2) { - return sortValue(o1.getManaCost(), o1.getColor()) - sortValue(o2.getManaCost(), o2.getColor()); - } - - static public int sortValue(List manaCost, ObjectColor color) { - int hash = 3; - hash = 23 * hash + (color.isWhite() || manaCost.contains("{W}") ? 1 : 0); - hash = 23 * hash + (color.isBlue() || manaCost.contains("{U}") ? 1 : 0); - hash = 23 * hash + (color.isBlack() || manaCost.contains("{B}") ? 1 : 0); - hash = 23 * hash + (color.isRed() || manaCost.contains("{R}") ? 1 : 0); - hash = 23 * hash + (color.isGreen() || manaCost.contains("{G}") ? 1 : 0); - return hash; + return CardUtil.getColorIdentitySortValue(o1.getManaCost(), o1.getColor(), o1.getRules()) + - CardUtil.getColorIdentitySortValue(o2.getManaCost(), o2.getColor(), o2.getRules()); } } diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index 4691eaa4f6f..bb949267611 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -63,7 +63,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 41; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 37; + private static final long CARD_CONTENT_VERSION = 38; private final Random random = new Random(); private Dao cardDao; From 95e626752b0c94e63fb91adfb11402b0b86fafcb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 16:12:33 +0200 Subject: [PATCH 23/29] * Greenwarden of Murasa - Fixed that it was possible to return Greenwarden of Murasa itself with its dies triggered ability. --- .../sets/battleforzendikar/GreenwardenOfMurasa.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java b/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java index 697845c16a6..84126d2ce39 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java @@ -33,10 +33,12 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -44,6 +46,7 @@ import mage.constants.Rarity; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; /** * @@ -99,10 +102,15 @@ class GreenwardenOfMurasaEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { + Card targetCard = game.getCard(getTargetPointer().getFirst(game, source)); + if (controller != null && sourceObject != null && targetCard != null) { if (controller.chooseUse(outcome, "Exile " + sourceObject.getLogName() + " to return card from your graveyard to your hand?", source, game)) { new ExileSourceEffect().apply(game, source); - return new ReturnToHandTargetEffect().apply(game, source); + // Setting the fixed target prevents to return Greenwarden of Murasa itself (becuase it's exiled meanwhile), + // but of course you can target it as the ability triggers I guess + Effect effect = new ReturnToHandTargetEffect(); + effect.setTargetPointer(new FixedTarget(targetCard.getId(), targetCard.getZoneChangeCounter(game))); + return effect.apply(game, source); } return true; } From 6249e21ff3eb34d75a963ae045df0c5f2d51bc20 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 16:26:00 +0200 Subject: [PATCH 24/29] Fixed possible null pointer exception. --- Mage/src/mage/players/PlayerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 0c0d8ebd7d1..8ab8b836b3c 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2927,7 +2927,7 @@ public abstract class PlayerImpl implements Player, Serializable { Set cardList = new HashSet<>(); for (UUID cardId : cards) { fromZone = game.getState().getZone(cardId); - if (fromZone.equals(Zone.BATTLEFIELD)) { + if (Zone.BATTLEFIELD.equals(fromZone)) { Permanent permanent = game.getPermanent(cardId); if (permanent != null) { cardList.add(permanent); From 7224ab1614f25175819f7d098fd38970da8fcdca Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 16:32:57 +0200 Subject: [PATCH 25/29] * Hull Breach - Fixed that the first target of the third mode was not destroyed. --- Mage.Sets/src/mage/sets/commander2013/HullBreach.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/commander2013/HullBreach.java b/Mage.Sets/src/mage/sets/commander2013/HullBreach.java index d6a51a65743..0ab48a04ed8 100644 --- a/Mage.Sets/src/mage/sets/commander2013/HullBreach.java +++ b/Mage.Sets/src/mage/sets/commander2013/HullBreach.java @@ -38,7 +38,7 @@ import mage.filter.common.FilterEnchantmentPermanent; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetArtifactPermanent; -import mage.target.targetpointer.SecondTargetPointer; +import mage.target.common.TargetEnchantmentPermanent; /** * @@ -62,12 +62,11 @@ public class HullBreach extends CardImpl { this.getSpellAbility().addMode(mode); // or destroy target artifact and target enchantment. mode = new Mode(); - Effect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new SecondTargetPointer()); + Effect effect = new DestroyTargetEffect(false, true); effect.setText("destroy target artifact and target enchantment"); mode.getEffects().add(effect); mode.getTargets().add(new TargetArtifactPermanent()); - mode.getTargets().add(new TargetPermanent(new FilterEnchantmentPermanent())); + mode.getTargets().add(new TargetEnchantmentPermanent()); this.getSpellAbility().addMode(mode); } From 822528d05cd44d690ea191021a131fe6c8ab1728 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 17:57:27 +0200 Subject: [PATCH 26/29] * Splice onto Arcane - Fixed that an arcane spell with no targets did not fizzle if a targeted spell was spliced with it that has no more legal targets on resolution (so all targets of the spell were illegal). --- .../src/mage/sets/alarareborn/Terminate.java | 59 ++++---- .../src/mage/sets/conflux/CelestialPurge.java | 16 +-- .../mage/sets/morningtide/Bitterblossom.java | 4 +- .../keywords/SpliceOnArcaneTest.java | 130 +++++++++++++----- .../oneshot/exile/CelestialPurgeTest.java | 68 +++++++++ Mage/src/mage/game/stack/Spell.java | 25 +++- 6 files changed, 224 insertions(+), 78 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/CelestialPurgeTest.java diff --git a/Mage.Sets/src/mage/sets/alarareborn/Terminate.java b/Mage.Sets/src/mage/sets/alarareborn/Terminate.java index d69a6c1ee7a..bb1c6bf8971 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/Terminate.java +++ b/Mage.Sets/src/mage/sets/alarareborn/Terminate.java @@ -1,38 +1,37 @@ /* -* 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. -*/ - + * 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.alarareborn; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.target.common.TargetCreaturePermanent; /** @@ -44,8 +43,6 @@ public class Terminate extends CardImpl { public Terminate(UUID ownerId) { super(ownerId, 46, "Terminate", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}{R}"); this.expansionSetCode = "ARB"; - - // Destroy target creature. It can't be regenerated. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/conflux/CelestialPurge.java b/Mage.Sets/src/mage/sets/conflux/CelestialPurge.java index cb2aeb00788..5547e8dcce9 100644 --- a/Mage.Sets/src/mage/sets/conflux/CelestialPurge.java +++ b/Mage.Sets/src/mage/sets/conflux/CelestialPurge.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,20 +20,19 @@ * 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.conflux; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.ObjectColor; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; @@ -57,6 +56,7 @@ public class CelestialPurge extends CardImpl { super(ownerId, 5, "Celestial Purge", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetCode = "CON"; + // Exile target black or red permanent. this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new ExileTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/morningtide/Bitterblossom.java b/Mage.Sets/src/mage/sets/morningtide/Bitterblossom.java index e7a64e2a949..783584d8e87 100644 --- a/Mage.Sets/src/mage/sets/morningtide/Bitterblossom.java +++ b/Mage.Sets/src/mage/sets/morningtide/Bitterblossom.java @@ -51,7 +51,6 @@ public class Bitterblossom extends CardImpl { this.expansionSetCode = "MOR"; this.subtype.add("Faerie"); - // At the beginning of your upkeep, you lose 1 life and put a 1/1 black Faerie Rogue creature token with flying onto the battlefield. Ability ability = new BeginningOfUpkeepTriggeredAbility(new LoseLifeSourceControllerEffect(1), TargetController.YOU, false); ability.addEffect(new CreateTokenEffect(new FaerieToken(), 1)); @@ -69,6 +68,7 @@ public class Bitterblossom extends CardImpl { } class FaerieToken extends Token { + FaerieToken() { super("Faerie Rogue", "1/1 black Faerie Rogue creature token with flying"); cardType.add(CardType.CREATURE); @@ -79,4 +79,4 @@ class FaerieToken extends Token { toughness = new MageInt(1); this.addAbility(FlyingAbility.getInstance()); } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java index 6eff8eaae26..0cd925879a7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java @@ -38,70 +38,70 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class SpliceOnArcaneTest extends CardTestPlayerBase { /** - * Test that it works to cast Through the Breach - * by slicing it on an arcane spell + * Test that it works to cast Through the Breach by slicing it on an arcane + * spell * */ @Test public void testSpliceThroughTheBreach() { - + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); // Sorcery - Arcane {R} // Lava Spike deals 3 damage to target player. - addCard(Zone.HAND, playerA, "Lava Spike",1); + addCard(Zone.HAND, playerA, "Lava Spike", 1); // You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step. // Splice onto Arcane {2}{R}{R} (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) - addCard(Zone.HAND, playerA, "Through the Breach",1); - addCard(Zone.HAND, playerA, "Silvercoat Lion",1); + addCard(Zone.HAND, playerA, "Through the Breach", 1); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lava Spike", playerB); setChoice(playerA, "Silvercoat Lion"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); assertLife(playerA, 20); assertLife(playerB, 17); - + assertGraveyardCount(playerA, "Lava Spike", 1); assertHandCount(playerA, "Through the Breach", 1); - assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); Assert.assertEquals("All available mana has to be used", 0, playerA.getManaAvailable(currentGame).size()); } - + @Test - public void testSpliceTorrentOfStone() { - + public void testSpliceTorrentOfStone() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); // Sorcery - Arcane {R} // Lava Spike deals 3 damage to target player. - addCard(Zone.HAND, playerA, "Lava Spike",1); + addCard(Zone.HAND, playerA, "Lava Spike", 1); // Torrent of Stone deals 4 damage to target creature. - // Splice onto Arcane-Sacrifice two Mountains. (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) - addCard(Zone.HAND, playerA, "Torrent of Stone",1); + // Splice onto Arcane-Sacrifice two Mountains. (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) + addCard(Zone.HAND, playerA, "Torrent of Stone", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion",1); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lava Spike", playerB); addTarget(playerA, "Silvercoat Lion"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); assertLife(playerA, 20); assertLife(playerB, 17); - + assertGraveyardCount(playerA, "Lava Spike", 1); assertHandCount(playerA, "Torrent of Stone", 1); assertGraveyardCount(playerB, "Silvercoat Lion", 1); - assertPermanentCount(playerA, "Mountain", 0); + assertPermanentCount(playerA, "Mountain", 0); Assert.assertEquals("No more mana available", "[]", playerA.getManaAvailable(currentGame).toString()); - } + } + /** * Nourishing Shoal's interaction with Splicing Through the Breach is * bugged. You should still need to pay 2RR as an additional cost, which is @@ -111,34 +111,94 @@ public class SpliceOnArcaneTest extends CardTestPlayerBase { * powerful. */ @Test - public void testSpliceThroughTheBreach2() { + public void testSpliceThroughTheBreach2() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); // You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost. // You gain X life. - addCard(Zone.HAND, playerA, "Nourishing Shoal",1); - addCard(Zone.HAND, playerA, "Giant Growth",1); + addCard(Zone.HAND, playerA, "Nourishing Shoal", 1); + addCard(Zone.HAND, playerA, "Giant Growth", 1); // You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step. // Splice onto Arcane {2}{R}{R} (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) - addCard(Zone.HAND, playerA, "Through the Breach",1); - addCard(Zone.HAND, playerA, "Silvercoat Lion",1); + addCard(Zone.HAND, playerA, "Through the Breach", 1); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nourishing Shoal"); setChoice(playerA, "Yes"); setChoice(playerA, "Silvercoat Lion"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); assertLife(playerA, 21); assertLife(playerB, 20); - + assertGraveyardCount(playerA, "Nourishing Shoal", 1); assertHandCount(playerA, "Through the Breach", 1); - assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); - - Assert.assertEquals("All available mana has to be used","[]", playerA.getManaAvailable(currentGame).toString()); - } - - + + Assert.assertEquals("All available mana has to be used", "[]", playerA.getManaAvailable(currentGame).toString()); + } + + /** + * Cards involved: Nourishing Shoal, Goryo's Vengeance, Griselbrand, + * Terminate + * + * I actually noticed this bug on the 1.4.3 client, but I didn't see it in + * the change log for 1.4.4, so I assume it is still unknown. Also, it is a + * bit of a rules corner case and I haven't seen anyone else report it, so + * the players of this deck may actually not realize it's incorrect. + * + * The scenario was that I cast a Nourishing Shoal with a Goryo's Vengeance + * spliced to it targeting Griselbrand in my graveyard and exiling + * Worldspine Wurm. My opponent responded with a Snapcaster Mage, so to + * deprive him of his ability to reuse his counterspell, I cast the Goryo's + * Vengeance on the Griselbrand. This one resolved. He then used Terminate + * on the Griselbrand after I had activated it once. When the Shoal tried to + * resolve, it should have been countered due to no legal target. However, + * it caused me to gain 11 life. It did not resurrect Griselbrand + * (correctly), but it should have done nothing at all. + * + * I include the info about the Terminate because thinking through, it could + * be pertinent. I would guess what is going on here is one of two things. + * Either the client doesn't recognize the Shoal with a spliced Vengeance as + * a spell with a single target (because Shoal normally doesn't have a + * target) or because the Griselbrand ended up back in the graveyard before + * the Shoal tried to resolve, the client thought its target was still + * valid. I lean toward the former since the Shoal/Vengeance properly failed + * to resurrect the now dead again Griselbrand, so I don't think it was + * reading that as the target, but I'm not certain. I will try to reproduce + * the error against a bot and update this report. + */ + @Test + public void testCounteredBecauseOfNoLegalTarget() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); + // You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost. + // You gain X life. + addCard(Zone.HAND, playerA, "Nourishing Shoal", 1); // "{X}{G}{G}" + // Return target legendary creature card from your graveyard to the battlefield. That creature gains haste. Exile it at the beginning of the next end step. + // Splice onto Arcane {2}{B} + addCard(Zone.HAND, playerA, "Goryo's Vengeance", 1); // {1}{B} + addCard(Zone.GRAVEYARD, playerA, "Griselbrand", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nourishing Shoal"); + setChoice(playerA, "X=3"); + setChoice(playerA, "Yes"); // splice + addTarget(playerA, "Griselbrand"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goryo's Vengeance", "Griselbrand", "Nourishing Shoal"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Nourishing Shoal", 1); + assertGraveyardCount(playerA, "Goryo's Vengeance", 1); + assertPermanentCount(playerA, "Griselbrand", 1); + + assertLife(playerA, 20); // no life gain because Nourishing Shoal has to be countered having no legal targets (from Goryo's V.) + assertLife(playerB, 20); + + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/CelestialPurgeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/CelestialPurgeTest.java new file mode 100644 index 00000000000..ba5ae1387d1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/exile/CelestialPurgeTest.java @@ -0,0 +1,68 @@ +/* + * 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 org.mage.test.cards.abilities.oneshot.exile; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class CelestialPurgeTest extends CardTestPlayerBase { + + /** + * I activated Celestial Purge trying to targeting a Bitterblossom but i + * couldn't so please fix that bug + */ + @Test + public void testExileWorks() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Exile target black or red permanent. + addCard(Zone.HAND, playerA, "Celestial Purge"); + + // At the beginning of your upkeep, you lose 1 life and put a 1/1 black Faerie Rogue creature token with flying onto the battlefield. + addCard(Zone.BATTLEFIELD, playerB, "Bitterblossom"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Celestial Purge", "Bitterblossom"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 19); + + assertGraveyardCount(playerA, "Celestial Purge", 1); + + assertExileCount("Bitterblossom", 1); + assertPermanentCount(playerB, "Faerie Rogue", 1); + + } +} diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index f51d8c3ed1c..b1cf6950138 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -171,6 +171,7 @@ public class Spell extends StackObjImpl implements Card { int index = 0; result = false; boolean legalParts = false; + boolean notTargeted = true; // check for legal parts for (SpellAbility spellAbility : this.spellAbilities) { // if muliple modes are selected, and there are modes with targets, then at least one mode has to have a legal target or @@ -178,10 +179,15 @@ public class Spell extends StackObjImpl implements Card { // If all targets are illegal when the spell tries to resolve, the spell is countered and none of its effects happen. // If at least one target is still legal at that time, the spell resolves, but an illegal target can't perform any actions // or have any actions performed on it. - legalParts |= spellAbilityHasLegalParts(spellAbility, game); + // if only a spliced spell has targets and all targets ar illegal, the complete spell is countered + if (hasTargets(spellAbility, game)) { + notTargeted = false; + legalParts |= spellAbilityHasLegalParts(spellAbility, game); + } + } // resolve if legal parts - if (legalParts) { + if (notTargeted || legalParts) { for (SpellAbility spellAbility : this.spellAbilities) { if (spellAbilityHasLegalParts(spellAbility, game)) { for (UUID modeId : spellAbility.getModes().getSelectedModes()) { @@ -262,6 +268,21 @@ public class Spell extends StackObjImpl implements Card { } } + private boolean hasTargets(SpellAbility spellAbility, Game game) { + if (spellAbility.getModes().getSelectedModes().size() > 1) { + for (UUID modeId : spellAbility.getModes().getSelectedModes()) { + spellAbility.getModes().setActiveMode(modeId); + if (!spellAbility.getTargets().isEmpty()) { + return true; + } + + } + return false; + } else { + return !spellAbility.getTargets().isEmpty(); + } + } + private boolean spellAbilityHasLegalParts(SpellAbility spellAbility, Game game) { if (spellAbility.getModes().getSelectedModes().size() > 1) { boolean targetedMode = false; From 5bed5aeff74e107d91f6ccf87284980372620036 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 19:21:23 +0200 Subject: [PATCH 27/29] * Fixed null pointer exception for download with mythicspoiler. --- .../plugins/card/dl/sources/MythicspoilerComSource.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java index d366c4e4623..ef80a0785ae 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java @@ -133,8 +133,10 @@ public class MythicspoilerComSource implements CardImageSource { } Elements cardsImages = doc.select("img[src^=cards/]"); // starts with cards/ - for (String text : aliasesStart) { - cardsImages.addAll(doc.select("img[src^=" + text + "]")); + if (!aliasesStart.isEmpty()) { + for (String text : aliasesStart) { + cardsImages.addAll(doc.select("img[src^=" + text + "]")); + } } if (cardsImages.isEmpty()) { break; From 0e64c069ce0fe584a66c61b9764d89cc11f0fd92 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 22:30:32 +0200 Subject: [PATCH 28/29] * Thraben Doomsayer - Fixed that the boosting ability was also appied to Thraben Doomsayer. --- .../src/mage/sets/darkascension/ThrabenDoomsayer.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/darkascension/ThrabenDoomsayer.java b/Mage.Sets/src/mage/sets/darkascension/ThrabenDoomsayer.java index 5da2eef4b10..7977e58f2d5 100644 --- a/Mage.Sets/src/mage/sets/darkascension/ThrabenDoomsayer.java +++ b/Mage.Sets/src/mage/sets/darkascension/ThrabenDoomsayer.java @@ -28,9 +28,6 @@ package mage.sets.darkascension; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -40,7 +37,9 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.Zone; /** @@ -61,7 +60,7 @@ public class ThrabenDoomsayer extends CardImpl { // {tap}: Put a 1/1 white Human creature token onto the battlefield. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new HumanToken()), new TapSourceCost())); // Fateful hour - As long as you have 5 or less life, other creatures you control get +2/+2. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield, false), + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield, true), FatefulHourCondition.getInstance(), "As long as you have 5 or less life, other creatures you control get +2/+2"))); } From 3943c090e643f87a4ed0dd34ad2da0889c0df428 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 Sep 2015 23:08:53 +0200 Subject: [PATCH 29/29] Some minor changes. --- .../sets/avacynrestored/AbundantGrowth.java | 15 ++++++++----- .../sets/battleforzendikar/MakindiPatrol.java | 2 +- .../src/mage/sets/magic2011/Triskelion.java | 22 ++++++++++--------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Mage.Sets/src/mage/sets/avacynrestored/AbundantGrowth.java b/Mage.Sets/src/mage/sets/avacynrestored/AbundantGrowth.java index af12b061514..f248e81664d 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/AbundantGrowth.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/AbundantGrowth.java @@ -27,22 +27,26 @@ */ package mage.sets.avacynrestored; -import mage.constants.*; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; -import java.util.UUID; - /** * @author noxx */ @@ -53,7 +57,6 @@ public class AbundantGrowth extends CardImpl { this.expansionSetCode = "AVR"; this.subtype.add("Aura"); - // Enchant land TargetPermanent auraTarget = new TargetLandPermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -66,7 +69,9 @@ public class AbundantGrowth extends CardImpl { // Enchanted land has "{tap}: Add one mana of any color to your mana pool." Ability gainedAbility = new AnyColorManaAbility(new TapSourceCost()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA))); + Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); + effect.setText("Enchanted land has \"{tap}: Add one mana of any color to your mana pool.\""); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public AbundantGrowth(final AbundantGrowth card) { diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/MakindiPatrol.java b/Mage.Sets/src/mage/sets/battleforzendikar/MakindiPatrol.java index 52a9f396492..693d998a174 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/MakindiPatrol.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/MakindiPatrol.java @@ -55,7 +55,7 @@ public class MakindiPatrol extends CardImpl { // Rally — Whenever Makindi Patrol or another Ally enters the battlefield under your control, creatures you control gain vigilance until end of turn. this.addAbility(new AllyEntersBattlefieldTriggeredAbility( - new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("creatures you control")), false)); + new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("creatures")), false)); } public MakindiPatrol(final MakindiPatrol card) { diff --git a/Mage.Sets/src/mage/sets/magic2011/Triskelion.java b/Mage.Sets/src/mage/sets/magic2011/Triskelion.java index ee0d37abde0..2ecf10dba0f 100644 --- a/Mage.Sets/src/mage/sets/magic2011/Triskelion.java +++ b/Mage.Sets/src/mage/sets/magic2011/Triskelion.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,26 +20,25 @@ * 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.magic2011; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.counters.CounterType; import mage.target.common.TargetCreatureOrPlayer; @@ -56,7 +55,10 @@ public class Triskelion extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + // Triskelion enters the battlefield with three +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), "with three +1/+1 counters on it")); + + // Remove a +1/+1 counter from Triskelion: Triskelion deals 1 damage to target creature or player. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new RemoveCountersSourceCost(CounterType.P1P1.createInstance())); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability);