From 9fc2b720932693259a9d96f23372fa181742cf81 Mon Sep 17 00:00:00 2001 From: nickmyers Date: Tue, 24 Feb 2015 15:37:03 -0600 Subject: [PATCH 01/61] Removed "all" from filter definition in ArenaOfTheAncients.java. Implemented BlinkmothUrn.java. Fixed bug in AmberPrison.java so that the controller is prompted whether to untap or not; before it was automatically untapping without asking. --- .../mage/sets/legends/ArenaOfTheAncients.java | 2 +- .../src/mage/sets/mirage/AmberPrison.java | 3 +- .../src/mage/sets/mirrodin/BlinkmothUrn.java | 83 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/mirrodin/BlinkmothUrn.java diff --git a/Mage.Sets/src/mage/sets/legends/ArenaOfTheAncients.java b/Mage.Sets/src/mage/sets/legends/ArenaOfTheAncients.java index 7f64b41104e..c5564f03ead 100644 --- a/Mage.Sets/src/mage/sets/legends/ArenaOfTheAncients.java +++ b/Mage.Sets/src/mage/sets/legends/ArenaOfTheAncients.java @@ -26,7 +26,7 @@ import mage.filter.predicate.mageobject.SupertypePredicate; */ public class ArenaOfTheAncients extends CardImpl { - final static FilterCreaturePermanent legendaryFilter = new FilterCreaturePermanent("all legendary creatures"); + final static FilterCreaturePermanent legendaryFilter = new FilterCreaturePermanent("legendary creatures"); static { legendaryFilter.add(new SupertypePredicate("Legendary")); } diff --git a/Mage.Sets/src/mage/sets/mirage/AmberPrison.java b/Mage.Sets/src/mage/sets/mirage/AmberPrison.java index 428ff3fbf21..1127a3244b4 100644 --- a/Mage.Sets/src/mage/sets/mirage/AmberPrison.java +++ b/Mage.Sets/src/mage/sets/mirage/AmberPrison.java @@ -178,7 +178,8 @@ class AmberPrisonUntapTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getType().equals(GameEvent.EventType.UNTAPPED) && event.getTargetId().equals(this.getSourceId()); + return event.getType().equals(GameEvent.EventType.UNTAP) && event.getTargetId().equals(this.getSourceId()); + } } diff --git a/Mage.Sets/src/mage/sets/mirrodin/BlinkmothUrn.java b/Mage.Sets/src/mage/sets/mirrodin/BlinkmothUrn.java new file mode 100644 index 00000000000..5eea8cdfb57 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/BlinkmothUrn.java @@ -0,0 +1,83 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.sets.mirrodin; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author nickmyers + */ +public class BlinkmothUrn extends CardImpl { + + public BlinkmothUrn(UUID ownerId) { + super(ownerId, 145, "Blinkmoth Urn", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); + this.expansionSetCode = "MRD"; + + // At the beginning of each player's precombat main phase, if + // Blinkmoth Urn is untapped, that player adds {1} to his or her + // mana pool for each artifact he or she controls. + this.addAbility(new BeginningOfPreCombatMainTriggeredAbility(new BlinkmothUrnEffect(), TargetController.ANY, false)); + } + + public BlinkmothUrn(final BlinkmothUrn card) { + super(card); + } + + @Override + public BlinkmothUrn copy() { + return new BlinkmothUrn(this); + } + +} + +class BlinkmothUrnEffect extends OneShotEffect { + + public BlinkmothUrnEffect() { + super(Outcome.PutManaInPool); + this.staticText = "if Blinkmoth Urn is untapped, that player adds {1} to his or her mana pool for each artifact he or she controls"; + } + + public BlinkmothUrnEffect(final BlinkmothUrnEffect effect) { + super(effect); + } + + @Override + public BlinkmothUrnEffect copy() { + return new BlinkmothUrnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getActivePlayerId()); + FilterArtifactPermanent filter = new FilterArtifactPermanent("artifacts you control"); + filter.add(new ControllerIdPredicate(game.getActivePlayerId())); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if(player != null && sourcePermanent != null && !sourcePermanent.isTapped()) { + player.getManaPool().addMana(Mana.ColorlessMana( + game.getState(). + getBattlefield(). + getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game). + size()), game, source, true); + return true; + } + return false; + } +} From 091fa9a52871771a2a44b7b598f0112bacacd489 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 24 Feb 2015 23:36:59 +0100 Subject: [PATCH 02/61] * Bestow - Fixed that the converted mana costs were calculated from the bestow costs instead of the base cost of the spell. --- .../cards/abilities/keywords/BestowTest.java | 42 ++++++++++++++++++- .../mage/abilities/keyword/BestowAbility.java | 2 + Mage/src/mage/game/stack/Spell.java | 24 +++++++---- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java index 017aa0d9c96..c684ea65402 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java @@ -231,5 +231,45 @@ public class BestowTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Nyxborn Rollicker", 0); assertGraveyardCount(playerA, "Nyxborn Rollicker", 1); - } + } + + /** + * Test that CMC of a spell cast with bestowed is correct + * Disdainful Stroke doesn't check converted mana cost correctly. Opponent was + * able to use it to counter a Hypnotic Siren cast with Bestow. + */ + @Test + public void bestowCheckForCorrectCMC() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + // Enchantment Creature — Siren + // 1/1 + // Bestow {5}{U}{U} (If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature.) + // Flying + // You control enchanted creature. + // Enchanted creature gets +1/+1 and has flying. + addCard(Zone.HAND, playerA, "Hypnotic Siren"); + // Instant {1}{U} + // Counter target spell with converted mana cost 4 or greater. + addCard(Zone.HAND, playerB, "Disdainful Stroke"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hypnotic Siren using bestow", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Disdainful Stroke", "Hypnotic Siren"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // + assertHandCount(playerA, "Hypnotic Siren", 0); + assertGraveyardCount(playerA, "Hypnotic Siren", 0); + assertHandCount(playerB, "Disdainful Stroke", 1); + assertPermanentCount(playerA, "Hypnotic Siren", 1); + + // because cast with bestow, Boon Satyr may not be tapped + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 3,3); + + } } diff --git a/Mage/src/mage/abilities/keyword/BestowAbility.java b/Mage/src/mage/abilities/keyword/BestowAbility.java index 6f933711cc3..5c3c1823904 100644 --- a/Mage/src/mage/abilities/keyword/BestowAbility.java +++ b/Mage/src/mage/abilities/keyword/BestowAbility.java @@ -41,6 +41,7 @@ import mage.constants.Duration; import mage.constants.Layer; import static mage.constants.Layer.TypeChangingEffects_4; import mage.constants.Outcome; +import mage.constants.SpellAbilityType; import mage.constants.SubLayer; import mage.constants.TimingRule; import mage.constants.Zone; @@ -108,6 +109,7 @@ public class BestowAbility extends SpellAbility { public BestowAbility(Card card, String manaString) { super(new ManaCostsImpl(manaString), card.getName() + " using bestow"); + this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; this.timing = TimingRule.SORCERY; TargetPermanent auraTarget = new TargetCreaturePermanent(); this.addTarget(auraTarget); diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 2ea2c858ebd..bed5d9ac863 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -28,6 +28,9 @@ package mage.game.stack; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.Mana; @@ -47,7 +50,11 @@ import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SpellAbilityType; +import mage.constants.Zone; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; @@ -59,10 +66,6 @@ import mage.target.Target; import mage.target.TargetAmount; import mage.watchers.Watcher; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * * @author BetaSteward_at_googlemail.com @@ -181,7 +184,7 @@ public class Spell implements StackObject, Card { // if muliple modes are selected, and there are modes with targets, then at least one mode has to have a legal target or // When resolving a fused split spell with multiple targets, treat it as you would any spell with multiple targets. // 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 + // 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); } @@ -635,7 +638,14 @@ public class Spell implements StackObject, Card { index = symbolString.indexOf("{X}"); } } - cmc += spellAbility.getManaCosts().convertedManaCost() + spellAbility.getManaCostsToPay().getX() * xMultiplier; + if (this.getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) { + cmc += spellAbility.getManaCostsToPay().getX() * xMultiplier; + } else { + cmc += spellAbility.getManaCosts().convertedManaCost() + spellAbility.getManaCostsToPay().getX() * xMultiplier; + } + } + if (this.getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) { + cmc += getCard().getManaCost().convertedManaCost(); } return cmc; } From 661da6153e41dbd00008260d4d50572312572897 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 24 Feb 2015 23:50:44 +0100 Subject: [PATCH 03/61] * Contaminated Bond - Fixed that the damage was wrongly dealt to aura controller instead of enchanted creatures controller. --- .../src/mage/sets/riseoftheeldrazi/ContaminatedGround.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java index 3374eecd587..5a2595a05fe 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java @@ -66,8 +66,10 @@ public class ContaminatedGround extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // Enchanted land is a Swamp. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesBasicLandEnchantedEffect("Swamp"))); + // Whenever enchanted land becomes tapped, its controller loses 2 life. this.addAbility(new ContaminatedGroundAbility()); } From 2cfec90add7eff68fec81d1fb09a4a9c36fd3baf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 25 Feb 2015 00:24:39 +0100 Subject: [PATCH 04/61] * Stoneforge Mystic - Fixed that all artifacts cards could be brought into play instead of only equipment artifacts. --- Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java b/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java index 18b13d48f61..fa9d92b6a6d 100644 --- a/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java +++ b/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java @@ -100,7 +100,7 @@ public class StoneforgeMystic extends CardImpl { class StoneforgeMysticEffect extends OneShotEffect { - private static final FilterArtifactCard filter = new FilterArtifactCard("Equipment card"); + private static final FilterArtifactCard filter = new FilterArtifactCard("an Equipment card from your hand"); static { filter.add(new SubtypePredicate("Equipment")); @@ -124,7 +124,7 @@ class StoneforgeMysticEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Target target = new TargetCardInHand(new FilterArtifactCard("an Equipment card from your hand")); + Target target = new TargetCardInHand(filter); if (target.canChoose(source.getSourceId(), source.getControllerId(), game) && controller.chooseUse(outcome, "Put an Equipment from your hand to battlefield?", game) && controller.chooseTarget(outcome, target, source, game)) { From e418d69068249f66f9c48757826163a647ed4e83 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 25 Feb 2015 01:18:42 +0100 Subject: [PATCH 05/61] * Contaminated Bond - Fixed that the damage was wrongly dealt to aura controller instead of enchanted creatures controller. --- .../sets/ninthedition/ContaminatedBond.java | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java b/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java index 7a1849f03f7..c2ec93bd388 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java +++ b/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java @@ -28,17 +28,21 @@ package mage.sets.ninthedition; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.keyword.EnchantAbility; 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.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -53,16 +57,15 @@ public class ContaminatedBond extends CardImpl { this.expansionSetCode = "9ED"; this.subtype.add("Aura"); - this.color.setBlack(true); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.UnboostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // Whenever enchanted creature attacks or blocks, its controller loses 3 life. - this.addAbility(new AttacksOrBlocksEnchantedTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeSourceControllerEffect(3))); + this.addAbility(new AttacksOrBlocksEnchantedTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeControllerAttachedEffect(3))); } public ContaminatedBond(final ContaminatedBond card) { @@ -74,3 +77,50 @@ public class ContaminatedBond extends CardImpl { return new ContaminatedBond(this); } } + +class LoseLifeControllerAttachedEffect extends OneShotEffect { + + protected DynamicValue amount; + + public LoseLifeControllerAttachedEffect(int amount) { + this(new StaticValue(amount)); + } + + public LoseLifeControllerAttachedEffect(DynamicValue amount) { + super(Outcome.Damage); + this.amount = amount; + staticText = "its controller loses " + amount.toString() +" life"; + } + + public LoseLifeControllerAttachedEffect(final LoseLifeControllerAttachedEffect effect) { + super(effect); + this.amount = effect.amount.copy(); + } + + @Override + public LoseLifeControllerAttachedEffect copy() { + return new LoseLifeControllerAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment == null) { + enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + } + if (enchantment != null && enchantment.getAttachedTo() != null) { + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature == null) { + creature = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + } + if (creature != null) { + Player player = game.getPlayer(creature.getControllerId()); + if (player != null) { + player.loseLife(amount.calculate(game, source, this), game); + return true; + } + } + } + return false; + } +} From 7ac423f1d67c9e936abdb7b86643e55c15f2f0dd Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 25 Feb 2015 01:19:27 +0100 Subject: [PATCH 06/61] * Praetor's Grasp - Fixed that the cast and reveal effects did not work. --- .../src/mage/sets/limitedalpha/ManaVault.java | 4 +- .../mage/sets/newphyrexia/PraetorsGrasp.java | 68 +++++++++++++------ Mage/src/mage/util/CardUtil.java | 7 ++ 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/Mage.Sets/src/mage/sets/limitedalpha/ManaVault.java b/Mage.Sets/src/mage/sets/limitedalpha/ManaVault.java index 512bddea368..c363dc2eeea 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/ManaVault.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/ManaVault.java @@ -62,14 +62,14 @@ public class ManaVault extends CardImpl { // At the beginning of your upkeep, you may pay {4}. If you do, untap Mana Vault. this.addAbility(new BeginningOfUpkeepTriggeredAbility( Zone.BATTLEFIELD, - new DoIfCostPaid(new UntapSourceEffect(), new GenericManaCost(4)), + new DoIfCostPaid(new UntapSourceEffect(), new GenericManaCost(4),"Pay {4} to untap {this}?"), TargetController.YOU, false)); // At the beginning of your draw step, if Mana Vault is tapped, it deals 1 damage to you. this.addAbility(new ConditionalTriggeredAbility( new BeginningOfDrawTriggeredAbility(Zone.BATTLEFIELD, new DamageControllerEffect(1), TargetController.YOU, false), SourceTappedCondition.getInstance(), - "At the beginning of your draw step, if Mana Vault is tapped, it deals 1 damage to you.", false)); + "At the beginning of your draw step, if {this} is tapped, it deals 1 damage to you.", false)); // {tap}: Add {3} to your mana pool. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(3), new TapSourceCost())); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java index 8742b4155d0..156ebe3ed29 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java @@ -27,6 +27,8 @@ */ package mage.sets.newphyrexia; +import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; @@ -34,14 +36,18 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.Cards; import mage.cards.CardsImpl; -import mage.constants.*; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; - -import java.util.UUID; -import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** * @@ -90,16 +96,18 @@ class PraetorsGraspEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(source.getFirstTarget()); - Player player = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (player != null && opponent != null && sourcePermanent != null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && opponent != null && sourceObject != null) { TargetCardInLibrary target = new TargetCardInLibrary(); - if (player.searchLibrary(target, game, opponent.getId())) { + if (controller.searchLibrary(target, game, opponent.getId())) { UUID targetId = target.getFirstTarget(); - Card card = opponent.getLibrary().remove(targetId, game); - if (card != null) { + Card card = opponent.getLibrary().getCard(targetId, game); + UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); + if (card != null && exileId != null) { card.setFaceDown(true); - card.moveToExile(getId(), sourcePermanent.getName(), source.getSourceId(), game); + game.informPlayers(controller.getName() + " moves the searched card face down to exile"); + card.moveToExile(exileId, sourceObject.getName(), source.getSourceId(), game); game.addEffect(new PraetorsGraspPlayEffect(card.getId()), source); game.addEffect(new PraetorsGraspRevealEffect(card.getId()), source); } @@ -139,10 +147,18 @@ class PraetorsGraspPlayEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { if (sourceId.equals(cardId)) { - Card card = game.getCard(cardId); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { - return true; + MageObject sourceObject = source.getSourceObject(game); + UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject, true); + if (exileId != null && sourceObject != null && controller != null) { + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null && exileZone.contains(cardId)) { + if (controller.chooseUse(outcome, "Play the exiled card?", game)) { + return true; + } + } else { + discard(); + } } } return false; @@ -178,17 +194,25 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { if (sourceId.equals(cardId)) { - Card card = game.getCard(cardId); - Card sourceCard = game.getCard(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { - if (controller.chooseUse(outcome, "Reveal exiled card?", game)) { - Cards cards = new CardsImpl(card); - controller.lookAtCards("Exiled with " + sourceCard.getName(), cards, game); + MageObject sourceObject = source.getSourceObject(game); + UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject, true); + if (exileId != null && sourceObject != null) { + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null && exileZone.contains(cardId)) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(cardId); + if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { + if (controller.chooseUse(outcome, "Reveal exiled card?", game)) { + Cards cards = new CardsImpl(card); + controller.lookAtCards("Exiled with " + sourceObject.getName(), cards, game); + } + } + } else { + discard(); } } } return false; } -} \ No newline at end of file +} diff --git a/Mage/src/mage/util/CardUtil.java b/Mage/src/mage/util/CardUtil.java index b817dba8481..5bc68966584 100644 --- a/Mage/src/mage/util/CardUtil.java +++ b/Mage/src/mage/util/CardUtil.java @@ -479,12 +479,19 @@ public class CardUtil { } public static UUID getObjectExileZoneId(Game game, MageObject mageObject) { + return getObjectExileZoneId(game, mageObject, false); + } + + public static UUID getObjectExileZoneId(Game game, MageObject mageObject, boolean previous) { int zoneChangeCounter = 0; if (mageObject instanceof Permanent) { zoneChangeCounter = ((Permanent) mageObject).getZoneChangeCounter(); } else if (mageObject instanceof Card) { zoneChangeCounter = ((Card) mageObject).getZoneChangeCounter(); } + if (zoneChangeCounter > 0 && previous) { + zoneChangeCounter--; + } return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT,mageObject.getId(), game, zoneChangeCounter, false), game); } From 9a1eb515062ad1f0b17212f97e4f9125aa06b8ea Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 24 Feb 2015 20:35:17 -0600 Subject: [PATCH 07/61] - Added Advice From the Fae, Puca's Mischief, Illuminated Folio, Furystoke Giant, Burn Trail, and Inescapable Brute. --- .../sets/shadowmoor/AdviceFromTheFae.java | 134 +++++++++++++ .../src/mage/sets/shadowmoor/BurnTrail.java | 64 ++++++ .../mage/sets/shadowmoor/FurystokeGiant.java | 81 ++++++++ .../sets/shadowmoor/IlluminatedFolio.java | 188 ++++++++++++++++++ .../sets/shadowmoor/InescapableBrute.java | 70 +++++++ .../mage/sets/shadowmoor/PucasMischief.java | 170 ++++++++++++++++ 6 files changed, 707 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.java create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java diff --git a/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java new file mode 100644 index 00000000000..aaa6b6e7b77 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java @@ -0,0 +1,134 @@ +/* + * 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.shadowmoor; + +import java.util.List; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +/** + * + * @author jeffwadsworth + */ +public class AdviceFromTheFae extends CardImpl { + + public AdviceFromTheFae(UUID ownerId) { + super(ownerId, 28, "Advice from the Fae", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2/U}{2/U}{2/U}"); + this.expansionSetCode = "SHM"; + + // ({2U} can be paid with any two mana or with {U}. This card's converted mana cost is 6.) + // Look at the top five cards of your library. If you control more creatures than each other player, put two of those cards into your hand. Otherwise, put one of them into your hand. Then put the rest on the bottom of your library in any order. + this.getSpellAbility().addEffect(new AdviceFromTheFaeEffect()); + + } + + public AdviceFromTheFae(final AdviceFromTheFae card) { + super(card); + } + + @Override + public AdviceFromTheFae copy() { + return new AdviceFromTheFae(this); + } +} + +class AdviceFromTheFaeEffect extends OneShotEffect { + + public AdviceFromTheFaeEffect() { + super(Outcome.DrawCard); + this.staticText = "Look at the top five cards of your library. If you control more creatures than each other player, put two of those cards into your hand. Otherwise, put one of them into your hand. Then put the rest on the bottom of your library in any order"; + } + + public AdviceFromTheFaeEffect(final AdviceFromTheFaeEffect effect) { + super(effect); + } + + @Override + public AdviceFromTheFaeEffect copy() { + return new AdviceFromTheFaeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (controller != null) { + List cardsFromTopLibrary = controller.getLibrary().getTopCards(game, 5); + Cards cards = new CardsImpl(Zone.HAND); + for (Card card : cardsFromTopLibrary) { + cards.add(card); + } + controller.lookAtCards(mageObject.getLogName(), cards, game); + int max = 0; + for (UUID playerId : controller.getInRange()) { + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new ControllerIdPredicate(playerId)); + if (playerId != controller.getId()) { + if (max < game.getBattlefield().countAll(filter, playerId, game)) { + max = game.getBattlefield().countAll(filter, playerId, game); + } + } + } + if (game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), controller.getId(), game) > max) { + TargetCard target = new TargetCard(2, Zone.HAND, new FilterCard()); + if (controller.choose(Outcome.DrawCard, cards, target, game)) { + controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getId(), game, Zone.LIBRARY); + cards.remove(game.getCard(target.getFirstTarget())); + controller.moveCardToHandWithInfo(game.getCard(target.getTargets().get(1)), source.getId(), game, Zone.LIBRARY); + cards.remove(game.getCard(target.getTargets().get(1))); + } + } else { + TargetCard target = new TargetCard(1, Zone.HAND, new FilterCard()); + if (controller.choose(Outcome.DrawCard, cards, target, game)) { + controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getId(), game, Zone.LIBRARY); + cards.remove(game.getCard(target.getFirstTarget())); + } + } + controller.putCardsOnBottomOfLibrary(cards, game, source, true); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java new file mode 100644 index 00000000000..0e4a193204d --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java @@ -0,0 +1,64 @@ +/* + * 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.shadowmoor; + +import java.util.UUID; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.ConspireAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author jeffwadsworth + */ +public class BurnTrail extends CardImpl { + + public BurnTrail(UUID ownerId) { + super(ownerId, 86, "Burn Trail", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{3}{R}"); + this.expansionSetCode = "SHM"; + + // Burn Trail deals 3 damage to target creature or player. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + + // Conspire + this.addAbility(new ConspireAbility(this)); + } + + public BurnTrail(final BurnTrail card) { + super(card); + } + + @Override + public BurnTrail copy() { + return new BurnTrail(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.java b/Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.java new file mode 100644 index 00000000000..9bdee8e1753 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.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.shadowmoor; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continious.GainAbilityAllEffect; +import mage.abilities.keyword.PersistAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author jeffwadsworth + */ +public class FurystokeGiant extends CardImpl { + + public FurystokeGiant(UUID ownerId) { + super(ownerId, 93, "Furystoke Giant", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + this.expansionSetCode = "SHM"; + this.subtype.add("Giant"); + this.subtype.add("Warrior"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Furystoke Giant enters the battlefield, other creatures you control gain "{tap}: This creature deals 2 damage to target creature or player" until end of turn. + SimpleActivatedAbility FurystokeGiantAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost()); + FurystokeGiantAbility.addTarget(new TargetCreatureOrPlayer()); + Effect effect = new GainAbilityAllEffect(FurystokeGiantAbility, Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true); + effect.setText("other creatures you control gain \"{T}: This creature deals 2 damage to target creature or player.\" until end of turn."); + this.addAbility(new EntersBattlefieldTriggeredAbility(effect)); + + // Persist + this.addAbility(new PersistAbility()); + + } + + public FurystokeGiant(final FurystokeGiant card) { + super(card); + } + + @Override + public FurystokeGiant copy() { + return new FurystokeGiant(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java new file mode 100644 index 00000000000..9b4601cb26d --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java @@ -0,0 +1,188 @@ +/* + * 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.shadowmoor; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author jeffwadsworth + */ +public class IlluminatedFolio extends CardImpl { + + public IlluminatedFolio(UUID ownerId) { + super(ownerId, 254, "Illuminated Folio", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{5}"); + this.expansionSetCode = "SHM"; + + // {1}, {tap}, Reveal two cards from your hand that share a color: Draw a card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new RevealTwoCardsSharedColorFromHandCost()); + this.addAbility(ability); + + } + + public IlluminatedFolio(final IlluminatedFolio card) { + super(card); + } + + @Override + public IlluminatedFolio copy() { + return new IlluminatedFolio(this); + } +} + +class RevealTwoCardsSharedColorFromHandCost extends RevealTargetFromHandCost { + + public RevealTwoCardsSharedColorFromHandCost() { + super(new TargetTwoCardsWithTheSameColorInHand()); + } + + public RevealTwoCardsSharedColorFromHandCost(RevealTwoCardsSharedColorFromHandCost cost) { + super(cost); + } + + @Override + public RevealTwoCardsSharedColorFromHandCost copy() { + return new RevealTwoCardsSharedColorFromHandCost(this); + } + +} + +class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { + + public TargetTwoCardsWithTheSameColorInHand() { + super(2, 2, new FilterCard("two cards from your hand that share a color")); + } + + public TargetTwoCardsWithTheSameColorInHand(final TargetTwoCardsWithTheSameColorInHand target) { + super(target); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + Set newPossibleTargets = new HashSet<>(); + Set possibleTargets = new HashSet<>(); + Player player = game.getPlayer(sourceControllerId); + for (Card card : player.getHand().getCards(filter, game)) { + possibleTargets.add(card.getId()); + } + + Cards cardsToCheck = new CardsImpl(); + cardsToCheck.addAll(possibleTargets); + if (targets.size() == 1) { + // first target is laready choosen, now only targets with the same name are selectable + for (Map.Entry entry : targets.entrySet()) { + Card chosenCard = cardsToCheck.get(entry.getKey(), game); + if (chosenCard != null) { + for (UUID cardToCheck : cardsToCheck) { + if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor().equals(game.getCard(cardToCheck).getColor())) { + newPossibleTargets.add(cardToCheck); + } + } + } + } + } else { + for (UUID cardToCheck : cardsToCheck) { + FilterCard colorFilter = new FilterCard(); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + if (cardsToCheck.count(colorFilter, game) > 1) { + newPossibleTargets.add(cardToCheck); + } + } + } + return newPossibleTargets; + } + + @Override + public boolean canChoose(UUID sourceControllerId, Game game) { + Cards cardsToCheck = new CardsImpl(); + Player player = game.getPlayer(sourceControllerId); + for (Card card : player.getHand().getCards(filter, game)) { + cardsToCheck.add(card.getId()); + } + int possibleCards = 0; + for (UUID cardToCheck : cardsToCheck) { + FilterCard colorFilter = new FilterCard(); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + if (cardsToCheck.count(colorFilter, game) > 1) { + ++possibleCards; + } + } + return possibleCards > 0; + } + + @Override + public boolean canTarget(UUID id, Game game) { + if (super.canTarget(id, game)) { + Card card = game.getCard(id); + if (card != null) { + if (targets.size() == 1) { + Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); + if (card2 != null && card2.getColor().equals(card.getColor())) { + return true; + } + } else { + FilterCard colorFilter = new FilterCard(); + colorFilter.add(new ColorPredicate(card.getColor())); + Player player = game.getPlayer(card.getOwnerId()); + if (player.getHand().getCards(colorFilter, game).size() > 1) { + return true; + } + } + } + } + return false; + } + + @Override + public TargetTwoCardsWithTheSameColorInHand copy() { + return new TargetTwoCardsWithTheSameColorInHand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java b/Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java new file mode 100644 index 00000000000..b379a9fd112 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java @@ -0,0 +1,70 @@ +/* + * 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.shadowmoor; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; +import mage.abilities.keyword.WitherAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public class InescapableBrute extends CardImpl { + + public InescapableBrute(UUID ownerId) { + super(ownerId, 95, "Inescapable Brute", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{5}{R}"); + this.expansionSetCode = "SHM"; + this.subtype.add("Giant"); + this.subtype.add("Warrior"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Wither + this.addAbility(WitherAbility.getInstance()); + + // Inescapable Brute must be blocked if able. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); + + } + + public InescapableBrute(final InescapableBrute card) { + super(card); + } + + @Override + public InescapableBrute copy() { + return new InescapableBrute(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java b/Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java new file mode 100644 index 00000000000..a7901d94bbc --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java @@ -0,0 +1,170 @@ +/* + * 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.shadowmoor; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.continious.ExchangeControlTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author jeffwadsworth + */ +public class PucasMischief extends CardImpl { + + private static final String rule = "you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost"; + + public PucasMischief(UUID ownerId) { + super(ownerId, 47, "Puca's Mischief", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + this.expansionSetCode = "SHM"; + + // At the beginning of your upkeep, you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost. + Ability ability = new BeginningOfUpkeepTriggeredAbility(new ExchangeControlTargetEffect(Duration.EndOfGame, rule, false, true), TargetController.YOU, true); + ability.addTarget(new TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent()); + ability.addTarget(new PucasMischiefSecondTarget()); + this.addAbility(ability); + + } + + public PucasMischief(final PucasMischief card) { + super(card); + } + + @Override + public PucasMischief copy() { + return new PucasMischief(this); + } +} + +class TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent extends TargetControlledPermanent { + + public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent() { + super(); + filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + setTargetName("nonland permanent you control"); + } + + public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent(final TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent target) { + super(target); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + return super.canTarget(controllerId, id, source, game); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + MageObject targetSource = game.getObject(sourceId); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { + possibleTargets.add(permanent.getId()); + } + } + return possibleTargets; + } + + @Override + public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent copy() { + return new TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent(this); + } +} + +class PucasMischiefSecondTarget extends TargetPermanent { + + private Permanent firstTarget = null; + + public PucasMischiefSecondTarget() { + super(); + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + setTargetName("permanent an opponent controls with an equal or lesser converted mana cost"); + } + + public PucasMischiefSecondTarget(final PucasMischiefSecondTarget target) { + super(target); + this.firstTarget = target.firstTarget; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (super.canTarget(id, source, game)) { + Permanent target1 = game.getPermanent(source.getFirstTarget()); + Permanent opponentPermanent = game.getPermanent(id); + if (target1 != null && opponentPermanent != null) { + return target1.getManaCost().convertedManaCost() >= opponentPermanent.getManaCost().convertedManaCost(); + } + } + return false; + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + if (firstTarget != null) { + MageObject targetSource = game.getObject(sourceId); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { + if (firstTarget.getManaCost().convertedManaCost() >= permanent.getManaCost().convertedManaCost()) { + possibleTargets.add(permanent.getId()); + } + } + } + } + return possibleTargets; + } + + @Override + public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) { + firstTarget = game.getPermanent(source.getFirstTarget()); + return super.chooseTarget(Outcome.GainControl, playerId, source, game); + } + + @Override + public PucasMischiefSecondTarget copy() { + return new PucasMischiefSecondTarget(this); + } +} From a8f8697a25276fe6300da0695b9093c2d267fdd2 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 25 Feb 2015 11:55:21 -0600 Subject: [PATCH 08/61] - Little adjustments to 2 cards. --- Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java | 6 +++--- Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java index aaa6b6e7b77..79aa96db8cf 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java @@ -96,7 +96,7 @@ class AdviceFromTheFaeEffect extends OneShotEffect { MageObject mageObject = game.getObject(source.getSourceId()); if (controller != null) { List cardsFromTopLibrary = controller.getLibrary().getTopCards(game, 5); - Cards cards = new CardsImpl(Zone.HAND); + Cards cards = new CardsImpl(Zone.LIBRARY); for (Card card : cardsFromTopLibrary) { cards.add(card); } @@ -112,7 +112,7 @@ class AdviceFromTheFaeEffect extends OneShotEffect { } } if (game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), controller.getId(), game) > max) { - TargetCard target = new TargetCard(2, Zone.HAND, new FilterCard()); + TargetCard target = new TargetCard(2, Zone.LIBRARY, new FilterCard()); if (controller.choose(Outcome.DrawCard, cards, target, game)) { controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getId(), game, Zone.LIBRARY); cards.remove(game.getCard(target.getFirstTarget())); @@ -120,7 +120,7 @@ class AdviceFromTheFaeEffect extends OneShotEffect { cards.remove(game.getCard(target.getTargets().get(1))); } } else { - TargetCard target = new TargetCard(1, Zone.HAND, new FilterCard()); + TargetCard target = new TargetCard(1, Zone.LIBRARY, new FilterCard()); if (controller.choose(Outcome.DrawCard, cards, target, game)) { controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getId(), game, Zone.LIBRARY); cards.remove(game.getCard(target.getFirstTarget())); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java index 9b4601cb26d..82b79bb4866 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java @@ -117,7 +117,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { Cards cardsToCheck = new CardsImpl(); cardsToCheck.addAll(possibleTargets); if (targets.size() == 1) { - // first target is laready choosen, now only targets with the same name are selectable + // first target is already choosen, now only targets with the shared color are selectable for (Map.Entry entry : targets.entrySet()) { Card chosenCard = cardsToCheck.get(entry.getKey(), game); if (chosenCard != null) { From 329165555b7703713333ee97d9656e2b78241a2f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 25 Feb 2015 22:21:17 +0100 Subject: [PATCH 09/61] * Undying - Fixed that the creature with undying returnd under the control of the previous controller instead of the card owner. --- .../cards/abilities/keywords/UndyingTest.java | 37 +++++++++++++++++++ .../abilities/keyword/UndyingAbility.java | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index e4e861283ce..2545cc81a7d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -76,4 +76,41 @@ public class UndyingTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Elite Vanguard", 3, 2); } + + /** + * Tests "Threads of Disloyalty enchanting Strangleroot Geist: after geist died it returns to the bf under opponent's control." + */ + @Test + public void testUndyingControlledReturnsToOwner() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // Strangleroot Geist 2/1 + // Haste + // Undying + // (When it dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Strangleroot Geist"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + // Threads of Disloyalty {1}{U}{U} + // Enchant creature with converted mana cost 2 or less + // You control enchanted creature. + addCard(Zone.HAND, playerB, "Threads of Disloyalty"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Strangleroot Geist"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Threads of Disloyalty", "Strangleroot Geist"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Strangleroot Geist"); + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Threads of Disloyalty", 1); + assertGraveyardCount(playerA, "Lightning Bolt",1); + assertPermanentCount(playerB, "Strangleroot Geist", 0); + assertPermanentCount(playerA, "Strangleroot Geist", 1); + assertPowerToughness(playerA, "Strangleroot Geist", 3, 2); + } + + } diff --git a/Mage/src/mage/abilities/keyword/UndyingAbility.java b/Mage/src/mage/abilities/keyword/UndyingAbility.java index e5aad5a8e76..f77fbd18708 100644 --- a/Mage/src/mage/abilities/keyword/UndyingAbility.java +++ b/Mage/src/mage/abilities/keyword/UndyingAbility.java @@ -23,7 +23,7 @@ public class UndyingAbility extends DiesTriggeredAbility { public UndyingAbility() { super(new UndyingEffect()); - this.addEffect(new ReturnSourceFromGraveyardToBattlefieldEffect()); + this.addEffect(new ReturnSourceFromGraveyardToBattlefieldEffect(false, true)); } public UndyingAbility(final UndyingAbility ability) { From 41898c8e0829c5c994d1fcd8e4965d3aae48e7a0 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 25 Feb 2015 16:45:28 -0600 Subject: [PATCH 10/61] - Little fix --- Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java index 79aa96db8cf..ae8ad26b0b3 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java @@ -114,15 +114,15 @@ class AdviceFromTheFaeEffect extends OneShotEffect { if (game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), controller.getId(), game) > max) { TargetCard target = new TargetCard(2, Zone.LIBRARY, new FilterCard()); if (controller.choose(Outcome.DrawCard, cards, target, game)) { - controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getId(), game, Zone.LIBRARY); + controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getSourceId(), game, Zone.LIBRARY); cards.remove(game.getCard(target.getFirstTarget())); - controller.moveCardToHandWithInfo(game.getCard(target.getTargets().get(1)), source.getId(), game, Zone.LIBRARY); + controller.moveCardToHandWithInfo(game.getCard(target.getTargets().get(1)), source.getSourceId(), game, Zone.LIBRARY); cards.remove(game.getCard(target.getTargets().get(1))); } } else { TargetCard target = new TargetCard(1, Zone.LIBRARY, new FilterCard()); if (controller.choose(Outcome.DrawCard, cards, target, game)) { - controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getId(), game, Zone.LIBRARY); + controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getSourceId(), game, Zone.LIBRARY); cards.remove(game.getCard(target.getFirstTarget())); } } From 957fa7d6474b089d401f32a80d5b2ab94638240d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 25 Feb 2015 23:47:53 +0100 Subject: [PATCH 11/61] Some minor changes. --- .../sets/avacynrestored/CavernOfSouls.java | 3 +- .../mage/sets/magic2015/ReturnToTheRanks.java | 3 -- .../cards/single/avr/CavernOfSoulsTest.java | 39 ++++++++++++++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java b/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java index 6a58f6f827c..b9b0f65f06d 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java @@ -36,6 +36,7 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.ContinuousRuleModifiyingEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.mana.ColorlessManaAbility; @@ -79,7 +80,7 @@ public class CavernOfSouls extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered. - this.addAbility(new ConditionalAnyColorManaAbility(1, new CavernOfSoulsManaBuilder())); + this.addAbility(new ConditionalAnyColorManaAbility(new TapSourceCost(), 1, new CavernOfSoulsManaBuilder(), true)); this.addWatcher(new CavernOfSoulsWatcher()); this.addAbility(new SimpleStaticAbility(Zone.ALL, new CavernOfSoulsCantCounterEffect())); } diff --git a/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java b/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java index 1786632046e..2bd7ada215c 100644 --- a/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java +++ b/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java @@ -29,7 +29,6 @@ package mage.sets.magic2015; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.ConvokeAbility; @@ -58,8 +57,6 @@ public class ReturnToTheRanks extends CardImpl { super(ownerId, 29, "Return to the Ranks", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{X}{W}{W}"); this.expansionSetCode = "M15"; - this.color.setWhite(true); - // Convoke this.addAbility(new ConvokeAbility()); // Return X target creature cards with converted mana cost 2 or less from your graveyard to the battlefield. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java index 88dc2964198..4b2fc04a518 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java @@ -185,5 +185,42 @@ public class CavernOfSoulsTest extends CardTestPlayerBase { // Check Horror on the Battlefield // assertPermanentCount(playerA, "Fume Spitter", 1); } - + + /** + * Return to the Ranks cannot be countered if mana produced by Cavern of Souls + * was used to pay X. Can be bug also for all other spells with X in their cost, not sure. + * + */ + @Test + public void testCastWithColorlessManaCanBeCountered() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Cavern of Souls"); + // Sorcery {X}{W}{W} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.) + // Return X target creature cards with converted mana cost 2 or less from your graveyard to the battlefield. + addCard(Zone.HAND, playerA, "Return to the Ranks"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + // {1}{U} Remove Soul - Counter target creature spell. + addCard(Zone.HAND, playerB, "Counterspell"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cavern of Souls"); + setChoice(playerA, "Drake"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Return to the Ranks", "Silvercoat Lion"); + setChoice(playerA, "X=1"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Return to the Ranks"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // check it was countered + assertGraveyardCount(playerA, "Return to the Ranks", 1); + assertGraveyardCount(playerB, "Counterspell", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 0); + + } } From f96a256c2871c75574a8ab65e696f002c68cc883 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 00:15:48 +0100 Subject: [PATCH 12/61] * Bramblewood Paragon - Fixed subtype check (not working for Changeling). --- Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java b/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java index ce2e58e89a1..419dbca04d7 100644 --- a/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java +++ b/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java @@ -106,7 +106,7 @@ class BramblewoodParagonReplacementEffect extends ReplacementEffectImpl { Permanent creature = game.getPermanent(event.getTargetId()); if (creature != null && creature.getControllerId().equals(source.getControllerId()) && creature.getCardType().contains(CardType.CREATURE) - && creature.getSubtype().contains("Warrior") + && creature.hasSubtype("Warrior") && !event.getTargetId().equals(source.getSourceId())) { return true; } From d45c8b52dd91a8e5608e8e864cbff58ed801605e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 00:43:43 +0100 Subject: [PATCH 13/61] * Tuktuk Scrapper - Fixed that the triggered ability did not work. --- .../mage/sets/worldwake/TuktukScrapper.java | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java b/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java index 0840c653c5b..f1476ca3aee 100644 --- a/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java +++ b/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java @@ -28,17 +28,17 @@ package mage.sets.worldwake; import java.util.UUID; - -import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -58,7 +58,6 @@ public class TuktukScrapper extends CardImpl { this.subtype.add("Artificer"); this.subtype.add("Ally"); - this.color.setRed(true); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -80,6 +79,7 @@ class TuktukScrapperTriggeredAbility extends TriggeredAbilityImpl { public TuktukScrapperTriggeredAbility() { super(Zone.BATTLEFIELD, new TuktukScrapperEffect(), true); + this.addTarget(new TargetArtifactPermanent()); } public TuktukScrapperTriggeredAbility(final TuktukScrapperTriggeredAbility ability) { @@ -91,15 +91,19 @@ class TuktukScrapperTriggeredAbility extends TriggeredAbilityImpl { return new TuktukScrapperTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getId() == this.getSourceId()) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + if (permanent.getId() == this.getSourceId()) { return true; } - if (permanent != null - && permanent.hasSubtype("Ally") + if (permanent.hasSubtype("Ally") && permanent.getControllerId().equals(this.getControllerId())) { return true; } @@ -115,11 +119,10 @@ class TuktukScrapperTriggeredAbility extends TriggeredAbilityImpl { class TuktukScrapperEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent(); + private static final FilterControlledPermanent filter = new FilterControlledPermanent(); static { filter.add(new SubtypePredicate("Ally")); - filter.add(new ControllerPredicate(TargetController.YOU)); } public TuktukScrapperEffect() { @@ -137,22 +140,18 @@ class TuktukScrapperEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - TargetArtifactPermanent target = new TargetArtifactPermanent(); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.DestroyPermanent, source.getControllerId(), source.getSourceId(), game)) { - Permanent targetedArtifact = game.getPermanent(target.getFirstTarget()); - if (targetedArtifact != null) { - Card artifact = game.getCard(targetedArtifact.getId()); - Player controller = game.getPlayer(targetedArtifact.getControllerId()); - targetedArtifact.destroy(id, game, true); - if (controller.getGraveyard().contains(artifact.getId())) { - int alliesControlled = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); - controller.damage(alliesControlled, id, game, false, true); - return true; - } + Permanent targetArtifact = game.getPermanent(getTargetPointer().getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && targetArtifact != null) { + targetArtifact.destroy(source.getSourceId(), game, false); + Player targetController = game.getPlayer(targetArtifact.getControllerId()); + if (targetController != null && game.getState().getZone(targetArtifact.getId()).equals(Zone.GRAVEYARD)) { + int alliesControlled = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + if (alliesControlled > 0) { + targetController.damage(alliesControlled, source.getSourceId(), game, false, true); } } + return true; } return false; } From 35f249cd44b900cba422846c411a035d1a21e77f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 01:04:09 +0100 Subject: [PATCH 14/61] * Hellkite Tyrant - Fixed wrong duration of control effect (fixes #752). --- Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java b/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java index 3e3bcea42e3..7d2da234f22 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java +++ b/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java @@ -139,7 +139,7 @@ class HellkiteTyrantControlEffect extends ContinuousEffectImpl { private final UUID controllerId; public HellkiteTyrantControlEffect(UUID controllerId) { - super(Duration.EndOfCombat, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); this.controllerId = controllerId; } From c681f1583cddb9eb2be282baca3c62fd7859ee74 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 01:18:45 +0100 Subject: [PATCH 15/61] * Ghostway - Fixed that the creatures returned wrongly tapped (fixes #753). --- .../src/mage/sets/guildpact/Ghostway.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java index 4e45ee3c595..ed6267976c2 100644 --- a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java +++ b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java @@ -28,18 +28,24 @@ package mage.sets.guildpact; import java.util.UUID; - -import mage.constants.*; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; /** * @@ -51,8 +57,6 @@ public class Ghostway extends CardImpl { super(ownerId, 6, "Ghostway", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "GPT"; - this.color.setWhite(true); - // Exile each creature you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step. this.getSpellAbility().addEffect(new GhostwayEffect()); } @@ -87,19 +91,25 @@ class GhostwayEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID exileId = source.getSourceId(); - if (exileId != null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject != null && controller != null) { + int numberCreatures = 0; + UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (creature != null) { - if (creature.moveToExile(source.getSourceId(), "Ghostway Exile", source.getSourceId(), game)) { - AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD, true)); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); - game.addDelayedTriggeredAbility(delayedAbility); - } + controller.moveCardToExileWithInfo(creature, exileId,sourceObject.getLogName(), source.getSourceId(), game, Zone.BATTLEFIELD); + numberCreatures++; } } + if (numberCreatures > 0) { + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false)); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game)); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } return false; @@ -109,4 +119,4 @@ class GhostwayEffect extends OneShotEffect { public GhostwayEffect copy() { return new GhostwayEffect(this); } -} \ No newline at end of file +} From fe10cde2b92864dc2961fc0d0b7a24a4c29c3f4c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 11:24:47 +0100 Subject: [PATCH 16/61] * Contamination - Fixed a bug that preventzed Contamination from working properly. --- Mage.Sets/src/mage/sets/urzassaga/Contamination.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/urzassaga/Contamination.java b/Mage.Sets/src/mage/sets/urzassaga/Contamination.java index c4ebedb2ada..d4ccf5e8ec8 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Contamination.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Contamination.java @@ -114,7 +114,7 @@ class ContaminationReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - MageObject mageObject = source.getSourceObject(game); + MageObject mageObject = game.getObject(event.getSourceId()); return mageObject != null && mageObject.getCardType().contains(CardType.LAND); } } From 972fe78898bd851cabf3da57bd7597b455bff766 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 12:07:28 +0100 Subject: [PATCH 17/61] * Training Grounds - Fixed that the reduction did not work correctly with hybrid mana. Fixed the check what ability is an activated ability (fixes #754). --- .../sets/riseoftheeldrazi/TrainingGrounds.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java index 2e27dfd92cf..0022b2defbc 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java @@ -35,9 +35,12 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.choices.ChoiceImpl; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.CostModificationType; import mage.constants.Duration; @@ -48,6 +51,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -76,10 +80,7 @@ public class TrainingGrounds extends CardImpl { class TrainingGroundsEffect extends CostModificationEffectImpl { private static final String effectText = "Activated abilities of creatures you control cost up to {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana"; - private static final FilterControlledCreaturePermanent filter; - static { - filter = new FilterControlledCreaturePermanent(); - } + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); public TrainingGroundsEffect() { super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); @@ -113,8 +114,7 @@ class TrainingGroundsEffect extends CostModificationEffectImpl { choice.setMessage("Reduce ability cost"); if(controller.choose(Outcome.Benefit, choice, game)){ int reduce = Integer.parseInt(choice.getChoice()); - mana.setColorless(mana.getColorless() - reduce); - abilityToModify.getManaCostsToPay().load(mana.toString()); + CardUtil.reduceCost(abilityToModify, reduce); } } return true; @@ -124,7 +124,7 @@ class TrainingGroundsEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof ActivatedAbility) { + if (abilityToModify.getAbilityType().equals(AbilityType.ACTIVATED)) { //Activated abilities of creatures you control Permanent permanent = game.getPermanent(abilityToModify.getSourceId()); if (permanent != null && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { From 4a5140d0abc02a145f21e793c886d712db6dfb2c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 13:52:02 +0100 Subject: [PATCH 18/61] Fixed a bug of cost paying handling not showing only the left over unpaid part of mana cost (fixes #185). Added the name of the object the cost is paid for to the feedback panel. --- .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../src/mage/player/human/HumanPlayer.java | 8 +++--- .../abilities/costs/mana/ManaCostImpl.java | 6 +++- .../abilities/costs/mana/ManaCostsImpl.java | 26 +++-------------- Mage/src/mage/players/Player.java | 2 +- Mage/src/mage/util/ManaUtil.java | 28 +++++++++++++++++++ 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 11d8d71fae1..83df4dce0ed 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1011,7 +1011,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - public boolean playMana(ManaCost unpaid, Game game) { + public boolean playMana(ManaCost unpaid, String promptText, Game game) { payManaMode = true; boolean result = playManaHandling(unpaid, game); payManaMode = false; diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 9c863bc9f3c..bef521c8db5 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -606,17 +606,17 @@ public class HumanPlayer extends PlayerImpl { @Override - public boolean playMana(ManaCost unpaid, Game game) { + public boolean playMana(ManaCost unpaid, String promptText, Game game) { payManaMode = true; - boolean result = playManaHandling(unpaid, game); + boolean result = playManaHandling(unpaid, promptText, game); payManaMode = false; return result; } - protected boolean playManaHandling(ManaCost unpaid, Game game) { + protected boolean playManaHandling(ManaCost unpaid, String promptText, Game game) { updateGameStatePriority("playMana", game); - game.firePlayManaEvent(playerId, "Pay " + unpaid.getText()); + game.firePlayManaEvent(playerId, "Pay " + promptText); waitForResponse(game); if (!this.isInGame()) { return false; diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java index 465a60b4fdc..55407b2415c 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java @@ -39,6 +39,7 @@ import mage.filter.Filter; import mage.game.Game; import mage.players.ManaPool; import mage.players.Player; +import mage.util.ManaUtil; public abstract class ManaCostImpl extends CostImpl implements ManaCost { @@ -218,12 +219,15 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { Player player = game.getPlayer(controllerId); assignPayment(game, ability, player.getManaPool()); while (!isPaid()) { - if (player.playMana(this, game)) { + ManaCost unpaid = this.getUnpaid(); + String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid); + if (player.playMana(unpaid, promptText, game)) { assignPayment(game, ability, player.getManaPool()); } else { return false; } + game.getState().getSpecialActions().removeManaActions(); } return true; } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java index 7b34a9701c8..77f84f7eae3 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java @@ -37,7 +37,6 @@ import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.VariableCost; -import mage.abilities.keyword.DelveAbility; import mage.abilities.mana.ManaOptions; import mage.constants.ColoredManaSymbol; import mage.filter.Filter; @@ -45,6 +44,7 @@ import mage.game.Game; import mage.players.ManaPool; import mage.players.Player; import mage.target.Targets; +import mage.util.ManaUtil; /** * @author BetaSteward_at_googlemail.com @@ -121,8 +121,9 @@ public class ManaCostsImpl extends ArrayList implements M Player player = game.getPlayer(controllerId); assignPayment(game, ability, player.getManaPool()); while (!isPaid()) { - addSpecialManaPayAbilities(ability, game); - if (player.playMana(this.getUnpaid(), game)) { + ManaCost unpaid = this.getUnpaid(); + String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid); + if (player.playMana(unpaid, promptText, game)) { assignPayment(game, ability, player.getManaPool()); } else { return false; @@ -132,25 +133,6 @@ public class ManaCostsImpl extends ArrayList implements M return true; } - /** - * This activates the special button if there exists special ways to pay the mana (Delve, Convoke) - * - * @param ability - * @param game - */ - private void addSpecialManaPayAbilities(Ability source, Game game) { - // check for special mana payment possibilities - MageObject mageObject = source.getSourceObject(game); - if (mageObject != null) { - for (Ability ability :mageObject.getAbilities()) { - if (ability instanceof AlternateManaPaymentAbility) { - ((AlternateManaPaymentAbility) ability).addSpecialAction(source, game, getUnpaid()); - } - } - } - } - - /** * bookmarks the current state and restores it if player doesn't pay the mana cost * diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index b22e86a7fd8..a9d847a8030 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -299,7 +299,7 @@ public interface Player extends MageItem, Copyable { boolean chooseUse(Outcome outcome, String message, Game game); boolean choose(Outcome outcome, Choice choice, Game game); boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game); - boolean playMana(ManaCost unpaid, Game game); + boolean playMana(ManaCost unpaid, String promptText, Game game); /** * Moves the cards from cards to the bottom of the players library. diff --git a/Mage/src/mage/util/ManaUtil.java b/Mage/src/mage/util/ManaUtil.java index 160a483d450..080efef553f 100644 --- a/Mage/src/mage/util/ManaUtil.java +++ b/Mage/src/mage/util/ManaUtil.java @@ -10,6 +10,10 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Set; import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.costs.mana.AlternateManaPaymentAbility; +import mage.game.Game; /** * @author noxx @@ -334,4 +338,28 @@ public class ManaUtil { return useableAbilities; } + + /** + * This activates the special button inthe feedback panel of the client + * if there exists special ways to pay the mana (e.g. Delve, Convoke) + * + * @param source ability the mana costs have to be paid for + * @param game + * @param unpaid mana that has still to be paid + * @return message to be shown in human players feedback area + */ + public static String addSpecialManaPayAbilities(Ability source, Game game, ManaCost unpaid) { + // check for special mana payment possibilities + MageObject mageObject = source.getSourceObject(game); + if (mageObject != null) { + for (Ability ability :mageObject.getAbilities()) { + if (ability instanceof AlternateManaPaymentAbility) { + ((AlternateManaPaymentAbility) ability).addSpecialAction(source, game, unpaid); + } + } + return unpaid.getText() + "
" + mageObject.getLogName() + "
"; + } else { + return unpaid.getText(); + } + } } From 39d1fd6f9073b6e9788fc44b2d23e362c78f0563 Mon Sep 17 00:00:00 2001 From: Plopman Date: Thu, 26 Feb 2015 16:07:34 +0100 Subject: [PATCH 19/61] [5ND] Added 7 cards --- .../CircleOfProtectionArtifacts.java | 53 ++++++ .../mage/sets/fifthdawn/ArmedResponse.java | 73 ++++++++ .../CircleOfProtectionArtifacts.java | 73 ++++++++ .../mage/sets/fifthdawn/EnsouledScimitar.java | 89 ++++++++++ .../src/mage/sets/fifthdawn/Retaliate.java | 64 +++++++ .../src/mage/sets/fifthdawn/StasisCocoon.java | 79 +++++++++ .../src/mage/sets/fifthdawn/SummonersEgg.java | 160 ++++++++++++++++++ .../mage/sets/fifthdawn/SummoningStation.java | 82 +++++++++ .../CircleOfProtectionArtifacts.java | 53 ++++++ .../CircleOfProtectionArtifacts.java | 53 ++++++ 10 files changed, 779 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java create mode 100644 Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java create mode 100644 Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java diff --git a/Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..2bfcc5801ed --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java @@ -0,0 +1,53 @@ +/* + * 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.antiquities; + +import java.util.UUID; + + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends mage.sets.fifthdawn.CircleOfProtectionArtifacts { + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId); + this.cardNumber = 97; + this.expansionSetCode = "ATQ"; + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java b/Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java new file mode 100644 index 00000000000..d42f0588ae7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java @@ -0,0 +1,73 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetAttackingCreature; + +/** + * + * @author Plopman + */ +public class ArmedResponse extends CardImpl { + + private static final FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("Equipment you control"); + + static { + filter.add(new SubtypePredicate("Equipment")); + } + + + public ArmedResponse(UUID ownerId) { + super(ownerId, 2, "Armed Response", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); + this.expansionSetCode = "5DN"; + + // Armed Response deals damage to target attacking creature equal to the number of Equipment you control. + Effect effect = new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)); + effect.setText("{source} deals damage to target attacking creature equal to the number of Equipment you control"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public ArmedResponse(final ArmedResponse card) { + super(card); + } + + @Override + public ArmedResponse copy() { + return new ArmedResponse(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..63fbd9de86b --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java @@ -0,0 +1,73 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; + +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterObject; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends CardImpl { + + private static final FilterObject filter = new FilterObject("artifact source"); + + static { + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + } + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId, 8, "Circle of Protection: Artifacts", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "5DN"; + + // {2}: The next time an artifact source of your choice would deal damage to you this turn, prevent that damage. + Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("2"))); + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java b/Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java new file mode 100644 index 00000000000..4c9dd258ff5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java @@ -0,0 +1,89 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continious.BecomesCreatureSourceEffect; +import mage.abilities.effects.common.continious.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FlyingAbility; +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.game.permanent.token.Token; + +/** + * + * @author Plopman + */ +public class EnsouledScimitar extends CardImpl { + + public EnsouledScimitar(UUID ownerId) { + super(ownerId, 119, "Ensouled Scimitar", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "5DN"; + this.subtype.add("Equipment"); + + // {3}: Ensouled Scimitar becomes a 1/5 Spirit artifact creature with flying until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new EnsouledScimitarToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{3}"))); + // Equipped creature gets +1/+5. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 5))); + // Equip {2} + this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{2}"))); + } + + public EnsouledScimitar(final EnsouledScimitar card) { + super(card); + } + + @Override + public EnsouledScimitar copy() { + return new EnsouledScimitar(this); + } +} + +class EnsouledScimitarToken extends Token { + + public EnsouledScimitarToken() { + super("Pincher", "1/5 Spirit artifact creature with flying"); + setOriginalExpansionSetCode("5ND"); + cardType.add(CardType.CREATURE); + subtype.add("Spirit"); + power = new MageInt(1); + toughness = new MageInt(5); + this.addAbility(FlyingAbility.getInstance()); + + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java b/Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java new file mode 100644 index 00000000000..b6759ba4273 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java @@ -0,0 +1,64 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.DamagedPlayerThisTurnPredicate; + +/** + * + * @author Plopman + */ +public class Retaliate extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that dealt damage to you this turn"); + static { + filter.add(new DamagedPlayerThisTurnPredicate(TargetController.YOU)); + } + public Retaliate(UUID ownerId) { + super(ownerId, 13, "Retaliate", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W}{W}"); + this.expansionSetCode = "5DN"; + + // Destroy all creatures that dealt damage to you this turn. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + public Retaliate(final Retaliate card) { + super(card); + } + + @Override + public Retaliate copy() { + return new Retaliate(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java b/Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java new file mode 100644 index 00000000000..c65531b98a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java @@ -0,0 +1,79 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBlockAttackActivateAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author Plopman + */ +public class StasisCocoon extends CardImpl { + + public StasisCocoon(UUID ownerId) { + super(ownerId, 18, "Stasis Cocoon", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "5DN"; + this.subtype.add("Aura"); + + // Enchant artifact + TargetPermanent auraTarget = new TargetArtifactPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + + // Enchanted artifact can't attack or block, and its activated abilities can't be activated. + Effect effect = new CantBlockAttackActivateAttachedEffect(); + effect.setText("Enchanted artifact can't attack or block, and its activated abilities can't be activated"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + } + + public StasisCocoon(final StasisCocoon card) { + super(card); + } + + @Override + public StasisCocoon copy() { + return new StasisCocoon(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java b/Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java new file mode 100644 index 00000000000..764fff6aca1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java @@ -0,0 +1,160 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +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.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +/** + * + * @author Plopman + */ +public class SummonersEgg extends CardImpl { + + public SummonersEgg(UUID ownerId) { + super(ownerId, 157, "Summoner's Egg", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "5DN"; + this.subtype.add("Construct"); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Imprint - When Summoner's Egg enters the battlefield, you may exile a card from your hand face down. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SummonersEggImprintEffect(), true, "Imprint - ")); + // When Summoner's Egg dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control. + this.addAbility(new DiesTriggeredAbility(new SummonersEggPutOntoBattlefieldEffect())); + } + + public SummonersEgg(final SummonersEgg card) { + super(card); + } + + @Override + public SummonersEgg copy() { + return new SummonersEgg(this); + } +} + +class SummonersEggImprintEffect extends OneShotEffect { + + public SummonersEggImprintEffect() { + super(Outcome.Benefit); + staticText = "exile a card from your hand face down"; + } + + public SummonersEggImprintEffect(SummonersEggImprintEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null) { + if (controller.getHand().size() > 0) { + TargetCard target = new TargetCard(Zone.HAND, new FilterCard()); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.choose(Outcome.Benefit, controller.getHand(), target, game)) { + Card card = controller.getHand().get(target.getFirstTarget(), game); + if (card != null) { + card.setFaceDown(true); + controller.moveCardToExileWithInfo(card, source.getSourceId(), sourcePermanent.getLogName() +" (Imprint)", source.getSourceId(), game, Zone.HAND); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.imprint(card.getId(), game); + permanent.addInfo("imprint", CardUtil.addToolTipMarkTags("[Imprinted card]")); + } + } + } + } + return true; + } + return false; + + } + + @Override + public SummonersEggImprintEffect copy() { + return new SummonersEggImprintEffect(this); + } + +} + +class SummonersEggPutOntoBattlefieldEffect extends OneShotEffect { + + public SummonersEggPutOntoBattlefieldEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control"; + } + + public SummonersEggPutOntoBattlefieldEffect(final SummonersEggPutOntoBattlefieldEffect effect) { + super(effect); + } + + @Override + public SummonersEggPutOntoBattlefieldEffect copy() { + return new SummonersEggPutOntoBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent SummonersEgg = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (SummonersEgg != null && SummonersEgg.getImprinted() != null && !SummonersEgg.getImprinted().isEmpty()) { + Card imprintedCard = game.getCard(SummonersEgg.getImprinted().get(0)); + if (imprintedCard != null && game.getState().getZone(imprintedCard.getId()).equals(Zone.EXILED)) { + //turn the exiled card face up. + imprintedCard.turnFaceUp(game, source.getControllerId()); + //If it's a creature card, + if(imprintedCard.getCardType().contains(CardType.CREATURE)){ + //put it onto the battlefield under your control + imprintedCard.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), source.getControllerId()); + } + } + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java b/Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java new file mode 100644 index 00000000000..6153ce3a0dc --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java @@ -0,0 +1,82 @@ +/* + * 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.fifthdawn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterArtifactPermanent; +import mage.game.permanent.token.Token; + +/** + * + * @author Plopman + */ +public class SummoningStation extends CardImpl { + + public SummoningStation(UUID ownerId) { + super(ownerId, 158, "Summoning Station", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{7}"); + this.expansionSetCode = "5DN"; + + // {tap}: Put a 2/2 colorless Pincher creature token onto the battlefield. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,new CreateTokenEffect(new PincherToken()), new TapSourceCost())); + // Whenever an artifact is put into a graveyard from the battlefield, you may untap Summoning Station. + this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility(new UntapSourceEffect(),true, new FilterArtifactPermanent(), false)); + } + + public SummoningStation(final SummoningStation card) { + super(card); + } + + @Override + public SummoningStation copy() { + return new SummoningStation(this); + } +} + +class PincherToken extends Token { + + public PincherToken() { + super("Pincher", "2/2 colorless Pincher creature token"); + setOriginalExpansionSetCode("5ND"); + cardType.add(CardType.CREATURE); + subtype.add("Pincher"); + power = new MageInt(2); + toughness = new MageInt(2); + + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..7ae404629fa --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java @@ -0,0 +1,53 @@ +/* + * 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.fifhedition; + +import java.util.UUID; + + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends mage.sets.fifthdawn.CircleOfProtectionArtifacts { + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId); + this.cardNumber = 292; + this.expansionSetCode = "5ED"; + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..7e630e27c15 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java @@ -0,0 +1,53 @@ +/* + * 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; + + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends mage.sets.fifthdawn.CircleOfProtectionArtifacts { + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId); + this.cardNumber = 263; + this.expansionSetCode = "4ED"; + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} From c35e51c7de19ebeef1ca10c9ca9290d55ac6a704 Mon Sep 17 00:00:00 2001 From: myersn024 Date: Thu, 26 Feb 2015 09:47:02 -0600 Subject: [PATCH 20/61] Implemented DwarvenMiner.java --- .../src/mage/sets/mirage/DwarvenMiner.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java diff --git a/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java b/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java new file mode 100644 index 00000000000..7f5d0171775 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.sets.mirage; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.Target; +import mage.target.common.TargetNonBasicLandPermanent; + +/** + * + * @author nick.myers + */ +public class DwarvenMiner extends CardImpl { + + public DwarvenMiner(UUID ownerId) { + super(ownerId, 169, "Dwarven Miner", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "MIR"; + this.subtype.add("Dwarf"); + + this.color.setRed(true); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {2}{R}, {tap}: Destroy target nonbasic land + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{R}")); + ability.addCost(new TapSourceCost()); + Target target = new TargetNonBasicLandPermanent(); + ability.addTarget(target); + this.addAbility(ability); + + } + + public DwarvenMiner(final DwarvenMiner card) { + super(card); + } + + @Override + public DwarvenMiner copy() { + return new DwarvenMiner(this); + } + + + +} From 705debfe8ac78bc7558faac3d294909700af34dc Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 16:48:21 +0100 Subject: [PATCH 21/61] Fixed some cards where it was possible to sacrifice not controlled permanents (e.g. Fireblast). --- .../mage/sets/alarareborn/DefilerOfSouls.java | 6 +- .../src/mage/sets/alarareborn/TimeSieve.java | 7 +- .../sets/betrayersofkamigawa/Lifespinner.java | 4 +- .../betrayersofkamigawa/TorrentOfStone.java | 4 +- .../championsofkamigawa/FeastOfWorms.java | 35 ++--- .../ShimatsuTheBloodcloaked.java | 4 +- .../championsofkamigawa/UyoSilentProphet.java | 6 +- .../mage/sets/dragonsmaze/CatchRelease.java | 51 ++----- Mage.Sets/src/mage/sets/exodus/Cataclysm.java | 46 ++---- .../src/mage/sets/fifthdawn/CosmicLarva.java | 5 +- .../sets/fifthdawn/KrarkClanEngineers.java | 5 +- .../mage/sets/fifthdawn/KrarkClanOgre.java | 4 +- .../src/mage/sets/gatecrash/DevourFlesh.java | 17 +-- .../src/mage/sets/iceage/GlacialChasm.java | 137 ++---------------- .../mage/sets/innistrad/DivineReckoning.java | 17 +-- .../sets/innistrad/GarrukTheVeilCursed.java | 31 ++-- .../mage/sets/jacevschandra/FathomSeer.java | 4 +- .../mage/sets/mercadianmasques/Pulverize.java | 4 +- .../src/mage/sets/odyssey/Dreamwinder.java | 7 +- .../src/mage/sets/onslaught/ReadTheRunes.java | 14 +- .../src/mage/sets/planeshift/SunkenHope.java | 8 +- .../saviorsofkamigawa/ChoiceOfDamnations.java | 9 +- .../scarsofmirrodin/KuldothaForgemaster.java | 4 +- .../sets/theros/AnthousaSetessanHero.java | 4 +- .../mage/sets/timespiral/WalkTheAeons.java | 4 +- Mage.Sets/src/mage/sets/urzassaga/Raze.java | 4 +- .../sets/vintagemasters/BragoKingEternal.java | 7 +- .../src/mage/sets/visions/Fireblast.java | 7 +- .../src/mage/sets/zendikar/WorldQueller.java | 4 +- .../effects/common/SacrificeEffect.java | 8 +- .../common/SacrificeOpponentsEffect.java | 12 +- ...FilterControlledPlaneswalkerPermanent.java | 58 ++++++++ .../common/FilterPlaneswalkerOrPlayer.java | 4 +- .../common/TargetControlledPermanent.java | 2 +- 34 files changed, 206 insertions(+), 337 deletions(-) create mode 100644 Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java diff --git a/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java b/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java index a963945ba80..8a6afe04c35 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java +++ b/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java @@ -36,9 +36,8 @@ import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.MonocoloredPredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -91,12 +90,11 @@ class DefilerOfSoulsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("monocolored creature"); + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("monocolored creature"); Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player == null) { return false; } - filter.add(new ControllerPredicate(TargetController.YOU)); filter.add(new MonocoloredPredicate()); int amount; diff --git a/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java b/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java index 1796af08d14..ed18c9a367c 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java +++ b/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetControlledPermanent; /** @@ -50,12 +50,9 @@ public class TimeSieve extends CardImpl { super(ownerId, 31, "Time Sieve", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{U}{B}"); this.expansionSetCode = "ARB"; - - - // {tap}, Sacrifice five artifacts: Take an extra turn after this one. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddExtraTurnControllerEffect(), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(5, 5, new FilterArtifactPermanent("five artifacts"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(5, 5, new FilterControlledArtifactPermanent("five artifacts"), true))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java index 680a7b39179..80c0a367d11 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.mageobject.SupertypePredicate; @@ -70,7 +70,7 @@ public class Lifespinner extends CardImpl { SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, new FilterCreaturePermanent("Spirit", "three Spirits"), false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, new FilterControlledCreaturePermanent("Spirit", "three Spirits"), false))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java index 6c148278bc0..ab7c99d6365 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java @@ -34,7 +34,7 @@ import mage.abilities.keyword.SpliceOntoArcaneAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; @@ -44,7 +44,7 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public class TorrentOfStone extends CardImpl { - private static final FilterLandPermanent filterSacrifice = new FilterLandPermanent("two Mountains"); + private static final FilterControlledLandPermanent filterSacrifice = new FilterControlledLandPermanent("two Mountains"); static { filterSacrifice.add(new SubtypePredicate("Mountain")); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java b/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java index 2f2f00a5193..112c298cc8f 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java @@ -32,13 +32,12 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -93,22 +92,24 @@ class FeastOfWormsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - Player targetPlayer = game.getPlayer(permanent.getControllerId()); - if (targetPlayer != null && permanent != null && (permanent.getSupertype().get(0).toString().equals("Legendary"))) { - FilterPermanent filter = new FilterLandPermanent("land to sacrifice"); - filter.add(new ControllerIdPredicate(targetPlayer.getId())); - TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, false); + Permanent permanent = game.getPermanentOrLKIBattlefield(id); + Player targetPlayer = null; + if (permanent != null) { + targetPlayer = game.getPlayer(permanent.getControllerId()); + } + if (targetPlayer != null && permanent != null && (permanent.getSupertype().get(0).equals("Legendary"))) { + FilterControlledPermanent filter = new FilterControlledLandPermanent("land to sacrifice"); + filter.add(new ControllerIdPredicate(targetPlayer.getId())); + TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, false); - if (target.canChoose(targetPlayer.getId(), game)) { - targetPlayer.choose(Outcome.Sacrifice, target, source.getSourceId(), game); - - Permanent land = game.getPermanent(target.getFirstTarget()); - if (land != null) { - return land.sacrifice(source.getSourceId(), game); - } - return true; + if (target.canChoose(targetPlayer.getId(), game)) { + targetPlayer.chooseTarget(Outcome.Sacrifice, target, source, game); + Permanent land = game.getPermanent(target.getFirstTarget()); + if (land != null) { + land.sacrifice(source.getSourceId(), game); } + } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java b/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java index d12185583cc..00e064850ed 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java @@ -36,7 +36,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -106,7 +106,7 @@ class ShimatsuTheBloodcloakedEffect extends ReplacementEffectImpl { Permanent creature = game.getPermanent(event.getTargetId()); Player controller = game.getPlayer(source.getControllerId()); if (creature != null && controller != null) { - Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterPermanent(), true); + Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterControlledPermanent(), true); if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { return false; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java b/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java index 8a639f0222b..0610678e95e 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java @@ -41,7 +41,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.Zone; import mage.filter.FilterSpell; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetSpell; @@ -67,7 +67,7 @@ public class UyoSilentProphet extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Moonfolk"); this.subtype.add("Wizard"); - this.color.setBlue(true); + this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -75,7 +75,7 @@ public class UyoSilentProphet extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {2}, Return two lands you control to their owner's hand: Copy target instant or sorcery spell. You may choose new targets for the copy. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyTargetSpellEffect(), new GenericManaCost(2)); - ability.addCost(new ReturnToHandTargetCost(new TargetControlledPermanent(2, 2, new FilterLandPermanent("lands"), false))); + ability.addCost(new ReturnToHandTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("lands"), false))); ability.addTarget(new TargetSpell(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java b/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java index a4d45bbd0fa..ef78adf890f 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java @@ -43,14 +43,12 @@ import mage.abilities.effects.common.continious.GainAbilityTargetEffect; import mage.abilities.effects.common.continious.GainControlTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.SplitCard; -import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.common.FilterArtifactPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.common.FilterLandPermanent; -import mage.filter.common.FilterPlaneswalkerPermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -70,13 +68,8 @@ public class CatchRelease extends SplitCard { super(ownerId, 125, "Catch", "Release", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{U}{R}", "{4}{R}{W}",true); this.expansionSetCode = "DGM"; - this.color.setBlue(true); - this.color.setRed(true); - this.color.setWhite(true); - // Catch // Gain control of target permanent until end of turn. Untap it. It gains haste until end of turn. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(new FilterPermanent())); getLeftHalfCard().getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); @@ -84,7 +77,6 @@ public class CatchRelease extends SplitCard { // Release // Each player sacrifices an artifact, a creature, an enchantment, a land, and a planeswalker. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new ReleaseSacrificeEffect()); } @@ -101,20 +93,6 @@ public class CatchRelease extends SplitCard { class ReleaseSacrificeEffect extends OneShotEffect { - private static final FilterArtifactPermanent filter1 = new FilterArtifactPermanent("artifact you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature you control"); - private static final FilterEnchantmentPermanent filter3 = new FilterEnchantmentPermanent("enchantment you control"); - private static final FilterLandPermanent filter4 = new FilterLandPermanent("land you control"); - private static final FilterPlaneswalkerPermanent filter5 = new FilterPlaneswalkerPermanent("planeswalker you control"); - - static { - filter1.add(new ControllerPredicate(TargetController.YOU)); - filter2.add(new ControllerPredicate(TargetController.YOU)); - filter3.add(new ControllerPredicate(TargetController.YOU)); - filter4.add(new ControllerPredicate(TargetController.YOU)); - filter5.add(new ControllerPredicate(TargetController.YOU)); - } - public ReleaseSacrificeEffect() { super(Outcome.DestroyPermanent); staticText = "Each player sacrifices an artifact, a creature, an enchantment, a land, and a planeswalker"; @@ -126,7 +104,7 @@ class ReleaseSacrificeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List chosen = new ArrayList(); + List chosen = new ArrayList<>(); Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return false; @@ -134,18 +112,11 @@ class ReleaseSacrificeEffect extends OneShotEffect { for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); - Target target1 = new TargetControlledPermanent(1, 1, filter1, false); - Target target2 = new TargetControlledPermanent(1, 1, filter2, false); - Target target3 = new TargetControlledPermanent(1, 1, filter3, false); - Target target4 = new TargetControlledPermanent(1, 1, filter4, false); - Target target5 = new TargetControlledPermanent(1, 1, filter5, false); - - - target1.setNotTarget(false); - target2.setNotTarget(false); - target3.setNotTarget(false); - target4.setNotTarget(false); - target5.setNotTarget(false); + Target target1 = new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent(), true); + Target target2 = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); + Target target3 = new TargetControlledPermanent(1, 1, new FilterControlledEnchantmentPermanent(), true); + Target target4 = new TargetControlledPermanent(1, 1, new FilterControlledLandPermanent(), true); + Target target5 = new TargetControlledPermanent(1, 1, new FilterControlledPlaneswalkerPermanent(), true); if (target1.canChoose(player.getId(), game)) { while (player.isInGame() && !target1.isChosen() && target1.canChoose(player.getId(), game)) { diff --git a/Mage.Sets/src/mage/sets/exodus/Cataclysm.java b/Mage.Sets/src/mage/sets/exodus/Cataclysm.java index 202824f77b0..e9eac061644 100644 --- a/Mage.Sets/src/mage/sets/exodus/Cataclysm.java +++ b/Mage.Sets/src/mage/sets/exodus/Cataclysm.java @@ -37,12 +37,10 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.TargetController; -import mage.filter.common.FilterArtifactPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -59,8 +57,6 @@ public class Cataclysm extends CardImpl { super(ownerId, 3, "Cataclysm", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); this.expansionSetCode = "EXO"; - this.color.setWhite(true); - // Each player chooses from the permanents he or she controls an artifact, a creature, an enchantment, and a land, then sacrifices the rest. this.getSpellAbility().addEffect(new CataclysmEffect()); } @@ -77,18 +73,6 @@ public class Cataclysm extends CardImpl { class CataclysmEffect extends OneShotEffect { - private static final FilterArtifactPermanent filter1 = new FilterArtifactPermanent("artifact you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature you control"); - private static final FilterEnchantmentPermanent filter3 = new FilterEnchantmentPermanent("enchantment you control"); - private static final FilterLandPermanent filter4 = new FilterLandPermanent("land you control"); - - static { - filter1.add(new ControllerPredicate(TargetController.YOU)); - filter2.add(new ControllerPredicate(TargetController.YOU)); - filter3.add(new ControllerPredicate(TargetController.YOU)); - filter4.add(new ControllerPredicate(TargetController.YOU)); - } - public CataclysmEffect() { super(Outcome.DestroyPermanent); staticText = "Each player chooses from among the permanents he or she controls an artifact, a creature, an enchantment, and a land, then sacrifices the rest"; @@ -105,20 +89,14 @@ class CataclysmEffect extends OneShotEffect { for (UUID playerId : game.getPlayerList()) { Player player = game.getPlayer(playerId); - Target target1 = new TargetControlledPermanent(1, 1, filter1, false); - Target target2 = new TargetControlledPermanent(1, 1, filter2, false); - Target target3 = new TargetControlledPermanent(1, 1, filter3, false); - Target target4 = new TargetControlledPermanent(1, 1, filter4, false); - - - target1.setNotTarget(true); - target2.setNotTarget(true); - target3.setNotTarget(true); - target4.setNotTarget(true); + Target target1 = new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent(), true); + Target target2 = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); + Target target3 = new TargetControlledPermanent(1, 1, new FilterControlledEnchantmentPermanent(), true); + Target target4 = new TargetControlledPermanent(1, 1, new FilterControlledLandPermanent(), true); if (target1.canChoose(player.getId(), game)) { while (player.isInGame() && !target1.isChosen() && target1.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target1, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target1, source, game); } Permanent artifact = game.getPermanent(target1.getFirstTarget()); if (artifact != null) { @@ -129,7 +107,7 @@ class CataclysmEffect extends OneShotEffect { if (target2.canChoose(player.getId(), game)) { while (player.isInGame() && !target2.isChosen() && target2.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target2, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target2, source, game); } Permanent creature = game.getPermanent(target2.getFirstTarget()); if (creature != null) { @@ -140,7 +118,7 @@ class CataclysmEffect extends OneShotEffect { if (target3.canChoose(player.getId(), game)) { while (player.isInGame() && !target3.isChosen() && target3.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target3, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target3, source, game); } Permanent enchantment = game.getPermanent(target3.getFirstTarget()); if (enchantment != null) { @@ -151,7 +129,7 @@ class CataclysmEffect extends OneShotEffect { if (target4.canChoose(player.getId(), game)) { while (player.isInGame() && !target4.isChosen() && target4.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target4, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target4, source, game); } Permanent land = game.getPermanent(target4.getFirstTarget()); if (land != null) { diff --git a/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java b/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java index dfaff47e1ed..9a5c5d9b662 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java @@ -37,8 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.target.common.TargetControlledPermanent; /** @@ -59,7 +58,7 @@ public class CosmicLarva extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // At the beginning of your upkeep, sacrifice Cosmic Larva unless you sacrifice two lands. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterLandPermanent("two lands"), true))), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("two lands"), true))), TargetController.YOU, false)); } public CosmicLarva(final CosmicLarva card) { diff --git a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java index 9c3f2a44dfd..3a756a953a6 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java @@ -38,7 +38,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetArtifactPermanent; import mage.target.common.TargetControlledPermanent; @@ -54,13 +54,12 @@ public class KrarkClanEngineers extends CardImpl { this.subtype.add("Goblin"); this.subtype.add("Artificer"); - this.color.setRed(true); this.power = new MageInt(2); this.toughness = new MageInt(2); // {R}, Sacrifice two artifacts: Destroy target artifact. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{R}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterArtifactPermanent("two artifacts"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("two artifacts"), true))); ability.addTarget(new TargetArtifactPermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java index ef5456e9ca9..2ffe0c13884 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java @@ -39,7 +39,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; @@ -60,7 +60,7 @@ public class KrarkClanOgre extends CardImpl { // {R}, Sacrifice an artifact: Target creature can't block this turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBlockTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{R}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, new FilterArtifactPermanent("an artifact"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent("an artifact"), true))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java b/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java index efd2afa8bca..08c0c7cd7c0 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java +++ b/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java @@ -35,10 +35,7 @@ import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -52,17 +49,10 @@ import mage.target.common.TargetControlledPermanent; */ public class DevourFlesh extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static{ - filter.add(new ControllerPredicate(TargetController.OPPONENT)); - } - public DevourFlesh (UUID ownerId) { super(ownerId, 63, "Devour Flesh", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "GTC"; - this.color.setBlack(true); - // Target player sacrifices a creature, then gains life equal to that creature's toughness. this.getSpellAbility().addEffect(new DevourFleshSacrificeEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); @@ -100,13 +90,12 @@ class DevourFleshSacrificeEffect extends OneShotEffect { if (player == null) { return false; } - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(player.getId())); + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); int realCount = game.getBattlefield().countAll(filter, player.getId(), game); if (realCount > 0) { Target target = new TargetControlledPermanent(1, 1, filter, true); while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { diff --git a/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java b/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java index 1a529307de7..80c73192bba 100644 --- a/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java +++ b/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java @@ -27,29 +27,23 @@ */ package mage.sets.iceage; -import mage.abilities.Ability; +import java.util.UUID; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.PreventAllDamageToControllerEffect; +import mage.abilities.effects.common.SacrificeControllerEffect; +import mage.abilities.effects.common.combat.CantAttackAllAnyPlayerEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; -import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetControlledPermanent; - -import java.util.UUID; /** * @@ -64,9 +58,11 @@ public class GlacialChasm extends CardImpl { // Cumulative upkeep-Pay 2 life. this.addAbility(new CumulativeUpkeepAbility(new PayLifeCost(2))); // When Glacial Chasm enters the battlefield, sacrifice a land. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(new FilterLandPermanent(), 1, ""))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(new FilterControlledLandPermanent(), 1, ""))); // Creatures you control can't attack. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackEffect())); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control"); + filter.add(new ControllerPredicate(TargetController.YOU)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAllAnyPlayerEffect(Duration.WhileOnBattlefield, filter))); // Prevent all damage that would be dealt to you. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PreventAllDamageToControllerEffect(Duration.WhileOnBattlefield))); } @@ -80,108 +76,3 @@ public class GlacialChasm extends CardImpl { return new GlacialChasm(this); } } -class SacrificeControllerEffect extends OneShotEffect{ - - private FilterPermanent filter; - private DynamicValue count; - - public SacrificeControllerEffect ( FilterPermanent filter, DynamicValue count, String preText ) { - super(Outcome.Sacrifice); - this.filter = filter; - this.count = count; - staticText = "sacrifice a land"; - } - - public SacrificeControllerEffect ( FilterPermanent filter, int count, String preText ) { - this(filter, new StaticValue(count), preText); - } - - public SacrificeControllerEffect ( SacrificeControllerEffect effect ) { - super(effect); - this.filter = effect.filter; - this.count = effect.count; - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - - if (player == null) { - return false; - } - - filter.add(new ControllerPredicate(TargetController.YOU)); - - int amount = count.calculate(game, source, this); - int realCount = game.getBattlefield().countAll(filter, player.getId(), game); - amount = Math.min(amount, realCount); - - Target target = new TargetControlledPermanent(amount, amount, filter, false); - - //A spell or ability could have removed the only legal target this player - //had, if thats the case this ability should fizzle. - if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { - boolean abilityApplied = false; - while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); - } - - for ( int idx = 0; idx < target.getTargets().size(); idx++) { - Permanent permanent = game.getPermanent((UUID)target.getTargets().get(idx)); - - if ( permanent != null ) { - abilityApplied |= permanent.sacrifice(source.getSourceId(), game); - } - } - - return abilityApplied; - } - return false; - } - - public void setAmount(DynamicValue amount) { - this.count = amount; - } - - @Override - public SacrificeControllerEffect copy() { - return new SacrificeControllerEffect(this); - } - -} - - -class CantAttackEffect extends ReplacementEffectImpl { - - public CantAttackEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Creatures you control can't attack"; - } - - public CantAttackEffect(final CantAttackEffect effect) { - super(effect); - } - - @Override - public CantAttackEffect copy() { - return new CantAttackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DECLARE_ATTACKER && source.getControllerId().equals(event.getPlayerId())) { - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java b/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java index 4b8a91f4066..90b7cc12eea 100644 --- a/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java +++ b/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java @@ -49,6 +49,7 @@ import mage.target.common.TargetControlledPermanent; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.filter.common.FilterControlledCreaturePermanent; /** * @author nantuko @@ -59,8 +60,6 @@ public class DivineReckoning extends CardImpl { super(ownerId, 10, "Divine Reckoning", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); this.expansionSetCode = "ISD"; - this.color.setWhite(true); - // Each player chooses a creature he or she controls. Destroy the rest. this.getSpellAbility().addEffect(new DivineReckoningEffect()); @@ -79,13 +78,7 @@ public class DivineReckoning extends CardImpl { } class DivineReckoningEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); - - static { - filter.add(new ControllerPredicate(TargetController.YOU)); - } - + public DivineReckoningEffect() { super(Outcome.DestroyPermanent); staticText = "Each player chooses a creature he or she controls. Destroy the rest"; @@ -97,16 +90,16 @@ class DivineReckoningEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List chosen = new ArrayList(); + List chosen = new ArrayList<>(); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); - Target target = new TargetControlledPermanent(1, 1, filter, false); + Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), false); if (target.canChoose(player.getId(), game)) { while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target, source, game); } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { diff --git a/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java b/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java index ed4c7e2360b..d71b89f3d53 100644 --- a/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java +++ b/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java @@ -42,11 +42,8 @@ import mage.cards.CardImpl; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.*; -import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.WolfTokenWithDeathtouch; @@ -56,6 +53,7 @@ import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; import java.util.UUID; +import mage.filter.common.FilterControlledCreaturePermanent; /** * @author nantuko @@ -132,12 +130,7 @@ class GarrukTheVeilCursedValue implements DynamicValue { class GarrukTheVeilCursedEffect extends OneShotEffect { - private static final FilterPermanent filterCreature = new FilterPermanent("a creature you control"); - - static { - filterCreature.add(new CardTypePredicate(CardType.CREATURE)); - filterCreature.add(new ControllerPredicate(TargetController.YOU)); - } + private static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("a creature you control"); public GarrukTheVeilCursedEffect() { super(Outcome.Benefit); @@ -150,22 +143,22 @@ class GarrukTheVeilCursedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); - if (player == null) { + if (controller == null) { return false; } // sacrifice a creature Target target = new TargetControlledPermanent(1, 1, filterCreature, false); boolean sacrificed = false; - if (target.canChoose(player.getId(), game)) { - while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + if (target.canChoose(controller.getId(), game)) { + while (controller.isInGame() && !target.isChosen() && target.canChoose(controller.getId(), game)) { + controller.chooseTarget(Outcome.Sacrifice, target, source, game); } for (int idx = 0; idx < target.getTargets().size(); idx++) { - Permanent permanent = game.getPermanent((UUID) target.getTargets().get(idx)); + Permanent permanent = game.getPermanent(target.getTargets().get(idx)); if (permanent != null) { sacrificed |= permanent.sacrifice(source.getSourceId(), game); } @@ -177,9 +170,9 @@ class GarrukTheVeilCursedEffect extends OneShotEffect { FilterCreatureCard filter = new FilterCreatureCard(); TargetCardInLibrary targetInLibrary = new TargetCardInLibrary(filter); Cards cards = new CardsImpl(); - if (player.searchLibrary(targetInLibrary, game)) { + if (controller.searchLibrary(targetInLibrary, game)) { for (UUID cardId : targetInLibrary.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); + Card card = controller.getLibrary().remove(cardId, game); if (card != null) { card.moveToZone(Zone.HAND, source.getSourceId(), game, false); cards.add(card); @@ -188,10 +181,10 @@ class GarrukTheVeilCursedEffect extends OneShotEffect { } // reveal if (cards.size() > 0) { - player.revealCards("Garruk, the Veil-Cursed", cards, game); + controller.revealCards("Garruk, the Veil-Cursed", cards, game); } // shuffle - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java b/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java index 3f4e1e169b5..4bb0680c422 100644 --- a/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java +++ b/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java @@ -36,7 +36,7 @@ import mage.abilities.keyword.MorphAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; @@ -46,7 +46,7 @@ import mage.target.common.TargetControlledPermanent; */ public class FathomSeer extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Islands"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Islands"); static { filter.add(new SubtypePredicate("Island")); diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java b/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java index 7c59715dd64..42ae20754d5 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java @@ -34,8 +34,8 @@ import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; @@ -45,7 +45,7 @@ import mage.target.common.TargetControlledPermanent; */ public class Pulverize extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("two Mountains"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("two Mountains"); static { filter.add(new SubtypePredicate("Mountain")); } diff --git a/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java b/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java index d9a583d09a3..a4991f9c39a 100644 --- a/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java +++ b/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java @@ -41,7 +41,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.Target; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetLandPermanent; @@ -57,7 +59,6 @@ public class Dreamwinder extends CardImpl { this.expansionSetCode = "ODY"; this.subtype.add("Serpent"); - this.color.setBlue(true); this.power = new MageInt(4); this.toughness = new MageInt(3); @@ -67,7 +68,9 @@ public class Dreamwinder extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesBasicLandTargetEffect(Duration.EndOfTurn, "Island"), new ManaCostsImpl("{U}")); Target target = new TargetLandPermanent(); ability.addTarget(target); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, new FilterLandPermanent("Island","an Island"), true))); + FilterControlledLandPermanent filter = new FilterControlledLandPermanent("an Island"); + filter.add(new SubtypePredicate("Island")); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, true))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java b/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java index 4b2a5e54b6b..ea7e7998ce4 100644 --- a/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java +++ b/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java @@ -34,7 +34,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -85,11 +85,11 @@ class ReadTheRunesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int drawnCards = player.drawCards(source.getManaCostsToPay().getX(), game); - Target target = new TargetControlledPermanent(0, drawnCards, new FilterPermanent(), true); - target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int drawnCards = controller.drawCards(source.getManaCostsToPay().getX(), game); + Target target = new TargetControlledPermanent(0, drawnCards, new FilterControlledPermanent(), true); + controller.chooseTarget(Outcome.Sacrifice, target, source, game); int sacrificedPermanents = 0; for (UUID permanentId : target.getTargets()) { Permanent permanent = game.getPermanent(permanentId); @@ -99,7 +99,7 @@ class ReadTheRunesEffect extends OneShotEffect { } } } - player.discard(drawnCards - sacrificedPermanents, source, game); + controller.discard(drawnCards - sacrificedPermanents, false, source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java b/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java index 5d03d7eeba2..255b4d31e4c 100644 --- a/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java +++ b/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java @@ -38,8 +38,7 @@ import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -97,10 +96,7 @@ class ReturnToHandEffect extends OneShotEffect { return false; } - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerPredicate(TargetController.YOU)); - Target target = new TargetControlledPermanent(1, 1, filter, true); - + Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); if (target.canChoose(player.getId(), game)) { while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { player.chooseTarget(Outcome.ReturnToHand, target, source, game); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java index 2d784a574d5..b28bd2b7923 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java @@ -36,6 +36,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -54,8 +55,6 @@ public class ChoiceOfDamnations extends CardImpl { this.expansionSetCode = "SOK"; this.subtype.add("Arcane"); - this.color.setBlack(true); - // Target opponent chooses a number. You may have that player lose that much life. If you don't, that player sacrifices all but that many permanents. this.getSpellAbility().addEffect(new ChoiceOfDamnationsEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); @@ -94,14 +93,14 @@ class ChoiceOfDamnationsEffect extends OneShotEffect { int amount = targetPlayer.getAmount(0, Integer.MAX_VALUE, "Chooses a number", game); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - StringBuilder sb = new StringBuilder("Shall ").append(targetPlayer.getName()).append(" lose ").append(amount).append(" life?"); - if (controller.chooseUse(outcome, sb.toString(), game)) { + String sb = String.valueOf("Shall " + targetPlayer.getName() + " lose ") + Integer.toString(amount) + " life?"; + if (controller.chooseUse(outcome, sb, game)) { targetPlayer.loseLife(amount, game); } else { int numberPermanents = game.getState().getBattlefield().countAll(new FilterPermanent(), targetPlayer.getId(), game); if (numberPermanents > amount) { int numberToSacrifice = numberPermanents - amount; - Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterPermanent(), false); + Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterControlledPermanent(), false); targetPlayer.chooseTarget(Outcome.Sacrifice, target, source, game); for (UUID uuid : target.getTargets()) { Permanent permanent = game.getPermanent(uuid); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java index 9e8149a88c9..052bc47d1aa 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java @@ -38,7 +38,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; import mage.filter.common.FilterArtifactCard; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; @@ -49,7 +49,7 @@ import mage.target.common.TargetControlledPermanent; public class KuldothaForgemaster extends CardImpl { private static final FilterArtifactCard filterArtifactCard = new FilterArtifactCard(); - private static final FilterArtifactPermanent filterArtifactPermanent = new FilterArtifactPermanent(); + private static final FilterControlledArtifactPermanent filterArtifactPermanent = new FilterControlledArtifactPermanent(); public KuldothaForgemaster(UUID ownerId) { super(ownerId, 169, "Kuldotha Forgemaster", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); diff --git a/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java b/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java index b00e0b4742b..2ae80e616c6 100644 --- a/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java +++ b/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java @@ -36,7 +36,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.game.permanent.token.Token; import mage.target.common.TargetControlledPermanent; @@ -59,7 +59,7 @@ public class AnthousaSetessanHero extends CardImpl { // Heroic - Whenever you cast a spell that targets Anthousa, Setessan Hero, up to three target lands you control each become 2/2 Warrior creatures until end of turn. They're still lands. Ability ability = new HeroicAbility(new BecomesCreatureTargetEffect(new AnthousaWarriorToken(), false, true, Duration.EndOfTurn)); - ability.addTarget(new TargetControlledPermanent(0,3,new FilterLandPermanent("lands"), false)); + ability.addTarget(new TargetControlledPermanent(0,3,new FilterControlledLandPermanent("lands"), false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java b/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java index c91545836ca..b9fbde54213 100644 --- a/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java +++ b/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java @@ -36,7 +36,7 @@ import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.BuybackAbility; import mage.cards.CardImpl; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.game.turn.TurnMod; @@ -49,7 +49,7 @@ import mage.target.common.TargetControlledPermanent; */ public class WalkTheAeons extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("three Islands"); + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("three Islands"); static { filter.add(new SubtypePredicate("Island")); diff --git a/Mage.Sets/src/mage/sets/urzassaga/Raze.java b/Mage.Sets/src/mage/sets/urzassaga/Raze.java index b3bb48bf80b..8b45e3946c2 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Raze.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Raze.java @@ -33,7 +33,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetLandPermanent; @@ -50,7 +50,7 @@ public class Raze extends CardImpl { this.color.setRed(true); // As an additional cost to cast Raze, sacrifice a land. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1, new FilterLandPermanent(), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1, new FilterControlledLandPermanent(), true))); // Destroy target land. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetLandPermanent()); diff --git a/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java b/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java index 3e2dce1821c..8e44b78f785 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java @@ -39,7 +39,10 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.common.TargetControlledPermanent; /** @@ -65,7 +68,9 @@ public class BragoKingEternal extends CardImpl { Effect effect = new ExileTargetEffect(this.getId(), this.getName(), Zone.BATTLEFIELD); effect.setText("exile any number of target nonland permanents you control"); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false); - ability.addTarget(new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterNonlandPermanent(), false)); + FilterControlledPermanent filterControlledNonlandPermanent = new FilterControlledPermanent(); + filterControlledNonlandPermanent.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + ability.addTarget(new TargetControlledPermanent(0, Integer.MAX_VALUE, filterControlledNonlandPermanent, false)); ability.addEffect(new ReturnFromExileEffect(this.getId(), Zone.BATTLEFIELD, ", then return those cards to the battlefield under their owner's control")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/visions/Fireblast.java b/Mage.Sets/src/mage/sets/visions/Fireblast.java index d53130cd225..131ddc9c722 100644 --- a/Mage.Sets/src/mage/sets/visions/Fireblast.java +++ b/Mage.Sets/src/mage/sets/visions/Fireblast.java @@ -30,15 +30,12 @@ package mage.sets.visions; import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.TargetController; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreatureOrPlayer; @@ -49,7 +46,7 @@ import mage.target.common.TargetCreatureOrPlayer; */ public class Fireblast extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Mountain"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Mountain"); static { filter.add(new SubtypePredicate("Mountain")); diff --git a/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java b/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java index 20f5f4debf2..a7952dad323 100644 --- a/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java +++ b/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java @@ -143,7 +143,7 @@ class WorldQuellerEffect extends OneShotEffect { type = CardType.TRIBAL; } if (type != null) { - FilterPermanent filter = new FilterControlledPermanent(new StringBuilder("permanent you control of type ").append(type.toString()).toString()); + FilterControlledPermanent filter = new FilterControlledPermanent(new StringBuilder("permanent you control of type ").append(type.toString()).toString()); filter.add(new CardTypePredicate(type)); TargetPermanent target = new TargetControlledPermanent(1, 1, filter, false); @@ -168,7 +168,7 @@ class WorldQuellerEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (target.canChoose(playerId, game)) { while (!target.isChosen() && target.canChoose(playerId, game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { diff --git a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java index 3c4fcd49f0d..cf601253514 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java @@ -39,9 +39,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetControlledPermanent; - -import java.util.UUID; +import mage.target.TargetPermanent; import mage.util.CardUtil; /** @@ -87,14 +85,14 @@ public class SacrificeEffect extends OneShotEffect{ int realCount = game.getBattlefield().countAll(filter, player.getId(), game); amount = Math.min(amount, realCount); - Target target = new TargetControlledPermanent(amount, amount, filter, true); + Target target = new TargetPermanent(amount, amount, filter, true); // A spell or ability could have removed the only legal target this player // had, if thats the case this ability should fizzle. if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { boolean abilityApplied = false; while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } for ( int idx = 0; idx < target.getTargets().size(); idx++) { diff --git a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java index a99816c682b..2320b261986 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -36,11 +36,13 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetControlledPermanent; +import mage.target.TargetPermanent; import mage.util.CardUtil; /** @@ -81,15 +83,17 @@ public class SacrificeOpponentsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List perms = new ArrayList(); + List perms = new ArrayList<>(); + filter.add(new ControllerPredicate(TargetController.YOU)); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); if (player != null) { + int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game)); - TargetControlledPermanent target = new TargetControlledPermanent(numTargets, numTargets, filter, false); + TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, false); if (target.canChoose(player.getId(), game)) { while (!target.isChosen() && player.isInGame()) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } perms.addAll(target.getTargets()); } diff --git a/Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java b/Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java new file mode 100644 index 00000000000..9853e6e8664 --- /dev/null +++ b/Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java @@ -0,0 +1,58 @@ +/* + * 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.filter.common; + +import mage.constants.CardType; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author LevelX2 + */ + +public class FilterControlledPlaneswalkerPermanent extends FilterControlledPermanent { + + public FilterControlledPlaneswalkerPermanent() { + this("planeswalker you control"); + } + + public FilterControlledPlaneswalkerPermanent(String name) { + super(name); + this.add(new CardTypePredicate(CardType.PLANESWALKER)); + } + + public FilterControlledPlaneswalkerPermanent(final FilterControlledPlaneswalkerPermanent filter) { + super(filter); + } + + @Override + public FilterControlledPlaneswalkerPermanent copy() { + return new FilterControlledPlaneswalkerPermanent(this); + } + +} diff --git a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java index 652fc368097..886db298e67 100644 --- a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java +++ b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java @@ -52,14 +52,14 @@ public class FilterPlaneswalkerOrPlayer extends FilterImpl { public FilterPlaneswalkerOrPlayer(Set defenders) { super("planeswalker or player"); - ArrayList> permanentPredicates = new ArrayList>(); + ArrayList> permanentPredicates = new ArrayList<>(); for (UUID defenderId : defenders) { permanentPredicates.add(new ControllerIdPredicate(defenderId)); } planeswalkerFilter = new FilterPlaneswalkerPermanent(); planeswalkerFilter.add(Predicates.or(permanentPredicates)); - ArrayList> playerPredicates = new ArrayList>(); + ArrayList> playerPredicates = new ArrayList<>(); for (UUID defenderId : defenders) { playerPredicates.add(new PlayerIdPredicate(defenderId)); } diff --git a/Mage/src/mage/target/common/TargetControlledPermanent.java b/Mage/src/mage/target/common/TargetControlledPermanent.java index e1eaccfa2da..24f0ddd7253 100644 --- a/Mage/src/mage/target/common/TargetControlledPermanent.java +++ b/Mage/src/mage/target/common/TargetControlledPermanent.java @@ -50,7 +50,7 @@ public class TargetControlledPermanent extends TargetPermanent { this(1, 1, filter, false); } - public TargetControlledPermanent(int minNumTargets, int maxNumTargets, FilterPermanent filter, boolean notTarget) { + public TargetControlledPermanent(int minNumTargets, int maxNumTargets, FilterControlledPermanent filter, boolean notTarget) { super(minNumTargets, maxNumTargets, filter, notTarget); this.targetName = filter.getMessage(); } From fd7f1362aeda2c0693db0a1efc55d12fe3e05922 Mon Sep 17 00:00:00 2001 From: myersn024 Date: Thu, 26 Feb 2015 11:17:38 -0600 Subject: [PATCH 22/61] Update DwarvenMiner.java --- Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java b/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java index 7f5d0171775..a62127cea2a 100644 --- a/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java +++ b/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java @@ -30,15 +30,13 @@ public class DwarvenMiner extends CardImpl { this.expansionSetCode = "MIR"; this.subtype.add("Dwarf"); - this.color.setRed(true); this.power = new MageInt(1); this.toughness = new MageInt(2); // {2}{R}, {tap}: Destroy target nonbasic land Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{R}")); ability.addCost(new TapSourceCost()); - Target target = new TargetNonBasicLandPermanent(); - ability.addTarget(target); + ability.addTarget(new TargetNonBasicLandPermanent()); this.addAbility(ability); } From debe2316b2d64f9db6c9cf3113d7d2afb32fad07 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 26 Feb 2015 20:38:48 +0100 Subject: [PATCH 23/61] * Echo - Fixed that the sacrifice effect also forced the player to sacrifice a new instance of the permanent with echo. --- .../cards/abilities/keywords/EchoTest.java | 85 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 2 +- .../mage/abilities/keyword/EchoAbility.java | 24 +++--- 3 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java new file mode 100644 index 00000000000..7188e0a989e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java @@ -0,0 +1,85 @@ +/* + * 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.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class EchoTest extends CardTestPlayerBase { + + /* + * I flickered an Avalanche Riders with its Echo trigger on the stack with Restoration Angel. + * When the trigger resolved, my Riders was sacrificed, even though it should have been + * considered a new permanent. + */ + + @Test + public void testEchoTriggerChecksIdentity() { + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Avalanche Riders Creature - Human Nomad 2/2 + // Haste + // Echo (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.) + // When Avalanche Riders enters the battlefield, destroy target land. + addCard(Zone.HAND, playerA, "Avalanche Riders"); + + // Restoration Angel {3}{W} + // Flash + // Flying + // When Restoration Angel enters the battlefield, you may exile target non-Angel creature you control, + // then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Restoration Angel"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Avalanche Riders"); + + castSpell(3, PhaseStep.UPKEEP, playerA, "Restoration Angel", null, "Echo {3}{R} (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)"); + addTarget(playerA, "Avalanche Riders"); + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Avalanche Riders", 1); + assertPermanentCount(playerA, "Restoration Angel", 1); + + assertPermanentCount(playerB, "Mountain", 0); + } + + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index d69b3f862ce..f336540ccf7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -531,7 +531,7 @@ public class TestPlayer extends ComputerPlayer { boolean result = true; for (int i = 1; i < groups.length; i++) { String group = groups[i]; - if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack")) { + if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") ) { break; } if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { diff --git a/Mage/src/mage/abilities/keyword/EchoAbility.java b/Mage/src/mage/abilities/keyword/EchoAbility.java index 7aef7242fe4..94215e8ae32 100644 --- a/Mage/src/mage/abilities/keyword/EchoAbility.java +++ b/Mage/src/mage/abilities/keyword/EchoAbility.java @@ -29,6 +29,7 @@ package mage.abilities.keyword; import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; @@ -40,7 +41,6 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -120,7 +120,7 @@ public class EchoAbility extends TriggeredAbilityImpl { if (manaEcho) { sb.append(" "); } else { - sb.append("-"); + sb.append("—"); } sb.append(echoCosts.getText()); sb.append(" (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)"); @@ -143,16 +143,16 @@ class EchoEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { - if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { - return true; - } - } - permanent.sacrifice(source.getSourceId(), game); + Player controller = game.getPlayer(source.getControllerId()); + MageObjectReference mor = new MageObjectReference(source.getSourceId(), game); + if (controller != null && mor.refersTo(source.getSourceObject(game))) { + if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", game)) { + cost.clearPaid(); + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { + return true; + } + } + mor.getPermanent(game).sacrifice(source.getSourceId(), game); return true; } return false; From b4966f5e68c40c242259381ecc534b0fcd406d7a Mon Sep 17 00:00:00 2001 From: myersn024 Date: Thu, 26 Feb 2015 14:25:11 -0600 Subject: [PATCH 24/61] Implemented AuraThief.java --- .../src/mage/sets/urzasdestiny/AuraThief.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java new file mode 100644 index 00000000000..556fb43250a --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java @@ -0,0 +1,85 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.sets.urzasdestiny; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continious.GainControlTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author nick.myers + */ + +public class AuraThief extends CardImpl { + + public AuraThief(UUID ownerId) { + super(ownerId, 26, "Aura Thief", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}"); + this.expansionSetCode = "UDS"; + this.subtype.add("Illusion"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Aura Thief dies, you gain control of all enchantments. You don't get + // to move Auras. + this.addAbility(new DiesTriggeredAbility(new AuraThiefDiesTriggeredEffect())); + } + + public AuraThief(final AuraThief card) { + super(card); + } + + @Override + public AuraThief copy() { + return new AuraThief(this); + } + +} + +class AuraThiefDiesTriggeredEffect extends OneShotEffect { + + public AuraThiefDiesTriggeredEffect() { + super(Outcome.Benefit); + this.staticText = "gain control of all enchantments"; + } + + public AuraThiefDiesTriggeredEffect(final AuraThiefDiesTriggeredEffect effect) { + super(effect); + } + + @Override + public AuraThiefDiesTriggeredEffect copy() { + return new AuraThiefDiesTriggeredEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean ret = false; + ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); + for(Permanent enchantment : game.getBattlefield().getAllActivePermanents(CardType.ENCHANTMENT)) { + gainControl.setTargetPointer(new FixedTarget(enchantment.getId())); + game.addEffect(gainControl, source); + ret = true; + } + return ret; + } +} From 86393db911751646c83358862d9cdd98a1bd51bf Mon Sep 17 00:00:00 2001 From: fireshoes Date: Thu, 26 Feb 2015 15:46:36 -0600 Subject: [PATCH 25/61] Added Decree of Annihilation, Pit Keeper, and Songs of the Damned. --- .../mage/sets/iceage/SongsOfTheDamned.java | 62 +++++++++ .../sets/scourge/DecreeOfAnnihilation.java | 130 ++++++++++++++++++ .../src/mage/sets/timespiral/PitKeeper.java | 95 +++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.java create mode 100644 Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java create mode 100644 Mage.Sets/src/mage/sets/timespiral/PitKeeper.java diff --git a/Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.java b/Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.java new file mode 100644 index 00000000000..5732302dad6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.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 mage.sets.iceage; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DynamicManaEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreatureCard; + +/** + * + * @author fireshoes + */ +public class SongsOfTheDamned extends CardImpl { + + public SongsOfTheDamned(UUID ownerId) { + super(ownerId, 48, "Songs of the Damned", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}"); + this.expansionSetCode = "ICE"; + + // Add {B} to your mana pool for each creature card in your graveyard. + DynamicManaEffect effect = new DynamicManaEffect(Mana.BlackMana, new CardsInControllerGraveyardCount(new FilterCreatureCard())); + this.getSpellAbility().addEffect(effect); + } + + public SongsOfTheDamned(final SongsOfTheDamned card) { + super(card); + } + + @Override + public SongsOfTheDamned copy() { + return new SongsOfTheDamned(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java b/Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java new file mode 100644 index 00000000000..8f1efa909a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java @@ -0,0 +1,130 @@ + /* + * 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.scourge; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class DecreeOfAnnihilation extends CardImpl { + + public DecreeOfAnnihilation(UUID ownerId) { + super(ownerId, 85, "Decree of Annihilation", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{8}{R}{R}"); + this.expansionSetCode = "SCG"; + + // Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands. + this.getSpellAbility().addEffect(new DecreeOfAnnihilationEffect()); + + // Cycling {5}{R}{R} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}"))); + + // When you cycle Decree of Annihilation, destroy all lands. + Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(new FilterLandPermanent("lands")), false); + this.addAbility(ability); + } + + public DecreeOfAnnihilation(final DecreeOfAnnihilation card) { + super(card); + } + + @Override + public DecreeOfAnnihilation copy() { + return new DecreeOfAnnihilation(this); + } +} + +class DecreeOfAnnihilationEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(""); + + static { + filter.add(Predicates.or( + new CardTypePredicate(CardType.ARTIFACT), + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.LAND))); + } + + public DecreeOfAnnihilationEffect() { + super(Outcome.Detriment); + staticText = "Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands"; + } + + public DecreeOfAnnihilationEffect(final DecreeOfAnnihilationEffect effect) { + super(effect); + } + + @Override + public DecreeOfAnnihilationEffect copy() { + return new DecreeOfAnnihilationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + permanent.moveToExile(id, "all artifacts, creatures, and land", id, game); + } + for (UUID playerId : game.getPlayerList()) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (UUID cid : player.getHand().copy()) { + Card c = game.getCard(cid); + if (c != null) { + c.moveToExile(null, null, source.getSourceId(), game); + } + } + for (UUID cid : player.getGraveyard().copy()) { + Card c = game.getCard(cid); + if (c != null) { + c.moveToExile(null, null, source.getSourceId(), game); + } + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/timespiral/PitKeeper.java b/Mage.Sets/src/mage/sets/timespiral/PitKeeper.java new file mode 100644 index 00000000000..a6e1d955a87 --- /dev/null +++ b/Mage.Sets/src/mage/sets/timespiral/PitKeeper.java @@ -0,0 +1,95 @@ +/* + * 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.timespiral; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class PitKeeper extends CardImpl { + + public PitKeeper(UUID ownerId) { + super(ownerId, 123, "Pit Keeper", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); + this.expansionSetCode = "TSP"; + this.subtype.add("Human"); + this.subtype.add("Wizard"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Pit Keeper enters the battlefield, if you have four or more creature cards in your graveyard, you may return target creature card from your graveyard to your hand. + TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true); + triggeredAbility.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); + this.addAbility(new ConditionalTriggeredAbility( + triggeredAbility, + new CreatureCardsInControllerGraveCondition(4), + "When {this} enters the battlefield, if you have four or more creature cards in your graveyard, you may return target creature card from your graveyard to your hand.")); + } + + public PitKeeper(final PitKeeper card) { + super(card); + } + + @Override + public PitKeeper copy() { + return new PitKeeper(this); + } +} + +class CreatureCardsInControllerGraveCondition implements Condition { + private int value; + + public CreatureCardsInControllerGraveCondition(int value) { + this.value = value; + } + + @Override + public boolean apply(Game game, Ability source) { + Player p = game.getPlayer(source.getControllerId()); + if (p != null && p.getGraveyard().count(new FilterCreatureCard(), game) >= value) + { + return true; + } + return false; + } +} \ No newline at end of file From 7a0e16fb3308dcac9c1329a27bd8c368bb4e2c1e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 01:06:11 +0100 Subject: [PATCH 26/61] * Vengeful Pharaoh - Fixed that it triggered for every creature doing simultaneously combat damage. Fixed that it wrongly also worked, if Vengeful Pharaoh already left the graveyard. --- .../mage/sets/magic2012/VengefulPharaoh.java | 74 ++++++++++++++----- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java b/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java index 44bc3e9619a..10abb1aac2d 100644 --- a/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java +++ b/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java @@ -28,22 +28,24 @@ package mage.sets.magic2012; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DeathtouchAbility; 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.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; +import mage.game.turn.Step; import mage.players.Player; import mage.target.common.TargetAttackingCreature; @@ -58,11 +60,13 @@ public class VengefulPharaoh extends CardImpl { this.expansionSetCode = "M12"; this.subtype.add("Zombie"); - this.color.setBlack(true); this.power = new MageInt(5); this.toughness = new MageInt(4); + // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.) this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever combat damage is dealt to you or a planeswalker you control, if Vengeful Pharaoh is in your graveyard, destroy target attacking creature, then put Vengeful Pharaoh on top of your library. this.addAbility(new VengefulPharaohTriggeredAbility()); } @@ -78,6 +82,12 @@ public class VengefulPharaoh extends CardImpl { class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { + Step stepTriggeredPlayer; + int turnTriggeredPlayer; + + Step stepTriggeredPlansewalker; + int turnTriggeredPlaneswalker; + public VengefulPharaohTriggeredAbility() { super(Zone.GRAVEYARD, new VengefulPharaohEffect(), false); this.addTarget(new TargetAttackingCreature()); @@ -85,6 +95,10 @@ class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { public VengefulPharaohTriggeredAbility(final VengefulPharaohTriggeredAbility ability) { super(ability); + this.stepTriggeredPlansewalker = ability.stepTriggeredPlansewalker; + this.turnTriggeredPlaneswalker = ability.turnTriggeredPlaneswalker; + this.stepTriggeredPlayer = ability.stepTriggeredPlayer; + this.turnTriggeredPlayer = ability.turnTriggeredPlayer; } @Override @@ -92,16 +106,41 @@ class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { return new VengefulPharaohTriggeredAbility(this); } + @Override + public boolean checkInterveningIfClause(Game game) { + // 9/22/2011 - If multiple creatures deal combat damage to you and to a planeswalker you control + // simultaneously, Vengeful Pharaoh will trigger twice. The first trigger will cause Vengeful Pharaoh + // to be put on top of your library. The second trigger will then do nothing, as Vengeful Pharaoh is + // no longer in your graveyard when it tries to resolve. Note that the second trigger will do nothing + // even if Vengeful Pharaoh is put back into your graveyard before it tries to resolve, as it's a + // different Vengeful Pharaoh than the one that was there before. + MageObjectReference mor = new MageObjectReference(getSourceId(), game); + return mor.refersTo(this.getSourceObject(game)); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER || event.getType() == EventType.DAMAGED_PLANESWALKER; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if ((event.getType() == EventType.DAMAGED_PLAYER && event.getTargetId().equals(this.getControllerId())) && ((DamagedEvent) event).isCombatDamage()) { - return true; + if (!game.getPhase().getStep().equals(stepTriggeredPlayer) || game.getTurnNum() != turnTriggeredPlayer) { + stepTriggeredPlayer = game.getPhase().getStep(); + turnTriggeredPlayer = game.getTurnNum(); + return true; + } } if (event.getType() == EventType.DAMAGED_PLANESWALKER && ((DamagedEvent) event).isCombatDamage()) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null && permanent.getControllerId().equals(this.getControllerId())) { - return true; + if (!game.getPhase().getStep().equals(stepTriggeredPlansewalker) || game.getTurnNum() != turnTriggeredPlaneswalker) { + stepTriggeredPlansewalker = game.getPhase().getStep(); + turnTriggeredPlaneswalker = game.getTurnNum(); + return true; + } } } return false; @@ -131,19 +170,16 @@ class VengefulPharaohEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean applied = false; - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.destroy(source.getSourceId(), game, false); - applied = true; - } - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); - if (card != null && player != null) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - applied = true; + if (card != null && controller != null) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + permanent.destroy(source.getSourceId(), game, false); + } + controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.GRAVEYARD, true, true); + return true; } - return applied; + return false; } } From a7db298711940fe4bf8f2bffa67883e3694dfc23 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 01:06:59 +0100 Subject: [PATCH 27/61] Some minor changes. --- Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java | 2 +- Mage/src/mage/watchers/common/MiracleWatcher.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java b/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java index ac58ae83140..72a68106def 100644 --- a/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java +++ b/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java @@ -68,7 +68,7 @@ public class PhantomNantuko extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Phantom Nantuko enters the battlefield with two +1/+1 counters on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2), true))); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2), true), "with two +1/+1 counters on it")); // If damage would be dealt to Phantom Nantuko, prevent that damage. Remove a +1/+1 counter from Phantom Nantuko. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomNantukoPreventionEffect())); // {tap}: Put a +1/+1 counter on Phantom Nantuko. diff --git a/Mage/src/mage/watchers/common/MiracleWatcher.java b/Mage/src/mage/watchers/common/MiracleWatcher.java index 7c184049941..c2fd6a518e9 100644 --- a/Mage/src/mage/watchers/common/MiracleWatcher.java +++ b/Mage/src/mage/watchers/common/MiracleWatcher.java @@ -99,7 +99,7 @@ public class MiracleWatcher extends Watcher { Cards cards = new CardsImpl(); cards.add(card); controller.lookAtCards("Miracle", cards, game); - if (controller.chooseUse(Outcome.Benefit, "Reveal card to be able to use Miracle?", game)) { + if (controller.chooseUse(Outcome.Benefit, "Reveal " + card.getName() + " to be able to use Miracle?", game)) { controller.revealCards("Miracle", cards, game); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.MIRACLE_CARD_REVEALED, card.getId(), card.getId(),controller.getId())); break; From 4c2570574a4a8c2aec5426038ac823cda0a59965 Mon Sep 17 00:00:00 2001 From: nickmyers Date: Thu, 26 Feb 2015 18:07:15 -0600 Subject: [PATCH 28/61] Edited AuraThief.java based on suggestions from LevelX2 --- Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java index 556fb43250a..380d1394100 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java @@ -18,6 +18,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.filter.common.FilterEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -59,7 +60,7 @@ class AuraThiefDiesTriggeredEffect extends OneShotEffect { public AuraThiefDiesTriggeredEffect() { super(Outcome.Benefit); - this.staticText = "gain control of all enchantments"; + this.staticText = "gain control of all enchantments. (You don't get to move Auras.)"; } public AuraThiefDiesTriggeredEffect(final AuraThiefDiesTriggeredEffect effect) { @@ -74,8 +75,9 @@ class AuraThiefDiesTriggeredEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { boolean ret = false; - ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); - for(Permanent enchantment : game.getBattlefield().getAllActivePermanents(CardType.ENCHANTMENT)) { + // ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); + for(Permanent enchantment : game.getBattlefield().getActivePermanents(new FilterEnchantmentPermanent(), source.getControllerId(), source.getControllerId(), game)) { + ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); gainControl.setTargetPointer(new FixedTarget(enchantment.getId())); game.addEffect(gainControl, source); ret = true; From 6f6b35d40f51e31412b997d60eb7176594009425 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 01:07:49 +0100 Subject: [PATCH 29/61] Some minor changes. --- .../java/mage/server/TableController.java | 4 +++- .../main/java/mage/server/TableManager.java | 24 ++++++------------- .../java/mage/server/game/GameWorker.java | 2 +- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index bec8fda788f..f2a236d52f7 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -924,7 +924,9 @@ public class TableController { } if (matchPlayer.getPlayer().isHuman()) { humanPlayers++; - if ((table.getState().equals(TableState.WAITING) || table.getState().equals(TableState.STARTING) || table.getState().equals(TableState.READY_TO_START)) || + if ((table.getState().equals(TableState.WAITING) || + table.getState().equals(TableState.STARTING) || + table.getState().equals(TableState.READY_TO_START)) || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { User user = UserManager.getInstance().getUser(userPlayerEntry.getKey()); diff --git a/Mage.Server/src/main/java/mage/server/TableManager.java b/Mage.Server/src/main/java/mage/server/TableManager.java index 1ef11e9f7cc..385634738f9 100644 --- a/Mage.Server/src/main/java/mage/server/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/TableManager.java @@ -395,8 +395,6 @@ public class TableManager { debugServerState(); } logger.debug("TABLE HEALTH CHECK"); - List toRemove = new ArrayList<>(); - ArrayList tableCopy = new ArrayList<>(); tableCopy.addAll(tables.values()); for (Table table : tableCopy) { @@ -406,13 +404,13 @@ public class TableManager { logger.debug(table.getId() + " [" + table.getName()+ "] " + formatter.format(table.getStartTime() == null ? table.getCreateTime() : table.getCreateTime()) +" (" + table.getState().toString() + ") " + (table.isTournament() ? "- Tournament":"")); TableController tableController = getController(table.getId()); if (tableController != null) { - if (table.isTournament()) { - if (!tableController.isTournamentStillValid()) { - toRemove.add(table.getId()); - } - } else { - if (!tableController.isMatchTableStillValid()) { - toRemove.add(table.getId()); + if ((table.isTournament() && !tableController.isTournamentStillValid()) || + (!table.isTournament() && !tableController.isMatchTableStillValid())) { + try { + logger.warn("Removing unhealthy tableId " + table.getId()); + removeTable(table.getId()); + } catch (Exception e) { + logger.error(e); } } } @@ -422,14 +420,6 @@ public class TableManager { logger.debug(Arrays.toString(ex.getStackTrace())); } } - for (UUID tableId : toRemove) { - try { - logger.warn("Removing unhealthy tableId " + tableId); - removeTable(tableId); - } catch (Exception e) { - logger.error(e); - } - } logger.debug("TABLE HEALTH CHECK - END"); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameWorker.java b/Mage.Server/src/main/java/mage/server/game/GameWorker.java index 16e785604f1..7a1ae9753c3 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameWorker.java +++ b/Mage.Server/src/main/java/mage/server/game/GameWorker.java @@ -62,7 +62,7 @@ public class GameWorker implements Callable { gameController.gameResult(game.getWinner()); game.cleanUp(); } catch (MageException ex) { - logger.fatal("GameWorker mage error [" + game.getId() + "]" +ex, ex); + logger.fatal("GameWorker mage error [" + game.getId() + "]" + ex, ex); ex.printStackTrace(); } catch (Exception e) { logger.fatal("GameWorker general exception [" + game.getId() + "]" + e.getMessage(), e); From fa596997699055d6edca47a05fa646093c30729f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 01:08:08 +0100 Subject: [PATCH 30/61] Made the storm counter more visible. --- .../main/java/mage/client/game/GamePanel.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 02c14fc8acb..88480f2d30e 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -27,6 +27,7 @@ */ package mage.client.game; +import com.sun.java.swing.plaf.windows.WindowsBorders; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -606,7 +607,14 @@ public final class GamePanel extends javax.swing.JPanel { else { this.txtStep.setText(""); } - this.txtPhasesBottomInfo.setText(" " + (game.getSpellsCastCurrentTurn() > 0 ? Integer.toString(game.getSpellsCastCurrentTurn()):"")); + if (game.getSpellsCastCurrentTurn() > 0) { + this.phasesBottomPanel.setVisible(true); + this.txtPhasesBottomInfo.setText(Integer.toString(game.getSpellsCastCurrentTurn())); + } else { + this.phasesBottomPanel.setVisible(false); + this.txtPhasesBottomInfo.setText(""); + } + this.txtActivePlayer.setText(game.getActivePlayerName()); this.txtPriority.setText(game.getPriorityPlayerName()); this.txtTurn.setText(Integer.toString(game.getTurn())); @@ -1508,8 +1516,9 @@ public final class GamePanel extends javax.swing.JPanel { phasesContainer.add(empty1, ratio); phasesContainer.add(jPhases); - JPanel phasesBottomPanel = new JPanel(); - phasesBottomPanel.setBackground(new Color(0, 0, 0, 0)); + phasesBottomPanel = new JPanel(); + phasesBottomPanel.setBackground(Color.LIGHT_GRAY); + phasesBottomPanel.setBorder(new LineBorder(Color.DARK_GRAY, 2)); phasesBottomPanel.add(txtPhasesBottomInfo); phasesContainer.add(phasesBottomPanel); @@ -1862,6 +1871,7 @@ public final class GamePanel extends javax.swing.JPanel { private javax.swing.JSplitPane jSplitPane2; private JPanel jPhases; private JPanel phasesContainer; + private JPanel phasesBottomPanel; private javax.swing.JLabel txtPhasesBottomInfo; private HoverButton currentStep; From ae108ba3387dcde2c109e7a23b3d9a1c06f04c0d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 01:08:24 +0100 Subject: [PATCH 31/61] Minor change to POM. --- Mage/pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Mage/pom.xml b/Mage/pom.xml index 169b4be24cd..4ec038a5e17 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -42,10 +42,6 @@ org.apache.maven.plugins maven-compiler-plugin - - 1.7 - 1.7 - maven-resources-plugin From af697616eccbf850668706c8c15d742807862b1a Mon Sep 17 00:00:00 2001 From: nickmyers Date: Thu, 26 Feb 2015 18:09:12 -0600 Subject: [PATCH 32/61] Removed commented code from AuraThief.java --- Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java index 380d1394100..6eec8702b01 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java @@ -75,7 +75,6 @@ class AuraThiefDiesTriggeredEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { boolean ret = false; - // ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); for(Permanent enchantment : game.getBattlefield().getActivePermanents(new FilterEnchantmentPermanent(), source.getControllerId(), source.getControllerId(), game)) { ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); gainControl.setTargetPointer(new FixedTarget(enchantment.getId())); From 96f08cad9c651d4034dd9a19fc8517c6187cb9cb Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 27 Feb 2015 00:58:54 -0600 Subject: [PATCH 33/61] - Added Force of Nature and Fork. Dude doing a video tutorial of XMage requested them. --- .../sets/ajanivsnicolbolas/SapseepForest.java | 52 ++++++++ .../mage/sets/archenemy/TorrentOfSouls.java | 52 ++++++++ .../mage/sets/fifthedition/ForceOfNature.java | 52 ++++++++ .../sets/fourthedition/ForceOfNature.java | 108 ++++++++++++++++ .../mage/sets/limitedalpha/ForceOfNature.java | 52 ++++++++ .../src/mage/sets/limitedalpha/Fork.java | 52 ++++++++ .../mage/sets/limitedbeta/ForceOfNature.java | 52 ++++++++ Mage.Sets/src/mage/sets/limitedbeta/Fork.java | 117 ++++++++++++++++++ .../mage/sets/ninthedition/ForceOfNature.java | 52 ++++++++ .../sets/revisededition/ForceOfNature.java | 52 ++++++++ .../src/mage/sets/revisededition/Fork.java | 52 ++++++++ .../sets/unlimitededition/ForceOfNature.java | 52 ++++++++ .../src/mage/sets/unlimitededition/Fork.java | 52 ++++++++ 13 files changed, 797 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.java create mode 100644 Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.java create mode 100644 Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/limitedalpha/Fork.java create mode 100644 Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/limitedbeta/Fork.java create mode 100644 Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/revisededition/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/revisededition/Fork.java create mode 100644 Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.java create mode 100644 Mage.Sets/src/mage/sets/unlimitededition/Fork.java diff --git a/Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.java b/Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.java new file mode 100644 index 00000000000..88947e98cf5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.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.ajanivsnicolbolas; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class SapseepForest extends mage.sets.shadowmoor.SapseepForest { + + public SapseepForest(UUID ownerId) { + super(ownerId); + this.cardNumber = 36; + this.expansionSetCode = "DDH"; + } + + public SapseepForest(final SapseepForest card) { + super(card); + } + + @Override + public SapseepForest copy() { + return new SapseepForest(this); + } +} diff --git a/Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.java b/Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.java new file mode 100644 index 00000000000..186818a4775 --- /dev/null +++ b/Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.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.archenemy; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class TorrentOfSouls extends mage.sets.shadowmoor.TorrentOfSouls { + + public TorrentOfSouls(UUID ownerId) { + super(ownerId); + this.cardNumber = 96; + this.expansionSetCode = "ARC"; + } + + public TorrentOfSouls(final TorrentOfSouls card) { + super(card); + } + + @Override + public TorrentOfSouls copy() { + return new TorrentOfSouls(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.java b/Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.java new file mode 100644 index 00000000000..58dc7137e64 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.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 jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 156; + this.expansionSetCode = "5ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java b/Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java new file mode 100644 index 00000000000..e1e9cd6060c --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java @@ -0,0 +1,108 @@ +/* + * 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.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends CardImpl { + + public ForceOfNature(UUID ownerId) { + super(ownerId, 129, "Force of Nature", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{G}{G}{G}{G}"); + this.expansionSetCode = "4ED"; + this.subtype.add("Elemental"); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your upkeep, Force of Nature deals 8 damage to you unless you pay {G}{G}{G}{G}. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ForceOfNatureEffect(), TargetController.YOU, false)); + + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} + +class ForceOfNatureEffect extends OneShotEffect { + + public ForceOfNatureEffect() { + super(Outcome.Damage); + this.staticText = "{this} deals 8 damage to you unless you pay {G}{G}{G}{G}"; + } + + public ForceOfNatureEffect(final ForceOfNatureEffect effect) { + super(effect); + } + + @Override + public ForceOfNatureEffect copy() { + return new ForceOfNatureEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Cost cost = new ManaCostsImpl("{G}{G}{G}{G}"); + String message = "Would you like to pay {G}{G}{G}{G} to prevent taking 8 damage from {this}?"; + if (!(controller.chooseUse(Outcome.Benefit, message, game) + && cost.pay(source, game, source.getSourceId(), controller.getId(), false))) { + controller.damage(8, source.getSourceId(), game, false, true); + } + return true; + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.java b/Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.java new file mode 100644 index 00000000000..a94af82d885 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.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.limitedalpha; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "LEA"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Fork.java b/Mage.Sets/src/mage/sets/limitedalpha/Fork.java new file mode 100644 index 00000000000..a81c7fcfc90 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/Fork.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.limitedalpha; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends mage.sets.limitedbeta.Fork { + + public Fork(UUID ownerId) { + super(ownerId); + this.cardNumber = 153; + this.expansionSetCode = "LEA"; + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.java b/Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.java new file mode 100644 index 00000000000..f432c6f73ee --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.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.limitedbeta; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "LEB"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java new file mode 100644 index 00000000000..4db929c4711 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java @@ -0,0 +1,117 @@ +/* + * 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.limitedbeta; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetSpell; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("target instant or sorcery spell"); + + static { + filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + public Fork(UUID ownerId) { + super(ownerId, 154, "Fork", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{R}{R}"); + this.expansionSetCode = "LEB"; + + // Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy. + this.getSpellAbility().addEffect(new ForkEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} + +class ForkEffect extends OneShotEffect { + + public ForkEffect() { + super(Outcome.Copy); + staticText = "Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy"; + } + + public ForkEffect(final ForkEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + if (spell != null) { + Spell copy = spell.copySpell(); + copy.getColor().setRed(true); + copy.setControllerId(controller.getId()); + copy.setCopiedSpell(true); + game.getStack().push(copy); + copy.chooseNewTargets(game, controller.getId()); + return true; + } + return false; + } + + @Override + public ForkEffect copy() { + return new ForkEffect(this); + } + + @Override + public String getText(Mode mode) { + StringBuilder sb = new StringBuilder(); + sb.append("Copy target ").append(mode.getTargets().get(0).getTargetName()).append(", except that the copy is red. You may choose new targets for the copy"); + return sb.toString(); + } +} diff --git a/Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.java b/Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.java new file mode 100644 index 00000000000..c3c06e4d9b8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.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.ninthedition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 242; + this.expansionSetCode = "9ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/revisededition/ForceOfNature.java b/Mage.Sets/src/mage/sets/revisededition/ForceOfNature.java new file mode 100644 index 00000000000..92c3cfc57a2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/ForceOfNature.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.revisededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "3ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/revisededition/Fork.java b/Mage.Sets/src/mage/sets/revisededition/Fork.java new file mode 100644 index 00000000000..146227cf017 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/Fork.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.revisededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends mage.sets.limitedbeta.Fork { + + public Fork(UUID ownerId) { + super(ownerId); + this.cardNumber = 152; + this.expansionSetCode = "3ED"; + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.java b/Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.java new file mode 100644 index 00000000000..8090c9cdac1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.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.unlimitededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "2ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/Fork.java b/Mage.Sets/src/mage/sets/unlimitededition/Fork.java new file mode 100644 index 00000000000..2bf29d69bb8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/Fork.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.unlimitededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends mage.sets.limitedbeta.Fork { + + public Fork(UUID ownerId) { + super(ownerId); + this.cardNumber = 153; + this.expansionSetCode = "2ED"; + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} From 80d3e6bd9a4ffb6fcb090f03fc3059675535dc74 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 15:04:42 +0100 Subject: [PATCH 34/61] Fixed a bug of AI target selection that caused endless loops during build of test project. --- .../main/java/mage/player/ai/ComputerPlayer.java | 14 ++++++++++---- .../oneshot/sacrifice/MorticianBeetleTest.java | 2 +- .../abilities/effects/common/SacrificeEffect.java | 14 +++++++------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 83df4dce0ed..a05390f98a8 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -75,6 +75,8 @@ import java.io.IOException; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; +import mage.filter.Filter; +import mage.filter.predicate.other.PlayerIdPredicate; /** @@ -1947,10 +1949,14 @@ public class ComputerPlayer extends PlayerImpl implements Player { } protected List threats(UUID playerId, UUID sourceId, FilterPermanent filter, Game game, List targets) { - List threats = (playerId == null || sourceId ==null) ? - game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game) : // all permanents within the range of the player - game.getBattlefield().getActivePermanents(filter, playerId, sourceId, game); - + List threats; + if (playerId == null) { + threats = game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game); // all permanents within the range of the player + } else { + FilterPermanent filterCopy = filter.copy(); + filterCopy.add(new PlayerIdPredicate(playerId)); + threats = game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game); + } Iterator it = threats.iterator(); while (it.hasNext()) { // remove permanents already targeted Permanent test = it.next(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java index c6e1535f5d1..9d109b6a759 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java @@ -11,7 +11,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class MorticianBeetleTest extends CardTestPlayerBase { /** - * Checks that pro black can still be sacrificed + * Checks that creature with protection black can still be sacrificed */ @Test public void testSacrifice() { diff --git a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java index cf601253514..3b635c7b7fe 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java @@ -52,19 +52,19 @@ public class SacrificeEffect extends OneShotEffect{ private String preText; private DynamicValue count; - public SacrificeEffect ( FilterPermanent filter, DynamicValue count, String preText ) { + public SacrificeEffect (FilterPermanent filter, int count, String preText ) { + this(filter, new StaticValue(count), preText); + } + + public SacrificeEffect (FilterPermanent filter, DynamicValue count, String preText ) { super(Outcome.Sacrifice); this.filter = filter; this.count = count; this.preText = preText; setText(); } - - public SacrificeEffect ( FilterPermanent filter, int count, String preText ) { - this(filter, new StaticValue(count), preText); - } - - public SacrificeEffect ( final SacrificeEffect effect ) { + + public SacrificeEffect (final SacrificeEffect effect ) { super(effect); this.filter = effect.filter; this.count = effect.count; From 50261650b619e1c8e62d55dea6b8ce5169fa9faa Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 15:05:53 +0100 Subject: [PATCH 35/61] * Mana Reflection - Fixed that multiple Mana Reflections did not stack, fixed a bug of mana calculation (fixes #758). --- .../mage/sets/shadowmoor/ManaReflection.java | 35 ++++----- .../common/PermanentTappedForManaWatcher.java | 76 ------------------- 2 files changed, 14 insertions(+), 97 deletions(-) delete mode 100644 Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java b/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java index 52a3efc1f76..a45459bf236 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java @@ -58,7 +58,6 @@ public class ManaReflection extends CardImpl { // If you tap a permanent for mana, it produces twice as much of that mana instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaReflectionReplacementEffect())); - this.addWatcher(new PermanentTappedForManaWatcher()); } @@ -92,41 +91,35 @@ class ManaReflectionReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Mana mana = ((ManaEvent) event).getMana(); if (mana.getBlack() > 0) { - ((ManaEvent) event).getMana().set(ManaType.BLACK, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.BLACK, mana.getBlack()* 2); } if (mana.getBlue() > 0) { - ((ManaEvent) event).getMana().set(ManaType.BLUE, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.BLUE, mana.getBlue() * 2); } if (mana.getWhite() > 0) { - ((ManaEvent) event).getMana().set(ManaType.WHITE, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.WHITE, mana.getWhite() * 2); } if (mana.getGreen() > 0) { - ((ManaEvent) event).getMana().set(ManaType.GREEN, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.GREEN, mana.getGreen() * 2); } if (mana.getRed() > 0) { - ((ManaEvent) event).getMana().set(ManaType.RED, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.RED, mana.getRed() * 2); } if (mana.getColorless() > 0) { - ((ManaEvent) event).getMana().set(ManaType.COLORLESS, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.COLORLESS, mana.getColorless() * 2); } return false; } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.ADD_MANA - && event.getPlayerId().equals(source.getControllerId()) - && game.getPermanentOrLKIBattlefield(event.getSourceId()) != null) { - UUID permanentId = game.getPermanentOrLKIBattlefield(event.getSourceId()).getId(); - PermanentTappedForManaWatcher watcher = (PermanentTappedForManaWatcher) game.getState().getWatchers().get("PermanentTappedForMana"); - if (watcher != null) { - if (watcher.permanentId.contains(permanentId)) { - watcher.permanentId.remove(permanentId); - return true; - } - } - } - return false; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getControllerId()) + && game.getPermanentOrLKIBattlefield(event.getSourceId()) != null; } @Override diff --git a/Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java b/Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java deleted file mode 100644 index 24065772ada..00000000000 --- a/Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ - -package mage.watchers.common; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; - -/** - * @author jeffwadsworth - */ - -public class PermanentTappedForManaWatcher extends Watcher { - - public List permanentId = new ArrayList<>(); - - public PermanentTappedForManaWatcher() { - super("PermanentTappedForMana", WatcherScope.GAME); - } - - public PermanentTappedForManaWatcher(final PermanentTappedForManaWatcher watcher) { - super(watcher); - } - - @Override - public PermanentTappedForManaWatcher copy() { - return new PermanentTappedForManaWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null) { - permanentId.add(permanent.getId()); - } - } - } - - @Override - public void reset() { - super.reset(); - permanentId.clear(); - } -} From e075cb2eba0f0a2f6b0cd73b0099596277a66acd Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 15:07:11 +0100 Subject: [PATCH 36/61] * Mana Reflection - minor change. --- Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java b/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java index a45459bf236..ba1e87ed85f 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java @@ -42,7 +42,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ManaEvent; -import mage.watchers.common.PermanentTappedForManaWatcher; /** * From dd916a30e4f9ec48cc862d48d93908d07c379d2b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 15:18:19 +0100 Subject: [PATCH 37/61] * Added undying test. --- .../cards/abilities/keywords/UndyingTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index 2545cc81a7d..b62a2598f59 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -112,5 +112,39 @@ public class UndyingTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Strangleroot Geist", 3, 2); } + /** + * Tests "Target creature with Undying will be exiled by Anafenza before it returns to battlefield + * + * Anafenza the foremost doesn't exile an undying creature when dying at the same time as + * that undying one. The undying comes back to the field when he shouldn't. + */ + @Test + public void testReplacementEffectPreventsReturnOfUndying() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Butcher Ghoul + // Creature - Zombie, 1/1 {1}{B} + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Butcher Ghoul"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + // Anafenza, the Foremost + // Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control. + // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. + addCard(Zone.BATTLEFIELD, playerB, "Anafenza, the Foremost"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Butcher Ghoul"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Anafenza, the Foremost", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertPermanentCount(playerA, "Butcher Ghoul", 0); + assertExileCount("Butcher Ghoul", 1); + } + + } From 11fb2412cb7112ec16eeee43f3d9dd92b4359925 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 15:18:38 +0100 Subject: [PATCH 38/61] Fixed a bug of AI target selection that caused endless loops during build of test project. --- .../src/main/java/mage/player/ai/ComputerPlayer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index a05390f98a8..cb9a85dda21 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -77,6 +77,7 @@ import java.util.*; import java.util.Map.Entry; import mage.filter.Filter; import mage.filter.predicate.other.PlayerIdPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; /** @@ -1954,7 +1955,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { threats = game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game); // all permanents within the range of the player } else { FilterPermanent filterCopy = filter.copy(); - filterCopy.add(new PlayerIdPredicate(playerId)); + filterCopy.add(new ControllerIdPredicate(playerId)); threats = game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game); } Iterator it = threats.iterator(); From 1b45c997aa8b1f46b9f01a1275a440af84c69670 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 18:08:31 +0100 Subject: [PATCH 39/61] Some minor changes. --- Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java | 2 ++ Mage/src/mage/players/PlayerImpl.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java b/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java index 3efc367388b..8f6456f0068 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java @@ -28,6 +28,7 @@ package mage.sets.bornofthegods; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -162,6 +163,7 @@ class SearingBloodDelayedEffect extends OneShotEffect { if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { + MageObject sourceObject = source.getSourceObject(game); player.damage(3, source.getSourceId(), game, false, true); return true; } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 78103f71a5a..f5ce14b6e0f 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1628,6 +1628,9 @@ public abstract class PlayerImpl implements Player, Serializable { MageObject source = game.getPermanentOrLKIBattlefield(sourceId); if (source == null) { source = game.getObject(sourceId); + if (source instanceof Card && !CardUtil.isPermanentCard((Card)source)) { + source = game.getLastKnownInformation(sourceId, Zone.STACK); + } if (source instanceof Spell) { sourceControllerId = ((Spell) source).getControllerId(); } else { From 6bd17716cd23e0f19142fb59c9c1bc44d87441e3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 27 Feb 2015 18:09:49 +0100 Subject: [PATCH 40/61] Some minor changes. --- .../org/mage/plugins/card/dl/sources/MtgImageSource.java | 4 +--- Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java index ca58560210d..0898c54bb33 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java @@ -28,8 +28,6 @@ package org.mage.plugins.card.dl.sources; -import java.net.URLEncoder; -import mage.cards.SplitCard; import org.mage.plugins.card.images.CardDownloadData; /** @@ -56,7 +54,7 @@ public class MtgImageSource implements CardImageSource { throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet); } StringBuilder url = new StringBuilder("http://mtgimage.com/set/"); - url.append(cardSet.toUpperCase()).append("/"); + url.append(cardSet.toUpperCase()).append("/"); if (card.isSplitCard()) { url.append(card.getDownloadName().replaceAll(" // ", "")); diff --git a/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java b/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java index 111b094beaa..4670aafb5e6 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java +++ b/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java @@ -142,12 +142,14 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { for (Ability ability :card.getAbilities()) { if (!oldAbilities.contains(ability)) { ability.setControllerId(source.getControllerId()); - game.getState().addAbility(ability, card.getId(), card); + ability.setSourceId(source.getSourceId()); + ability.setSourceObject(source.getSourceObject(game)); + game.getState().addAbility(ability, card); } } } - game.informPlayers(new StringBuilder(controller.getName()).append(" suspends (").append(4).append(") ").append(card.getName()).toString()); + game.informPlayers(controller.getName() + " suspends 4 - " + card.getName()); return true; } } From 961fb5e31cdef20275365aedc8b144c62fdb7e06 Mon Sep 17 00:00:00 2001 From: mnapoleon Date: Fri, 27 Feb 2015 14:18:15 -0500 Subject: [PATCH 41/61] added Mourning card to Invasion set --- .../src/mage/sets/invasion/Mourning.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/invasion/Mourning.java diff --git a/Mage.Sets/src/mage/sets/invasion/Mourning.java b/Mage.Sets/src/mage/sets/invasion/Mourning.java new file mode 100644 index 00000000000..c6c5f8f5a83 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/Mourning.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.invasion; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.effects.common.continious.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author michael.napoleon@gmail.com + */ +public class Mourning extends CardImpl { + + public Mourning(UUID ownerId) { + super(ownerId, 111, "Mourning", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + this.expansionSetCode = "INV"; + 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 gets -2/-0. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(-2, 0))); + + // {B}: Return Mourning to its owner's hand. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandSourceEffect(true), new ManaCostsImpl("{B}"))); + } + + public Mourning(final Mourning card) { + super(card); + } + + @Override + public Mourning copy() { + return new Mourning(this); + } +} From 80620c71c3dd068d20ad87909ed851bced066818 Mon Sep 17 00:00:00 2001 From: mnapoleon Date: Fri, 27 Feb 2015 15:30:17 -0500 Subject: [PATCH 42/61] implemented Goblin Spy --- .../src/mage/sets/invasion/GoblinSpy.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/invasion/GoblinSpy.java diff --git a/Mage.Sets/src/mage/sets/invasion/GoblinSpy.java b/Mage.Sets/src/mage/sets/invasion/GoblinSpy.java new file mode 100644 index 00000000000..187ef26af8f --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/GoblinSpy.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.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continious.PlayWithTheTopCardRevealedEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author anonymous + */ +public class GoblinSpy extends CardImpl { + + public GoblinSpy(UUID ownerId) { + super(ownerId, 145, "Goblin Spy", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{R}"); + this.expansionSetCode = "INV"; + this.subtype.add("Goblin"); + this.subtype.add("Rogue"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Play with the top card of your library revealed. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); + } + + public GoblinSpy(final GoblinSpy card) { + super(card); + } + + @Override + public GoblinSpy copy() { + return new GoblinSpy(this); + } +} From f995af804a575e47e371b67dbf6b29d670f4d943 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 27 Feb 2015 17:53:45 -0600 Subject: [PATCH 43/61] - Added Declaration of Naught --- .../sets/morningtide/DeclarationOfNaught.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java diff --git a/Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java b/Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java new file mode 100644 index 00000000000..7f91e4a9af4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java @@ -0,0 +1,87 @@ +/* + * 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.morningtide; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.target.TargetSpell; + +/** + * + * @author jeffwadsworth + */ +public class DeclarationOfNaught extends CardImpl { + + static final private FilterSpell filter = new FilterSpell("spell with the chosen name"); + + public DeclarationOfNaught(UUID ownerId) { + super(ownerId, 29, "Declaration of Naught", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{U}{U}"); + this.expansionSetCode = "MOR"; + + // As Declaration of Naught enters the battlefield, name a card. + this.addAbility(new AsEntersBattlefieldAbility(new NameACardEffect(NameACardEffect.TypeOfName.ALL))); + + // {U}: Counter target spell with the chosen name. + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}")); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SimpleActivatedAbility) { + ability.getTargets().clear(); + FilterSpell filter2 = new FilterSpell("spell with the chosen name"); + filter2.add(new NamePredicate((String) game.getState().getValue(ability.getSourceId().toString() + NameACardEffect.INFO_KEY))); + TargetSpell target = new TargetSpell(1, filter2); + ability.addTarget(target); + } + } + + public DeclarationOfNaught(final DeclarationOfNaught card) { + super(card); + } + + @Override + public DeclarationOfNaught copy() { + return new DeclarationOfNaught(this); + } +} \ No newline at end of file From 5e640b9318867a900ae34066dc149e176888358f Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 27 Feb 2015 18:09:02 -0600 Subject: [PATCH 44/61] - Added Wingrattle Scarecrow --- .../sets/shadowmoor/WingrattleScarecrow.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java diff --git a/Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java b/Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java new file mode 100644 index 00000000000..a3a57bbded3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java @@ -0,0 +1,89 @@ +/* + * 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.shadowmoor; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinousEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.continious.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PersistAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; + +/** + * + * @author jeffwadsworth + */ +public class WingrattleScarecrow extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a blue creature"); + private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("a black creature"); + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + filter2.add(new ColorPredicate(ObjectColor.BLACK)); + } + + private static final String rule = "{this} has flying as long as you control a blue creature"; + private static final String rule2 = "{this} has persist as long as you control a black creature"; + + public WingrattleScarecrow(UUID ownerId) { + super(ownerId, 270, "Wingrattle Scarecrow", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + this.expansionSetCode = "SHM"; + this.subtype.add("Scarecrow"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Wingrattle Scarecrow has flying as long as you control a blue creature. + ContinuousEffect effect = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinousEffect(effect, new PermanentsOnTheBattlefieldCondition(filter), rule))); + + // Wingrattle Scarecrow has persist as long as you control a black creature. + ContinuousEffect effect2 = new GainAbilitySourceEffect(new PersistAbility(), Duration.WhileOnBattlefield); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinousEffect(effect2, new PermanentsOnTheBattlefieldCondition(filter2), rule2))); + + } + + public WingrattleScarecrow(final WingrattleScarecrow card) { + super(card); + } + + @Override + public WingrattleScarecrow copy() { + return new WingrattleScarecrow(this); + } +} From 61e5c2d7fe884fbc928508a8ac7bc1e0f82d0ae5 Mon Sep 17 00:00:00 2001 From: Michael Napoleon Date: Fri, 27 Feb 2015 22:12:43 -0500 Subject: [PATCH 45/61] fixed author name --- Mage.Sets/src/mage/sets/invasion/GoblinSpy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/invasion/GoblinSpy.java b/Mage.Sets/src/mage/sets/invasion/GoblinSpy.java index 187ef26af8f..ad79d745dbd 100644 --- a/Mage.Sets/src/mage/sets/invasion/GoblinSpy.java +++ b/Mage.Sets/src/mage/sets/invasion/GoblinSpy.java @@ -38,7 +38,7 @@ import mage.constants.Zone; /** * - * @author anonymous + * @author michael.napoleon@gmail.com */ public class GoblinSpy extends CardImpl { From 01ef3f93548e301840257449fb606e95de515bea Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 28 Feb 2015 12:55:49 +0100 Subject: [PATCH 46/61] * Fixed a bug that replacemet effects did not work if the source object of the replacemet effect also changed zone (fixes #759). --- .../cards/abilities/keywords/UndyingTest.java | 36 ++++++++++++++++++- .../abilities/effects/ContinuousEffects.java | 3 +- .../abilities/keyword/UndyingAbility.java | 3 -- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index b62a2598f59..3f827caec13 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -146,5 +146,39 @@ public class UndyingTest extends CardTestPlayerBase { assertExileCount("Butcher Ghoul", 1); } - + /** + * Tests "Target creature with Undying will be exiled by Anafenza before it returns to battlefield + * if both leave the battlefield at the same time + * + * Anafenza the foremost doesn't exile an undying creature when dying at the same time as + * that undying one. The undying comes back to the field when he shouldn't. + */ + @Test + public void testReplacementEffectPreventsReturnOfUndyingWrath() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Butcher Ghoul + // Creature - Zombie, 1/1 {1}{B} + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Butcher Ghoul"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + // Destroy all creatures. They can't be regenerated. + addCard(Zone.HAND, playerB, "Wrath of God"); + // Anafenza, the Foremost + // Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control. + // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. + addCard(Zone.BATTLEFIELD, playerB, "Anafenza, the Foremost"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Wrath of God"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Anafenza, the Foremost", 1); + assertGraveyardCount(playerB, "Wrath of God", 1); + + assertPermanentCount(playerA, "Butcher Ghoul", 0); + assertExileCount("Butcher Ghoul", 1); + } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 2bbc85cf77b..2f0de6e876f 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -331,6 +331,7 @@ public class ContinuousEffects implements Serializable { if(auraReplacementEffect.checksEventType(event, game) && auraReplacementEffect.applies(event, null, game)){ replaceEffects.put(auraReplacementEffect, null); } + boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects for (ReplacementEffect effect: replacementEffects) { if (!effect.checksEventType(event, game)) { @@ -344,7 +345,7 @@ public class ContinuousEffects implements Serializable { HashSet abilities = replacementEffects.getAbility(effect.getId()); HashSet applicableAbilities = new HashSet<>(); for (Ability ability : abilities) { - if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) { + if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, checkLKI)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) { if (checkAbilityStillExists(ability, effect, event, game)) { diff --git a/Mage/src/mage/abilities/keyword/UndyingAbility.java b/Mage/src/mage/abilities/keyword/UndyingAbility.java index f77fbd18708..be16dd65a6f 100644 --- a/Mage/src/mage/abilities/keyword/UndyingAbility.java +++ b/Mage/src/mage/abilities/keyword/UndyingAbility.java @@ -40,7 +40,6 @@ public class UndyingAbility extends DiesTriggeredAbility { if (super.checkTrigger(event, game)) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (!permanent.getCounters().containsKey(CounterType.P1P1) || permanent.getCounters().getCount(CounterType.P1P1) == 0) { - Logger.getLogger(UndyingAbility.class).info("Undying trigger: " + getSourceId()); game.getState().setValue("undying" + getSourceId(),permanent.getId()); return true; } @@ -115,8 +114,6 @@ class UndyingReplacementEffect extends ReplacementEffectImpl { if (event.getTargetId().equals(source.getSourceId())) { // Check if undying condition is true UUID targetId = (UUID) game.getState().getValue("undying" + source.getSourceId()); - Logger.getLogger(UndyingReplacementEffect.class).info("Undying replacement applies: " + targetId + " eventSourceId " + event.getTargetId()); - if (targetId != null && targetId.equals(source.getSourceId())) { return true; } From 07db858f34b7ee806344a1494063a7aae0bd215e Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Sat, 28 Feb 2015 16:16:10 -0800 Subject: [PATCH 47/61] Implement Tiny Leaders for XMage Initial implementation of Tiny Leaders for XMage --- .../src/mage/deck/TinyLeaders.java | 202 ++++++++++++++++++ .../Mage.Game.TinyLeadersDuel/pom.xml | 50 +++++ .../src/mage/game/TinyLeadersDuel.java | 64 ++++++ .../src/mage/game/TinyLeadersDuelMatch.java | 58 +++++ .../src/mage/game/TinyLeadersDuelType.java | 58 +++++ .../target/classes/.netbeans_automatic_build | 0 .../classes/mage/game/TinyLeadersDuel.class | Bin 0 -> 1171 bytes .../mage/game/TinyLeadersDuelMatch.class | Bin 0 -> 1305 bytes .../mage/game/TinyLeadersDuelType.class | Bin 0 -> 996 bytes .../target/mage-game-tinyleadersduel.jar | Bin 0 -> 4259 bytes .../target/maven-archiver/pom.properties | 5 + .../test-classes/.netbeans_automatic_build | 0 Mage.Server.Plugins/pom.xml | 11 +- Mage.Server/config/config.xml | 4 +- Mage.Server/pom.xml | 6 + Mage/src/mage/game/GameTinyLeadersImpl.java | 116 ++++++++++ pom.xml | 7 +- 17 files changed, 567 insertions(+), 14 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/.netbeans_automatic_build create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuel.class create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuelMatch.class create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuelType.class create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/mage-game-tinyleadersduel.jar create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/maven-archiver/pom.properties create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/test-classes/.netbeans_automatic_build create mode 100644 Mage/src/mage/game/GameTinyLeadersImpl.java diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java new file mode 100644 index 00000000000..620160c0874 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -0,0 +1,202 @@ +/* + * 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 + * 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.deck; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidator; +import mage.constants.CardType; +import mage.filter.FilterMana; +import mage.util.CardUtil; + +/** + * + * @author JRHerlehy + */ +public class TinyLeaders extends DeckValidator { + + protected List banned = new ArrayList<>(); + protected List bannedCommander = new ArrayList<>(); + + public TinyLeaders() { + this("Tiny Leaders"); + //Banned list from tinyleaders.blodspot.ca/p/ban-list.html + //Ban list updated as of 11/08/14 + banned.add("Ancestral Recall"); + banned.add("Balance"); + banned.add("Black Lotus"); + banned.add("Channel"); + banned.add("Counterbalance"); + banned.add("Demonic Tutor"); + banned.add("Earthcraft"); + banned.add("Edric, Spymaster of Trest"); + banned.add("Fastbond"); + banned.add("Goblin Recruiter"); + banned.add("Hermit Druid"); + banned.add("Imperial Seal"); + banned.add("Library of Alexandria"); + banned.add("Karakas"); + banned.add("Mana Crypt"); + banned.add("Mana Drain"); + banned.add("Mana Vault"); + banned.add("metalworker"); + banned.add("Mind Twist"); + banned.add("Mishra's Workshop"); + banned.add("Mox Emerald"); + banned.add("Mox Jet"); + banned.add("Mox Pearl"); + banned.add("Mox Ruby"); + banned.add("Mox Sapphire"); + banned.add("Necropotence"); + banned.add("Painter's Servant"); + banned.add("Shahrazad"); + banned.add("Skullclamp"); + banned.add("Sol Ring"); + banned.add("Strip Mine"); + banned.add("Survival of the Fittest"); + banned.add("Sword of Body and Mind"); + banned.add("Time Vault"); + banned.add("Time Walk"); + banned.add("Timetwister"); + banned.add("Tolarian Academy"); + banned.add("Umezawa's Jitte"); + banned.add("Vampiric Tutor"); + banned.add("Yawgmoth's Will"); + + //Additionally, these Legendary creatures cannot be used as Commanders + bannedCommander.add("Erayo, Soratami Ascendant"); + bannedCommander.add("Rofellos, Llanowar Emissary"); + bannedCommander.add("Derevi, Empyrical Tactician"); + } + + public TinyLeaders(String name) { + super(name); + } + + /** + * + * @param deck + * @return - True if deck is valid + */ + @Override + public boolean validate(Deck deck) { + boolean valid = true; + + if (deck.getCards().size() != 49) { + invalid.put("Deck", "Must contain 49 cards: has " + deck.getCards().size() + " cards"); + valid = false; + } + + List basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", + "Snow-Covered Forest", "Snow-Covered Island", "Snow-Covered Mountain", "Snow-Covered Swamp", "Snow-Covered Plains")); + Map counts = new HashMap<>(); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); + for (Map.Entry entry : counts.entrySet()) { + if (entry.getValue() > 1) { + if (!basicLandNames.contains(entry.getKey()) && !entry.getKey().equals("Relentless Rats") && !entry.getKey().equals("Shadowborn Apostle")) { + invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + valid = false; + } + } + } + + for (String bannedCard : banned) { + if (counts.containsKey(bannedCard)) { + invalid.put(bannedCard, "Banned"); + valid = false; + } + } + + if (deck.getSideboard().size() == 1) { + Card commander = (Card) deck.getSideboard().toArray()[0]; + + /** + * 905.5b - Each card must have a converted mana cost of three of less. + * Cards with {X} in their mana cost count X as zero. + * Split and double-face cards are legal only if both of their halves would be legal independently. + */ + + if (commander == null || commander.getManaCost().convertedManaCost() > 3) { + invalid.put("Commander", "Commander invalide "); + return false; + } + if ((commander.getCardType().contains(CardType.CREATURE) && commander.getSupertype().contains("Legendary")) + || (commander.getCardType().contains(CardType.PLANESWALKER) && commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { + if (!bannedCommander.contains(commander.getName())) { + FilterMana color = CardUtil.getColorIdentity(commander); + for (Card card : deck.getCards()) { + if (!cardHasValideColor(color, card)) { + invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); + valid = false; + } + + //905.5b - Converted mana cost must be 3 or less + if (card.getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); + valid = false; + } + } + } else { + invalid.put("Commander", "Commander banned (" + commander.getName() + ")"); + valid = false; + } + } else { + invalid.put("Commander", "Commander invalide (" + commander.getName() + ")"); + valid = false; + } + } else { + invalid.put("Commander", "Sideboard must contain only the commander"); + valid = false; + } + + return valid; + } + + /** + * + * @param commander FilterMana object with Color Identity of Commander set + * @param card Card to validate + * @return True if card has a valid color identity + */ + public boolean cardHasValideColor(FilterMana commander, Card card) { + FilterMana cardColor = CardUtil.getColorIdentity(card); + return !(cardColor.isBlack() && !commander.isBlack() + || cardColor.isBlue() && !commander.isBlue() + || cardColor.isGreen() && !commander.isGreen() + || cardColor.isRed() && !commander.isRed() + || cardColor.isWhite() && !commander.isWhite()); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml new file mode 100644 index 00000000000..1d6f0d0a37f --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.mage + mage-server-plugins + 1.3.0 + + + mage-game-tinyleadersduel + jar + Mage Game Tiny Leaders Two Player + + + + ${project.groupId} + mage + 1.3.0 + + + + + src + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + maven-resources-plugin + + UTF-8 + + + + + + mage-game-tinyleadersduel + + + + + diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java new file mode 100644 index 00000000000..b8e05f76073 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java @@ -0,0 +1,64 @@ +/* + * 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.game; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.match.MatchType; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuel extends GameTinyLeadersImpl { + + public TinyLeadersDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public TinyLeadersDuel(final TinyLeadersDuel game) { + super(game); + } + + @Override + public MatchType getGameType() { + return new TinyLeadersDuelType(); + } + + @Override + public int getNumPlayers() { + return 2; + } + + @Override + public TinyLeadersDuel copy() { + return new TinyLeadersDuel(this); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java new file mode 100644 index 00000000000..d5b9ada2d5a --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java @@ -0,0 +1,58 @@ +/* + * 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.game; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuelMatch extends MatchImpl { + + public TinyLeadersDuelMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + //Tiny Leaders Play Rule 13: Players begin the game with 25 life. + int startLife = 25; + + TinyLeadersDuel game = new TinyLeadersDuel(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife); + game.setStartMessage(this.createGameStartMessage()); + + //"Tucking a Tiny Leader is legal + game.setAlsoLibrary(false); + this.initGame(game); + games.add(game); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java new file mode 100644 index 00000000000..4c8055c8fb7 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java @@ -0,0 +1,58 @@ +/* + * 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.game; + +import mage.game.match.MatchType; + +/** + * + * @author JRHerlehy + */ +class TinyLeadersDuelType extends MatchType { + + public TinyLeadersDuelType() { + this.name = "Tiny Leaders Two Player Duel"; + this.maxPlayers = 2; + this.minPlayers = 2; + this.numTeams = 0; + this.useAttackOption = false; + this.useRange = false; + this.sideboardingAllowed = false; + } + + protected TinyLeadersDuelType(final TinyLeadersDuelType matchType){ + super(matchType); + } + + @Override + public TinyLeadersDuelType copy() { + return new TinyLeadersDuelType(this); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/.netbeans_automatic_build b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/.netbeans_automatic_build new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuel.class b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuel.class new file mode 100644 index 0000000000000000000000000000000000000000..64eb001f6daa7cb2140bea1f19298270dd1030e3 GIT binary patch literal 1171 zcma)5+iuf95Ivj3xwr`}p%)6ICFRoGR7;cxq(G$#1d5cl2%<=RW9x2GgJVaIgOq>b z0bYniB|daj;WfhR<$dTdhVVpf#tU4sN?bAOnDu7X46iw?V8f=O3&gr+)Viw)=1#?jJz)g z%IH|mq^;z+@)b?JT@s%{-=3?LeEo*inp!4bjXuv3FN3Jp5L~5 z#IY6XE;Jcb&L;v(edWKRRcA7!s8p#&Y8y!3yfBV<Kxk^y>!9DHnygewH)XoR zNj&+_#uTrRvnVm@Z{4wHs+SfweKnS}_g;RKdE}t;P$P-p_7~N}1{qWr*!(Mq0ULRg zuyuthsQ>ppwgy96U?b~01ZW+rSfdZZd+W4H1!^0i+5ushGhM4s zk%%kp5x)^AtrN9v6#k|yT_`IpcM(JoFKwx;fC$4`O0U z)Wiqyp^RsC8(NCSCOtEAF5mg)%sKn(_xGOw7V##AIA%25>p>sx$M66Tl}IX)iXnt_ zj104Cmr>%8I?gF)qZf}cuVF#MB11H1IhI^ym`)a|ydsPWuL`5erFmi$mE1d%R?YFU zsY8ayMy)Iu;swhQyY*^GxCgvslV+e$Gr4`pT}z#t%7{F%Jcg+@+yl!wF9=>1uD4ki zwx1x&pw)sT43llne<8up<4Nwy9U{pPPAXD?reGbDhUJN*x&gy@8&XGF${5@FWQriE zh9`91uhm^sY+LF!V;%lJ&AC(lk?TmHpB!41>TuODikljibUekfjukx9@f=wdm(%eA zt2);3QpY-8>Da)gjxB6!*wOJC#C`Igz&?j7ncK>fub$Zqqpe_7pUWFEOtdU*QO%d7 z_pAuHE+scV1S*DfG8L51q^FnMk)Bbk+tNC-`MGf0eW{k+6#kYw6{_6K^*GQ z?YeMGK>-O0*mi{=NZYD#hnj?*lCHno?7{zM?PPZ?OFpl9W5jd?mqL|eU-eBMt8oh2bFh7) zl**r>t_tnw#)$y8E-l+ASe~TpeLka04ONAg%M4d2?3GT1DYK~~Ow%mK>01DrZh}KU z$pJd8(m6zHG(CF(_Qkgkl2l&-AcZcZF+{tOzzf5)Q@^NcUBaLy!!5EYtk0wk)4G-z z%l$y;eflfH4Mc*aD_ElA7to5CbOYTD^km2$OJ^GBC99tKh8rQgYmu0QMsNm!&eH`J uh|Ut`XjT@nOvG1wVpBoNCb2Mb7{>(Bn#Lq<(?5hegb^Xll&@6kt&o01iA}$6?}ZakTe<-lKO}=P4wGsds9~T(QLP3|CN3~ z5>5O8{wU*2yGm`0d^x)_bMBclbMKFzU%vy`$HP44v64eY!yHx_42Cs^b%qUw+YFmb z-O6G+PeGNz%)%1LJa&91IuuA&&6YswNzhdSg@)s+7elYD!lrDylqofWj&xfxbU2Qa zsp!NR2&^_d*;95;ddhA({-~j3SA~P8L*+I{eRUwPUcH3Ey){opofAGmJ~}~vB(PRB zFF7c%@)u{UVL$(^M7{IJ!7%KoXAY0C^bdcvck+YOQ9w~(nd?S8fYCe+j90E4snFnI zbnIYP#~svk*bH|W?qN@-yCAsFP{0H7^xuUrX;H@#$^zvJ=8Sb(f=?Ue)w4m{m444Y zjzY)p@l>AtII%_-AdWcU_0Mj~$uu6$DB(3xr zQVeM;d4>!_mLbQWeI{}OCBiLegxe%oMII)JTDV3lFJl)K+WH1+RI#x@EPc|1>enes z@j_z*hoqLET*lftNB%-RKhEwUO?Sl#ixXC?Kz!UFE!qy>ORTkzB>e%}H_W}Ef%H!l qOw4A+g-26nw24%)P69V$0h_)okhZ?y%Ix9Isl(~mhrq3Ii{)Pf?ZpNF literal 0 HcmV?d00001 diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/mage-game-tinyleadersduel.jar b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/mage-game-tinyleadersduel.jar new file mode 100644 index 0000000000000000000000000000000000000000..a19a3f23a90c4971b742245eaca445506e0af56f GIT binary patch literal 4259 zcmbVP2{@GP*SC))Wf_DrWl0)@8CkMt9mY(Sk+Q~wvXAUSmTYC;$u_nuA(WcR8nTmp zNfL!D<+VotnIyj6@?F<^p6i+Co|$vb{hV{3`*;5iQk|5Hnus9DUIxlP20uTj@K3ms zj=X@Hrt(=NUJXb@iNBJ5Qv>c-gUf5GDJyB~2*Q=W!dqK3)CB~)Xw?PIw6}IV(h_p@L`N~gebE+#nP)loB_^&2&oRBZ1rzO+v49Q0DlwS z_s?OGCY;JUw^1rnA+^&| zE03BdJZt$ufoD}}?Sz1928RQ`YqqAVh;Uz6PkYB2_cW4@`-6ux2l7;(c@v{l$Q7e_ zXk{T-;X!oE@xX@rLB`ts97im5%g$iRWx_`x({iXP0YfTy|FUv6Z-1EXN#izJzp=FiXT8zEC)G{7j1vgARHC2jnJZCyyz)DGy8i zexz&=w03A1H4F29swn9VK)WL_5wfHjf!R-03s#~1pR9&@FI&NreY~=JNvIu_5!G?N zrHZ4`d<^|1g)th1Kx>_(z_uE7mNDa313EQjsV^ zT!iqFisfgqP$oZ#wUj;;w%ne|g^}`ILFZh{b2H>)%1KZRmZ2F@TNBMzIktrfS$Dcu zC1lnOiV~W2lBtT9y>rVNVS+76>!6y``8+l$fLGu#;64zy0fK~(xI=2w9azOfU^ zIemcQq8JGgQ8d|3Isdr^xeuyAxT%wweZH%)$PCr;%k!aa#VuU)UjtJ2Gh4MH$4Fq>1Yi6GgRw*W$+ zdK420oeqx~N1s@X%W#~I)Zs**xLqzEs8-Q(dI_1Y{d#bwmP=zcp-Z?RLz>s%B#USm zFO4Yb11h%niLP0^tkAo}EkUQzto!jSZO0V50zGJhy)FO3%Kb#uaY+e-q@nr~wjUE9 zOJrY9F)zNUhnVXcwAW+!lP=Yk1vBAs<@0awY0~{7n1WC-0SD9C=$#D^5M_v9ls_8KvFs5ad-WogI@_n(xu*Cwh=nam*-Sx8 zmJ`nSJh?Ku>?x?t8N6crNr|Vc9yA^<@?ybR2}Jr~kV~fdWrbyhrG<}1%m+c=>xJA5X2bGXowA4HGU~o!6N2xhWK_d?qP@_o*|~FZ@57T}D>~wyB3iaLq>x9* z`&w!EagV2(^E~RcIbHl}E}8Te8a5ei*D*roEMBUX7lSCWN($~vrnoMF!Zxm~8?l|f zs%aA3Ul5!zBzgG3SwjhwX>1!sWvSUX^9+S`+%UgO$&F6Kh!Hg31P5AD44TY*h(D7b zxozik{Wbz^D-9?&{IvSH?zGc4nY7o@pq#FcB=y8B$G(>%7gdbZ)xrZCx{#uos?Kkp zA63*GkKUwZ&D#j^@lOLuO7~Kk4{$lw-Sji-Wz0*MhF0_`$8M+VFGl;0k3|~g4ASwj z5sS`})t<>drFvzMN`zk@EG>VJr$fu6OD0kxfZ@|AXtw}KQs+gkya4cnYAPw1b;-6A2B z+&~X+2`9efCP@$C5J&JO_lp|Ym^!0v4`35V|E+B2Xd(Fja1fCLtO%w81a2bWgy$UA zJDkHD&pGzdwD8|(n#6zdXK!aC=xSqKq+N5bM~LqEG&nQl#)M!f*#iYlFr5|+_+qEg zT}Yd|!(5Yd5-P~s;%aX2FqTGP_(H7I%O#wf8zZ^`g{Zdn%=3pXu!Q&Zffdd?1tfMS zm8q!43N~-W)4D|q_I#*?=mF{g&cl^f9<#0w<6GFjl<0dm)P|fgU{mMuQ##g=3qlo# zoi_q23+R0T4ZZ3=3ea{Ha=-!KvQ*ue+Af1Jop|AQZh+S%CM?C2_;C&!whfT4pRcR! zea6u`2377KmJ$(pq2j~&*ZjAS^9ZM}rbl0f@@S?-j(hSCt+v^94{LVHyoriLsz8Ua zSSIecmMo&nCJtRb36-_J1Beij*WZF?=Er&pWaC`bU({?)gs%bZmR`7iJ{$J9C}e?L znxs#{C_TrA&73&OL8t~v>UpdN!fw1Z(-XJSw7_~{dMedr>7k~(*jY4rQ~F!bH_IoK zFQYEry=LDi3PWuqu(ydgQr%nLjy9%T{wN_%r3bQ1n?EV}XiQ`4Q*DQiE=G8A{;6qk z3SZTGbk^t!yuU5IJjrJxsorj1QW;k)y|5mI6|*kluJi zOALn@kb4_@98Z{NkAzU2PeFKg*3jz+M~+uR`oy4>H-_8Kt)&=U(tCAVQrzxj!`Me< zy>w5_O&2Gx*G3N~S%GLOkb6sj_i5=O16a^>?PT$svyt4^k4`~+^jP{$d;lK!8>voA z0wn!&BS2_@c7%@Tvpiw+YuE3X?X^Vvn-4-uv@2L&Ui|Qv*}ewppos}h%8n2w?#B;* z!o}a;bteLX5y50**W_mlL$KQ;ko&16p^4cQtS={i;rCjfKejacBPX22`%h^C8{HMG zZw?+r|LSZXH~rn?eh?;{?z@8Z&7(NL)ZaH{QpG$-Mage.Game.CommanderFreeForAll Mage.Game.FreeForAll Mage.Game.TwoPlayerDuel - Mage.Player.AI - Mage.Player.AIMinimax - Mage.Player.AI.MA - Mage.Player.AIMCTS - Mage.Player.AI.DraftBot Mage.Player.Human - Mage.Tournament.BoosterDraft - Mage.Tournament.Constructed - Mage.Tournament.Sealed - + Mage.Game.TinyLeadersDuel + \ No newline at end of file diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 81bf9f2ea4d..c1aff2d008d 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -41,13 +41,14 @@ - + + @@ -91,6 +92,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 461172aaab3..f9f5951f3da 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -133,6 +133,12 @@ ${project.version} runtime + + ${project.groupId} + mage-game-tinyleadersduel + ${project.version} + runtime + diff --git a/Mage/src/mage/game/GameTinyLeadersImpl.java b/Mage/src/mage/game/GameTinyLeadersImpl.java new file mode 100644 index 00000000000..8b421f21d7a --- /dev/null +++ b/Mage/src/mage/game/GameTinyLeadersImpl.java @@ -0,0 +1,116 @@ +/* + * 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.game; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EmptyEffect; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continious.CommanderManaReplacementEffect; +import mage.abilities.effects.common.continious.CommanderReplacementEffect; +import mage.abilities.effects.common.cost.CommanderCostModification; +import mage.cards.Card; +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.turn.TurnMod; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author Justin + */ +public abstract class GameTinyLeadersImpl extends GameImpl{ + + protected boolean alsoLibrary; // replace also commander going to library + protected boolean startingPlayerSkipsDraw = true; + + public GameTinyLeadersImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public GameTinyLeadersImpl(final GameTinyLeadersImpl game) { + super(game); + this.alsoLibrary = game.alsoLibrary; + this.startingPlayerSkipsDraw = game.startingPlayerSkipsDraw; + } + + @Override + protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new EmptyEffect("Commander effects")); + //Move tiny leader to command zone + for (UUID playerId: state.getPlayerList(startingPlayerId)) { + Player player = getPlayer(playerId); + if (player != null){ + if (player.getSideboard().size() > 0){ + Card commander = getCard((UUID)player.getSideboard().toArray()[0]); + if (commander != null) { + player.setCommanderId(commander.getId()); + commander.moveToZone(Zone.COMMAND, null, this, true); + ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoLibrary)); + ability.addEffect(new CommanderCostModification(commander.getId())); + ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); + getState().setValue(commander.getId() + "_castCount", 0); + } + } + } + + } + this.getState().addAbility(ability, null); + super.init(choosingPlayerId, gameOptions); + if (startingPlayerSkipsDraw) { + state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); + } + } + + @Override + public Set getOpponents(UUID playerId) { + Set opponents = new HashSet<>(); + for (UUID opponentId: this.getPlayer(playerId).getInRange()) { + if (!opponentId.equals(playerId)) { + opponents.add(opponentId); + } + } + return opponents; + } + + @Override + public boolean isOpponent(Player player, UUID playerToCheck) { + return !player.getId().equals(playerToCheck); + } + + public void setAlsoLibrary(boolean alsoLibrary) { + this.alsoLibrary = alsoLibrary; + } + +} diff --git a/pom.xml b/pom.xml index f024001d95b..bc9933a845f 100644 --- a/pom.xml +++ b/pom.xml @@ -50,17 +50,16 @@ - Mage Mage.Common - Mage.Server - Mage.Sets Mage.Client Mage.Plugins + Mage + Mage.Server + Mage.Sets Mage.Server.Plugins Mage.Server.Console Mage.Tests Mage.Updater - Mage.Stats From d87ec679659cb0b843849412e28ac2338293be7a Mon Sep 17 00:00:00 2001 From: Justin Herlehy Date: Sat, 28 Feb 2015 19:55:32 -0800 Subject: [PATCH 48/61] Implementation of Tiny Leaders for XMage Initial Implementation of Tiny Leaders --- .gitignore | 4 +- .../src/mage/deck/TinyLeaders.java | 202 ++++++++++++++++++ .../Mage.Game.TinyLeadersDuel/pom.xml | 50 +++++ .../src/mage/game/TinyLeadersDuel.java | 64 ++++++ .../src/mage/game/TinyLeadersDuelMatch.java | 58 +++++ .../src/mage/game/TinyLeadersDuelType.java | 58 +++++ Mage.Server.Plugins/pom.xml | 11 +- Mage/src/mage/game/GameTinyLeadersImpl.java | 116 ++++++++++ pom.xml | 7 +- 9 files changed, 556 insertions(+), 14 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java create mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java create mode 100644 Mage/src/mage/game/GameTinyLeadersImpl.java diff --git a/.gitignore b/.gitignore index 53ce9bb177c..25ca0b875c2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ Mage.Server.Plugins/Mage.Deck.Limited/target Mage.Server.Plugins/Mage.Game.CommanderDuel/target Mage.Server.Plugins/Mage.Game.FreeForAll/target Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/target +Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target Mage.Server.Plugins/Mage.Player.AI/target Mage.Server.Plugins/Mage.Player.AIMinimax/target Mage.Server.Plugins/Mage.Player.AI.MA/target @@ -82,4 +83,5 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target /Mage.Server/config/ai.please.cast.this.txt /Mage.Stats/target/ -/Utils/*_unimplemented.txt \ No newline at end of file +/Utils/*_unimplemented.txt +*.netbeans_automatic_build \ No newline at end of file diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java new file mode 100644 index 00000000000..620160c0874 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -0,0 +1,202 @@ +/* + * 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 + * 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.deck; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidator; +import mage.constants.CardType; +import mage.filter.FilterMana; +import mage.util.CardUtil; + +/** + * + * @author JRHerlehy + */ +public class TinyLeaders extends DeckValidator { + + protected List banned = new ArrayList<>(); + protected List bannedCommander = new ArrayList<>(); + + public TinyLeaders() { + this("Tiny Leaders"); + //Banned list from tinyleaders.blodspot.ca/p/ban-list.html + //Ban list updated as of 11/08/14 + banned.add("Ancestral Recall"); + banned.add("Balance"); + banned.add("Black Lotus"); + banned.add("Channel"); + banned.add("Counterbalance"); + banned.add("Demonic Tutor"); + banned.add("Earthcraft"); + banned.add("Edric, Spymaster of Trest"); + banned.add("Fastbond"); + banned.add("Goblin Recruiter"); + banned.add("Hermit Druid"); + banned.add("Imperial Seal"); + banned.add("Library of Alexandria"); + banned.add("Karakas"); + banned.add("Mana Crypt"); + banned.add("Mana Drain"); + banned.add("Mana Vault"); + banned.add("metalworker"); + banned.add("Mind Twist"); + banned.add("Mishra's Workshop"); + banned.add("Mox Emerald"); + banned.add("Mox Jet"); + banned.add("Mox Pearl"); + banned.add("Mox Ruby"); + banned.add("Mox Sapphire"); + banned.add("Necropotence"); + banned.add("Painter's Servant"); + banned.add("Shahrazad"); + banned.add("Skullclamp"); + banned.add("Sol Ring"); + banned.add("Strip Mine"); + banned.add("Survival of the Fittest"); + banned.add("Sword of Body and Mind"); + banned.add("Time Vault"); + banned.add("Time Walk"); + banned.add("Timetwister"); + banned.add("Tolarian Academy"); + banned.add("Umezawa's Jitte"); + banned.add("Vampiric Tutor"); + banned.add("Yawgmoth's Will"); + + //Additionally, these Legendary creatures cannot be used as Commanders + bannedCommander.add("Erayo, Soratami Ascendant"); + bannedCommander.add("Rofellos, Llanowar Emissary"); + bannedCommander.add("Derevi, Empyrical Tactician"); + } + + public TinyLeaders(String name) { + super(name); + } + + /** + * + * @param deck + * @return - True if deck is valid + */ + @Override + public boolean validate(Deck deck) { + boolean valid = true; + + if (deck.getCards().size() != 49) { + invalid.put("Deck", "Must contain 49 cards: has " + deck.getCards().size() + " cards"); + valid = false; + } + + List basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", + "Snow-Covered Forest", "Snow-Covered Island", "Snow-Covered Mountain", "Snow-Covered Swamp", "Snow-Covered Plains")); + Map counts = new HashMap<>(); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); + for (Map.Entry entry : counts.entrySet()) { + if (entry.getValue() > 1) { + if (!basicLandNames.contains(entry.getKey()) && !entry.getKey().equals("Relentless Rats") && !entry.getKey().equals("Shadowborn Apostle")) { + invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + valid = false; + } + } + } + + for (String bannedCard : banned) { + if (counts.containsKey(bannedCard)) { + invalid.put(bannedCard, "Banned"); + valid = false; + } + } + + if (deck.getSideboard().size() == 1) { + Card commander = (Card) deck.getSideboard().toArray()[0]; + + /** + * 905.5b - Each card must have a converted mana cost of three of less. + * Cards with {X} in their mana cost count X as zero. + * Split and double-face cards are legal only if both of their halves would be legal independently. + */ + + if (commander == null || commander.getManaCost().convertedManaCost() > 3) { + invalid.put("Commander", "Commander invalide "); + return false; + } + if ((commander.getCardType().contains(CardType.CREATURE) && commander.getSupertype().contains("Legendary")) + || (commander.getCardType().contains(CardType.PLANESWALKER) && commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { + if (!bannedCommander.contains(commander.getName())) { + FilterMana color = CardUtil.getColorIdentity(commander); + for (Card card : deck.getCards()) { + if (!cardHasValideColor(color, card)) { + invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); + valid = false; + } + + //905.5b - Converted mana cost must be 3 or less + if (card.getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); + valid = false; + } + } + } else { + invalid.put("Commander", "Commander banned (" + commander.getName() + ")"); + valid = false; + } + } else { + invalid.put("Commander", "Commander invalide (" + commander.getName() + ")"); + valid = false; + } + } else { + invalid.put("Commander", "Sideboard must contain only the commander"); + valid = false; + } + + return valid; + } + + /** + * + * @param commander FilterMana object with Color Identity of Commander set + * @param card Card to validate + * @return True if card has a valid color identity + */ + public boolean cardHasValideColor(FilterMana commander, Card card) { + FilterMana cardColor = CardUtil.getColorIdentity(card); + return !(cardColor.isBlack() && !commander.isBlack() + || cardColor.isBlue() && !commander.isBlue() + || cardColor.isGreen() && !commander.isGreen() + || cardColor.isRed() && !commander.isRed() + || cardColor.isWhite() && !commander.isWhite()); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml new file mode 100644 index 00000000000..1d6f0d0a37f --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.mage + mage-server-plugins + 1.3.0 + + + mage-game-tinyleadersduel + jar + Mage Game Tiny Leaders Two Player + + + + ${project.groupId} + mage + 1.3.0 + + + + + src + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + maven-resources-plugin + + UTF-8 + + + + + + mage-game-tinyleadersduel + + + + + diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java new file mode 100644 index 00000000000..b8e05f76073 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java @@ -0,0 +1,64 @@ +/* + * 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.game; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.match.MatchType; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuel extends GameTinyLeadersImpl { + + public TinyLeadersDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public TinyLeadersDuel(final TinyLeadersDuel game) { + super(game); + } + + @Override + public MatchType getGameType() { + return new TinyLeadersDuelType(); + } + + @Override + public int getNumPlayers() { + return 2; + } + + @Override + public TinyLeadersDuel copy() { + return new TinyLeadersDuel(this); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java new file mode 100644 index 00000000000..d5b9ada2d5a --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java @@ -0,0 +1,58 @@ +/* + * 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.game; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuelMatch extends MatchImpl { + + public TinyLeadersDuelMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + //Tiny Leaders Play Rule 13: Players begin the game with 25 life. + int startLife = 25; + + TinyLeadersDuel game = new TinyLeadersDuel(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife); + game.setStartMessage(this.createGameStartMessage()); + + //"Tucking a Tiny Leader is legal + game.setAlsoLibrary(false); + this.initGame(game); + games.add(game); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java new file mode 100644 index 00000000000..4c8055c8fb7 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java @@ -0,0 +1,58 @@ +/* + * 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.game; + +import mage.game.match.MatchType; + +/** + * + * @author JRHerlehy + */ +class TinyLeadersDuelType extends MatchType { + + public TinyLeadersDuelType() { + this.name = "Tiny Leaders Two Player Duel"; + this.maxPlayers = 2; + this.minPlayers = 2; + this.numTeams = 0; + this.useAttackOption = false; + this.useRange = false; + this.sideboardingAllowed = false; + } + + protected TinyLeadersDuelType(final TinyLeadersDuelType matchType){ + super(matchType); + } + + @Override + public TinyLeadersDuelType copy() { + return new TinyLeadersDuelType(this); + } + +} diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index 42835c4d180..eb8c3742aaf 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -21,15 +21,8 @@ Mage.Game.CommanderFreeForAll Mage.Game.FreeForAll Mage.Game.TwoPlayerDuel - Mage.Player.AI - Mage.Player.AIMinimax - Mage.Player.AI.MA - Mage.Player.AIMCTS - Mage.Player.AI.DraftBot Mage.Player.Human - Mage.Tournament.BoosterDraft - Mage.Tournament.Constructed - Mage.Tournament.Sealed - + Mage.Game.TinyLeadersDuel + \ No newline at end of file diff --git a/Mage/src/mage/game/GameTinyLeadersImpl.java b/Mage/src/mage/game/GameTinyLeadersImpl.java new file mode 100644 index 00000000000..8b421f21d7a --- /dev/null +++ b/Mage/src/mage/game/GameTinyLeadersImpl.java @@ -0,0 +1,116 @@ +/* + * 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.game; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EmptyEffect; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continious.CommanderManaReplacementEffect; +import mage.abilities.effects.common.continious.CommanderReplacementEffect; +import mage.abilities.effects.common.cost.CommanderCostModification; +import mage.cards.Card; +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.turn.TurnMod; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author Justin + */ +public abstract class GameTinyLeadersImpl extends GameImpl{ + + protected boolean alsoLibrary; // replace also commander going to library + protected boolean startingPlayerSkipsDraw = true; + + public GameTinyLeadersImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public GameTinyLeadersImpl(final GameTinyLeadersImpl game) { + super(game); + this.alsoLibrary = game.alsoLibrary; + this.startingPlayerSkipsDraw = game.startingPlayerSkipsDraw; + } + + @Override + protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new EmptyEffect("Commander effects")); + //Move tiny leader to command zone + for (UUID playerId: state.getPlayerList(startingPlayerId)) { + Player player = getPlayer(playerId); + if (player != null){ + if (player.getSideboard().size() > 0){ + Card commander = getCard((UUID)player.getSideboard().toArray()[0]); + if (commander != null) { + player.setCommanderId(commander.getId()); + commander.moveToZone(Zone.COMMAND, null, this, true); + ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoLibrary)); + ability.addEffect(new CommanderCostModification(commander.getId())); + ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); + getState().setValue(commander.getId() + "_castCount", 0); + } + } + } + + } + this.getState().addAbility(ability, null); + super.init(choosingPlayerId, gameOptions); + if (startingPlayerSkipsDraw) { + state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); + } + } + + @Override + public Set getOpponents(UUID playerId) { + Set opponents = new HashSet<>(); + for (UUID opponentId: this.getPlayer(playerId).getInRange()) { + if (!opponentId.equals(playerId)) { + opponents.add(opponentId); + } + } + return opponents; + } + + @Override + public boolean isOpponent(Player player, UUID playerToCheck) { + return !player.getId().equals(playerToCheck); + } + + public void setAlsoLibrary(boolean alsoLibrary) { + this.alsoLibrary = alsoLibrary; + } + +} diff --git a/pom.xml b/pom.xml index f024001d95b..bc9933a845f 100644 --- a/pom.xml +++ b/pom.xml @@ -50,17 +50,16 @@ - Mage Mage.Common - Mage.Server - Mage.Sets Mage.Client Mage.Plugins + Mage + Mage.Server + Mage.Sets Mage.Server.Plugins Mage.Server.Console Mage.Tests Mage.Updater - Mage.Stats From 3544060ada6d57b726b8aff1a7f4d6f4b68c5730 Mon Sep 17 00:00:00 2001 From: Justin Herlehy Date: Sat, 28 Feb 2015 22:03:14 -0800 Subject: [PATCH 49/61] Server Project file Configuration Changed to the Server pom file to include the Tiny Leaders Duel Format --- Mage.Server/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 461172aaab3..f9f5951f3da 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -133,6 +133,12 @@ ${project.version} runtime + + ${project.groupId} + mage-game-tinyleadersduel + ${project.version} + runtime + From 752f237b8db72951884f867f3c8c29206a023f12 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 1 Mar 2015 01:39:38 -0600 Subject: [PATCH 50/61] - Added Extraplanar Lens. --- .../mage/sets/mirrodin/ExtraplanarLens.java | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java diff --git a/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java b/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java new file mode 100644 index 00000000000..77ff0f92646 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java @@ -0,0 +1,173 @@ +/* + * 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.mirrodin; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AddManaOfAnyTypeProducedEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ManaEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public class ExtraplanarLens extends CardImpl { + + public ExtraplanarLens(UUID ownerId) { + super(ownerId, 169, "Extraplanar Lens", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MRD"; + + // Imprint - When Extraplanar Lens enters the battlefield, you may exile target land you control. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ExtraplanarLensImprintEffect(), true, "Imprint - ")); + + // Whenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana to his or her mana pool of any type that land produced. + this.addAbility(new ExtraplanarLensTriggeredAbility()); + + } + + public ExtraplanarLens(final ExtraplanarLens card) { + super(card); + } + + @Override + public ExtraplanarLens copy() { + return new ExtraplanarLens(this); + } +} + +class ExtraplanarLensImprintEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("land you control"); + + static { + filter.add(new CardTypePredicate(CardType.LAND)); + } + + public ExtraplanarLensImprintEffect() { + super(Outcome.Neutral); + staticText = "you may exile target land you control"; + } + + public ExtraplanarLensImprintEffect(ExtraplanarLensImprintEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent extraplanarLens = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null) { + if (game.getBattlefield().countAll(filter, controller.getId(), game) > 0) { + TargetPermanent target = new TargetPermanent(1, filter); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.choose(Outcome.Neutral, target, source.getSourceId(), game)) { + Permanent targetLand = game.getPermanent(target.getFirstTarget()); + if (targetLand != null) { + targetLand.moveToExile(null, extraplanarLens.getLogName() + " (Imprint)", source.getSourceId(), game); + extraplanarLens.imprint(targetLand.getId(), game); + extraplanarLens.addInfo("imprint", CardUtil.addToolTipMarkTags("[Imprinted card - " + targetLand.getLogName() + "]")); + } + } + return true; + } + } + return false; + } + + @Override + public ExtraplanarLensImprintEffect copy() { + return new ExtraplanarLensImprintEffect(this); + } + +} + +class ExtraplanarLensTriggeredAbility extends TriggeredAbilityImpl { + + public ExtraplanarLensTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddManaOfAnyTypeProducedEffect()); + } + + public ExtraplanarLensTriggeredAbility(final ExtraplanarLensTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent landTappedForMana = game.getPermanentOrLKIBattlefield(event.getSourceId()); + Permanent extraplanarLens = game.getPermanent(getSourceId()); + if (extraplanarLens != null + && landTappedForMana != null) { + MageObject object = game.getObject(extraplanarLens.getImprinted().get(0)); + if (landTappedForMana.getName().equals(object.getName()) + && landTappedForMana.getCardType().contains(CardType.LAND)) { + ManaEvent mEvent = (ManaEvent) event; + for (Effect effect : getEffects()) { + effect.setValue("mana", mEvent.getMana()); + } + getEffects().get(0).setTargetPointer(new FixedTarget(landTappedForMana.getId())); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return new StringBuilder("Whenever a land with the same name as the exiled card is tapped for mana, ").append(super.getRule()).toString(); + } + + @Override + public ExtraplanarLensTriggeredAbility copy() { + return new ExtraplanarLensTriggeredAbility(this); + } + +} From 166205218b1b8aa71c5ff7059b8dcad6eb949109 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 1 Mar 2015 09:29:22 +0100 Subject: [PATCH 51/61] * AEtherspouts - Fixed a bug that could lead to endless loop if player left during resolution. --- Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java b/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java index a75ec54960a..e4bce4bc10a 100644 --- a/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java +++ b/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java @@ -106,6 +106,7 @@ class AEtherspoutsEffect extends OneShotEffect { PlayerList playerList = game.getPlayerList(); playerList.setCurrent(game.getActivePlayerId()); Player player = game.getPlayer(game.getActivePlayerId()); + Player activePlayer = player; do { ArrayList permanentsToTop = new ArrayList<>(); ArrayList permanentsToBottom = new ArrayList<>(); @@ -134,7 +135,10 @@ class AEtherspoutsEffect extends OneShotEffect { } } TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on the top of library (last choosen will be the top most)")); - while (player.isInGame() && cards.size() > 1) { + while (cards.size() > 1) { + if (!player.isInGame()) { + return false; + } player.choose(Outcome.Neutral, cards, target, game); Card card = cards.get(target.getFirstTarget(), game); if (card != null) { @@ -173,6 +177,7 @@ class AEtherspoutsEffect extends OneShotEffect { target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on bottom of library (last choosen will be bottommost card)")); while (player.isInGame() && cards.size() > 1) { player.choose(Outcome.Neutral, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); if (card != null) { cards.remove(card); @@ -195,7 +200,7 @@ class AEtherspoutsEffect extends OneShotEffect { player.moveCardToLibraryWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD, false, false); } player = playerList.getNext(game); - } while (player != null && !player.getId().equals(game.getActivePlayerId())); + } while (player != null && !player.getId().equals(game.getActivePlayerId()) && activePlayer.isInGame()); return true; } return false; From e5da40976ca52b57fba12fdbc1813dad82c4eae7 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 1 Mar 2015 03:07:07 -0600 Subject: [PATCH 52/61] - Added Soul Foundry. Small changes. --- .../mage/sets/mirrodin/ExtraplanarLens.java | 28 +-- .../src/mage/sets/mirrodin/SoulFoundry.java | 188 ++++++++++++++++++ 2 files changed, 204 insertions(+), 12 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java diff --git a/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java b/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java index 77ff0f92646..9d85c13d076 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java +++ b/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java @@ -28,13 +28,13 @@ package mage.sets.mirrodin; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AddManaOfAnyTypeProducedEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -66,7 +66,7 @@ public class ExtraplanarLens extends CardImpl { // Whenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana to his or her mana pool of any type that land produced. this.addAbility(new ExtraplanarLensTriggeredAbility()); - + } public ExtraplanarLens(final ExtraplanarLens card) { @@ -144,17 +144,21 @@ class ExtraplanarLensTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { Permanent landTappedForMana = game.getPermanentOrLKIBattlefield(event.getSourceId()); Permanent extraplanarLens = game.getPermanent(getSourceId()); - if (extraplanarLens != null - && landTappedForMana != null) { - MageObject object = game.getObject(extraplanarLens.getImprinted().get(0)); - if (landTappedForMana.getName().equals(object.getName()) - && landTappedForMana.getCardType().contains(CardType.LAND)) { - ManaEvent mEvent = (ManaEvent) event; - for (Effect effect : getEffects()) { - effect.setValue("mana", mEvent.getMana()); + if (extraplanarLens != null + && landTappedForMana != null + && !extraplanarLens.getImprinted().isEmpty()) { + Card imprinted = game.getCard(extraplanarLens.getImprinted().get(0)); + if (imprinted != null + && game.getState().getZone(imprinted.getId()).equals(Zone.EXILED)) { + if (landTappedForMana.getName().equals(imprinted.getName()) + && landTappedForMana.getCardType().contains(CardType.LAND)) { + ManaEvent mEvent = (ManaEvent) event; + for (Effect effect : getEffects()) { + effect.setValue("mana", mEvent.getMana()); + } + getEffects().get(0).setTargetPointer(new FixedTarget(landTappedForMana.getId())); + return true; } - getEffects().get(0).setTargetPointer(new FixedTarget(landTappedForMana.getId())); - return true; } } return false; diff --git a/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java new file mode 100644 index 00000000000..21843f6da21 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java @@ -0,0 +1,188 @@ +/* + * 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.mirrodin; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +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.filter.FilterCard; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.EmptyToken; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public class SoulFoundry extends CardImpl { + + public SoulFoundry(UUID ownerId) { + super(ownerId, 246, "Soul Foundry", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}"); + this.expansionSetCode = "MRD"; + + // Imprint - When Soul Foundry enters the battlefield, you may exile a creature card from your hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SoulFoundryImprintEffect(), true, "Imprint - ")); + + // {X}, {tap}: Put a token that's a copy of the exiled card onto the battlefield. X is the converted mana cost of that card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulFoundryEffect(), new ManaCostsImpl("{X}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + } + + public SoulFoundry(final SoulFoundry card) { + super(card); + } + + @Override + public void adjustCosts(Ability ability, Game game) { + Permanent card = game.getPermanent(ability.getSourceId()); + if (card != null) { + if (card.getImprinted().size() > 0) { + Card imprinted = game.getCard(card.getImprinted().get(0)); + if (imprinted != null) { + ability.getManaCostsToPay().add(0, new GenericManaCost(imprinted.getManaCost().convertedManaCost())); + } + } + } + + // no {X} anymore as we already have imprinted the card with defined manacost + for (ManaCost cost : ability.getManaCostsToPay()) { + if (cost instanceof VariableCost) { + cost.setPaid(); + } + } + } + + @Override + public SoulFoundry copy() { + return new SoulFoundry(this); + } +} + +class SoulFoundryImprintEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("creature card from your hand"); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + + public SoulFoundryImprintEffect() { + super(Outcome.Neutral); + staticText = "you may exile a creature card from your hand"; + } + + public SoulFoundryImprintEffect(SoulFoundryImprintEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null) { + if (controller.getHand().size() > 0) { + TargetCard target = new TargetCard(Zone.HAND, filter); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.choose(Outcome.Benefit, controller.getHand(), target, game)) { + Card card = controller.getHand().get(target.getFirstTarget(), game); + if (card != null) { + controller.moveCardToExileWithInfo(card, source.getSourceId(), sourcePermanent.getLogName() + " (Imprint)", source.getSourceId(), game, Zone.HAND); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.imprint(card.getId(), game); + permanent.addInfo("imprint", CardUtil.addToolTipMarkTags("[Imprinted card - " + card.getLogName() + "]")); + } + } + } + } + return true; + } + return false; + } + + @Override + public SoulFoundryImprintEffect copy() { + return new SoulFoundryImprintEffect(this); + } +} + +class SoulFoundryEffect extends OneShotEffect { + + public SoulFoundryEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Put a token that's a copy of the exiled card onto the battlefield. X is the converted mana cost of that card"; + } + + public SoulFoundryEffect(final SoulFoundryEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent soulFoundry = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (soulFoundry != null + && soulFoundry.getImprinted() != null + && !soulFoundry.getImprinted().isEmpty()) { + Card imprinted = game.getCard(soulFoundry.getImprinted().get(0)); + if (imprinted != null + && game.getState().getZone(imprinted.getId()).equals(Zone.EXILED)) { + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(imprinted); + return token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + } + } + } + return false; + } + + @Override + public SoulFoundryEffect copy() { + return new SoulFoundryEffect(this); + } +} From 6e95e5ef70895825f799aae31d8e85cfb3f53f24 Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Sun, 1 Mar 2015 01:32:15 -0800 Subject: [PATCH 53/61] Removed pushed target folder from TinyLeadersDuel --- .../target/classes/.netbeans_automatic_build | 0 .../classes/mage/game/TinyLeadersDuel.class | Bin 1171 -> 0 bytes .../classes/mage/game/TinyLeadersDuelMatch.class | Bin 1305 -> 0 bytes .../classes/mage/game/TinyLeadersDuelType.class | Bin 996 -> 0 bytes .../target/mage-game-tinyleadersduel.jar | Bin 4259 -> 0 bytes .../target/maven-archiver/pom.properties | 5 ----- .../test-classes/.netbeans_automatic_build | 0 7 files changed, 5 deletions(-) delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/.netbeans_automatic_build delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuel.class delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuelMatch.class delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuelType.class delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/mage-game-tinyleadersduel.jar delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/maven-archiver/pom.properties delete mode 100644 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/test-classes/.netbeans_automatic_build diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/.netbeans_automatic_build b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/.netbeans_automatic_build deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuel.class b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/classes/mage/game/TinyLeadersDuel.class deleted file mode 100644 index 64eb001f6daa7cb2140bea1f19298270dd1030e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1171 zcma)5+iuf95Ivj3xwr`}p%)6ICFRoGR7;cxq(G$#1d5cl2%<=RW9x2GgJVaIgOq>b z0bYniB|daj;WfhR<$dTdhVVpf#tU4sN?bAOnDu7X46iw?V8f=O3&gr+)Viw)=1#?jJz)g z%IH|mq^;z+@)b?JT@s%{-=3?LeEo*inp!4bjXuv3FN3Jp5L~5 z#IY6XE;Jcb&L;v(edWKRRcA7!s8p#&Y8y!3yfBV<Kxk^y>!9DHnygewH)XoR zNj&+_#uTrRvnVm@Z{4wHs+SfweKnS}_g;RKdE}t;P$P-p_7~N}1{qWr*!(Mq0ULRg zuyuthsQ>ppwgy96U?b~01ZW+rSfdZZd+W4H1!^0i+5ushGhM4s zk%%kp5x)^AtrN9v6#k|yT_`IpcM(JoFKwx;fC$4`O0U z)Wiqyp^RsC8(NCSCOtEAF5mg)%sKn(_xGOw7V##AIA%25>p>sx$M66Tl}IX)iXnt_ zj104Cmr>%8I?gF)qZf}cuVF#MB11H1IhI^ym`)a|ydsPWuL`5erFmi$mE1d%R?YFU zsY8ayMy)Iu;swhQyY*^GxCgvslV+e$Gr4`pT}z#t%7{F%Jcg+@+yl!wF9=>1uD4ki zwx1x&pw)sT43llne<8up<4Nwy9U{pPPAXD?reGbDhUJN*x&gy@8&XGF${5@FWQriE zh9`91uhm^sY+LF!V;%lJ&AC(lk?TmHpB!41>TuODikljibUekfjukx9@f=wdm(%eA zt2);3QpY-8>Da)gjxB6!*wOJC#C`Igz&?j7ncK>fub$Zqqpe_7pUWFEOtdU*QO%d7 z_pAuHE+scV1S*DfG8L51q^FnMk)Bbk+tNC-`MGf0eW{k+6#kYw6{_6K^*GQ z?YeMGK>-O0*mi{=NZYD#hnj?*lCHno?7{zM?PPZ?OFpl9W5jd?mqL|eU-eBMt8oh2bFh7) zl**r>t_tnw#)$y8E-l+ASe~TpeLka04ONAg%M4d2?3GT1DYK~~Ow%mK>01DrZh}KU z$pJd8(m6zHG(CF(_Qkgkl2l&-AcZcZF+{tOzzf5)Q@^NcUBaLy!!5EYtk0wk)4G-z z%l$y;eflfH4Mc*aD_ElA7to5CbOYTD^km2$OJ^GBC99tKh8rQgYmu0QMsNm!&eH`J uh|Ut`XjT@nOvG1wVpBoNCb2Mb7{>(Bn#Lq<(?5hegb^Xll&@6kt&o01iA}$6?}ZakTe<-lKO}=P4wGsds9~T(QLP3|CN3~ z5>5O8{wU*2yGm`0d^x)_bMBclbMKFzU%vy`$HP44v64eY!yHx_42Cs^b%qUw+YFmb z-O6G+PeGNz%)%1LJa&91IuuA&&6YswNzhdSg@)s+7elYD!lrDylqofWj&xfxbU2Qa zsp!NR2&^_d*;95;ddhA({-~j3SA~P8L*+I{eRUwPUcH3Ey){opofAGmJ~}~vB(PRB zFF7c%@)u{UVL$(^M7{IJ!7%KoXAY0C^bdcvck+YOQ9w~(nd?S8fYCe+j90E4snFnI zbnIYP#~svk*bH|W?qN@-yCAsFP{0H7^xuUrX;H@#$^zvJ=8Sb(f=?Ue)w4m{m444Y zjzY)p@l>AtII%_-AdWcU_0Mj~$uu6$DB(3xr zQVeM;d4>!_mLbQWeI{}OCBiLegxe%oMII)JTDV3lFJl)K+WH1+RI#x@EPc|1>enes z@j_z*hoqLET*lftNB%-RKhEwUO?Sl#ixXC?Kz!UFE!qy>ORTkzB>e%}H_W}Ef%H!l qOw4A+g-26nw24%)P69V$0h_)okhZ?y%Ix9Isl(~mhrq3Ii{)Pf?ZpNF diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/mage-game-tinyleadersduel.jar b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target/mage-game-tinyleadersduel.jar deleted file mode 100644 index a19a3f23a90c4971b742245eaca445506e0af56f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4259 zcmbVP2{@GP*SC))Wf_DrWl0)@8CkMt9mY(Sk+Q~wvXAUSmTYC;$u_nuA(WcR8nTmp zNfL!D<+VotnIyj6@?F<^p6i+Co|$vb{hV{3`*;5iQk|5Hnus9DUIxlP20uTj@K3ms zj=X@Hrt(=NUJXb@iNBJ5Qv>c-gUf5GDJyB~2*Q=W!dqK3)CB~)Xw?PIw6}IV(h_p@L`N~gebE+#nP)loB_^&2&oRBZ1rzO+v49Q0DlwS z_s?OGCY;JUw^1rnA+^&| zE03BdJZt$ufoD}}?Sz1928RQ`YqqAVh;Uz6PkYB2_cW4@`-6ux2l7;(c@v{l$Q7e_ zXk{T-;X!oE@xX@rLB`ts97im5%g$iRWx_`x({iXP0YfTy|FUv6Z-1EXN#izJzp=FiXT8zEC)G{7j1vgARHC2jnJZCyyz)DGy8i zexz&=w03A1H4F29swn9VK)WL_5wfHjf!R-03s#~1pR9&@FI&NreY~=JNvIu_5!G?N zrHZ4`d<^|1g)th1Kx>_(z_uE7mNDa313EQjsV^ zT!iqFisfgqP$oZ#wUj;;w%ne|g^}`ILFZh{b2H>)%1KZRmZ2F@TNBMzIktrfS$Dcu zC1lnOiV~W2lBtT9y>rVNVS+76>!6y``8+l$fLGu#;64zy0fK~(xI=2w9azOfU^ zIemcQq8JGgQ8d|3Isdr^xeuyAxT%wweZH%)$PCr;%k!aa#VuU)UjtJ2Gh4MH$4Fq>1Yi6GgRw*W$+ zdK420oeqx~N1s@X%W#~I)Zs**xLqzEs8-Q(dI_1Y{d#bwmP=zcp-Z?RLz>s%B#USm zFO4Yb11h%niLP0^tkAo}EkUQzto!jSZO0V50zGJhy)FO3%Kb#uaY+e-q@nr~wjUE9 zOJrY9F)zNUhnVXcwAW+!lP=Yk1vBAs<@0awY0~{7n1WC-0SD9C=$#D^5M_v9ls_8KvFs5ad-WogI@_n(xu*Cwh=nam*-Sx8 zmJ`nSJh?Ku>?x?t8N6crNr|Vc9yA^<@?ybR2}Jr~kV~fdWrbyhrG<}1%m+c=>xJA5X2bGXowA4HGU~o!6N2xhWK_d?qP@_o*|~FZ@57T}D>~wyB3iaLq>x9* z`&w!EagV2(^E~RcIbHl}E}8Te8a5ei*D*roEMBUX7lSCWN($~vrnoMF!Zxm~8?l|f zs%aA3Ul5!zBzgG3SwjhwX>1!sWvSUX^9+S`+%UgO$&F6Kh!Hg31P5AD44TY*h(D7b zxozik{Wbz^D-9?&{IvSH?zGc4nY7o@pq#FcB=y8B$G(>%7gdbZ)xrZCx{#uos?Kkp zA63*GkKUwZ&D#j^@lOLuO7~Kk4{$lw-Sji-Wz0*MhF0_`$8M+VFGl;0k3|~g4ASwj z5sS`})t<>drFvzMN`zk@EG>VJr$fu6OD0kxfZ@|AXtw}KQs+gkya4cnYAPw1b;-6A2B z+&~X+2`9efCP@$C5J&JO_lp|Ym^!0v4`35V|E+B2Xd(Fja1fCLtO%w81a2bWgy$UA zJDkHD&pGzdwD8|(n#6zdXK!aC=xSqKq+N5bM~LqEG&nQl#)M!f*#iYlFr5|+_+qEg zT}Yd|!(5Yd5-P~s;%aX2FqTGP_(H7I%O#wf8zZ^`g{Zdn%=3pXu!Q&Zffdd?1tfMS zm8q!43N~-W)4D|q_I#*?=mF{g&cl^f9<#0w<6GFjl<0dm)P|fgU{mMuQ##g=3qlo# zoi_q23+R0T4ZZ3=3ea{Ha=-!KvQ*ue+Af1Jop|AQZh+S%CM?C2_;C&!whfT4pRcR! zea6u`2377KmJ$(pq2j~&*ZjAS^9ZM}rbl0f@@S?-j(hSCt+v^94{LVHyoriLsz8Ua zSSIecmMo&nCJtRb36-_J1Beij*WZF?=Er&pWaC`bU({?)gs%bZmR`7iJ{$J9C}e?L znxs#{C_TrA&73&OL8t~v>UpdN!fw1Z(-XJSw7_~{dMedr>7k~(*jY4rQ~F!bH_IoK zFQYEry=LDi3PWuqu(ydgQr%nLjy9%T{wN_%r3bQ1n?EV}XiQ`4Q*DQiE=G8A{;6qk z3SZTGbk^t!yuU5IJjrJxsorj1QW;k)y|5mI6|*kluJi zOALn@kb4_@98Z{NkAzU2PeFKg*3jz+M~+uR`oy4>H-_8Kt)&=U(tCAVQrzxj!`Me< zy>w5_O&2Gx*G3N~S%GLOkb6sj_i5=O16a^>?PT$svyt4^k4`~+^jP{$d;lK!8>voA z0wn!&BS2_@c7%@Tvpiw+YuE3X?X^Vvn-4-uv@2L&Ui|Qv*}ewppos}h%8n2w?#B;* z!o}a;bteLX5y50**W_mlL$KQ;ko&16p^4cQtS={i;rCjfKejacBPX22`%h^C8{HMG zZw?+r|LSZXH~rn?eh?;{?z@8Z&7(NL)ZaH{QpG$- Date: Sun, 1 Mar 2015 01:34:49 -0800 Subject: [PATCH 54/61] Fixed Tiny Leaders Duel Type Fixed missing class access level for TinyLeadersDuelType --- .gitignore | 5 ++++- Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml | 4 ++-- .../src/mage/game/TinyLeadersDuelMatch.java | 2 +- .../src/mage/game/TinyLeadersDuelType.java | 2 +- Mage.Server.Plugins/pom.xml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 25ca0b875c2..77d9061c6ab 100644 --- a/.gitignore +++ b/.gitignore @@ -84,4 +84,7 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target /Mage.Server/config/ai.please.cast.this.txt /Mage.Stats/target/ /Utils/*_unimplemented.txt -*.netbeans_automatic_build \ No newline at end of file +*.netbeans_automatic_build +*.txt +Mage.Client/serverlist.txt +Mage.Client/serverlist.txt \ No newline at end of file diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml index 1d6f0d0a37f..ccf6b733a04 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-4.0.0.xsd"> 4.0.0 @@ -18,7 +18,7 @@ ${project.groupId} mage - 1.3.0 + ${project.version} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java index d5b9ada2d5a..6349384e7c1 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java @@ -49,7 +49,7 @@ public class TinyLeadersDuelMatch extends MatchImpl { TinyLeadersDuel game = new TinyLeadersDuel(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife); game.setStartMessage(this.createGameStartMessage()); - //"Tucking a Tiny Leader is legal + //Tucking a Tiny Leader is legal game.setAlsoLibrary(false); this.initGame(game); games.add(game); diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java index 4c8055c8fb7..26d97bde138 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java @@ -34,7 +34,7 @@ import mage.game.match.MatchType; * * @author JRHerlehy */ -class TinyLeadersDuelType extends MatchType { +public class TinyLeadersDuelType extends MatchType { public TinyLeadersDuelType() { this.name = "Tiny Leaders Two Player Duel"; diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index eb8c3742aaf..5235f4a7a17 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -17,7 +17,7 @@ Mage.Deck.Constructed Mage.Deck.Limited - Mage.Game.CommanderDuel + Mage.Game.CommanderDuel Mage.Game.CommanderFreeForAll Mage.Game.FreeForAll Mage.Game.TwoPlayerDuel From b58c5b222667f8b9733fa1ca10bfb50d82b0b118 Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Sun, 1 Mar 2015 01:39:13 -0800 Subject: [PATCH 55/61] Fix .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 77d9061c6ab..e98869c519f 100644 --- a/.gitignore +++ b/.gitignore @@ -86,5 +86,4 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target /Utils/*_unimplemented.txt *.netbeans_automatic_build *.txt -Mage.Client/serverlist.txt Mage.Client/serverlist.txt \ No newline at end of file From a3b55d762532029af10b00c6fe9befefaee6fc6b Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Sun, 1 Mar 2015 01:47:18 -0800 Subject: [PATCH 56/61] Author Credit Fixed author credit to match my GitHub Name --- Mage/src/mage/game/GameTinyLeadersImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/mage/game/GameTinyLeadersImpl.java b/Mage/src/mage/game/GameTinyLeadersImpl.java index 8b421f21d7a..1094ef3cf76 100644 --- a/Mage/src/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/mage/game/GameTinyLeadersImpl.java @@ -48,7 +48,7 @@ import mage.util.CardUtil; /** * - * @author Justin + * @author JRHerlehy */ public abstract class GameTinyLeadersImpl extends GameImpl{ From 8a6da4abe34f389b91591c05585ce9076081aa02 Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Sun, 1 Mar 2015 02:31:56 -0800 Subject: [PATCH 57/61] Allowing Sideboarding Tiny Leaders allows sideboarding. Turned on sideboarding option. --- .../src/mage/game/TinyLeadersDuelType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java index 26d97bde138..7936d4e8909 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java @@ -43,7 +43,7 @@ public class TinyLeadersDuelType extends MatchType { this.numTeams = 0; this.useAttackOption = false; this.useRange = false; - this.sideboardingAllowed = false; + this.sideboardingAllowed = true; } protected TinyLeadersDuelType(final TinyLeadersDuelType matchType){ From 0a0a399259fdd1f9bc8a94fd39dfdef718629b0c Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Sun, 1 Mar 2015 03:12:25 -0800 Subject: [PATCH 58/61] Add Sideboard Allows a deck to have a sideboard. The player must have saved the deck with the CARDNAME of their commander in the NAME field of the Deck Editor to have it process as valid. --- .../src/mage/deck/TinyLeaders.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index 620160c0874..1680d0cf4b0 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -139,8 +139,14 @@ public class TinyLeaders extends DeckValidator { } } - if (deck.getSideboard().size() == 1) { - Card commander = (Card) deck.getSideboard().toArray()[0]; + if (deck.getSideboard().size() <= 11) { + Card commander = null; + + for (Card card : deck.getSideboard()) { + if (card.getName().equalsIgnoreCase(deck.getName())) { + commander = card; + } + } /** * 905.5b - Each card must have a converted mana cost of three of less. @@ -149,7 +155,8 @@ public class TinyLeaders extends DeckValidator { */ if (commander == null || commander.getManaCost().convertedManaCost() > 3) { - invalid.put("Commander", "Commander invalide "); + if (commander == null) invalid.put("Leader", "Please be sure to set your leader in the NAME field in the DECK EDITOR"); + if (commander != null && commander.getManaCost().convertedManaCost() > 3) invalid.put("Leader", "Commander CMC is Greater than 3"); return false; } if ((commander.getCardType().contains(CardType.CREATURE) && commander.getSupertype().contains("Legendary")) From 8849538723e843379b9fb7068bd871c912eb8221 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 1 Mar 2015 16:06:13 +0100 Subject: [PATCH 59/61] * Fixed that library selections could not be deslected if multiple cards have to be selected. --- .../src/mage/player/human/HumanPlayer.java | 26 +++++++++++++++---- .../target/common/TargetCardInLibrary.java | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index bef521c8db5..b29ea9d02d9 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -432,13 +432,29 @@ public class HumanPlayer extends PlayerImpl { if (target.getTargets().size() >= target.getNumberOfTargets()) { required = false; } - game.fireSelectTargetEvent(playerId, target.getMessage(), cards, required, getOptions(target, null)); + Map options = getOptions(target, null); + List chosen = target.getTargets(); + options.put("chosen", (Serializable)chosen); + List choosable = new ArrayList<>(); + for (UUID cardId : cards) { + if (target.canTarget(cardId, cards, game)) { + choosable.add(cardId); + } + } + if (!choosable.isEmpty()) { + options.put("choosable", (Serializable) choosable); + } + game.fireSelectTargetEvent(playerId, target.getMessage(), cards, required, options); waitForResponse(game); if (response.getUUID() != null) { - if (target.canTarget(response.getUUID(), cards, game)) { - target.addTarget(response.getUUID(), source, game); - if(target.doneChosing()){ - return true; + if (target.getTargets().contains(response.getUUID())) { // if already included remove it + target.remove(response.getUUID()); + } else { + if (target.canTarget(response.getUUID(), cards, game)) { + target.addTarget(response.getUUID(), source, game); + if (target.doneChosing()) { + return true; + } } } } else { diff --git a/Mage/src/mage/target/common/TargetCardInLibrary.java b/Mage/src/mage/target/common/TargetCardInLibrary.java index 1f9793e214a..69221dc3d8e 100644 --- a/Mage/src/mage/target/common/TargetCardInLibrary.java +++ b/Mage/src/mage/target/common/TargetCardInLibrary.java @@ -92,7 +92,7 @@ public class TargetCardInLibrary extends TargetCard { Collections.sort(cards, new CardNameComparator()); while (!isChosen() && !doneChosing()) { chosen = targets.size() >= minNumberOfTargets; - if (!player.choose(outcome, new CardsImpl(Zone.LIBRARY, cards), this, game)) { + if (!player.chooseTarget(outcome, new CardsImpl(Zone.LIBRARY, cards), this, null, game)) { return chosen; } chosen = targets.size() >= minNumberOfTargets; From 7ce1e6de19891cae2590ac522da1968cadf079f8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 1 Mar 2015 17:22:39 +0100 Subject: [PATCH 60/61] * Some minor changes. --- .../Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java | 8 ++++++-- .../src/main/java/mage/player/ai/ComputerPlayer.java | 3 +++ .../org/mage/test/cards/abilities/curses/CursesTest.java | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index 1680d0cf4b0..686eb4cb907 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -155,8 +155,12 @@ public class TinyLeaders extends DeckValidator { */ if (commander == null || commander.getManaCost().convertedManaCost() > 3) { - if (commander == null) invalid.put("Leader", "Please be sure to set your leader in the NAME field in the DECK EDITOR"); - if (commander != null && commander.getManaCost().convertedManaCost() > 3) invalid.put("Leader", "Commander CMC is Greater than 3"); + if (commander == null) { + invalid.put("Leader", "Please be sure to set your leader in the NAME field in the DECK EDITOR"); + } + if (commander != null && commander.getManaCost().convertedManaCost() > 3) { + invalid.put("Leader", "Commander CMC is Greater than 3"); + } return false; } if ((commander.getCardType().contains(CardType.CREATURE) && commander.getSupertype().contains("Legendary")) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index cb9a85dda21..df1a684fa0c 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1286,6 +1286,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (card != null) { target.addTarget(card.getId(), source, game); cardChoices.remove(card); + } else { + // We don't have any valid target to choose so stop choosing + return target.getTargets().size() < target.getNumberOfTargets(); } if (outcome.equals(Outcome.Neutral) && target.getTargets().size() > target.getNumberOfTargets() + (target.getMaxNumberOfTargets() - target.getNumberOfTargets()) / 2) { return true; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index f466f34a33b..9b34753da42 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -192,6 +192,9 @@ public class CursesTest extends CardTestPlayerBase { @Test public void testCurseOfMisfortune1() { removeAllCardsFromLibrary(playerA); + + // At the beginning of your upkeep, you may search your library for a Curse card that doesn't have the same name as a + // Curse attached to enchanted player, put it onto the battlefield attached to that player, then shuffle your library. addCard(Zone.LIBRARY, playerA, "Curse of Misfortunes", 2); addCard(Zone.HAND, playerA, "Curse of Misfortunes"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); From d231eee02469b60d1e7d09cf7c671b9d3890be33 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 1 Mar 2015 18:24:31 +0100 Subject: [PATCH 61/61] * Comet Storm - Fixed wrong damage handling for target players. --- Mage.Sets/src/mage/sets/worldwake/CometStorm.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/CometStorm.java b/Mage.Sets/src/mage/sets/worldwake/CometStorm.java index 4bcf99dbe54..c34084ceb02 100644 --- a/Mage.Sets/src/mage/sets/worldwake/CometStorm.java +++ b/Mage.Sets/src/mage/sets/worldwake/CometStorm.java @@ -53,8 +53,6 @@ public class CometStorm extends CardImpl { super(ownerId, 76, "Comet Storm", Rarity.MYTHIC, new CardType[]{CardType.INSTANT}, "{X}{R}{R}"); this.expansionSetCode = "WWK"; - this.color.setRed(true); - // Multikicker {1} this.addAbility(new MultikickerAbility("{1}")); @@ -96,8 +94,8 @@ class CometStormEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int damage = source.getManaCostsToPay().getX(); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { for (UUID uuid : this.getTargetPointer().getTargets(game, source)) { Permanent permanent = game.getPermanent(uuid); Player player = game.getPlayer(uuid); @@ -105,7 +103,7 @@ class CometStormEffect extends OneShotEffect { permanent.damage(damage, source.getSourceId(), game, false, true); } if (player != null) { - player.damage(damage, source.getSourceId(), game, true, false); + player.damage(damage, source.getSourceId(), game, false, true); } } return true;