From 9e941914088393783a25fc6e8251c8cb7bb2c53e Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:11:45 +0000 Subject: [PATCH 001/127] Implemented Vodalian War Machine --- .../src/mage/cards/v/VodalianWarMachine.java | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/v/VodalianWarMachine.java diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java new file mode 100644 index 0000000000..09e17dcfd9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -0,0 +1,208 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.v; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.watchers.Watcher; + +/** + * + * @author L_J + */ +public class VodalianWarMachine extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Merfolk you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.MERFOLK)); + } + + public VodalianWarMachine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Tap an untapped Merfolk you control: Vodalian War Machine can attack this turn as though it didn't have defender. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); + this.addAbility(ability); + + // Tap an untapped Merfolk you control: Vodalian War Machine gets +2/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); + + // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. + this.addAbility(new DiesTriggeredAbility(new VodalianWarMachineEffect(), false), new VodalianWarMachineWatcher()); + } + + public VodalianWarMachine(final VodalianWarMachine card) { + super(card); + } + + @Override + public VodalianWarMachine copy() { + return new VodalianWarMachine(this); + } +} + +class VodalianWarMachineEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); + + static { + filter.add(new SubtypePredicate(SubType.MERFOLK)); + } + + public VodalianWarMachineEffect() { + super(Outcome.Detriment); + staticText = "destroy all " + filter.getMessage(); + } + + public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { + super(effect); + } + + @Override + public VodalianWarMachineEffect copy() { + return new VodalianWarMachineEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (sourcePermanent != null) { + VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId()) != null) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId()).contains(permanent.getId())) { + permanent.destroy(source.getSourceId(), game, false); + } + } + return true; + } + } + return false; + } + +} + +class VodalianWarMachineWatcher extends Watcher { + + public Map> tappedMerfolkIds = new HashMap<>(); + + public VodalianWarMachineWatcher() { + super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public VodalianWarMachineWatcher(final VodalianWarMachineWatcher watcher) { + super(watcher); + this.tappedMerfolkIds = watcher.tappedMerfolkIds; + } + + @Override + public VodalianWarMachineWatcher copy() { + return new VodalianWarMachineWatcher(this); + } + + public Set getTappedMerfolkIds(UUID sourceId) { + return tappedMerfolkIds.get(sourceId); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { + if (event.getSourceId() != null) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null) { + Ability ability = stackAbility.getStackAbility(); + if (ability != null) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapTargetCost && cost.isPaid()) { + TapTargetCost tapCost = (TapTargetCost) cost; + if (tapCost.getTarget().isChosen()) { + Set toAdd; + if (tappedMerfolkIds.get(event.getSourceId()) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = tappedMerfolkIds.get(event.getSourceId()); + } + for (UUID targetId : tapCost.getTarget().getTargets()) { + toAdd.add(targetId); + } + tappedMerfolkIds.put(event.getSourceId(), toAdd); + break; + } + } + } + } + } + } + } + } + + @Override + public void reset() { + super.reset(); + tappedMerfolkIds.clear(); + } +} From 93280d1835bb0d287bba1bbfb4eb44f312e53463 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:13:38 +0000 Subject: [PATCH 002/127] Implemented Vodalian War Machine --- .../main/java/mage/abilities/costs/common/TapTargetCost.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java index ef095d353c..82ed1c371f 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java @@ -78,6 +78,10 @@ public class TapTargetCost extends CostImpl { public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { return target.canChoose(sourceId, controllerId, game); } + + public TargetControlledPermanent getTarget() { + return target; + } @Override public TapTargetCost copy() { From 7b42c36644a310aa1605a944fed8c30315648957 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:14:50 +0000 Subject: [PATCH 003/127] Implemented Vodalian War Machine (Fallen Empires compete) --- Mage.Sets/src/mage/sets/FallenEmpires.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/FallenEmpires.java b/Mage.Sets/src/mage/sets/FallenEmpires.java index 224a062831..a6a5870493 100644 --- a/Mage.Sets/src/mage/sets/FallenEmpires.java +++ b/Mage.Sets/src/mage/sets/FallenEmpires.java @@ -268,6 +268,7 @@ public class FallenEmpires extends ExpansionSet { cards.add(new SetCardInfo("Vodalian Soldiers", 63, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vodalian Soldiers", 64, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vodalian Soldiers", 65, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vodalian War Machine", 66, Rarity.RARE, mage.cards.v.VodalianWarMachine.class)); cards.add(new SetCardInfo("Zelyon Sword", 176, Rarity.RARE, mage.cards.z.ZelyonSword.class)); } } From 155fbb86636e25a5170e60daf9b90515cbb18453 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 21 Feb 2018 12:02:58 -0600 Subject: [PATCH 004/127] - Added requested card Parallel Thoughts --- .../src/mage/cards/p/ParallelThoughts.java | 181 ++++++++++++++++++ Mage.Sets/src/mage/sets/Scourge.java | 1 + 2 files changed, 182 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/ParallelThoughts.java diff --git a/Mage.Sets/src/mage/cards/p/ParallelThoughts.java b/Mage.Sets/src/mage/cards/p/ParallelThoughts.java new file mode 100644 index 0000000000..8a803ed86f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParallelThoughts.java @@ -0,0 +1,181 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.p; + +import java.util.Arrays; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; +import mage.util.RandomUtil; + +/** + * + * @author jeffwadsworth + */ +public class ParallelThoughts extends CardImpl { + + public ParallelThoughts(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); + + // When Parallel Thoughts enters the battlefield, search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ParallelThoughtsSearchEffect())); + + // If you would draw a card, you may instead put the top card of the pile you exiled into your hand. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ParallelThoughtsReplacementEffect())); + + } + + public ParallelThoughts(final ParallelThoughts card) { + super(card); + } + + @Override + public ParallelThoughts copy() { + return new ParallelThoughts(this); + } +} + +class ParallelThoughtsSearchEffect extends OneShotEffect { + + ParallelThoughtsSearchEffect() { + super(Outcome.Neutral); + this.staticText = "search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library"; + } + + ParallelThoughtsSearchEffect(final ParallelThoughtsSearchEffect effect) { + super(effect); + } + + @Override + public ParallelThoughtsSearchEffect copy() { + return new ParallelThoughtsSearchEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + Cards cardsInExilePile = new CardsImpl(); + if (controller != null + && permanent != null) { + TargetCardInLibrary target = new TargetCardInLibrary(7, new FilterCard()); + if (controller.searchLibrary(target, game)) { + for (UUID targetId : target.getTargets()) { + Card card = controller.getLibrary().getCard(targetId, game); + if (card != null) { + cardsInExilePile.add(card); + } + } + // shuffle that exiled pile + + UUID[] shuffled = cardsInExilePile.toArray(new UUID[0]); + for (int n = shuffled.length - 1; n > 0; n--) { + int r = RandomUtil.nextInt(n + 1); + UUID temp = shuffled[n]; + shuffled[n] = shuffled[r]; + shuffled[r] = temp; + } + cardsInExilePile.clear(); + cardsInExilePile.addAll(Arrays.asList(shuffled)); + + // move to exile zone and turn face down + + for (Card card : cardsInExilePile.getCards(game)) { + controller.moveCardsToExile(card, source, game, false, CardUtil.getCardExileZoneId(game, source), permanent.getLogName()); + card.setFaceDown(true, game); + } + + // shuffle controller library + + controller.shuffleLibrary(source, game); + } + return true; + } + return false; + } +} + +class ParallelThoughtsReplacementEffect extends ReplacementEffectImpl { + + ParallelThoughtsReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.DrawCard); + staticText = "If you would draw a card, you may instead put the top card of the pile you exiled with Parallel Thoughts into your hand"; + } + + ParallelThoughtsReplacementEffect(final ParallelThoughtsReplacementEffect effect) { + super(effect); + } + + @Override + public ParallelThoughtsReplacementEffect copy() { + return new ParallelThoughtsReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null + && !game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)).getCards(game).isEmpty()) { + Card card = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)).getCards(game).iterator().next(); + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); + } + } + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getControllerId().equals(event.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index ad8585d582..8dff799906 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -142,6 +142,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Nefashu", 70, Rarity.RARE, mage.cards.n.Nefashu.class)); cards.add(new SetCardInfo("Noble Templar", 19, Rarity.COMMON, mage.cards.n.NobleTemplar.class)); cards.add(new SetCardInfo("One with Nature", 125, Rarity.UNCOMMON, mage.cards.o.OneWithNature.class)); + cards.add(new SetCardInfo("Parallel Thoughts", 44, Rarity.RARE, mage.cards.p.ParallelThoughts.class)); cards.add(new SetCardInfo("Pemmin's Aura", 45, Rarity.UNCOMMON, mage.cards.p.PemminsAura.class)); cards.add(new SetCardInfo("Primitive Etchings", 126, Rarity.RARE, mage.cards.p.PrimitiveEtchings.class)); cards.add(new SetCardInfo("Putrid Raptor", 71, Rarity.UNCOMMON, mage.cards.p.PutridRaptor.class)); From 3a2fe879dcdb1fdcaf6a4adc2542c0b7f5addb4c Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:42:00 +0000 Subject: [PATCH 005/127] Implemented Glyph of Doom --- Mage.Sets/src/mage/cards/g/GlyphOfDoom.java | 153 ++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GlyphOfDoom.java diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java b/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java new file mode 100644 index 0000000000..748661c0a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java @@ -0,0 +1,153 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.g; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.BlockedAttackerWatcher; + +/** + * + * @author LevelX2 & L_J + */ +public class GlyphOfDoom extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Wall creature"); + + static { + filter.add(new SubtypePredicate(SubType.WALL)); + } + + public GlyphOfDoom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Choose target Wall creature. At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn. + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addEffect(new InfoEffect("Choose target Wall creature")); + this.getSpellAbility().addEffect(new GlyphOfDoomCreateDelayedTriggeredAbilityEffect()); + this.getSpellAbility().addWatcher(new BlockedAttackerWatcher()); + } + + public GlyphOfDoom(final GlyphOfDoom card) { + super(card); + } + + @Override + public GlyphOfDoom copy() { + return new GlyphOfDoom(this); + } +} + +class GlyphOfDoomCreateDelayedTriggeredAbilityEffect extends OneShotEffect { + + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect() { + super(Outcome.Benefit); + this.staticText = "At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn"; + } + + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect(final GlyphOfDoomCreateDelayedTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect copy() { + return new GlyphOfDoomCreateDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!source.getTargets().isEmpty() && source.getFirstTarget() != null) { + MageObjectReference mor = new MageObjectReference(source.getFirstTarget(), game); + AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new GlyphOfDoomEffect(mor)); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } + return false; + } +} + +class GlyphOfDoomEffect extends OneShotEffect { + + MageObjectReference targetCreature; + + public GlyphOfDoomEffect(MageObjectReference targetCreature) { + super(Outcome.DestroyPermanent); + this.targetCreature = targetCreature; + } + + public GlyphOfDoomEffect(final GlyphOfDoomEffect effect) { + super(effect); + targetCreature = effect.targetCreature; + } + + @Override + public GlyphOfDoomEffect copy() { + return new GlyphOfDoomEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && targetCreature != null) { + BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName()); + if (watcher != null) { + List toDestroy = new ArrayList<>(); + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) { + if (!creature.getId().equals(targetCreature.getSourceId())) { + if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), targetCreature, game)) { + toDestroy.add(creature); + } + } + } + for (Permanent creature : toDestroy) { + creature.destroy(source.getSourceId(), game, false); + } + return true; + } + } + return false; + } +} From a61fd3ecd5711ec6ba437bb1161cc14b6ccab0d8 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:42:50 +0000 Subject: [PATCH 006/127] Implemented Wood Elemental --- Mage.Sets/src/mage/cards/w/WoodElemental.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WoodElemental.java diff --git a/Mage.Sets/src/mage/cards/w/WoodElemental.java b/Mage.Sets/src/mage/cards/w/WoodElemental.java new file mode 100644 index 0000000000..492ce5485d --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WoodElemental.java @@ -0,0 +1,133 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author L_J + */ +public class WoodElemental extends CardImpl { + + public WoodElemental(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // As Wood Elemental enters the battlefield, sacrifice any number of untapped Forests. + this.addAbility(new AsEntersBattlefieldAbility(new WoodElementalEffect())); + + // Wood Elemental's power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); + } + + public WoodElemental(final WoodElemental card) { + super(card); + } + + @Override + public WoodElemental copy() { + return new WoodElemental(this); + } +} + +class WoodElementalEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped Forests you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.FOREST)); + } + + public WoodElementalEffect() { + super(Outcome.Sacrifice); + staticText = "sacrifice any number of untapped Forests"; + } + + public WoodElementalEffect(final WoodElementalEffect effect) { + super(effect); + } + + @Override + public WoodElementalEffect copy() { + return new WoodElementalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card sourceCard = game.getCard(source.getSourceId()); + if (controller != null && sourceCard != null) { + Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, filter, true); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.chooseTarget(Outcome.Detriment, target, source, game)) { + if (!target.getTargets().isEmpty()) { + int sacrificedForests = target.getTargets().size(); + game.informPlayers(controller.getLogName() + " sacrifices " + sacrificedForests + " untapped Forests for " + sourceCard.getLogName()); + for (UUID targetId : target.getTargets()) { + Permanent targetPermanent = game.getPermanent(targetId); + if (targetPermanent != null) { + targetPermanent.sacrifice(source.getSourceId(), game); + } + } + game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + return true; + } + } + } + return false; + } +} From 4c6e13d2839a1982b42c9f01c75ade036f947673 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:44:13 +0000 Subject: [PATCH 007/127] Implemented Glyph of Doom and Wood Elemental --- Mage.Sets/src/mage/sets/Legends.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index ffd0535857..779015f3cd 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -133,6 +133,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Giant Strength", 147, Rarity.COMMON, mage.cards.g.GiantStrength.class)); cards.add(new SetCardInfo("Giant Turtle", 102, Rarity.COMMON, mage.cards.g.GiantTurtle.class)); cards.add(new SetCardInfo("Glyph of Destruction", 148, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class)); + cards.add(new SetCardInfo("Glyph of Doom", 14, Rarity.COMMON, mage.cards.g.GlyphOfDoom.class)); cards.add(new SetCardInfo("Glyph of Life", 184, Rarity.COMMON, mage.cards.g.GlyphOfLife.class)); cards.add(new SetCardInfo("Gravity Sphere", 149, Rarity.RARE, mage.cards.g.GravitySphere.class)); cards.add(new SetCardInfo("Great Defender", 185, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); @@ -299,6 +300,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Winds of Change", 169, Rarity.UNCOMMON, mage.cards.w.WindsOfChange.class)); cards.add(new SetCardInfo("Winter Blast", 127, Rarity.RARE, mage.cards.w.WinterBlast.class)); cards.add(new SetCardInfo("Wolverine Pack", 128, Rarity.COMMON, mage.cards.w.WolverinePack.class)); + cards.add(new SetCardInfo("Wood Elemental", 129, Rarity.RARE, mage.cards.w.WoodElemental.class)); cards.add(new SetCardInfo("Xira Arien", 310, Rarity.RARE, mage.cards.x.XiraArien.class)); cards.add(new SetCardInfo("Zephyr Falcon", 86, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); } From 8b6d443fd385825698eb68bbeafd75f603d1afd0 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:44:51 +0000 Subject: [PATCH 008/127] Implemented Wood Elemental --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 03ff7d240a..c60ecead21 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -316,6 +316,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Wild Aesthir", 34, Rarity.COMMON, mage.cards.w.WildAesthir.class)); cards.add(new SetCardInfo("Wild Griffin", 35, Rarity.COMMON, mage.cards.w.WildGriffin.class)); cards.add(new SetCardInfo("Wild Ox", 174, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); + cards.add(new SetCardInfo("Wood Elemental", 175, Rarity.RARE, mage.cards.w.WoodElemental.class)); cards.add(new SetCardInfo("Xenic Poltergeist", 104, Rarity.UNCOMMON, mage.cards.x.XenicPoltergeist.class)); cards.add(new SetCardInfo("Yotian Soldier", 240, Rarity.COMMON, mage.cards.y.YotianSoldier.class)); cards.add(new SetCardInfo("Zombie Master", 105, Rarity.UNCOMMON, mage.cards.z.ZombieMaster.class)); From f86cc8a04ad2fb1ec8ae16656533753f7c26ea8e Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 22:07:34 +0000 Subject: [PATCH 009/127] Implemented Orim's Prayer --- Mage.Sets/src/mage/cards/o/OrimsPrayer.java | 94 +++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/o/OrimsPrayer.java diff --git a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java new file mode 100644 index 0000000000..4ad6e297a7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java @@ -0,0 +1,94 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.o; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.dynamicvalue.common.AttackingCreatureCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +/** + * + * @author L_J + */ +public class OrimsPrayer extends CardImpl { + + public OrimsPrayer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); + + // Whenever one or more creatures attack you, you gain 1 life for each attacking creature. + this.addAbility(new OrimsPrayerTriggeredAbility()); + } + + public OrimsPrayer(final OrimsPrayer card) { + super(card); + } + + @Override + public OrimsPrayer copy() { + return new OrimsPrayer(this); + } +} + +class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { + + public OrimsPrayerTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainLifeEffect(new AttackingCreatureCount())); + } + + public OrimsPrayerTriggeredAbility(final OrimsPrayerTriggeredAbility ability) { + super(ability); + } + + @Override + public OrimsPrayerTriggeredAbility copy() { + return new OrimsPrayerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getCombat().getDefenders().contains(getControllerId()); + } + + @Override + public String getRule() { + return "Whenever one or more creatures attack you, " + super.getRule(); + } +} From 35a065cb326a64a7b2ee3b56a265ab94b2f06643 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 22:08:25 +0000 Subject: [PATCH 010/127] Implemented Orim's Prayer --- Mage.Sets/src/mage/sets/Tempest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Tempest.java b/Mage.Sets/src/mage/sets/Tempest.java index e764804b94..26a956063f 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -219,6 +219,7 @@ public class Tempest extends ExpansionSet { cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); cards.add(new SetCardInfo("Oracle en-Vec", 243, Rarity.RARE, mage.cards.o.OracleEnVec.class)); cards.add(new SetCardInfo("Orim, Samite Healer", 244, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); + cards.add(new SetCardInfo("Orim's Prayer", 245, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); cards.add(new SetCardInfo("Overrun", 137, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); cards.add(new SetCardInfo("Pacifism", 246, Rarity.COMMON, mage.cards.p.Pacifism.class)); cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); From f31b1ab7e35c4ea3e2adc6cca3bfd1ff550aa6af Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Thu, 22 Feb 2018 00:02:23 +0100 Subject: [PATCH 011/127] Improve some ability text --- .../src/mage/cards/b/BrassTalonChimera.java | 2 +- Mage.Sets/src/mage/cards/c/CallToGlory.java | 43 ++----------------- Mage.Sets/src/mage/cards/e/ExaltedDragon.java | 2 +- Mage.Sets/src/mage/cards/k/KaerveksSpite.java | 2 +- Mage.Sets/src/mage/cards/l/Leviathan.java | 2 +- Mage.Sets/src/mage/cards/l/LyevDecree.java | 4 +- .../src/mage/cards/m/MerfolkRaiders.java | 4 +- .../src/mage/cards/m/MilitantInquisitor.java | 2 +- Mage.Sets/src/mage/cards/s/SpireTracer.java | 2 +- Mage.Sets/src/mage/cards/s/SpiritCairn.java | 2 +- Mage.Sets/src/mage/cards/s/SpiritMirror.java | 2 +- .../common/CantBeRegeneratedTargetEffect.java | 1 - .../common/CreateTokenCopyTargetEffect.java | 2 +- .../common/PreventDamageByTargetEffect.java | 2 +- ...NextDamageFromChosenSourceToYouEffect.java | 2 +- ...ardOfLibraryIntoGraveEachPlayerEffect.java | 9 ++-- .../TapAllTargetPlayerControlsEffect.java | 2 +- .../cost/SpellsCostIncreasementAllEffect.java | 2 +- .../abilities/keyword/DefenderAbility.java | 2 +- .../abilities/keyword/LifelinkAbility.java | 2 +- .../abilities/keyword/ProtectionAbility.java | 2 +- .../abilities/keyword/TotemArmorAbility.java | 2 +- .../main/java/mage/filter/StaticFilters.java | 6 +++ 23 files changed, 37 insertions(+), 64 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java b/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java index fc3b22aba5..7620727df5 100644 --- a/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java +++ b/Mage.Sets/src/mage/cards/b/BrassTalonChimera.java @@ -53,7 +53,7 @@ import java.util.UUID; */ public class BrassTalonChimera extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Chimera creature you control"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Chimera creature"); static { filter.add(new SubtypePredicate(SubType.CHIMERA)); diff --git a/Mage.Sets/src/mage/cards/c/CallToGlory.java b/Mage.Sets/src/mage/cards/c/CallToGlory.java index 9d678be515..b2c31e6112 100644 --- a/Mage.Sets/src/mage/cards/c/CallToGlory.java +++ b/Mage.Sets/src/mage/cards/c/CallToGlory.java @@ -28,21 +28,16 @@ package mage.cards.c; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.UntapAllEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @author Loki @@ -57,8 +52,9 @@ public class CallToGlory extends CardImpl { public CallToGlory(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); - - this.getSpellAbility().addEffect(new CalltoGloryFirstEffect()); + + //Untap all creatures you control. Samurai creatures you control get +1/+1 until end of turn. + this.getSpellAbility().addEffect(new UntapAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE)); this.getSpellAbility().addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn, filter, false)); } @@ -70,35 +66,4 @@ public class CallToGlory extends CardImpl { public CallToGlory copy() { return new CallToGlory(this); } - -} - -class CalltoGloryFirstEffect extends OneShotEffect { - - public CalltoGloryFirstEffect() { - super(Outcome.Untap); - staticText = "Untap all creatures you control"; - } - - public CalltoGloryFirstEffect(final CalltoGloryFirstEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Permanent creature : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game)) { - creature.untap(game); - } - return true; - } - return false; - } - - @Override - public CalltoGloryFirstEffect copy() { - return new CalltoGloryFirstEffect(this); - } - } diff --git a/Mage.Sets/src/mage/cards/e/ExaltedDragon.java b/Mage.Sets/src/mage/cards/e/ExaltedDragon.java index 55ee01bf79..941f15b0e4 100644 --- a/Mage.Sets/src/mage/cards/e/ExaltedDragon.java +++ b/Mage.Sets/src/mage/cards/e/ExaltedDragon.java @@ -80,7 +80,7 @@ class ExaltedDragonCostToAttackBlockEffect extends PayCostToAttackBlockEffectImp ExaltedDragonCostToAttackBlockEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledLandPermanent("a land")))); - staticText = "{this} can't attack unless you sacrifice a land (This cost is paid as attackers are declared.)"; + staticText = "{this} can't attack unless you sacrifice a land. (This cost is paid as attackers are declared.)"; } ExaltedDragonCostToAttackBlockEffect(ExaltedDragonCostToAttackBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java index 349fa1ccf8..8602a8c41b 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java @@ -16,7 +16,7 @@ public class KaerveksSpite extends CardImpl { super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}"); // As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand. - this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("all permanents you control"))); + this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("permanents you control"))); this.getSpellAbility().addCost(new DiscardHandCost()); // Target player loses 5 life. diff --git a/Mage.Sets/src/mage/cards/l/Leviathan.java b/Mage.Sets/src/mage/cards/l/Leviathan.java index cf79670e67..7f5d4b1cb2 100644 --- a/Mage.Sets/src/mage/cards/l/Leviathan.java +++ b/Mage.Sets/src/mage/cards/l/Leviathan.java @@ -118,7 +118,7 @@ class LeviathanCostToAttackBlockEffect extends PayCostToAttackBlockEffectImpl { LeviathanCostToAttackBlockEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, new SacrificeTargetCost(new TargetControlledPermanent(2, 2, filter, false))); - staticText = "{this} can't attack unless you sacrifice two Islands (This cost is paid as attackers are declared.)"; + staticText = "{this} can't attack unless you sacrifice two Islands. (This cost is paid as attackers are declared.)"; } LeviathanCostToAttackBlockEffect(LeviathanCostToAttackBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LyevDecree.java b/Mage.Sets/src/mage/cards/l/LyevDecree.java index c332e4bf09..70326ded26 100644 --- a/Mage.Sets/src/mage/cards/l/LyevDecree.java +++ b/Mage.Sets/src/mage/cards/l/LyevDecree.java @@ -47,7 +47,7 @@ import mage.target.common.TargetCreaturePermanent; public class LyevDecree extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponent controls"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponents control"); static { filter.add(new ControllerPredicate(TargetController.OPPONENT)); } @@ -56,7 +56,7 @@ public class LyevDecree extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); - // Detain up to two target creatures your opponent controls. + // Detain up to two target creatures your opponents control. this.getSpellAbility().addEffect(new DetainTargetEffect()); Target target = new TargetCreaturePermanent(0,2,filter,false); this.getSpellAbility().addTarget(target); diff --git a/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java b/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java index 21611866db..09e408b51b 100644 --- a/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java +++ b/Mage.Sets/src/mage/cards/m/MerfolkRaiders.java @@ -50,10 +50,10 @@ public class MerfolkRaiders extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Phasing - this.addAbility(PhasingAbility.getInstance()); // Islandwalk this.addAbility(new IslandwalkAbility()); + // Phasing + this.addAbility(PhasingAbility.getInstance()); } public MerfolkRaiders(final MerfolkRaiders card) { diff --git a/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java b/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java index 05df6afc78..9b3e812798 100644 --- a/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java +++ b/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java @@ -49,7 +49,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class MilitantInquisitor extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("equipment you control"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); static { filter.add(new CardTypePredicate(CardType.ARTIFACT)); diff --git a/Mage.Sets/src/mage/cards/s/SpireTracer.java b/Mage.Sets/src/mage/cards/s/SpireTracer.java index 8c36e4d18c..7846832cbf 100644 --- a/Mage.Sets/src/mage/cards/s/SpireTracer.java +++ b/Mage.Sets/src/mage/cards/s/SpireTracer.java @@ -76,7 +76,7 @@ class CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect extends RestrictionE public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect() { super(Duration.WhileOnBattlefield); - staticText = "Can't be blocked except by creatures with flying or reach"; + staticText = "{this} can't be blocked except by creatures with flying or reach"; } public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect(final CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SpiritCairn.java b/Mage.Sets/src/mage/cards/s/SpiritCairn.java index 0153958df4..1d1f92e0b4 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritCairn.java +++ b/Mage.Sets/src/mage/cards/s/SpiritCairn.java @@ -51,7 +51,7 @@ public class SpiritCairn extends CardImpl { // Whenever a player discards a card, you may pay {W}. If you do, create a 1/1 white Spirit creature token with flying. this.addAbility(new SimpleTriggeredAbility(Zone.BATTLEFIELD, GameEvent.EventType.DISCARDED_CARD, new DoIfCostPaid(new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl("{W}")), - "Whenever a player discards a card, you ", + "Whenever a player discards a card, ", false, false)); } diff --git a/Mage.Sets/src/mage/cards/s/SpiritMirror.java b/Mage.Sets/src/mage/cards/s/SpiritMirror.java index 77edaee312..e9c03b254c 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritMirror.java +++ b/Mage.Sets/src/mage/cards/s/SpiritMirror.java @@ -66,7 +66,7 @@ public class SpiritMirror extends CardImpl { this.addAbility(new ConditionalTriggeredAbility( new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new ReflectionToken()), TargetController.YOU, false), new PermanentsOnTheBattlefieldCondition(filterToken, ComparisonType.EQUAL_TO, 0, false), - "At the beginning of your upkeep, if there are no Reflection tokens on the battlefield, create a 2/2 white Reflection creature token")); + "At the beginning of your upkeep, if there are no Reflection tokens on the battlefield, create a 2/2 white Reflection creature token.")); // {0}: Destroy target Reflection. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new GenericManaCost(0)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java index a0219501fb..d844803f73 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java @@ -81,7 +81,6 @@ public class CantBeRegeneratedTargetEffect extends ContinuousRuleModifyingEffect } sb.append(" can't be regenerated"); if (!duration.toString().isEmpty()) { - sb.append(' '); if (duration == Duration.EndOfTurn) { sb.append(" this turn"); } else { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 993422a0d4..6bb2ce18c5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -286,7 +286,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { sb.append("tokens that are copies of target "); } if (!mode.getTargets().isEmpty()) { - sb.append(mode.getTargets().get(0).getMessage()); + sb.append(mode.getTargets().get(0).getTargetName()); } else { throw new UnsupportedOperationException("Using default rule generation of target effect without having a target object"); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java index e26affa441..081cd81b8c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByTargetEffect.java @@ -96,7 +96,7 @@ public class PreventDamageByTargetEffect extends PreventionEffectImpl { StringBuilder sb = new StringBuilder(); sb.append("Prevent all"); if (onlyCombat) { - sb.append(" combat "); + sb.append(" combat"); } sb.append(" damage target "); sb.append(mode.getTargets().get(0).getTargetName()).append(" would deal ").append(duration.toString()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java index 6164d9c2de..86f10acf76 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java @@ -71,7 +71,7 @@ public class PreventNextDamageFromChosenSourceToYouEffect extends PreventionEffe StringBuilder sb = new StringBuilder("The next time a ").append(targetSource.getFilter().getMessage()); sb.append(" of your choice would deal damage to you"); if (duration == Duration.EndOfTurn) { - sb.append(" this turn"); + sb.append(" this turn"); } sb.append(", prevent that damage"); return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java index 77ebb1377d..0a378703ab 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java @@ -123,9 +123,12 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect throw new UnsupportedOperationException("TargetController type not supported."); } sb.append("puts the top "); - sb.append(CardUtil.numberToText(numberCards.toString(), "a")); - sb.append(" card"); - sb.append(numberCards.toString().equals("1") ? "" : "s"); + if(numberCards.toString().equals("1")) { + sb.append("card"); + } else { + sb.append(CardUtil.numberToText(numberCards.toString())); + sb.append(" cards"); + } sb.append(" of his or her library into his or her graveyard"); return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java index 08ca0c506f..ac2bee4e1d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TapAllTargetPlayerControlsEffect.java @@ -80,6 +80,6 @@ public class TapAllTargetPlayerControlsEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "tap all " + filter.toString() + " target " + mode.getTargets().get(0).getMessage() + " controls"; + return "tap all " + filter.toString() + " target " + mode.getTargets().get(0).getTargetName() + " controls"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java index 95d6d6e524..6b371b0dc4 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java @@ -48,7 +48,7 @@ public class SpellsCostIncreasementAllEffect extends CostModificationEffectImpl private int amount; public SpellsCostIncreasementAllEffect(int amount) { - this(new FilterCard("All Spells "), amount); + this(new FilterCard("Spells"), amount); } public SpellsCostIncreasementAllEffect(FilterCard filter, int amount) { diff --git a/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java b/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java index 293ad9adb2..a6cf1ce515 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DefenderAbility.java @@ -56,7 +56,7 @@ public class DefenderAbility extends StaticAbility implements MageSingleton { @Override public String getRule() { - return "Defender"; + return "defender"; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java b/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java index c22dde451a..7a12b66426 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/LifelinkAbility.java @@ -56,7 +56,7 @@ public class LifelinkAbility extends StaticAbility implements MageSingleton { @Override public String getRule() { - return "Lifelink (Damage dealt by this creature also causes you to gain that much life.)"; + return "lifelink (Damage dealt by this creature also causes you to gain that much life.)"; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 5f1b480406..46471af292 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -89,7 +89,7 @@ public class ProtectionAbility extends StaticAbility { @Override public String getRule() { - return "Protection from " + filter.getMessage() + (removeAuras ? "" : ". This effect doesn't remove auras."); + return "protection from " + filter.getMessage() + (removeAuras ? "" : ". This effect doesn't remove auras."); } public boolean canTarget(MageObject source, Game game) { diff --git a/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java b/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java index 604f164504..f31280d1bf 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java @@ -65,7 +65,7 @@ public class TotemArmorAbility extends SimpleStaticAbility { @Override public String getRule() { - return "Totem Armor (If enchanted creature would be destroyed, instead remove all damage from it and destroy this Aura.)"; + return "Totem armor (If enchanted creature would be destroyed, instead remove all damage from it and destroy this Aura.)"; } } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index c724734fa8..e6ccc99657 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -179,7 +179,13 @@ public final class StaticFilters { static { FILTER_CONTROLLED_CREATURE.setLockedFilter(true); + } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_CREATURES = new FilterControlledCreaturePermanent("creatures you control"); + + static { + FILTER_CONTROLLED_CREATURES.setLockedFilter(true); } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_A_CREATURE = new FilterControlledCreaturePermanent("a creature you control"); static { From bf3682fa77d708cda2f5e1e67307e28abba23a29 Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Thu, 22 Feb 2018 00:09:51 +0100 Subject: [PATCH 012/127] Move confirmation OptionPane from SessionImpl to ConsolePanel --- .../main/java/mage/remote/SessionImpl.java | 54 ++++++------------- .../mage/server/console/ConsolePanel.java | 42 +++++++++++++-- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 187b8179c1..1f7bca2c0c 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -32,7 +32,6 @@ import java.lang.reflect.UndeclaredThrowableException; import java.net.*; import java.util.*; import java.util.concurrent.TimeUnit; -import javax.swing.*; import mage.MageException; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; @@ -1437,12 +1436,9 @@ public class SessionImpl implements Session { @Override public boolean endUserSession(String userSessionId) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to end userSessionId " + userSessionId + '?', "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.endUserSession(sessionId, userSessionId); - return true; - } + if (isConnected()) { + server.endUserSession(sessionId, userSessionId); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1455,12 +1451,9 @@ public class SessionImpl implements Session { @Override public boolean muteUserChat(String userName, long durationMinutes) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to mute user " + userName + " for " + durationMinutes + " minutes?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.muteUser(sessionId, userName, durationMinutes); - return true; - } + if (isConnected()) { + server.muteUser(sessionId, userName, durationMinutes); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1473,12 +1466,9 @@ public class SessionImpl implements Session { @Override public boolean setActivation(String userName, boolean active) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to set active to " + active + " for user: " + userName + '?', "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.setActivation(sessionId, userName, active); - return true; - } + if (isConnected()) { + server.setActivation(sessionId, userName, active); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1491,20 +1481,9 @@ public class SessionImpl implements Session { @Override public boolean toggleActivation(String userName) { try { - if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to active?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - return setActivation(userName, true); - } - if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to INactive?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - return setActivation(userName, false); - } - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to toggle activation for user: " + userName + '?', "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.toggleActivation(sessionId, userName); - return true; - } + if (isConnected()) { + server.toggleActivation(sessionId, userName); + return true; } } catch (MageException ex) { handleMageException(ex); @@ -1517,12 +1496,9 @@ public class SessionImpl implements Session { @Override public boolean lockUser(String userName, long durationMinute) { try { - if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to lock user: " + userName + " for " + durationMinute + " minutes?", "WARNING", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - if (isConnected()) { - server.lockUser(sessionId, userName, durationMinute); - return true; - } + if (isConnected()) { + server.lockUser(sessionId, userName, durationMinute); + return true; } } catch (MageException ex) { handleMageException(ex); diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java index dd5e8b70a7..af9afb4666 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java @@ -345,22 +345,54 @@ public class ConsolePanel extends javax.swing.JPanel { private void btnEndSessionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEndSessionActionPerformed int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().endUserSession((String) tableUserModel.getValueAt(row, TableUserModel.POS_GAME_INFO)); + String userSessionId = (String) tableUserModel.getValueAt(row, TableUserModel.POS_GAME_INFO); + + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to end userSessionId " + userSessionId + '?', "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().endUserSession(userSessionId); + } }//GEN-LAST:event_btnEndSessionActionPerformed private void btnMuteUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMuteUserActionPerformed - int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().muteUserChat((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME), ((Number) spinnerMuteDurationMinutes.getValue()).longValue()); + int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); + String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME); + long durationMinute = ((Number) spinnerMuteDurationMinutes.getValue()).longValue(); + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to mute user: " + userName + " for " + durationMinute + " minutes?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().muteUserChat(userName, durationMinute); + } }//GEN-LAST:event_btnMuteUserActionPerformed private void btnDeActivateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeActivateActionPerformed int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().toggleActivation((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME)); + String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME); + + if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to active?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().setActivation(userName, true); + return; + } + if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to inactive?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().setActivation(userName, false); + return; + } + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to toggle activation for user: " + userName + '?', "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().toggleActivation(userName); + return; + } + } }//GEN-LAST:event_btnDeActivateActionPerformed private void btnLockUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLockUserActionPerformed int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow()); - ConsoleFrame.getSession().lockUser((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME), ((Number) spinnerMuteDurationMinutes.getValue()).longValue()); + String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME); + long durationMinute = ((Number) spinnerMuteDurationMinutes.getValue()).longValue(); + if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to lock user: " + userName + " for " + durationMinute + " minutes?", "WARNING", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + ConsoleFrame.getSession().lockUser(userName, durationMinute); + } }//GEN-LAST:event_btnLockUserActionPerformed private void btnRemoveTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveTableActionPerformed From 62b6ee30eb939eaa2774148bde91fde663772f9b Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 21 Feb 2018 17:18:35 -0600 Subject: [PATCH 013/127] - Added requested card Telekinetic Bonds --- .../src/mage/cards/t/TelekineticBonds.java | 66 +++++++++++++++++++ Mage.Sets/src/mage/sets/Judgment.java | 1 + .../DiscardsACardPlayerTriggeredAbility.java | 57 ++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TelekineticBonds.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/t/TelekineticBonds.java b/Mage.Sets/src/mage/cards/t/TelekineticBonds.java new file mode 100644 index 0000000000..7f77b77987 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TelekineticBonds.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DiscardsACardPlayerTriggeredAbility; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.MayTapOrUntapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetPermanent; + +/** + * + * @author jeffwadsworth + */ +public class TelekineticBonds extends CardImpl { + + public TelekineticBonds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}{U}"); + + + // Whenever a player discards a card, you may pay {1}{U}. If you do, you may tap or untap target permanent. + Ability ability = new DiscardsACardPlayerTriggeredAbility(new DoIfCostPaid(new MayTapOrUntapTargetEffect(), new ManaCostsImpl("{1}{U}")), true); + ability.addTarget(new TargetPermanent()); + this.addAbility(ability); + + } + + public TelekineticBonds(final TelekineticBonds card) { + super(card); + } + + @Override + public TelekineticBonds copy() { + return new TelekineticBonds(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Judgment.java b/Mage.Sets/src/mage/sets/Judgment.java index 9007e49c9e..fa99c1a1b3 100644 --- a/Mage.Sets/src/mage/sets/Judgment.java +++ b/Mage.Sets/src/mage/sets/Judgment.java @@ -163,6 +163,7 @@ public class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Swelter", 101, Rarity.UNCOMMON, mage.cards.s.Swelter.class)); cards.add(new SetCardInfo("Swirling Sandstorm", 102, Rarity.COMMON, mage.cards.s.SwirlingSandstorm.class)); cards.add(new SetCardInfo("Sylvan Safekeeper", 133, Rarity.RARE, mage.cards.s.SylvanSafekeeper.class)); + cards.add(new SetCardInfo("Telekinetic Bonds", 52, Rarity.RARE, mage.cards.t.TelekineticBonds.class)); cards.add(new SetCardInfo("Test of Endurance", 29, Rarity.RARE, mage.cards.t.TestOfEndurance.class)); cards.add(new SetCardInfo("Thriss, Nantuko Primus", 134, Rarity.RARE, mage.cards.t.ThrissNantukoPrimus.class)); cards.add(new SetCardInfo("Toxic Stench", 74, Rarity.COMMON, mage.cards.t.ToxicStench.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.java new file mode 100644 index 0000000000..5c4ac7641c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DiscardsACardPlayerTriggeredAbility.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.abilities.effects.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ + + public class DiscardsACardPlayerTriggeredAbility extends TriggeredAbilityImpl { + + private SetTargetPointer setTargetPointer; + + public DiscardsACardPlayerTriggeredAbility(Effect effect, boolean isOptional) { + this(effect, isOptional, SetTargetPointer.NONE); + } + + public DiscardsACardPlayerTriggeredAbility(Effect effect, boolean isOptional, SetTargetPointer setTargetPointer) { + super(Zone.BATTLEFIELD, effect, isOptional); + this.setTargetPointer = setTargetPointer; + } + + public DiscardsACardPlayerTriggeredAbility(final DiscardsACardPlayerTriggeredAbility ability) { + super(ability); + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DiscardsACardPlayerTriggeredAbility copy() { + return new DiscardsACardPlayerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return true; + } + + @Override + public String getRule() { + return "Whenever player discards a card, " + super.getRule(); + } +} \ No newline at end of file From 75e028a1c30f1ac0d16f276fd62176f965bb5fdf Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 22 Feb 2018 08:06:03 -0600 Subject: [PATCH 014/127] - Added requested card Penance --- Mage.Sets/src/mage/cards/p/Penance.java | 115 ++++++++++++++++++ .../PutCardFromHandOnTopOfLibraryCost.java | 60 +++++++++ 2 files changed, 175 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/Penance.java create mode 100644 Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java diff --git a/Mage.Sets/src/mage/cards/p/Penance.java b/Mage.Sets/src/mage/cards/p/Penance.java new file mode 100644 index 0000000000..45616fedf3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Penance.java @@ -0,0 +1,115 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.p; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.TargetSource; + +/** + * + * @author jeffwadsworth + */ +public class Penance extends CardImpl { + + public Penance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Put a card from your hand on top of your library: The next time a black or red source of your choice would deal damage this turn, prevent that damage. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PenanceEffect(), new PutCardFromHandOnTopOfLibraryCost())); + + } + + public Penance(final Penance card) { + super(card); + } + + @Override + public Penance copy() { + return new Penance(this); + } +} + +class PenanceEffect extends PreventionEffectImpl { + + private final TargetSource target; + + public PenanceEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "The next time a black or red source of your choice would deal damage to you this turn, prevent that damage."; + this.target = new TargetSource(); + } + + public PenanceEffect(final PenanceEffect effect) { + super(effect); + this.target = effect.target.copy(); + } + + @Override + public PenanceEffect copy() { + return new PenanceEffect(this); + } + + @Override + public void init(Ability source, Game game) { + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + this.used = true; + this.discard(); // only one use + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!this.used + && super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getControllerId()) + && event.getSourceId().equals(target.getFirstTarget())) { + return (game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.BLACK) + || game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.RED)); + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java new file mode 100644 index 0000000000..44fa2a8a5e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/PutCardFromHandOnTopOfLibraryCost.java @@ -0,0 +1,60 @@ +/* + * 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.abilities.costs.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author jeffwadsworth + */ + +public class PutCardFromHandOnTopOfLibraryCost extends CostImpl { + + public PutCardFromHandOnTopOfLibraryCost() { + this.text = "Put a card from your hand on top of your library"; + } + + public PutCardFromHandOnTopOfLibraryCost(PutCardFromHandOnTopOfLibraryCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + TargetCardInHand targetCardInHand = new TargetCardInHand(); + targetCardInHand.setRequired(false); + Card card; + if (targetCardInHand.canChoose(controllerId, game) + && controller.choose(Outcome.PreventDamage, targetCardInHand, sourceId, game)) { + card = game.getCard(targetCardInHand.getFirstTarget()); + paid = controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.HAND, true, true); + } + return paid; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + return (controller != null + && !controller.getHand().isEmpty()); + } + + @Override + public PutCardFromHandOnTopOfLibraryCost copy() { + return new PutCardFromHandOnTopOfLibraryCost(this); + } +} + From 88dd301f828d0000f0daef49bebc6f04dd3c2713 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 22 Feb 2018 08:09:44 -0600 Subject: [PATCH 015/127] - Added Penance to the Exodus set --- Mage.Sets/src/mage/sets/Exodus.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Exodus.java b/Mage.Sets/src/mage/sets/Exodus.java index 851bd91649..7c4066522a 100644 --- a/Mage.Sets/src/mage/sets/Exodus.java +++ b/Mage.Sets/src/mage/sets/Exodus.java @@ -123,6 +123,7 @@ public class Exodus extends ExpansionSet { cards.add(new SetCardInfo("Pandemonium", 93, Rarity.RARE, mage.cards.p.Pandemonium.class)); cards.add(new SetCardInfo("Peace of Mind", 13, Rarity.UNCOMMON, mage.cards.p.PeaceOfMind.class)); cards.add(new SetCardInfo("Pegasus Stampede", 14, Rarity.UNCOMMON, mage.cards.p.PegasusStampede.class)); + cards.add(new SetCardInfo("Penance", 15, Rarity.UNCOMMON, mage.cards.p.Penance.class)); cards.add(new SetCardInfo("Pit Spawn", 70, Rarity.RARE, mage.cards.p.PitSpawn.class)); cards.add(new SetCardInfo("Plaguebearer", 71, Rarity.RARE, mage.cards.p.Plaguebearer.class)); cards.add(new SetCardInfo("Plated Rootwalla", 116, Rarity.COMMON, mage.cards.p.PlatedRootwalla.class)); From c02c5a175ba25842fe673ded1b00295c6fba55a9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 22 Feb 2018 17:46:23 +0100 Subject: [PATCH 016/127] * Fixed some target pointer handling. --- .../src/mage/cards/a/ActOfAuthority.java | 9 ++- .../src/mage/cards/a/AdarkarValkyrie.java | 2 +- Mage.Sets/src/mage/cards/a/AnkhOfMishra.java | 18 +++--- .../src/mage/cards/a/ArbiterOfTheIdeal.java | 41 ++++++------- .../src/mage/cards/a/ArchonOfRedemption.java | 57 ++++--------------- .../src/mage/cards/a/ArrogantBloodlord.java | 11 ++-- .../src/main/java/mage/abilities/Ability.java | 12 ++++ .../main/java/mage/abilities/AbilityImpl.java | 13 +++++ .../java/mage/game/stack/StackAbility.java | 10 +++- .../targetpointer/FirstTargetPointer.java | 20 +++++-- .../target/targetpointer/FixedTarget.java | 22 +++---- .../targetpointer/SecondTargetPointer.java | 16 +++++- .../target/targetpointer/TargetPointer.java | 6 ++ .../targetpointer/ThirdTargetPointer.java | 11 ++++ 14 files changed, 140 insertions(+), 108 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java index 913dc43fbb..38dcd6ca16 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfAuthority.java +++ b/Mage.Sets/src/mage/cards/a/ActOfAuthority.java @@ -94,9 +94,12 @@ class ActOfAuthorityEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent targetPermanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (targetPermanent != null && new ExileTargetEffect().apply(game, source)) { - ContinuousEffect effect = new ActOfAuthorityGainControlEffect(Duration.Custom, targetPermanent.getControllerId()); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); - game.addEffect(effect, source); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null) { + ContinuousEffect effect = new ActOfAuthorityGainControlEffect(Duration.Custom, targetPermanent.getControllerId()); + effect.setTargetPointer(new FixedTarget(sourcePermanent, game)); + game.addEffect(effect, source); + } return true; } return false; diff --git a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java index ae3abfebab..0fccaadd0d 100644 --- a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java +++ b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java @@ -109,7 +109,7 @@ class AdarkarValkyrieEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(new FixedTarget(this.getTargetPointer().getFirst(game, source))); + DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(getTargetPointer().getFixedTarget(game, source)); game.addDelayedTriggeredAbility(delayedAbility, source); return false; } diff --git a/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java b/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java index 38272e1e8e..a31aa9dbf4 100644 --- a/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java +++ b/Mage.Sets/src/mage/cards/a/AnkhOfMishra.java @@ -45,16 +45,16 @@ import mage.target.targetpointer.FixedTarget; /** * * @author KholdFuzion - + * */ public class AnkhOfMishra extends CardImpl { - + public AnkhOfMishra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Whenever a land enters the battlefield, Ankh of Mishra deals 2 damage to that land's controller. this.addAbility(new AnkhOfMishraAbility()); - + } public AnkhOfMishra(final AnkhOfMishra card) { @@ -70,16 +70,16 @@ public class AnkhOfMishra extends CardImpl { class AnkhOfMishraAbility extends TriggeredAbilityImpl { public AnkhOfMishraAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(2)); + super(Zone.BATTLEFIELD, new DamageTargetEffect(2)); } AnkhOfMishraAbility(final AnkhOfMishraAbility ability) { - super(ability); + super(ability); } @Override public AnkhOfMishraAbility copy() { - return new AnkhOfMishraAbility(this); + return new AnkhOfMishraAbility(this); } @Override @@ -104,6 +104,6 @@ class AnkhOfMishraAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a land enters the battlefield, Ankh of Mishra deals 2 damage to that land's controller."; + return "Whenever a land enters the battlefield, {this} deals 2 damage to that land's controller."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java b/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java index 31453ff15b..de172fddb1 100644 --- a/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java +++ b/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java @@ -29,6 +29,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -53,7 +54,7 @@ import mage.target.targetpointer.FixedTarget; public class ArbiterOfTheIdeal extends CardImpl { public ArbiterOfTheIdeal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); this.subtype.add(SubType.SPHINX); this.power = new MageInt(4); @@ -79,6 +80,7 @@ public class ArbiterOfTheIdeal extends CardImpl { class ArbiterOfTheIdealEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard(); + static { filter.add(Predicates.or( new CardTypePredicate(CardType.ARTIFACT), @@ -102,32 +104,25 @@ class ArbiterOfTheIdealEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } - - if (player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); - Cards cards = new CardsImpl(); - cards.add(card); - player.revealCards("Arbiter of the Ideal", cards, game); - - if (card != null) { - if (filter.match(card, game) && player.chooseUse(outcome, new StringBuilder("Put ").append(card.getName()).append("onto battlefield?").toString(), source, game)) { - card.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), source.getControllerId()); - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - permanent.addCounters(new Counter("Manifestation"), source, game); - ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT); - effect.setTargetPointer(new FixedTarget(permanent.getId())); - game.addEffect(effect, source); - } + Card card = controller.getLibrary().getFromTop(game); + if (card != null) { + controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); + if (filter.match(card, game) && controller.chooseUse(outcome, "Put " + card.getName() + "onto battlefield?", source, game)) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + permanent.addCounters(new Counter("Manifestation"), source, game); + ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); } } - return true; } - - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java index 6b3507d348..50342b73b4 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java @@ -29,23 +29,18 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @author Loki @@ -53,7 +48,7 @@ import mage.target.targetpointer.FixedTarget; public class ArchonOfRedemption extends CardImpl { public ArchonOfRedemption(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.ARCHON); this.power = new MageInt(3); @@ -75,8 +70,9 @@ public class ArchonOfRedemption extends CardImpl { } class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { + ArchonOfRedemptionTriggeredAbility() { - super(Zone.BATTLEFIELD, new ArchonOfRedemptionEffect(), true); + super(Zone.BATTLEFIELD, null, true); } ArchonOfRedemptionTriggeredAbility(final ArchonOfRedemptionTriggeredAbility ability) { @@ -95,15 +91,13 @@ class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanent(targetId); - if (permanent.getControllerId().equals(this.controllerId) + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent.getControllerId().equals(getControllerId()) && permanent.isCreature() - && (targetId.equals(this.getSourceId()) - || (permanent.getAbilities().contains(FlyingAbility.getInstance()) && !targetId.equals(this.getSourceId())))) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } + && (permanent.getId().equals(getSourceId()) + || (permanent.getAbilities().contains(FlyingAbility.getInstance())))) { + this.getEffects().clear(); + this.addEffect(new GainLifeEffect(permanent.getPower().getValue())); return true; } return false; @@ -111,35 +105,6 @@ class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power"; + return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power."; } } - -class ArchonOfRedemptionEffect extends OneShotEffect { - ArchonOfRedemptionEffect() { - super(Outcome.GainLife); - } - - ArchonOfRedemptionEffect(final ArchonOfRedemptionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(targetPointer.getFirst(game, source)); - Player player = game.getPlayer(source.getControllerId()); - if (p == null) { - p = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); - } - if (p != null && player != null) { - player.gainLife(p.getPower().getValue(), game); - return true; - } - return false; - } - - @Override - public ArchonOfRedemptionEffect copy() { - return new ArchonOfRedemptionEffect(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java b/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java index e6c1110059..0aa16e9cfe 100644 --- a/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java +++ b/Mage.Sets/src/mage/cards/a/ArrogantBloodlord.java @@ -53,7 +53,7 @@ import mage.target.targetpointer.FixedTarget; public class ArrogantBloodlord extends CardImpl { public ArrogantBloodlord(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.KNIGHT); @@ -104,11 +104,8 @@ class ArrogantBloodlordTriggeredAbility extends TriggeredAbilityImpl { && Objects.equals(blocked, arrogantBloodlord)) { return true; } - if (blocker != null && Objects.equals(blocker, arrogantBloodlord) - && game.getPermanent(event.getTargetId()).getPower().getValue() < 2) { - return true; - } - return false; + return blocker != null && Objects.equals(blocker, arrogantBloodlord) + && game.getPermanent(event.getTargetId()).getPower().getValue() < 2; } @Override @@ -133,7 +130,7 @@ class ArrogantBloodlordEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); - delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(source.getSourceId())); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); game.addDelayedTriggeredAbility(delayedAbility, source); return true; } diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index d6baff95d4..d0a8f783aa 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -45,6 +45,7 @@ import mage.constants.Zone; import mage.game.Controllable; 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.Targets; @@ -535,6 +536,17 @@ public interface Ability extends Controllable, Serializable { */ MageObject getSourceObjectIfItStillExists(Game game); + /** + * Returns the permanent that actually existed while the ability triggerd or + * an ability was activated only if it has not changed zone meanwhile. If + * not set yet, the current permanent if one exists will be retrieved from + * the game and returned. + * + * @param game + * @return + */ + Permanent getSourcePermanentIfItStillExists(Game game); + String getTargetDescription(Targets targets, Game game); void setCanFizzle(boolean canFizzle); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 23f8176212..25528d242b 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1211,6 +1211,19 @@ public abstract class AbilityImpl implements Ability { return null; } + @Override + public Permanent getSourcePermanentIfItStillExists(Game game) { + if (sourceObject == null) { + setSourceObject(game.getObject(getSourceId()), game); + } + if (sourceObject instanceof Permanent) { + if (game.getState().getZoneChangeCounter(getSourceId()) == getSourceObjectZoneChangeCounter()) { + return (Permanent) sourceObject; + } + } + return null; + } + @Override public int getSourceObjectZoneChangeCounter() { return sourceObjectZoneChangeCounter; diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 07509f5786..00eabb629a 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -50,6 +50,7 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.Targets; @@ -529,12 +530,17 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public MageObject getSourceObject(Game game) { - return game.getBaseObject(getSourceId()); + return this.ability.getSourceObject(game); } @Override public MageObject getSourceObjectIfItStillExists(Game game) { - throw new UnsupportedOperationException("Not supported."); + return this.ability.getSourceObjectIfItStillExists(game); + } + + @Override + public Permanent getSourcePermanentIfItStillExists(Game game) { + return this.ability.getSourcePermanentIfItStillExists(game); } @Override diff --git a/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java index 01d2b3c7db..82241949eb 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/FirstTargetPointer.java @@ -35,7 +35,7 @@ public class FirstTargetPointer implements TargetPointer { Card card = game.getCard(target); if (card != null) { this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); - } + } } } } @@ -66,9 +66,9 @@ public class FirstTargetPointer implements TargetPointer { if (zoneChangeCounter.containsKey(targetId)) { Card card = game.getCard(targetId); if (card != null && zoneChangeCounter.containsKey(targetId) - && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { - // because if dies trigger has to trigger as permanent has already moved zone, we have to check if target was on the battlefield immed. before - // but no longer if new permanent is already on the battlefield + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + // because if dies trigger has to trigger as permanent has already moved zone, we have to check if target was on the battlefield immed. before + // but no longer if new permanent is already on the battlefield Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); if (permanent == null || permanent.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { return null; @@ -82,4 +82,16 @@ public class FirstTargetPointer implements TargetPointer { public TargetPointer copy() { return new FirstTargetPointer(this); } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } + } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java index 397e4e7de3..c931962322 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java @@ -1,15 +1,14 @@ package mage.target.targetpointer; -import mage.abilities.Ability; -import mage.cards.Card; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; - import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; public class FixedTarget implements TargetPointer { @@ -65,10 +64,7 @@ public class FixedTarget implements TargetPointer { public void init(Game game, Ability source) { if (!initialized) { initialized = true; - Card card = game.getCard(targetId); - if (card != null) { - this.zoneChangeCounter = card.getZoneChangeCounter(game); - } + this.zoneChangeCounter = game.getState().getZoneChangeCounter(targetId); } } @@ -121,4 +117,10 @@ public class FixedTarget implements TargetPointer { return permanent; } + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + init(game, source); + return this; + } + } diff --git a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java index db301a7055..4917726dfa 100644 --- a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java @@ -1,11 +1,10 @@ package mage.target.targetpointer; +import java.util.*; import mage.abilities.Ability; import mage.cards.Card; import mage.game.Game; -import java.util.*; - public class SecondTargetPointer implements TargetPointer { private Map zoneChangeCounter = new HashMap<>(); @@ -59,7 +58,7 @@ public class SecondTargetPointer implements TargetPointer { if (zoneChangeCounter.containsKey(targetId)) { Card card = game.getCard(targetId); if (card != null && zoneChangeCounter.containsKey(targetId) - && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { return null; } } @@ -72,4 +71,15 @@ public class SecondTargetPointer implements TargetPointer { public TargetPointer copy() { return new SecondTargetPointer(this); } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java index 0b77cd2bf6..6dd021c9ac 100644 --- a/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/TargetPointer.java @@ -7,8 +7,14 @@ import mage.abilities.Ability; import mage.game.Game; public interface TargetPointer extends Serializable { + void init(Game game, Ability source); + List getTargets(Game game, Ability source); + UUID getFirst(Game game, Ability source); + TargetPointer copy(); + + FixedTarget getFixedTarget(Game game, Ability source); } diff --git a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java index 2fdbf05037..206bfe2eb9 100644 --- a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java @@ -84,4 +84,15 @@ public class ThirdTargetPointer implements TargetPointer { public TargetPointer copy() { return new ThirdTargetPointer(this); } + + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + + } } From 7bbe01b25aa00409c2afe198917d23f4c1bda55a Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 06:35:13 +0000 Subject: [PATCH 017/127] Updated Vodalian War Machine watcher with MageObjectReference --- .../src/mage/cards/v/VodalianWarMachine.java | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 09e17dcfd9..7a972a8726 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -132,9 +133,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId()) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId()).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), game).contains(permanent.getId())) { permanent.destroy(source.getSourceId(), game, false); } } @@ -148,7 +149,7 @@ class VodalianWarMachineEffect extends OneShotEffect { class VodalianWarMachineWatcher extends Watcher { - public Map> tappedMerfolkIds = new HashMap<>(); + public Map> tappedMerfolkIds = new HashMap<>(); public VodalianWarMachineWatcher() { super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); @@ -164,33 +165,38 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId) { - return tappedMerfolkIds.get(sourceId); + public Set getTappedMerfolkIds(UUID sourceId, Game game) { + MageObjectReference mor = new MageObjectReference(sourceId, game); + return tappedMerfolkIds.get(mor); } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { if (event.getSourceId() != null) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null) { - Ability ability = stackAbility.getStackAbility(); - if (ability != null) { - for (Cost cost : ability.getCosts()) { - if (cost instanceof TapTargetCost && cost.isPaid()) { - TapTargetCost tapCost = (TapTargetCost) cost; - if (tapCost.getTarget().isChosen()) { - Set toAdd; - if (tappedMerfolkIds.get(event.getSourceId()) == null) { - toAdd = new HashSet<>(); - } else { - toAdd = tappedMerfolkIds.get(event.getSourceId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (sourcePermanent != null) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null) { + Ability ability = stackAbility.getStackAbility(); + if (ability != null) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapTargetCost && cost.isPaid()) { + TapTargetCost tapCost = (TapTargetCost) cost; + if (tapCost.getTarget().isChosen()) { + MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game) + 1, game); + Set toAdd; + if (tappedMerfolkIds.get(mor) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = tappedMerfolkIds.get(mor); + } + for (UUID targetId : tapCost.getTarget().getTargets()) { + toAdd.add(targetId); + } + tappedMerfolkIds.put(mor, toAdd); + break; } - for (UUID targetId : tapCost.getTarget().getTargets()) { - toAdd.add(targetId); - } - tappedMerfolkIds.put(event.getSourceId(), toAdd); - break; } } } From 5cb1d75a1d3eab8acffb674ddeeccaed480558ca Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 07:10:19 +0000 Subject: [PATCH 018/127] Some more changes Now it catches even instances of being exiled from owner's GY while the ability is still on the stack --- .../src/mage/cards/v/VodalianWarMachine.java | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 7a972a8726..75c3b50aab 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -39,6 +39,7 @@ import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -58,6 +59,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.target.common.TargetControlledCreaturePermanent; @@ -93,7 +95,7 @@ public class VodalianWarMachine extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. - this.addAbility(new DiesTriggeredAbility(new VodalianWarMachineEffect(), false), new VodalianWarMachineWatcher()); + this.addAbility(new VodalianWarMachineTriggeredAbility(), new VodalianWarMachineWatcher()); } public VodalianWarMachine(final VodalianWarMachine card) { @@ -106,7 +108,51 @@ public class VodalianWarMachine extends CardImpl { } } +class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { + + public VodalianWarMachineTriggeredAbility() { + super(new VodalianWarMachineEffect(), false); + } + + public VodalianWarMachineTriggeredAbility(VodalianWarMachineTriggeredAbility ability) { + super(ability); + } + + @Override + public VodalianWarMachineTriggeredAbility copy() { + return new VodalianWarMachineTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent before = ((ZoneChangeEvent) event).getTarget(); + if (before == null) { + return false; + } + if (super.checkTrigger(event, game)) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget().isTransformable()) { + if (!zEvent.getTarget().getAbilities().contains(this)) { + return false; + } + } + for (Effect effect : getEffects()) { + effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); + if (effect instanceof VodalianWarMachineEffect) { + VodalianWarMachineEffect effectToSet = (VodalianWarMachineEffect) effect; + effectToSet.counter = before.getZoneChangeCounter(game); + } + } + return true; + } + return false; + } + +} + class VodalianWarMachineEffect extends OneShotEffect { + + protected int counter; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); @@ -121,6 +167,7 @@ class VodalianWarMachineEffect extends OneShotEffect { public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { super(effect); + counter = effect.counter; } @Override @@ -133,9 +180,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), game) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), game).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game).contains(permanent.getId())) { permanent.destroy(source.getSourceId(), game, false); } } @@ -165,8 +212,8 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId, Game game) { - MageObjectReference mor = new MageObjectReference(sourceId, game); + public Set getTappedMerfolkIds(UUID sourceId, int counter, Game game) { + MageObjectReference mor = new MageObjectReference(sourceId, counter, game); return tappedMerfolkIds.get(mor); } @@ -184,7 +231,7 @@ class VodalianWarMachineWatcher extends Watcher { if (cost instanceof TapTargetCost && cost.isPaid()) { TapTargetCost tapCost = (TapTargetCost) cost; if (tapCost.getTarget().isChosen()) { - MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game) + 1, game); + MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game), game); Set toAdd; if (tappedMerfolkIds.get(mor) == null) { toAdd = new HashSet<>(); From 03454d326196d33411ab3b989e6ead643c1f83c4 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:40:12 +0000 Subject: [PATCH 019/127] Wood Elemental edit --- Mage.Sets/src/mage/cards/w/WoodElemental.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/w/WoodElemental.java b/Mage.Sets/src/mage/cards/w/WoodElemental.java index 492ce5485d..ac6bc424c7 100644 --- a/Mage.Sets/src/mage/cards/w/WoodElemental.java +++ b/Mage.Sets/src/mage/cards/w/WoodElemental.java @@ -70,7 +70,7 @@ public class WoodElemental extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new WoodElementalEffect())); // Wood Elemental's power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); } public WoodElemental(final WoodElemental card) { @@ -123,7 +123,7 @@ class WoodElementalEffect extends OneShotEffect { targetPermanent.sacrifice(source.getSourceId(), game); } } - game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.Custom, SubLayer.SetPT_7b), source); return true; } } From 38930c707948156c6265af82bc22237b250b2860 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:41:10 +0000 Subject: [PATCH 020/127] Minion of the Wastes --- Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java index 35064a7591..2714adcea4 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java @@ -105,7 +105,7 @@ class MinionOfTheWastesEffect extends OneShotEffect { controller.loseLife(payAmount, game, false); game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) .append(" pays ").append(payAmount).append(" life").toString()); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); return true; } return false; From 2a26ef24a536cc5d80dd22586b3fb4c327560db3 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:41:27 +0000 Subject: [PATCH 021/127] Nameless Race edit --- Mage.Sets/src/mage/cards/n/NamelessRace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/n/NamelessRace.java b/Mage.Sets/src/mage/cards/n/NamelessRace.java index 0f413268eb..615f1a48fc 100644 --- a/Mage.Sets/src/mage/cards/n/NamelessRace.java +++ b/Mage.Sets/src/mage/cards/n/NamelessRace.java @@ -128,7 +128,7 @@ class NamelessRaceEffect extends OneShotEffect { controller.loseLife(payAmount, game, false); game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) .append(" pays ").append(payAmount).append(" life").toString()); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); return true; } return false; From a2b6f83a3e52c7c52bea7e02c9800a17efdbc0a1 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 23 Feb 2018 13:02:22 +0400 Subject: [PATCH 022/127] Fixed compile error --- .../src/main/java/mage/server/console/ConsolePanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java index af9afb4666..4b729539d8 100644 --- a/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java +++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsolePanel.java @@ -381,7 +381,6 @@ public class ConsolePanel extends javax.swing.JPanel { JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { ConsoleFrame.getSession().toggleActivation(userName); return; - } } }//GEN-LAST:event_btnDeActivateActionPerformed From 5b0e71021dccb0cf5590a23e768a59b033856726 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 23 Feb 2018 16:15:59 +0400 Subject: [PATCH 023/127] Fixed compile error on getFixedTarget and failed test on new lower names --- Mage.Sets/src/mage/cards/p/Progenitus.java | 5 ----- .../src/mage/cards/t/TeferisProtection.java | 7 +------ .../test/cards/single/lrw/CairnWandererTest.java | 16 ++++++++-------- .../mage/target/targetpointer/FixedTargets.java | 9 +++++++++ .../targetpointer/SecondTargetPointer.java | 1 - 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/Progenitus.java b/Mage.Sets/src/mage/cards/p/Progenitus.java index ecfc73fa50..afec039d10 100644 --- a/Mage.Sets/src/mage/cards/p/Progenitus.java +++ b/Mage.Sets/src/mage/cards/p/Progenitus.java @@ -93,11 +93,6 @@ class ProgenitusProtectionAbility extends ProtectionAbility { return new ProgenitusProtectionAbility(this); } - @Override - public String getRule() { - return "Protection from everything"; - } - @Override public boolean canTarget(MageObject source, Game game) { return false; diff --git a/Mage.Sets/src/mage/cards/t/TeferisProtection.java b/Mage.Sets/src/mage/cards/t/TeferisProtection.java index 98701104c9..ec5de97c6b 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisProtection.java +++ b/Mage.Sets/src/mage/cards/t/TeferisProtection.java @@ -141,12 +141,7 @@ class TeferisProtectionAbility extends ProtectionAbility { public TeferisProtectionAbility copy() { return new TeferisProtectionAbility(this); } - - @Override - public String getRule() { - return "Protection from everything"; - } - + @Override public boolean canTarget(MageObject source, Game game) { return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java index 2f3e6851fe..6f96643b13 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/CairnWandererTest.java @@ -80,28 +80,28 @@ public class CairnWandererTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerA, "Typhoid Rats"); // Testing HasteAbility. - addCard(Zone.GRAVEYARD, playerA, "Raging Goblin"); + addCard(Zone.GRAVEYARD, playerB, "Raging Goblin"); // Testing LandwalkAbility. - addCard(Zone.GRAVEYARD, playerA, "Zodiac Rooster"); + addCard(Zone.GRAVEYARD, playerB, "Zodiac Rooster"); // Testing LifelinkAbility. - addCard(Zone.GRAVEYARD, playerA, "Trained Caracal"); + addCard(Zone.GRAVEYARD, playerB, "Trained Caracal"); // Testing ProtectionAbility. - addCard(Zone.GRAVEYARD, playerA, "Progenitus"); + addCard(Zone.GRAVEYARD, playerB, "Progenitus"); // Testing ReachAbility. - addCard(Zone.GRAVEYARD, playerA, "Tree Monkey"); + addCard(Zone.GRAVEYARD, playerB, "Tree Monkey"); // Testing TrampleAbility. - addCard(Zone.GRAVEYARD, playerA, "Defiant Elf"); + addCard(Zone.GRAVEYARD, playerB, "Defiant Elf"); // Testing ShroudAbility. - addCard(Zone.GRAVEYARD, playerA, "Elvish Lookout"); + addCard(Zone.GRAVEYARD, playerB, "Elvish Lookout"); // Testing VigilanceAbility. - addCard(Zone.GRAVEYARD, playerA, "Veteran Cavalier"); + addCard(Zone.GRAVEYARD, playerB, "Veteran Cavalier"); execute(); diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index bb231a51c7..d5c099526b 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -87,4 +87,13 @@ public class FixedTargets implements TargetPointer { return new FixedTargets(this); } + @Override + public FixedTarget getFixedTarget(Game game, Ability source) { + this.init(game, source); + UUID firstId = getFirst(game, source); + if (firstId != null) { + return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); + } + return null; + } } diff --git a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java index 4917726dfa..9d2779b4ef 100644 --- a/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java +++ b/Mage/src/main/java/mage/target/targetpointer/SecondTargetPointer.java @@ -80,6 +80,5 @@ public class SecondTargetPointer implements TargetPointer { return new FixedTarget(firstId, game.getState().getZoneChangeCounter(firstId)); } return null; - } } From ecb95db541e5b86554cd1d1d1ed6afc67b9d73be Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 13:36:23 +0000 Subject: [PATCH 024/127] Eye for an Eye rewrite (fixes #4103) --- Mage.Sets/src/mage/cards/e/EyeForAnEye.java | 103 ++++++++++---------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/EyeForAnEye.java b/Mage.Sets/src/mage/cards/e/EyeForAnEye.java index 08245b25ab..9c62933fbf 100644 --- a/Mage.Sets/src/mage/cards/e/EyeForAnEye.java +++ b/Mage.Sets/src/mage/cards/e/EyeForAnEye.java @@ -28,24 +28,22 @@ package mage.cards.e; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; +import mage.target.TargetSource; /** - * - * @author MarcoMarin + * + * @author L_J */ public class EyeForAnEye extends CardImpl { @@ -53,8 +51,7 @@ public class EyeForAnEye extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{W}"); // The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and Eye for an Eye deals that much damage to that source's controller. - this.addAbility(new EyeForAnEyeTriggeredAbility(new EyeForAnEyeEffect())); - + this.getSpellAbility().addEffect(new EyeForAnEyeEffect()); } public EyeForAnEye(final EyeForAnEye card) { @@ -67,48 +64,19 @@ public class EyeForAnEye extends CardImpl { } } -class EyeForAnEyeTriggeredAbility extends TriggeredAbilityImpl { +class EyeForAnEyeEffect extends ReplacementEffectImpl { - public EyeForAnEyeTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - public EyeForAnEyeTriggeredAbility(final EyeForAnEyeTriggeredAbility ability) { - super(ability); - } - - @Override - public EyeForAnEyeTriggeredAbility copy() { - return new EyeForAnEyeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject sourceObject = game.getObject(event.getSourceId()); - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(sourceObject.getId()))); - return true; - } - - @Override - public String getRule() { - return "The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and {this} deals that much damage to that source's controller."; - } -} - -class EyeForAnEyeEffect extends OneShotEffect { + private final TargetSource damageSource; public EyeForAnEyeEffect() { - super(Outcome.Damage); + super(Duration.EndOfTurn, Outcome.RedirectDamage); + staticText = "The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and {this} deals that much damage to that source's controller"; + this.damageSource = new TargetSource(); } public EyeForAnEyeEffect(final EyeForAnEyeEffect effect) { super(effect); + this.damageSource = effect.damageSource.copy(); } @Override @@ -116,15 +84,48 @@ class EyeForAnEyeEffect extends OneShotEffect { return new EyeForAnEyeEffect(this); } + @Override + public void init(Ability source, Game game) { + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + @Override public boolean apply(Game game, Ability source) { - Integer damageAmount = (Integer) this.getValue("damageAmount"); - UUID targetId = this.targetPointer.getFirst(game, source); - if (damageAmount != null && targetId != null) { - Player player = game.getPlayer(targetId); - if (player != null) { - player.damage(damageAmount, targetId, game, false, true); + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + DamageEvent damageEvent = (DamageEvent) event; + if (controller != null) { + controller.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects()); + UUID sourceControllerId = game.getControllerId(damageEvent.getSourceId()); + if (sourceControllerId != null) { + Player sourceController = game.getPlayer(sourceControllerId); + if (sourceController != null) { + sourceController.damage(damageEvent.getAmount(), source.getSourceId(), game, false, true); return true; + } + } + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + DamageEvent damageEvent = (DamageEvent) event; + if (controller != null) { + if (controller.getId() == damageEvent.getTargetId() && damageEvent.getSourceId().equals(damageSource.getFirstTarget())) { + this.discard(); + return true; } } return false; From e5ef545d8f8f3a818386bfbc9d5325d9e75d3bbe Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 23 Feb 2018 18:21:08 +0400 Subject: [PATCH 025/127] * UI: fixed wrong feedback panel color for non active turn; --- Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java | 1 - Mage.Client/src/main/java/mage/client/game/HelperPanel.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 5992f5a424..de2eb91505 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -64,7 +64,6 @@ public class FeedbackPanel extends javax.swing.JPanel { private static final Logger LOGGER = Logger.getLogger(FeedbackPanel.class); public enum FeedbackMode { - INFORM, QUESTION, CONFIRM, CANCEL, SELECT, END } diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index 04ccc0db76..1bd86d7a3d 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -394,7 +394,7 @@ public class HelperPanel extends JPanel { } } else { // inform about other players - this.setOpaque(false); + this.mainPanel.setOpaque(false); } if (buttons.size() == 0) { From ac09be4b2b6651793e91d96ab60563d414a6cd90 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 14:45:01 +0000 Subject: [PATCH 026/127] Fixed Torment of Hailfire interaction with Sigarda (fixes #4452) --- .../src/mage/cards/t/TormentOfHailfire.java | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java index 504c68648c..7855e56b7a 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java @@ -86,31 +86,37 @@ class TormentOfHailfireEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int repeat = source.getManaCostsToPay().getX(); - for (int i = 0; i < repeat; i++) { + for (int i = 1; i <= repeat; i++) { for (UUID opponentId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); - if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", - "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - if (opponent.choose(outcome, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - continue; + boolean hasChosen = false; + while (!hasChosen) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); + if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", + "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); + if (opponent.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + if (permanent.sacrifice(source.getSourceId(), game)) { + hasChosen = true; + continue; + } + } } } + if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", + "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { + opponent.discardOne(false, source, game); + hasChosen = true; + continue; + } + opponent.loseLife(3, game, false); + hasChosen = true; } - if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", - "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { - opponent.discardOne(false, source, game); - continue; - } - opponent.loseLife(3, game, false); } } - } return true; } From a98f0f03c7ec4255950036287ff3721401f79b30 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 14:46:40 +0000 Subject: [PATCH 027/127] Code cleanup --- .../src/mage/cards/t/TormentOfHailfire.java | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java index 7855e56b7a..209582a7bf 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java @@ -87,36 +87,31 @@ class TormentOfHailfireEffect extends OneShotEffect { if (controller != null) { int repeat = source.getManaCostsToPay().getX(); for (int i = 1; i <= repeat; i++) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) { - boolean hasChosen = false; - while (!hasChosen) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); - if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", - "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - if (opponent.choose(outcome, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - if (permanent.sacrifice(source.getSourceId(), game)) { - hasChosen = true; - continue; - } + for (UUID opponentId : game.getOpponents(source.getControllerId())) {z + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); + if (permanents > 0 && opponent.chooseUse(outcome, "Sacrifices a nonland permanent? (Iteration " + i + " of " + repeat + ")", + "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); + if (opponent.choose(outcome, target, source.getSourceId(), game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + if (permanent.sacrifice(source.getSourceId(), game)) { + continue; } } } - if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", - "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { - opponent.discardOne(false, source, game); - hasChosen = true; - continue; - } - opponent.loseLife(3, game, false); - hasChosen = true; } + if (!opponent.getHand().isEmpty() && opponent.chooseUse(outcome, "Discard a card? (Iteration " + i + " of " + repeat + ")", + "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { + opponent.discardOne(false, source, game); + continue; + } + opponent.loseLife(3, game, false); } } + } return true; } From 6c2e66cd57022c2c964d615731f40a2b55636e0c Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 14:48:08 +0000 Subject: [PATCH 028/127] Typo --- Mage.Sets/src/mage/cards/t/TormentOfHailfire.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java index 209582a7bf..79e9e389ec 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfHailfire.java @@ -87,7 +87,7 @@ class TormentOfHailfireEffect extends OneShotEffect { if (controller != null) { int repeat = source.getManaCostsToPay().getX(); for (int i = 1; i <= repeat; i++) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) {z + for (UUID opponentId : game.getOpponents(source.getControllerId())) { Player opponent = game.getPlayer(opponentId); if (opponent != null) { int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, opponentId, game); From ecf7ca0a5b8ffecd7d9249dc334fec418bec9f21 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 23 Feb 2018 16:00:49 +0100 Subject: [PATCH 029/127] * City of Solitude - added a specific message fo rthe rule changing effect. --- .../src/mage/cards/c/CityOfSolitude.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CityOfSolitude.java b/Mage.Sets/src/mage/cards/c/CityOfSolitude.java index 03cef0637f..dbcc7129ba 100644 --- a/Mage.Sets/src/mage/cards/c/CityOfSolitude.java +++ b/Mage.Sets/src/mage/cards/c/CityOfSolitude.java @@ -28,6 +28,7 @@ package mage.cards.c; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -48,7 +49,7 @@ import mage.game.events.GameEvent.EventType; public class CityOfSolitude extends CardImpl { public CityOfSolitude(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Players can cast spells and activate abilities only during their own turns. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityOfSolitudeEffect())); @@ -74,12 +75,22 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl { CityOfSolitudeEffect(final CityOfSolitudeEffect effect) { super(effect); } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.CAST_SPELL || event.getType() == EventType.ACTIVATE_ABILITY; } - + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject sourceObject = game.getObject(source.getSourceId()); + MageObject eventObject = game.getObject(event.getSourceId()); + if (sourceObject != null && eventObject != null) { + return "You can cast or activate anability of " + eventObject.getIdName() + " only during your own turns (" + sourceObject.getIdName() + "). "; + } + return null; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { return !game.getActivePlayerId().equals(event.getPlayerId()); @@ -89,4 +100,4 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl { public CityOfSolitudeEffect copy() { return new CityOfSolitudeEffect(this); } -} \ No newline at end of file +} From 3a5924270dfd9e2ccc95be320c9a7453dd0b1b18 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 23 Feb 2018 11:41:57 -0600 Subject: [PATCH 030/127] - Added requested card Protective Sphere --- .../src/mage/cards/p/ProtectiveSphere.java | 134 +++ Mage.Sets/src/mage/sets/Invasion.java | 775 +++++++++--------- 2 files changed, 522 insertions(+), 387 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/p/ProtectiveSphere.java diff --git a/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java b/Mage.Sets/src/mage/cards/p/ProtectiveSphere.java new file mode 100644 index 0000000000..61b8104232 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProtectiveSphere.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.cards.p; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.Mana; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetSource; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public class ProtectiveSphere extends CardImpl { + + public ProtectiveSphere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // {1}, Pay 1 life: Prevent all damage that would be dealt to you this turn by a source of your choice that shares a color with the mana spent on this activation cost. + Ability ability = new SimpleActivatedAbility(new ProtectiveSphereEffect(), new ManaCostsImpl("{1}")); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + + } + + public ProtectiveSphere(final ProtectiveSphere card) { + super(card); + } + + @Override + public ProtectiveSphere copy() { + return new ProtectiveSphere(this); + } +} + +class ProtectiveSphereEffect extends PreventionEffectImpl { + + private final TargetSource target; + private static Mana manaUsed; + private static List colorsOfChosenSource = new ArrayList<>(); + + public ProtectiveSphereEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "Prevent all damage that would be dealt to you this turn by a source of your choice that shares a color with the mana spent on this activation cost."; + this.target = new TargetSource(); + } + + public ProtectiveSphereEffect(final ProtectiveSphereEffect effect) { + super(effect); + this.target = effect.target.copy(); + } + + @Override + public ProtectiveSphereEffect copy() { + return new ProtectiveSphereEffect(this); + } + + @Override + public void init(Ability source, Game game) { + target.setNotTarget(true); + target.setRequired(false); + Player controller = game.getPlayer(source.getControllerId()); + Permanent protectiveSphere = game.getPermanent(source.getSourceId()); + if (controller != null + && protectiveSphere != null) { + game.getState().setValue("ProtectiveSphere" + source.getSourceId().toString(), source.getManaCostsToPay().getUsedManaToPay()); //store the mana used to pay + protectiveSphere.addInfo("MANA USED", CardUtil.addToolTipMarkTags("Last mana used for protective ability: " + source.getManaCostsToPay().getUsedManaToPay()), game); + } + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + manaUsed = (Mana) game.getState().getValue("ProtectiveSphere" + source.getSourceId().toString()); + if (super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getControllerId()) + && event.getSourceId().equals(target.getFirstTarget())) { + colorsOfChosenSource = game.getObject(target.getFirstTarget()).getColor(game).getColors(); + if (colorsOfChosenSource.stream().anyMatch((c) -> (manaUsed.getColor(c.getColoredManaSymbol()) > 0))) { + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Invasion.java b/Mage.Sets/src/mage/sets/Invasion.java index 5e88651b8f..8e7acf64f8 100644 --- a/Mage.Sets/src/mage/sets/Invasion.java +++ b/Mage.Sets/src/mage/sets/Invasion.java @@ -1,387 +1,388 @@ -/* - * 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; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author North - */ -public class Invasion extends ExpansionSet { - - private static final Invasion instance = new Invasion(); - - public static Invasion getInstance() { - return instance; - } - - private Invasion() { - super("Invasion", "INV", ExpansionSet.buildDate(2000, 9, 2), SetType.EXPANSION); - this.blockName = "Invasion"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Absorb", 226, Rarity.RARE, mage.cards.a.Absorb.class)); - cards.add(new SetCardInfo("Addle", 91, Rarity.UNCOMMON, mage.cards.a.Addle.class)); - cards.add(new SetCardInfo("Aether Rift", 227, Rarity.RARE, mage.cards.a.AetherRift.class)); - cards.add(new SetCardInfo("Aggressive Urge", 181, Rarity.COMMON, mage.cards.a.AggressiveUrge.class)); - cards.add(new SetCardInfo("Agonizing Demise", 92, Rarity.COMMON, mage.cards.a.AgonizingDemise.class)); - cards.add(new SetCardInfo("Alabaster Leech", 1, Rarity.RARE, mage.cards.a.AlabasterLeech.class)); - cards.add(new SetCardInfo("Alloy Golem", 297, Rarity.UNCOMMON, mage.cards.a.AlloyGolem.class)); - cards.add(new SetCardInfo("Ancient Kavu", 136, Rarity.COMMON, mage.cards.a.AncientKavu.class)); - cards.add(new SetCardInfo("Ancient Spring", 319, Rarity.COMMON, mage.cards.a.AncientSpring.class)); - cards.add(new SetCardInfo("Andradite Leech", 93, Rarity.RARE, mage.cards.a.AndraditeLeech.class)); - cards.add(new SetCardInfo("Angelic Shield", 228, Rarity.UNCOMMON, mage.cards.a.AngelicShield.class)); - cards.add(new SetCardInfo("Angel of Mercy", 2, Rarity.UNCOMMON, mage.cards.a.AngelOfMercy.class)); - cards.add(new SetCardInfo("Annihilate", 94, Rarity.UNCOMMON, mage.cards.a.Annihilate.class)); - cards.add(new SetCardInfo("Archaeological Dig", 320, Rarity.UNCOMMON, mage.cards.a.ArchaeologicalDig.class)); - cards.add(new SetCardInfo("Ardent Soldier", 3, Rarity.COMMON, mage.cards.a.ArdentSoldier.class)); - cards.add(new SetCardInfo("Armadillo Cloak", 229, Rarity.COMMON, mage.cards.a.ArmadilloCloak.class)); - cards.add(new SetCardInfo("Armored Guardian", 230, Rarity.RARE, mage.cards.a.ArmoredGuardian.class)); - cards.add(new SetCardInfo("Artifact Mutation", 231, Rarity.RARE, mage.cards.a.ArtifactMutation.class)); - cards.add(new SetCardInfo("Assault // Battery", 295, Rarity.UNCOMMON, mage.cards.a.AssaultBattery.class)); - cards.add(new SetCardInfo("Atalya, Samite Master", 4, Rarity.RARE, mage.cards.a.AtalyaSamiteMaster.class)); - cards.add(new SetCardInfo("Aura Mutation", 232, Rarity.RARE, mage.cards.a.AuraMutation.class)); - cards.add(new SetCardInfo("Aura Shards", 233, Rarity.UNCOMMON, mage.cards.a.AuraShards.class)); - cards.add(new SetCardInfo("Backlash", 234, Rarity.UNCOMMON, mage.cards.b.Backlash.class)); - cards.add(new SetCardInfo("Barrin's Spite", 235, Rarity.RARE, mage.cards.b.BarrinsSpite.class)); - cards.add(new SetCardInfo("Barrin's Unmaking", 46, Rarity.COMMON, mage.cards.b.BarrinsUnmaking.class)); - cards.add(new SetCardInfo("Benalish Emissary", 5, Rarity.UNCOMMON, mage.cards.b.BenalishEmissary.class)); - cards.add(new SetCardInfo("Benalish Heralds", 6, Rarity.UNCOMMON, mage.cards.b.BenalishHeralds.class)); - cards.add(new SetCardInfo("Benalish Lancer", 7, Rarity.COMMON, mage.cards.b.BenalishLancer.class)); - cards.add(new SetCardInfo("Benalish Trapper", 8, Rarity.COMMON, mage.cards.b.BenalishTrapper.class)); - cards.add(new SetCardInfo("Bind", 182, Rarity.RARE, mage.cards.b.Bind.class)); - cards.add(new SetCardInfo("Blazing Specter", 236, Rarity.RARE, mage.cards.b.BlazingSpecter.class)); - cards.add(new SetCardInfo("Blinding Light", 9, Rarity.UNCOMMON, mage.cards.b.BlindingLight.class)); - cards.add(new SetCardInfo("Blind Seer", 47, Rarity.RARE, mage.cards.b.BlindSeer.class)); - cards.add(new SetCardInfo("Bloodstone Cameo", 298, Rarity.UNCOMMON, mage.cards.b.BloodstoneCameo.class)); - cards.add(new SetCardInfo("Blurred Mongoose", 183, Rarity.RARE, mage.cards.b.BlurredMongoose.class)); - cards.add(new SetCardInfo("Bog Initiate", 95, Rarity.COMMON, mage.cards.b.BogInitiate.class)); - cards.add(new SetCardInfo("Breaking Wave", 48, Rarity.RARE, mage.cards.b.BreakingWave.class)); - cards.add(new SetCardInfo("Breath of Darigaaz", 138, Rarity.UNCOMMON, mage.cards.b.BreathOfDarigaaz.class)); - cards.add(new SetCardInfo("Callous Giant", 139, Rarity.RARE, mage.cards.c.CallousGiant.class)); - cards.add(new SetCardInfo("Canopy Surge", 184, Rarity.UNCOMMON, mage.cards.c.CanopySurge.class)); - cards.add(new SetCardInfo("Capashen Unicorn", 10, Rarity.COMMON, mage.cards.c.CapashenUnicorn.class)); - cards.add(new SetCardInfo("Captain Sisay", 237, Rarity.RARE, mage.cards.c.CaptainSisay.class)); - cards.add(new SetCardInfo("Cauldron Dance", 238, Rarity.UNCOMMON, mage.cards.c.CauldronDance.class)); - cards.add(new SetCardInfo("Chaotic Strike", 140, Rarity.UNCOMMON, mage.cards.c.ChaoticStrike.class)); - cards.add(new SetCardInfo("Charging Troll", 239, Rarity.UNCOMMON, mage.cards.c.ChargingTroll.class)); - cards.add(new SetCardInfo("Chromatic Sphere", 299, Rarity.UNCOMMON, mage.cards.c.ChromaticSphere.class)); - cards.add(new SetCardInfo("Cinder Shade", 240, Rarity.UNCOMMON, mage.cards.c.CinderShade.class)); - cards.add(new SetCardInfo("Coalition Victory", 241, Rarity.RARE, mage.cards.c.CoalitionVictory.class)); - cards.add(new SetCardInfo("Coastal Tower", 321, Rarity.UNCOMMON, mage.cards.c.CoastalTower.class)); - cards.add(new SetCardInfo("Collapsing Borders", 141, Rarity.RARE, mage.cards.c.CollapsingBorders.class)); - cards.add(new SetCardInfo("Collective Restraint", 49, Rarity.RARE, mage.cards.c.CollectiveRestraint.class)); - cards.add(new SetCardInfo("Cremate", 96, Rarity.UNCOMMON, mage.cards.c.Cremate.class)); - cards.add(new SetCardInfo("Crimson Acolyte", 11, Rarity.COMMON, mage.cards.c.CrimsonAcolyte.class)); - cards.add(new SetCardInfo("Crosis's Attendant", 300, Rarity.UNCOMMON, mage.cards.c.CrosissAttendant.class)); - cards.add(new SetCardInfo("Crosis, the Purger", 242, Rarity.RARE, mage.cards.c.CrosisThePurger.class)); - cards.add(new SetCardInfo("Crown of Flames", 142, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); - cards.add(new SetCardInfo("Crusading Knight", 12, Rarity.RARE, mage.cards.c.CrusadingKnight.class)); - cards.add(new SetCardInfo("Crypt Angel", 97, Rarity.RARE, mage.cards.c.CryptAngel.class)); - cards.add(new SetCardInfo("Cursed Flesh", 98, Rarity.COMMON, mage.cards.c.CursedFlesh.class)); - cards.add(new SetCardInfo("Darigaaz's Attendant", 301, Rarity.UNCOMMON, mage.cards.d.DarigaazsAttendant.class)); - cards.add(new SetCardInfo("Darigaaz, the Igniter", 243, Rarity.RARE, mage.cards.d.DarigaazTheIgniter.class)); - cards.add(new SetCardInfo("Defiling Tears", 99, Rarity.UNCOMMON, mage.cards.d.DefilingTears.class)); - cards.add(new SetCardInfo("Devouring Strossus", 101, Rarity.RARE, mage.cards.d.DevouringStrossus.class)); - cards.add(new SetCardInfo("Dismantling Blow", 14, Rarity.COMMON, mage.cards.d.DismantlingBlow.class)); - cards.add(new SetCardInfo("Disrupt", 51, Rarity.UNCOMMON, mage.cards.d.Disrupt.class)); - cards.add(new SetCardInfo("Distorting Wake", 52, Rarity.RARE, mage.cards.d.DistortingWake.class)); - cards.add(new SetCardInfo("Divine Presence", 15, Rarity.RARE, mage.cards.d.DivinePresence.class)); - cards.add(new SetCardInfo("Do or Die", 102, Rarity.RARE, mage.cards.d.DoOrDie.class)); - cards.add(new SetCardInfo("Drake-Skull Cameo", 302, Rarity.UNCOMMON, mage.cards.d.DrakeSkullCameo.class)); - cards.add(new SetCardInfo("Dream Thrush", 53, Rarity.COMMON, mage.cards.d.DreamThrush.class)); - cards.add(new SetCardInfo("Dredge", 103, Rarity.UNCOMMON, mage.cards.d.Dredge.class)); - cards.add(new SetCardInfo("Dromar's Attendant", 303, Rarity.UNCOMMON, mage.cards.d.DromarsAttendant.class)); - cards.add(new SetCardInfo("Dromar, the Banisher", 244, Rarity.RARE, mage.cards.d.DromarTheBanisher.class)); - cards.add(new SetCardInfo("Dueling Grounds", 245, Rarity.RARE, mage.cards.d.DuelingGrounds.class)); - cards.add(new SetCardInfo("Duskwalker", 104, Rarity.COMMON, mage.cards.d.Duskwalker.class)); - cards.add(new SetCardInfo("Elfhame Palace", 322, Rarity.UNCOMMON, mage.cards.e.ElfhamePalace.class)); - cards.add(new SetCardInfo("Elfhame Sanctuary", 185, Rarity.UNCOMMON, mage.cards.e.ElfhameSanctuary.class)); - cards.add(new SetCardInfo("Elvish Champion", 186, Rarity.RARE, mage.cards.e.ElvishChampion.class)); - cards.add(new SetCardInfo("Empress Galina", 54, Rarity.RARE, mage.cards.e.EmpressGalina.class)); - cards.add(new SetCardInfo("Exclude", 56, Rarity.COMMON, mage.cards.e.Exclude.class)); - cards.add(new SetCardInfo("Exotic Curse", 105, Rarity.COMMON, mage.cards.e.ExoticCurse.class)); - cards.add(new SetCardInfo("Explosive Growth", 187, Rarity.COMMON, mage.cards.e.ExplosiveGrowth.class)); - cards.add(new SetCardInfo("Fact or Fiction", 57, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); - cards.add(new SetCardInfo("Faerie Squadron", 58, Rarity.COMMON, mage.cards.f.FaerieSquadron.class)); - cards.add(new SetCardInfo("Fertile Ground", 188, Rarity.COMMON, mage.cards.f.FertileGround.class)); - cards.add(new SetCardInfo("Firebrand Ranger", 143, Rarity.UNCOMMON, mage.cards.f.FirebrandRanger.class)); - cards.add(new SetCardInfo("Firescreamer", 106, Rarity.COMMON, mage.cards.f.Firescreamer.class)); - cards.add(new SetCardInfo("Fires of Yavimaya", 246, Rarity.UNCOMMON, mage.cards.f.FiresOfYavimaya.class)); - cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Frenzied Tilling", 247, Rarity.COMMON, mage.cards.f.FrenziedTilling.class)); - cards.add(new SetCardInfo("Galina's Knight", 248, Rarity.COMMON, mage.cards.g.GalinasKnight.class)); - cards.add(new SetCardInfo("Geothermal Crevice", 323, Rarity.COMMON, mage.cards.g.GeothermalCrevice.class)); - cards.add(new SetCardInfo("Ghitu Fire", 144, Rarity.RARE, mage.cards.g.GhituFire.class)); - cards.add(new SetCardInfo("Glimmering Angel", 17, Rarity.COMMON, mage.cards.g.GlimmeringAngel.class)); - cards.add(new SetCardInfo("Global Ruin", 18, Rarity.RARE, mage.cards.g.GlobalRuin.class)); - cards.add(new SetCardInfo("Goblin Spy", 145, Rarity.UNCOMMON, mage.cards.g.GoblinSpy.class)); - cards.add(new SetCardInfo("Goham Djinn", 107, Rarity.UNCOMMON, mage.cards.g.GohamDjinn.class)); - cards.add(new SetCardInfo("Halam Djinn", 146, Rarity.UNCOMMON, mage.cards.h.HalamDjinn.class)); - cards.add(new SetCardInfo("Hanna, Ship's Navigator", 249, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); - cards.add(new SetCardInfo("Harrow", 189, Rarity.COMMON, mage.cards.h.Harrow.class)); - cards.add(new SetCardInfo("Harsh Judgment", 19, Rarity.RARE, mage.cards.h.HarshJudgment.class)); - cards.add(new SetCardInfo("Hate Weaver", 108, Rarity.UNCOMMON, mage.cards.h.HateWeaver.class)); - cards.add(new SetCardInfo("Heroes' Reunion", 250, Rarity.UNCOMMON, mage.cards.h.HeroesReunion.class)); - cards.add(new SetCardInfo("Holy Day", 20, Rarity.COMMON, mage.cards.h.HolyDay.class)); - cards.add(new SetCardInfo("Hooded Kavu", 147, Rarity.COMMON, mage.cards.h.HoodedKavu.class)); - cards.add(new SetCardInfo("Horned Cheetah", 251, Rarity.UNCOMMON, mage.cards.h.HornedCheetah.class)); - cards.add(new SetCardInfo("Hunting Kavu", 252, Rarity.UNCOMMON, mage.cards.h.HuntingKavu.class)); - cards.add(new SetCardInfo("Hypnotic Cloud", 109, Rarity.COMMON, mage.cards.h.HypnoticCloud.class)); - cards.add(new SetCardInfo("Irrigation Ditch", 324, Rarity.COMMON, mage.cards.i.IrrigationDitch.class)); - cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jade Leech", 190, Rarity.RARE, mage.cards.j.JadeLeech.class)); - cards.add(new SetCardInfo("Juntu Stakes", 304, Rarity.RARE, mage.cards.j.JuntuStakes.class)); - cards.add(new SetCardInfo("Kangee, Aerie Keeper", 253, Rarity.RARE, mage.cards.k.KangeeAerieKeeper.class)); - cards.add(new SetCardInfo("Kavu Aggressor", 148, Rarity.COMMON, mage.cards.k.KavuAggressor.class)); - cards.add(new SetCardInfo("Kavu Chameleon", 191, Rarity.UNCOMMON, mage.cards.k.KavuChameleon.class)); - cards.add(new SetCardInfo("Kavu Climber", 192, Rarity.COMMON, mage.cards.k.KavuClimber.class)); - cards.add(new SetCardInfo("Kavu Lair", 193, Rarity.RARE, mage.cards.k.KavuLair.class)); - cards.add(new SetCardInfo("Kavu Monarch", 149, Rarity.RARE, mage.cards.k.KavuMonarch.class)); - cards.add(new SetCardInfo("Kavu Runner", 150, Rarity.UNCOMMON, mage.cards.k.KavuRunner.class)); - cards.add(new SetCardInfo("Kavu Scout", 151, Rarity.COMMON, mage.cards.k.KavuScout.class)); - cards.add(new SetCardInfo("Kavu Titan", 194, Rarity.RARE, mage.cards.k.KavuTitan.class)); - cards.add(new SetCardInfo("Keldon Necropolis", 325, Rarity.RARE, mage.cards.k.KeldonNecropolis.class)); - cards.add(new SetCardInfo("Liberate", 21, Rarity.UNCOMMON, mage.cards.l.Liberate.class)); - cards.add(new SetCardInfo("Lightning Dart", 152, Rarity.UNCOMMON, mage.cards.l.LightningDart.class)); - cards.add(new SetCardInfo("Llanowar Cavalry", 195, Rarity.COMMON, mage.cards.l.LlanowarCavalry.class)); - cards.add(new SetCardInfo("Llanowar Elite", 196, Rarity.COMMON, mage.cards.l.LlanowarElite.class)); - cards.add(new SetCardInfo("Llanowar Knight", 254, Rarity.COMMON, mage.cards.l.LlanowarKnight.class)); - cards.add(new SetCardInfo("Llanowar Vanguard", 197, Rarity.COMMON, mage.cards.l.LlanowarVanguard.class)); - cards.add(new SetCardInfo("Lobotomy", 255, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); - cards.add(new SetCardInfo("Lotus Guardian", 305, Rarity.RARE, mage.cards.l.LotusGuardian.class)); - cards.add(new SetCardInfo("Mana Maze", 59, Rarity.RARE, mage.cards.m.ManaMaze.class)); - cards.add(new SetCardInfo("Maniacal Rage", 155, Rarity.COMMON, mage.cards.m.ManiacalRage.class)); - cards.add(new SetCardInfo("Manipulate Fate", 60, Rarity.UNCOMMON, mage.cards.m.ManipulateFate.class)); - cards.add(new SetCardInfo("Marauding Knight", 110, Rarity.RARE, mage.cards.m.MaraudingKnight.class)); - cards.add(new SetCardInfo("Metathran Transport", 62, Rarity.UNCOMMON, mage.cards.m.MetathranTransport.class)); - cards.add(new SetCardInfo("Metathran Zombie", 63, Rarity.COMMON, mage.cards.m.MetathranZombie.class)); - cards.add(new SetCardInfo("Meteor Storm", 256, Rarity.RARE, mage.cards.m.MeteorStorm.class)); - cards.add(new SetCardInfo("Might Weaver", 198, Rarity.UNCOMMON, mage.cards.m.MightWeaver.class)); - cards.add(new SetCardInfo("Molimo, Maro-Sorcerer", 199, Rarity.RARE, mage.cards.m.MolimoMaroSorcerer.class)); - cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mourning", 111, Rarity.COMMON, mage.cards.m.Mourning.class)); - cards.add(new SetCardInfo("Nightscape Apprentice", 112, Rarity.COMMON, mage.cards.n.NightscapeApprentice.class)); - cards.add(new SetCardInfo("Nightscape Master", 113, Rarity.RARE, mage.cards.n.NightscapeMaster.class)); - cards.add(new SetCardInfo("Noble Panther", 257, Rarity.RARE, mage.cards.n.NoblePanther.class)); - cards.add(new SetCardInfo("Nomadic Elf", 200, Rarity.COMMON, mage.cards.n.NomadicElf.class)); - cards.add(new SetCardInfo("Obliterate", 156, Rarity.RARE, mage.cards.o.Obliterate.class)); - cards.add(new SetCardInfo("Obsidian Acolyte", 22, Rarity.COMMON, mage.cards.o.ObsidianAcolyte.class)); - cards.add(new SetCardInfo("Opt", 64, Rarity.COMMON, mage.cards.o.Opt.class)); - cards.add(new SetCardInfo("Ordered Migration", 258, Rarity.UNCOMMON, mage.cards.o.OrderedMigration.class)); - cards.add(new SetCardInfo("Orim's Touch", 23, Rarity.COMMON, mage.cards.o.OrimsTouch.class)); - cards.add(new SetCardInfo("Overabundance", 259, Rarity.RARE, mage.cards.o.Overabundance.class)); - cards.add(new SetCardInfo("Overload", 157, Rarity.COMMON, mage.cards.o.Overload.class)); - cards.add(new SetCardInfo("Pain // Suffering", 294, Rarity.UNCOMMON, mage.cards.p.PainSuffering.class)); - cards.add(new SetCardInfo("Phantasmal Terrain", 65, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); - cards.add(new SetCardInfo("Phyrexian Altar", 306, Rarity.RARE, mage.cards.p.PhyrexianAltar.class)); - cards.add(new SetCardInfo("Phyrexian Battleflies", 114, Rarity.COMMON, mage.cards.p.PhyrexianBattleflies.class)); - cards.add(new SetCardInfo("Phyrexian Delver", 115, Rarity.RARE, mage.cards.p.PhyrexianDelver.class)); - cards.add(new SetCardInfo("Phyrexian Infiltrator", 116, Rarity.RARE, mage.cards.p.PhyrexianInfiltrator.class)); - cards.add(new SetCardInfo("Phyrexian Lens", 307, Rarity.RARE, mage.cards.p.PhyrexianLens.class)); - cards.add(new SetCardInfo("Phyrexian Reaper", 117, Rarity.COMMON, mage.cards.p.PhyrexianReaper.class)); - cards.add(new SetCardInfo("Phyrexian Slayer", 118, Rarity.COMMON, mage.cards.p.PhyrexianSlayer.class)); - cards.add(new SetCardInfo("Pincer Spider", 201, Rarity.COMMON, mage.cards.p.PincerSpider.class)); - cards.add(new SetCardInfo("Plague Spitter", 119, Rarity.UNCOMMON, mage.cards.p.PlagueSpitter.class)); - cards.add(new SetCardInfo("Plague Spores", 260, Rarity.COMMON, mage.cards.p.PlagueSpores.class)); - cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Planar Portal", 308, Rarity.RARE, mage.cards.p.PlanarPortal.class)); - cards.add(new SetCardInfo("Pouncing Kavu", 158, Rarity.COMMON, mage.cards.p.PouncingKavu.class)); - cards.add(new SetCardInfo("Power Armor", 309, Rarity.UNCOMMON, mage.cards.p.PowerArmor.class)); - cards.add(new SetCardInfo("Prison Barricade", 25, Rarity.COMMON, mage.cards.p.PrisonBarricade.class)); - cards.add(new SetCardInfo("Probe", 66, Rarity.COMMON, mage.cards.p.Probe.class)); - cards.add(new SetCardInfo("Prohibit", 67, Rarity.COMMON, mage.cards.p.Prohibit.class)); - cards.add(new SetCardInfo("Pure Reflection", 27, Rarity.RARE, mage.cards.p.PureReflection.class)); - cards.add(new SetCardInfo("Pyre Zombie", 261, Rarity.RARE, mage.cards.p.PyreZombie.class)); - cards.add(new SetCardInfo("Quirion Elves", 203, Rarity.COMMON, mage.cards.q.QuirionElves.class)); - cards.add(new SetCardInfo("Quirion Sentinel", 204, Rarity.COMMON, mage.cards.q.QuirionSentinel.class)); - cards.add(new SetCardInfo("Quirion Trailblazer", 205, Rarity.COMMON, mage.cards.q.QuirionTrailblazer.class)); - cards.add(new SetCardInfo("Rage Weaver", 159, Rarity.UNCOMMON, mage.cards.r.RageWeaver.class)); - cards.add(new SetCardInfo("Raging Kavu", 262, Rarity.RARE, mage.cards.r.RagingKavu.class)); - cards.add(new SetCardInfo("Rainbow Crow", 69, Rarity.UNCOMMON, mage.cards.r.RainbowCrow.class)); - cards.add(new SetCardInfo("Rampant Elephant", 28, Rarity.COMMON, mage.cards.r.RampantElephant.class)); - cards.add(new SetCardInfo("Ravenous Rats", 120, Rarity.COMMON, mage.cards.r.RavenousRats.class)); - cards.add(new SetCardInfo("Razorfoot Griffin", 29, Rarity.COMMON, mage.cards.r.RazorfootGriffin.class)); - cards.add(new SetCardInfo("Reckless Assault", 263, Rarity.RARE, mage.cards.r.RecklessAssault.class)); - cards.add(new SetCardInfo("Reckless Spite", 121, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); - cards.add(new SetCardInfo("Recoil", 264, Rarity.COMMON, mage.cards.r.Recoil.class)); - cards.add(new SetCardInfo("Recover", 122, Rarity.COMMON, mage.cards.r.Recover.class)); - cards.add(new SetCardInfo("Repulse", 70, Rarity.COMMON, mage.cards.r.Repulse.class)); - cards.add(new SetCardInfo("Restock", 206, Rarity.RARE, mage.cards.r.Restock.class)); - cards.add(new SetCardInfo("Restrain", 30, Rarity.COMMON, mage.cards.r.Restrain.class)); - cards.add(new SetCardInfo("Reviving Dose", 31, Rarity.COMMON, mage.cards.r.RevivingDose.class)); - cards.add(new SetCardInfo("Reviving Vapors", 265, Rarity.UNCOMMON, mage.cards.r.RevivingVapors.class)); - cards.add(new SetCardInfo("Rewards of Diversity", 32, Rarity.UNCOMMON, mage.cards.r.RewardsOfDiversity.class)); - cards.add(new SetCardInfo("Reya Dawnbringer", 33, Rarity.RARE, mage.cards.r.ReyaDawnbringer.class)); - cards.add(new SetCardInfo("Riptide Crab", 266, Rarity.UNCOMMON, mage.cards.r.RiptideCrab.class)); - cards.add(new SetCardInfo("Rith's Attendant", 310, Rarity.UNCOMMON, mage.cards.r.RithsAttendant.class)); - cards.add(new SetCardInfo("Rith, the Awakener", 267, Rarity.RARE, mage.cards.r.RithTheAwakener.class)); - cards.add(new SetCardInfo("Rogue Kavu", 160, Rarity.COMMON, mage.cards.r.RogueKavu.class)); - cards.add(new SetCardInfo("Rooting Kavu", 207, Rarity.UNCOMMON, mage.cards.r.RootingKavu.class)); - cards.add(new SetCardInfo("Rout", 34, Rarity.RARE, mage.cards.r.Rout.class)); - cards.add(new SetCardInfo("Ruby Leech", 161, Rarity.RARE, mage.cards.r.RubyLeech.class)); - cards.add(new SetCardInfo("Ruham Djinn", 35, Rarity.UNCOMMON, mage.cards.r.RuhamDjinn.class)); - cards.add(new SetCardInfo("Sabertooth Nishoba", 268, Rarity.RARE, mage.cards.s.SabertoothNishoba.class)); - cards.add(new SetCardInfo("Salt Marsh", 326, Rarity.UNCOMMON, mage.cards.s.SaltMarsh.class)); - cards.add(new SetCardInfo("Samite Archer", 269, Rarity.UNCOMMON, mage.cards.s.SamiteArcher.class)); - cards.add(new SetCardInfo("Sapphire Leech", 71, Rarity.RARE, mage.cards.s.SapphireLeech.class)); - cards.add(new SetCardInfo("Saproling Symbiosis", 209, Rarity.RARE, mage.cards.s.SaprolingSymbiosis.class)); - cards.add(new SetCardInfo("Savage Offensive", 162, Rarity.COMMON, mage.cards.s.SavageOffensive.class)); - cards.add(new SetCardInfo("Scarred Puma", 163, Rarity.COMMON, mage.cards.s.ScarredPuma.class)); - cards.add(new SetCardInfo("Scavenged Weaponry", 123, Rarity.COMMON, mage.cards.s.ScavengedWeaponry.class)); - cards.add(new SetCardInfo("Scorching Lava", 164, Rarity.COMMON, mage.cards.s.ScorchingLava.class)); - cards.add(new SetCardInfo("Scouting Trek", 210, Rarity.UNCOMMON, mage.cards.s.ScoutingTrek.class)); - cards.add(new SetCardInfo("Seashell Cameo", 311, Rarity.UNCOMMON, mage.cards.s.SeashellCameo.class)); - cards.add(new SetCardInfo("Seer's Vision", 270, Rarity.UNCOMMON, mage.cards.s.SeersVision.class)); - cards.add(new SetCardInfo("Serpentine Kavu", 211, Rarity.COMMON, mage.cards.s.SerpentineKavu.class)); - cards.add(new SetCardInfo("Shackles", 37, Rarity.COMMON, mage.cards.s.Shackles.class)); - cards.add(new SetCardInfo("Shimmering Wings", 72, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); - cards.add(new SetCardInfo("Shivan Emissary", 166, Rarity.UNCOMMON, mage.cards.s.ShivanEmissary.class)); - cards.add(new SetCardInfo("Shivan Harvest", 167, Rarity.UNCOMMON, mage.cards.s.ShivanHarvest.class)); - cards.add(new SetCardInfo("Shivan Oasis", 327, Rarity.UNCOMMON, mage.cards.s.ShivanOasis.class)); - cards.add(new SetCardInfo("Shivan Zombie", 271, Rarity.COMMON, mage.cards.s.ShivanZombie.class)); - cards.add(new SetCardInfo("Shoreline Raider", 73, Rarity.COMMON, mage.cards.s.ShorelineRaider.class)); - cards.add(new SetCardInfo("Simoon", 272, Rarity.UNCOMMON, mage.cards.s.Simoon.class)); - cards.add(new SetCardInfo("Skittish Kavu", 168, Rarity.UNCOMMON, mage.cards.s.SkittishKavu.class)); - cards.add(new SetCardInfo("Skizzik", 169, Rarity.RARE, mage.cards.s.Skizzik.class)); - cards.add(new SetCardInfo("Sky Weaver", 74, Rarity.UNCOMMON, mage.cards.s.SkyWeaver.class)); - cards.add(new SetCardInfo("Sleeper's Robe", 273, Rarity.UNCOMMON, mage.cards.s.SleepersRobe.class)); - cards.add(new SetCardInfo("Slimy Kavu", 170, Rarity.COMMON, mage.cards.s.SlimyKavu.class)); - cards.add(new SetCardInfo("Slinking Serpent", 274, Rarity.UNCOMMON, mage.cards.s.SlinkingSerpent.class)); - cards.add(new SetCardInfo("Smoldering Tar", 275, Rarity.UNCOMMON, mage.cards.s.SmolderingTar.class)); - cards.add(new SetCardInfo("Soul Burn", 124, Rarity.COMMON, mage.cards.s.SoulBurn.class)); - cards.add(new SetCardInfo("Sparring Golem", 312, Rarity.UNCOMMON, mage.cards.s.SparringGolem.class)); - cards.add(new SetCardInfo("Spinal Embrace", 276, Rarity.RARE, mage.cards.s.SpinalEmbrace.class)); - cards.add(new SetCardInfo("Spirit of Resistance", 38, Rarity.RARE, mage.cards.s.SpiritOfResistance.class)); - cards.add(new SetCardInfo("Spirit Weaver", 39, Rarity.UNCOMMON, mage.cards.s.SpiritWeaver.class)); - cards.add(new SetCardInfo("Spite // Malice", 293, Rarity.UNCOMMON, mage.cards.s.SpiteMalice.class)); - cards.add(new SetCardInfo("Spreading Plague", 125, Rarity.RARE, mage.cards.s.SpreadingPlague.class)); - cards.add(new SetCardInfo("Stalking Assassin", 277, Rarity.RARE, mage.cards.s.StalkingAssassin.class)); - cards.add(new SetCardInfo("Stand // Deliver", 292, Rarity.UNCOMMON, mage.cards.s.StandDeliver.class)); - cards.add(new SetCardInfo("Sterling Grove", 278, Rarity.UNCOMMON, mage.cards.s.SterlingGrove.class)); - cards.add(new SetCardInfo("Stormscape Apprentice", 75, Rarity.COMMON, mage.cards.s.StormscapeApprentice.class)); - cards.add(new SetCardInfo("Stormscape Master", 76, Rarity.RARE, mage.cards.s.StormscapeMaster.class)); - cards.add(new SetCardInfo("Strength of Unity", 40, Rarity.COMMON, mage.cards.s.StrengthOfUnity.class)); - cards.add(new SetCardInfo("Stun", 172, Rarity.COMMON, mage.cards.s.Stun.class)); - cards.add(new SetCardInfo("Sulam Djinn", 212, Rarity.UNCOMMON, mage.cards.s.SulamDjinn.class)); - cards.add(new SetCardInfo("Sulfur Vent", 328, Rarity.COMMON, mage.cards.s.SulfurVent.class)); - cards.add(new SetCardInfo("Sunscape Apprentice", 41, Rarity.COMMON, mage.cards.s.SunscapeApprentice.class)); - cards.add(new SetCardInfo("Sunscape Master", 42, Rarity.RARE, mage.cards.s.SunscapeMaster.class)); - cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sway of Illusion", 77, Rarity.UNCOMMON, mage.cards.s.SwayOfIllusion.class)); - cards.add(new SetCardInfo("Tainted Well", 126, Rarity.COMMON, mage.cards.t.TaintedWell.class)); - cards.add(new SetCardInfo("Tangle", 213, Rarity.UNCOMMON, mage.cards.t.Tangle.class)); - cards.add(new SetCardInfo("Tectonic Instability", 173, Rarity.RARE, mage.cards.t.TectonicInstability.class)); - cards.add(new SetCardInfo("Teferi's Care", 43, Rarity.UNCOMMON, mage.cards.t.TeferisCare.class)); - cards.add(new SetCardInfo("Teferi's Moat", 279, Rarity.RARE, mage.cards.t.TeferisMoat.class)); - cards.add(new SetCardInfo("Teferi's Response", 78, Rarity.RARE, mage.cards.t.TeferisResponse.class)); - cards.add(new SetCardInfo("Tek", 313, Rarity.RARE, mage.cards.t.Tek.class)); - cards.add(new SetCardInfo("Temporal Distortion", 79, Rarity.RARE, mage.cards.t.TemporalDistortion.class)); - cards.add(new SetCardInfo("Thicket Elemental", 214, Rarity.RARE, mage.cards.t.ThicketElemental.class)); - cards.add(new SetCardInfo("Thornscape Apprentice", 215, Rarity.COMMON, mage.cards.t.ThornscapeApprentice.class)); - cards.add(new SetCardInfo("Thornscape Master", 216, Rarity.RARE, mage.cards.t.ThornscapeMaster.class)); - cards.add(new SetCardInfo("Thunderscape Apprentice", 174, Rarity.COMMON, mage.cards.t.ThunderscapeApprentice.class)); - cards.add(new SetCardInfo("Thunderscape Master", 175, Rarity.RARE, mage.cards.t.ThunderscapeMaster.class)); - cards.add(new SetCardInfo("Tidal Visionary", 80, Rarity.COMMON, mage.cards.t.TidalVisionary.class)); - cards.add(new SetCardInfo("Tigereye Cameo", 314, Rarity.UNCOMMON, mage.cards.t.TigereyeCameo.class)); - cards.add(new SetCardInfo("Tinder Farm", 329, Rarity.COMMON, mage.cards.t.TinderFarm.class)); - cards.add(new SetCardInfo("Tolarian Emissary", 81, Rarity.UNCOMMON, mage.cards.t.TolarianEmissary.class)); - cards.add(new SetCardInfo("Tower Drake", 82, Rarity.COMMON, mage.cards.t.TowerDrake.class)); - cards.add(new SetCardInfo("Tranquility", 217, Rarity.COMMON, mage.cards.t.Tranquility.class)); - cards.add(new SetCardInfo("Traveler's Cloak", 83, Rarity.COMMON, mage.cards.t.TravelersCloak.class)); - cards.add(new SetCardInfo("Treefolk Healer", 218, Rarity.UNCOMMON, mage.cards.t.TreefolkHealer.class)); - cards.add(new SetCardInfo("Trench Wurm", 127, Rarity.UNCOMMON, mage.cards.t.TrenchWurm.class)); - cards.add(new SetCardInfo("Treva's Attendant", 315, Rarity.UNCOMMON, mage.cards.t.TrevasAttendant.class)); - cards.add(new SetCardInfo("Treva, the Renewer", 280, Rarity.RARE, mage.cards.t.TrevaTheRenewer.class)); - cards.add(new SetCardInfo("Tribal Flames", 176, Rarity.COMMON, mage.cards.t.TribalFlames.class)); - cards.add(new SetCardInfo("Troll-Horn Cameo", 316, Rarity.UNCOMMON, mage.cards.t.TrollHornCameo.class)); - cards.add(new SetCardInfo("Tsabo's Assassin", 128, Rarity.RARE, mage.cards.t.TsabosAssassin.class)); - cards.add(new SetCardInfo("Tsabo's Decree", 129, Rarity.RARE, mage.cards.t.TsabosDecree.class)); - cards.add(new SetCardInfo("Tsabo's Web", 317, Rarity.RARE, mage.cards.t.TsabosWeb.class)); - cards.add(new SetCardInfo("Tsabo Tavoc", 281, Rarity.RARE, mage.cards.t.TsaboTavoc.class)); - cards.add(new SetCardInfo("Turf Wound", 177, Rarity.COMMON, mage.cards.t.TurfWound.class)); - cards.add(new SetCardInfo("Twilight's Call", 130, Rarity.RARE, mage.cards.t.TwilightsCall.class)); - cards.add(new SetCardInfo("Undermine", 282, Rarity.RARE, mage.cards.u.Undermine.class)); - cards.add(new SetCardInfo("Urborg Drake", 283, Rarity.UNCOMMON, mage.cards.u.UrborgDrake.class)); - cards.add(new SetCardInfo("Urborg Emissary", 131, Rarity.UNCOMMON, mage.cards.u.UrborgEmissary.class)); - cards.add(new SetCardInfo("Urborg Phantom", 132, Rarity.COMMON, mage.cards.u.UrborgPhantom.class)); - cards.add(new SetCardInfo("Urborg Shambler", 133, Rarity.UNCOMMON, mage.cards.u.UrborgShambler.class)); - cards.add(new SetCardInfo("Urborg Skeleton", 134, Rarity.COMMON, mage.cards.u.UrborgSkeleton.class)); - cards.add(new SetCardInfo("Urborg Volcano", 330, Rarity.UNCOMMON, mage.cards.u.UrborgVolcano.class)); - cards.add(new SetCardInfo("Urza's Filter", 318, Rarity.RARE, mage.cards.u.UrzasFilter.class)); - cards.add(new SetCardInfo("Urza's Rage", 178, Rarity.RARE, mage.cards.u.UrzasRage.class)); - cards.add(new SetCardInfo("Utopia Tree", 219, Rarity.RARE, mage.cards.u.UtopiaTree.class)); - cards.add(new SetCardInfo("Verdeloth the Ancient", 220, Rarity.RARE, mage.cards.v.VerdelothTheAncient.class)); - cards.add(new SetCardInfo("Verduran Emissary", 221, Rarity.UNCOMMON, mage.cards.v.VerduranEmissary.class)); - cards.add(new SetCardInfo("Viashino Grappler", 179, Rarity.COMMON, mage.cards.v.ViashinoGrappler.class)); - cards.add(new SetCardInfo("Vicious Kavu", 284, Rarity.UNCOMMON, mage.cards.v.ViciousKavu.class)); - cards.add(new SetCardInfo("Vile Consumption", 285, Rarity.RARE, mage.cards.v.VileConsumption.class)); - cards.add(new SetCardInfo("Vodalian Hypnotist", 84, Rarity.UNCOMMON, mage.cards.v.VodalianHypnotist.class)); - cards.add(new SetCardInfo("Vodalian Merchant", 85, Rarity.COMMON, mage.cards.v.VodalianMerchant.class)); - cards.add(new SetCardInfo("Vodalian Serpent", 86, Rarity.COMMON, mage.cards.v.VodalianSerpent.class)); - cards.add(new SetCardInfo("Vodalian Zombie", 286, Rarity.COMMON, mage.cards.v.VodalianZombie.class)); - cards.add(new SetCardInfo("Void", 287, Rarity.RARE, mage.cards.v.Void.class)); - cards.add(new SetCardInfo("Voracious Cobra", 288, Rarity.UNCOMMON, mage.cards.v.VoraciousCobra.class)); - cards.add(new SetCardInfo("Wallop", 223, Rarity.UNCOMMON, mage.cards.w.Wallop.class)); - cards.add(new SetCardInfo("Wandering Stream", 224, Rarity.COMMON, mage.cards.w.WanderingStream.class)); - cards.add(new SetCardInfo("Wash Out", 87, Rarity.UNCOMMON, mage.cards.w.WashOut.class)); - cards.add(new SetCardInfo("Wax // Wane", 296, Rarity.UNCOMMON, mage.cards.w.WaxWane.class)); - cards.add(new SetCardInfo("Wayfaring Giant", 44, Rarity.UNCOMMON, mage.cards.w.WayfaringGiant.class)); - cards.add(new SetCardInfo("Whip Silk", 225, Rarity.COMMON, mage.cards.w.WhipSilk.class)); - cards.add(new SetCardInfo("Wings of Hope", 289, Rarity.COMMON, mage.cards.w.WingsOfHope.class)); - cards.add(new SetCardInfo("Winnow", 45, Rarity.RARE, mage.cards.w.Winnow.class)); - cards.add(new SetCardInfo("Worldly Counsel", 89, Rarity.COMMON, mage.cards.w.WorldlyCounsel.class)); - cards.add(new SetCardInfo("Yavimaya Barbarian", 290, Rarity.COMMON, mage.cards.y.YavimayaBarbarian.class)); - cards.add(new SetCardInfo("Yavimaya Kavu", 291, Rarity.UNCOMMON, mage.cards.y.YavimayaKavu.class)); - cards.add(new SetCardInfo("Yawgmoth's Agenda", 135, Rarity.RARE, mage.cards.y.YawgmothsAgenda.class)); - cards.add(new SetCardInfo("Zanam Djinn", 90, Rarity.UNCOMMON, mage.cards.z.ZanamDjinn.class)); - cards.add(new SetCardInfo("Zap", 180, Rarity.COMMON, mage.cards.z.Zap.class)); - } -} +/* + * 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; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author North + */ +public class Invasion extends ExpansionSet { + + private static final Invasion instance = new Invasion(); + + public static Invasion getInstance() { + return instance; + } + + private Invasion() { + super("Invasion", "INV", ExpansionSet.buildDate(2000, 9, 2), SetType.EXPANSION); + this.blockName = "Invasion"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Absorb", 226, Rarity.RARE, mage.cards.a.Absorb.class)); + cards.add(new SetCardInfo("Addle", 91, Rarity.UNCOMMON, mage.cards.a.Addle.class)); + cards.add(new SetCardInfo("Aether Rift", 227, Rarity.RARE, mage.cards.a.AetherRift.class)); + cards.add(new SetCardInfo("Aggressive Urge", 181, Rarity.COMMON, mage.cards.a.AggressiveUrge.class)); + cards.add(new SetCardInfo("Agonizing Demise", 92, Rarity.COMMON, mage.cards.a.AgonizingDemise.class)); + cards.add(new SetCardInfo("Alabaster Leech", 1, Rarity.RARE, mage.cards.a.AlabasterLeech.class)); + cards.add(new SetCardInfo("Alloy Golem", 297, Rarity.UNCOMMON, mage.cards.a.AlloyGolem.class)); + cards.add(new SetCardInfo("Ancient Kavu", 136, Rarity.COMMON, mage.cards.a.AncientKavu.class)); + cards.add(new SetCardInfo("Ancient Spring", 319, Rarity.COMMON, mage.cards.a.AncientSpring.class)); + cards.add(new SetCardInfo("Andradite Leech", 93, Rarity.RARE, mage.cards.a.AndraditeLeech.class)); + cards.add(new SetCardInfo("Angelic Shield", 228, Rarity.UNCOMMON, mage.cards.a.AngelicShield.class)); + cards.add(new SetCardInfo("Angel of Mercy", 2, Rarity.UNCOMMON, mage.cards.a.AngelOfMercy.class)); + cards.add(new SetCardInfo("Annihilate", 94, Rarity.UNCOMMON, mage.cards.a.Annihilate.class)); + cards.add(new SetCardInfo("Archaeological Dig", 320, Rarity.UNCOMMON, mage.cards.a.ArchaeologicalDig.class)); + cards.add(new SetCardInfo("Ardent Soldier", 3, Rarity.COMMON, mage.cards.a.ArdentSoldier.class)); + cards.add(new SetCardInfo("Armadillo Cloak", 229, Rarity.COMMON, mage.cards.a.ArmadilloCloak.class)); + cards.add(new SetCardInfo("Armored Guardian", 230, Rarity.RARE, mage.cards.a.ArmoredGuardian.class)); + cards.add(new SetCardInfo("Artifact Mutation", 231, Rarity.RARE, mage.cards.a.ArtifactMutation.class)); + cards.add(new SetCardInfo("Assault // Battery", 295, Rarity.UNCOMMON, mage.cards.a.AssaultBattery.class)); + cards.add(new SetCardInfo("Atalya, Samite Master", 4, Rarity.RARE, mage.cards.a.AtalyaSamiteMaster.class)); + cards.add(new SetCardInfo("Aura Mutation", 232, Rarity.RARE, mage.cards.a.AuraMutation.class)); + cards.add(new SetCardInfo("Aura Shards", 233, Rarity.UNCOMMON, mage.cards.a.AuraShards.class)); + cards.add(new SetCardInfo("Backlash", 234, Rarity.UNCOMMON, mage.cards.b.Backlash.class)); + cards.add(new SetCardInfo("Barrin's Spite", 235, Rarity.RARE, mage.cards.b.BarrinsSpite.class)); + cards.add(new SetCardInfo("Barrin's Unmaking", 46, Rarity.COMMON, mage.cards.b.BarrinsUnmaking.class)); + cards.add(new SetCardInfo("Benalish Emissary", 5, Rarity.UNCOMMON, mage.cards.b.BenalishEmissary.class)); + cards.add(new SetCardInfo("Benalish Heralds", 6, Rarity.UNCOMMON, mage.cards.b.BenalishHeralds.class)); + cards.add(new SetCardInfo("Benalish Lancer", 7, Rarity.COMMON, mage.cards.b.BenalishLancer.class)); + cards.add(new SetCardInfo("Benalish Trapper", 8, Rarity.COMMON, mage.cards.b.BenalishTrapper.class)); + cards.add(new SetCardInfo("Bind", 182, Rarity.RARE, mage.cards.b.Bind.class)); + cards.add(new SetCardInfo("Blazing Specter", 236, Rarity.RARE, mage.cards.b.BlazingSpecter.class)); + cards.add(new SetCardInfo("Blinding Light", 9, Rarity.UNCOMMON, mage.cards.b.BlindingLight.class)); + cards.add(new SetCardInfo("Blind Seer", 47, Rarity.RARE, mage.cards.b.BlindSeer.class)); + cards.add(new SetCardInfo("Bloodstone Cameo", 298, Rarity.UNCOMMON, mage.cards.b.BloodstoneCameo.class)); + cards.add(new SetCardInfo("Blurred Mongoose", 183, Rarity.RARE, mage.cards.b.BlurredMongoose.class)); + cards.add(new SetCardInfo("Bog Initiate", 95, Rarity.COMMON, mage.cards.b.BogInitiate.class)); + cards.add(new SetCardInfo("Breaking Wave", 48, Rarity.RARE, mage.cards.b.BreakingWave.class)); + cards.add(new SetCardInfo("Breath of Darigaaz", 138, Rarity.UNCOMMON, mage.cards.b.BreathOfDarigaaz.class)); + cards.add(new SetCardInfo("Callous Giant", 139, Rarity.RARE, mage.cards.c.CallousGiant.class)); + cards.add(new SetCardInfo("Canopy Surge", 184, Rarity.UNCOMMON, mage.cards.c.CanopySurge.class)); + cards.add(new SetCardInfo("Capashen Unicorn", 10, Rarity.COMMON, mage.cards.c.CapashenUnicorn.class)); + cards.add(new SetCardInfo("Captain Sisay", 237, Rarity.RARE, mage.cards.c.CaptainSisay.class)); + cards.add(new SetCardInfo("Cauldron Dance", 238, Rarity.UNCOMMON, mage.cards.c.CauldronDance.class)); + cards.add(new SetCardInfo("Chaotic Strike", 140, Rarity.UNCOMMON, mage.cards.c.ChaoticStrike.class)); + cards.add(new SetCardInfo("Charging Troll", 239, Rarity.UNCOMMON, mage.cards.c.ChargingTroll.class)); + cards.add(new SetCardInfo("Chromatic Sphere", 299, Rarity.UNCOMMON, mage.cards.c.ChromaticSphere.class)); + cards.add(new SetCardInfo("Cinder Shade", 240, Rarity.UNCOMMON, mage.cards.c.CinderShade.class)); + cards.add(new SetCardInfo("Coalition Victory", 241, Rarity.RARE, mage.cards.c.CoalitionVictory.class)); + cards.add(new SetCardInfo("Coastal Tower", 321, Rarity.UNCOMMON, mage.cards.c.CoastalTower.class)); + cards.add(new SetCardInfo("Collapsing Borders", 141, Rarity.RARE, mage.cards.c.CollapsingBorders.class)); + cards.add(new SetCardInfo("Collective Restraint", 49, Rarity.RARE, mage.cards.c.CollectiveRestraint.class)); + cards.add(new SetCardInfo("Cremate", 96, Rarity.UNCOMMON, mage.cards.c.Cremate.class)); + cards.add(new SetCardInfo("Crimson Acolyte", 11, Rarity.COMMON, mage.cards.c.CrimsonAcolyte.class)); + cards.add(new SetCardInfo("Crosis's Attendant", 300, Rarity.UNCOMMON, mage.cards.c.CrosissAttendant.class)); + cards.add(new SetCardInfo("Crosis, the Purger", 242, Rarity.RARE, mage.cards.c.CrosisThePurger.class)); + cards.add(new SetCardInfo("Crown of Flames", 142, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); + cards.add(new SetCardInfo("Crusading Knight", 12, Rarity.RARE, mage.cards.c.CrusadingKnight.class)); + cards.add(new SetCardInfo("Crypt Angel", 97, Rarity.RARE, mage.cards.c.CryptAngel.class)); + cards.add(new SetCardInfo("Cursed Flesh", 98, Rarity.COMMON, mage.cards.c.CursedFlesh.class)); + cards.add(new SetCardInfo("Darigaaz's Attendant", 301, Rarity.UNCOMMON, mage.cards.d.DarigaazsAttendant.class)); + cards.add(new SetCardInfo("Darigaaz, the Igniter", 243, Rarity.RARE, mage.cards.d.DarigaazTheIgniter.class)); + cards.add(new SetCardInfo("Defiling Tears", 99, Rarity.UNCOMMON, mage.cards.d.DefilingTears.class)); + cards.add(new SetCardInfo("Devouring Strossus", 101, Rarity.RARE, mage.cards.d.DevouringStrossus.class)); + cards.add(new SetCardInfo("Dismantling Blow", 14, Rarity.COMMON, mage.cards.d.DismantlingBlow.class)); + cards.add(new SetCardInfo("Disrupt", 51, Rarity.UNCOMMON, mage.cards.d.Disrupt.class)); + cards.add(new SetCardInfo("Distorting Wake", 52, Rarity.RARE, mage.cards.d.DistortingWake.class)); + cards.add(new SetCardInfo("Divine Presence", 15, Rarity.RARE, mage.cards.d.DivinePresence.class)); + cards.add(new SetCardInfo("Do or Die", 102, Rarity.RARE, mage.cards.d.DoOrDie.class)); + cards.add(new SetCardInfo("Drake-Skull Cameo", 302, Rarity.UNCOMMON, mage.cards.d.DrakeSkullCameo.class)); + cards.add(new SetCardInfo("Dream Thrush", 53, Rarity.COMMON, mage.cards.d.DreamThrush.class)); + cards.add(new SetCardInfo("Dredge", 103, Rarity.UNCOMMON, mage.cards.d.Dredge.class)); + cards.add(new SetCardInfo("Dromar's Attendant", 303, Rarity.UNCOMMON, mage.cards.d.DromarsAttendant.class)); + cards.add(new SetCardInfo("Dromar, the Banisher", 244, Rarity.RARE, mage.cards.d.DromarTheBanisher.class)); + cards.add(new SetCardInfo("Dueling Grounds", 245, Rarity.RARE, mage.cards.d.DuelingGrounds.class)); + cards.add(new SetCardInfo("Duskwalker", 104, Rarity.COMMON, mage.cards.d.Duskwalker.class)); + cards.add(new SetCardInfo("Elfhame Palace", 322, Rarity.UNCOMMON, mage.cards.e.ElfhamePalace.class)); + cards.add(new SetCardInfo("Elfhame Sanctuary", 185, Rarity.UNCOMMON, mage.cards.e.ElfhameSanctuary.class)); + cards.add(new SetCardInfo("Elvish Champion", 186, Rarity.RARE, mage.cards.e.ElvishChampion.class)); + cards.add(new SetCardInfo("Empress Galina", 54, Rarity.RARE, mage.cards.e.EmpressGalina.class)); + cards.add(new SetCardInfo("Exclude", 56, Rarity.COMMON, mage.cards.e.Exclude.class)); + cards.add(new SetCardInfo("Exotic Curse", 105, Rarity.COMMON, mage.cards.e.ExoticCurse.class)); + cards.add(new SetCardInfo("Explosive Growth", 187, Rarity.COMMON, mage.cards.e.ExplosiveGrowth.class)); + cards.add(new SetCardInfo("Fact or Fiction", 57, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); + cards.add(new SetCardInfo("Faerie Squadron", 58, Rarity.COMMON, mage.cards.f.FaerieSquadron.class)); + cards.add(new SetCardInfo("Fertile Ground", 188, Rarity.COMMON, mage.cards.f.FertileGround.class)); + cards.add(new SetCardInfo("Firebrand Ranger", 143, Rarity.UNCOMMON, mage.cards.f.FirebrandRanger.class)); + cards.add(new SetCardInfo("Firescreamer", 106, Rarity.COMMON, mage.cards.f.Firescreamer.class)); + cards.add(new SetCardInfo("Fires of Yavimaya", 246, Rarity.UNCOMMON, mage.cards.f.FiresOfYavimaya.class)); + cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frenzied Tilling", 247, Rarity.COMMON, mage.cards.f.FrenziedTilling.class)); + cards.add(new SetCardInfo("Galina's Knight", 248, Rarity.COMMON, mage.cards.g.GalinasKnight.class)); + cards.add(new SetCardInfo("Geothermal Crevice", 323, Rarity.COMMON, mage.cards.g.GeothermalCrevice.class)); + cards.add(new SetCardInfo("Ghitu Fire", 144, Rarity.RARE, mage.cards.g.GhituFire.class)); + cards.add(new SetCardInfo("Glimmering Angel", 17, Rarity.COMMON, mage.cards.g.GlimmeringAngel.class)); + cards.add(new SetCardInfo("Global Ruin", 18, Rarity.RARE, mage.cards.g.GlobalRuin.class)); + cards.add(new SetCardInfo("Goblin Spy", 145, Rarity.UNCOMMON, mage.cards.g.GoblinSpy.class)); + cards.add(new SetCardInfo("Goham Djinn", 107, Rarity.UNCOMMON, mage.cards.g.GohamDjinn.class)); + cards.add(new SetCardInfo("Halam Djinn", 146, Rarity.UNCOMMON, mage.cards.h.HalamDjinn.class)); + cards.add(new SetCardInfo("Hanna, Ship's Navigator", 249, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); + cards.add(new SetCardInfo("Harrow", 189, Rarity.COMMON, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("Harsh Judgment", 19, Rarity.RARE, mage.cards.h.HarshJudgment.class)); + cards.add(new SetCardInfo("Hate Weaver", 108, Rarity.UNCOMMON, mage.cards.h.HateWeaver.class)); + cards.add(new SetCardInfo("Heroes' Reunion", 250, Rarity.UNCOMMON, mage.cards.h.HeroesReunion.class)); + cards.add(new SetCardInfo("Holy Day", 20, Rarity.COMMON, mage.cards.h.HolyDay.class)); + cards.add(new SetCardInfo("Hooded Kavu", 147, Rarity.COMMON, mage.cards.h.HoodedKavu.class)); + cards.add(new SetCardInfo("Horned Cheetah", 251, Rarity.UNCOMMON, mage.cards.h.HornedCheetah.class)); + cards.add(new SetCardInfo("Hunting Kavu", 252, Rarity.UNCOMMON, mage.cards.h.HuntingKavu.class)); + cards.add(new SetCardInfo("Hypnotic Cloud", 109, Rarity.COMMON, mage.cards.h.HypnoticCloud.class)); + cards.add(new SetCardInfo("Irrigation Ditch", 324, Rarity.COMMON, mage.cards.i.IrrigationDitch.class)); + cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jade Leech", 190, Rarity.RARE, mage.cards.j.JadeLeech.class)); + cards.add(new SetCardInfo("Juntu Stakes", 304, Rarity.RARE, mage.cards.j.JuntuStakes.class)); + cards.add(new SetCardInfo("Kangee, Aerie Keeper", 253, Rarity.RARE, mage.cards.k.KangeeAerieKeeper.class)); + cards.add(new SetCardInfo("Kavu Aggressor", 148, Rarity.COMMON, mage.cards.k.KavuAggressor.class)); + cards.add(new SetCardInfo("Kavu Chameleon", 191, Rarity.UNCOMMON, mage.cards.k.KavuChameleon.class)); + cards.add(new SetCardInfo("Kavu Climber", 192, Rarity.COMMON, mage.cards.k.KavuClimber.class)); + cards.add(new SetCardInfo("Kavu Lair", 193, Rarity.RARE, mage.cards.k.KavuLair.class)); + cards.add(new SetCardInfo("Kavu Monarch", 149, Rarity.RARE, mage.cards.k.KavuMonarch.class)); + cards.add(new SetCardInfo("Kavu Runner", 150, Rarity.UNCOMMON, mage.cards.k.KavuRunner.class)); + cards.add(new SetCardInfo("Kavu Scout", 151, Rarity.COMMON, mage.cards.k.KavuScout.class)); + cards.add(new SetCardInfo("Kavu Titan", 194, Rarity.RARE, mage.cards.k.KavuTitan.class)); + cards.add(new SetCardInfo("Keldon Necropolis", 325, Rarity.RARE, mage.cards.k.KeldonNecropolis.class)); + cards.add(new SetCardInfo("Liberate", 21, Rarity.UNCOMMON, mage.cards.l.Liberate.class)); + cards.add(new SetCardInfo("Lightning Dart", 152, Rarity.UNCOMMON, mage.cards.l.LightningDart.class)); + cards.add(new SetCardInfo("Llanowar Cavalry", 195, Rarity.COMMON, mage.cards.l.LlanowarCavalry.class)); + cards.add(new SetCardInfo("Llanowar Elite", 196, Rarity.COMMON, mage.cards.l.LlanowarElite.class)); + cards.add(new SetCardInfo("Llanowar Knight", 254, Rarity.COMMON, mage.cards.l.LlanowarKnight.class)); + cards.add(new SetCardInfo("Llanowar Vanguard", 197, Rarity.COMMON, mage.cards.l.LlanowarVanguard.class)); + cards.add(new SetCardInfo("Lobotomy", 255, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); + cards.add(new SetCardInfo("Lotus Guardian", 305, Rarity.RARE, mage.cards.l.LotusGuardian.class)); + cards.add(new SetCardInfo("Mana Maze", 59, Rarity.RARE, mage.cards.m.ManaMaze.class)); + cards.add(new SetCardInfo("Maniacal Rage", 155, Rarity.COMMON, mage.cards.m.ManiacalRage.class)); + cards.add(new SetCardInfo("Manipulate Fate", 60, Rarity.UNCOMMON, mage.cards.m.ManipulateFate.class)); + cards.add(new SetCardInfo("Marauding Knight", 110, Rarity.RARE, mage.cards.m.MaraudingKnight.class)); + cards.add(new SetCardInfo("Metathran Transport", 62, Rarity.UNCOMMON, mage.cards.m.MetathranTransport.class)); + cards.add(new SetCardInfo("Metathran Zombie", 63, Rarity.COMMON, mage.cards.m.MetathranZombie.class)); + cards.add(new SetCardInfo("Meteor Storm", 256, Rarity.RARE, mage.cards.m.MeteorStorm.class)); + cards.add(new SetCardInfo("Might Weaver", 198, Rarity.UNCOMMON, mage.cards.m.MightWeaver.class)); + cards.add(new SetCardInfo("Molimo, Maro-Sorcerer", 199, Rarity.RARE, mage.cards.m.MolimoMaroSorcerer.class)); + cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mourning", 111, Rarity.COMMON, mage.cards.m.Mourning.class)); + cards.add(new SetCardInfo("Nightscape Apprentice", 112, Rarity.COMMON, mage.cards.n.NightscapeApprentice.class)); + cards.add(new SetCardInfo("Nightscape Master", 113, Rarity.RARE, mage.cards.n.NightscapeMaster.class)); + cards.add(new SetCardInfo("Noble Panther", 257, Rarity.RARE, mage.cards.n.NoblePanther.class)); + cards.add(new SetCardInfo("Nomadic Elf", 200, Rarity.COMMON, mage.cards.n.NomadicElf.class)); + cards.add(new SetCardInfo("Obliterate", 156, Rarity.RARE, mage.cards.o.Obliterate.class)); + cards.add(new SetCardInfo("Obsidian Acolyte", 22, Rarity.COMMON, mage.cards.o.ObsidianAcolyte.class)); + cards.add(new SetCardInfo("Opt", 64, Rarity.COMMON, mage.cards.o.Opt.class)); + cards.add(new SetCardInfo("Ordered Migration", 258, Rarity.UNCOMMON, mage.cards.o.OrderedMigration.class)); + cards.add(new SetCardInfo("Orim's Touch", 23, Rarity.COMMON, mage.cards.o.OrimsTouch.class)); + cards.add(new SetCardInfo("Overabundance", 259, Rarity.RARE, mage.cards.o.Overabundance.class)); + cards.add(new SetCardInfo("Overload", 157, Rarity.COMMON, mage.cards.o.Overload.class)); + cards.add(new SetCardInfo("Pain // Suffering", 294, Rarity.UNCOMMON, mage.cards.p.PainSuffering.class)); + cards.add(new SetCardInfo("Phantasmal Terrain", 65, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class)); + cards.add(new SetCardInfo("Phyrexian Altar", 306, Rarity.RARE, mage.cards.p.PhyrexianAltar.class)); + cards.add(new SetCardInfo("Phyrexian Battleflies", 114, Rarity.COMMON, mage.cards.p.PhyrexianBattleflies.class)); + cards.add(new SetCardInfo("Phyrexian Delver", 115, Rarity.RARE, mage.cards.p.PhyrexianDelver.class)); + cards.add(new SetCardInfo("Phyrexian Infiltrator", 116, Rarity.RARE, mage.cards.p.PhyrexianInfiltrator.class)); + cards.add(new SetCardInfo("Phyrexian Lens", 307, Rarity.RARE, mage.cards.p.PhyrexianLens.class)); + cards.add(new SetCardInfo("Phyrexian Reaper", 117, Rarity.COMMON, mage.cards.p.PhyrexianReaper.class)); + cards.add(new SetCardInfo("Phyrexian Slayer", 118, Rarity.COMMON, mage.cards.p.PhyrexianSlayer.class)); + cards.add(new SetCardInfo("Pincer Spider", 201, Rarity.COMMON, mage.cards.p.PincerSpider.class)); + cards.add(new SetCardInfo("Plague Spitter", 119, Rarity.UNCOMMON, mage.cards.p.PlagueSpitter.class)); + cards.add(new SetCardInfo("Plague Spores", 260, Rarity.COMMON, mage.cards.p.PlagueSpores.class)); + cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Planar Portal", 308, Rarity.RARE, mage.cards.p.PlanarPortal.class)); + cards.add(new SetCardInfo("Pouncing Kavu", 158, Rarity.COMMON, mage.cards.p.PouncingKavu.class)); + cards.add(new SetCardInfo("Power Armor", 309, Rarity.UNCOMMON, mage.cards.p.PowerArmor.class)); + cards.add(new SetCardInfo("Prison Barricade", 25, Rarity.COMMON, mage.cards.p.PrisonBarricade.class)); + cards.add(new SetCardInfo("Probe", 66, Rarity.COMMON, mage.cards.p.Probe.class)); + cards.add(new SetCardInfo("Prohibit", 67, Rarity.COMMON, mage.cards.p.Prohibit.class)); + cards.add(new SetCardInfo("Protective Sphere", 26, Rarity.COMMON, mage.cards.p.ProtectiveSphere.class)); + cards.add(new SetCardInfo("Pure Reflection", 27, Rarity.RARE, mage.cards.p.PureReflection.class)); + cards.add(new SetCardInfo("Pyre Zombie", 261, Rarity.RARE, mage.cards.p.PyreZombie.class)); + cards.add(new SetCardInfo("Quirion Elves", 203, Rarity.COMMON, mage.cards.q.QuirionElves.class)); + cards.add(new SetCardInfo("Quirion Sentinel", 204, Rarity.COMMON, mage.cards.q.QuirionSentinel.class)); + cards.add(new SetCardInfo("Quirion Trailblazer", 205, Rarity.COMMON, mage.cards.q.QuirionTrailblazer.class)); + cards.add(new SetCardInfo("Rage Weaver", 159, Rarity.UNCOMMON, mage.cards.r.RageWeaver.class)); + cards.add(new SetCardInfo("Raging Kavu", 262, Rarity.RARE, mage.cards.r.RagingKavu.class)); + cards.add(new SetCardInfo("Rainbow Crow", 69, Rarity.UNCOMMON, mage.cards.r.RainbowCrow.class)); + cards.add(new SetCardInfo("Rampant Elephant", 28, Rarity.COMMON, mage.cards.r.RampantElephant.class)); + cards.add(new SetCardInfo("Ravenous Rats", 120, Rarity.COMMON, mage.cards.r.RavenousRats.class)); + cards.add(new SetCardInfo("Razorfoot Griffin", 29, Rarity.COMMON, mage.cards.r.RazorfootGriffin.class)); + cards.add(new SetCardInfo("Reckless Assault", 263, Rarity.RARE, mage.cards.r.RecklessAssault.class)); + cards.add(new SetCardInfo("Reckless Spite", 121, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); + cards.add(new SetCardInfo("Recoil", 264, Rarity.COMMON, mage.cards.r.Recoil.class)); + cards.add(new SetCardInfo("Recover", 122, Rarity.COMMON, mage.cards.r.Recover.class)); + cards.add(new SetCardInfo("Repulse", 70, Rarity.COMMON, mage.cards.r.Repulse.class)); + cards.add(new SetCardInfo("Restock", 206, Rarity.RARE, mage.cards.r.Restock.class)); + cards.add(new SetCardInfo("Restrain", 30, Rarity.COMMON, mage.cards.r.Restrain.class)); + cards.add(new SetCardInfo("Reviving Dose", 31, Rarity.COMMON, mage.cards.r.RevivingDose.class)); + cards.add(new SetCardInfo("Reviving Vapors", 265, Rarity.UNCOMMON, mage.cards.r.RevivingVapors.class)); + cards.add(new SetCardInfo("Rewards of Diversity", 32, Rarity.UNCOMMON, mage.cards.r.RewardsOfDiversity.class)); + cards.add(new SetCardInfo("Reya Dawnbringer", 33, Rarity.RARE, mage.cards.r.ReyaDawnbringer.class)); + cards.add(new SetCardInfo("Riptide Crab", 266, Rarity.UNCOMMON, mage.cards.r.RiptideCrab.class)); + cards.add(new SetCardInfo("Rith's Attendant", 310, Rarity.UNCOMMON, mage.cards.r.RithsAttendant.class)); + cards.add(new SetCardInfo("Rith, the Awakener", 267, Rarity.RARE, mage.cards.r.RithTheAwakener.class)); + cards.add(new SetCardInfo("Rogue Kavu", 160, Rarity.COMMON, mage.cards.r.RogueKavu.class)); + cards.add(new SetCardInfo("Rooting Kavu", 207, Rarity.UNCOMMON, mage.cards.r.RootingKavu.class)); + cards.add(new SetCardInfo("Rout", 34, Rarity.RARE, mage.cards.r.Rout.class)); + cards.add(new SetCardInfo("Ruby Leech", 161, Rarity.RARE, mage.cards.r.RubyLeech.class)); + cards.add(new SetCardInfo("Ruham Djinn", 35, Rarity.UNCOMMON, mage.cards.r.RuhamDjinn.class)); + cards.add(new SetCardInfo("Sabertooth Nishoba", 268, Rarity.RARE, mage.cards.s.SabertoothNishoba.class)); + cards.add(new SetCardInfo("Salt Marsh", 326, Rarity.UNCOMMON, mage.cards.s.SaltMarsh.class)); + cards.add(new SetCardInfo("Samite Archer", 269, Rarity.UNCOMMON, mage.cards.s.SamiteArcher.class)); + cards.add(new SetCardInfo("Sapphire Leech", 71, Rarity.RARE, mage.cards.s.SapphireLeech.class)); + cards.add(new SetCardInfo("Saproling Symbiosis", 209, Rarity.RARE, mage.cards.s.SaprolingSymbiosis.class)); + cards.add(new SetCardInfo("Savage Offensive", 162, Rarity.COMMON, mage.cards.s.SavageOffensive.class)); + cards.add(new SetCardInfo("Scarred Puma", 163, Rarity.COMMON, mage.cards.s.ScarredPuma.class)); + cards.add(new SetCardInfo("Scavenged Weaponry", 123, Rarity.COMMON, mage.cards.s.ScavengedWeaponry.class)); + cards.add(new SetCardInfo("Scorching Lava", 164, Rarity.COMMON, mage.cards.s.ScorchingLava.class)); + cards.add(new SetCardInfo("Scouting Trek", 210, Rarity.UNCOMMON, mage.cards.s.ScoutingTrek.class)); + cards.add(new SetCardInfo("Seashell Cameo", 311, Rarity.UNCOMMON, mage.cards.s.SeashellCameo.class)); + cards.add(new SetCardInfo("Seer's Vision", 270, Rarity.UNCOMMON, mage.cards.s.SeersVision.class)); + cards.add(new SetCardInfo("Serpentine Kavu", 211, Rarity.COMMON, mage.cards.s.SerpentineKavu.class)); + cards.add(new SetCardInfo("Shackles", 37, Rarity.COMMON, mage.cards.s.Shackles.class)); + cards.add(new SetCardInfo("Shimmering Wings", 72, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); + cards.add(new SetCardInfo("Shivan Emissary", 166, Rarity.UNCOMMON, mage.cards.s.ShivanEmissary.class)); + cards.add(new SetCardInfo("Shivan Harvest", 167, Rarity.UNCOMMON, mage.cards.s.ShivanHarvest.class)); + cards.add(new SetCardInfo("Shivan Oasis", 327, Rarity.UNCOMMON, mage.cards.s.ShivanOasis.class)); + cards.add(new SetCardInfo("Shivan Zombie", 271, Rarity.COMMON, mage.cards.s.ShivanZombie.class)); + cards.add(new SetCardInfo("Shoreline Raider", 73, Rarity.COMMON, mage.cards.s.ShorelineRaider.class)); + cards.add(new SetCardInfo("Simoon", 272, Rarity.UNCOMMON, mage.cards.s.Simoon.class)); + cards.add(new SetCardInfo("Skittish Kavu", 168, Rarity.UNCOMMON, mage.cards.s.SkittishKavu.class)); + cards.add(new SetCardInfo("Skizzik", 169, Rarity.RARE, mage.cards.s.Skizzik.class)); + cards.add(new SetCardInfo("Sky Weaver", 74, Rarity.UNCOMMON, mage.cards.s.SkyWeaver.class)); + cards.add(new SetCardInfo("Sleeper's Robe", 273, Rarity.UNCOMMON, mage.cards.s.SleepersRobe.class)); + cards.add(new SetCardInfo("Slimy Kavu", 170, Rarity.COMMON, mage.cards.s.SlimyKavu.class)); + cards.add(new SetCardInfo("Slinking Serpent", 274, Rarity.UNCOMMON, mage.cards.s.SlinkingSerpent.class)); + cards.add(new SetCardInfo("Smoldering Tar", 275, Rarity.UNCOMMON, mage.cards.s.SmolderingTar.class)); + cards.add(new SetCardInfo("Soul Burn", 124, Rarity.COMMON, mage.cards.s.SoulBurn.class)); + cards.add(new SetCardInfo("Sparring Golem", 312, Rarity.UNCOMMON, mage.cards.s.SparringGolem.class)); + cards.add(new SetCardInfo("Spinal Embrace", 276, Rarity.RARE, mage.cards.s.SpinalEmbrace.class)); + cards.add(new SetCardInfo("Spirit of Resistance", 38, Rarity.RARE, mage.cards.s.SpiritOfResistance.class)); + cards.add(new SetCardInfo("Spirit Weaver", 39, Rarity.UNCOMMON, mage.cards.s.SpiritWeaver.class)); + cards.add(new SetCardInfo("Spite // Malice", 293, Rarity.UNCOMMON, mage.cards.s.SpiteMalice.class)); + cards.add(new SetCardInfo("Spreading Plague", 125, Rarity.RARE, mage.cards.s.SpreadingPlague.class)); + cards.add(new SetCardInfo("Stalking Assassin", 277, Rarity.RARE, mage.cards.s.StalkingAssassin.class)); + cards.add(new SetCardInfo("Stand // Deliver", 292, Rarity.UNCOMMON, mage.cards.s.StandDeliver.class)); + cards.add(new SetCardInfo("Sterling Grove", 278, Rarity.UNCOMMON, mage.cards.s.SterlingGrove.class)); + cards.add(new SetCardInfo("Stormscape Apprentice", 75, Rarity.COMMON, mage.cards.s.StormscapeApprentice.class)); + cards.add(new SetCardInfo("Stormscape Master", 76, Rarity.RARE, mage.cards.s.StormscapeMaster.class)); + cards.add(new SetCardInfo("Strength of Unity", 40, Rarity.COMMON, mage.cards.s.StrengthOfUnity.class)); + cards.add(new SetCardInfo("Stun", 172, Rarity.COMMON, mage.cards.s.Stun.class)); + cards.add(new SetCardInfo("Sulam Djinn", 212, Rarity.UNCOMMON, mage.cards.s.SulamDjinn.class)); + cards.add(new SetCardInfo("Sulfur Vent", 328, Rarity.COMMON, mage.cards.s.SulfurVent.class)); + cards.add(new SetCardInfo("Sunscape Apprentice", 41, Rarity.COMMON, mage.cards.s.SunscapeApprentice.class)); + cards.add(new SetCardInfo("Sunscape Master", 42, Rarity.RARE, mage.cards.s.SunscapeMaster.class)); + cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sway of Illusion", 77, Rarity.UNCOMMON, mage.cards.s.SwayOfIllusion.class)); + cards.add(new SetCardInfo("Tainted Well", 126, Rarity.COMMON, mage.cards.t.TaintedWell.class)); + cards.add(new SetCardInfo("Tangle", 213, Rarity.UNCOMMON, mage.cards.t.Tangle.class)); + cards.add(new SetCardInfo("Tectonic Instability", 173, Rarity.RARE, mage.cards.t.TectonicInstability.class)); + cards.add(new SetCardInfo("Teferi's Care", 43, Rarity.UNCOMMON, mage.cards.t.TeferisCare.class)); + cards.add(new SetCardInfo("Teferi's Moat", 279, Rarity.RARE, mage.cards.t.TeferisMoat.class)); + cards.add(new SetCardInfo("Teferi's Response", 78, Rarity.RARE, mage.cards.t.TeferisResponse.class)); + cards.add(new SetCardInfo("Tek", 313, Rarity.RARE, mage.cards.t.Tek.class)); + cards.add(new SetCardInfo("Temporal Distortion", 79, Rarity.RARE, mage.cards.t.TemporalDistortion.class)); + cards.add(new SetCardInfo("Thicket Elemental", 214, Rarity.RARE, mage.cards.t.ThicketElemental.class)); + cards.add(new SetCardInfo("Thornscape Apprentice", 215, Rarity.COMMON, mage.cards.t.ThornscapeApprentice.class)); + cards.add(new SetCardInfo("Thornscape Master", 216, Rarity.RARE, mage.cards.t.ThornscapeMaster.class)); + cards.add(new SetCardInfo("Thunderscape Apprentice", 174, Rarity.COMMON, mage.cards.t.ThunderscapeApprentice.class)); + cards.add(new SetCardInfo("Thunderscape Master", 175, Rarity.RARE, mage.cards.t.ThunderscapeMaster.class)); + cards.add(new SetCardInfo("Tidal Visionary", 80, Rarity.COMMON, mage.cards.t.TidalVisionary.class)); + cards.add(new SetCardInfo("Tigereye Cameo", 314, Rarity.UNCOMMON, mage.cards.t.TigereyeCameo.class)); + cards.add(new SetCardInfo("Tinder Farm", 329, Rarity.COMMON, mage.cards.t.TinderFarm.class)); + cards.add(new SetCardInfo("Tolarian Emissary", 81, Rarity.UNCOMMON, mage.cards.t.TolarianEmissary.class)); + cards.add(new SetCardInfo("Tower Drake", 82, Rarity.COMMON, mage.cards.t.TowerDrake.class)); + cards.add(new SetCardInfo("Tranquility", 217, Rarity.COMMON, mage.cards.t.Tranquility.class)); + cards.add(new SetCardInfo("Traveler's Cloak", 83, Rarity.COMMON, mage.cards.t.TravelersCloak.class)); + cards.add(new SetCardInfo("Treefolk Healer", 218, Rarity.UNCOMMON, mage.cards.t.TreefolkHealer.class)); + cards.add(new SetCardInfo("Trench Wurm", 127, Rarity.UNCOMMON, mage.cards.t.TrenchWurm.class)); + cards.add(new SetCardInfo("Treva's Attendant", 315, Rarity.UNCOMMON, mage.cards.t.TrevasAttendant.class)); + cards.add(new SetCardInfo("Treva, the Renewer", 280, Rarity.RARE, mage.cards.t.TrevaTheRenewer.class)); + cards.add(new SetCardInfo("Tribal Flames", 176, Rarity.COMMON, mage.cards.t.TribalFlames.class)); + cards.add(new SetCardInfo("Troll-Horn Cameo", 316, Rarity.UNCOMMON, mage.cards.t.TrollHornCameo.class)); + cards.add(new SetCardInfo("Tsabo's Assassin", 128, Rarity.RARE, mage.cards.t.TsabosAssassin.class)); + cards.add(new SetCardInfo("Tsabo's Decree", 129, Rarity.RARE, mage.cards.t.TsabosDecree.class)); + cards.add(new SetCardInfo("Tsabo's Web", 317, Rarity.RARE, mage.cards.t.TsabosWeb.class)); + cards.add(new SetCardInfo("Tsabo Tavoc", 281, Rarity.RARE, mage.cards.t.TsaboTavoc.class)); + cards.add(new SetCardInfo("Turf Wound", 177, Rarity.COMMON, mage.cards.t.TurfWound.class)); + cards.add(new SetCardInfo("Twilight's Call", 130, Rarity.RARE, mage.cards.t.TwilightsCall.class)); + cards.add(new SetCardInfo("Undermine", 282, Rarity.RARE, mage.cards.u.Undermine.class)); + cards.add(new SetCardInfo("Urborg Drake", 283, Rarity.UNCOMMON, mage.cards.u.UrborgDrake.class)); + cards.add(new SetCardInfo("Urborg Emissary", 131, Rarity.UNCOMMON, mage.cards.u.UrborgEmissary.class)); + cards.add(new SetCardInfo("Urborg Phantom", 132, Rarity.COMMON, mage.cards.u.UrborgPhantom.class)); + cards.add(new SetCardInfo("Urborg Shambler", 133, Rarity.UNCOMMON, mage.cards.u.UrborgShambler.class)); + cards.add(new SetCardInfo("Urborg Skeleton", 134, Rarity.COMMON, mage.cards.u.UrborgSkeleton.class)); + cards.add(new SetCardInfo("Urborg Volcano", 330, Rarity.UNCOMMON, mage.cards.u.UrborgVolcano.class)); + cards.add(new SetCardInfo("Urza's Filter", 318, Rarity.RARE, mage.cards.u.UrzasFilter.class)); + cards.add(new SetCardInfo("Urza's Rage", 178, Rarity.RARE, mage.cards.u.UrzasRage.class)); + cards.add(new SetCardInfo("Utopia Tree", 219, Rarity.RARE, mage.cards.u.UtopiaTree.class)); + cards.add(new SetCardInfo("Verdeloth the Ancient", 220, Rarity.RARE, mage.cards.v.VerdelothTheAncient.class)); + cards.add(new SetCardInfo("Verduran Emissary", 221, Rarity.UNCOMMON, mage.cards.v.VerduranEmissary.class)); + cards.add(new SetCardInfo("Viashino Grappler", 179, Rarity.COMMON, mage.cards.v.ViashinoGrappler.class)); + cards.add(new SetCardInfo("Vicious Kavu", 284, Rarity.UNCOMMON, mage.cards.v.ViciousKavu.class)); + cards.add(new SetCardInfo("Vile Consumption", 285, Rarity.RARE, mage.cards.v.VileConsumption.class)); + cards.add(new SetCardInfo("Vodalian Hypnotist", 84, Rarity.UNCOMMON, mage.cards.v.VodalianHypnotist.class)); + cards.add(new SetCardInfo("Vodalian Merchant", 85, Rarity.COMMON, mage.cards.v.VodalianMerchant.class)); + cards.add(new SetCardInfo("Vodalian Serpent", 86, Rarity.COMMON, mage.cards.v.VodalianSerpent.class)); + cards.add(new SetCardInfo("Vodalian Zombie", 286, Rarity.COMMON, mage.cards.v.VodalianZombie.class)); + cards.add(new SetCardInfo("Void", 287, Rarity.RARE, mage.cards.v.Void.class)); + cards.add(new SetCardInfo("Voracious Cobra", 288, Rarity.UNCOMMON, mage.cards.v.VoraciousCobra.class)); + cards.add(new SetCardInfo("Wallop", 223, Rarity.UNCOMMON, mage.cards.w.Wallop.class)); + cards.add(new SetCardInfo("Wandering Stream", 224, Rarity.COMMON, mage.cards.w.WanderingStream.class)); + cards.add(new SetCardInfo("Wash Out", 87, Rarity.UNCOMMON, mage.cards.w.WashOut.class)); + cards.add(new SetCardInfo("Wax // Wane", 296, Rarity.UNCOMMON, mage.cards.w.WaxWane.class)); + cards.add(new SetCardInfo("Wayfaring Giant", 44, Rarity.UNCOMMON, mage.cards.w.WayfaringGiant.class)); + cards.add(new SetCardInfo("Whip Silk", 225, Rarity.COMMON, mage.cards.w.WhipSilk.class)); + cards.add(new SetCardInfo("Wings of Hope", 289, Rarity.COMMON, mage.cards.w.WingsOfHope.class)); + cards.add(new SetCardInfo("Winnow", 45, Rarity.RARE, mage.cards.w.Winnow.class)); + cards.add(new SetCardInfo("Worldly Counsel", 89, Rarity.COMMON, mage.cards.w.WorldlyCounsel.class)); + cards.add(new SetCardInfo("Yavimaya Barbarian", 290, Rarity.COMMON, mage.cards.y.YavimayaBarbarian.class)); + cards.add(new SetCardInfo("Yavimaya Kavu", 291, Rarity.UNCOMMON, mage.cards.y.YavimayaKavu.class)); + cards.add(new SetCardInfo("Yawgmoth's Agenda", 135, Rarity.RARE, mage.cards.y.YawgmothsAgenda.class)); + cards.add(new SetCardInfo("Zanam Djinn", 90, Rarity.UNCOMMON, mage.cards.z.ZanamDjinn.class)); + cards.add(new SetCardInfo("Zap", 180, Rarity.COMMON, mage.cards.z.Zap.class)); + } +} From cdbe1b0ad92715d7a4923131922c955629b30551 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 00:07:45 +0100 Subject: [PATCH 031/127] * Dinosaur Hunter - Fixed that its ability also triggered for non combat damage. --- .../src/mage/cards/d/DinosaurHunter.java | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java index 9711eb8b51..3e76380422 100644 --- a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java +++ b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java @@ -28,17 +28,14 @@ package mage.cards.d; import java.util.UUID; - import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.*; -import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -48,12 +45,6 @@ import mage.target.targetpointer.FixedTarget; */ public class DinosaurHunter extends CardImpl { - private static final FilterControlledCreaturePermanent filterAnotherDino = new FilterControlledCreaturePermanent(); - static { - filterAnotherDino.add(new AnotherPredicate()); - filterAnotherDino.add(new SubtypePredicate(SubType.DINOSAUR)); - } - public DinosaurHunter(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); @@ -78,11 +69,6 @@ public class DinosaurHunter extends CardImpl { class DinosaurHunterAbility extends TriggeredAbilityImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - static { - filter.add(new SubtypePredicate(SubType.DINOSAUR)); - } - DinosaurHunterAbility() { super(Zone.BATTLEFIELD, new DestroyTargetEffect()); } @@ -103,11 +89,13 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanet = game.getPermanent(event.getSourceId()); - Permanent targetPermanet = game.getPermanent(event.getTargetId()); - if (sourcePermanet != null && targetPermanet != null && event.getSourceId().equals(sourceId) && filter.match(targetPermanet, game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); - return true; + if (((DamageEvent) event).isCombatDamage() + && event.getSourceId().equals(getSourceId())) { + Permanent targetPermanet = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (targetPermanet.hasSubtype(SubType.DINOSAUR, game)) { + getEffects().get(0).setTargetPointer(new FixedTarget(targetPermanet, game)); + return true; + } } return false; } @@ -116,4 +104,4 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever {this} deals combat damage to a Dinosaur, destroy that creature."; } -} \ No newline at end of file +} From cdb2404bb63936ad778a3c4a043af9661375f5b8 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 23 Feb 2018 17:12:38 -0600 Subject: [PATCH 032/127] - Added requested card Mercadia's Downfall --- .../src/mage/cards/m/MercadiasDownfall.java | 112 ++++++++++++++++++ Mage.Sets/src/mage/sets/MercadianMasques.java | 1 + 2 files changed, 113 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MercadiasDownfall.java diff --git a/Mage.Sets/src/mage/cards/m/MercadiasDownfall.java b/Mage.Sets/src/mage/cards/m/MercadiasDownfall.java new file mode 100644 index 0000000000..c905d973c0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MercadiasDownfall.java @@ -0,0 +1,112 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SuperType; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; + +/** + * + * @author jeffwadsworth + */ +public class MercadiasDownfall extends CardImpl { + + private static String rule = "Each attacking creature gets +1/+0 until end of turn for each nonbasic land defending player controls"; + + public MercadiasDownfall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Each attacking creature gets +1/+0 until end of turn for each nonbasic land defending player controls. + this.getSpellAbility().addEffect(new BoostAllEffect(new DefendersNonBasicLandCount(), new StaticValue(0), Duration.EndOfTurn, new FilterAttackingCreature(), true, rule)); + + } + + public MercadiasDownfall(final MercadiasDownfall card) { + super(card); + } + + @Override + public MercadiasDownfall copy() { + return new MercadiasDownfall(this); + } + + class DefendersNonBasicLandCount implements DynamicValue { + + UUID defenderId; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + for (CombatGroup group : game.getCombat().getGroups()) { + defenderId = group.getDefenderId(); + if (group.isDefenderIsPlaneswalker()) { + Permanent permanent = game.getPermanent(defenderId); + if (permanent != null) { + defenderId = permanent.getControllerId(); + } + } + FilterLandPermanent filter = new FilterLandPermanent("nonbasic land"); + filter.add(Predicates.not(new SupertypePredicate(SuperType.BASIC))); + System.out.println("The number of nonbasic lands is " + game.getBattlefield().countAll(filter, defenderId, game)); + return game.getBattlefield().countAll(filter, defenderId, game); + } + return 0; + } + + @Override + public DynamicValue copy() { + return new DefendersNonBasicLandCount(); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "the number of nonbasic lands defending player controls"; + } + } + +} diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index 5ca90702ac..2b167bc1d4 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -219,6 +219,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Maggot Therapy", 145, Rarity.COMMON, mage.cards.m.MaggotTherapy.class)); cards.add(new SetCardInfo("Magistrate's Scepter", 304, Rarity.RARE, mage.cards.m.MagistratesScepter.class)); cards.add(new SetCardInfo("Magistrate's Veto", 204, Rarity.UNCOMMON, mage.cards.m.MagistratesVeto.class)); + cards.add(new SetCardInfo("Mercadia's Downfall", 205, Rarity.UNCOMMON, mage.cards.m.MercadiasDownfall.class)); cards.add(new SetCardInfo("Mercadian Bazaar", 321, Rarity.UNCOMMON, mage.cards.m.MercadianBazaar.class)); cards.add(new SetCardInfo("Midnight Ritual", 146, Rarity.RARE, mage.cards.m.MidnightRitual.class)); cards.add(new SetCardInfo("Misdirection", 87, Rarity.RARE, mage.cards.m.Misdirection.class)); From 9a95e49ac9c8a8a0305118bb35c9853ceb90d2eb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 01:00:23 +0100 Subject: [PATCH 033/127] * Fixed Stifle rule text and some other minor things related to TragetActivatedAbility. --- Mage.Sets/src/mage/cards/a/AyeshaTanaka.java | 36 +++---------- Mage.Sets/src/mage/cards/b/BrownOuphe.java | 35 +++---------- Mage.Sets/src/mage/cards/i/Interdict.java | 14 +++-- Mage.Sets/src/mage/cards/r/Rust.java | 33 ++---------- .../ability/ArtifactSourcePredicate.java | 51 +++++++++++++++++++ .../target/common/TargetActivatedAbility.java | 2 +- .../TargetActivatedOrTriggeredAbility.java | 2 +- 7 files changed, 80 insertions(+), 93 deletions(-) create mode 100644 Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java diff --git a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java index d16fa76af3..f6e44e5283 100644 --- a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java +++ b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java @@ -37,16 +37,13 @@ import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.BandingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackAbility; -import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.filter.FilterAbility; +import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.target.common.TargetActivatedAbility; /** * @@ -54,26 +51,26 @@ import mage.target.common.TargetActivatedOrTriggeredAbility; */ public class AyeshaTanaka extends CardImpl { - private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); + private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source"); static { filter.add(new ArtifactSourcePredicate()); } public AyeshaTanaka(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}{U}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARTIFICER); this.power = new MageInt(2); this.toughness = new MageInt(2); - + // Banding this.addAbility(BandingAbility.getInstance()); // {T}: Counter target activated ability from an artifact source unless that ability's controller pays {W}. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new ManaCostsImpl("{W}")), new TapSourceCost()); - ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); + ability.addTarget(new TargetActivatedAbility(filter)); this.addAbility(ability); } @@ -86,22 +83,3 @@ public class AyeshaTanaka extends CardImpl { return new AyeshaTanaka(this); } } - -class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } - - @Override - public boolean apply(Ability input, Game game) { - if (input instanceof StackAbility) { - return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED; - } - return false; - } - - @Override - public String toString() { - return "Source(Artifact)"; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrownOuphe.java b/Mage.Sets/src/mage/cards/b/BrownOuphe.java index 1810831dd0..06bc31afbf 100644 --- a/Mage.Sets/src/mage/cards/b/BrownOuphe.java +++ b/Mage.Sets/src/mage/cards/b/BrownOuphe.java @@ -27,6 +27,7 @@ */ package mage.cards.b; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -35,17 +36,12 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackAbility; -import mage.target.common.TargetActivatedOrTriggeredAbility; - -import java.util.UUID; +import mage.filter.FilterAbility; +import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.target.common.TargetActivatedAbility; /** * @@ -53,7 +49,7 @@ import java.util.UUID; */ public class BrownOuphe extends CardImpl { - private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); + private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source"); static { filter.add(new ArtifactSourcePredicate()); @@ -69,7 +65,7 @@ public class BrownOuphe extends CardImpl { // {1}{G}, {tap}: Counter target activated ability from an artifact source. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl<>("{1}{G}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); + ability.addTarget(new TargetActivatedAbility(filter)); this.addAbility(ability); } @@ -82,22 +78,3 @@ public class BrownOuphe extends CardImpl { return new BrownOuphe(this); } } - -class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } - - @Override - public boolean apply(Ability input, Game game) { - if (input instanceof StackAbility) { - return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED; - } - return false; - } - - @Override - public String toString() { - return "Source(Artifact)"; - } -} diff --git a/Mage.Sets/src/mage/cards/i/Interdict.java b/Mage.Sets/src/mage/cards/i/Interdict.java index 5fcc1c5244..4ae659f513 100644 --- a/Mage.Sets/src/mage/cards/i/Interdict.java +++ b/Mage.Sets/src/mage/cards/i/Interdict.java @@ -28,6 +28,7 @@ package mage.cards.i; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; @@ -88,7 +89,7 @@ class InterdictPredicate implements Predicate { @Override public boolean apply(Ability input, Game game) { if (input instanceof StackAbility && input.getAbilityType() == AbilityType.ACTIVATED) { - Permanent sourceObject = game.getPermanentOrLKIBattlefield(input.getSourceId()); + MageObject sourceObject = input.getSourceObject(game); if (sourceObject != null) { return (sourceObject.isArtifact() || sourceObject.isEnchantment() @@ -120,9 +121,12 @@ class InterdictCounterEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); if (stackObject != null && game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game)) { - InterdictCantActivateEffect effect = new InterdictCantActivateEffect(); - effect.setTargetPointer(new FixedTarget(stackObject.getSourceId())); - game.getContinuousEffects().addEffect(effect, source); + Permanent sourcePermanent = stackObject.getStackAbility().getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null) { + InterdictCantActivateEffect effect = new InterdictCantActivateEffect(); + effect.setTargetPointer(new FixedTarget(sourcePermanent, game)); + game.getContinuousEffects().addEffect(effect, source); + } return true; } return false; @@ -143,7 +147,7 @@ class InterdictCantActivateEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { - return getTargetPointer().getFirst(game, source).equals(permanent.getId()); + return permanent.getId().equals(getTargetPointer().getFirst(game, source)); } @Override diff --git a/Mage.Sets/src/mage/cards/r/Rust.java b/Mage.Sets/src/mage/cards/r/Rust.java index 21dd121bf9..9bf2bac561 100644 --- a/Mage.Sets/src/mage/cards/r/Rust.java +++ b/Mage.Sets/src/mage/cards/r/Rust.java @@ -28,17 +28,13 @@ package mage.cards.r; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; -import mage.filter.FilterStackObject; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackAbility; -import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.filter.FilterAbility; +import mage.filter.predicate.ability.ArtifactSourcePredicate; +import mage.target.common.TargetActivatedAbility; /** * @@ -46,7 +42,7 @@ import mage.target.common.TargetActivatedOrTriggeredAbility; */ public class Rust extends CardImpl { - private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); + private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source"); static { filter.add(new ArtifactSourcePredicate()); @@ -57,7 +53,7 @@ public class Rust extends CardImpl { // Counter target activated ability from an artifact source. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetActivatedOrTriggeredAbility(filter)); + this.getSpellAbility().addTarget(new TargetActivatedAbility(filter)); } public Rust(final Rust card) { @@ -69,22 +65,3 @@ public class Rust extends CardImpl { return new Rust(this); } } - -class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } - - @Override - public boolean apply(Ability input, Game game) { - if (input instanceof StackAbility) { - return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED; - } - return false; - } - - @Override - public String toString() { - return "Source(Artifact)"; - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java new file mode 100644 index 0000000000..36ff6b0032 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/ability/ArtifactSourcePredicate.java @@ -0,0 +1,51 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com 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.predicate.ability; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class ArtifactSourcePredicate implements Predicate { + + @Override + public boolean apply(Ability input, Game game) { + MageObject sourceObject = input.getSourceObject(game); + return sourceObject != null && sourceObject.isArtifact(); + } + + @Override + public String toString() { + return "Source(Artifact)"; + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java index 00e65d3b11..b107528f6e 100644 --- a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java @@ -57,7 +57,7 @@ public class TargetActivatedAbility extends TargetObject { this.minNumberOfTargets = 1; this.maxNumberOfTargets = 1; this.zone = Zone.STACK; - this.targetName = "activated ability"; + this.targetName = filter.getMessage(); this.filter = filter; } diff --git a/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java b/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java index 8c9df2be20..3e3eeff5c2 100644 --- a/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetActivatedOrTriggeredAbility.java @@ -44,7 +44,7 @@ public class TargetActivatedOrTriggeredAbility extends TargetObject { protected final FilterStackObject filter; public TargetActivatedOrTriggeredAbility() { - this(new FilterStackObject()); + this(new FilterStackObject("activated or triggered ability")); } public TargetActivatedOrTriggeredAbility(FilterStackObject filter) { From dfb94469946385e7a17d44af775b9c8063b08027 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 10:26:11 +0100 Subject: [PATCH 034/127] * Some minor mainly fixed target changes. --- Mage.Sets/src/mage/cards/c/CoffinQueen.java | 38 +++++++++---------- .../src/mage/cards/l/LaquatussChampion.java | 7 +--- .../effects/common/DetainAllEffect.java | 30 +++++++-------- .../effects/common/RegenerateAllEffect.java | 2 +- .../abilities/keyword/PersistAbility.java | 3 -- 5 files changed, 33 insertions(+), 47 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CoffinQueen.java b/Mage.Sets/src/mage/cards/c/CoffinQueen.java index ca66fe8f4a..c7ea4503d5 100644 --- a/Mage.Sets/src/mage/cards/c/CoffinQueen.java +++ b/Mage.Sets/src/mage/cards/c/CoffinQueen.java @@ -41,9 +41,9 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; @@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget; public class CoffinQueen extends CardImpl { public CoffinQueen(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); @@ -68,14 +68,14 @@ public class CoffinQueen extends CardImpl { // You may choose not to untap Coffin Queen during your untap step. this.addAbility(new SkipUntapOptionalAbility()); - + // {2}{B}, {tap}: Put target creature card from a graveyard onto the battlefield under your control. When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{B}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); - ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect()); + ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect()); this.addAbility(ability); - + } public CoffinQueen(final CoffinQueen card) { @@ -87,33 +87,30 @@ public class CoffinQueen extends CardImpl { return new CoffinQueen(this); } } + class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect { - + public CoffinQueenCreateDelayedTriggerEffect() { super(Outcome.Detriment); - this.staticText = "When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature"; + this.staticText = "When {this} becomes untapped or you lose control of {this}, exile that creature."; } - + public CoffinQueenCreateDelayedTriggerEffect(final CoffinQueenCreateDelayedTriggerEffect effect) { super(effect); } - + @Override public CoffinQueenCreateDelayedTriggerEffect copy() { return new CoffinQueenCreateDelayedTriggerEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Permanent controlledCreature = game.getPermanent(source.getFirstTarget()); if (controlledCreature != null) { DelayedTriggeredAbility delayedAbility = new CoffinQueenDelayedTriggeredAbility(); - delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature.getId())); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - delayedAbility.init(game); - game.addDelayedTriggeredAbility(delayedAbility); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature, game)); + game.addDelayedTriggeredAbility(delayedAbility, source); return true; } return false; @@ -123,7 +120,7 @@ class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect { class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility { CoffinQueenDelayedTriggeredAbility() { - super(new ExileTargetEffect(), Duration.EndOfGame, true); + super(new ExileTargetEffect(), Duration.EndOfGame, true); } CoffinQueenDelayedTriggeredAbility(CoffinQueenDelayedTriggeredAbility ability) { @@ -136,7 +133,6 @@ class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility { || event.getType() == EventType.UNTAPPED; } - @Override public boolean checkTrigger(GameEvent event, Game game) { if (EventType.LOST_CONTROL == event.getType() @@ -146,14 +142,14 @@ class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility { return EventType.UNTAPPED == event.getType() && event.getTargetId() != null && event.getTargetId().equals(getSourceId()); } - + @Override public CoffinQueenDelayedTriggeredAbility copy() { return new CoffinQueenDelayedTriggeredAbility(this); } - + @Override public String getRule() { - return "When {this} becomes untapped or you lose control of {this}, exile that creature"; + return "When {this} becomes untapped or you lose control of {this}, exile that creature."; } } diff --git a/Mage.Sets/src/mage/cards/l/LaquatussChampion.java b/Mage.Sets/src/mage/cards/l/LaquatussChampion.java index 0e91897a87..909d6a6231 100644 --- a/Mage.Sets/src/mage/cards/l/LaquatussChampion.java +++ b/Mage.Sets/src/mage/cards/l/LaquatussChampion.java @@ -35,7 +35,6 @@ import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.AdjustingSourceCosts; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.GainLifeTargetEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.RegenerateSourceEffect; @@ -58,7 +57,7 @@ import mage.util.CardUtil; public class LaquatussChampion extends CardImpl { public LaquatussChampion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); this.subtype.add(SubType.NIGHTMARE); this.subtype.add(SubType.HORROR); @@ -126,9 +125,7 @@ class LaquatussChampionLeavesBattlefieldTriggeredAbility extends LeavesBattlefie String key = CardUtil.getCardZoneString("targetPlayer", this.getSourceId(), game, true); Object object = game.getState().getValue(key); if (object instanceof UUID) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget((UUID) object)); - } + this.getEffects().setTargetPointer(new FixedTarget((UUID) object)); return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java index 56005a8775..774304b6ea 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DetainAllEffect.java @@ -24,19 +24,17 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.abilities.effects.common; import java.util.ArrayList; import java.util.List; import java.util.UUID; - -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.filter.FilterPermanent; import mage.game.Game; @@ -48,7 +46,6 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - public class DetainAllEffect extends OneShotEffect { private FilterPermanent filter = new FilterPermanent(); @@ -56,7 +53,7 @@ public class DetainAllEffect extends OneShotEffect { public DetainAllEffect(FilterPermanent filter) { super(Outcome.Benefit); this.filter = filter; - this.staticText = new StringBuilder("detain ").append(filter.getMessage()).toString(); + this.staticText = "detain " + filter.getMessage(); } public DetainAllEffect(final DetainAllEffect effect) { @@ -73,10 +70,10 @@ public class DetainAllEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List detainedObjects = new ArrayList<>(); for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers("Detained permanent: " + permanent.getName()); - FixedTarget fixedTarget = new FixedTarget(permanent.getId()); - fixedTarget.init(game, source); + } + FixedTarget fixedTarget = new FixedTarget(permanent, game); detainedObjects.add(fixedTarget); } @@ -103,23 +100,22 @@ class DetainAllRestrictionEffect extends RestrictionEffect { @Override public void init(Ability source, Game game) { super.init(source, game); - for(FixedTarget fixedTarget :this.detainedObjects) { + for (FixedTarget fixedTarget : this.detainedObjects) { Permanent permanent = game.getPermanent(fixedTarget.getFirst(game, source)); if (permanent != null) { - permanent.addInfo(new StringBuilder("detain").append(getId()).toString(),"[Detained]", game); + permanent.addInfo(new StringBuilder("detain").append(getId()).toString(), "[Detained]", game); } } } @Override public boolean isInactive(Ability source, Game game) { - if (game.getPhase().getStep().getType() == PhaseStep.UNTAP && game.getStep().getStepPart() == Step.StepPart.PRE) - { + if (game.getPhase().getStep().getType() == PhaseStep.UNTAP && game.getStep().getStepPart() == Step.StepPart.PRE) { if (game.getActivePlayerId().equals(source.getControllerId()) || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - for(FixedTarget fixedTarget :this.detainedObjects) { + for (FixedTarget fixedTarget : this.detainedObjects) { Permanent permanent = game.getPermanent(fixedTarget.getFirst(game, source)); if (permanent != null) { - permanent.addInfo(new StringBuilder("detain").append(getId()).toString(),"", game); + permanent.addInfo(new StringBuilder("detain").append(getId()).toString(), "", game); } } return true; @@ -130,7 +126,7 @@ class DetainAllRestrictionEffect extends RestrictionEffect { @Override public boolean applies(Permanent permanent, Ability source, Game game) { - for(FixedTarget fixedTarget :this.detainedObjects) { + for (FixedTarget fixedTarget : this.detainedObjects) { UUID targetId = fixedTarget.getFirst(game, source); if (targetId != null && targetId.equals(permanent.getId())) { return true; @@ -148,7 +144,7 @@ class DetainAllRestrictionEffect extends RestrictionEffect { public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { return false; } - + @Override public boolean canUseActivatedAbilities(Permanent permanent, Ability source, Game game) { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java index f224789f66..2482ff947f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateAllEffect.java @@ -63,7 +63,7 @@ public class RegenerateAllEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { RegenerateTargetEffect regenEffect = new RegenerateTargetEffect(); - regenEffect.setTargetPointer(new FixedTarget(permanent.getId())); + regenEffect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(regenEffect, source); } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java index 085d893a49..bf4ca8188f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java @@ -38,7 +38,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; public class PersistAbility extends DiesTriggeredAbility { @@ -66,8 +65,6 @@ public class PersistAbility extends DiesTriggeredAbility { if (super.checkTrigger(event, game)) { Permanent permanent = ((ZoneChangeEvent) event).getTarget(); if (permanent.getCounters(game).getCount(CounterType.M1M1) == 0) { - FixedTarget fixedTarget = new FixedTarget(permanent.getId()); - fixedTarget.init(game, this); return true; } } From 3d372e7100fce8ba9db50b75067ead72478843af Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 12:23:50 +0100 Subject: [PATCH 035/127] * Fixed bug of Defiant Vanguard not destroying aby creatures from blocking. Added test. --- .../src/mage/cards/d/DefiantVanguard.java | 4 +- .../cards/triggers/DefiantVanguardTest.java | 84 +++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java diff --git a/Mage.Sets/src/mage/cards/d/DefiantVanguard.java b/Mage.Sets/src/mage/cards/d/DefiantVanguard.java index 3b28b8d6dc..0a5c3decb5 100644 --- a/Mage.Sets/src/mage/cards/d/DefiantVanguard.java +++ b/Mage.Sets/src/mage/cards/d/DefiantVanguard.java @@ -124,7 +124,7 @@ class DefiantVanguardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent thisCreature = game.getPermanentOrLKIBattlefield(source.getId()); + Permanent thisCreature = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (controller != null && thisCreature != null) { BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName()); if (watcher != null) { @@ -136,10 +136,10 @@ class DefiantVanguardEffect extends OneShotEffect { } } } + thisCreature.destroy(source.getSourceId(), game, false); for (Permanent creature : toDestroy) { creature.destroy(source.getSourceId(), game, false); } - thisCreature.destroy(source.getSourceId(), game, false); return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java new file mode 100644 index 0000000000..dc4b99e66f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DefiantVanguardTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DefiantVanguardTest extends CardTestPlayerBase { + + @Test + public void testAllDestroyed() { + // When Defiant Vanguard blocks, at end of combat, destroy it and all creatures it blocked this turn. + // {5}, {tap}: Search your library for a Rebel permanent card with converted mana cost 4 or less and put it onto the battlefield. Then shuffle your library. + addCard(Zone.BATTLEFIELD, playerA, "Defiant Vanguard", 1); // Creature {2}{W} 2/2 + + addCard(Zone.BATTLEFIELD, playerB, "Bane Alley Blackguard", 1); // Creature {1}{B} 1/3 + + attack(2, playerB, "Bane Alley Blackguard"); + block(2, playerA, "Defiant Vanguard", "Bane Alley Blackguard"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Defiant Vanguard", 1); + assertGraveyardCount(playerB, "Bane Alley Blackguard", 1); + } + + @Test + public void testSaveCreatureWithCloudshift() { + // When Defiant Vanguard blocks, at end of combat, destroy it and all creatures it blocked this turn. + // {5}, {tap}: Search your library for a Rebel permanent card with converted mana cost 4 or less and put it onto the battlefield. Then shuffle your library. + addCard(Zone.BATTLEFIELD, playerA, "Defiant Vanguard", 1); // Creature {2}{W} 2/2 + + addCard(Zone.BATTLEFIELD, playerB, "Bane Alley Blackguard", 1); // Creature {1}{B} 1/3 + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerB, "Cloudshift", 1); // Instant {W} + + attack(2, playerB, "Bane Alley Blackguard"); + block(2, playerA, "Defiant Vanguard", "Bane Alley Blackguard"); + + castSpell(2, PhaseStep.END_COMBAT, playerB, "Cloudshift", "Bane Alley Blackguard", "At end of combat, destroy it and all creatures it blocked this turn."); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Defiant Vanguard", 1); + assertGraveyardCount(playerB, "Cloudshift", 1); + assertPermanentCount(playerB, "Bane Alley Blackguard", 1); + + } + +} From 735a7668a1f2ff7d6d603ed1ae7ff096d4ce76f2 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:26:32 +0000 Subject: [PATCH 036/127] Implemented Honorable Passage --- .../src/mage/cards/h/HonorablePassage.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HonorablePassage.java diff --git a/Mage.Sets/src/mage/cards/h/HonorablePassage.java b/Mage.Sets/src/mage/cards/h/HonorablePassage.java new file mode 100644 index 0000000000..585d6256e0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HonorablePassage.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.cards.h; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author L_J + */ +public class HonorablePassage extends CardImpl { + + public HonorablePassage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + + // The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, Honorable Passage deals that much damage to the source's controller. + this.getSpellAbility().addEffect(new HonorablePassageEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + } + + public HonorablePassage(final HonorablePassage card) { + super(card); + } + + @Override + public HonorablePassage copy() { + return new HonorablePassage(this); + } +} + +class HonorablePassageEffect extends PreventNextDamageFromChosenSourceToTargetEffect { + + public HonorablePassageEffect() { + super(Duration.EndOfTurn); + } + + public HonorablePassageEffect(final HonorablePassageEffect effect) { + super(effect); + } + + @Override + public HonorablePassageEffect copy() { + return new HonorablePassageEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + PreventionEffectData preventEffectData = preventDamageAction(event, source, game); + if (preventEffectData.getPreventedDamage() > 0) { + MageObject sourceObject = game.getObject(event.getSourceId()); + if (sourceObject != null && sourceObject.getColor(game).isRed()) { + UUID sourceControllerId = game.getControllerId(event.getSourceId()); + if (sourceControllerId != null) { + Player sourceController = game.getPlayer(sourceControllerId); + if (sourceController != null) { + sourceController.damage(damage, source.getSourceId(), game, false, true); + } + } + } + this.used = true; + } + return false; + } + + @Override + public String getText(Mode mode) { + return "The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, {this} deals that much damage to the source's controller"; + } +} From 41e156ed320d2d90af651ca9dfd63e3bc9f0cf6a Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:27:44 +0000 Subject: [PATCH 037/127] Implemented Wind Shear --- Mage.Sets/src/mage/cards/w/WindShear.java | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WindShear.java diff --git a/Mage.Sets/src/mage/cards/w/WindShear.java b/Mage.Sets/src/mage/cards/w/WindShear.java new file mode 100644 index 0000000000..4d70f93c00 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WindShear.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.w; + +import java.util.UUID; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AbilityPredicate; + +/** + * + * @author L_J + */ +public class WindShear extends CardImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public WindShear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); + + // Attacking creatures with flying get -2/-2 and lose flying until end of turn. + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn, filter, false).setText("Attacking creatures with flying get -2/-2")); + this.getSpellAbility().addEffect(new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filter).setText("and lose flying until end of turn")); + } + + public WindShear(final WindShear card) { + super(card); + } + + @Override + public WindShear copy() { + return new WindShear(this); + } +} From 3c2acc407c3651c9d92c1ece1e1cb831b6eea661 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:28:49 +0000 Subject: [PATCH 038/127] Implemented Foreshadow --- Mage.Sets/src/mage/cards/f/Foreshadow.java | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/f/Foreshadow.java diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java new file mode 100644 index 0000000000..54db650304 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java @@ -0,0 +1,110 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author Quercitron & L_J + */ +public class Foreshadow extends CardImpl { + + public Foreshadow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + + // Choose a card name, then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card. + this.getSpellAbility().addEffect(new NameACardEffect(NameACardEffect.TypeOfName.ALL)); + this.getSpellAbility().addEffect(new ForeshadowEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Draw a card at the beginning of the next turn's upkeep. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); + } + + public Foreshadow(final Foreshadow card) { + super(card); + } + + @Override + public Foreshadow copy() { + return new Foreshadow(this); + } +} + +class ForeshadowEffect extends OneShotEffect { + + public ForeshadowEffect() { + super(Outcome.DrawCard); + this.staticText = ", then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card"; + } + + public ForeshadowEffect(final ForeshadowEffect effect) { + super(effect); + } + + @Override + public ForeshadowEffect copy() { + return new ForeshadowEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) { + Card card = targetPlayer.getLibrary().getFromTop(game); + if (card != null) { + controller.moveCards(card, Zone.GRAVEYARD, source, game); + if (card.getName().equals(cardName)) { + controller.drawCards(1, game); + } + } + return true; + } + return false; + } + +} From 22a49caf56dd8de4c1d3cfb45ed02107debfb841 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:29:43 +0000 Subject: [PATCH 039/127] Implemented Honorable Passage --- Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java index 119cb767c9..d124325bcc 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java +++ b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java @@ -96,6 +96,7 @@ public class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); cards.add(new SetCardInfo("Grinning Totem", 110, Rarity.SPECIAL, mage.cards.g.GrinningTotem.class)); cards.add(new SetCardInfo("Hail Storm", 79, Rarity.SPECIAL, mage.cards.h.HailStorm.class)); + cards.add(new SetCardInfo("Honorable Passage", 9, Rarity.SPECIAL, mage.cards.h.HonorablePassage.class)); cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.COMMON, mage.cards.h.HuntingMoa.class)); cards.add(new SetCardInfo("Icatian Javelineers", 10, Rarity.SPECIAL, IcatianJavelineers.class)); cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.COMMON, mage.cards.j.JasmineBoreal.class)); From fa783708feee4724061824d3f1c6daf7641996df Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:30:16 +0000 Subject: [PATCH 040/127] Implemented cards --- Mage.Sets/src/mage/sets/Visions.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/Visions.java b/Mage.Sets/src/mage/sets/Visions.java index b537d3b33d..e1458607a9 100644 --- a/Mage.Sets/src/mage/sets/Visions.java +++ b/Mage.Sets/src/mage/sets/Visions.java @@ -63,7 +63,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Brass-Talon Chimera", 142, Rarity.UNCOMMON, mage.cards.b.BrassTalonChimera.class)); cards.add(new SetCardInfo("Breathstealer's Crypt", 127, Rarity.RARE, mage.cards.b.BreathstealersCrypt.class)); cards.add(new SetCardInfo("Breezekeeper", 27, Rarity.COMMON, mage.cards.b.Breezekeeper.class)); - cards.add(new SetCardInfo("Brood of Cockroaches", 3, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); + cards.add(new SetCardInfo("Brood of Cockroaches", 3, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); cards.add(new SetCardInfo("Bull Elephant", 51, Rarity.COMMON, mage.cards.b.BullElephant.class)); cards.add(new SetCardInfo("Chronatog", 28, Rarity.RARE, mage.cards.c.Chronatog.class)); cards.add(new SetCardInfo("City of Solitude", 52, Rarity.RARE, mage.cards.c.CityOfSolitude.class)); @@ -96,6 +96,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Fireblast", 79, Rarity.COMMON, mage.cards.f.Fireblast.class)); cards.add(new SetCardInfo("Firestorm Hellkite", 130, Rarity.RARE, mage.cards.f.FirestormHellkite.class)); cards.add(new SetCardInfo("Flooded Shoreline", 32, Rarity.RARE, mage.cards.f.FloodedShoreline.class)); + cards.add(new SetCardInfo("Foreshadow", 33, Rarity.UNCOMMON, mage.cards.f.Foreshadow.class)); cards.add(new SetCardInfo("Freewind Falcon", 105, Rarity.COMMON, mage.cards.f.FreewindFalcon.class)); cards.add(new SetCardInfo("Funeral Charm", 11, Rarity.COMMON, mage.cards.f.FuneralCharm.class)); cards.add(new SetCardInfo("Giant Caterpillar", 58, Rarity.COMMON, mage.cards.g.GiantCaterpillar.class)); @@ -105,6 +106,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Griffin Canyon", 163, Rarity.RARE, mage.cards.g.GriffinCanyon.class)); cards.add(new SetCardInfo("Hearth Charm", 82, Rarity.COMMON, mage.cards.h.HearthCharm.class)); cards.add(new SetCardInfo("Helm of Awakening", 145, Rarity.UNCOMMON, mage.cards.h.HelmOfAwakening.class)); + cards.add(new SetCardInfo("Honorable Passage", 107, Rarity.UNCOMMON, mage.cards.h.HonorablePassage.class)); cards.add(new SetCardInfo("Hope Charm", 108, Rarity.COMMON, mage.cards.h.HopeCharm.class)); cards.add(new SetCardInfo("Hulking Cyclops", 84, Rarity.UNCOMMON, mage.cards.h.HulkingCyclops.class)); cards.add(new SetCardInfo("Impulse", 34, Rarity.COMMON, mage.cards.i.Impulse.class)); @@ -199,6 +201,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Warthog", 74, Rarity.COMMON, mage.cards.w.Warthog.class)); cards.add(new SetCardInfo("Waterspout Djinn", 50, Rarity.UNCOMMON, mage.cards.w.WaterspoutDjinn.class)); cards.add(new SetCardInfo("Wicked Reward", 25, Rarity.COMMON, mage.cards.w.WickedReward.class)); + cards.add(new SetCardInfo("Wind Shear", 75, Rarity.UNCOMMON, mage.cards.w.WindShear.class)); cards.add(new SetCardInfo("Zhalfirin Crusader", 125, Rarity.RARE, mage.cards.z.ZhalfirinCrusader.class)); } } From 8238139192ec43ecbc28dfe73fb254e22f8e400f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 24 Feb 2018 19:52:25 +0400 Subject: [PATCH 041/127] * UI: added SVG icons support on x64 linux systems (see #4421, must cleanup xmage\mage-client\lib folder); --- Mage.Client/pom.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index c37a0646de..db453f4cd3 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -147,14 +147,9 @@ https://stackoverflow.com/questions/714243/sax2-driver-class-org-apache-crimson-parser-xmlreaderimpl-not-found-when-using --> - batik - batik-transcoder - 1.6-1 - - - crimson - crimson - 1.1.3 + org.apache.xmlgraphics + batik-transcoder + 1.7 From 800daff3043d30411cbf152fae5f3e1e5a875a44 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 17:10:13 +0100 Subject: [PATCH 042/127] * Fixed Vodalian War Machine watcher handling related to MageObjectReferences. --- .../src/mage/cards/v/VodalianWarMachine.java | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 75c3b50aab..68cbfa766d 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -30,6 +30,7 @@ package mage.cards.v; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import mage.MageInt; @@ -39,7 +40,6 @@ import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -47,9 +47,9 @@ import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.WatcherScope; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; @@ -79,7 +79,7 @@ public class VodalianWarMachine extends CardImpl { } public VodalianWarMachine(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.WALL); this.power = new MageInt(0); this.toughness = new MageInt(4); @@ -90,10 +90,10 @@ public class VodalianWarMachine extends CardImpl { // Tap an untapped Merfolk you control: Vodalian War Machine can attack this turn as though it didn't have defender. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); this.addAbility(ability); - + // Tap an untapped Merfolk you control: Vodalian War Machine gets +2/+1 until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); - + // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. this.addAbility(new VodalianWarMachineTriggeredAbility(), new VodalianWarMachineWatcher()); } @@ -136,13 +136,6 @@ class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { return false; } } - for (Effect effect : getEffects()) { - effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); - if (effect instanceof VodalianWarMachineEffect) { - VodalianWarMachineEffect effectToSet = (VodalianWarMachineEffect) effect; - effectToSet.counter = before.getZoneChangeCounter(game); - } - } return true; } return false; @@ -151,8 +144,6 @@ class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { } class VodalianWarMachineEffect extends OneShotEffect { - - protected int counter; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); @@ -167,7 +158,6 @@ class VodalianWarMachineEffect extends OneShotEffect { public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { super(effect); - counter = effect.counter; } @Override @@ -180,9 +170,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent, game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent, game).contains(new MageObjectReference(permanent, game))) { permanent.destroy(source.getSourceId(), game, false); } } @@ -196,7 +186,7 @@ class VodalianWarMachineEffect extends OneShotEffect { class VodalianWarMachineWatcher extends Watcher { - public Map> tappedMerfolkIds = new HashMap<>(); + public Map> tappedMerfolkIds = new HashMap<>(); public VodalianWarMachineWatcher() { super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); @@ -204,7 +194,10 @@ class VodalianWarMachineWatcher extends Watcher { public VodalianWarMachineWatcher(final VodalianWarMachineWatcher watcher) { super(watcher); - this.tappedMerfolkIds = watcher.tappedMerfolkIds; + // We have for sure to use copied collections, but there is no need to copy the MageObjectReference objects. + for (Entry> entry : watcher.tappedMerfolkIds.entrySet()) { + this.tappedMerfolkIds.put(entry.getKey(), new HashSet<>(entry.getValue())); + } } @Override @@ -212,9 +205,8 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId, int counter, Game game) { - MageObjectReference mor = new MageObjectReference(sourceId, counter, game); - return tappedMerfolkIds.get(mor); + public Set getTappedMerfolkIds(Permanent permanent, Game game) { + return tappedMerfolkIds.get(new MageObjectReference(permanent, game)); } @Override @@ -232,14 +224,14 @@ class VodalianWarMachineWatcher extends Watcher { TapTargetCost tapCost = (TapTargetCost) cost; if (tapCost.getTarget().isChosen()) { MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game), game); - Set toAdd; + Set toAdd; if (tappedMerfolkIds.get(mor) == null) { toAdd = new HashSet<>(); } else { toAdd = tappedMerfolkIds.get(mor); } for (UUID targetId : tapCost.getTarget().getTargets()) { - toAdd.add(targetId); + toAdd.add(new MageObjectReference(targetId, game)); } tappedMerfolkIds.put(mor, toAdd); break; From 309d4685e8771f0fd3ae3fbe830ee7cc9fdf5e4d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 24 Feb 2018 17:28:55 +0100 Subject: [PATCH 043/127] * Cleaned up FixedTargets MageObjectReference handling. --- Mage/src/main/java/mage/MageObjectReference.java | 6 +----- .../mage/target/targetpointer/FixedTargets.java | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index 0c4cde8808..9fd237f7c8 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -47,7 +47,7 @@ public class MageObjectReference implements Comparable, Ser private static final Logger logger = Logger.getLogger(MageObjectReference.class); private final UUID sourceId; - private int zoneChangeCounter; + private final int zoneChangeCounter; public MageObjectReference(MageObject mageObject, Game game) { this.sourceId = mageObject.getId(); @@ -167,8 +167,4 @@ public class MageObjectReference implements Comparable, Ser } return null; } - - public void setZoneChangeCounter(int zoneChangeCounter) { - this.zoneChangeCounter = zoneChangeCounter; - } } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index d5c099526b..e220491074 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -21,10 +21,12 @@ import mage.game.permanent.Permanent; public class FixedTargets implements TargetPointer { final ArrayList targets = new ArrayList<>(); + final ArrayList targetsNotInitialized = new ArrayList<>(); + private boolean initialized; public FixedTargets(UUID targetId) { - targets.add(new MageObjectReference(targetId)); + targetsNotInitialized.add(targetId); this.initialized = false; } @@ -46,6 +48,7 @@ public class FixedTargets implements TargetPointer { private FixedTargets(final FixedTargets fixedTargets) { this.targets.addAll(fixedTargets.targets); + this.targetsNotInitialized.addAll(fixedTargets.targetsNotInitialized); this.initialized = fixedTargets.initialized; } @@ -53,8 +56,8 @@ public class FixedTargets implements TargetPointer { public void init(Game game, Ability source) { if (!initialized) { initialized = true; - for (MageObjectReference mor : targets) { - mor.setZoneChangeCounter(game.getState().getZoneChangeCounter(mor.getSourceId())); + for (UUID targetId : targetsNotInitialized) { + targets.add(new MageObjectReference(targetId, game.getState().getZoneChangeCounter(targetId), game)); } } } @@ -87,6 +90,13 @@ public class FixedTargets implements TargetPointer { return new FixedTargets(this); } + /** + * Returns a fixed target for (and only) the first taget + * + * @param game + * @param source + * @return + */ @Override public FixedTarget getFixedTarget(Game game, Ability source) { this.init(game, source); From 4b89342c345ac274415af5409c6fd46b38373679 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 21:52:05 +0000 Subject: [PATCH 044/127] Added support for Blaze of Glory --- .../main/java/mage/game/combat/Combat.java | 102 ++++++++++++++++-- 1 file changed, 91 insertions(+), 11 deletions(-) diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 9746308866..52b77917cc 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -750,6 +750,78 @@ public class Combat implements Serializable, Copyable { potentialBlockers.add(creature.getId()); } } + // check the mustBlockAllAttackers requirement for creatures already blocking (Blaze of Glory) ------------------------------- + if (effect.mustBlockAllAttackers(game)) { + // find all the attackers that the creature can block (and no restictions prevent this) + Set attackersToBlock = new HashSet<>(); + boolean mayBlock = false; + for (UUID attackingCreatureId : getAttackers()) { + if (creature.canBlock(attackingCreatureId, game)) { + Permanent attackingCreature = game.getPermanent(attackingCreatureId); + if (attackingCreature != null) { + // check if the attacker is already blocked by a max of blockers, so blocker can't block it also + if (attackingCreature.getMaxBlockedBy() != 0) { // 0 = no restriction about the number of possible blockers + int alreadyBlockingCreatures = 0; + for (CombatGroup group : getGroups()) { + if (group.getAttackers().contains(attackingCreatureId)) { + alreadyBlockingCreatures = group.getBlockers().size(); + break; + } + } + if (attackingCreature.getMaxBlockedBy() <= alreadyBlockingCreatures) { + continue; // Attacker can't be blocked by more blockers so check next attacker + } + } + // check restrictions of the creature to block that prevent it can be blocked (note L_J: not sure what this refers to...) + + // check if enough possible blockers are available, if true, mayBlock can be set to true + if (attackingCreature.getMinBlockedBy() > 1) { + int alreadyBlockingCreatures = 0; + for (CombatGroup group : getGroups()) { + if (group.getAttackers().contains(attackingCreatureId)) { + alreadyBlockingCreatures = group.getBlockers().size(); + break; + } + } + if (attackingCreature.getMinBlockedBy() >= alreadyBlockingCreatures) { + continue; // Attacker can't be blocked by the current blocker amount so check next attacker + } + } else { + attackersToBlock.add(attackingCreatureId); + } + } + } + } + if (!attackersToBlock.isEmpty()) { + for (UUID attackerId : attackersToBlock) { + if (!findGroup(attackerId).getBlockers().contains(creature.getId())) { + mayBlock = true; + break; + } + } + } + // if creature can block more attackers, inform human player or set blocks for AI player + if (mayBlock) { + if (controller.isHuman()) { + if (!game.isSimulation()) { + game.informPlayer(controller, "Creature should block all attackers it's able to this turn: " + creature.getIdName()); + } + } else { + Player defender = game.getPlayer(creature.getControllerId()); + if (defender != null) { + for (UUID attackingCreatureId : getAttackers()) { + if (creature.canBlock(attackingCreatureId, game) + && !findGroup(attackingCreatureId).getBlockers().contains(creature.getId()) + && attackersToBlock.contains(attackingCreatureId)) { + // TODO: might need to revisit this (calls some pickBlockerOrder instances even for a single blocker - damage distribution appears to be working correctly however) + defender.declareBlocker(defender.getId(), creature.getId(), attackingCreatureId, game); + } + } + } + } + return false; + } + } } } @@ -773,10 +845,9 @@ public class Combat implements Serializable, Copyable { } } - // check the mustBlockAny requirement ---------------------------------------- - if (effect.mustBlockAny(game)) { - // check that it can block at least one of the attackers - // and no restictions prevent this + // check the mustBlockAny requirement (and mustBlockAllAttackers for not blocking creatures) ---------------------------------------- + if (effect.mustBlockAny(game) || effect.mustBlockAllAttackers(game)) { + // check that it can block at least one of the attackers and no restictions prevent this boolean mayBlock = false; for (UUID attackingCreatureId : getAttackers()) { if (creature.canBlock(attackingCreatureId, game)) { @@ -792,15 +863,23 @@ public class Combat implements Serializable, Copyable { } } if (attackingCreature.getMaxBlockedBy() <= alreadyBlockingCreatures) { - // Attacker can't be blocked by more blockers so check next attacker - continue; + continue; // Attacker can't be blocked by more blockers so check next attacker } } - // check restrictions of the creature to block that prevent it can be blocked + // check restrictions of the creature to block that prevent it can be blocked (note L_J: not sure what this refers to...) + // check if enough possible blockers are available, if true, mayBlock can be set to true if (attackingCreature.getMinBlockedBy() > 1) { - // TODO: check if enough possible blockers are available, if true, mayBlock can be set to true - + int alreadyBlockingCreatures = 0; + for (CombatGroup group : getGroups()) { + if (group.getAttackers().contains(attackingCreatureId)) { + alreadyBlockingCreatures = group.getBlockers().size(); + break; + } + } + if (attackingCreature.getMinBlockedBy() >= alreadyBlockingCreatures) { + continue; // Attacker can't be blocked by the current blocker amount so check next attacker + } } else { mayBlock = true; break; @@ -808,7 +887,7 @@ public class Combat implements Serializable, Copyable { } } } - // if so inform human player or set block for AI player + // if creature can block, inform human player or set block for AI player if (mayBlock) { if (controller.isHuman()) { if (!game.isSimulation()) { @@ -818,7 +897,8 @@ public class Combat implements Serializable, Copyable { Player defender = game.getPlayer(creature.getControllerId()); if (defender != null) { for (UUID attackingCreatureId : getAttackers()) { - if (creature.canBlock(attackingCreatureId, game)) { + if (creature.canBlock(attackingCreatureId, game) + && !findGroup(attackingCreatureId).getBlockers().contains(creature.getId())) { defender.declareBlocker(defender.getId(), creature.getId(), attackingCreatureId, game); break; } From a204630180f186016ad1062ab6f87e9d0bc601c5 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:00:16 +0000 Subject: [PATCH 045/127] Implemented Blaze of Glory --- Mage.Sets/src/mage/cards/b/BlazeOfGlory.java | 127 +++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BlazeOfGlory.java diff --git a/Mage.Sets/src/mage/cards/b/BlazeOfGlory.java b/Mage.Sets/src/mage/cards/b/BlazeOfGlory.java new file mode 100644 index 0000000000..c8e22ca563 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlazeOfGlory.java @@ -0,0 +1,127 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.b; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.BeforeBlockersAreDeclaredCondition; +import mage.abilities.effects.RequirementEffect; +import mage.abilities.effects.common.combat.CanBlockAdditionalCreatureTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TurnPhase; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectPlayer; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class BlazeOfGlory extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(new BlazeOfGloryDefendingPlayerControlsPredicate()); + } + + public BlazeOfGlory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Cast Blaze of Glory only during combat before blockers are declared. + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(TurnPhase.COMBAT, BeforeBlockersAreDeclaredCondition.instance)); + + // Target creature defending player controls can block any number of creatures this turn. It blocks each attacking creature this turn if able. + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addEffect(new CanBlockAdditionalCreatureTargetEffect(Duration.EndOfTurn, 0)); + this.getSpellAbility().addEffect(new BlazeOfGloryRequirementEffect()); + } + + public BlazeOfGlory(final BlazeOfGlory card) { + super(card); + } + + @Override + public BlazeOfGlory copy() { + return new BlazeOfGlory(this); + } +} + +class BlazeOfGloryDefendingPlayerControlsPredicate implements ObjectPlayerPredicate> { + + @Override + public boolean apply(ObjectPlayer input, Game game) { + return game.getCombat().getPlayerDefenders(game).contains(input.getObject().getControllerId()); + } +} + +class BlazeOfGloryRequirementEffect extends RequirementEffect { + + public BlazeOfGloryRequirementEffect() { + super(Duration.EndOfTurn); + this.staticText = "It blocks each attacking creature this turn if able"; + } + + public BlazeOfGloryRequirementEffect(final BlazeOfGloryRequirementEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(targetPointer.getFirst(game, source)); + } + + @Override + public boolean mustAttack(Game game) { + return false; + } + + @Override + public boolean mustBlock(Game game) { + return true; + } + + @Override + public boolean mustBlockAllAttackers(Game game) { + return true; + } + + @Override + public BlazeOfGloryRequirementEffect copy() { + return new BlazeOfGloryRequirementEffect(this); + } + +} From 05123dafbf7c86dffa999836fd12d58d9a229c7a Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:13 +0000 Subject: [PATCH 046/127] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/LimitedEditionAlpha.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java index 4a87e3baac..8b7de5a757 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionAlpha.java @@ -44,6 +44,7 @@ public class LimitedEditionAlpha extends ExpansionSet { cards.add(new SetCardInfo("Black Lotus", 232, Rarity.RARE, mage.cards.b.BlackLotus.class)); cards.add(new SetCardInfo("Black Vise", 233, Rarity.UNCOMMON, mage.cards.b.BlackVise.class)); cards.add(new SetCardInfo("Black Ward", 189, Rarity.UNCOMMON, mage.cards.b.BlackWard.class)); + cards.add(new SetCardInfo("Blaze of Glory", 190, Rarity.RARE, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blessing", 191, Rarity.RARE, mage.cards.b.Blessing.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 50, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Blue Ward", 192, Rarity.UNCOMMON, mage.cards.b.BlueWard.class)); From 38a5bd01f213cfec9cba5f0a172d5e3a67a3cd46 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:17 +0000 Subject: [PATCH 047/127] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/LimitedEditionBeta.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java index aed946ed8a..9b642609e4 100644 --- a/Mage.Sets/src/mage/sets/LimitedEditionBeta.java +++ b/Mage.Sets/src/mage/sets/LimitedEditionBeta.java @@ -44,6 +44,7 @@ public class LimitedEditionBeta extends ExpansionSet { cards.add(new SetCardInfo("Black Lotus", 233, Rarity.RARE, mage.cards.b.BlackLotus.class)); cards.add(new SetCardInfo("Black Vise", 234, Rarity.UNCOMMON, mage.cards.b.BlackVise.class)); cards.add(new SetCardInfo("Black Ward", 5, Rarity.UNCOMMON, mage.cards.b.BlackWard.class)); + cards.add(new SetCardInfo("Blaze of Glory", 6, Rarity.RARE, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blessing", 7, Rarity.RARE, mage.cards.b.Blessing.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 50, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Blue Ward", 8, Rarity.UNCOMMON, mage.cards.b.BlueWard.class)); From 9b3ed94cd4716b81f6ef0a7a0abd4576a250e037 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:25 +0000 Subject: [PATCH 048/127] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/UnlimitedEdition.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/UnlimitedEdition.java b/Mage.Sets/src/mage/sets/UnlimitedEdition.java index c1213ef331..a27c910c64 100644 --- a/Mage.Sets/src/mage/sets/UnlimitedEdition.java +++ b/Mage.Sets/src/mage/sets/UnlimitedEdition.java @@ -44,6 +44,7 @@ public class UnlimitedEdition extends ExpansionSet { cards.add(new SetCardInfo("Black Lotus", 233, Rarity.RARE, mage.cards.b.BlackLotus.class)); cards.add(new SetCardInfo("Black Vise", 234, Rarity.UNCOMMON, mage.cards.b.BlackVise.class)); cards.add(new SetCardInfo("Black Ward", 189, Rarity.UNCOMMON, mage.cards.b.BlackWard.class)); + cards.add(new SetCardInfo("Blaze of Glory", 190, Rarity.RARE, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blessing", 191, Rarity.RARE, mage.cards.b.Blessing.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 50, Rarity.COMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Blue Ward", 192, Rarity.UNCOMMON, mage.cards.b.BlueWard.class)); From a6048f497f6d8c2b4615c4dd1dd108bb89f89b42 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 22:03:31 +0000 Subject: [PATCH 049/127] Implemented Blaze of Glory --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 03ff7d240a..4ce69c5590 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -91,6 +91,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Bee Sting", 144, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); cards.add(new SetCardInfo("Bird Maiden", 110, Rarity.COMMON, mage.cards.b.BirdMaiden.class)); cards.add(new SetCardInfo("Black Knight", 71, Rarity.UNCOMMON, mage.cards.b.BlackKnight.class)); + cards.add(new SetCardInfo("Blaze of Glory", 7, Rarity.UNCOMMON, mage.cards.b.BlazeOfGlory.class)); cards.add(new SetCardInfo("Blue Elemental Blast", 39, Rarity.UNCOMMON, mage.cards.b.BlueElementalBlast.class)); cards.add(new SetCardInfo("Book of Rass", 183, Rarity.UNCOMMON, mage.cards.b.BookOfRass.class)); cards.add(new SetCardInfo("Bottle of Suleiman", 184, Rarity.RARE, mage.cards.b.BottleOfSuleiman.class)); From 4c88b8e6e3977ceb73768fe7de775dc8d2003916 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 10:39:33 +0100 Subject: [PATCH 050/127] * Fixed a bug of AttackedThisStepWatcher (fixes #4549). --- .../java/mage/watchers/common/PlayerAttackedStepWatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java index 03f56c9beb..0fb1362cd2 100644 --- a/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/PlayerAttackedStepWatcher.java @@ -44,7 +44,7 @@ public class PlayerAttackedStepWatcher extends Watcher { private final Map playerAttacked = new HashMap<>(); public PlayerAttackedStepWatcher() { - super(PlayerAttackedWatcher.class.getSimpleName(), WatcherScope.GAME); + super(PlayerAttackedStepWatcher.class.getSimpleName(), WatcherScope.GAME); } public PlayerAttackedStepWatcher(final PlayerAttackedStepWatcher watcher) { From d7b19a1866aca8cdb13adc2a8dbc0e066ba85ac7 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 25 Feb 2018 14:20:42 +0400 Subject: [PATCH 051/127] Fixed random game ends from 89b6aeacd6ef5c5827ad9033746c00e26197f943 --- Mage.Server/src/main/java/mage/server/TableController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index c45f07adc4..7015788472 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -990,7 +990,7 @@ public class TableController { || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { Optional user = UserManager.instance.getUser(userPlayerEntry.getKey()); - if (!user.isPresent() || user.get().isActive()) { + if (!user.isPresent() || !user.get().isActive()) { logger.warn("- Active user of match is missing: " + matchPlayer.getName()); logger.warn("-- matchId:" + match.getId()); logger.warn("-- userId:" + userPlayerEntry.getKey()); From 7e5f4f580d9c8239c20de1523aa2ea043d167bc2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 12:33:57 +0100 Subject: [PATCH 052/127] * Fixed wrong user active check for table health. --- Mage.Server/src/main/java/mage/server/TableController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index c45f07adc4..7015788472 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -990,7 +990,7 @@ public class TableController { || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { Optional user = UserManager.instance.getUser(userPlayerEntry.getKey()); - if (!user.isPresent() || user.get().isActive()) { + if (!user.isPresent() || !user.get().isActive()) { logger.warn("- Active user of match is missing: " + matchPlayer.getName()); logger.warn("-- matchId:" + match.getId()); logger.warn("-- userId:" + userPlayerEntry.getKey()); From 36ef666f0e4bbdb199c6e463589c0d8747880750 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 11:52:43 +0000 Subject: [PATCH 053/127] Implemented Harsh Justice --- Mage.Sets/src/mage/cards/h/HarshJustice.java | 170 +++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HarshJustice.java diff --git a/Mage.Sets/src/mage/cards/h/HarshJustice.java b/Mage.Sets/src/mage/cards/h/HarshJustice.java new file mode 100644 index 0000000000..bfbf9c70af --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HarshJustice.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.cards.h; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author LevelX2 & L_J + */ +public class HarshJustice extends CardImpl { + + public HarshJustice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Cast Harsh Justice only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // This turn, whenever an attacking creature deals combat damage to you, it deals that much damage to its controller. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new HarshJusticeTriggeredAbility())); + } + + public HarshJustice(final HarshJustice card) { + super(card); + } + + @Override + public HarshJustice copy() { + return new HarshJustice(this); + } +} + +class HarshJusticeTriggeredAbility extends DelayedTriggeredAbility { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creature"); + + static { + filter.add(new AttackingPredicate()); + } + + public HarshJusticeTriggeredAbility() { + super(new HarshJusticeEffect(), Duration.EndOfTurn, false); + } + + public HarshJusticeTriggeredAbility(final HarshJusticeTriggeredAbility ability) { + super(ability); + } + + @Override + public HarshJusticeTriggeredAbility copy() { + return new HarshJusticeTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player controller = game.getPlayer(this.getControllerId()); + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + Permanent damagePermanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); + if (controller != null && damagePermanent != null) { + if (damageEvent.isCombatDamage() && controller.getId().equals(damageEvent.getTargetId()) && filter.match(damagePermanent, game)) { + for (Effect effect : this.getEffects()) { + effect.setValue("damage", damageEvent.getAmount()); + effect.setValue("sourceId", damageEvent.getSourceId()); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "This turn, whenever an attacking creature deals combat damage to you, " + super.getRule(); + } +} + +class HarshJusticeEffect extends OneShotEffect { + + public HarshJusticeEffect() { + super(Outcome.Benefit); + this.staticText = "it deals that much damage to its controller"; + } + + public HarshJusticeEffect(final HarshJusticeEffect effect) { + super(effect); + } + + @Override + public HarshJusticeEffect copy() { + return new HarshJusticeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int damage = (Integer) this.getValue("damage"); + UUID sourceId = (UUID) this.getValue("sourceId"); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (sourceObject != null && damage > 0 && sourceId != null) { + Permanent targetObject = game.getPermanentOrLKIBattlefield(sourceId); + if (targetObject != null) { + Player controller = game.getPlayer(targetObject.getControllerId()); + if (controller != null) { + game.informPlayers(sourceObject.getLogName() + ": " + targetObject.getLogName() + " deals " + damage + " damage to " + controller.getLogName()); + controller.damage(damage, sourceId, game, false, true); + return true; + } + } + } + return false; + } +} From 44e5366c48a14bc51bf6238df1ab628687d3a512 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 11:53:58 +0000 Subject: [PATCH 054/127] Implemented Harsh Justice --- Mage.Sets/src/mage/sets/Portal.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index 8c15c619ed..b672a059ab 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -71,7 +71,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Armageddon", 167, Rarity.RARE, mage.cards.a.Armageddon.class)); cards.add(new SetCardInfo("Armored Pegasus", 168, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); cards.add(new SetCardInfo("Arrogant Vampire", 1, Rarity.UNCOMMON, mage.cards.a.ArrogantVampire.class)); - cards.add(new SetCardInfo("Assassin's Blade", 2, Rarity.UNCOMMON, mage.cards.a.AssassinsBlade.class)); + cards.add(new SetCardInfo("Assassin's Blade", 2, Rarity.UNCOMMON, mage.cards.a.AssassinsBlade.class)); cards.add(new SetCardInfo("Balance of Power", 42, Rarity.RARE, mage.cards.b.BalanceOfPower.class)); cards.add(new SetCardInfo("Baleful Stare", 43, Rarity.UNCOMMON, mage.cards.b.BalefulStare.class)); cards.add(new SetCardInfo("Bee Sting", 83, Rarity.UNCOMMON, mage.cards.b.BeeSting.class)); @@ -95,14 +95,14 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Cloud Dragon", 46, Rarity.RARE, mage.cards.c.CloudDragon.class)); cards.add(new SetCardInfo("Cloud Pirates", 47, Rarity.COMMON, mage.cards.c.CloudPirates.class)); cards.add(new SetCardInfo("Cloud Spirit", 48, Rarity.UNCOMMON, mage.cards.c.CloudSpirit.class)); - cards.add(new SetCardInfo("Command of Unsummoning", 49, Rarity.UNCOMMON, mage.cards.c.CommandOfUnsummoning.class)); + cards.add(new SetCardInfo("Command of Unsummoning", 49, Rarity.UNCOMMON, mage.cards.c.CommandOfUnsummoning.class)); cards.add(new SetCardInfo("Coral Eel", 50, Rarity.COMMON, mage.cards.c.CoralEel.class)); cards.add(new SetCardInfo("Craven Giant", 126, Rarity.COMMON, mage.cards.c.CravenGiant.class)); cards.add(new SetCardInfo("Craven Knight", 7, Rarity.COMMON, mage.cards.c.CravenKnight.class)); cards.add(new SetCardInfo("Cruel Bargain", 8, Rarity.RARE, mage.cards.c.CruelBargain.class)); cards.add(new SetCardInfo("Cruel Tutor", 9, Rarity.RARE, mage.cards.c.CruelTutor.class)); cards.add(new SetCardInfo("Deep-Sea Serpent", 52, Rarity.UNCOMMON, mage.cards.d.DeepSeaSerpent.class)); - cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); + cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); cards.add(new SetCardInfo("Deja Vu", 53, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Desert Drake", 127, Rarity.UNCOMMON, mage.cards.d.DesertDrake.class)); cards.add(new SetCardInfo("Devastation", 128, Rarity.RARE, mage.cards.d.Devastation.class)); @@ -143,6 +143,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Grizzly Bears", 94, Rarity.COMMON, mage.cards.g.GrizzlyBears.class)); cards.add(new SetCardInfo("Hand of Death", 18, Rarity.COMMON, mage.cards.h.HandOfDeath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hand of Death", 19, Rarity.COMMON, mage.cards.h.HandOfDeath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harsh Justice", 180, Rarity.RARE, mage.cards.h.HarshJustice.class)); cards.add(new SetCardInfo("Highland Giant", 137, Rarity.COMMON, mage.cards.h.HighlandGiant.class)); cards.add(new SetCardInfo("Hill Giant", 138, Rarity.COMMON, mage.cards.h.HillGiant.class)); cards.add(new SetCardInfo("Horned Turtle", 58, Rarity.COMMON, mage.cards.h.HornedTurtle.class)); @@ -217,7 +218,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Sacred Knight", 186, Rarity.COMMON, mage.cards.s.SacredKnight.class)); cards.add(new SetCardInfo("Sacred Nectar", 187, Rarity.COMMON, mage.cards.s.SacredNectar.class)); cards.add(new SetCardInfo("Scorching Spear", 154, Rarity.COMMON, mage.cards.s.ScorchingSpear.class)); - cards.add(new SetCardInfo("Scorching Winds", 155, Rarity.UNCOMMON, mage.cards.s.ScorchingWinds.class)); + cards.add(new SetCardInfo("Scorching Winds", 155, Rarity.UNCOMMON, mage.cards.s.ScorchingWinds.class)); cards.add(new SetCardInfo("Seasoned Marshal", 188, Rarity.UNCOMMON, mage.cards.s.SeasonedMarshal.class)); cards.add(new SetCardInfo("Serpent Assassin", 31, Rarity.RARE, mage.cards.s.SerpentAssassin.class)); cards.add(new SetCardInfo("Serpent Warrior", 32, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); @@ -252,7 +253,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Tidal Surge", 75, Rarity.COMMON, mage.cards.t.TidalSurge.class)); cards.add(new SetCardInfo("Time Ebb", 76, Rarity.COMMON, mage.cards.t.TimeEbb.class)); cards.add(new SetCardInfo("Touch of Brilliance", 77, Rarity.COMMON, mage.cards.t.TouchOfBrilliance.class)); - cards.add(new SetCardInfo("Treetop Defense", 116, Rarity.RARE, mage.cards.t.TreetopDefense.class)); + cards.add(new SetCardInfo("Treetop Defense", 116, Rarity.RARE, mage.cards.t.TreetopDefense.class)); cards.add(new SetCardInfo("Undying Beast", 36, Rarity.COMMON, mage.cards.u.UndyingBeast.class)); cards.add(new SetCardInfo("Untamed Wilds", 117, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); cards.add(new SetCardInfo("Valorous Charge", 196, Rarity.UNCOMMON, mage.cards.v.ValorousCharge.class)); From aa4997607f188efce10cb37a1cd3ea50b4b72d94 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 11:54:31 +0000 Subject: [PATCH 055/127] Implemented Harsh Justice --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 4ce69c5590..b67d9200c2 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -170,6 +170,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Grapeshot Catapult", 204, Rarity.UNCOMMON, mage.cards.g.GrapeshotCatapult.class)); cards.add(new SetCardInfo("Gravebind", 84, Rarity.COMMON, mage.cards.g.Gravebind.class)); cards.add(new SetCardInfo("Guardian Beast", 85, Rarity.RARE, mage.cards.g.GuardianBeast.class)); + cards.add(new SetCardInfo("Harsh Justice", 13, Rarity.RARE, mage.cards.h.HarshJustice.class)); cards.add(new SetCardInfo("Hasran Ogress", 86, Rarity.COMMON, mage.cards.h.HasranOgress.class)); cards.add(new SetCardInfo("Healing Salve", 14, Rarity.COMMON, mage.cards.h.HealingSalve.class)); cards.add(new SetCardInfo("Horn of Deafening", 205, Rarity.UNCOMMON, mage.cards.h.HornOfDeafening.class)); From 2c28f64294288a33faa60cd65d74105aefc2d7bc Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 14:26:02 +0000 Subject: [PATCH 056/127] CantBeBlockedByCreaturesAllEffect duration bugfix --- .../common/combat/CantBeBlockedByCreaturesAllEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java index fa528cfcdf..11dfb626a5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAllEffect.java @@ -45,7 +45,7 @@ public class CantBeBlockedByCreaturesAllEffect extends RestrictionEffect { private final FilterCreaturePermanent filterCreatures; public CantBeBlockedByCreaturesAllEffect(FilterCreaturePermanent filterCreatures, FilterCreaturePermanent filterBlockedBy, Duration duration) { - super(Duration.WhileOnBattlefield); + super(duration); this.filterCreatures = filterCreatures; this.filterBlockedBy = filterBlockedBy; staticText = new StringBuilder(filterCreatures.getMessage()).append(" can't be blocked ") From d3eeaa28a4235edef18b91e07f33cadfbb924dce Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:20:31 +0000 Subject: [PATCH 057/127] Implemented Deep Wood --- Mage.Sets/src/mage/cards/d/DeepWood.java | 110 +++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DeepWood.java diff --git a/Mage.Sets/src/mage/cards/d/DeepWood.java b/Mage.Sets/src/mage/cards/d/DeepWood.java new file mode 100644 index 0000000000..bb1d3a1405 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeepWood.java @@ -0,0 +1,110 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.d; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.events.DamagePlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author L_J + */ +public class DeepWood extends CardImpl { + + public DeepWood(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Cast Deep Wood only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Prevent all damage that would be dealt to you this turn by attacking creatures. + this.getSpellAbility().addEffect(new DeepWoodEffect()); + } + + public DeepWood(final DeepWood card) { + super(card); + } + + @Override + public DeepWood copy() { + return new DeepWood(this); + } +} + +class DeepWoodEffect extends PreventionEffectImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + + DeepWoodEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false); + staticText = "Prevent all damage that would be dealt to you this turn by attacking creatures"; + } + + DeepWoodEffect(final DeepWoodEffect effect) { + super(effect); + } + + @Override + public DeepWoodEffect copy() { + return new DeepWoodEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) { + DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; + if (event.getTargetId().equals(source.getControllerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); + if (permanent != null && filter.match(permanent, game)) { + return true; + } + } + } + return false; + } +} From 65428726dac8334d8e3f2d614b8a794f21ae47c9 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:21:00 +0000 Subject: [PATCH 058/127] Implemented Dread Charge --- Mage.Sets/src/mage/cards/d/DreadCharge.java | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DreadCharge.java diff --git a/Mage.Sets/src/mage/cards/d/DreadCharge.java b/Mage.Sets/src/mage/cards/d/DreadCharge.java new file mode 100644 index 0000000000..e7c8819cbf --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreadCharge.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.cards.d; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; + +/** + * + * @author L_J + */ +public class DreadCharge extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Black creatures you control"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("except by black creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + filter2.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public DreadCharge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); + + // Black creatures you control can't be blocked this turn except by black creatures. + this.getSpellAbility().addEffect(new CantBeBlockedByCreaturesAllEffect(filter, filter2, Duration.EndOfTurn)); + } + + public DreadCharge(final DreadCharge card) { + super(card); + } + + @Override + public DreadCharge copy() { + return new DreadCharge(this); + } +} From 5152d34c417a1482e7cccf8dc4fc20a59a29e58d Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:21:05 +0000 Subject: [PATCH 059/127] Implemented Cruel Fate --- Mage.Sets/src/mage/cards/c/CruelFate.java | 142 ++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CruelFate.java diff --git a/Mage.Sets/src/mage/cards/c/CruelFate.java b/Mage.Sets/src/mage/cards/c/CruelFate.java new file mode 100644 index 0000000000..ce16987c8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CruelFate.java @@ -0,0 +1,142 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 & L_J + */ +public class CruelFate extends CardImpl { + + public CruelFate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}"); + + // Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of his or her library in any order. + this.getSpellAbility().addEffect(new CruelFateEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + } + + public CruelFate(final CruelFate card) { + super(card); + } + + @Override + public CruelFate copy() { + return new CruelFate(this); + } +} + +class CruelFateEffect extends OneShotEffect { + + public CruelFateEffect() { + super(Outcome.DrawCard); + this.staticText = "Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of his or her library in any order"; + } + + public CruelFateEffect(final CruelFateEffect effect) { + super(effect); + } + + @Override + public CruelFateEffect copy() { + return new CruelFateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card sourceCard = game.getCard(source.getSourceId()); + if (sourceCard != null) { + Player you = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null && you != null) { + Cards cards = new CardsImpl(); + int count = Math.min(player.getLibrary().size(), 5); + for (int i = 0; i < count; i++) { + Card card = player.getLibrary().removeFromTop(game); + if (card != null) { + cards.add(card); + } + } + + you.lookAtCards(sourceCard.getIdName(), cards, game); + + // card to put into opponent's graveyard + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put into target opponent's graveyard")); + if (player.canRespond()) { + if (cards.size() > 1) { + you.choose(Outcome.Detriment, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); + if (card != null) { + cards.remove(card); + card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + } + } + else if (cards.size() == 1) { + Card card = cards.get(cards.iterator().next(), game); + card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true); + } + } + // cards to put on the top of opponent's library + TargetCard target2 = new TargetCard(Zone.LIBRARY, new FilterCard("card to put on the top of target opponent's library")); + while (player.canRespond() && cards.size() > 1) { + you.choose(Outcome.Neutral, cards, target2, game); + Card card = cards.get(target2.getFirstTarget(), game); + if (card != null) { + cards.remove(card); + card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + } + target2.clearChosen(); + } + if (cards.size() == 1) { + Card card = cards.get(cards.iterator().next(), game); + card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + } + return true; + } + } + return false; + } +} From b347ab3eb8743bc161fca559742a8f6a429afcd0 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:22:41 +0000 Subject: [PATCH 060/127] Implemented Temporary Truce --- .../src/mage/cards/t/TemporaryTruce.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TemporaryTruce.java diff --git a/Mage.Sets/src/mage/cards/t/TemporaryTruce.java b/Mage.Sets/src/mage/cards/t/TemporaryTruce.java new file mode 100644 index 0000000000..0efb4ea8dd --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TemporaryTruce.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.cards.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public class TemporaryTruce extends CardImpl { + + public TemporaryTruce(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}"); + + // Each player may draw up to two cards. For each card less than two a player draws this way, that player gains 2 life. + this.getSpellAbility().addEffect(new TemporaryTruceEffect()); + } + + public TemporaryTruce(final TemporaryTruce card) { + super(card); + } + + @Override + public TemporaryTruce copy() { + return new TemporaryTruce(this); + } +} + +class TemporaryTruceEffect extends OneShotEffect { + + TemporaryTruceEffect() { + super(Outcome.DrawCard); + this.staticText = "Each player may draw up to two cards. For each card less than two a player draws this way, that player gains 2 life"; + } + + TemporaryTruceEffect(final TemporaryTruceEffect effect) { + super(effect); + } + + @Override + public TemporaryTruceEffect copy() { + return new TemporaryTruceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + int cardsToDraw = player.getAmount(0, 2, "Draw how many cards?", game); + player.drawCards(cardsToDraw, game); + player.gainLife((2 - cardsToDraw) * 2, game); + } + } + return true; + } + return false; + } +} From 7e83497ba46e94b2c70ae0798ab003bb9fd89861 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:24:30 +0000 Subject: [PATCH 061/127] Implemented cards --- Mage.Sets/src/mage/sets/Portal.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Portal.java b/Mage.Sets/src/mage/sets/Portal.java index b672a059ab..36adbfaa9b 100644 --- a/Mage.Sets/src/mage/sets/Portal.java +++ b/Mage.Sets/src/mage/sets/Portal.java @@ -100,14 +100,17 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Craven Giant", 126, Rarity.COMMON, mage.cards.c.CravenGiant.class)); cards.add(new SetCardInfo("Craven Knight", 7, Rarity.COMMON, mage.cards.c.CravenKnight.class)); cards.add(new SetCardInfo("Cruel Bargain", 8, Rarity.RARE, mage.cards.c.CruelBargain.class)); + cards.add(new SetCardInfo("Cruel Fate", 51, Rarity.RARE, mage.cards.c.CruelFate.class)); cards.add(new SetCardInfo("Cruel Tutor", 9, Rarity.RARE, mage.cards.c.CruelTutor.class)); cards.add(new SetCardInfo("Deep-Sea Serpent", 52, Rarity.UNCOMMON, mage.cards.d.DeepSeaSerpent.class)); + cards.add(new SetCardInfo("Deep Wood", 86, Rarity.UNCOMMON, mage.cards.d.DeepWood.class)); cards.add(new SetCardInfo("Defiant Stand", 174, Rarity.UNCOMMON, mage.cards.d.DefiantStand.class)); cards.add(new SetCardInfo("Deja Vu", 53, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Desert Drake", 127, Rarity.UNCOMMON, mage.cards.d.DesertDrake.class)); cards.add(new SetCardInfo("Devastation", 128, Rarity.RARE, mage.cards.d.Devastation.class)); cards.add(new SetCardInfo("Devoted Hero", 175, Rarity.COMMON, mage.cards.d.DevotedHero.class)); cards.add(new SetCardInfo("Djinn of the Lamp", 54, Rarity.RARE, mage.cards.d.DjinnOfTheLamp.class)); + cards.add(new SetCardInfo("Dread Charge", 10, Rarity.RARE, mage.cards.d.DreadCharge.class)); cards.add(new SetCardInfo("Dread Reaper", 11, Rarity.RARE, mage.cards.d.DreadReaper.class)); cards.add(new SetCardInfo("Dry Spell", 12, Rarity.UNCOMMON, DrySpell.class)); cards.add(new SetCardInfo("Earthquake", 129, Rarity.RARE, mage.cards.e.Earthquake.class)); @@ -246,6 +249,7 @@ public class Portal extends ExpansionSet { cards.add(new SetCardInfo("Sylvan Tutor", 114, Rarity.RARE, mage.cards.s.SylvanTutor.class)); cards.add(new SetCardInfo("Symbol of Unsummoning", 71, Rarity.COMMON, mage.cards.s.SymbolOfUnsummoning.class)); cards.add(new SetCardInfo("Taunt", 72, Rarity.RARE, mage.cards.t.Taunt.class)); + cards.add(new SetCardInfo("Temporary Truce", 195, Rarity.RARE, mage.cards.t.TemporaryTruce.class)); cards.add(new SetCardInfo("Theft of Dreams", 73, Rarity.UNCOMMON, mage.cards.t.TheftOfDreams.class)); cards.add(new SetCardInfo("Thing from the Deep", 74, Rarity.RARE, mage.cards.t.ThingFromTheDeep.class)); cards.add(new SetCardInfo("Thundering Wurm", 115, Rarity.RARE, mage.cards.t.ThunderingWurm.class)); From 5fac17c0d4e1a40bb2ff030a4423456a50baa1b1 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 15:24:55 +0000 Subject: [PATCH 062/127] Implemented Deep Wood --- Mage.Sets/src/mage/sets/PortalSecondAge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/PortalSecondAge.java b/Mage.Sets/src/mage/sets/PortalSecondAge.java index f014ac1968..c393575a31 100644 --- a/Mage.Sets/src/mage/sets/PortalSecondAge.java +++ b/Mage.Sets/src/mage/sets/PortalSecondAge.java @@ -95,6 +95,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Dakmor Sorceress", 11, Rarity.RARE, mage.cards.d.DakmorSorceress.class)); cards.add(new SetCardInfo("Dark Offering", 12, Rarity.UNCOMMON, mage.cards.d.DarkOffering.class)); cards.add(new SetCardInfo("Deathcoil Wurm", 65, Rarity.RARE, mage.cards.d.DeathcoilWurm.class)); + cards.add(new SetCardInfo("Deep Wood", 66, Rarity.UNCOMMON, mage.cards.d.DeepWood.class)); cards.add(new SetCardInfo("Deja Vu", 35, Rarity.COMMON, mage.cards.d.DejaVu.class)); cards.add(new SetCardInfo("Denizen of the Deep", 36, Rarity.RARE, mage.cards.d.DenizenOfTheDeep.class)); cards.add(new SetCardInfo("Earthquake", 94, Rarity.RARE, mage.cards.e.Earthquake.class)); From 2fceafda9388efa574947e1eb5463c9d151b76c1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 21:53:47 +0100 Subject: [PATCH 063/127] * Fixed a bug that mana in the mana pool could not be used to pay mana costs that could only be payed with "you may spend mana as thought" effects (fixes #2581). --- Mage.Sets/src/mage/cards/h/HostageTaker.java | 6 +-- .../cards/asthough/SpendOtherManaTest.java | 30 +++++++++++++++ Mage/src/main/java/mage/Mana.java | 38 ++++++++++++++++--- Mage/src/main/java/mage/players/ManaPool.java | 13 ++++++- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HostageTaker.java b/Mage.Sets/src/mage/cards/h/HostageTaker.java index 92605814ab..73bc0f278c 100644 --- a/Mage.Sets/src/mage/cards/h/HostageTaker.java +++ b/Mage.Sets/src/mage/cards/h/HostageTaker.java @@ -41,10 +41,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AsThoughEffectType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.ManaType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; @@ -118,8 +118,8 @@ class HostageTakerExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent card = game.getPermanent(targetPointer.getFirst(game, source)); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent card = game.getPermanent(getTargetPointer().getFirst(game, source)); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (permanent != null && card != null) { Player controller = game.getPlayer(card.getControllerId()); if (controller != null) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java index a71cef7116..76d22a2f12 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java @@ -134,4 +134,34 @@ public class SpendOtherManaTest extends CardTestPlayerBase { assertHandCount(playerA, "Nissa, Voice of Zendikar", 0); assertPermanentCount(playerA, "Nissa, Voice of Zendikar", 1); } + + @Test + public void testUseSpendManaAsThoughWithManaFromPool() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // Creature {1}{W} + + // When Hostage Taker enters the battlefield, exile another target artifact or creature until Hostage Taker leaves the battlefield. + // You may cast that card as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell. + addCard(Zone.HAND, playerA, "Hostage Taker"); // {2}{U}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hostage Taker"); + setChoice(playerA, "Silvercoat Lion"); + + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R} to your mana pool."); // red mana to pool + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R} to your mana pool."); // red mana to pool + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); // cast it from exile with red mana from pool + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Hostage Taker", 1); + assertTappedCount("Mountain", true, 4); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + + } + } diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index b2461d2b36..0e9aaf2825 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -131,6 +131,35 @@ public class Mana implements Comparable, Serializable, Copyable { } } + public Mana(final ManaType manaType) { + Objects.requireNonNull(manaType, "The passed in ManaType can not be null"); + switch (manaType) { + case GREEN: + green = 1; + break; + case RED: + red = 1; + break; + case BLACK: + black = 1; + break; + case BLUE: + blue = 1; + break; + case WHITE: + white = 1; + break; + case COLORLESS: + colorless = 1; + break; + case GENERIC: + generic = 1; + break; + default: + throw new IllegalArgumentException("Unknown manaType: " + manaType); + } + } + /** * Creates a {@link Mana} object with the passed in {@code num} of Red mana. * {@code num} can not be a negative value. Negative values will be logged @@ -222,13 +251,12 @@ public class Mana implements Comparable, Serializable, Copyable { } /** - * Creates a {@link Mana} object with the passed in {@code num} of Any - * mana. {@code num} can not be a negative value. Negative values will be - * logged and set to 0. + * Creates a {@link Mana} object with the passed in {@code num} of Any mana. + * {@code num} can not be a negative value. Negative values will be logged + * and set to 0. * * @param num value of Any mana to create. - * @return a {@link Mana} object with the passed in {@code num} of Any - * mana. + * @return a {@link Mana} object with the passed in {@code num} of Any mana. */ public static Mana AnyMana(int num) { return new Mana(0, 0, 0, 0, 0, 0, notNegative(num, "Any"), 0); diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index 62ddedf6d6..565e96cac2 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -118,10 +118,19 @@ public class ManaPool implements Serializable { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } + ManaType possibleAsThoughtPoolManaType = null; if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && manaType != unlockedManaType) { // if automatic restricted payment and there is already mana in the pool // and the needed mana type was not unlocked, nothing will be paid - return false; + if (unlockedManaType != null) { + ManaPoolItem checkItem = new ManaPoolItem(); + checkItem.add(unlockedManaType, 1); + possibleAsThoughtPoolManaType = game.getContinuousEffects().asThoughMana(manaType, checkItem, ability.getSourceId(), ability, ability.getControllerId(), game); + } + // Check if it's possible to use mana as thought for the unlocked manatype in the mana pool for this ability + if (possibleAsThoughtPoolManaType == null || possibleAsThoughtPoolManaType != unlockedManaType) { + return false; // if it's not possible return + } } if (getConditional(manaType, ability, filter, game, costToPay) > 0) { @@ -138,7 +147,7 @@ public class ManaPool implements Serializable { } } } - if (manaType != unlockedManaType && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { + if (possibleAsThoughtPoolManaType == null && manaType != unlockedManaType && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { // no mana added beyond the stock so don't auto pay this continue; } From ba483dd87a94b8b5f627f44f66329f27925cc771 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 25 Feb 2018 21:59:06 +0100 Subject: [PATCH 064/127] * Added a test for the new Blood Moon rule (set to @Ignored) because new rule is not implemented. --- .../cards/abilities/enters/BloodMoonTest.java | 168 +++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java index 52b8740aac..d2844086f6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BloodMoonTest.java @@ -5,10 +5,13 @@ */ package org.mage.test.cards.abilities.enters; +import mage.constants.CardType; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -90,7 +93,7 @@ public class BloodMoonTest extends CardTestPlayerBase { * Spreading Seas was played turn 3 in a Steam Vents, Blood Moon turn 7 or * something * - * The enchanted Steam Vents was producing only U when ir should produce + * The enchanted Steam Vents was producing only U when it should produce * only R because of blood moon's time stamp. * * http://blogs.magicjudges.org/articles/2013/06/18/blood-moon-in-a-modern-environment/ @@ -238,4 +241,167 @@ public class BloodMoonTest extends CardTestPlayerBase { Assert.assertTrue("The mana the land can produce should be [{R}] but it's " + playerA.getManaAvailable(currentGame).toString(), playerA.getManaAvailable(currentGame).toString().equals("[{R}]")); } + + /** + * If Blood Moon enters the battlefield with an animated Mutavault in play, + * the Mutavault stays a 2/2 creature with all creature types and “gains” + * the land type mountain (it can also tap for R). However, once the turn + * ends, the Mutavault will loses both of its abilities and become a non + * basic mountain named Mutavault. + */ + @Test + public void testBloodMoonMutavault() { + + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + // {T}: Add {C} to your mana pool. + // {1}: Mutavault becomes a 2/2 creature with all creature types until end of turn. It's still a land. + addCard(Zone.BATTLEFIELD, playerA, "Mutavault", 1); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.HAND, playerB, "Blood Moon", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: "); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blood Moon"); + + setStopAt(2, PhaseStep.END_TURN); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertPowerToughness(playerA, "Mutavault", 2, 2); + assertType("Mutavault", CardType.LAND, SubType.MOUNTAIN); + assertType("Swamp", CardType.LAND, SubType.SWAMP); + + Assert.assertTrue("The mana the land can produce should be [{R}] but it's " + playerA.getManaAvailable(currentGame).toString(), playerA.getManaAvailable(currentGame).toString().equals("[{R}]")); + } + + @Test + public void testBloodMoonMutavaultEnd() { + + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + // {T}: Add {C} to your mana pool. + // {1}: Mutavault becomes a 2/2 creature with all creature types until end of turn. It's still a land. + addCard(Zone.BATTLEFIELD, playerA, "Mutavault", 1); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.HAND, playerB, "Blood Moon", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: "); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blood Moon"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertPowerToughness(playerA, "Mutavault", 0, 0); + assertType("Mutavault", CardType.LAND, SubType.MOUNTAIN); + assertType("Swamp", CardType.LAND, SubType.SWAMP); + + Assert.assertTrue("The mana the lands can produce should be [{B}{R}] but it's " + playerA.getManaAvailable(currentGame).toString(), playerA.getManaAvailable(currentGame).toString().equals("[{B}{R}]")); + } + + /** + * If Blood Moon is on the battlefield, Flagstones of Trokair will enter the + * battlefield as a legendary non-basic Mountain. If Flagstones of Trokair + * is put into the graveyard due to “Legends rule” or because it was + * destroyed, its ability doesn’t trigger, because it doesn’t exist: it + * won’t fetch you a Plains. + */ + @Test + public void testBloodMoonFlagstonesOfTrokair() { + // {T}: Add {W} to your mana pool. + // When Flagstones of Trokair is put into a graveyard from the battlefield, you may search + // your library for a Plains card and put it onto the battlefield tapped. If you do, shuffle your library. + addCard(Zone.HAND, playerA, "Flagstones of Trokair", 1); + addCard(Zone.LIBRARY, playerA, "Plains"); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.BATTLEFIELD, playerB, "Blood Moon"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + addCard(Zone.HAND, playerB, "Stone Rain"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flagstones of Trokair"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Stone Rain", "Flagstones of Trokair"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertGraveyardCount(playerB, "Stone Rain", 1); + + assertPermanentCount(playerA, "Flagstones of Trokair", 0); + assertGraveyardCount(playerA, "Flagstones of Trokair", 1); + + assertPermanentCount(playerA, 0); + } + + /** + * Because Blood Moon’s static ability operates only when it’s on the + * battlefield and begins affecting any nonbasic lands immediately. In fact, + * Blood Moon’s effect is so quick that once a non-basic land hits the + * battlefield it’s going to be affected. Therefore: + * + * If a nonbasic land’s has EtB triggered abilities, these will not trigger + * because the ability isn’t there (it’s gone). Effects that modify the way + * the land enters the battlefield are replacement effects. They are applied + * before the permanent enters the battlefield and taking into account + * continuous effects that already exist and would apply to the permanent. + * (see CR 614.12). + * + * 614.12 Some replacement effects modify how a permanent enters the + * battlefield. (See rules 614.1c-d.) Such effects may come from the + * permanent itself if they affect only that permanent (as opposed to a + * general subset of permanents that includes it). They may also come from + * other sources. To determine which replacement effects apply and how they + * apply, check the characteristics of the permanent as it would exist on + * the battlefield, taking into account replacement effects that have + * already modified how it enters the battlefield (see rule 616.1), + * continuous effects from the permanent's own static abilities that would + * apply to it once it's on the battlefield, and continuous effects that + * ..........................................=========================== + * already exist and would apply to the permanent. + * ================================================ + * + * Madblind Mountain enters the battlefield untapped, as a nonbasic Mountain + * with single mana ability and no other. + */ + @Test + @Ignore + public void testBloodMoonMadblindMountain() { + // {T}: Add {R} to your mana pool. + // Madblind Mountain enters the battlefield tapped. + // {R}, {tap}: Shuffle your library. Activate this ability only if you control two or more red permanents. + addCard(Zone.HAND, playerA, "Madblind Mountain", 1); + + // Blood Moon 2R + // Enchantment + // Nonbasic lands are Mountains + addCard(Zone.BATTLEFIELD, playerB, "Blood Moon"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Madblind Mountain"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertPermanentCount(playerB, "Blood Moon", 1); + assertPermanentCount(playerA, "Madblind Mountain", 1); + + assertTapped("Madblind Mountain", false); // it may not be tapped because the etB effect was removed by Blood Moon + + } } From 9e2fda2a77564605aae2055293b36554be114f22 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:38:05 +0000 Subject: [PATCH 065/127] Implemented Eunuchs' Intrigues --- .../src/mage/cards/e/EunuchsIntrigues.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java diff --git a/Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java b/Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java new file mode 100644 index 0000000000..7592c51744 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EunuchsIntrigues.java @@ -0,0 +1,145 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.e; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 & L_J + */ +public class EunuchsIntrigues extends CardImpl { + + public EunuchsIntrigues(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn. + this.getSpellAbility().addEffect(new EunuchsIntriguesEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public EunuchsIntrigues(final EunuchsIntrigues card) { + super(card); + } + + @Override + public EunuchsIntrigues copy() { + return new EunuchsIntrigues(this); + } +} + +class EunuchsIntriguesEffect extends OneShotEffect { + + EunuchsIntriguesEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn."; + } + + EunuchsIntriguesEffect(final EunuchsIntriguesEffect effect) { + super(effect); + } + + @Override + public EunuchsIntriguesEffect copy() { + return new EunuchsIntriguesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); + filter.add(new ControllerIdPredicate(player.getId())); + Target target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) { + player.chooseTarget(Outcome.DestroyPermanent, target, source, game); + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as his only creature able to block this turn"); + } + } + game.addEffect(new EunuchsIntriguesRestrictionEffect(target.getFirstTarget()), source); + return true; + } +} + +class EunuchsIntriguesRestrictionEffect extends RestrictionEffect { + + protected UUID targetId; + + public EunuchsIntriguesRestrictionEffect(UUID targetId) { + super(Duration.EndOfTurn); + this.targetId = targetId; + } + + public EunuchsIntriguesRestrictionEffect(final EunuchsIntriguesRestrictionEffect effect) { + super(effect); + targetId = effect.targetId; + } + + @Override + public EunuchsIntriguesRestrictionEffect copy() { + return new EunuchsIntriguesRestrictionEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + if (permanent.getControllerId().equals(source.getFirstTarget())) { + return true; + } + return false; + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { + if (targetId != null && blocker.getId().equals(targetId)) { + return true; + } + return false; + } +} From cb677fa9837f292b2b90e34871bd8ed9aef42bd5 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:38:47 +0000 Subject: [PATCH 066/127] Implemented Goblin War Cry --- Mage.Sets/src/mage/cards/g/GoblinWarCry.java | 145 +++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GoblinWarCry.java diff --git a/Mage.Sets/src/mage/cards/g/GoblinWarCry.java b/Mage.Sets/src/mage/cards/g/GoblinWarCry.java new file mode 100644 index 0000000000..a1d6a1939a --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoblinWarCry.java @@ -0,0 +1,145 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.g; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author TheElk801 & L_J + */ +public class GoblinWarCry extends CardImpl { + + public GoblinWarCry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn. + this.getSpellAbility().addEffect(new GoblinWarCryEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public GoblinWarCry(final GoblinWarCry card) { + super(card); + } + + @Override + public GoblinWarCry copy() { + return new GoblinWarCry(this); + } +} + +class GoblinWarCryEffect extends OneShotEffect { + + GoblinWarCryEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn."; + } + + GoblinWarCryEffect(final GoblinWarCryEffect effect) { + super(effect); + } + + @Override + public GoblinWarCryEffect copy() { + return new GoblinWarCryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); + filter.add(new ControllerIdPredicate(player.getId())); + Target target = new TargetPermanent(1, 1, filter, true); + if (target.canChoose(source.getSourceId(), player.getId(), game)) { + while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) { + player.chooseTarget(Outcome.DestroyPermanent, target, source, game); + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as his only creature able to block this turn"); + } + } + game.addEffect(new GoblinWarCryRestrictionEffect(target.getFirstTarget()), source); + return true; + } +} + +class GoblinWarCryRestrictionEffect extends RestrictionEffect { + + protected UUID targetId; + + public GoblinWarCryRestrictionEffect(UUID targetId) { + super(Duration.EndOfTurn); + this.targetId = targetId; + } + + public GoblinWarCryRestrictionEffect(final GoblinWarCryRestrictionEffect effect) { + super(effect); + targetId = effect.targetId; + } + + @Override + public GoblinWarCryRestrictionEffect copy() { + return new GoblinWarCryRestrictionEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + if (permanent.getControllerId().equals(source.getFirstTarget())) { + return true; + } + return false; + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { + if (targetId != null && blocker.getId().equals(targetId)) { + return true; + } + return false; + } +} From 47c3117dd322fee6ffb1f9a1a83f797bd37f5808 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:39:25 +0000 Subject: [PATCH 067/127] Implemented Heavy Fog --- Mage.Sets/src/mage/cards/h/HeavyFog.java | 110 +++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HeavyFog.java diff --git a/Mage.Sets/src/mage/cards/h/HeavyFog.java b/Mage.Sets/src/mage/cards/h/HeavyFog.java new file mode 100644 index 0000000000..e69e57ebf0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeavyFog.java @@ -0,0 +1,110 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.h; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.events.DamagePlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author L_J + */ +public class HeavyFog extends CardImpl { + + public HeavyFog(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Cast Deep Wood only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Prevent all damage that would be dealt to you this turn by attacking creatures. + this.getSpellAbility().addEffect(new HeavyFogEffect()); + } + + public HeavyFog(final HeavyFog card) { + super(card); + } + + @Override + public HeavyFog copy() { + return new HeavyFog(this); + } +} + +class HeavyFogEffect extends PreventionEffectImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + + HeavyFogEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false); + staticText = "Prevent all damage that would be dealt to you this turn by attacking creatures"; + } + + HeavyFogEffect(final HeavyFogEffect effect) { + super(effect); + } + + @Override + public HeavyFogEffect copy() { + return new HeavyFogEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) { + DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; + if (event.getTargetId().equals(source.getControllerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId()); + if (permanent != null && filter.match(permanent, game)) { + return true; + } + } + } + return false; + } +} From 2892573fd3870d84087a0a8aa0f12bc83f24f86c Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:39:57 +0000 Subject: [PATCH 068/127] Implemented Just Fate --- Mage.Sets/src/mage/cards/j/JustFate.java | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/j/JustFate.java diff --git a/Mage.Sets/src/mage/cards/j/JustFate.java b/Mage.Sets/src/mage/cards/j/JustFate.java new file mode 100644 index 0000000000..214396cd31 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JustFate.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.cards.j; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.target.common.TargetAttackingCreature; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author TheElk801 + */ +public class JustFate extends CardImpl { + + public JustFate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Cast Just Fate only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Destroy target attacking creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public JustFate(final JustFate card) { + super(card); + } + + @Override + public JustFate copy() { + return new JustFate(this); + } +} From 3af8e0335092e1e524b745e2ecf3fc49833ffe09 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:40:30 +0000 Subject: [PATCH 069/127] Implemented Kongming's Contraptions --- .../mage/cards/k/KongmingsContraptions.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KongmingsContraptions.java diff --git a/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java b/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java new file mode 100644 index 0000000000..d03db0bfd1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java @@ -0,0 +1,80 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.condition.common.IsStepCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.common.FilterAttackingCreature; +import mage.target.TargetPermanent; +import mage.watchers.common.PlayerAttackedStepWatcher; + +/** + * + * @author L_J + */ +public class KongmingsContraptions extends CardImpl { + + public KongmingsContraptions(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {T}: Kongming's Contraptions deals 2 damage to target attacking creature. Activate this ability only during the declare attackers step and only if you've been attacked this step. + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost(), + new CompoundCondition("during the declare attackers step and only if you've been attacked this step", + new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), AttackedThisStepCondition.instance) + ); + ability.addTarget(new TargetPermanent(new FilterAttackingCreature())); + this.addAbility(ability, new PlayerAttackedStepWatcher()); + } + + public KongmingsContraptions(final KongmingsContraptions card) { + super(card); + } + + @Override + public KongmingsContraptions copy() { + return new KongmingsContraptions(this); + } + +} From 5104442d6d9156d1b7bda337fcab555413e02530 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:41:18 +0000 Subject: [PATCH 070/127] Implemented Warrior's Stand --- Mage.Sets/src/mage/cards/w/WarriorsStand.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WarriorsStand.java diff --git a/Mage.Sets/src/mage/cards/w/WarriorsStand.java b/Mage.Sets/src/mage/cards/w/WarriorsStand.java new file mode 100644 index 0000000000..52b4327e47 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WarriorsStand.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.w; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.common.AttackedThisStepCondition; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; +import mage.filter.StaticFilters; +import mage.watchers.common.PlayerAttackedStepWatcher; +/** + * + * @author L_J + */ +public class WarriorsStand extends CardImpl { + + public WarriorsStand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Cast Warrior's Stand only during the declare attackers step and only if you've been attacked this step. + Ability ability = new CastOnlyDuringPhaseStepSourceAbility( + TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance, + "Cast {this} only during the declare attackers step and only if you've been attacked this step." + ); + ability.addWatcher(new PlayerAttackedStepWatcher()); + this.addAbility(ability); + + // Creatures you control get +2/+2 until end of turn. + this.getSpellAbility().addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, false)); + } + + public WarriorsStand(final WarriorsStand card) { + super(card); + } + + @Override + public WarriorsStand copy() { + return new WarriorsStand(this); + } +} From 84f2d5cb272cda97b02ac87c9093abb486a9db45 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:42:07 +0000 Subject: [PATCH 071/127] Implemented cards --- Mage.Sets/src/mage/sets/PortalThreeKingdoms.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java index bc64a4afe9..611ca80470 100644 --- a/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java +++ b/Mage.Sets/src/mage/sets/PortalThreeKingdoms.java @@ -83,6 +83,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Dong Zhou, the Tyrant", 109, Rarity.RARE, mage.cards.d.DongZhouTheTyrant.class)); cards.add(new SetCardInfo("Eightfold Maze", 2, Rarity.RARE, mage.cards.e.EightfoldMaze.class)); cards.add(new SetCardInfo("Empty City Ruse", 3, Rarity.UNCOMMON, mage.cards.e.EmptyCityRuse.class)); + cards.add(new SetCardInfo("Eunuchs' Intrigues", 110, Rarity.UNCOMMON, mage.cards.e.EunuchsIntrigues.class)); cards.add(new SetCardInfo("Exhaustion", 42, Rarity.RARE, mage.cards.e.Exhaustion.class)); cards.add(new SetCardInfo("Extinguish", 43, Rarity.COMMON, mage.cards.e.Extinguish.class)); cards.add(new SetCardInfo("False Defeat", 4, Rarity.COMMON, mage.cards.f.FalseDefeat.class)); @@ -99,6 +100,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Ghostly Visit", 76, Rarity.COMMON, mage.cards.g.GhostlyVisit.class)); cards.add(new SetCardInfo("Guan Yu's 1,000-Li March", 7, Rarity.RARE, mage.cards.g.GuanYus1000LiMarch.class)); cards.add(new SetCardInfo("Guan Yu, Sainted Warrior", 6, Rarity.RARE, mage.cards.g.GuanYuSaintedWarrior.class)); + cards.add(new SetCardInfo("Heavy Fog", 136, Rarity.UNCOMMON, mage.cards.h.HeavyFog.class)); cards.add(new SetCardInfo("Huang Zhong, Shu General", 8, Rarity.RARE, mage.cards.h.HuangZhongShuGeneral.class)); cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 137, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); cards.add(new SetCardInfo("Hunting Cheetah", 138, Rarity.UNCOMMON, mage.cards.h.HuntingCheetah.class)); @@ -110,6 +112,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Island", 170, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 171, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kongming, \"Sleeping Dragon\"", 9, Rarity.RARE, mage.cards.k.KongmingSleepingDragon.class)); + cards.add(new SetCardInfo("Kongming's Contraptions", 10, Rarity.RARE, mage.cards.k.KongmingsContraptions.class)); cards.add(new SetCardInfo("Lady Sun", 45, Rarity.RARE, mage.cards.l.LadySun.class)); cards.add(new SetCardInfo("Lady Zhurong, Warrior Queen", 139, Rarity.RARE, mage.cards.l.LadyZhurongWarriorQueen.class)); cards.add(new SetCardInfo("Liu Bei, Lord of Shu", 11, Rarity.RARE, mage.cards.l.LiuBeiLordOfShu.class)); @@ -185,6 +188,7 @@ public class PortalThreeKingdoms extends ExpansionSet { cards.add(new SetCardInfo("Virtuous Charge", 29, Rarity.COMMON, mage.cards.v.VirtuousCharge.class)); cards.add(new SetCardInfo("Volunteer Militia", 30, Rarity.COMMON, mage.cards.v.VolunteerMilitia.class)); cards.add(new SetCardInfo("Warrior's Oath", 124, Rarity.RARE, mage.cards.w.WarriorsOath.class)); + cards.add(new SetCardInfo("Warrior's Stand", 31, Rarity.UNCOMMON, mage.cards.w.WarriorsStand.class)); cards.add(new SetCardInfo("Wei Ambush Force", 85, Rarity.COMMON, mage.cards.w.WeiAmbushForce.class)); cards.add(new SetCardInfo("Wei Assassins", 86, Rarity.UNCOMMON, mage.cards.w.WeiAssassins.class)); cards.add(new SetCardInfo("Wei Elite Companions", 87, Rarity.UNCOMMON, mage.cards.w.WeiEliteCompanions.class)); From a87cbc12c76273257c1c4d0d62dac948beaf2f9a Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:42:46 +0000 Subject: [PATCH 072/127] Implemented cards --- Mage.Sets/src/mage/sets/PortalSecondAge.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mage.Sets/src/mage/sets/PortalSecondAge.java b/Mage.Sets/src/mage/sets/PortalSecondAge.java index c393575a31..827a31692c 100644 --- a/Mage.Sets/src/mage/sets/PortalSecondAge.java +++ b/Mage.Sets/src/mage/sets/PortalSecondAge.java @@ -117,6 +117,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Goblin Mountaineer", 101, Rarity.COMMON, mage.cards.g.GoblinMountaineer.class)); cards.add(new SetCardInfo("Goblin Piker", 102, Rarity.COMMON, mage.cards.g.GoblinPiker.class)); cards.add(new SetCardInfo("Goblin Raider", 103, Rarity.COMMON, mage.cards.g.GoblinRaider.class)); + cards.add(new SetCardInfo("Goblin War Cry", 104, Rarity.UNCOMMON, mage.cards.g.GoblinWarCry.class)); cards.add(new SetCardInfo("Goblin War Strike", 105, Rarity.COMMON, mage.cards.g.GoblinWarStrike.class)); cards.add(new SetCardInfo("Golden Bear", 67, Rarity.COMMON, mage.cards.g.GoldenBear.class)); cards.add(new SetCardInfo("Hand of Death", 14, Rarity.COMMON, mage.cards.h.HandOfDeath.class)); @@ -128,6 +129,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Island", 155, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 156, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jagged Lightning", 106, Rarity.UNCOMMON, mage.cards.j.JaggedLightning.class)); + cards.add(new SetCardInfo("Just Fate", 137, Rarity.RARE, mage.cards.j.JustFate.class)); cards.add(new SetCardInfo("Kiss of Death", 16, Rarity.UNCOMMON, mage.cards.k.KissOfDeath.class)); cards.add(new SetCardInfo("Lava Axe", 107, Rarity.COMMON, mage.cards.l.LavaAxe.class)); cards.add(new SetCardInfo("Lone Wolf", 71, Rarity.UNCOMMON, mage.cards.l.LoneWolf.class)); @@ -213,6 +215,7 @@ public class PortalSecondAge extends ExpansionSet { cards.add(new SetCardInfo("Vengeance", 147, Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); cards.add(new SetCardInfo("Volcanic Hammer", 119, Rarity.COMMON, mage.cards.v.VolcanicHammer.class)); cards.add(new SetCardInfo("Volunteer Militia", 148, Rarity.COMMON, mage.cards.v.VolunteerMilitia.class)); + cards.add(new SetCardInfo("Warrior's Stand", 149, Rarity.UNCOMMON, mage.cards.w.WarriorsStand.class)); cards.add(new SetCardInfo("Wildfire", 120, Rarity.RARE, mage.cards.w.Wildfire.class)); cards.add(new SetCardInfo("Wild Griffin", 150, Rarity.COMMON, mage.cards.w.WildGriffin.class)); cards.add(new SetCardInfo("Wild Ox", 90, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); From 5246b960059898180acc2cd0a944c3ff894ede1e Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:43:58 +0000 Subject: [PATCH 073/127] Implemented Heavy Fog --- Mage.Sets/src/mage/sets/MastersEditionIII.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIII.java b/Mage.Sets/src/mage/sets/MastersEditionIII.java index 6ba6134346..dfc7254072 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIII.java @@ -137,6 +137,7 @@ public class MastersEditionIII extends ExpansionSet { cards.add(new SetCardInfo("Hammerheim", 207, Rarity.UNCOMMON, mage.cards.h.Hammerheim.class)); cards.add(new SetCardInfo("Hazezon Tamar", 151, Rarity.RARE, mage.cards.h.HazezonTamar.class)); cards.add(new SetCardInfo("Heal", 14, Rarity.COMMON, mage.cards.h.Heal.class)); + cards.add(new SetCardInfo("Heavy Fog", 122, Rarity.COMMON, mage.cards.h.HeavyFog.class)); cards.add(new SetCardInfo("Hellfire", 70, Rarity.RARE, mage.cards.h.Hellfire.class)); cards.add(new SetCardInfo("Hua Tuo, Honored Physician", 123, Rarity.RARE, mage.cards.h.HuaTuoHonoredPhysician.class)); cards.add(new SetCardInfo("Hunding Gjornersen", 152, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class)); From 2da32a767cb7a3b3c1455168b1ce1c9ee9d4e342 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 25 Feb 2018 21:44:52 +0000 Subject: [PATCH 074/127] Implemented Just Fate --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index b67d9200c2..1912527bc2 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -183,6 +183,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Jade Monolith", 208, Rarity.RARE, mage.cards.j.JadeMonolith.class)); cards.add(new SetCardInfo("Juggernaut", 209, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class)); cards.add(new SetCardInfo("Junún Efreet", 88, Rarity.UNCOMMON, mage.cards.j.JununEfreet.class)); + cards.add(new SetCardInfo("Just Fate", 16, Rarity.COMMON, mage.cards.j.JustFate.class)); cards.add(new SetCardInfo("Kismet", 17, Rarity.RARE, mage.cards.k.Kismet.class)); cards.add(new SetCardInfo("Kormus Bell", 210, Rarity.RARE, mage.cards.k.KormusBell.class)); cards.add(new SetCardInfo("Kudzu", 159, Rarity.UNCOMMON, mage.cards.k.Kudzu.class)); From 576c1aec57ca67c3f61b03bd8e50189f1c4fa002 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 26 Feb 2018 12:47:18 +1100 Subject: [PATCH 075/127] Adding in way to render Zendikar full art lands. Things still to do: 1) Get everyone to download the full art Face images 2) Add in the collector number to the Face images (/FACE/ZEN/Island..jpg for example) 3) Add in the ability to draw say BFZ / HOU full art lands as well. 4) Maybe even add in an UST (Unstable) way of rendering lands (with the swoosh of opaque full art down the bottom). --- .../mage/card/arcane/CardPanelRenderImpl.java | 2 +- .../org/mage/card/arcane/CardRenderer.java | 51 +++-- .../mage/card/arcane/CardRendererUtils.java | 34 ++- .../mage/card/arcane/ModernCardRenderer.java | 209 ++++++++++++++---- .../card/arcane/ModernSplitCardRenderer.java | 58 ++--- 5 files changed, 270 insertions(+), 84 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java index 950069c3fb..498778c2b3 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java @@ -302,7 +302,7 @@ public class CardPanelRenderImpl extends CardPanel { = new CardPanelAttributes(cardWidth, cardHeight, isChoosable(), isSelected()); // Draw card itself - cardRenderer.draw(g2d, attribs); + cardRenderer.draw(g2d, attribs, image); // Done g2d.dispose(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java index 35ec4fc214..d32fa0cdd0 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java @@ -17,10 +17,10 @@ import mage.view.PermanentView; import java.awt.*; import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; import java.awt.image.RasterFormatException; import java.util.ArrayList; import java.util.List; +import java.awt.image.BufferedImage; /** * @author stravant@gmail.com @@ -201,7 +201,8 @@ public abstract class CardRenderer { // The Draw Method // The draw method takes the information caculated by the constructor // and uses it to draw to a concrete size of card and graphics. - public void draw(Graphics2D g, CardPanelAttributes attribs) { + public void draw(Graphics2D g, CardPanelAttributes attribs, BufferedImage image) { + // Pre template method layout, to calculate shared layout info layout(attribs.cardWidth, attribs.cardHeight); isSelected = attribs.isSelected; @@ -211,7 +212,7 @@ public abstract class CardRenderer { drawBorder(g); drawBackground(g); drawArt(g); - drawFrame(g); + drawFrame(g, image); if (!cardView.isAbility()) { drawOverlays(g); drawCounters(g); @@ -226,7 +227,7 @@ public abstract class CardRenderer { protected abstract void drawArt(Graphics2D g); - protected abstract void drawFrame(Graphics2D g); + protected abstract void drawFrame(Graphics2D g, BufferedImage image); // Template methods that are possible to override, but unlikely to be // overridden. @@ -462,22 +463,44 @@ public abstract class CardRenderer { } } else { StringBuilder sbType = new StringBuilder(); - for (SuperType superType : cardView.getSuperTypes()) { - sbType.append(superType).append(' '); - } - for (CardType cardType : cardView.getCardTypes()) { - sbType.append(cardType.toString()).append(' '); - } - if (!cardView.getSubTypes().isEmpty()) { - sbType.append("- "); - for (SubType subType : cardView.getSubTypes()) { - sbType.append(subType).append(' '); + String spType = getCardSuperTypeLine(); + String subType = getCardSubTypeLine(); + if (spType.equalsIgnoreCase("")) { + sbType.append(subType); + } else { + sbType.append(spType); + if (!subType.equalsIgnoreCase("")) { + sbType.append("- "); + sbType.append(subType); } } + return sbType.toString(); } } + protected String getCardSuperTypeLine() { + StringBuilder spType = new StringBuilder(); + for (SuperType superType : cardView.getSuperTypes()) { + spType.append(superType).append(' '); + } + for (CardType cardType : cardView.getCardTypes()) { + spType.append(cardType.toString()).append(' '); + } + return spType.toString(); + } + + protected String getCardSubTypeLine() { + StringBuilder subType = new StringBuilder(); + + if (!cardView.getSubTypes().isEmpty()) { + for (SubType sType : cardView.getSubTypes()) { + subType.append(sType).append(' '); + } + } + return subType.toString(); + } + // Set the card art image (CardPanel will give it to us when it // is loaded and ready) public void setArtImage(Image image) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index eca040234f..f38d9b0f75 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -52,7 +52,7 @@ public final class CardRendererUtils { return bimage; } - private static Color abitbrighter(Color c) { + public static Color abitbrighter(Color c) { int r = c.getRed(); int g = c.getGreen(); int b = c.getBlue(); @@ -68,7 +68,7 @@ public final class CardRendererUtils { alpha); } - private static Color abitdarker(Color c) { + public static Color abitdarker(Color c) { int r = c.getRed(); int g = c.getGreen(); int b = c.getBlue(); @@ -107,6 +107,36 @@ public final class CardRendererUtils { g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); } + + public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { + g.setColor(new Color(0, 0, 0, 150)); + + g.drawOval(x - 1, y, bevel * 2, h); + g.setPaint(border); + g.drawOval(x, y, bevel * 2 - 1, h - 1); + g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); + g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); + g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); + + // The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4 + g.drawOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); + + g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); + g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); + g.setPaint(fill); + g.setPaint(fill); + g.setColor(abitbrighter(g.getColor())); + g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); + g.setPaint(fill); + g.setColor(abitdarker(g.getColor())); + g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); + + g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); + g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); + g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); + + g.fillOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); + } // Get the width of a mana cost rendered with ManaSymbols.draw public static int getManaCostWidth(String manaCost, int symbolSize) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index a75c12fe63..16c0fc996b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -7,6 +7,7 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; +import java.awt.geom.QuadCurve2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; @@ -312,6 +313,8 @@ public class ModernCardRenderer extends CardRenderer { Rectangle2D rect; if (useInventionFrame()) { rect = new Rectangle2D.Float(0, 0, 1, 1); + } else if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + rect = new Rectangle2D.Float(.079f, .11f, .84f, .84f); } else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f); } else { @@ -352,7 +355,7 @@ public class ModernCardRenderer extends CardRenderer { if (artImage != null && !cardView.isFaceDown()) { boolean useFaceArt = false; - if (faceArtImage != null) { + if (faceArtImage != null && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { useFaceArt = true; } @@ -395,17 +398,22 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); - } else { + } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { drawArtIntoRect(g, totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); + } else { + /* drawArtIntoRect(g, + totalContentInset + 1, totalContentInset + boxHeight, + contentWidth - 2, typeLineY, + sourceRect, shouldPreserveAspect);*/ } } } @Override - protected void drawFrame(Graphics2D g) { + protected void drawFrame(Graphics2D g, BufferedImage image) { // Get the card colors to base the frame on ObjectColor frameColors = getFrameObjectColor(); @@ -421,12 +429,13 @@ public class ModernCardRenderer extends CardRenderer { // Draw the main card content border g.setPaint(borderPaint); + if (cardView.getFrameStyle() == FrameStyle.KLD_INVENTION) { g.drawImage(FRAME_INVENTION, 0, 0, cardWidth, cardHeight, null); g.drawRect( totalContentInset, typeLineY, contentWidth - 1, cardHeight - borderWidth * 3 - typeLineY - 1); - } else { + } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { g.drawRect( totalContentInset, totalContentInset, contentWidth - 1, cardHeight - borderWidth * 3 - totalContentInset - 1); @@ -437,11 +446,13 @@ public class ModernCardRenderer extends CardRenderer { g.setPaint(new Color(255, 255, 255, 150)); } else { g.setPaint(textboxPaint); - } - g.fillRect( - totalContentInset + 1, typeLineY, - contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); + + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + g.fillRect( + totalContentInset + 1, typeLineY, + contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); + } // If it's a planeswalker, extend the textbox left border by some if (cardView.isPlanesWalker()) { @@ -451,7 +462,7 @@ public class ModernCardRenderer extends CardRenderer { cardWidth / 16, cardHeight - typeLineY - boxHeight - borderWidth * 3); } - if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION) { + if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { // Draw a shadow highlight at the right edge of the content frame g.setColor(new Color(0, 0, 0, 100)); g.fillRect( @@ -470,26 +481,31 @@ public class ModernCardRenderer extends CardRenderer { cardWidth - 2 * borderWidth, boxHeight, contentInset, borderPaint, boxColor); - // Draw the type line box - CardRendererUtils.drawRoundedBox(g, - borderWidth, typeLineY, - cardWidth - 2 * borderWidth, boxHeight, - contentInset, - borderPaint, boxColor); + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + CardRendererUtils.drawRoundedBox(g, + borderWidth, typeLineY, + cardWidth - 2 * borderWidth, boxHeight, + contentInset, + borderPaint, boxColor); - // Draw a small separator between the type line and box, and shadow - // at the left of the texbox, and above the name line - g.setColor(new Color(0, 0, 0, 150)); - g.fillRect( - totalContentInset - 1, totalContentInset - 1, - contentWidth + 1, 1); - g.fillRect( - totalContentInset + 1, typeLineY + boxHeight, - contentWidth - 2, 1); - g.fillRect( - cardWidth - totalContentInset - 1, typeLineY + boxHeight, - 1, cardHeight - borderWidth * 3 - typeLineY - boxHeight); + // Draw a small separator between the type line and box, and shadow + // at the left of the texbox, and above the name line + g.setColor(new Color(0, 0, 0, 150)); + g.fillRect( + totalContentInset - 1, totalContentInset - 1, + contentWidth + 1, 1); + g.fillRect( + totalContentInset + 1, typeLineY + boxHeight, + contentWidth - 2, 1); + g.fillRect( + cardWidth - totalContentInset - 1, typeLineY + boxHeight, + 1, cardHeight - borderWidth * 3 - typeLineY - boxHeight); + // Draw the type line + drawTypeLine(g, getCardTypeLine(), + totalContentInset, typeLineY, + contentWidth, boxHeight, true); + } // Draw the transform circle int nameOffset = drawTransformationCircle(g, borderPaint); @@ -502,20 +518,115 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + nameOffset, totalContentInset, contentWidth - nameOffset, boxHeight); - // Draw the type line - drawTypeLine(g, getCardTypeLine(), - totalContentInset, typeLineY, - contentWidth, boxHeight); - // Draw the textbox rules - drawRulesText(g, textboxKeywords, textboxRules, - totalContentInset + 2, typeLineY + boxHeight + 2, - contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + drawRulesText(g, textboxKeywords, textboxRules, + totalContentInset + 2, typeLineY + boxHeight + 2, + contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); + } else { + int x = totalContentInset; + int y = typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset; + int w = contentWidth - 2; + int h = boxHeight - 4; + + CardRendererUtils.drawZendikarLandBox(g, + x, y, w, h, + contentInset, + borderPaint, boxColor); + drawTypeLine(g, getCardSuperTypeLine(), + totalContentInset + 2, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + contentWidth / 2 - boxHeight, boxHeight - 4, false); + drawTypeLine(g, getCardSubTypeLine(), + totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + 3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true); + + // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... + int x2 = x; + int y2 = y; + int topxdelta = 45 * contentWidth / 1000; + int botxdelta = 58 * contentWidth / 1000; + int ctrlx = 0; + int ctrly = (totalContentInset + y2) / 2; + + drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, + x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, + cardWidth, cardHeight); + + QuadCurve2D q = new QuadCurve2D.Float(); + q.setCurve(x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2); + g.setColor(boxColor); + g.setPaint(borderPaint); + g.draw(q); + q.setCurve(x + topxdelta - 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta - 1, y2); + g.draw(q); + q.setCurve(x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2); + g.draw(q); + q.setCurve(x + contentWidth - topxdelta + 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta + 1, y2); + g.draw(q); + + g.setColor(Color.BLACK); + q.setCurve(x + topxdelta + 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta + 1, y2 - 1); + g.draw(q); + q.setCurve(x + contentWidth - topxdelta - 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta - 1, y2 - 1); + g.draw(q); + + drawRulesText(g, textboxKeywords, textboxRules, + x, y, + w, h); + } // Draw the bottom right stuff drawBottomRight(g, borderPaint, boxColor); } + public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int ctrlx, int ctrly, int w, int h, + int x2, int y2, int ctrlx2, int ctrly2, int w2, int h2, + int cardWidth, int cardHeight) { + + BufferedImage artToUse = faceArtImage; + if (faceArtImage == null) { + if (artImage == null) { + return; + } + artToUse = artImage; + } + int srcW = artToUse.getWidth(); + int srcH = artToUse.getHeight(); + + Rectangle2D rect = new Rectangle2D.Float(); + rect.setRect(0, 0, srcW, srcH); + g2.clip(rect); + + QuadCurve2D q = new QuadCurve2D.Float(); + q.setCurve(x, y, ctrlx, ctrly, w, h); + g2.setClip(null); + g2.clip(q); + //g2.drawImage(faceArtImage, x, y, (x2-x), (h2-y), null); + Rectangle2D r = q.getBounds2D(); + int minX = (int) r.getX(); + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + + QuadCurve2D q2 = new QuadCurve2D.Float(); + q2.setCurve(x2, y2, ctrlx2, ctrly2, w2, h2); + g2.setClip(null); + g2.clip(q2); + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + + Polygon p = new Polygon(); + //x = 10; + //y = 0; + p.addPoint(x, y); + p.addPoint(x2, y); + p.addPoint(w2, h2); + p.addPoint(w, h2); + p.addPoint(x, y); + g2.setClip(null); + g2.clip(p); + + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + g2.setClip(null); + } + // Draw the name line protected void drawNameLine(Graphics2D g, String baseName, String manaCost, int x, int y, int w, int h) { // Width of the mana symbols @@ -566,13 +677,13 @@ public class ModernCardRenderer extends CardRenderer { } // Draw the type line (color indicator, types, and expansion symbol) - protected void drawTypeLine(Graphics2D g, String baseTypeLine, int x, int y, int w, int h) { + protected void drawTypeLine(Graphics2D g, String baseTypeLine, int x, int y, int w, int h, boolean withSymbol) { // Draw expansion symbol - int expansionSymbolWidth; + int expansionSymbolWidth = 0; if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_SET_SYMBOL, "false").equals("false")) { if (cardView.isAbility()) { expansionSymbolWidth = 0; - } else { + } else if (withSymbol) { expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h); } } else { @@ -792,9 +903,20 @@ public class ModernCardRenderer extends CardRenderer { } // Basic mana draw mana symbol in textbox (for basic lands) - if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand()) { - drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); - return; + if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } else // Big circle in the middle for Zendikar lands + if (allRules.size() == 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } else { + if (allRules.size() > 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + } + return; + } } // Go through possible font sizes in descending order to find the best fit @@ -847,6 +969,11 @@ public class ModernCardRenderer extends CardRenderer { ManaSymbols.draw(g, symbs, x + (w - manaCostWidth) / 2, y + (h - symbHeight) / 2, symbHeight, Color.black, 2); } + private void drawBasicManaSymbol(Graphics2D g, int x, int y, int w, int h, String symbol) { + String symbs = symbol; + ManaSymbols.draw(g, symbs, x, y, w, Color.black, 2); + } + // Get the first line of the textbox, the keyword string private static String getKeywordRulesString(ArrayList keywords) { StringBuilder builder = new StringBuilder(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java index 98d01254c6..0310d8e3ac 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java @@ -8,6 +8,7 @@ import mage.view.CardView; import java.awt.*; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; @@ -17,6 +18,7 @@ import java.util.List; public class ModernSplitCardRenderer extends ModernCardRenderer { private class HalfCardProps { + int x, y, w, h, cw, ch; String name; @@ -27,7 +29,11 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { ArrayList keywords = new ArrayList<>(); } - private static ArrayList ONLY_LAND_TYPE = new ArrayList() {{add(CardType.LAND);}}; + private static ArrayList ONLY_LAND_TYPE = new ArrayList() { + { + add(CardType.LAND); + } + }; // Right and left halves of the card content private HalfCardProps rightHalf = new HalfCardProps(); @@ -88,20 +94,20 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Decide size of divider if (isAftermath()) { dividerSize = borderWidth; - dividerAt = (int)(cardHeight*0.54); + dividerAt = (int) (cardHeight * 0.54); } else { - int availHeight = cardHeight - totalContentInset - 3*borderWidth; - dividerSize = borderWidth*2; - dividerAt = (int)(totalContentInset + availHeight * 0.5 - borderWidth); + int availHeight = cardHeight - totalContentInset - 3 * borderWidth; + dividerSize = borderWidth * 2; + dividerAt = (int) (totalContentInset + availHeight * 0.5 - borderWidth); } // Decide size of each halves box rightHalf.x = leftHalf.x = totalContentInset; - rightHalf.w = leftHalf.w = cardWidth - 2*totalContentInset; + rightHalf.w = leftHalf.w = cardWidth - 2 * totalContentInset; leftHalf.y = totalContentInset; leftHalf.h = dividerAt - totalContentInset; rightHalf.y = dividerAt + dividerSize; - rightHalf.h = cardHeight - rightHalf.y - borderWidth*3; + rightHalf.h = cardHeight - rightHalf.y - borderWidth * 3; // Content width / height (Exchanged from width / height if the card part is rotated) if (isAftermath()) { @@ -126,7 +132,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { private ObjectColor getColorFromManaCostHack(ManaCosts costs) { ObjectColor c = new ObjectColor(); List symbols = costs.getSymbols(); - for (String symbol: symbols) { + for (String symbol : symbols) { if (symbol.contains("W")) { c.setWhite(true); } else if (symbol.contains("U")) { @@ -154,18 +160,18 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw main part (most of card) g.fillRoundRect( borderWidth, borderWidth, - cardWidth - 2*borderWidth, leftHalf.h + contentInset - borderWidth - 2*cornerRadius + (cornerRadius - 1), + cardWidth - 2 * borderWidth, leftHalf.h + contentInset - borderWidth - 2 * cornerRadius + (cornerRadius - 1), cornerRadius - 1, cornerRadius - 1); // Draw the M15 rounded "swoosh" at the bottom g.fillRoundRect( - borderWidth, dividerAt - borderWidth - 4*cornerRadius, - cardWidth - 2*borderWidth, cornerRadius * 4, + borderWidth, dividerAt - borderWidth - 4 * cornerRadius, + cardWidth - 2 * borderWidth, cornerRadius * 4, cornerRadius * 2, cornerRadius * 2); // Draw the cutout into the "swoosh" for the textbox to lie over g.fillRect( - borderWidth + contentInset, dividerAt - 2*borderWidth, + borderWidth + contentInset, dividerAt - 2 * borderWidth, cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); } @@ -176,8 +182,8 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw the M15 rounded "swoosh"es at the top and bottom g.fillRoundRect( borderWidth, dividerAt + dividerSize + borderWidth, - cardWidth - 2*borderWidth, rightHalf.h - 2*borderWidth, - cornerRadius*2, cornerRadius*2); + cardWidth - 2 * borderWidth, rightHalf.h - 2 * borderWidth, + cornerRadius * 2, cornerRadius * 2); // Draw the cutout into the "swoosh" for the textbox to lie over g.fillRect( @@ -236,8 +242,8 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Background of textbox g.setPaint(textboxPaint); g.fillRect( - 1, typeLineY, - half.cw - 2, half.ch - typeLineY - 1); + 1, typeLineY, + half.cw - 2, half.ch - typeLineY - 1); // Draw the name line box CardRendererUtils.drawRoundedBox(g, @@ -261,7 +267,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw the type line drawTypeLine(g, half.typeLineString, 0, typeLineY, - half.cw, boxHeight - 4); + half.cw, boxHeight - 4, true); // Draw the textbox rules drawRulesText(g, half.keywords, half.rules, @@ -270,13 +276,13 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } private Graphics2D getUnmodifiedHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(leftHalf.x, leftHalf.y); return g2; } private Graphics2D getAftermathHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(rightHalf.x, rightHalf.y); g2.rotate(Math.PI / 2); g2.translate(0, -rightHalf.w); @@ -284,7 +290,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } private Graphics2D getLeftHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(leftHalf.x, leftHalf.y); g2.rotate(-Math.PI / 2); g2.translate(-leftHalf.cw, 0); @@ -292,7 +298,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } private Graphics2D getRightHalfContext(Graphics2D g) { - Graphics2D g2 = (Graphics2D)g.create(); + Graphics2D g2 = (Graphics2D) g.create(); g2.translate(rightHalf.x, rightHalf.y); g2.rotate(-Math.PI / 2); g2.translate(-rightHalf.cw, 0); @@ -300,13 +306,13 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { } @Override - protected void drawFrame(Graphics2D g) { + protected void drawFrame(Graphics2D g, BufferedImage image) { if (isAftermath()) { - drawSplitHalfFrame(getUnmodifiedHalfContext(g), leftHalf, (int)(leftHalf.ch * TYPE_LINE_Y_FRAC)); + drawSplitHalfFrame(getUnmodifiedHalfContext(g), leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC)); drawSplitHalfFrame(getAftermathHalfContext(g), rightHalf, (rightHalf.ch - boxHeight) / 2); } else { - drawSplitHalfFrame(getLeftHalfContext(g), leftHalf, (int)(leftHalf.ch * TYPE_LINE_Y_FRAC)); - drawSplitHalfFrame(getRightHalfContext(g), rightHalf, (int)(rightHalf.ch * TYPE_LINE_Y_FRAC)); + drawSplitHalfFrame(getLeftHalfContext(g), leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC)); + drawSplitHalfFrame(getRightHalfContext(g), rightHalf, (int) (rightHalf.ch * TYPE_LINE_Y_FRAC)); if (isFuse()) { Graphics2D g2 = getRightHalfContext(g); int totalFuseBoxWidth = rightHalf.cw * 2 + 2 * borderWidth + dividerSize; @@ -319,7 +325,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { borderPaint, boxColor); drawNameLine(g2, "Fuse (You may cast both halves from your hand)", "", 0, rightHalf.ch, - totalFuseBoxWidth - 2*borderWidth, boxHeight); + totalFuseBoxWidth - 2 * borderWidth, boxHeight); } } } From afeb1c16ac962a16939a5235b5659b38646f2da1 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 26 Feb 2018 15:22:59 +1100 Subject: [PATCH 076/127] Adding in way to render Zendikar full art lands. Things still to do: 1) Get everyone to download the full art Face images 2) Add in the collector number to the Face images (/FACE/ZEN/Island..jpg for example) 3) Add in the ability to draw say BFZ / HOU full art lands as well. 4) Maybe even add in an UST (Unstable) way of rendering lands (with the swoosh of opaque full art down the bottom). --- .../java/org/mage/card/arcane/ModernCardRenderer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 16c0fc996b..3f04098a7b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -584,14 +584,25 @@ public class ModernCardRenderer extends CardRenderer { int cardWidth, int cardHeight) { BufferedImage artToUse = faceArtImage; + boolean hadToUseFullArt = false; if (faceArtImage == null) { if (artImage == null) { return; } + hadToUseFullArt = true; artToUse = artImage; } int srcW = artToUse.getWidth(); int srcH = artToUse.getHeight(); + + if (hadToUseFullArt) { + // Get a box based on the standard scan from gatherer. + // Width = 185/223 pixels (centered) + // Height = 220/310, 38 pixels from top + int subx = 19 * srcW / 223; + int suby = 38 * srcH / 310; + artToUse = artImage.getSubimage(subx, suby, 185*srcW / 223, 220*srcH / 310); + } Rectangle2D rect = new Rectangle2D.Float(); rect.setRect(0, 0, srcW, srcH); From 1dd88b0ea77f5ac684cc8ab89617054980e5328e Mon Sep 17 00:00:00 2001 From: lcoviedo Date: Mon, 26 Feb 2018 12:27:19 +0000 Subject: [PATCH 077/127] spells cost reduction text --- Mage.Sets/src/mage/cards/t/TheImmortalSun.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java index 4b1fa19c8a..8b303e3e38 100644 --- a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java +++ b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java @@ -68,7 +68,7 @@ public class TheImmortalSun extends CardImpl { this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1) .setText("draw an additional card"), TargetController.YOU, false)); // Spells you cast cost {1} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCard("Spells you cast"), 1))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCard("Spells"), 1))); // Creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield))); } From aeb1fa6bb3e906218ee5176d0cecc0b546bb93a6 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 26 Feb 2018 16:48:38 +0400 Subject: [PATCH 078/127] Card text fixes --- Mage.Sets/src/mage/cards/c/ConduitOfRuin.java | 2 +- Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java | 2 +- Mage.Sets/src/mage/cards/j/JacesSanctum.java | 2 +- Mage.Sets/src/mage/cards/k/KrosanDrover.java | 5 ++++- Mage.Sets/src/mage/cards/m/ManaMatrix.java | 2 +- Mage.Sets/src/mage/cards/p/PlanarGate.java | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java index b24a933ae8..5ea158c1f4 100644 --- a/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java +++ b/Mage.Sets/src/mage/cards/c/ConduitOfRuin.java @@ -81,7 +81,7 @@ public class ConduitOfRuin extends CardImpl { // The first creature spell you cast each turn costs {2} less to cast. Effect effect = new SpellsCostReductionControllerEffect(filterCost, 2); - effect.setText("The first creature spell you cast each turn costs {2} less to cast"); + effect.setText("The first creature spell you cast each turn costs {2} less to cast."); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect), new ConduitOfRuinWatcher()); } diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java b/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java index 6c31a6de0a..4fdc337841 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfThePantheon.java @@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; */ public class HeraldOfThePantheon extends CardImpl { - private static final FilterCard filter = new FilterCard("enchantment spells"); + private static final FilterCard filter = new FilterCard("Enchantment spells"); private static final FilterSpell filter2 = new FilterSpell("an enchantment spell"); static { diff --git a/Mage.Sets/src/mage/cards/j/JacesSanctum.java b/Mage.Sets/src/mage/cards/j/JacesSanctum.java index 717038e90b..0acd517e84 100644 --- a/Mage.Sets/src/mage/cards/j/JacesSanctum.java +++ b/Mage.Sets/src/mage/cards/j/JacesSanctum.java @@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; public class JacesSanctum extends CardImpl { - private static final FilterCard filter = new FilterCard("instant and sorcery spells"); + private static final FilterCard filter = new FilterCard("Instant and sorcery spells"); private static final FilterSpell filter2 = new FilterSpell("an instant or sorcery spell"); diff --git a/Mage.Sets/src/mage/cards/k/KrosanDrover.java b/Mage.Sets/src/mage/cards/k/KrosanDrover.java index b8237a2beb..7bd3c7d341 100644 --- a/Mage.Sets/src/mage/cards/k/KrosanDrover.java +++ b/Mage.Sets/src/mage/cards/k/KrosanDrover.java @@ -30,6 +30,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -61,7 +62,9 @@ public class KrosanDrover extends CardImpl { this.toughness = new MageInt(2); // Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 2))); + Effect effect = new SpellsCostReductionControllerEffect(filter, 2); + effect.setText("Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast."); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public KrosanDrover(final KrosanDrover card) { diff --git a/Mage.Sets/src/mage/cards/m/ManaMatrix.java b/Mage.Sets/src/mage/cards/m/ManaMatrix.java index e2e0fac2c5..1777b831fd 100644 --- a/Mage.Sets/src/mage/cards/m/ManaMatrix.java +++ b/Mage.Sets/src/mage/cards/m/ManaMatrix.java @@ -44,7 +44,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; */ public class ManaMatrix extends CardImpl { - private static final FilterCard filter = new FilterCard("instant and enchantment spells"); + private static final FilterCard filter = new FilterCard("Instant and enchantment spells"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/p/PlanarGate.java b/Mage.Sets/src/mage/cards/p/PlanarGate.java index 8ef656f71f..42cea9d0df 100644 --- a/Mage.Sets/src/mage/cards/p/PlanarGate.java +++ b/Mage.Sets/src/mage/cards/p/PlanarGate.java @@ -46,7 +46,7 @@ public class PlanarGate extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}"); // Creature spells you cast cost up to {2} less to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCreatureCard("creature spells"), 2, true))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCreatureCard("Creature spells"), 2, true))); } public PlanarGate(final PlanarGate card) { From 5f98cb685e6dbfae3e45ab852863fd23798457e2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 26 Feb 2018 16:17:16 +0100 Subject: [PATCH 079/127] * Hushwing Gryff - Fixed that log message was sent out too often (fixes #4548). --- .../src/mage/cards/a/AmbuscadeShaman.java | 12 +++++------- Mage.Sets/src/mage/cards/h/HushwingGryff.java | 18 ++++++++++++------ Mage.Sets/src/mage/cards/t/TorporOrb.java | 16 +++++++++++----- .../test/cards/replacement/TorporOrbTest.java | 3 ++- .../java/mage/abilities/TriggeredAbility.java | 11 +++++++++++ 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java index ec6cf2b57a..ce6a6d5522 100644 --- a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java +++ b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java @@ -51,17 +51,17 @@ import mage.target.targetpointer.FixedTarget; public class AmbuscadeShaman extends CardImpl { public AmbuscadeShaman(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ORC); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Ambuscade Shaman or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn. - Effect effect = new BoostTargetEffect(2,2, Duration.EndOfTurn); + Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); effect.setText("that creature gets +2/+2 until end of turn"); this.addAbility(new AmbuscadeShamanTriggeredAbility(effect)); - + // Dash {3}{B} (You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)); this.addAbility(new DashAbility(this, "{3}{B}")); @@ -103,9 +103,7 @@ class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl { Permanent permanent = game.getPermanent(targetId); if (permanent.getControllerId().equals(this.controllerId) && permanent.isCreature()) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } return false; @@ -115,4 +113,4 @@ class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever {this} or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/h/HushwingGryff.java b/Mage.Sets/src/mage/cards/h/HushwingGryff.java index b3316468dc..97eac84084 100644 --- a/Mage.Sets/src/mage/cards/h/HushwingGryff.java +++ b/Mage.Sets/src/mage/cards/h/HushwingGryff.java @@ -31,6 +31,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.keyword.FlashAbility; @@ -39,9 +40,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; @@ -55,7 +56,7 @@ import mage.game.permanent.Permanent; public class HushwingGryff extends CardImpl { public HushwingGryff(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HIPPOGRIFF); this.power = new MageInt(2); @@ -92,10 +93,15 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(event.getSourceId()); + MageObject enteringObject = game.getObject(event.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (mageObject != null && sourceObject != null) { - return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger"; + Ability ability = (Ability) getValue("targetAbility"); + if (enteringObject != null && sourceObject != null && ability != null) { + MageObject abilitObject = game.getObject(ability.getSourceId()); + if (abilitObject != null) { + return sourceObject.getLogName() + " prevented ability of " + abilitObject.getLogName() + + " to trigger for " + enteringObject.getLogName() + " entering the battlefield."; + } } return null; } @@ -111,7 +117,7 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { if (ability != null && ability.getAbilityType() == AbilityType.TRIGGERED) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); if (permanent != null && permanent.isCreature()) { - return true; + return (((TriggeredAbility) ability).checkTrigger(event, game)); } } return false; diff --git a/Mage.Sets/src/mage/cards/t/TorporOrb.java b/Mage.Sets/src/mage/cards/t/TorporOrb.java index 8aafc5c297..8375b7c62a 100644 --- a/Mage.Sets/src/mage/cards/t/TorporOrb.java +++ b/Mage.Sets/src/mage/cards/t/TorporOrb.java @@ -30,6 +30,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.cards.CardImpl; @@ -51,7 +52,7 @@ import mage.game.permanent.Permanent; public class TorporOrb extends CardImpl { public TorporOrb(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Creatures entering the battlefield don't cause abilities to trigger. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TorporOrbEffect())); @@ -89,7 +90,7 @@ class TorporOrbEffect extends ContinuousRuleModifyingEffectImpl { if (ability != null && ability.getAbilityType() == AbilityType.TRIGGERED) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); if (permanent != null && permanent.isCreature()) { - return true; + return (((TriggeredAbility) ability).checkTrigger(event, game)); } } return false; @@ -97,10 +98,15 @@ class TorporOrbEffect extends ContinuousRuleModifyingEffectImpl { @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(event.getSourceId()); + MageObject enteringObject = game.getObject(event.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (mageObject != null && sourceObject != null) { - return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger"; + Ability ability = (Ability) getValue("targetAbility"); + if (enteringObject != null && sourceObject != null && ability != null) { + MageObject abilitObject = game.getObject(ability.getSourceId()); + if (abilitObject != null) { + return sourceObject.getLogName() + " prevented ability of " + abilitObject.getLogName() + + " to trigger for " + enteringObject.getLogName() + " entering the battlefield."; + } } return null; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java index c8694f3540..36b5edb530 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java @@ -41,7 +41,8 @@ public class TorporOrbTest extends CardTestPlayerBase { */ @Test public void testPitTweller() { - addCard(Zone.BATTLEFIELD, playerB, "Torpor Orb"); + // Creatures entering the battlefield don't cause abilities to trigger. + addCard(Zone.BATTLEFIELD, playerB, "Hushwing Gryff"); addCard(Zone.BATTLEFIELD, playerB, "Treacherous Pit-Dweller"); // 4/3 addCard(Zone.HAND, playerA, "Lightning Bolt"); diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index 638079c1e3..93ca77a32c 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -50,6 +50,17 @@ public interface TriggeredAbility extends Ability { */ boolean checkEventType(GameEvent event, Game game); + /** + * This method checks if the event has to trigger the ability. It's + * important to do nothing unique within this method, that can't be done + * multiple times. Because some abilities call this to check if an ability + * is relevant (e.g. Torpor Orb), so the method is calle dmultiple times for + * the same event. + * + * @param event + * @param game + * @return + */ boolean checkTrigger(GameEvent event, Game game); boolean checkInterveningIfClause(Game game); From fe3a023c36e5e925ee257184ac0f43fca2b381f6 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 26 Feb 2018 11:18:17 -0600 Subject: [PATCH 080/127] - Fixed Djinn Illuminatus #4552 --- Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java b/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java index 94c30a7feb..4eb5d4f1f0 100644 --- a/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java +++ b/Mage.Sets/src/mage/cards/d/DjinnIlluminatus.java @@ -108,7 +108,8 @@ class DjinnIlluminatusGainReplicateEffect extends ContinuousEffectImpl { if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId()) - && djinn.getControllerId().equals(source.getControllerId())) { // verify that the controller of the djinn cast that spell + && djinn.getControllerId().equals(source.getControllerId()) // verify that the controller of the djinn cast that spell + && !stackObject.getManaCost().isEmpty()) { //handle cases like Ancestral Vision Spell spell = (Spell) stackObject; if (filter.match(stackObject, game)) { ReplicateAbility replicateAbility = replicateAbilities.computeIfAbsent(spell.getId(), k -> new ReplicateAbility(spell.getCard(), spell.getSpellAbility().getManaCosts().getText())); From 6aeb3c7c3a9e523384fc92c160842a9a1948508a Mon Sep 17 00:00:00 2001 From: Shaun Hannah Date: Mon, 26 Feb 2018 19:23:00 -0500 Subject: [PATCH 081/127] [WIP] Consumable JSON game logs As discussed in https://github.com/magefree/mage/issues/4515 This exposes a JSON log of game interactions that can be analyzed. This is just a first pass, to get up to speed with how the messaging works. It'd like to trim down the messages much further so they don't include redundant information in each message. Also gson supports much more advances serialization options; such as using the @Expose annotation. We should probably use that, but I ran into some issues (I'm not a java developer, so still learning). TODO: These currently only exist on the client side; ideally we'd submit this logs back up to a central server after the games completion; thinking this could be simple via an S3 file drop, and a Lambda function to process and expose the logs; maybe via a kafka stream. Examples of log messages are below: ``` { "gameId": "2cede8c5-ff8e-4f8c-b9ac-66af53c0a254", "sessionId": "5c4o149-678483-je42ycva-1-je42ycw2-4", "type": "GAME_SELECT", "value": { "gameView": { "priorityTime": 3000, "players": [ { "name": "computer", "life": 20, "counters": {} }, { "name": "hooptie", "life": 20, "counters": {} } ], "hand": { "425d774f-ee0c-4a9b-8516-c98f886943f0": { "name": "Springleaf Drum", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "425d774f-ee0c-4a9b-8516-c98f886943f0" }, "dd41bb4b-7fc3-4a3c-a69c-d18e281a1bff": { "name": "Blade of the Bloodchief", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "dd41bb4b-7fc3-4a3c-a69c-d18e281a1bff" }, "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806": { "name": "Ornithopter", "power": "0", "toughness": "2", "loyalty": "", "manaCost": [ "{0}" ], "convertedManaCost": 0, "type": 0, "paid": false, "id": "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" }, "a2f9cc13-e71a-4c9c-96aa-5424ea1a6b64": { "name": "Springleaf Drum", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "a2f9cc13-e71a-4c9c-96aa-5424ea1a6b64" }, "91239f4f-9003-4c48-8ca1-4c318f892489": { "name": "Cranial Plating", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{2}" ], "convertedManaCost": 2, "type": 0, "paid": false, "id": "91239f4f-9003-4c48-8ca1-4c318f892489" }, "feb268d8-0535-4a9c-8915-83dd92a08c4c": { "name": "Arcbound Ravager", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{2}" ], "convertedManaCost": 2, "type": 0, "paid": false, "id": "feb268d8-0535-4a9c-8915-83dd92a08c4c" } }, "canPlayInHand": [ "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" ], "stack": {}, "combat": [], "phase": "PRECOMBAT_MAIN", "step": "PRECOMBAT_MAIN" }, "message": "Play spells and abilities.", "options": { "queryType": "SELECT" } } } { "gameId": "2cede8c5-ff8e-4f8c-b9ac-66af53c0a254", "sessionId": "5c4o149-678483-je42ycva-1-je42ycw2-4", "type": "SEND_PLAYER_UUID", "value": "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" } { "gameId": "2cede8c5-ff8e-4f8c-b9ac-66af53c0a254", "sessionId": "5c4o149-678483-je42ycva-1-je42ycw2-4", "type": "GAME_CHOOSE_PILE", "value": { "choices": { "1ecf8671-be4c-4060-a76b-af614235a5b7": "Cast Ornithopter" } } } { "gameId": "2cede8c5-ff8e-4f8c-b9ac-66af53c0a254", "sessionId": "5c4o149-3v2cj2-je43178o-1-je43179f-4", "type": "GAME_INIT", "value": { "priorityTime": 3000, "players": [ { "name": "computer", "life": 20, "counters": {} }, { "name": "hooptie", "life": 20, "counters": {} } ], "hand": { "425d774f-ee0c-4a9b-8516-c98f886943f0": { "name": "Springleaf Drum", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "425d774f-ee0c-4a9b-8516-c98f886943f0" }, "dd41bb4b-7fc3-4a3c-a69c-d18e281a1bff": { "name": "Blade of the Bloodchief", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "dd41bb4b-7fc3-4a3c-a69c-d18e281a1bff" }, "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806": { "name": "Ornithopter", "power": "0", "toughness": "2", "loyalty": "", "manaCost": [ "{0}" ], "convertedManaCost": 0, "type": 0, "paid": false, "id": "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" }, "a2f9cc13-e71a-4c9c-96aa-5424ea1a6b64": { "name": "Springleaf Drum", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "a2f9cc13-e71a-4c9c-96aa-5424ea1a6b64" }, "91239f4f-9003-4c48-8ca1-4c318f892489": { "name": "Cranial Plating", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{2}" ], "convertedManaCost": 2, "type": 0, "paid": false, "id": "91239f4f-9003-4c48-8ca1-4c318f892489" }, "feb268d8-0535-4a9c-8915-83dd92a08c4c": { "name": "Arcbound Ravager", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{2}" ], "convertedManaCost": 2, "type": 0, "paid": false, "id": "feb268d8-0535-4a9c-8915-83dd92a08c4c" } }, "canPlayInHand": [ "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" ], "stack": {}, "combat": [], "phase": "PRECOMBAT_MAIN", "step": "PRECOMBAT_MAIN" } } { "gameId": "2cede8c5-ff8e-4f8c-b9ac-66af53c0a254", "sessionId": "5c4o149-3v2cj2-je43178o-1-je43179f-4", "type": "GAME_SELECT", "value": { "gameView": { "priorityTime": 3000, "players": [ { "name": "computer", "life": 20, "counters": {} }, { "name": "hooptie", "life": 20, "counters": {} } ], "hand": { "425d774f-ee0c-4a9b-8516-c98f886943f0": { "name": "Springleaf Drum", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "425d774f-ee0c-4a9b-8516-c98f886943f0" }, "dd41bb4b-7fc3-4a3c-a69c-d18e281a1bff": { "name": "Blade of the Bloodchief", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "dd41bb4b-7fc3-4a3c-a69c-d18e281a1bff" }, "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806": { "name": "Ornithopter", "power": "0", "toughness": "2", "loyalty": "", "manaCost": [ "{0}" ], "convertedManaCost": 0, "type": 0, "paid": false, "id": "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" }, "a2f9cc13-e71a-4c9c-96aa-5424ea1a6b64": { "name": "Springleaf Drum", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{1}" ], "convertedManaCost": 1, "type": 0, "paid": false, "id": "a2f9cc13-e71a-4c9c-96aa-5424ea1a6b64" }, "91239f4f-9003-4c48-8ca1-4c318f892489": { "name": "Cranial Plating", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{2}" ], "convertedManaCost": 2, "type": 0, "paid": false, "id": "91239f4f-9003-4c48-8ca1-4c318f892489" }, "feb268d8-0535-4a9c-8915-83dd92a08c4c": { "name": "Arcbound Ravager", "power": "0", "toughness": "0", "loyalty": "", "manaCost": [ "{2}" ], "convertedManaCost": 2, "type": 0, "paid": false, "id": "feb268d8-0535-4a9c-8915-83dd92a08c4c" } }, "canPlayInHand": [ "06eb0a6c-1e70-4dc0-bd1c-93b6ea444806" ], "stack": {}, "combat": [], "phase": "PRECOMBAT_MAIN", "step": "PRECOMBAT_MAIN" }, "message": "Play spells and abilities.", "options": { "queryType": "SELECT" } } } { "gameId": "2cede8c5-ff8e-4f8c-b9ac-66af53c0a254", "sessionId": "5c4o149-678483-je42ycva-1-je42ycw2-4", "type": "SEND_PLAYER_UUID", "value": "1ecf8671-be4c-4060-a76b-af614235a5b7" } ``` --- .../client/remote/CallbackClientImpl.java | 36 ++++- Mage.Common/pom.xml | 7 +- .../src/main/java/mage/remote/ActionData.java | 133 ++++++++++++++++++ .../src/main/java/mage/remote/Session.java | 3 +- .../main/java/mage/remote/SessionImpl.java | 42 ++++++ .../java/mage/view/GameClientMessage.java | 10 ++ .../src/main/java/mage/view/GameView.java | 13 +- 7 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 Mage.Common/src/main/java/mage/remote/ActionData.java diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 386c2156a6..253186a482 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -31,6 +31,9 @@ import java.awt.event.KeyEvent; import java.util.List; import java.util.UUID; import javax.swing.*; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import mage.cards.decks.Deck; import mage.client.MageFrame; import mage.client.SessionHandler; @@ -47,6 +50,9 @@ import mage.client.util.audio.AudioManager; import mage.client.util.object.SaveObjectUtil; import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.ClientCallback; +import mage.remote.ActionData; +import mage.remote.Session; +import mage.remote.SessionImpl; import mage.utils.CompressUtil; import mage.view.*; import mage.view.ChatMessage.MessageType; @@ -102,7 +108,6 @@ public class CallbackClientImpl implements CallbackClient { break; case CHATMESSAGE: { ChatMessage message = (ChatMessage) callback.getData(); - // Drop messages from ignored users if (message.getUsername() != null && IgnoreList.IGNORED_MESSAGE_TYPES.contains(message.getMessageType())) { final String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); @@ -183,6 +188,7 @@ public class CallbackClientImpl implements CallbackClient { case GAME_INIT: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_INIT", callback.getObjectId(), (GameView) callback.getData()); panel.init((GameView) callback.getData()); } break; @@ -190,6 +196,7 @@ public class CallbackClientImpl implements CallbackClient { case GAME_OVER: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); panel.endMessage((String) callback.getData(), callback.getMessageId()); } break; @@ -201,6 +208,7 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_ASK", callback.getObjectId(), message); panel.ask(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions()); } break; @@ -208,8 +216,10 @@ public class CallbackClientImpl implements CallbackClient { case GAME_TARGET: // e.g. Pick triggered ability { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_TARGET", callback.getObjectId(), message); panel.pickTarget(message.getMessage(), message.getCardsView(), message.getGameView(), message.getTargets(), message.isFlag(), message.getOptions(), callback.getMessageId()); } @@ -217,8 +227,10 @@ public class CallbackClientImpl implements CallbackClient { } case GAME_SELECT: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_SELECT", callback.getObjectId(), message); panel.select(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions()); } break; @@ -226,6 +238,7 @@ public class CallbackClientImpl implements CallbackClient { case GAME_CHOOSE_ABILITY: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), callback.getData()); panel.pickAbility((AbilityPickerView) callback.getData()); } break; @@ -234,15 +247,18 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), message); panel.pickPile(message.getMessage(), message.getPile1(), message.getPile2()); } break; } case GAME_CHOOSE_CHOICE: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_CHOOSE_CHOICE", callback.getObjectId(), message); panel.getChoice(message.getChoice(), callback.getObjectId()); } break; @@ -251,34 +267,44 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_PLAY_MANA", callback.getObjectId(), message); panel.playMana(message.getMessage(), message.getGameView(), message.getOptions(), callback.getMessageId()); } break; } case GAME_PLAY_XMANA: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_PLAY_XMANA", callback.getObjectId(), message); panel.playXMana(message.getMessage(), message.getGameView(), callback.getMessageId()); } break; } case GAME_GET_AMOUNT: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_GET_AMOUNT", callback.getObjectId(), message); + panel.getAmount(message.getMin(), message.getMax(), message.getMessage()); } break; } case GAME_UPDATE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); + if (panel != null) { + appendJsonEvent("GAME_UPDATE", callback.getObjectId(), callback.getData()); + panel.updateGame((GameView) callback.getData()); } break; } case END_GAME_INFO: + appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); MageFrame.getInstance().showGameEndDialog((GameEndView) callback.getData()); break; case SHOW_USERMESSAGE: @@ -293,6 +319,7 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_INFORM", callback.getObjectId(), message); panel.inform(message.getMessage(), message.getGameView(), callback.getMessageId()); } } @@ -375,7 +402,12 @@ public class CallbackClientImpl implements CallbackClient { } }); } - + private void appendJsonEvent(String name, UUID gameId, Object value) { + Session session = SessionHandler.getSession(); + ActionData actionData = new ActionData(name, gameId); + actionData.value = value; + session.appendJsonLog(actionData); + } private void createChatStartMessage(ChatPanelBasic chatPanel) { chatPanel.setStartMessageDone(true); ChatPanelBasic usedPanel = chatPanel; diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index 27914c196d..17075941d0 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -25,6 +25,7 @@ jspf-core 0.9.1 + org.jboss.remoting jboss-remoting @@ -50,7 +51,11 @@ trove 1.0.2 - + + com.google.code.gson + gson + 2.8.2 + diff --git a/Mage.Common/src/main/java/mage/remote/ActionData.java b/Mage.Common/src/main/java/mage/remote/ActionData.java new file mode 100644 index 0000000000..4fdd32ef06 --- /dev/null +++ b/Mage.Common/src/main/java/mage/remote/ActionData.java @@ -0,0 +1,133 @@ +/* + * Copyright 2018 nanarpuss_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.remote; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import mage.remote.interfaces.*; + +import java.util.UUID; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; + +public class ActionData { + public UUID gameId; + public String sessionId; + public String type; + public Object value; + public String message; + + public String toJson() { + GsonBuilder gsonBuilder = new GsonBuilder(); + Gson gson = gsonBuilder.setExclusionStrategies(new CustomExclusionStrategy()).create(); + + return gson.toJson(this); + } + + public ActionData(String type, UUID gameId, String sessionId) { + this.type = type; + this.sessionId = sessionId; + this.gameId = gameId; + } + + public ActionData(String type, UUID gameId) { + this.type = type; + this.gameId = gameId; + } + + public class CustomExclusionStrategy implements ExclusionStrategy { + // FIXME: Very crude way of whitelisting, as it applies to all levels of the JSON tree. + private final java.util.Set KEEP = new java.util.HashSet( + java.util.Arrays.asList( + new String[]{ + "id", + "choice", + "damage", + "abilityType", + "ability", + "abilities", + "method", + "data", + "options", + "life", + "players", + "zone", + "step", + "phase", + "attackers", + "blockers", + "tapped", + "damage", + "combat", + "paid", + "hand", + "stack", + "convertedManaCost", + "gameId", + "canPlayInHand", + "gameView", + "sessionId", + "power", + "choices", + "targets", + "loyalty", + "toughness", + "power", + "type", + "priorityTime", + "manaCost", + "value", + "message", + "cardsView", + "name", + "count", + "counters", + "battlefield", + "parentId" + })); + + public CustomExclusionStrategy() {} + + // This method is called for all fields. if the method returns true the + // field is excluded from serialization + @Override + public boolean shouldSkipField(FieldAttributes f) { + String name = f.getName(); + return !KEEP.contains(name); + } + + // This method is called for all classes. If the method returns true the + // class is excluded. + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + } +} diff --git a/Mage.Common/src/main/java/mage/remote/Session.java b/Mage.Common/src/main/java/mage/remote/Session.java index 8cb062c63e..67022d59e8 100644 --- a/Mage.Common/src/main/java/mage/remote/Session.java +++ b/Mage.Common/src/main/java/mage/remote/Session.java @@ -38,6 +38,7 @@ import mage.remote.interfaces.PlayerActions; import mage.remote.interfaces.Replays; import mage.remote.interfaces.ServerState; import mage.remote.interfaces.Testable; +import mage.remote.ActionData; /** * Extracted interface for SessionImpl class. @@ -45,5 +46,5 @@ import mage.remote.interfaces.Testable; * @author noxx */ public interface Session extends ClientData, Connect, GamePlay, GameTypes, ServerState, ChatSession, Feedback, PlayerActions, Replays, Testable { - + public void appendJsonLog(ActionData actionData); } diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 1f7bca2c0c..907de603b0 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -32,6 +32,11 @@ import java.lang.reflect.UndeclaredThrowableException; import java.net.*; import java.util.*; import java.util.concurrent.TimeUnit; +import java.io.BufferedWriter; +import java.io.PrintWriter; +import java.io.FileWriter; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import mage.MageException; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; @@ -50,6 +55,7 @@ import mage.interfaces.callback.ClientCallback; import mage.players.PlayerType; import mage.players.net.UserData; import mage.utils.CompressUtil; +import mage.remote.ActionData; import mage.view.*; import org.apache.log4j.Logger; import org.jboss.remoting.*; @@ -798,6 +804,9 @@ public class SessionImpl implements Session { public boolean sendPlayerUUID(UUID gameId, UUID data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_UUID", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); server.sendPlayerUUID(gameId, sessionId, data); return true; } @@ -813,6 +822,10 @@ public class SessionImpl implements Session { public boolean sendPlayerBoolean(UUID gameId, boolean data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_BOOLEAN", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerBoolean(gameId, sessionId, data); return true; } @@ -828,6 +841,10 @@ public class SessionImpl implements Session { public boolean sendPlayerInteger(UUID gameId, int data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_INTEGER", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerInteger(gameId, sessionId, data); return true; } @@ -843,6 +860,10 @@ public class SessionImpl implements Session { public boolean sendPlayerString(UUID gameId, String data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_STRING", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerString(gameId, sessionId, data); return true; } @@ -858,6 +879,9 @@ public class SessionImpl implements Session { public boolean sendPlayerManaType(UUID gameId, UUID playerId, ManaType data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_MANA_TYPE", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); server.sendPlayerManaType(gameId, playerId, sessionId, data); return true; } @@ -869,6 +893,19 @@ public class SessionImpl implements Session { return false; } + @Override + public void appendJsonLog(ActionData actionData) { + actionData.sessionId = getSessionId(); + + String logFileName = "game-" + actionData.gameId + ".json"; + System.out.println("Logging to " + logFileName); + try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logFileName, true)))) { + out.println(actionData.toJson()); + } catch (IOException e) { + System.err.println(e); + } + } + @Override public DraftPickView sendCardPick(UUID draftId, UUID cardId, Set hiddenCards) { try { @@ -1274,6 +1311,11 @@ public class SessionImpl implements Session { public boolean sendPlayerAction(PlayerAction passPriorityAction, UUID gameId, Object data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_ACTION", gameId, getSessionId()); + + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerAction(passPriorityAction, gameId, sessionId, data); return true; } diff --git a/Mage.Common/src/main/java/mage/view/GameClientMessage.java b/Mage.Common/src/main/java/mage/view/GameClientMessage.java index 33af5e25e9..8e8a8b93aa 100644 --- a/Mage.Common/src/main/java/mage/view/GameClientMessage.java +++ b/Mage.Common/src/main/java/mage/view/GameClientMessage.java @@ -32,6 +32,9 @@ import java.io.Serializable; import java.util.Map; import java.util.Set; import java.util.UUID; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import mage.choices.Choice; /** @@ -155,4 +158,11 @@ public class GameClientMessage implements Serializable { return choice; } + public String toJson() { + Gson gson = new GsonBuilder() + .excludeFieldsWithoutExposeAnnotation() + .create(); + return gson.toJson(this); + } + } diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 2e16415ad6..b354503c56 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -28,11 +28,17 @@ package mage.view; import java.io.Serializable; +import java.io.BufferedWriter; +import java.io.PrintWriter; +import java.io.FileWriter; +import java.io.IOException; + import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; + import mage.MageObject; import mage.abilities.costs.Cost; import mage.cards.Card; @@ -54,6 +60,8 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.watchers.common.CastSpellLastTurnWatcher; import org.apache.log4j.Logger; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; /** * @@ -64,7 +72,6 @@ public class GameView implements Serializable { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(GameView.class); - private final int priorityTime; private final List players = new ArrayList<>(); private CardsView hand; @@ -351,4 +358,8 @@ public class GameView implements Serializable { return rollbackTurnsAllowed; } + public String toJson() { + Gson gson = new GsonBuilder().create(); + return gson.toJson(this); + } } From 4b331355defb6bc255a6fa4347574706ec0385d8 Mon Sep 17 00:00:00 2001 From: Shaun Hannah Date: Mon, 26 Feb 2018 19:24:00 -0500 Subject: [PATCH 082/127] [WIP] SPIKE - gson @Expose --- .../src/main/java/mage/remote/ActionData.java | 6 ++++++ Mage.Common/src/main/java/mage/view/CardView.java | 9 ++++++++- .../main/java/mage/view/GameClientMessage.java | 15 ++++++++++++++- .../src/main/java/mage/view/SimpleCardView.java | 3 +++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/main/java/mage/remote/ActionData.java b/Mage.Common/src/main/java/mage/remote/ActionData.java index 4fdd32ef06..d173535b14 100644 --- a/Mage.Common/src/main/java/mage/remote/ActionData.java +++ b/Mage.Common/src/main/java/mage/remote/ActionData.java @@ -34,14 +34,20 @@ import mage.remote.interfaces.*; import java.util.UUID; +import com.google.gson.annotations.Expose; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; public class ActionData { + @Expose public UUID gameId; + @Expose public String sessionId; + @Expose public String type; + @Expose public Object value; + @Expose public String message; public String toJson() { diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index d9154f7418..b2ed194eb8 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -53,6 +53,8 @@ import mage.target.Target; import mage.target.Targets; import mage.util.SubTypeList; +import com.google.gson.annotations.Expose; + /** * @author BetaSteward_at_googlemail.com */ @@ -61,11 +63,17 @@ public class CardView extends SimpleCardView { private static final long serialVersionUID = 1L; protected UUID parentId; + @Expose protected String name; + @Expose protected String displayName; + @Expose protected List rules; + @Expose protected String power; + @Expose protected String toughness; + @Expose protected String loyalty = ""; protected String startingLoyalty; protected EnumSet cardTypes; @@ -110,7 +118,6 @@ public class CardView extends SimpleCardView { protected ArtRect artRect = ArtRect.NORMAL; protected List targets; - protected UUID pairedCard; protected List bandedCards; protected boolean paid; diff --git a/Mage.Common/src/main/java/mage/view/GameClientMessage.java b/Mage.Common/src/main/java/mage/view/GameClientMessage.java index 8e8a8b93aa..1774c766f9 100644 --- a/Mage.Common/src/main/java/mage/view/GameClientMessage.java +++ b/Mage.Common/src/main/java/mage/view/GameClientMessage.java @@ -35,6 +35,8 @@ import java.util.UUID; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; + import mage.choices.Choice; /** @@ -42,18 +44,29 @@ import mage.choices.Choice; * @author BetaSteward_at_googlemail.com */ public class GameClientMessage implements Serializable { + @Expose private static final long serialVersionUID = 1L; - + @Expose private GameView gameView; + @Expose private CardsView cardsView; + @Expose private CardsView cardsView2; + @Expose private String message; + @Expose private boolean flag; + @Expose private String[] strings; + @Expose private Set targets; + @Expose private int min; + @Expose private int max; + @Expose private Map options; + @Expose private Choice choice; public GameClientMessage(GameView gameView) { diff --git a/Mage.Common/src/main/java/mage/view/SimpleCardView.java b/Mage.Common/src/main/java/mage/view/SimpleCardView.java index 709e45ad83..a137f0ca0e 100644 --- a/Mage.Common/src/main/java/mage/view/SimpleCardView.java +++ b/Mage.Common/src/main/java/mage/view/SimpleCardView.java @@ -28,6 +28,8 @@ package mage.view; +import com.google.gson.annotations.Expose; + import java.io.Serializable; import java.util.UUID; @@ -36,6 +38,7 @@ import java.util.UUID; * @author BetaSteward_at_googlemail.com */ public class SimpleCardView implements Serializable { + @Expose protected UUID id; protected String expansionSetCode; protected String tokenSetCode; From 7f94c724af4663ce2b732f47b99f377002310498 Mon Sep 17 00:00:00 2001 From: Shaun Hannah Date: Mon, 26 Feb 2018 19:25:00 -0500 Subject: [PATCH 083/127] Git ignore the game-.json logs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 92368d784d..2b2ae770ee 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,4 @@ client_secrets.json dependency-reduced-pom.xml mage-bundle +/Mage.Client/game-*.json From 7c7f88ab3c41b0c5eae330a36f99fa2dde48347b Mon Sep 17 00:00:00 2001 From: Shaun Hannah Date: Tue, 27 Feb 2018 05:17:20 -0500 Subject: [PATCH 084/127] [WIP] S3 Upload of JSON log --- Mage.Client/pom.xml | 6 +++ .../client/remote/CallbackClientImpl.java | 13 ++++- .../java/mage/client/remote/S3Uploader.java | 47 +++++++++++++++++++ Mage.Server/pom.xml | 6 +++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/remote/S3Uploader.java diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index db453f4cd3..40fcfe1b91 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -15,6 +15,7 @@ Mage Client + org.mage mage @@ -68,6 +69,11 @@ jetlang 0.2.9 + + com.amazonaws + aws-java-sdk-s3 + 1.11.286 + com.jgoodies forms diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 253186a482..f16b0bee0e 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -35,6 +35,7 @@ import javax.swing.*; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import mage.cards.decks.Deck; +import mage.client.remote.S3Uploader; import mage.client.MageFrame; import mage.client.SessionHandler; import mage.client.chat.ChatPanelBasic; @@ -194,9 +195,16 @@ public class CallbackClientImpl implements CallbackClient { break; } case GAME_OVER: { + GamePanel panel = MageFrame.getGame(callback.getObjectId()); + if (panel != null) { appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); + ActionData actionData = appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); + String logFileName = "game-" + actionData.gameId + ".json"; + + S3Uploader.upload(logFileName, actionData.gameId.toString()); + panel.endMessage((String) callback.getData(), callback.getMessageId()); } break; @@ -304,8 +312,8 @@ public class CallbackClientImpl implements CallbackClient { break; } case END_GAME_INFO: - appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); MageFrame.getInstance().showGameEndDialog((GameEndView) callback.getData()); + break; case SHOW_USERMESSAGE: List messageData = (List) callback.getData(); @@ -402,11 +410,12 @@ public class CallbackClientImpl implements CallbackClient { } }); } - private void appendJsonEvent(String name, UUID gameId, Object value) { + private ActionData appendJsonEvent(String name, UUID gameId, Object value) { Session session = SessionHandler.getSession(); ActionData actionData = new ActionData(name, gameId); actionData.value = value; session.appendJsonLog(actionData); + return actionData; } private void createChatStartMessage(ChatPanelBasic chatPanel) { chatPanel.setStartMessageDone(true); diff --git a/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java b/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java new file mode 100644 index 0000000000..21e9504f41 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java @@ -0,0 +1,47 @@ +package mage.client.remote; + +import java.io.File; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.transfer.TransferManager; +import com.amazonaws.services.s3.transfer.Upload; +import org.apache.log4j.Logger; + +import javax.xml.crypto.Data; + + +public class S3Uploader { + private static final Logger logger = Logger.getLogger(S3Uploader.class); + + public static Boolean upload(String filePath, String keyName) throws Exception { + String existingBucketName = System.getenv("S3_BUCKET") != null ? System.getenv("S3_BUCKET") + : "xmage-game-logs-dev"; + + String accessKeyId = System.getenv("AWS_ACCESS_ID"); + String secretKeyId = System.getenv("AWS_SECRET_KEY"); + + if(accessKeyId == "" || secretKeyId == "" || existingBucketName == "") { + logger.info("Aborting json log sync."); + return false; + } + + String path = new File("./" + filePath).getCanonicalPath(); + logger.info("Syncing " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId); + + BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKeyId, secretKeyId); + TransferManager tm = new TransferManager(awsCreds); + Upload upload = tm.upload(existingBucketName, "/game/" + keyName + ".json", new File(path)); + + try { + upload.waitForUploadResult(); + logger.info("Sync Complete For " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId); + new File(path); + return true; + } catch (AmazonClientException amazonClientException) { + System.out.println("Unable to upload file, upload was aborted."); + amazonClientException.printStackTrace(); + return false; + } + } +} diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index f756f0f7c1..20899230cc 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -76,6 +76,12 @@ ${project.version} runtime + + org.apache.commons + commons-compress + 1.16.1 + + ${project.groupId} mage-game-commanderfreeforall From a28e273dcc42368a50ac2d7e577fdf01af7981a9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Wed, 28 Feb 2018 00:02:14 +1100 Subject: [PATCH 085/127] Modifying zendikar curved box to use a path instead. (Will be easier to then draw BFZ lands similarly). --- .../mage/card/arcane/ModernCardRenderer.java | 98 ++++++++----------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 3f04098a7b..51e7957759 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -7,7 +7,7 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; -import java.awt.geom.QuadCurve2D; +import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; @@ -550,25 +550,7 @@ public class ModernCardRenderer extends CardRenderer { drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, - cardWidth, cardHeight); - - QuadCurve2D q = new QuadCurve2D.Float(); - q.setCurve(x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2); - g.setColor(boxColor); - g.setPaint(borderPaint); - g.draw(q); - q.setCurve(x + topxdelta - 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta - 1, y2); - g.draw(q); - q.setCurve(x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2); - g.draw(q); - q.setCurve(x + contentWidth - topxdelta + 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta + 1, y2); - g.draw(q); - - g.setColor(Color.BLACK); - q.setCurve(x + topxdelta + 1, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta + 1, y2 - 1); - g.draw(q); - q.setCurve(x + contentWidth - topxdelta - 1, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta - 1, y2 - 1); - g.draw(q); + boxColor, borderPaint); drawRulesText(g, textboxKeywords, textboxRules, x, y, @@ -581,61 +563,57 @@ public class ModernCardRenderer extends CardRenderer { public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int ctrlx, int ctrly, int w, int h, int x2, int y2, int ctrlx2, int ctrly2, int w2, int h2, - int cardWidth, int cardHeight) { + Color boxColor, Paint paint) { BufferedImage artToUse = faceArtImage; boolean hadToUseFullArt = false; if (faceArtImage == null) { if (artImage == null) { return; - } + } hadToUseFullArt = true; artToUse = artImage; } int srcW = artToUse.getWidth(); int srcH = artToUse.getHeight(); - + if (hadToUseFullArt) { // Get a box based on the standard scan from gatherer. // Width = 185/223 pixels (centered) // Height = 220/310, 38 pixels from top - int subx = 19 * srcW / 223; - int suby = 38 * srcH / 310; - artToUse = artImage.getSubimage(subx, suby, 185*srcW / 223, 220*srcH / 310); + int subx = 19 * srcW / 223; + int suby = 38 * srcH / 310; + artToUse = artImage.getSubimage(subx, suby, 185 * srcW / 223, 220 * srcH / 310); } - Rectangle2D rect = new Rectangle2D.Float(); - rect.setRect(0, 0, srcW, srcH); - g2.clip(rect); + Path2D.Double curve = new Path2D.Double(); + curve.moveTo(x, y); + curve.quadTo(ctrlx, ctrly, w, h); + curve.lineTo(w2, h); + curve.quadTo(ctrlx2, ctrly2, x2, y); + curve.lineTo(x, y); - QuadCurve2D q = new QuadCurve2D.Float(); - q.setCurve(x, y, ctrlx, ctrly, w, h); - g2.setClip(null); - g2.clip(q); - //g2.drawImage(faceArtImage, x, y, (x2-x), (h2-y), null); - Rectangle2D r = q.getBounds2D(); + Path2D.Double innercurve = new Path2D.Double(); + innercurve.moveTo(x + 1, y + 1); + innercurve.quadTo(ctrlx + 1, ctrly + 1, w + 1, h - 1); + innercurve.lineTo(w2 - 1, h - 1); + innercurve.quadTo(ctrlx2 - 1, ctrly2 - 1, x2 - 1, y + 1); + innercurve.lineTo(x + 1, y + 1); + + Rectangle2D r = curve.getBounds2D(); int minX = (int) r.getX(); + + g2.setClip(innercurve); g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); - QuadCurve2D q2 = new QuadCurve2D.Float(); - q2.setCurve(x2, y2, ctrlx2, ctrly2, w2, h2); g2.setClip(null); - g2.clip(q2); - g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + g2.setColor(CardRendererUtils.abitdarker(boxColor)); + g2.setPaint(paint); + g2.draw(curve); - Polygon p = new Polygon(); - //x = 10; - //y = 0; - p.addPoint(x, y); - p.addPoint(x2, y); - p.addPoint(w2, h2); - p.addPoint(w, h2); - p.addPoint(x, y); - g2.setClip(null); - g2.clip(p); - - g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); - g2.setClip(null); + g2.setColor(Color.black); + //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); + g2.draw(innercurve); } // Draw the name line @@ -919,14 +897,16 @@ public class ModernCardRenderer extends CardRenderer { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands - if (allRules.size() == 1) { - drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); - return; - } else { - if (allRules.size() > 1) { - drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + { + if (allRules.size() == 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } else { + if (allRules.size() > 1) { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + } + return; } - return; } } From 596ca5be867db81f46082827fe46c2dcb6de27de Mon Sep 17 00:00:00 2001 From: spjspj Date: Wed, 28 Feb 2018 01:25:29 +1100 Subject: [PATCH 086/127] Adding in way to render BFZ style full art lands (BFZ and HOU full art lands) Things still to do: 1) Get everyone to download the full art Face images ... 2) Add in the collector number to the Face images (/FACE/ZEN/Island..jpg for example) 4) Maybe even add in an UST (Unstable) way of rendering lands (with the swoosh of opaque full art down the bottom). --- .../mage/card/arcane/ModernCardRenderer.java | 120 ++++++++++++++---- 1 file changed, 95 insertions(+), 25 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 51e7957759..3cd5cbe0bd 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -313,7 +313,7 @@ public class ModernCardRenderer extends CardRenderer { Rectangle2D rect; if (useInventionFrame()) { rect = new Rectangle2D.Float(0, 0, 1, 1); - } else if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + } else if (isZendikarFullArtLand()) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .84f); } else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f); @@ -332,6 +332,10 @@ public class ModernCardRenderer extends CardRenderer { return TYPE_LINE_Y_FRAC; } } + + private boolean isZendikarFullArtLand() { + return cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC; + } protected boolean isSourceArtFullArt() { int color = artImage.getRGB(0, artImage.getHeight() / 2); @@ -355,7 +359,7 @@ public class ModernCardRenderer extends CardRenderer { if (artImage != null && !cardView.isFaceDown()) { boolean useFaceArt = false; - if (faceArtImage != null && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (faceArtImage != null && !isZendikarFullArtLand()) { useFaceArt = true; } @@ -398,16 +402,11 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); - } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + } else if (!isZendikarFullArtLand()) { drawArtIntoRect(g, totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, sourceRect, shouldPreserveAspect); - } else { - /* drawArtIntoRect(g, - totalContentInset + 1, totalContentInset + boxHeight, - contentWidth - 2, typeLineY, - sourceRect, shouldPreserveAspect);*/ } } } @@ -435,7 +434,7 @@ public class ModernCardRenderer extends CardRenderer { g.drawRect( totalContentInset, typeLineY, contentWidth - 1, cardHeight - borderWidth * 3 - typeLineY - 1); - } else if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + } else if (!isZendikarFullArtLand()) { g.drawRect( totalContentInset, totalContentInset, contentWidth - 1, cardHeight - borderWidth * 3 - totalContentInset - 1); @@ -448,7 +447,7 @@ public class ModernCardRenderer extends CardRenderer { g.setPaint(textboxPaint); } - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (!isZendikarFullArtLand()) { g.fillRect( totalContentInset + 1, typeLineY, contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); @@ -462,7 +461,7 @@ public class ModernCardRenderer extends CardRenderer { cardWidth / 16, cardHeight - typeLineY - boxHeight - borderWidth * 3); } - if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && !isZendikarFullArtLand()) { // Draw a shadow highlight at the right edge of the content frame g.setColor(new Color(0, 0, 0, 100)); g.fillRect( @@ -482,7 +481,7 @@ public class ModernCardRenderer extends CardRenderer { contentInset, borderPaint, boxColor); // Draw the type line box - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (!isZendikarFullArtLand()) { CardRendererUtils.drawRoundedBox(g, borderWidth, typeLineY, cardWidth - 2 * borderWidth, boxHeight, @@ -519,7 +518,7 @@ public class ModernCardRenderer extends CardRenderer { contentWidth - nameOffset, boxHeight); // Draw the textbox rules - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (!isZendikarFullArtLand()) { drawRulesText(g, textboxKeywords, textboxRules, totalContentInset + 2, typeLineY + boxHeight + 2, contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); @@ -540,17 +539,31 @@ public class ModernCardRenderer extends CardRenderer { totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, 3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true); - // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... - int x2 = x; - int y2 = y; - int topxdelta = 45 * contentWidth / 1000; - int botxdelta = 58 * contentWidth / 1000; - int ctrlx = 0; - int ctrly = (totalContentInset + y2) / 2; + if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { + // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... + int x2 = x; + int y2 = y; + int topxdelta = 45 * contentWidth / 1000; + int botxdelta = 58 * contentWidth / 1000; + int ctrlx = 0; + int ctrly = (totalContentInset + y2) / 2; - drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, - x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, - boxColor, borderPaint); + drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, + x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, + boxColor, borderPaint); + } else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) { + // Draw curved lines (BFZ land style) + int y2 = y; + int yb = totalContentInset + boxHeight; + int topxdelta = 45 * contentWidth / 1000; + int endydelta = 60 * (totalContentInset + y2) / 265; + int x2 = x + contentWidth; + + // Curve ends at 60 out of 265 + drawBFZCurvedFace(g, image, x, yb, x2, y2, + topxdelta, endydelta, + boxColor, borderPaint); + } drawRulesText(g, textboxKeywords, textboxRules, x, y, @@ -615,6 +628,63 @@ public class ModernCardRenderer extends CardRenderer { //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); g2.draw(innercurve); } + + public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, + int topxdelta, int endydelta, + Color boxColor, Paint paint) { + BufferedImage artToUse = faceArtImage; + boolean hadToUseFullArt = false; + if (faceArtImage == null) { + if (artImage == null) { + return; + } + hadToUseFullArt = true; + artToUse = artImage; + } + int srcW = artToUse.getWidth(); + int srcH = artToUse.getHeight(); + + if (hadToUseFullArt) { + // Get a box based on the standard scan from gatherer. + // Width = 185/223 pixels (centered) + // Height = 220/310, 38 pixels from top + int subx = 19 * srcW / 223; + int suby = 38 * srcH / 310; + artToUse = artImage.getSubimage(subx, suby, 185 * srcW / 223, 220 * srcH / 310); + } + + Path2D.Double curve = new Path2D.Double(); + curve.moveTo(x + topxdelta, y); + curve.quadTo(x, y + endydelta / 2, x, y + endydelta); + curve.lineTo(x, y2); + curve.lineTo(x2, y2); + curve.lineTo(x2, y + endydelta); + curve.quadTo(x2, y + endydelta / 2, x2 - topxdelta, y); + curve.lineTo(x + topxdelta, y); + + Path2D.Double innercurve = new Path2D.Double(); + innercurve.moveTo(x + topxdelta, y+1); + innercurve.quadTo(x+1, y + endydelta / 2, x+1, y + endydelta); + innercurve.lineTo(x+1, y2-1); + innercurve.lineTo(x2-1, y2-1); + innercurve.lineTo(x2-1, y + endydelta); + innercurve.quadTo(x2-1, y + endydelta / 2, x2 - topxdelta, y+1); + innercurve.lineTo(x + topxdelta, y+1); + + Rectangle2D r = curve.getBounds2D(); + int minX = (int) r.getX(); + + g2.setClip(curve); + g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, y2 - y, null); + + g2.setClip(null); + g2.setColor(CardRendererUtils.abitdarker(boxColor)); + g2.setPaint(paint); + g2.draw(curve); + + g2.setColor(Color.black); + g2.draw(innercurve); + } // Draw the name line protected void drawNameLine(Graphics2D g, String baseName, String manaCost, int x, int y, int w, int h) { @@ -892,8 +962,8 @@ public class ModernCardRenderer extends CardRenderer { } // Basic mana draw mana symbol in textbox (for basic lands) - if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { - if (cardView.getFrameStyle() != FrameStyle.ZEN_FULL_ART_BASIC) { + if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || isZendikarFullArtLand()) { + if (!isZendikarFullArtLand()) { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands From 8887cca3876df941fac42d9788a66cffea3c16fa Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 1 Mar 2018 00:32:30 +1100 Subject: [PATCH 087/127] slightly offcenter --- .../mage/card/arcane/CardRendererUtils.java | 17 ++++++------ .../mage/card/arcane/ModernCardRenderer.java | 27 ++++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index f38d9b0f75..3702f176cb 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -51,7 +51,7 @@ public final class CardRendererUtils { // Return the buffered image return bimage; } - + public static Color abitbrighter(Color c) { int r = c.getRed(); int g = c.getGreen(); @@ -107,7 +107,7 @@ public final class CardRendererUtils { g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); } - + public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) { g.setColor(new Color(0, 0, 0, 150)); @@ -117,25 +117,24 @@ public final class CardRendererUtils { g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1); g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3); g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3); - + // The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4 - g.drawOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); - + g.drawOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); + g.drawRect(x + bevel, y, w - 2 * bevel, h - 1); g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3); g.setPaint(fill); - g.setPaint(fill); g.setColor(abitbrighter(g.getColor())); g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); g.setPaint(fill); g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); - + g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); - - g.fillOval(x + w / 2 - h - h/8, y - 3*h/4, 9*h/4, 9*h/4); + + g.fillOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4); } // Get the width of a mana cost rendered with ManaSymbols.draw diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 3cd5cbe0bd..08c74ab324 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -332,7 +332,7 @@ public class ModernCardRenderer extends CardRenderer { return TYPE_LINE_Y_FRAC; } } - + private boolean isZendikarFullArtLand() { return cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC; } @@ -525,7 +525,7 @@ public class ModernCardRenderer extends CardRenderer { } else { int x = totalContentInset; int y = typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset; - int w = contentWidth - 2; + int w = contentWidth; int h = boxHeight - 4; CardRendererUtils.drawZendikarLandBox(g, @@ -558,7 +558,7 @@ public class ModernCardRenderer extends CardRenderer { int topxdelta = 45 * contentWidth / 1000; int endydelta = 60 * (totalContentInset + y2) / 265; int x2 = x + contentWidth; - + // Curve ends at 60 out of 265 drawBFZCurvedFace(g, image, x, yb, x2, y2, topxdelta, endydelta, @@ -628,8 +628,8 @@ public class ModernCardRenderer extends CardRenderer { //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); g2.draw(innercurve); } - - public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, + + public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, int topxdelta, int endydelta, Color boxColor, Paint paint) { BufferedImage artToUse = faceArtImage; @@ -663,13 +663,13 @@ public class ModernCardRenderer extends CardRenderer { curve.lineTo(x + topxdelta, y); Path2D.Double innercurve = new Path2D.Double(); - innercurve.moveTo(x + topxdelta, y+1); - innercurve.quadTo(x+1, y + endydelta / 2, x+1, y + endydelta); - innercurve.lineTo(x+1, y2-1); - innercurve.lineTo(x2-1, y2-1); - innercurve.lineTo(x2-1, y + endydelta); - innercurve.quadTo(x2-1, y + endydelta / 2, x2 - topxdelta, y+1); - innercurve.lineTo(x + topxdelta, y+1); + innercurve.moveTo(x + topxdelta, y + 1); + innercurve.quadTo(x + 1, y + endydelta / 2, x + 1, y + endydelta); + innercurve.lineTo(x + 1, y2 - 1); + innercurve.lineTo(x2 - 1, y2 - 1); + innercurve.lineTo(x2 - 1, y + endydelta); + innercurve.quadTo(x2 - 1, y + endydelta / 2, x2 - topxdelta, y + 1); + innercurve.lineTo(x + topxdelta, y + 1); Rectangle2D r = curve.getBounds2D(); int minX = (int) r.getX(); @@ -969,7 +969,8 @@ public class ModernCardRenderer extends CardRenderer { } else // Big circle in the middle for Zendikar lands { if (allRules.size() == 1) { - drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + // Size of mana symbol = 9/4 * h, 3/4h above line + drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else { if (allRules.size() > 1) { From 8c614a90066724963822ae6e07548f42092c5749 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 1 Mar 2018 00:49:43 +1100 Subject: [PATCH 088/127] slightly offcenter --- .../src/main/java/org/mage/card/arcane/ModernCardRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 08c74ab324..c65b92e931 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -533,7 +533,7 @@ public class ModernCardRenderer extends CardRenderer { contentInset, borderPaint, boxColor); drawTypeLine(g, getCardSuperTypeLine(), - totalContentInset + 2, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + totalContentInset + contentInset, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, contentWidth / 2 - boxHeight, boxHeight - 4, false); drawTypeLine(g, getCardSubTypeLine(), totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, From 8c8b4ce019e67b69363de3097479edf1c8591288 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 28 Feb 2018 17:24:42 +0100 Subject: [PATCH 089/127] * Added a check if life of players is going down during infinite loop check. Is so no Draw check message is shown (fixes #4557). --- .../mage/cards/n/NivMizzetTheFiremind.java | 9 +- Mage.Sets/src/mage/cards/s/StuffyDoll.java | 6 +- .../java/org/mage/test/player/TestPlayer.java | 98 +++++++++++++++---- Mage/src/main/java/mage/game/GameImpl.java | 20 ++++ 4 files changed, 107 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java b/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java index e4b3cc805c..486e8cfd29 100644 --- a/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java +++ b/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java @@ -51,18 +51,23 @@ import mage.target.common.TargetCreatureOrPlayer; public class NivMizzetTheFiremind extends CardImpl { public NivMizzetTheFiremind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}{R}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DRAGON); this.subtype.add(SubType.WIZARD); - this.power = new MageInt(4); this.toughness = new MageInt(4); + + // Flying this.addAbility(FlyingAbility.getInstance()); + + // Whenever you draw a card, Niv-Mizzet, the Firemind deals 1 damage to target creature or player. Ability ability = new DrawCardControllerTriggeredAbility(new DamageTargetEffect(1), false); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); + + // {T}: Draw a card. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/s/StuffyDoll.java b/Mage.Sets/src/mage/cards/s/StuffyDoll.java index 5a7a8986ac..99f01cfe7e 100644 --- a/Mage.Sets/src/mage/cards/s/StuffyDoll.java +++ b/Mage.Sets/src/mage/cards/s/StuffyDoll.java @@ -41,8 +41,8 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -56,7 +56,7 @@ import mage.players.Player; public class StuffyDoll extends CardImpl { public StuffyDoll(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(0); this.toughness = new MageInt(1); @@ -67,7 +67,7 @@ public class StuffyDoll extends CardImpl { this.addAbility(IndestructibleAbility.getInstance()); // Whenever Stuffy Doll is dealt damage, it deals that much damage to the chosen player. this.addAbility(new StuffyDollTriggeredAbility()); - // {tap}: Stuffy Doll deals 1 damage to itself. + // {T}: Stuffy Doll deals 1 damage to itself. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageSelfEffect(1), new TapSourceCost())); } 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 417fc5a46d..41d0c95c6d 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 @@ -740,7 +740,7 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Choice choice, Game game) { if (!choices.isEmpty()) { - if(choice.setChoiceByAnswers(choices, true)){ + if (choice.setChoiceByAnswers(choices, true)) { return true; } } @@ -2167,6 +2167,24 @@ public class TestPlayer implements Player { @Override public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { + switch (ability.getSpellAbilityType()) { + case SPLIT: + case SPLIT_FUSED: + case SPLIT_AFTERMATH: + if (!choices.isEmpty()) { + MageObject object = game.getObject(ability.getSourceId()); + if (object != null) { + LinkedHashMap useableAbilities = computerPlayer.getSpellAbilities(object, game.getState().getZone(object.getId()), game); + for (String choose : choices) { + for (ActivatedAbility actiavtedAbility : useableAbilities.values()) { + if (actiavtedAbility.getRule().startsWith(choose)) { + return (SpellAbility) actiavtedAbility; + } + } + } + } + } + } return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana); } @@ -2176,13 +2194,17 @@ public class TestPlayer implements Player { } @Override - public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { + public boolean choose(Outcome outcome, Target target, + UUID sourceId, Game game + ) { // needed to call here the TestPlayer because it's overwitten return choose(outcome, target, sourceId, game, null); } @Override - public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { + public boolean choose(Outcome outcome, Cards cards, + TargetCard target, Game game + ) { if (!choices.isEmpty()) { for (String choose2 : choices) { // TODO: More targetting to fix @@ -2212,58 +2234,78 @@ public class TestPlayer implements Player { } @Override - public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, + Ability source, Game game + ) { return computerPlayer.chooseTargetAmount(outcome, target, source, game); } @Override - public boolean chooseMulligan(Game game) { + public boolean chooseMulligan(Game game + ) { return computerPlayer.chooseMulligan(game); } @Override - public boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game) { + public boolean choosePile(Outcome outcome, String message, + List pile1, List pile2, + Game game + ) { return computerPlayer.choosePile(outcome, message, pile1, pile2, game); } @Override - public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) { + public boolean playMana(Ability ability, ManaCost unpaid, + String promptText, Game game + ) { groupsForTargetHandling = null; return computerPlayer.playMana(ability, unpaid, promptText, game); } @Override - public UUID chooseAttackerOrder(List attacker, Game game) { + public UUID chooseAttackerOrder(List attacker, Game game + ) { return computerPlayer.chooseAttackerOrder(attacker, game); } @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { + public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, + List blockerOrder, Game game + ) { return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); } @Override - public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { + public void assignDamage(int damage, List targets, + String singleTargetName, UUID sourceId, + Game game + ) { computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); } @Override - public void sideboard(Match match, Deck deck) { + public void sideboard(Match match, Deck deck + ) { computerPlayer.sideboard(match, deck); } @Override - public void construct(Tournament tournament, Deck deck) { + public void construct(Tournament tournament, Deck deck + ) { computerPlayer.construct(tournament, deck); } @Override - public void pickCard(List cards, Deck deck, Draft draft) { + public void pickCard(List cards, Deck deck, + Draft draft + ) { computerPlayer.pickCard(cards, deck, draft); } @Override - public boolean scry(int value, Ability source, Game game) { + public boolean scry(int value, Ability source, + Game game + ) { // Don't scry at the start of the game. if (game.getTurnNum() == 1 && game.getStep() == null) { return false; @@ -2272,37 +2314,51 @@ public class TestPlayer implements Player { } @Override - public boolean moveCards(Card card, Zone toZone, Ability source, Game game) { + public boolean moveCards(Card card, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(card, toZone, source, game); } @Override - public boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Card card, Zone toZone, + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + ) { return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @Override - public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) { + public boolean moveCards(Cards cards, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game) { + public boolean moveCards(Set cards, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Set cards, Zone toZone, + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + ) { return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @Override - public boolean hasDesignation(DesignationType designationName) { + public boolean hasDesignation(DesignationType designationName + ) { return computerPlayer.hasDesignation(designationName); } @Override - public void addDesignation(Designation designation) { + public void addDesignation(Designation designation + ) { computerPlayer.addDesignation(designation); } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index efb8984d58..3c9714d00e 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -156,9 +156,12 @@ public abstract class GameImpl implements Game, Serializable { private final int startLife; protected PlayerList playerList; + // infinite loop check (no copy of this attributes neccessary) private int infiniteLoopCounter; // used to check if the game is in an infinite loop private int lastNumberOfAbilitiesOnTheStack; // used to check how long no new ability was put to stack + private List lastPlayersLifes = null; // if life is going down, it's no infinite loop private final LinkedList stackObjectsCheck = new LinkedList<>(); // used to check if different sources used the stack + // used to set the counters a permanent adds the battlefield (if no replacement effect is used e.g. Persist) protected Map enterWithCounters = new HashMap<>(); // used to proceed player conceding requests @@ -1418,6 +1421,23 @@ public abstract class GameImpl implements Game, Serializable { protected void checkInfiniteLoop(UUID removedStackObjectSourceId) { if (stackObjectsCheck.contains(removedStackObjectSourceId) && getStack().size() >= lastNumberOfAbilitiesOnTheStack) { + // Create a list of players life + List newLastPlayersLifes = new ArrayList<>(); + for (Player player : this.getPlayers().values()) { + newLastPlayersLifes.add(player.getLife()); + } + // Check if a player is loosing life + if (lastPlayersLifes != null && lastPlayersLifes.size() == newLastPlayersLifes.size()) { + for (int i = 0; i < newLastPlayersLifes.size(); i++) { + if (newLastPlayersLifes.get(i) < lastPlayersLifes.get(i)) { + // player is loosing life + lastPlayersLifes = null; + infiniteLoopCounter = 0; // reset the infinite counter + } + } + } else { + lastPlayersLifes = newLastPlayersLifes; + } infiniteLoopCounter++; if (infiniteLoopCounter > 15) { Player controller = getPlayer(getControllerId(removedStackObjectSourceId)); From e5a5e74862c8becce066ec02135d705e7e685355 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Thu, 1 Mar 2018 16:50:46 +0100 Subject: [PATCH 090/127] added MMQ UphillBattle added MercadianMasques card Uphill Battle --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 110 ++++++++++++++++++ Mage.Sets/src/mage/sets/MercadianMasques.java | 1 + 2 files changed, 111 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/u/UphillBattle.java diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java new file mode 100644 index 0000000000..66d343af99 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -0,0 +1,110 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.u; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.CastFromHandWatcher; + +/** + * + * @author fireshoes + */ +public class UphillBattle extends CardImpl { + + public UphillBattle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()), new CastFromHandWatcher()); + } + + public UphillBattle(final UphillBattle card) { + super(card); + } + + @Override + public UphillBattle copy() { + return new UphillBattle(this); + } +} + +class UphillBattleTapEffect extends ReplacementEffectImpl { + + UphillBattleTapEffect() { + super(Duration.WhileOnBattlefield, Outcome.Tap); + staticText = "Creatures played by your opponents enter the battlefield tapped"; + } + + UphillBattleTapEffect(final UphillBattleTapEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); + + CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getSimpleName()); + + if (target != null && watcher != null && watcher.spellWasCastFromHand(source.getSourceId())) { + target.setTapped(true); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + if (permanent != null && (permanent.isCreature())) { + return true; + } + } + return false; + } + + @Override + public UphillBattleTapEffect copy() { + return new UphillBattleTapEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index 5ca90702ac..233c8fc0ec 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -357,6 +357,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Two-Headed Dragon", 221, Rarity.RARE, mage.cards.t.TwoHeadedDragon.class)); cards.add(new SetCardInfo("Undertaker", 167, Rarity.COMMON, mage.cards.u.Undertaker.class)); cards.add(new SetCardInfo("Unmask", 168, Rarity.RARE, mage.cards.u.Unmask.class)); + cards.add(new SetCardInfo("Uphill Battle", 222, Rarity.UNCOMMON, mage.cards.u.UphillBattle.class)); cards.add(new SetCardInfo("Vendetta", 170, Rarity.COMMON, mage.cards.v.Vendetta.class)); cards.add(new SetCardInfo("Venomous Dragonfly", 282, Rarity.COMMON, mage.cards.v.VenomousDragonfly.class)); cards.add(new SetCardInfo("Vernal Equinox", 283, Rarity.RARE, mage.cards.v.VernalEquinox.class)); From fa36c9080c5577f32def5d0e27e2eac1c2e8f4ed Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Thu, 1 Mar 2018 17:10:13 +0100 Subject: [PATCH 091/127] fixed typo in checksumhelper --- Mage.Updater/src/main/java/com/magefree/update/Updater.java | 4 ++-- .../helpers/{ChechsumHelper.java => ChecksumHelper.java} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename Mage.Updater/src/main/java/com/magefree/update/helpers/{ChechsumHelper.java => ChecksumHelper.java} (97%) diff --git a/Mage.Updater/src/main/java/com/magefree/update/Updater.java b/Mage.Updater/src/main/java/com/magefree/update/Updater.java index ed39bc80b9..fdceb24444 100644 --- a/Mage.Updater/src/main/java/com/magefree/update/Updater.java +++ b/Mage.Updater/src/main/java/com/magefree/update/Updater.java @@ -1,6 +1,6 @@ package com.magefree.update; -import com.magefree.update.helpers.ChechsumHelper; +import com.magefree.update.helpers.ChecksumHelper; import com.magefree.update.helpers.FileHelper; import java.io.File; @@ -67,7 +67,7 @@ public class Updater { public HashMap readLocalData() throws Exception { HashMap result = new HashMap<>(); for (File f : findFiles()) { - result.put(f.getPath().replaceAll("\\\\", "/"), ChechsumHelper.getSHA1Checksum(f.getPath())); + result.put(f.getPath().replaceAll("\\\\", "/"), ChecksumHelper.getSHA1Checksum(f.getPath())); } return result; } diff --git a/Mage.Updater/src/main/java/com/magefree/update/helpers/ChechsumHelper.java b/Mage.Updater/src/main/java/com/magefree/update/helpers/ChecksumHelper.java similarity index 97% rename from Mage.Updater/src/main/java/com/magefree/update/helpers/ChechsumHelper.java rename to Mage.Updater/src/main/java/com/magefree/update/helpers/ChecksumHelper.java index c4d035eeed..c815ede0db 100644 --- a/Mage.Updater/src/main/java/com/magefree/update/helpers/ChechsumHelper.java +++ b/Mage.Updater/src/main/java/com/magefree/update/helpers/ChecksumHelper.java @@ -7,7 +7,7 @@ import java.security.MessageDigest; /** * @author Loki */ -public final class ChechsumHelper { +public final class ChecksumHelper { public static byte[] createChecksum(String filename) throws Exception { InputStream fis = null; From 07c76e0784b96181c5f6469b92c2886a686c958b Mon Sep 17 00:00:00 2001 From: Danny Plenge Date: Thu, 1 Mar 2018 17:11:52 +0100 Subject: [PATCH 092/127] Test file for Uphill Battle card. --- Mage.Client/test.init | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Mage.Client/test.init diff --git a/Mage.Client/test.init b/Mage.Client/test.init new file mode 100644 index 0000000000..86238c724a --- /dev/null +++ b/Mage.Client/test.init @@ -0,0 +1,28 @@ +[init] +battlefield:Human:Forest:5 +battlefield:Human:Plains:5 +battlefield:Human:Mountain:5 +battlefield:Human:Swamp:5 +battlefield:Human:Island:5 +hand:Human:Lightning Bolt:2 + +[current test] +graveyard:Human:Bloodghast:1 +graveyard:Computer:Bloodghast:1 +hand:Human:Bloodghast:1 +hand:Computer:Bloodghast:1 + +[2x bear to me] +battlefield:Human:Kitesail Corsair:2 + +[2x bear to comp] +battlefield:Computer:Kitesail Corsair:2 +//battlefield:Computer:Grizzly Bears:1 + +// create any useful commands +[clone] +hand:Human:Clone:3 +[force attack] +hand:Human:Pit Fight:3 +[exile] +hand:Human:Angelic Edict:3 \ No newline at end of file From 60cc0525da861c14608beed6f2bd50cd31c4bfa9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 2 Mar 2018 18:22:09 +1100 Subject: [PATCH 093/127] Fix zendikar full art land drawings. - Realized curve fits part of an off-centered ellipse (a 295x700 sized ellipse, with the top left being 197 pixels off the top of the top of the curved image on a scanned ZEN forest with bounding rects of: 293x337) --- .../mage/card/arcane/ModernCardRenderer.java | 55 +++++++------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index c65b92e931..b34eab7801 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -7,6 +7,7 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; +import java.awt.geom.Arc2D; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; @@ -541,15 +542,10 @@ public class ModernCardRenderer extends CardRenderer { if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... - int x2 = x; + int x2 = x + contentWidth; int y2 = y; - int topxdelta = 45 * contentWidth / 1000; - int botxdelta = 58 * contentWidth / 1000; - int ctrlx = 0; - int ctrly = (totalContentInset + y2) / 2; - - drawZendikarCurvedFace(g, image, x + topxdelta, totalContentInset + boxHeight, ctrlx, ctrly, x2 + botxdelta, y2, - x + contentWidth - topxdelta, totalContentInset + boxHeight, cardWidth, ctrly, x2 + contentWidth - botxdelta, y2, + int thisy = totalContentInset + boxHeight; + drawZendikarCurvedFace(g, image, x, thisy, x2, y2, boxColor, borderPaint); } else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) { // Draw curved lines (BFZ land style) @@ -574,8 +570,7 @@ public class ModernCardRenderer extends CardRenderer { drawBottomRight(g, borderPaint, boxColor); } - public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int ctrlx, int ctrly, int w, int h, - int x2, int y2, int ctrlx2, int ctrly2, int w2, int h2, + public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, Color boxColor, Paint paint) { BufferedImage artToUse = faceArtImage; @@ -600,33 +595,27 @@ public class ModernCardRenderer extends CardRenderer { } Path2D.Double curve = new Path2D.Double(); - curve.moveTo(x, y); - curve.quadTo(ctrlx, ctrly, w, h); - curve.lineTo(w2, h); - curve.quadTo(ctrlx2, ctrly2, x2, y); - curve.lineTo(x, y); - Path2D.Double innercurve = new Path2D.Double(); - innercurve.moveTo(x + 1, y + 1); - innercurve.quadTo(ctrlx + 1, ctrly + 1, w + 1, h - 1); - innercurve.lineTo(w2 - 1, h - 1); - innercurve.quadTo(ctrlx2 - 1, ctrly2 - 1, x2 - 1, y + 1); - innercurve.lineTo(x + 1, y + 1); + int ew = x2 - x; + int eh = 700 * (y2 - y) / 335; + Arc2D arc = new Arc2D.Double(x, y - 197 * eh / 700, ew, eh, 0, 360, Arc2D.OPEN); + Arc2D innerarc = new Arc2D.Double(x + 1, y - 197 * eh / 700 + 1, ew - 2, eh - 2, 0, 360, Arc2D.OPEN); + + curve.append(new Rectangle2D.Double(x, y, x2 - x, y2 - y), false); + g2.setClip(new Rectangle2D.Double(x, y, x2 - x, y2 - y)); + g2.setClip(arc); Rectangle2D r = curve.getBounds2D(); - int minX = (int) r.getX(); + g2.drawImage(artToUse, x, y, x2 - x, y2 - y, null); + g2.setClip(null); + g2.setClip(new Rectangle2D.Double(x, y, x2 - x, y2 - y)); - g2.setClip(innercurve); - g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, h2 - y, null); + g2.setColor(CardRendererUtils.abitdarker(boxColor)); + g2.draw(arc); + g2.setColor(Color.black); + g2.draw(innerarc); g2.setClip(null); - g2.setColor(CardRendererUtils.abitdarker(boxColor)); - g2.setPaint(paint); - g2.draw(curve); - - g2.setColor(Color.black); - //curve.transform(AffineTransform.getTranslateInstance(-1,-1)); - g2.draw(innercurve); } public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, @@ -967,8 +956,7 @@ public class ModernCardRenderer extends CardRenderer { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands - { - if (allRules.size() == 1) { + if (allRules.size() == 1) { // Size of mana symbol = 9/4 * h, 3/4h above line drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; @@ -978,7 +966,6 @@ public class ModernCardRenderer extends CardRenderer { } return; } - } } // Go through possible font sizes in descending order to find the best fit From f2410b7ef1cedd4a1035c72a697981e765d0cad9 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Fri, 2 Mar 2018 14:32:12 +0100 Subject: [PATCH 094/127] Fixed creatures not getting tapped by Uphill Battle The condition was checking if the Uphill Battle was cast from hand, instead of the actual card entering the battlefield --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 66d343af99..721027f2da 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -28,11 +28,9 @@ package mage.cards.u; import java.util.UUID; -import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -44,13 +42,12 @@ import mage.watchers.common.CastFromHandWatcher; /** * - * @author fireshoes + * @author chrvanorle */ public class UphillBattle extends CardImpl { - + public UphillBattle(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()), new CastFromHandWatcher()); } @@ -78,10 +75,9 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getSimpleName()); - if (target != null && watcher != null && watcher.spellWasCastFromHand(source.getSourceId())) { + if (target != null && watcher != null && watcher.spellWasCastFromHand(target.getId())) { target.setTapped(true); } return false; From ae99a1b4445719dfed96940cad9e0e957c2c4ffa Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 2 Mar 2018 21:42:16 +0400 Subject: [PATCH 095/127] Fixed tests --- Mage/src/main/java/mage/game/GameImpl.java | 1 + Mage/src/main/java/mage/players/PlayerImpl.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 3c9714d00e..572a2a4307 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1433,6 +1433,7 @@ public abstract class GameImpl implements Game, Serializable { // player is loosing life lastPlayersLifes = null; infiniteLoopCounter = 0; // reset the infinite counter + break; } } } else { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a61e8023f3..b47fab6df9 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1274,7 +1274,7 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - protected LinkedHashMap getSpellAbilities(MageObject object, Zone zone, Game game) { + public LinkedHashMap getSpellAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); for (Ability ability : object.getAbilities()) { if (ability instanceof SpellAbility) { From 08f15416ba996d03717bcb450132b5d015883131 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 2 Mar 2018 22:06:24 +0400 Subject: [PATCH 096/127] * Add new full set Masters 25 (only Karona's Zealot must be implemented); --- .../plugins/card/dl/sources/GathererSets.java | 2 +- .../card/dl/sources/ScryfallImageSource.java | 2 +- .../src/main/resources/image.url.properties | 4 +- Mage.Sets/src/mage/sets/Masters25.java | 255 +++++++++++++++++- .../mage/cards/repository/CardRepository.java | 2 +- Utils/mtg-sets-data.txt | 2 +- 6 files changed, 259 insertions(+), 8 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index 38b5ff2b43..385204f822 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -66,7 +66,7 @@ public class GathererSets implements Iterable { //"APAC" -- gatherer do not have that set, scrly have PALP //"ARENA" -- is't many set with different codes, not one "CLASH", "CP", "DD3GVL", "DPA", "EURO", "FNMP", "GPX", "GRC", "GUR", "H17", "JR", "MBP", "MGDC", "MLP", "MPRP", "MPS-AKH", "PTC", "S00", "S99", "SUS", "SWS", "UGIN", "UGL", "V10", "V17", "WMCQ", // need to fix - "H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "M25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok + "H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "A25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok // current testing }; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java index 6f664975ff..aafb2d1568 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java @@ -208,7 +208,7 @@ public enum ScryfallImageSource implements CardImageSource { supportedSets.add("RIX"); supportedSets.add("WMCQ"); supportedSets.add("PPRO"); -// supportedSets.add("A25"); + supportedSets.add("A25"); // supportedSets.add("DOM"); // supportedSets.add("M19"); diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index 63ffa1a49a..3aba474b2f 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -73,6 +73,6 @@ dd3evg=ddaevg dd3gvl=ddagvl dd3jvc=ddajvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK,M19,M25,DOM,H17 +ignore.urls=TOK,M19,DOM,H17 # sets ordered by release time (newest goes first) -token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file +token.lookup.order=M19,A25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Masters25.java b/Mage.Sets/src/mage/sets/Masters25.java index cb35f2c36f..54c3278628 100644 --- a/Mage.Sets/src/mage/sets/Masters25.java +++ b/Mage.Sets/src/mage/sets/Masters25.java @@ -29,9 +29,10 @@ package mage.sets; /** * - * @author fireshoes + * @author JayDi85 */ import mage.cards.ExpansionSet; +import mage.constants.Rarity; import mage.constants.SetType; public class Masters25 extends ExpansionSet { @@ -43,7 +44,7 @@ public class Masters25 extends ExpansionSet { } private Masters25() { - super("Masters 25", "M25", ExpansionSet.buildDate(2018, 3, 16), SetType.SUPPLEMENTAL); + super("Masters 25", "A25", ExpansionSet.buildDate(2018, 3, 16), SetType.SUPPLEMENTAL); this.blockName = "Reprint"; this.hasBasicLands = false; this.hasBoosters = true; @@ -52,5 +53,255 @@ public class Masters25 extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + + cards.add(new SetCardInfo("Act of Heroism", 1, Rarity.COMMON, mage.cards.a.ActOfHeroism.class)); + cards.add(new SetCardInfo("Akroma, Angel of Wrath", 2, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfWrath.class)); + cards.add(new SetCardInfo("Akroma's Vengeance", 3, Rarity.RARE, mage.cards.a.AkromasVengeance.class)); + cards.add(new SetCardInfo("Angelic Page", 4, Rarity.UNCOMMON, mage.cards.a.AngelicPage.class)); + cards.add(new SetCardInfo("Armageddon", 5, Rarity.MYTHIC, mage.cards.a.Armageddon.class)); + cards.add(new SetCardInfo("Auramancer", 6, Rarity.COMMON, mage.cards.a.Auramancer.class)); + cards.add(new SetCardInfo("Cloudshift", 7, Rarity.COMMON, mage.cards.c.Cloudshift.class)); + cards.add(new SetCardInfo("Congregate", 8, Rarity.UNCOMMON, mage.cards.c.Congregate.class)); + cards.add(new SetCardInfo("Darien, King of Kjeldor", 9, Rarity.RARE, mage.cards.d.DarienKingOfKjeldor.class)); + cards.add(new SetCardInfo("Dauntless Cathar", 10, Rarity.COMMON, mage.cards.d.DauntlessCathar.class)); + cards.add(new SetCardInfo("Decree of Justice", 11, Rarity.RARE, mage.cards.d.DecreeOfJustice.class)); + cards.add(new SetCardInfo("Disenchant", 12, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Fencing Ace", 13, Rarity.COMMON, mage.cards.f.FencingAce.class)); + cards.add(new SetCardInfo("Fiend Hunter", 14, Rarity.UNCOMMON, mage.cards.f.FiendHunter.class)); + cards.add(new SetCardInfo("Geist of the Moors", 15, Rarity.COMMON, mage.cards.g.GeistOfTheMoors.class)); + cards.add(new SetCardInfo("Gods Willing", 16, Rarity.COMMON, mage.cards.g.GodsWilling.class)); + cards.add(new SetCardInfo("Griffin Protector", 17, Rarity.COMMON, mage.cards.g.GriffinProtector.class)); + //cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); + cards.add(new SetCardInfo("Knight of the Skyward Eye", 19, Rarity.COMMON, mage.cards.k.KnightOfTheSkywardEye.class)); + cards.add(new SetCardInfo("Kongming, 'Sleeping Dragon'", 20, Rarity.UNCOMMON, mage.cards.k.KongmingSleepingDragon.class)); + cards.add(new SetCardInfo("Kor Firewalker", 21, Rarity.UNCOMMON, mage.cards.k.KorFirewalker.class)); + cards.add(new SetCardInfo("Loyal Sentry", 22, Rarity.COMMON, mage.cards.l.LoyalSentry.class)); + cards.add(new SetCardInfo("Luminarch Ascension", 23, Rarity.RARE, mage.cards.l.LuminarchAscension.class)); + cards.add(new SetCardInfo("Lunarch Mantle", 24, Rarity.COMMON, mage.cards.l.LunarchMantle.class)); + cards.add(new SetCardInfo("Noble Templar", 25, Rarity.COMMON, mage.cards.n.NobleTemplar.class)); + cards.add(new SetCardInfo("Nyx-Fleece Ram", 26, Rarity.UNCOMMON, mage.cards.n.NyxFleeceRam.class)); + cards.add(new SetCardInfo("Ordeal of Heliod", 27, Rarity.UNCOMMON, mage.cards.o.OrdealOfHeliod.class)); + cards.add(new SetCardInfo("Pacifism", 28, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Path of Peace", 29, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); + cards.add(new SetCardInfo("Promise of Bunrei", 30, Rarity.UNCOMMON, mage.cards.p.PromiseOfBunrei.class)); + cards.add(new SetCardInfo("Renewed Faith", 31, Rarity.COMMON, mage.cards.r.RenewedFaith.class)); + cards.add(new SetCardInfo("Rest in Peace", 32, Rarity.RARE, mage.cards.r.RestInPeace.class)); + cards.add(new SetCardInfo("Savannah Lions", 33, Rarity.COMMON, mage.cards.s.SavannahLions.class)); + cards.add(new SetCardInfo("Squadron Hawk", 34, Rarity.COMMON, mage.cards.s.SquadronHawk.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 35, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 36, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class)); + cards.add(new SetCardInfo("Urbis Protector", 37, Rarity.UNCOMMON, mage.cards.u.UrbisProtector.class)); + cards.add(new SetCardInfo("Valor in Akros", 38, Rarity.UNCOMMON, mage.cards.v.ValorInAkros.class)); + cards.add(new SetCardInfo("Whitemane Lion", 39, Rarity.COMMON, mage.cards.w.WhitemaneLion.class)); + cards.add(new SetCardInfo("Accumulated Knowledge", 40, Rarity.COMMON, mage.cards.a.AccumulatedKnowledge.class)); + cards.add(new SetCardInfo("Arcane Denial", 41, Rarity.COMMON, mage.cards.a.ArcaneDenial.class)); + cards.add(new SetCardInfo("Bident of Thassa", 42, Rarity.RARE, mage.cards.b.BidentOfThassa.class)); + cards.add(new SetCardInfo("Blue Elemental Blast", 43, Rarity.UNCOMMON, mage.cards.b.BlueElementalBlast.class)); + cards.add(new SetCardInfo("Blue Sun's Zenith", 44, Rarity.RARE, mage.cards.b.BlueSunsZenith.class)); + cards.add(new SetCardInfo("Borrowing 100,000 Arrows", 45, Rarity.COMMON, mage.cards.b.Borrowing100000Arrows.class)); + cards.add(new SetCardInfo("Brainstorm", 46, Rarity.COMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Brine Elemental", 47, Rarity.UNCOMMON, mage.cards.b.BrineElemental.class)); + cards.add(new SetCardInfo("Choking Tethers", 48, Rarity.COMMON, mage.cards.c.ChokingTethers.class)); + cards.add(new SetCardInfo("Coralhelm Guide", 49, Rarity.COMMON, mage.cards.c.CoralhelmGuide.class)); + cards.add(new SetCardInfo("Counterspell", 50, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Court Hussar", 51, Rarity.COMMON, mage.cards.c.CourtHussar.class)); + cards.add(new SetCardInfo("Curiosity", 52, Rarity.UNCOMMON, mage.cards.c.Curiosity.class)); + cards.add(new SetCardInfo("Cursecatcher", 53, Rarity.UNCOMMON, mage.cards.c.Cursecatcher.class)); + cards.add(new SetCardInfo("Dragon's Eye Savants", 54, Rarity.COMMON, mage.cards.d.DragonsEyeSavants.class)); + cards.add(new SetCardInfo("Exclude", 55, Rarity.UNCOMMON, mage.cards.e.Exclude.class)); + cards.add(new SetCardInfo("Fathom Seer", 56, Rarity.COMMON, mage.cards.f.FathomSeer.class)); + cards.add(new SetCardInfo("Flash", 57, Rarity.RARE, mage.cards.f.Flash.class)); + cards.add(new SetCardInfo("Freed from the Real", 58, Rarity.UNCOMMON, mage.cards.f.FreedFromTheReal.class)); + cards.add(new SetCardInfo("Genju of the Falls", 59, Rarity.UNCOMMON, mage.cards.g.GenjuOfTheFalls.class)); + cards.add(new SetCardInfo("Ghost Ship", 60, Rarity.COMMON, mage.cards.g.GhostShip.class)); + cards.add(new SetCardInfo("Horseshoe Crab", 61, Rarity.COMMON, mage.cards.h.HorseshoeCrab.class)); + cards.add(new SetCardInfo("Jace, the Mind Sculptor", 62, Rarity.MYTHIC, mage.cards.j.JaceTheMindSculptor.class)); + cards.add(new SetCardInfo("Jalira, Master Polymorphist", 63, Rarity.UNCOMMON, mage.cards.j.JaliraMasterPolymorphist.class)); + cards.add(new SetCardInfo("Man-o'-War", 64, Rarity.COMMON, mage.cards.m.ManOWar.class)); + cards.add(new SetCardInfo("Merfolk Looter", 65, Rarity.UNCOMMON, mage.cards.m.MerfolkLooter.class)); + cards.add(new SetCardInfo("Murder of Crows", 66, Rarity.UNCOMMON, mage.cards.m.MurderOfCrows.class)); + cards.add(new SetCardInfo("Mystic of the Hidden Way", 67, Rarity.COMMON, mage.cards.m.MysticOfTheHiddenWay.class)); + cards.add(new SetCardInfo("Pact of Negation", 68, Rarity.RARE, mage.cards.p.PactOfNegation.class)); + cards.add(new SetCardInfo("Phantasmal Bear", 69, Rarity.COMMON, mage.cards.p.PhantasmalBear.class)); + cards.add(new SetCardInfo("Reef Worm", 70, Rarity.RARE, mage.cards.r.ReefWorm.class)); + cards.add(new SetCardInfo("Retraction Helix", 71, Rarity.COMMON, mage.cards.r.RetractionHelix.class)); + cards.add(new SetCardInfo("Shoreline Ranger", 72, Rarity.COMMON, mage.cards.s.ShorelineRanger.class)); + cards.add(new SetCardInfo("Sift", 73, Rarity.COMMON, mage.cards.s.Sift.class)); + cards.add(new SetCardInfo("Totally Lost", 74, Rarity.COMMON, mage.cards.t.TotallyLost.class)); + cards.add(new SetCardInfo("Twisted Image", 75, Rarity.UNCOMMON, mage.cards.t.TwistedImage.class)); + cards.add(new SetCardInfo("Vendilion Clique", 76, Rarity.MYTHIC, mage.cards.v.VendilionClique.class)); + cards.add(new SetCardInfo("Vesuvan Shapeshifter", 77, Rarity.RARE, mage.cards.v.VesuvanShapeshifter.class)); + cards.add(new SetCardInfo("Willbender", 78, Rarity.UNCOMMON, mage.cards.w.Willbender.class)); + cards.add(new SetCardInfo("Ancient Craving", 79, Rarity.UNCOMMON, mage.cards.a.AncientCraving.class)); + cards.add(new SetCardInfo("Bloodhunter Bat", 80, Rarity.COMMON, mage.cards.b.BloodhunterBat.class)); + cards.add(new SetCardInfo("Caustic Tar", 81, Rarity.UNCOMMON, mage.cards.c.CausticTar.class)); + cards.add(new SetCardInfo("Dark Ritual", 82, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Deadly Designs", 83, Rarity.UNCOMMON, mage.cards.d.DeadlyDesigns.class)); + cards.add(new SetCardInfo("Death's-Head Buzzard", 84, Rarity.COMMON, mage.cards.d.DeathsHeadBuzzard.class)); + cards.add(new SetCardInfo("Diabolic Edict", 85, Rarity.COMMON, mage.cards.d.DiabolicEdict.class)); + cards.add(new SetCardInfo("Dirge of Dread", 86, Rarity.COMMON, mage.cards.d.DirgeOfDread.class)); + cards.add(new SetCardInfo("Disfigure", 87, Rarity.COMMON, mage.cards.d.Disfigure.class)); + cards.add(new SetCardInfo("Doomsday", 88, Rarity.MYTHIC, mage.cards.d.Doomsday.class)); + cards.add(new SetCardInfo("Dusk Legion Zealot", 89, Rarity.COMMON, mage.cards.d.DuskLegionZealot.class)); + cards.add(new SetCardInfo("Erg Raiders", 90, Rarity.COMMON, mage.cards.e.ErgRaiders.class)); + cards.add(new SetCardInfo("Fallen Angel", 91, Rarity.UNCOMMON, mage.cards.f.FallenAngel.class)); + cards.add(new SetCardInfo("Hell's Caretaker", 92, Rarity.RARE, mage.cards.h.HellsCaretaker.class)); + cards.add(new SetCardInfo("Horror of the Broken Lands", 93, Rarity.COMMON, mage.cards.h.HorrorOfTheBrokenLands.class)); + cards.add(new SetCardInfo("Ihsan's Shade", 94, Rarity.UNCOMMON, mage.cards.i.IhsansShade.class)); + cards.add(new SetCardInfo("Laquatus's Champion", 95, Rarity.RARE, mage.cards.l.LaquatussChampion.class)); + cards.add(new SetCardInfo("Living Death", 96, Rarity.RARE, mage.cards.l.LivingDeath.class)); + cards.add(new SetCardInfo("Mesmeric Fiend", 97, Rarity.UNCOMMON, mage.cards.m.MesmericFiend.class)); + cards.add(new SetCardInfo("Murder", 98, Rarity.COMMON, mage.cards.m.Murder.class)); + cards.add(new SetCardInfo("Nezumi Cutthroat", 99, Rarity.COMMON, mage.cards.n.NezumiCutthroat.class)); + cards.add(new SetCardInfo("Phyrexian Ghoul", 100, Rarity.COMMON, mage.cards.p.PhyrexianGhoul.class)); + cards.add(new SetCardInfo("Phyrexian Obliterator", 101, Rarity.MYTHIC, mage.cards.p.PhyrexianObliterator.class)); + cards.add(new SetCardInfo("Plague Wind", 102, Rarity.RARE, mage.cards.p.PlagueWind.class)); + cards.add(new SetCardInfo("Ratcatcher", 103, Rarity.RARE, mage.cards.r.Ratcatcher.class)); + cards.add(new SetCardInfo("Ravenous Chupacabra", 104, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class)); + cards.add(new SetCardInfo("Relentless Rats", 105, Rarity.COMMON, mage.cards.r.RelentlessRats.class)); + cards.add(new SetCardInfo("Returned Phalanx", 106, Rarity.COMMON, mage.cards.r.ReturnedPhalanx.class)); + cards.add(new SetCardInfo("Ruthless Ripper", 107, Rarity.COMMON, mage.cards.r.RuthlessRipper.class)); + cards.add(new SetCardInfo("Street Wraith", 108, Rarity.UNCOMMON, mage.cards.s.StreetWraith.class)); + cards.add(new SetCardInfo("Supernatural Stamina", 109, Rarity.COMMON, mage.cards.s.SupernaturalStamina.class)); + cards.add(new SetCardInfo("Triskaidekaphobia", 110, Rarity.RARE, mage.cards.t.Triskaidekaphobia.class)); + cards.add(new SetCardInfo("Twisted Abomination", 111, Rarity.COMMON, mage.cards.t.TwistedAbomination.class)); + cards.add(new SetCardInfo("Undead Gladiator", 112, Rarity.UNCOMMON, mage.cards.u.UndeadGladiator.class)); + cards.add(new SetCardInfo("Unearth", 113, Rarity.COMMON, mage.cards.u.Unearth.class)); + cards.add(new SetCardInfo("Vampire Lacerator", 114, Rarity.COMMON, mage.cards.v.VampireLacerator.class)); + cards.add(new SetCardInfo("Will-o'-the-Wisp", 115, Rarity.UNCOMMON, mage.cards.w.WillOTheWisp.class)); + cards.add(new SetCardInfo("Zombify", 116, Rarity.UNCOMMON, mage.cards.z.Zombify.class)); + cards.add(new SetCardInfo("Zulaport Cutthroat", 117, Rarity.UNCOMMON, mage.cards.z.ZulaportCutthroat.class)); + cards.add(new SetCardInfo("Act of Treason", 118, Rarity.COMMON, mage.cards.a.ActOfTreason.class)); + cards.add(new SetCardInfo("Akroma, Angel of Fury", 119, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfFury.class)); + cards.add(new SetCardInfo("Balduvian Horde", 120, Rarity.COMMON, mage.cards.b.BalduvianHorde.class)); + cards.add(new SetCardInfo("Ball Lightning", 121, Rarity.RARE, mage.cards.b.BallLightning.class)); + cards.add(new SetCardInfo("Blood Moon", 122, Rarity.RARE, mage.cards.b.BloodMoon.class)); + cards.add(new SetCardInfo("Browbeat", 123, Rarity.UNCOMMON, mage.cards.b.Browbeat.class)); + cards.add(new SetCardInfo("Chandra's Outrage", 124, Rarity.COMMON, mage.cards.c.ChandrasOutrage.class)); + cards.add(new SetCardInfo("Chartooth Cougar", 125, Rarity.COMMON, mage.cards.c.ChartoothCougar.class)); + cards.add(new SetCardInfo("Cinder Storm", 126, Rarity.COMMON, mage.cards.c.CinderStorm.class)); + cards.add(new SetCardInfo("Crimson Mage", 127, Rarity.COMMON, mage.cards.c.CrimsonMage.class)); + cards.add(new SetCardInfo("Eidolon of the Great Revel", 128, Rarity.RARE, mage.cards.e.EidolonOfTheGreatRevel.class)); + cards.add(new SetCardInfo("Enthralling Victor", 129, Rarity.UNCOMMON, mage.cards.e.EnthrallingVictor.class)); + cards.add(new SetCardInfo("Fortune Thief", 130, Rarity.RARE, mage.cards.f.FortuneThief.class)); + cards.add(new SetCardInfo("Frenzied Goblin", 131, Rarity.COMMON, mage.cards.f.FrenziedGoblin.class)); + cards.add(new SetCardInfo("Genju of the Spires", 132, Rarity.UNCOMMON, mage.cards.g.GenjuOfTheSpires.class)); + cards.add(new SetCardInfo("Goblin War Drums", 133, Rarity.UNCOMMON, mage.cards.g.GoblinWarDrums.class)); + cards.add(new SetCardInfo("Hordeling Outburst", 134, Rarity.COMMON, mage.cards.h.HordelingOutburst.class)); + cards.add(new SetCardInfo("Humble Defector", 135, Rarity.UNCOMMON, mage.cards.h.HumbleDefector.class)); + cards.add(new SetCardInfo("Imperial Recruiter", 136, Rarity.MYTHIC, mage.cards.i.ImperialRecruiter.class)); + cards.add(new SetCardInfo("Ire Shaman", 137, Rarity.UNCOMMON, mage.cards.i.IreShaman.class)); + cards.add(new SetCardInfo("Izzet Chemister", 138, Rarity.RARE, mage.cards.i.IzzetChemister.class)); + cards.add(new SetCardInfo("Jackal Pup", 139, Rarity.COMMON, mage.cards.j.JackalPup.class)); + cards.add(new SetCardInfo("Kindle", 140, Rarity.COMMON, mage.cards.k.Kindle.class)); + cards.add(new SetCardInfo("Lightning Bolt", 141, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class)); + cards.add(new SetCardInfo("Magus of the Wheel", 142, Rarity.RARE, mage.cards.m.MagusOfTheWheel.class)); + cards.add(new SetCardInfo("Mogg Flunkies", 143, Rarity.COMMON, mage.cards.m.MoggFlunkies.class)); + cards.add(new SetCardInfo("Pillage", 144, Rarity.COMMON, mage.cards.p.Pillage.class)); + cards.add(new SetCardInfo("Pyre Hound", 145, Rarity.COMMON, mage.cards.p.PyreHound.class)); + cards.add(new SetCardInfo("Pyroclasm", 146, Rarity.UNCOMMON, mage.cards.p.Pyroclasm.class)); + cards.add(new SetCardInfo("Red Elemental Blast", 147, Rarity.UNCOMMON, mage.cards.r.RedElementalBlast.class)); + cards.add(new SetCardInfo("Simian Spirit Guide", 148, Rarity.UNCOMMON, mage.cards.s.SimianSpiritGuide.class)); + cards.add(new SetCardInfo("Skeletonize", 149, Rarity.COMMON, mage.cards.s.Skeletonize.class)); + cards.add(new SetCardInfo("Skirk Commando", 150, Rarity.COMMON, mage.cards.s.SkirkCommando.class)); + cards.add(new SetCardInfo("Soulbright Flamekin", 151, Rarity.COMMON, mage.cards.s.SoulbrightFlamekin.class)); + cards.add(new SetCardInfo("Spikeshot Goblin", 152, Rarity.UNCOMMON, mage.cards.s.SpikeshotGoblin.class)); + cards.add(new SetCardInfo("Thresher Lizard", 153, Rarity.COMMON, mage.cards.t.ThresherLizard.class)); + cards.add(new SetCardInfo("Trumpet Blast", 154, Rarity.COMMON, mage.cards.t.TrumpetBlast.class)); + cards.add(new SetCardInfo("Uncaged Fury", 155, Rarity.COMMON, mage.cards.u.UncagedFury.class)); + cards.add(new SetCardInfo("Zada, Hedron Grinder", 156, Rarity.UNCOMMON, mage.cards.z.ZadaHedronGrinder.class)); + cards.add(new SetCardInfo("Ainok Survivalist", 157, Rarity.COMMON, mage.cards.a.AinokSurvivalist.class)); + cards.add(new SetCardInfo("Ambassador Oak", 158, Rarity.COMMON, mage.cards.a.AmbassadorOak.class)); + cards.add(new SetCardInfo("Ancient Stirrings", 159, Rarity.UNCOMMON, mage.cards.a.AncientStirrings.class)); + cards.add(new SetCardInfo("Arbor Elf", 160, Rarity.COMMON, mage.cards.a.ArborElf.class)); + cards.add(new SetCardInfo("Azusa, Lost but Seeking", 161, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class)); + cards.add(new SetCardInfo("Broodhatch Nantuko", 162, Rarity.UNCOMMON, mage.cards.b.BroodhatchNantuko.class)); + cards.add(new SetCardInfo("Colossal Dreadmaw", 163, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class)); + cards.add(new SetCardInfo("Courser of Kruphix", 164, Rarity.RARE, mage.cards.c.CourserOfKruphix.class)); + cards.add(new SetCardInfo("Cultivate", 165, Rarity.COMMON, mage.cards.c.Cultivate.class)); + cards.add(new SetCardInfo("Echoing Courage", 166, Rarity.COMMON, mage.cards.e.EchoingCourage.class)); + cards.add(new SetCardInfo("Elvish Aberration", 167, Rarity.COMMON, mage.cards.e.ElvishAberration.class)); + cards.add(new SetCardInfo("Elvish Piper", 168, Rarity.RARE, mage.cards.e.ElvishPiper.class)); + cards.add(new SetCardInfo("Ember Weaver", 169, Rarity.COMMON, mage.cards.e.EmberWeaver.class)); + cards.add(new SetCardInfo("Epic Confrontation", 170, Rarity.COMMON, mage.cards.e.EpicConfrontation.class)); + cards.add(new SetCardInfo("Fierce Empath", 171, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class)); + cards.add(new SetCardInfo("Giant Growth", 172, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Invigorate", 173, Rarity.UNCOMMON, mage.cards.i.Invigorate.class)); + cards.add(new SetCardInfo("Iwamori of the Open Fist", 174, Rarity.UNCOMMON, mage.cards.i.IwamoriOfTheOpenFist.class)); + cards.add(new SetCardInfo("Kavu Climber", 175, Rarity.COMMON, mage.cards.k.KavuClimber.class)); + cards.add(new SetCardInfo("Kavu Predator", 176, Rarity.UNCOMMON, mage.cards.k.KavuPredator.class)); + cards.add(new SetCardInfo("Krosan Colossus", 177, Rarity.UNCOMMON, mage.cards.k.KrosanColossus.class)); + cards.add(new SetCardInfo("Krosan Tusker", 178, Rarity.UNCOMMON, mage.cards.k.KrosanTusker.class)); + cards.add(new SetCardInfo("Living Wish", 179, Rarity.RARE, mage.cards.l.LivingWish.class)); + cards.add(new SetCardInfo("Lull", 180, Rarity.COMMON, mage.cards.l.Lull.class)); + cards.add(new SetCardInfo("Master of the Wild Hunt", 181, Rarity.MYTHIC, mage.cards.m.MasterOfTheWildHunt.class)); + cards.add(new SetCardInfo("Nettle Sentinel", 182, Rarity.COMMON, mage.cards.n.NettleSentinel.class)); + cards.add(new SetCardInfo("Plummet", 183, Rarity.COMMON, mage.cards.p.Plummet.class)); + cards.add(new SetCardInfo("Presence of Gond", 184, Rarity.COMMON, mage.cards.p.PresenceOfGond.class)); + cards.add(new SetCardInfo("Protean Hulk", 185, Rarity.RARE, mage.cards.p.ProteanHulk.class)); + cards.add(new SetCardInfo("Rancor", 186, Rarity.UNCOMMON, mage.cards.r.Rancor.class)); + cards.add(new SetCardInfo("Regrowth", 187, Rarity.UNCOMMON, mage.cards.r.Regrowth.class)); + cards.add(new SetCardInfo("Stampede Driver", 188, Rarity.UNCOMMON, mage.cards.s.StampedeDriver.class)); + cards.add(new SetCardInfo("Summoner's Pact", 189, Rarity.RARE, mage.cards.s.SummonersPact.class)); + cards.add(new SetCardInfo("Timberpack Wolf", 190, Rarity.COMMON, mage.cards.t.TimberpackWolf.class)); + cards.add(new SetCardInfo("Tree of Redemption", 191, Rarity.MYTHIC, mage.cards.t.TreeOfRedemption.class)); + cards.add(new SetCardInfo("Utopia Sprawl", 192, Rarity.UNCOMMON, mage.cards.u.UtopiaSprawl.class)); + cards.add(new SetCardInfo("Vessel of Nascency", 193, Rarity.COMMON, mage.cards.v.VesselOfNascency.class)); + cards.add(new SetCardInfo("Wildheart Invoker", 194, Rarity.COMMON, mage.cards.w.WildheartInvoker.class)); + cards.add(new SetCardInfo("Woolly Loxodon", 195, Rarity.COMMON, mage.cards.w.WoollyLoxodon.class)); + cards.add(new SetCardInfo("Animar, Soul of Elements", 196, Rarity.MYTHIC, mage.cards.a.AnimarSoulOfElements.class)); + cards.add(new SetCardInfo("Baloth Null", 197, Rarity.UNCOMMON, mage.cards.b.BalothNull.class)); + cards.add(new SetCardInfo("Blightning", 198, Rarity.UNCOMMON, mage.cards.b.Blightning.class)); + cards.add(new SetCardInfo("Boros Charm", 199, Rarity.UNCOMMON, mage.cards.b.BorosCharm.class)); + cards.add(new SetCardInfo("Brion Stoutarm", 200, Rarity.RARE, mage.cards.b.BrionStoutarm.class)); + cards.add(new SetCardInfo("Cloudblazer", 201, Rarity.UNCOMMON, mage.cards.c.Cloudblazer.class)); + cards.add(new SetCardInfo("Conflux", 202, Rarity.RARE, mage.cards.c.Conflux.class)); + cards.add(new SetCardInfo("Eladamri's Call", 203, Rarity.RARE, mage.cards.e.EladamrisCall.class)); + cards.add(new SetCardInfo("Gisela, Blade of Goldnight", 204, Rarity.MYTHIC, mage.cards.g.GiselaBladeOfGoldnight.class)); + cards.add(new SetCardInfo("Grenzo, Dungeon Warden", 205, Rarity.RARE, mage.cards.g.GrenzoDungeonWarden.class)); + cards.add(new SetCardInfo("Hanna, Ship's Navigator", 206, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); + cards.add(new SetCardInfo("Lorescale Coatl", 207, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class)); + cards.add(new SetCardInfo("Mystic Snake", 208, Rarity.RARE, mage.cards.m.MysticSnake.class)); + cards.add(new SetCardInfo("Nicol Bolas", 209, Rarity.RARE, mage.cards.n.NicolBolas.class)); + cards.add(new SetCardInfo("Niv-Mizzet, the Firemind", 210, Rarity.RARE, mage.cards.n.NivMizzetTheFiremind.class)); + cards.add(new SetCardInfo("Notion Thief", 211, Rarity.RARE, mage.cards.n.NotionThief.class)); + cards.add(new SetCardInfo("Pernicious Deed", 212, Rarity.RARE, mage.cards.p.PerniciousDeed.class)); + cards.add(new SetCardInfo("Pillory of the Sleepless", 213, Rarity.UNCOMMON, mage.cards.p.PilloryOfTheSleepless.class)); + cards.add(new SetCardInfo("Prossh, Skyraider of Kher", 214, Rarity.MYTHIC, mage.cards.p.ProsshSkyraiderOfKher.class)); + cards.add(new SetCardInfo("Quicksilver Dagger", 215, Rarity.UNCOMMON, mage.cards.q.QuicksilverDagger.class)); + cards.add(new SetCardInfo("Ruric Thar, the Unbowed", 216, Rarity.RARE, mage.cards.r.RuricTharTheUnbowed.class)); + cards.add(new SetCardInfo("Shadowmage Infiltrator", 217, Rarity.UNCOMMON, mage.cards.s.ShadowmageInfiltrator.class)); + cards.add(new SetCardInfo("Stangg", 218, Rarity.UNCOMMON, mage.cards.s.Stangg.class)); + cards.add(new SetCardInfo("Vindicate", 219, Rarity.RARE, mage.cards.v.Vindicate.class)); + cards.add(new SetCardInfo("Watchwolf", 220, Rarity.UNCOMMON, mage.cards.w.Watchwolf.class)); + cards.add(new SetCardInfo("Assembly-Worker", 221, Rarity.COMMON, mage.cards.a.AssemblyWorker.class)); + cards.add(new SetCardInfo("Chalice of the Void", 222, Rarity.MYTHIC, mage.cards.c.ChaliceOfTheVoid.class)); + cards.add(new SetCardInfo("Coalition Relic", 223, Rarity.RARE, mage.cards.c.CoalitionRelic.class)); + cards.add(new SetCardInfo("Ensnaring Bridge", 224, Rarity.MYTHIC, mage.cards.e.EnsnaringBridge.class)); + cards.add(new SetCardInfo("Heavy Arbalest", 225, Rarity.UNCOMMON, mage.cards.h.HeavyArbalest.class)); + cards.add(new SetCardInfo("Nihil Spellbomb", 226, Rarity.COMMON, mage.cards.n.NihilSpellbomb.class)); + cards.add(new SetCardInfo("Perilous Myr", 227, Rarity.UNCOMMON, mage.cards.p.PerilousMyr.class)); + cards.add(new SetCardInfo("Primal Clay", 228, Rarity.COMMON, mage.cards.p.PrimalClay.class)); + cards.add(new SetCardInfo("Prophetic Prism", 229, Rarity.COMMON, mage.cards.p.PropheticPrism.class)); + cards.add(new SetCardInfo("Sai of the Shinobi", 230, Rarity.UNCOMMON, mage.cards.s.SaiOfTheShinobi.class)); + cards.add(new SetCardInfo("Self-Assembler", 231, Rarity.COMMON, mage.cards.s.SelfAssembler.class)); + cards.add(new SetCardInfo("Strionic Resonator", 232, Rarity.RARE, mage.cards.s.StrionicResonator.class)); + cards.add(new SetCardInfo("Sundering Titan", 233, Rarity.RARE, mage.cards.s.SunderingTitan.class)); + cards.add(new SetCardInfo("Swiftfoot Boots", 234, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Treasure Keeper", 235, Rarity.UNCOMMON, mage.cards.t.TreasureKeeper.class)); + cards.add(new SetCardInfo("Ash Barrens", 236, Rarity.UNCOMMON, mage.cards.a.AshBarrens.class)); + cards.add(new SetCardInfo("Cascade Bluffs", 237, Rarity.RARE, mage.cards.c.CascadeBluffs.class)); + cards.add(new SetCardInfo("Fetid Heath", 238, Rarity.RARE, mage.cards.f.FetidHeath.class)); + cards.add(new SetCardInfo("Flooded Grove", 239, Rarity.RARE, mage.cards.f.FloodedGrove.class)); + cards.add(new SetCardInfo("Haunted Fengraf", 240, Rarity.COMMON, mage.cards.h.HauntedFengraf.class)); + cards.add(new SetCardInfo("Mikokoro, Center of the Sea", 241, Rarity.RARE, mage.cards.m.MikokoroCenterOfTheSea.class)); + cards.add(new SetCardInfo("Mishra's Factory", 242, Rarity.UNCOMMON, mage.cards.m.MishrasFactory.class)); + cards.add(new SetCardInfo("Myriad Landscape", 243, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Pendelhaven", 244, Rarity.RARE, mage.cards.p.Pendelhaven.class)); + cards.add(new SetCardInfo("Quicksand", 245, Rarity.UNCOMMON, mage.cards.q.Quicksand.class)); + cards.add(new SetCardInfo("Rishadan Port", 246, Rarity.RARE, mage.cards.r.RishadanPort.class)); + cards.add(new SetCardInfo("Rugged Prairie", 247, Rarity.RARE, mage.cards.r.RuggedPrairie.class)); + cards.add(new SetCardInfo("Twilight Mire", 248, Rarity.RARE, mage.cards.t.TwilightMire.class)); + cards.add(new SetCardInfo("Zoetic Cavern", 249, Rarity.UNCOMMON, mage.cards.z.ZoeticCavern.class)); } } diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 02b9c5abf5..9538db973e 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -58,7 +58,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 51; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 104; + private static final long CARD_CONTENT_VERSION = 105; private Dao cardDao; private Set classNames; diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index 9a6faa767d..ea542b580f 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -117,7 +117,7 @@ Magic 2013|M13| Magic 2014|M14| Magic 2015|M15| Core 2019|M19| -Masters 25|M25| +Masters 25|A25| Magic: The Gathering-Commander|CMD| Magic: The Gathering-Conspiracy|CNS| Media Inserts|MBP| From 8cf065e3458400baf560a7a7e8edd117bc7a0748 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 3 Mar 2018 23:51:47 +1100 Subject: [PATCH 097/127] For actual cards on modo, the full background is always printed to it (not just part of the top left corner of the background). --- .../mage/card/arcane/ModernCardRenderer.java | 81 ++++++++++++++---- .../cardrender/background_texture_vehicle.png | Bin 685 -> 19249 bytes 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index b34eab7801..25264f7cde 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -8,8 +8,10 @@ package org.mage.card.arcane; import java.awt.*; import java.awt.font.*; import java.awt.geom.Arc2D; +import java.awt.geom.Area; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -70,6 +72,13 @@ public class ModernCardRenderer extends CardRenderer { BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage()); return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight())); } + + private static BufferedImage loadBackgroundImage(String name) { + URL url = ModernCardRenderer.class.getResource("/cardrender/background_texture_" + name + ".png"); + ImageIcon icon = new ImageIcon(url); + BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage()); + return img; + } private static BufferedImage loadFramePart(String name) { URL url = ModernCardRenderer.class.getResource("/cardrender/" + name + ".png"); @@ -99,6 +108,16 @@ public class ModernCardRenderer extends CardRenderer { public static final Paint BG_TEXTURE_ARTIFACT = loadBackgroundTexture("artifact"); public static final Paint BG_TEXTURE_LAND = loadBackgroundTexture("land"); public static final Paint BG_TEXTURE_VEHICLE = loadBackgroundTexture("vehicle"); + + public static final BufferedImage BG_IMG_WHITE = loadBackgroundImage("white"); + public static final BufferedImage BG_IMG_BLUE = loadBackgroundImage("blue"); + public static final BufferedImage BG_IMG_BLACK = loadBackgroundImage("black"); + public static final BufferedImage BG_IMG_RED = loadBackgroundImage("red"); + public static final BufferedImage BG_IMG_GREEN = loadBackgroundImage("green"); + public static final BufferedImage BG_IMG_GOLD = loadBackgroundImage("gold"); + public static final BufferedImage BG_IMG_ARTIFACT = loadBackgroundImage("artifact"); + public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land"); + public static final BufferedImage BG_IMG_VEHICLE = loadBackgroundImage("vehicle"); public static final BufferedImage FRAME_INVENTION = loadFramePart("invention_frame"); @@ -281,27 +300,30 @@ public class ModernCardRenderer extends CardRenderer { // Just draw a brown rectangle drawCardBack(g); } else { - BufferedImage bufferedImage = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB); - - // Set texture to paint with - g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes())); + BufferedImage bg = getBackgroundImage(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()); + if (bg == null) { + return; + } + int bgw = bg.getWidth(); + int bgh = bg.getHeight(); // Draw main part (most of card) - g.fillRoundRect( - borderWidth, borderWidth, + RoundRectangle2D rr = new RoundRectangle2D.Double(borderWidth, borderWidth, cardWidth - borderWidth * 2, cardHeight - borderWidth * 4 - cornerRadius * 2, cornerRadius - 1, cornerRadius - 1); + Area a = new Area(rr); - // Draw the M15 rounded "swoosh" at the bottom - g.fillRoundRect( - borderWidth, cardHeight - borderWidth * 4 - cornerRadius * 4, + RoundRectangle2D rr2 = new RoundRectangle2D.Double(borderWidth, cardHeight - borderWidth * 4 - cornerRadius * 4, cardWidth - borderWidth * 2, cornerRadius * 4, cornerRadius * 2, cornerRadius * 2); - - // Draw the cutout into the "swoosh" for the textbox to lie over - g.fillRect( - borderWidth + contentInset, cardHeight - borderWidth * 5, - cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); + a.add(new Area(rr2)); + + // Draw the M15 rounded "swoosh" at the bottom + Rectangle r = new Rectangle(borderWidth + contentInset, cardHeight - borderWidth * 5, cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); + a.add(new Area(r)); + g.setClip(a); + g.drawImage(bg, 0, 0, cardWidth, cardHeight, 0, 0, bgw, bgh, BOX_BLUE, null); + g.setClip(null); } } @@ -545,7 +567,7 @@ public class ModernCardRenderer extends CardRenderer { int x2 = x + contentWidth; int y2 = y; int thisy = totalContentInset + boxHeight; - drawZendikarCurvedFace(g, image, x, thisy, x2, y2, + drawZendikarCurvedFace(g, image, x, thisy, x2, y2, boxColor, borderPaint); } else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) { // Draw curved lines (BFZ land style) @@ -1249,7 +1271,34 @@ public class ModernCardRenderer extends CardRenderer { return new Color(71, 86, 101); } } - + + // Determine which background image to use from a set of colors + // and the current card. + protected static BufferedImage getBackgroundImage(ObjectColor colors, Collection types, SubTypeList subTypes) { + if (subTypes.contains(SubType.VEHICLE)) { + return BG_IMG_VEHICLE; + } else if (types.contains(CardType.LAND)) { + return BG_IMG_LAND; + } else if (types.contains(CardType.ARTIFACT)) { + return BG_IMG_ARTIFACT; + } else if (colors.isMulticolored()) { + return BG_IMG_GOLD; + } else if (colors.isWhite()) { + return BG_IMG_WHITE; + } else if (colors.isBlue()) { + return BG_IMG_BLUE; + } else if (colors.isBlack()) { + return BG_IMG_BLACK; + } else if (colors.isRed()) { + return BG_IMG_RED; + } else if (colors.isGreen()) { + return BG_IMG_GREEN; + } else { + // Colorless + return null; + } + } + // Get the box color for the given colors protected Color getBoxColor(ObjectColor colors, Collection types, boolean isNightCard) { if (cardView.isAbility()) { diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_vehicle.png b/Mage.Client/src/main/resources/cardrender/background_texture_vehicle.png index 2f58f34a36174e2509d793d5414726fc8157be6b..87282e34bc8e3e7e5d1a5e9545257de32c40cd3e 100644 GIT binary patch literal 19249 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 XvsaK>K~^c800000NkvXXu0mjf8eAxT From da76c7687da4baa9c2013ba76ccb50bc85c25a62 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 4 Mar 2018 02:17:21 +1100 Subject: [PATCH 098/127] Missing deovid type background --- .../mage/card/arcane/ModernCardRenderer.java | 5 +++-- .../cardrender/background_texture_colorless.png | Bin 0 -> 58324 bytes 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_colorless.png diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 25264f7cde..67e5192061 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -118,7 +118,8 @@ public class ModernCardRenderer extends CardRenderer { public static final BufferedImage BG_IMG_ARTIFACT = loadBackgroundImage("artifact"); public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land"); public static final BufferedImage BG_IMG_VEHICLE = loadBackgroundImage("vehicle"); - + public static final BufferedImage BG_IMG_COLORLESS = loadBackgroundImage("colorless"); + public static final BufferedImage FRAME_INVENTION = loadFramePart("invention_frame"); public static final Color BORDER_WHITE = new Color(216, 203, 188); @@ -1295,7 +1296,7 @@ public class ModernCardRenderer extends CardRenderer { return BG_IMG_GREEN; } else { // Colorless - return null; + return BG_IMG_COLORLESS; } } diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_colorless.png b/Mage.Client/src/main/resources/cardrender/background_texture_colorless.png new file mode 100644 index 0000000000000000000000000000000000000000..9a27821dab6dd62804804b1cbf378dd3d2700fa9 GIT binary patch literal 58324 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 G)&BwicLyy1 literal 0 HcmV?d00001 From 9e001acfe27df103ea7514e9986356770518af92 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:42:43 +0000 Subject: [PATCH 099/127] Implemented Karona's Zealot --- Mage.Sets/src/mage/cards/k/KaronasZealot.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KaronasZealot.java diff --git a/Mage.Sets/src/mage/cards/k/KaronasZealot.java b/Mage.Sets/src/mage/cards/k/KaronasZealot.java new file mode 100644 index 0000000000..6e07f81e71 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaronasZealot.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public class KaronasZealot extends CardImpl { + + public KaronasZealot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Morph {3}{W}{W} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{W}{W}"))); + + // When Karona's Zealot is turned face up, all damage that would be dealt to it this turn is dealt to target creature instead. + Ability ability = new TurnedFaceUpSourceTriggeredAbility(new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE, false) + .setText("all damage that would be dealt to it this turn is dealt to target creature instead")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public KaronasZealot(final KaronasZealot card) { + super(card); + } + + @Override + public KaronasZealot copy() { + return new KaronasZealot(this); + } +} From 0b3822ab7c72675582e8e57ec2f82dd2c11276b0 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:43:44 +0000 Subject: [PATCH 100/127] Implemented Karona's Zealot --- Mage.Sets/src/mage/sets/Masters25.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/Masters25.java b/Mage.Sets/src/mage/sets/Masters25.java index 54c3278628..83b55f6041 100644 --- a/Mage.Sets/src/mage/sets/Masters25.java +++ b/Mage.Sets/src/mage/sets/Masters25.java @@ -71,7 +71,7 @@ public class Masters25 extends ExpansionSet { cards.add(new SetCardInfo("Geist of the Moors", 15, Rarity.COMMON, mage.cards.g.GeistOfTheMoors.class)); cards.add(new SetCardInfo("Gods Willing", 16, Rarity.COMMON, mage.cards.g.GodsWilling.class)); cards.add(new SetCardInfo("Griffin Protector", 17, Rarity.COMMON, mage.cards.g.GriffinProtector.class)); - //cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); + cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); cards.add(new SetCardInfo("Knight of the Skyward Eye", 19, Rarity.COMMON, mage.cards.k.KnightOfTheSkywardEye.class)); cards.add(new SetCardInfo("Kongming, 'Sleeping Dragon'", 20, Rarity.UNCOMMON, mage.cards.k.KongmingSleepingDragon.class)); cards.add(new SetCardInfo("Kor Firewalker", 21, Rarity.UNCOMMON, mage.cards.k.KorFirewalker.class)); From 17c1800cb6a83187ea013e44f444413575afea0c Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:44:16 +0000 Subject: [PATCH 101/127] Implemented Karona's Zealot --- Mage.Sets/src/mage/sets/Scourge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index 8dff799906..a58b86f8ff 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -126,6 +126,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Guilty Conscience", 17, Rarity.COMMON, mage.cards.g.GuiltyConscience.class)); cards.add(new SetCardInfo("Hindering Touch", 37, Rarity.COMMON, mage.cards.h.HinderingTouch.class)); cards.add(new SetCardInfo("Hunting Pack", 121, Rarity.UNCOMMON, mage.cards.h.HuntingPack.class)); + cards.add(new SetCardInfo("Karona's Zealot", 18, Rarity.UNCOMMON, mage.cards.k.KaronasZealot.class)); cards.add(new SetCardInfo("Karona, False God", 138, Rarity.RARE, mage.cards.k.KaronaFalseGod.class)); cards.add(new SetCardInfo("Krosan Drover", 122, Rarity.COMMON, mage.cards.k.KrosanDrover.class)); cards.add(new SetCardInfo("Krosan Warchief", 123, Rarity.UNCOMMON, mage.cards.k.KrosanWarchief.class)); From bb9e438e51b524d58988371fb0a61f0e31847c43 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 3 Mar 2018 23:57:56 +0000 Subject: [PATCH 102/127] Added missing Scarecrow --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 52c8831313..e06cd8e06c 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -241,6 +241,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Sandstorm", 164, Rarity.COMMON, mage.cards.s.Sandstorm.class)); cards.add(new SetCardInfo("Savannah Lions", 24, Rarity.UNCOMMON, mage.cards.s.SavannahLions.class)); cards.add(new SetCardInfo("Savannah", 250, Rarity.RARE, mage.cards.s.Savannah.class)); + cards.add(new SetCardInfo("Scarecrow", 225, Rarity.UNCOMMON, mage.cards.s.Scarecrow.class)); cards.add(new SetCardInfo("Scarwood Bandits", 165, Rarity.RARE, mage.cards.s.ScarwoodBandits.class)); cards.add(new SetCardInfo("Scavenger Folk", 166, Rarity.COMMON, mage.cards.s.ScavengerFolk.class)); cards.add(new SetCardInfo("Scavenging Ghoul", 95, Rarity.UNCOMMON, mage.cards.s.ScavengingGhoul.class)); From be70ecd7f3882c1dee1335f312c51ec1aa44b542 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 4 Mar 2018 17:57:51 +1100 Subject: [PATCH 103/127] Add a singleton search option for deck editor search --- .../mage/client/deckeditor/CardSelector.form | 26 +++++++++++++++++ .../mage/client/deckeditor/CardSelector.java | 27 ++++++++++++++++- .../predicate/other/CardTextPredicate.java | 29 +++++++++++++++++-- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form index e9b349e111..17513f7a0d 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form @@ -645,6 +645,8 @@ + + @@ -659,6 +661,7 @@ + @@ -846,6 +849,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index d23a409824..f6fc566cef 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -232,7 +232,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene FilterCard filter = new FilterCard(); String name = jTextFieldSearch.getText().trim(); - filter.add(new CardTextPredicate(name, chkNames.isSelected(), chkTypes.isSelected(), chkRules.isSelected())); + filter.add(new CardTextPredicate(name, chkNames.isSelected(), chkTypes.isSelected(), chkRules.isSelected(), chkUnique.isSelected())); if (limited) { ArrayList> predicates = new ArrayList<>(); @@ -543,6 +543,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene chkNames = new javax.swing.JCheckBox(); chkTypes = new javax.swing.JCheckBox(); chkRules = new javax.swing.JCheckBox(); + chkUnique = new javax.swing.JCheckBox(); jButtonSearch = new javax.swing.JButton(); jButtonClean = new javax.swing.JButton(); cardCountLabel = new javax.swing.JLabel(); @@ -1062,6 +1063,22 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene chkRulesActionPerformed(evt); } }); + + chkUnique.setSelected(true); + chkUnique.setText("Unique"); + chkUnique.setToolTipText("Singleton results only."); + chkUnique.setFocusable(false); + chkUnique.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); + chkUnique.setMaximumSize(new java.awt.Dimension(69, 16)); + chkUnique.setMinimumSize(new java.awt.Dimension(69, 16)); + chkUnique.setPreferredSize(new java.awt.Dimension(69, 16)); + chkUnique.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + chkUnique.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + chkUniqueActionPerformed(evt); + } + }); + jButtonSearch.setText("Search"); jButtonSearch.setToolTipText("Performs the search."); @@ -1109,6 +1126,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene .addComponent(chkTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(chkRules, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkUnique, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(5, 5, 5) .addComponent(cardCountLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -1122,6 +1141,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene .addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(chkTypes, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(chkRules, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(chkUnique, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(chkNames, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(cardSelectorBottomPanelLayout.createSequentialGroup() .addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1341,6 +1361,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene // TODO add your handling code here: }//GEN-LAST:event_chkRulesActionPerformed + private void chkUniqueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRulesActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_chkRulesActionPerformed + private void btnExpansionSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExpansionSearchActionPerformed FastSearchUtil.showFastSearchForStringComboBox(cbExpansionSet, "Select set or expansion"); }//GEN-LAST:event_btnExpansionSearchActionPerformed @@ -1418,6 +1442,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene private javax.swing.JCheckBox chkPennyDreadful; private javax.swing.JCheckBox chkPiles; private javax.swing.JCheckBox chkRules; + private javax.swing.JCheckBox chkUnique; private javax.swing.JCheckBox chkTypes; private javax.swing.JButton jButtonAddToMain; private javax.swing.JButton jButtonAddToSideboard; diff --git a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java index 36cf356ce4..503d290e05 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java @@ -27,6 +27,7 @@ */ package mage.filter.predicate.other; +import java.util.HashMap; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.SubType; @@ -44,21 +45,38 @@ public class CardTextPredicate implements Predicate { private final boolean inNames; private final boolean inTypes; private final boolean inRules; + private final boolean isUnique; + private HashMap seenCards = null; - public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules) { + public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules, boolean isUnique) { this.text = text; this.inNames = inNames; this.inTypes = inTypes; this.inRules = inRules; + this.isUnique = isUnique; + seenCards = new HashMap<>(); } @Override public boolean apply(Card input, Game game) { - if (text.isEmpty()) { + if (text.isEmpty() && !isUnique) { return true; } + + if (text.isEmpty() && isUnique) { + boolean found = !seenCards.keySet().contains(input.getName()); + seenCards.put(input.getName(), true); + return found; + } + // first check in card name if (inNames && input.getName().toLowerCase().contains(text.toLowerCase())) { + if (isUnique && seenCards.keySet().contains(input.getName())) { + return false; + } + if (isUnique) { + seenCards.put(input.getName(), true); + } return true; } @@ -105,11 +123,18 @@ public class CardTextPredicate implements Predicate { } } } + + if (found && isUnique && seenCards.keySet().contains(input.getName())) { + found = false; + } if (!found) { return false; } } + if (isUnique) { + seenCards.put(input.getName(), true); + } return true; } From d2c39a1339f2033df3689bb2ba1bdf1f1dada173 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:37:22 +0000 Subject: [PATCH 104/127] Included duration for BecomesChosenCreatureTypeTargetEffect --- ...BecomesChosenCreatureTypeTargetEffect.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java index 982f160fb6..ef3759eaa9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java @@ -17,17 +17,24 @@ import mage.target.targetpointer.FixedTarget; public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { private final boolean nonWall; + private final Duration duration; public BecomesChosenCreatureTypeTargetEffect() { - this(false); + this(false, Duration.EndOfTurn); } public BecomesChosenCreatureTypeTargetEffect(boolean nonWall) { + this(nonWall, Duration.EndOfTurn); + } + + public BecomesChosenCreatureTypeTargetEffect(boolean nonWall, Duration duration) { super(Outcome.BoostCreature); this.nonWall = nonWall; - if (nonWall) { + this.duration = duration; + if(nonWall) { staticText = "choose a creature type other than Wall. Target creature becomes that type until end of turn"; - } else { + } + else { staticText = "target creature becomes the creature type of your choice until end of turn"; } @@ -36,6 +43,7 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { public BecomesChosenCreatureTypeTargetEffect(final BecomesChosenCreatureTypeTargetEffect effect) { super(effect); this.nonWall = effect.nonWall; + this.duration = effect.duration; } @Override @@ -46,21 +54,23 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { if (player != null && card != null) { Choice typeChoice = new ChoiceCreatureType(); String msg = "Choose a creature type"; - if (nonWall) { + if(nonWall) { msg += " other than Wall"; } typeChoice.setMessage(msg); - if (nonWall) { + if(nonWall) { typeChoice.getChoices().remove(SubType.WALL.getDescription()); } - if (!player.choose(Outcome.BoostCreature, typeChoice, game)) { - return false; + while (!player.choose(Outcome.BoostCreature, typeChoice, game)) { + if (!player.canRespond()) { + return false; + } } game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice()); chosenType = typeChoice.getChoice(); if (chosenType != null && !chosenType.isEmpty()) { // ADD TYPE TO TARGET - ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(Duration.EndOfTurn, SubType.byDescription(chosenType)); + ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(duration, SubType.byDescription(chosenType)); effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); game.addEffect(effect, source); return true; From 5bd58ca56e3ecbbcb08d10020e6c126798bbc790 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:38:38 +0000 Subject: [PATCH 105/127] Included duration for BecomesChosenCreatureTypeSourceEffect --- .../BecomesChosenCreatureTypeSourceEffect.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java index 56d90e8f21..4ee8f6462e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeSourceEffect.java @@ -3,6 +3,7 @@ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; @@ -10,17 +11,28 @@ import mage.target.targetpointer.FixedTarget; public class BecomesChosenCreatureTypeSourceEffect extends OneShotEffect { + private final boolean nonWall; + private final Duration duration; + public BecomesChosenCreatureTypeSourceEffect() { - this(false); + this(false, Duration.EndOfTurn); } public BecomesChosenCreatureTypeSourceEffect(boolean nonWall) { + this(nonWall, Duration.EndOfTurn); + } + + public BecomesChosenCreatureTypeSourceEffect(boolean nonWall, Duration duration) { super(Outcome.BoostCreature); - staticText = "{this} becomes the creature type of your choice until end of turn."; + this.nonWall = nonWall; + this.duration = duration; + staticText = "{this} becomes the creature type of your choice" + (duration == Duration.EndOfTurn ? " until end of turn." : ""); } public BecomesChosenCreatureTypeSourceEffect(final BecomesChosenCreatureTypeSourceEffect effect) { super(effect); + this.nonWall = effect.nonWall; + this.duration = effect.duration; } @Override @@ -29,7 +41,7 @@ public class BecomesChosenCreatureTypeSourceEffect extends OneShotEffect { if (sourcePerm == null) { return false; } - Effect effect = new BecomesChosenCreatureTypeTargetEffect(); + Effect effect = new BecomesChosenCreatureTypeTargetEffect(nonWall, duration); effect.setTargetPointer(new FixedTarget(sourcePerm, game)); return effect.apply(game, source); } From 4f4028174e112cd53e21c30817e484e43669c17d Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:41:23 +0000 Subject: [PATCH 106/127] Implemented Proteus Machine --- .../src/mage/cards/p/ProteusMachine.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/ProteusMachine.java diff --git a/Mage.Sets/src/mage/cards/p/ProteusMachine.java b/Mage.Sets/src/mage/cards/p/ProteusMachine.java new file mode 100644 index 0000000000..6fbff759cc --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProteusMachine.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.cards.p; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeSourceEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.SubType; + +/** + * + * @author L_J + */ +public class ProteusMachine extends CardImpl { + + public ProteusMachine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + this.subtype.add(SubType.SHAPESHIFTER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Morph {0} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{0}"))); + + // When Proteus Machine is turned face up, it becomes the creature type of your choice. (This effect lasts indefinitely.) + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new BecomesChosenCreatureTypeSourceEffect(false, Duration.Custom))); + } + + public ProteusMachine(final ProteusMachine card) { + super(card); + } + + @Override + public ProteusMachine copy() { + return new ProteusMachine(this); + } +} From 9f6e52296941d507bcb8276657bcbfd986faa3f8 Mon Sep 17 00:00:00 2001 From: L_J Date: Sun, 4 Mar 2018 10:42:00 +0000 Subject: [PATCH 107/127] Implemented Proteus Machine --- Mage.Sets/src/mage/sets/Scourge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index a58b86f8ff..3c5bf9920f 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -146,6 +146,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Parallel Thoughts", 44, Rarity.RARE, mage.cards.p.ParallelThoughts.class)); cards.add(new SetCardInfo("Pemmin's Aura", 45, Rarity.UNCOMMON, mage.cards.p.PemminsAura.class)); cards.add(new SetCardInfo("Primitive Etchings", 126, Rarity.RARE, mage.cards.p.PrimitiveEtchings.class)); + cards.add(new SetCardInfo("Proteus Machine", 141, Rarity.UNCOMMON, mage.cards.p.ProteusMachine.class)); cards.add(new SetCardInfo("Putrid Raptor", 71, Rarity.UNCOMMON, mage.cards.p.PutridRaptor.class)); cards.add(new SetCardInfo("Pyrostatic Pillar", 100, Rarity.UNCOMMON, mage.cards.p.PyrostaticPillar.class)); cards.add(new SetCardInfo("Rain of Blades", 20, Rarity.UNCOMMON, mage.cards.r.RainOfBlades.class)); From a059909fd290eeb9cd4f2e542c50b140f3ae33e8 Mon Sep 17 00:00:00 2001 From: Danny Plenge Date: Tue, 6 Mar 2018 09:52:39 +0100 Subject: [PATCH 108/127] Removed unnecessary file which I accidently added. --- Mage.Client/test.init | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 Mage.Client/test.init diff --git a/Mage.Client/test.init b/Mage.Client/test.init deleted file mode 100644 index 86238c724a..0000000000 --- a/Mage.Client/test.init +++ /dev/null @@ -1,28 +0,0 @@ -[init] -battlefield:Human:Forest:5 -battlefield:Human:Plains:5 -battlefield:Human:Mountain:5 -battlefield:Human:Swamp:5 -battlefield:Human:Island:5 -hand:Human:Lightning Bolt:2 - -[current test] -graveyard:Human:Bloodghast:1 -graveyard:Computer:Bloodghast:1 -hand:Human:Bloodghast:1 -hand:Computer:Bloodghast:1 - -[2x bear to me] -battlefield:Human:Kitesail Corsair:2 - -[2x bear to comp] -battlefield:Computer:Kitesail Corsair:2 -//battlefield:Computer:Grizzly Bears:1 - -// create any useful commands -[clone] -hand:Human:Clone:3 -[force attack] -hand:Human:Pit Fight:3 -[exile] -hand:Human:Angelic Edict:3 \ No newline at end of file From 0fd5c4fba1a3808d2179908252e728d13333dd42 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Tue, 6 Mar 2018 16:48:15 +0100 Subject: [PATCH 109/127] check on play instead of cast, updated ability to check for creature lands --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 73 ++++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 721027f2da..59729ff95b 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -27,6 +27,8 @@ */ package mage.cards.u; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -38,7 +40,8 @@ import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.watchers.common.CastFromHandWatcher; +import mage.watchers.Watcher; +import mage.watchers.common.CreatureWasCastWatcher; /** * @@ -48,7 +51,10 @@ public class UphillBattle extends CardImpl { public UphillBattle(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()), new CastFromHandWatcher()); + Ability tapAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()); + tapAbility.addWatcher(new CreatureWasCastWatcher()); + tapAbility.addWatcher(new PlayCreatureLandWatcher()); + addAbility(tapAbility); } public UphillBattle(final UphillBattle card) { @@ -61,6 +67,58 @@ public class UphillBattle extends CardImpl { } } +class PlayCreatureLandWatcher extends Watcher { + + final Set playerPlayedLand = new HashSet<>(); // player that played land + final Set landPlayed = new HashSet<>(); // land played + + public PlayCreatureLandWatcher() { + super(PlayCreatureLandWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public PlayCreatureLandWatcher(final PlayCreatureLandWatcher watcher) { + super(watcher); + playerPlayedLand.addAll(watcher.playerPlayedLand); + landPlayed.addAll(watcher.landPlayed); + } + + @Override + public PlayCreatureLandWatcher copy() { + return new PlayCreatureLandWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.PLAY_LAND) { + + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent != null + && permanent.isLand() + && permanent.isCreature() + && !playerPlayedLand.contains(event.getPlayerId())) { + playerPlayedLand.add(event.getPlayerId()); + landPlayed.add(event.getTargetId()); + } + } + } + + @Override + public void reset() { + playerPlayedLand.clear(); + landPlayed.clear(); + super.reset(); + } + + public boolean landPlayed(UUID playerId) { + return playerPlayedLand.contains(playerId); + } + + public boolean wasLandPlayed(UUID landId) { + return landPlayed.contains(landId); + } +} + + class UphillBattleTapEffect extends ReplacementEffectImpl { UphillBattleTapEffect() { @@ -71,13 +129,16 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { UphillBattleTapEffect(final UphillBattleTapEffect effect) { super(effect); } - + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getSimpleName()); + CreatureWasCastWatcher creatureSpellWatcher = (CreatureWasCastWatcher) game.getState().getWatchers().get(CreatureWasCastWatcher.class.getSimpleName()); + PlayCreatureLandWatcher landWatcher = (PlayCreatureLandWatcher) game.getState().getWatchers().get(PlayCreatureLandWatcher.class.getSimpleName()); - if (target != null && watcher != null && watcher.spellWasCastFromHand(target.getId())) { + if (target != null + && ((creatureSpellWatcher != null && creatureSpellWatcher.wasCreatureCastThisTurn(target.getId())) + || (landWatcher != null && landWatcher.wasLandPlayed(target.getId())))) { target.setTapped(true); } return false; @@ -92,7 +153,7 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && (permanent.isCreature())) { + if (permanent != null && permanent.isCreature()) { return true; } } From 8576dbea00366c39bedff233975023e9fff18d9f Mon Sep 17 00:00:00 2001 From: Sinsedrix Date: Wed, 7 Mar 2018 09:56:20 +0100 Subject: [PATCH 110/127] Implement Builder's Bane --- Mage.Sets/src/mage/cards/b/BuildersBane.java | 118 +++++++++++++++++++ Mage.Sets/src/mage/sets/Mirage.java | 1 + 2 files changed, 119 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BuildersBane.java diff --git a/Mage.Sets/src/mage/cards/b/BuildersBane.java b/Mage.Sets/src/mage/cards/b/BuildersBane.java new file mode 100644 index 0000000000..896aca49e0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BuildersBane.java @@ -0,0 +1,118 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.b; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author sinsedrix + */ +public class BuildersBane extends CardImpl { + + public BuildersBane(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); + + this.getSpellAbility().addTarget(new TargetArtifactPermanent()); + this.getSpellAbility().addEffect(new BuildersBaneEffect()); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + int xValue = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetArtifactPermanent(xValue, xValue)); + } + } + + public BuildersBane(final BuildersBane card) { + super(card); + } + + @Override + public BuildersBane copy() { + return new BuildersBane(this); + } +} + +class BuildersBaneEffect extends OneShotEffect { + + public BuildersBaneEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Destroy X target artifacts. Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way."; + } + + public BuildersBaneEffect(final BuildersBaneEffect effect) { + super(effect); + } + + @Override + public BuildersBaneEffect copy() { + return new BuildersBaneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map destroyedArtifactPerPlayer = new HashMap<>(); + + // Destroy X target artifacts. + for (UUID targetID : this.targetPointer.getTargets(game, source)) { + Permanent permanent = game.getPermanent(targetID); + if (permanent != null) { + if (permanent.destroy(source.getSourceId(), game, false)) { + if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { + destroyedArtifactPerPlayer.merge(permanent.getControllerId(), 1, Integer::sum); + } + } + } + } + + // Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way. + for (Map.Entry entry : destroyedArtifactPerPlayer.entrySet()) { + Player player = game.getState().getPlayer(entry.getKey()); + player.damage(entry.getValue(), source.getSourceId(), game, false, true); + } + + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/Mirage.java b/Mage.Sets/src/mage/sets/Mirage.java index 27052f442c..5db3aa9154 100644 --- a/Mage.Sets/src/mage/sets/Mirage.java +++ b/Mage.Sets/src/mage/sets/Mirage.java @@ -79,6 +79,7 @@ public class Mirage extends ExpansionSet { cards.add(new SetCardInfo("Boomerang", 56, Rarity.COMMON, mage.cards.b.Boomerang.class)); cards.add(new SetCardInfo("Breathstealer", 7, Rarity.COMMON, mage.cards.b.Breathstealer.class)); cards.add(new SetCardInfo("Brushwagg", 106, Rarity.RARE, mage.cards.b.Brushwagg.class)); + cards.add(new SetCardInfo("Builder's Bane", 160, Rarity.COMMON, mage.cards.b.BuildersBane.class)); cards.add(new SetCardInfo("Burning Palm Efreet", 161, Rarity.UNCOMMON, mage.cards.b.BurningPalmEfreet.class)); cards.add(new SetCardInfo("Burning Shield Askari", 162, Rarity.COMMON, mage.cards.b.BurningShieldAskari.class)); cards.add(new SetCardInfo("Cadaverous Bloom", 318, Rarity.RARE, mage.cards.c.CadaverousBloom.class)); From 9dbd1e8b41fd6881cb6a4b4f188ff9d0188c7501 Mon Sep 17 00:00:00 2001 From: JRHerlehy Date: Wed, 7 Mar 2018 23:26:02 -0800 Subject: [PATCH 111/127] Fix Exhume Set the "notTarget" flag for the ability to allow it to work under cards restricting targeting of cards in graveyards. --- Mage.Sets/src/mage/cards/e/Exhume.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/cards/e/Exhume.java b/Mage.Sets/src/mage/cards/e/Exhume.java index 678d17dddc..a06a50309e 100644 --- a/Mage.Sets/src/mage/cards/e/Exhume.java +++ b/Mage.Sets/src/mage/cards/e/Exhume.java @@ -94,6 +94,7 @@ class ExhumeEffect extends OneShotEffect { FilterCreatureCard filterCreatureCard = new FilterCreatureCard("creature card from your graveyard"); filterCreatureCard.add(new OwnerIdPredicate(playerId)); TargetCardInGraveyard target = new TargetCardInGraveyard(filterCreatureCard); + target.setNotTarget(true); if (target.canChoose(playerId, game) && player.chooseTarget(outcome, target, source, game)) { Card card = game.getCard(target.getFirstTarget()); From 4c7b0d50d2c4cf5f40827b19f34f1b01fef12d86 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 8 Mar 2018 23:31:51 +1100 Subject: [PATCH 112/127] Add a 'spectators allowed' option for regular tables (aka non tournaments). --- .../java/mage/client/dialog/NewTableDialog.form | 11 +++++++++++ .../java/mage/client/dialog/NewTableDialog.java | 14 ++++++++++++-- .../mage/client/dialog/PreferencesDialog.java | 1 + .../java/mage/client/table/TablesPanel.java | 7 +++++-- .../src/main/java/mage/view/TableView.java | 8 ++++++++ .../main/java/mage/game/match/MatchOptions.java | 17 ++++++++++++----- 6 files changed, 49 insertions(+), 9 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form index a5a176504e..5f240b931c 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -41,6 +41,9 @@ + + + @@ -152,6 +155,7 @@ + @@ -274,6 +278,13 @@ + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 10413c6787..337475674e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -102,6 +102,7 @@ public class NewTableDialog extends MageDialog { lblGameType = new javax.swing.JLabel(); cbGameType = new javax.swing.JComboBox(); chkRollbackTurnsAllowed = new javax.swing.JCheckBox(); + chkSpectatorsAllowed = new javax.swing.JCheckBox(); chkRated = new javax.swing.JCheckBox(); lblFreeMulligans = new javax.swing.JLabel(); spnFreeMulligans = new javax.swing.JSpinner(); @@ -151,6 +152,9 @@ public class NewTableDialog extends MageDialog { chkRollbackTurnsAllowed.setText("Allow rollbacks"); chkRollbackTurnsAllowed.setToolTipText("Allow to rollback to the start of previous turns
\nif all players agree.\n"); + chkSpectatorsAllowed.setText("Allow Spectators"); + chkSpectatorsAllowed.setToolTipText("Allow spectators to watch.\n"); + chkRated.setText("Rated"); chkRated.setToolTipText("Indicates if matches will be rated."); @@ -231,7 +235,9 @@ public class NewTableDialog extends MageDialog { .addGap(13, 13, 13) .addComponent(lblFreeMulligans) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(13, 13, 13) + .addComponent(chkSpectatorsAllowed)) .addGroup(layout.createSequentialGroup() .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -325,7 +331,8 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblFreeMulligans) - .addComponent(chkRollbackTurnsAllowed)) + .addComponent(chkRollbackTurnsAllowed) + .addComponent(chkSpectatorsAllowed)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblGameType))) @@ -401,6 +408,7 @@ public class NewTableDialog extends MageDialog { options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem()); options.setWinsNeeded((Integer) this.spnNumWins.getValue()); options.setRollbackTurnsAllowed(chkRollbackTurnsAllowed.isSelected()); + options.setSpectatorsAllowed(chkSpectatorsAllowed.isSelected()); options.setRated(chkRated.isSelected()); options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue()); options.setPassword(this.txtPassword.getText()); @@ -658,6 +666,7 @@ public class NewTableDialog extends MageDialog { } this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS + versionStr, "2"))); this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED + versionStr, "Yes").equals("Yes")); + this.chkSpectatorsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_SPECTATORS_ALLOWED + versionStr, "Yes").equals("Yes")); this.chkRated.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RATED + versionStr, "No").equals("Yes")); this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS + versionStr, "0"))); @@ -739,6 +748,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JComboBox cbSkillLevel; private javax.swing.JComboBox cbTimeLimit; private javax.swing.JCheckBox chkRollbackTurnsAllowed; + private javax.swing.JCheckBox chkSpectatorsAllowed; private javax.swing.JCheckBox chkRated; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 831900e297..869694af4f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -229,6 +229,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TABLE_GAME_TYPE = "newTableGameType"; public static final String KEY_NEW_TABLE_NUMBER_OF_WINS = "newTableNumberOfWins"; public static final String KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED = "newTableRollbackTurnsAllowed"; + public static final String KEY_NEW_TABLE_SPECTATORS_ALLOWED = "newTableSpectatorsAllowed"; public static final String KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS = "newTableNumberOfFreeMulligans"; public static final String KEY_NEW_TABLE_DECK_FILE = "newTableDeckFile"; public static final String KEY_NEW_TABLE_RANGE = "newTableRange"; diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 547282f16d..5c11b3c6dc 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1444,11 +1444,14 @@ class TableTableModel extends AbstractTableModel { if (tables[arg0].isTournament()) { return "Show"; } else { - owner = tables[arg0].getControllerName(); + owner = tables[arg0].getControllerName(); if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) { return ""; } - return "Watch"; + if (tables[arg0].getSpectatorsAllowed()) { + return "Watch"; + } + return ""; } default: return ""; diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java index dd7de79d34..3480328b61 100644 --- a/Mage.Common/src/main/java/mage/view/TableView.java +++ b/Mage.Common/src/main/java/mage/view/TableView.java @@ -65,6 +65,7 @@ public class TableView implements Serializable { private final boolean limited; private final boolean rated; private final boolean passworded; + private final boolean spectatorsAllowed; public TableView(Table table) { this.tableId = table.getId(); @@ -139,6 +140,7 @@ public class TableView implements Serializable { this.limited = table.getMatch().getOptions().isLimited(); this.rated = table.getMatch().getOptions().isRated(); this.passworded = !table.getMatch().getOptions().getPassword().isEmpty(); + this.spectatorsAllowed = table.getMatch().getOptions().isSpectatorsAllowed(); } else { // TOURNAMENT if (table.getTournament().getOptions().getNumberRounds() > 0) { @@ -186,6 +188,7 @@ public class TableView implements Serializable { this.limited = table.getTournament().getOptions().getMatchOptions().isLimited(); this.rated = table.getTournament().getOptions().getMatchOptions().isRated(); this.passworded = !table.getTournament().getOptions().getPassword().isEmpty(); + this.spectatorsAllowed = table.getTournament().getOptions().isWatchingAllowed(); } } @@ -200,6 +203,11 @@ public class TableView implements Serializable { public String getControllerName() { return controllerName; } + + public boolean getSpectatorsAllowed() { + return spectatorsAllowed; + } + public String getGameType() { return gameType; diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java index 5b1f4a6df9..e0dbb153fa 100644 --- a/Mage/src/main/java/mage/game/match/MatchOptions.java +++ b/Mage/src/main/java/mage/game/match/MatchOptions.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.game.match; import mage.constants.MatchTimeLimit; @@ -61,6 +60,7 @@ public class MatchOptions implements Serializable { protected String password; protected SkillLevel skillLevel; protected boolean rollbackTurnsAllowed; + protected boolean spectatorsAllowed; protected int quitRatio; protected int edhPowerLevel; protected boolean rated; @@ -79,8 +79,7 @@ public class MatchOptions implements Serializable { this.multiPlayer = false; this.numSeats = 2; }*/ - - public MatchOptions(String name, String gameType, boolean multiPlayer, int numSeats ) { + public MatchOptions(String name, String gameType, boolean multiPlayer, int numSeats) { this.name = name; this.gameType = gameType; this.password = ""; @@ -178,7 +177,7 @@ public class MatchOptions implements Serializable { public MatchTimeLimit getMatchTimeLimit() { return this.matchTimeLimit; } - + public void setMatchTimeLimit(MatchTimeLimit matchTimeLimit) { this.matchTimeLimit = matchTimeLimit; } @@ -207,6 +206,14 @@ public class MatchOptions implements Serializable { this.rollbackTurnsAllowed = rollbackTurnsAllowed; } + public boolean isSpectatorsAllowed() { + return spectatorsAllowed; + } + + public void setSpectatorsAllowed(boolean spectatorsAllowed) { + this.spectatorsAllowed = spectatorsAllowed; + } + public int getQuitRatio() { return quitRatio; } @@ -214,7 +221,7 @@ public class MatchOptions implements Serializable { public void setQuitRatio(int quitRatio) { this.quitRatio = quitRatio; } - + public int getEdhPowerLevel() { return edhPowerLevel; } From 79d4e7e9b1a7399d74f3c430fc288425b7cd2042 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 8 Mar 2018 23:45:09 +1100 Subject: [PATCH 113/127] Add a 'spectators allowed' option for regular tables (aka non tournaments). --- .../src/main/java/mage/client/dialog/NewTableDialog.form | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form index 5f240b931c..ff7ddd6845 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -281,7 +281,7 @@ - + From 0b8e8c000747f51728ae52eaa2af92f215d37005 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Thu, 8 Mar 2018 16:17:36 +0100 Subject: [PATCH 114/127] Fire PLAY_LAND event when a player plays a land, fixed UphillBattle PlayCreatureLandWatcher Fire a PLAY_LAND event when a player plays a land. This is necessary for the PlayCreatureLandWatcher to know if a land was played, or put into thte battlefield (make distinction between playing Dryad Arbor and using Sneak Attack to put it on the battlefield) --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 10 +++++----- Mage/src/main/java/mage/players/PlayerImpl.java | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 59729ff95b..35064c1075 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -33,6 +33,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -90,11 +91,10 @@ class PlayCreatureLandWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.PLAY_LAND) { - - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent != null - && permanent.isLand() - && permanent.isCreature() + Card card = game.getCard(event.getTargetId()); + if (card != null + && card.isLand() + && card.isCreature() && !playerPlayedLand.contains(event.getPlayerId())) { playerPlayedLand.add(event.getPlayerId()); landPlayed.add(event.getTargetId()); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index b47fab6df9..da939fea99 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1097,6 +1097,8 @@ public abstract class PlayerImpl implements Player, Serializable { if (!ignoreTiming && !playLandAbility.canActivate(this.playerId, game)) { return false; } + + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId)); //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) { // int bookmark = game.bookmarkState(); @@ -1113,7 +1115,7 @@ public abstract class PlayerImpl implements Player, Serializable { // what makes no real sense. So it makes no sense to generally do a restorState here. // restoreState(bookmark, card.getName(), game); } - // if the to play the land is replaced (e.g. Kjeldoran Outpos and don't sacrificing a Plains) it's a valid satte so returning true here + // if the to play the land is replaced (e.g. Kjeldoran Outpos and don't sacrificing a Plains) it's a valid state so returning true here return true; } From de61e10a906d6accc50afa490629eae603892377 Mon Sep 17 00:00:00 2001 From: Sinsedrix Date: Thu, 8 Mar 2018 22:07:27 +0100 Subject: [PATCH 115/127] Implemented: Builder's Bane from Mirage --- Mage.Sets/src/mage/cards/b/BuildersBane.java | 239 ++++++++++--------- 1 file changed, 121 insertions(+), 118 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BuildersBane.java b/Mage.Sets/src/mage/cards/b/BuildersBane.java index 896aca49e0..2dc7950731 100644 --- a/Mage.Sets/src/mage/cards/b/BuildersBane.java +++ b/Mage.Sets/src/mage/cards/b/BuildersBane.java @@ -1,118 +1,121 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.cards.b; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetArtifactPermanent; - -/** - * - * @author sinsedrix - */ -public class BuildersBane extends CardImpl { - - public BuildersBane(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); - - this.getSpellAbility().addTarget(new TargetArtifactPermanent()); - this.getSpellAbility().addEffect(new BuildersBaneEffect()); - } - - @Override - public void adjustTargets(Ability ability, Game game) { - if (ability instanceof SpellAbility) { - ability.getTargets().clear(); - int xValue = ability.getManaCostsToPay().getX(); - ability.addTarget(new TargetArtifactPermanent(xValue, xValue)); - } - } - - public BuildersBane(final BuildersBane card) { - super(card); - } - - @Override - public BuildersBane copy() { - return new BuildersBane(this); - } -} - -class BuildersBaneEffect extends OneShotEffect { - - public BuildersBaneEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "Destroy X target artifacts. Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way."; - } - - public BuildersBaneEffect(final BuildersBaneEffect effect) { - super(effect); - } - - @Override - public BuildersBaneEffect copy() { - return new BuildersBaneEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Map destroyedArtifactPerPlayer = new HashMap<>(); - - // Destroy X target artifacts. - for (UUID targetID : this.targetPointer.getTargets(game, source)) { - Permanent permanent = game.getPermanent(targetID); - if (permanent != null) { - if (permanent.destroy(source.getSourceId(), game, false)) { - if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { - destroyedArtifactPerPlayer.merge(permanent.getControllerId(), 1, Integer::sum); - } - } - } - } - - // Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way. - for (Map.Entry entry : destroyedArtifactPerPlayer.entrySet()) { - Player player = game.getState().getPlayer(entry.getKey()); - player.damage(entry.getValue(), source.getSourceId(), game, false, true); - } - - return true; - } -} +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.b; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author sinsedrix + */ +public class BuildersBane extends CardImpl { + + public BuildersBane(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); + + // Destroy X target artifacts. Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way. + this.getSpellAbility().addTarget(new TargetArtifactPermanent()); + this.getSpellAbility().addEffect(new BuildersBaneEffect()); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + int xValue = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetArtifactPermanent(xValue, xValue)); + } + } + + public BuildersBane(final BuildersBane card) { + super(card); + } + + @Override + public BuildersBane copy() { + return new BuildersBane(this); + } +} + +class BuildersBaneEffect extends OneShotEffect { + + public BuildersBaneEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Destroy X target artifacts. {this} deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way"; + } + + public BuildersBaneEffect(final BuildersBaneEffect effect) { + super(effect); + } + + @Override + public BuildersBaneEffect copy() { + return new BuildersBaneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map destroyedArtifactPerPlayer = new HashMap<>(); + + // Destroy X target artifacts. + for (UUID targetID : this.targetPointer.getTargets(game, source)) { + Permanent permanent = game.getPermanent(targetID); + if (permanent != null) { + if (permanent.destroy(source.getSourceId(), game, false)) { + if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { + destroyedArtifactPerPlayer.merge(permanent.getControllerId(), 1, Integer::sum); + } + } + } + } + + // Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way. + for (Map.Entry entry : destroyedArtifactPerPlayer.entrySet()) { + Player player = game.getPlayer(entry.getKey()); + if(player != null) { + player.damage(entry.getValue(), source.getSourceId(), game, false, true); + } + } + + return true; + } +} From 82e8606b40f1dd84f5503e7aa7ea58c75c8eeba6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 8 Mar 2018 23:26:55 +0100 Subject: [PATCH 116/127] * Added Master 25 to Wizard download source. --- .../mage/plugins/card/dl/sources/WizardCardsImageSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java index f13d7fbf1c..db32e1f151 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java @@ -268,7 +268,7 @@ public enum WizardCardsImageSource implements CardImageSource { supportedSets.add("V17"); // From the Vault: Transform supportedSets.add("UST"); // Unstable supportedSets.add("RIX"); // Rivals of Ixalan -// supportedSets.add("A25"); // Masters 25 + supportedSets.add("A25"); // Masters 25 // supportedSets.add("DOM"); // Dominaria // supportedSets.add("M19"); // Core 2019 From 15602cdfb3b4e07eb78f241ec9e0fa0b425d74c2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 8 Mar 2018 23:27:19 +0100 Subject: [PATCH 117/127] Small change to Uphill Battle. --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 11 ++++++----- Mage/src/main/java/mage/game/permanent/Permanent.java | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 35064c1075..86ed97b472 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -49,9 +49,11 @@ import mage.watchers.common.CreatureWasCastWatcher; * @author chrvanorle */ public class UphillBattle extends CardImpl { - + public UphillBattle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + // Creatures played by your opponents enter the battlefield tapped. Ability tapAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()); tapAbility.addWatcher(new CreatureWasCastWatcher()); tapAbility.addWatcher(new PlayCreatureLandWatcher()); @@ -118,7 +120,6 @@ class PlayCreatureLandWatcher extends Watcher { } } - class UphillBattleTapEffect extends ReplacementEffectImpl { UphillBattleTapEffect() { @@ -129,13 +130,13 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { UphillBattleTapEffect(final UphillBattleTapEffect effect) { super(effect); } - + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); CreatureWasCastWatcher creatureSpellWatcher = (CreatureWasCastWatcher) game.getState().getWatchers().get(CreatureWasCastWatcher.class.getSimpleName()); PlayCreatureLandWatcher landWatcher = (PlayCreatureLandWatcher) game.getState().getWatchers().get(PlayCreatureLandWatcher.class.getSimpleName()); - + if (target != null && ((creatureSpellWatcher != null && creatureSpellWatcher.wasCreatureCastThisTurn(target.getId())) || (landWatcher != null && landWatcher.wasLandPlayed(target.getId())))) { diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 8700e560df..8b9273e824 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -59,7 +59,6 @@ public interface Permanent extends Card, Controllable { * @param tapped * @deprecated */ - @Deprecated void setTapped(boolean tapped); boolean canTap(); From a452c6650e2e925e45ef012f78adc2056f902607 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 8 Mar 2018 16:28:08 -0600 Subject: [PATCH 118/127] - Added requested card Morality Shift. --- Mage.Sets/src/mage/cards/m/MoralityShift.java | 99 +++++++++++++++++++ Mage.Sets/src/mage/sets/Judgment.java | 1 + 2 files changed, 100 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MoralityShift.java diff --git a/Mage.Sets/src/mage/cards/m/MoralityShift.java b/Mage.Sets/src/mage/cards/m/MoralityShift.java new file mode 100644 index 0000000000..23ab1dea3a --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoralityShift.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.List; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public class MoralityShift extends CardImpl { + + public MoralityShift(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}"); + + + // Exchange your graveyard and library. Then shuffle your library. + this.getSpellAbility().addEffect(new MoralityShiftEffect()); + + } + + public MoralityShift(final MoralityShift card) { + super(card); + } + + @Override + public MoralityShift copy() { + return new MoralityShift(this); + } +} + +class MoralityShiftEffect extends OneShotEffect { + + MoralityShiftEffect() { + super(Outcome.AIDontUseIt); + staticText = "Exchange your graveyard and library. Then shuffle your library."; + } + + MoralityShiftEffect(MoralityShiftEffect effect) { + super(effect); + } + + @Override + public MoralityShiftEffect copy() { + return new MoralityShiftEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + List copyLibrary = controller.getLibrary().getCards(game); + Set copyGraveyard = controller.getGraveyard().getCards(game); + controller.getLibrary().clear(); + controller.getGraveyard().clear(); + controller.getGraveyard().addAll(copyLibrary); + controller.getLibrary().addAll(copyGraveyard, game); + controller.shuffleLibrary(source, game); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Judgment.java b/Mage.Sets/src/mage/sets/Judgment.java index fa99c1a1b3..3ba00a1bfa 100644 --- a/Mage.Sets/src/mage/sets/Judgment.java +++ b/Mage.Sets/src/mage/sets/Judgment.java @@ -130,6 +130,7 @@ public class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Mirari's Wake", 139, Rarity.RARE, mage.cards.m.MirarisWake.class)); cards.add(new SetCardInfo("Mirror Wall", 47, Rarity.COMMON, mage.cards.m.MirrorWall.class)); cards.add(new SetCardInfo("Mist of Stagnation", 48, Rarity.RARE, mage.cards.m.MistOfStagnation.class)); + cards.add(new SetCardInfo("Morality Shift", 70, Rarity.RARE, mage.cards.m.MoralityShift.class)); cards.add(new SetCardInfo("Nantuko Monastery", 142, Rarity.UNCOMMON, mage.cards.n.NantukoMonastery.class)); cards.add(new SetCardInfo("Nantuko Tracer", 125, Rarity.COMMON, mage.cards.n.NantukoTracer.class)); cards.add(new SetCardInfo("Nomad Mythmaker", 15, Rarity.RARE, mage.cards.n.NomadMythmaker.class)); From 51c68842aab0eb80d50304b20e5068f70a4ba7f9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 8 Mar 2018 23:40:55 +0100 Subject: [PATCH 119/127] Some minor changes to JSON game log (#4587). --- .../client/remote/CallbackClientImpl.java | 7 +- .../java/mage/client/remote/S3Uploader.java | 16 ++- .../src/main/java/mage/remote/ActionData.java | 107 +++++++++--------- .../src/main/java/mage/remote/Session.java | 5 +- .../main/java/mage/remote/SessionImpl.java | 13 +-- 5 files changed, 68 insertions(+), 80 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index f16b0bee0e..758758e7ae 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -31,11 +31,7 @@ import java.awt.event.KeyEvent; import java.util.List; import java.util.UUID; import javax.swing.*; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import mage.cards.decks.Deck; -import mage.client.remote.S3Uploader; import mage.client.MageFrame; import mage.client.SessionHandler; import mage.client.chat.ChatPanelBasic; @@ -53,7 +49,6 @@ import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.ClientCallback; import mage.remote.ActionData; import mage.remote.Session; -import mage.remote.SessionImpl; import mage.utils.CompressUtil; import mage.view.*; import mage.view.ChatMessage.MessageType; @@ -410,6 +405,7 @@ public class CallbackClientImpl implements CallbackClient { } }); } + private ActionData appendJsonEvent(String name, UUID gameId, Object value) { Session session = SessionHandler.getSession(); ActionData actionData = new ActionData(name, gameId); @@ -417,6 +413,7 @@ public class CallbackClientImpl implements CallbackClient { session.appendJsonLog(actionData); return actionData; } + private void createChatStartMessage(ChatPanelBasic chatPanel) { chatPanel.setStartMessageDone(true); ChatPanelBasic usedPanel = chatPanel; diff --git a/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java b/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java index 21e9504f41..35a1b538ce 100644 --- a/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java +++ b/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java @@ -1,27 +1,26 @@ package mage.client.remote; -import java.io.File; - import com.amazonaws.AmazonClientException; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.s3.transfer.TransferManager; import com.amazonaws.services.s3.transfer.Upload; +import java.io.File; import org.apache.log4j.Logger; -import javax.xml.crypto.Data; - - public class S3Uploader { + private static final Logger logger = Logger.getLogger(S3Uploader.class); public static Boolean upload(String filePath, String keyName) throws Exception { String existingBucketName = System.getenv("S3_BUCKET") != null ? System.getenv("S3_BUCKET") - : "xmage-game-logs-dev"; + : "xmage-game-logs-dev"; String accessKeyId = System.getenv("AWS_ACCESS_ID"); String secretKeyId = System.getenv("AWS_SECRET_KEY"); - if(accessKeyId == "" || secretKeyId == "" || existingBucketName == "") { + if (accessKeyId == null || "".equals(accessKeyId) + || secretKeyId == null || "".equals(secretKeyId) + || existingBucketName == null || "".equals(existingBucketName)) { logger.info("Aborting json log sync."); return false; } @@ -39,8 +38,7 @@ public class S3Uploader { new File(path); return true; } catch (AmazonClientException amazonClientException) { - System.out.println("Unable to upload file, upload was aborted."); - amazonClientException.printStackTrace(); + logger.fatal("Unable to upload file, upload was aborted.", amazonClientException); return false; } } diff --git a/Mage.Common/src/main/java/mage/remote/ActionData.java b/Mage.Common/src/main/java/mage/remote/ActionData.java index d173535b14..51d079a0ac 100644 --- a/Mage.Common/src/main/java/mage/remote/ActionData.java +++ b/Mage.Common/src/main/java/mage/remote/ActionData.java @@ -25,20 +25,17 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.remote; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import mage.remote.interfaces.*; - -import java.util.UUID; - -import com.google.gson.annotations.Expose; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; +import java.util.UUID; public class ActionData { + @Expose public UUID gameId; @Expose @@ -69,57 +66,59 @@ public class ActionData { } public class CustomExclusionStrategy implements ExclusionStrategy { + // FIXME: Very crude way of whitelisting, as it applies to all levels of the JSON tree. - private final java.util.Set KEEP = new java.util.HashSet( + private final java.util.Set KEEP = new java.util.HashSet<>( java.util.Arrays.asList( new String[]{ - "id", - "choice", - "damage", - "abilityType", - "ability", - "abilities", - "method", - "data", - "options", - "life", - "players", - "zone", - "step", - "phase", - "attackers", - "blockers", - "tapped", - "damage", - "combat", - "paid", - "hand", - "stack", - "convertedManaCost", - "gameId", - "canPlayInHand", - "gameView", - "sessionId", - "power", - "choices", - "targets", - "loyalty", - "toughness", - "power", - "type", - "priorityTime", - "manaCost", - "value", - "message", - "cardsView", - "name", - "count", - "counters", - "battlefield", - "parentId" + "id", + "choice", + "damage", + "abilityType", + "ability", + "abilities", + "method", + "data", + "options", + "life", + "players", + "zone", + "step", + "phase", + "attackers", + "blockers", + "tapped", + "damage", + "combat", + "paid", + "hand", + "stack", + "convertedManaCost", + "gameId", + "canPlayInHand", + "gameView", + "sessionId", + "power", + "choices", + "targets", + "loyalty", + "toughness", + "power", + "type", + "priorityTime", + "manaCost", + "value", + "message", + "cardsView", + "name", + "count", + "counters", + "battlefield", + "parentId" })); - public CustomExclusionStrategy() {} + public CustomExclusionStrategy() { + } // This method is called for all fields. if the method returns true the // field is excluded from serialization diff --git a/Mage.Common/src/main/java/mage/remote/Session.java b/Mage.Common/src/main/java/mage/remote/Session.java index 67022d59e8..e7f04049ca 100644 --- a/Mage.Common/src/main/java/mage/remote/Session.java +++ b/Mage.Common/src/main/java/mage/remote/Session.java @@ -24,8 +24,7 @@ * 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.remote; import mage.remote.interfaces.ChatSession; @@ -38,7 +37,6 @@ import mage.remote.interfaces.PlayerActions; import mage.remote.interfaces.Replays; import mage.remote.interfaces.ServerState; import mage.remote.interfaces.Testable; -import mage.remote.ActionData; /** * Extracted interface for SessionImpl class. @@ -46,5 +44,6 @@ import mage.remote.ActionData; * @author noxx */ public interface Session extends ClientData, Connect, GamePlay, GameTypes, ServerState, ChatSession, Feedback, PlayerActions, Replays, Testable { + public void appendJsonLog(ActionData actionData); } diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 907de603b0..318e628f38 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -27,16 +27,14 @@ */ package mage.remote; +import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; +import java.io.PrintWriter; import java.lang.reflect.UndeclaredThrowableException; import java.net.*; import java.util.*; import java.util.concurrent.TimeUnit; -import java.io.BufferedWriter; -import java.io.PrintWriter; -import java.io.FileWriter; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import mage.MageException; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; @@ -55,7 +53,6 @@ import mage.interfaces.callback.ClientCallback; import mage.players.PlayerType; import mage.players.net.UserData; import mage.utils.CompressUtil; -import mage.remote.ActionData; import mage.view.*; import org.apache.log4j.Logger; import org.jboss.remoting.*; @@ -896,10 +893,8 @@ public class SessionImpl implements Session { @Override public void appendJsonLog(ActionData actionData) { actionData.sessionId = getSessionId(); - String logFileName = "game-" + actionData.gameId + ".json"; - System.out.println("Logging to " + logFileName); - try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logFileName, true)))) { + try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logFileName, true)))) { out.println(actionData.toJson()); } catch (IOException e) { System.err.println(e); From fcc5c7524364ac57f90a14fce014db139a897fd9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 8 Mar 2018 23:48:15 +0100 Subject: [PATCH 120/127] Fixed target pointer handling of No Mercy (#4574). --- Mage.Sets/src/mage/cards/n/NoMercy.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/n/NoMercy.java b/Mage.Sets/src/mage/cards/n/NoMercy.java index 59815c6adb..7c4d0079f4 100644 --- a/Mage.Sets/src/mage/cards/n/NoMercy.java +++ b/Mage.Sets/src/mage/cards/n/NoMercy.java @@ -29,7 +29,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -48,7 +47,7 @@ import mage.target.targetpointer.FixedTarget; public class NoMercy extends CardImpl { public NoMercy(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); // Whenever a creature deals damage to you, destroy it. this.addAbility(new NoMercyTriggeredAbility()); @@ -88,9 +87,7 @@ public class NoMercy extends CardImpl { if (event.getPlayerId().equals(this.getControllerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isCreature()) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } } From 0c0e1804f1c59f857af425c2bca5d7d2c6ee5c5d Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 9 Mar 2018 23:58:05 +1100 Subject: [PATCH 121/127] Mox Lotus (UNH) --- Mage.Sets/src/mage/cards/m/MoxLotus.java | 70 +++++++++++++++++++ Mage.Sets/src/mage/sets/Unhinged.java | 2 + Mage/src/main/java/mage/Mana.java | 35 ++++++++-- .../abilities/mana/SimpleManaAbility.java | 2 +- Utils/mtg-cards-data.txt | 1 + 5 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/m/MoxLotus.java diff --git a/Mage.Sets/src/mage/cards/m/MoxLotus.java b/Mage.Sets/src/mage/cards/m/MoxLotus.java new file mode 100644 index 0000000000..ced2a724b6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoxLotus.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.cards.m; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AddManaOfAnyColorEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +/** + * + * @author spjspj/psjpsj + */ +public class MoxLotus extends CardImpl { + + public MoxLotus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{15}"); + + // {t}: Add infinity (or 1*10^9 to account for a potential mana reflection) to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1000000000), new TapSourceCost())); + + // {100}: Add one mana of any color to your mana pool. + Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1), new ManaCostsImpl("{100}")); + this.addAbility(ability); + + // You don't lose life due to mana burn. + // Situation normal?? + } + + public MoxLotus(final MoxLotus card) { + super(card); + } + + @Override + public MoxLotus copy() { + return new MoxLotus(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Unhinged.java b/Mage.Sets/src/mage/sets/Unhinged.java index 222281febe..b4e4aedb0d 100644 --- a/Mage.Sets/src/mage/sets/Unhinged.java +++ b/Mage.Sets/src/mage/sets/Unhinged.java @@ -20,10 +20,12 @@ public class Unhinged extends ExpansionSet { private Unhinged() { super("Unhinged", "UNH", ExpansionSet.buildDate(2004, 11, 20), SetType.JOKESET); + cards.add(new SetCardInfo("Forest", 140, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Island", 137, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Johnny, Combo Player", 35, Rarity.RARE, mage.cards.j.JohnnyComboPlayer.class)); cards.add(new SetCardInfo("Mountain", 139, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Mox Lotus", 124, Rarity.RARE, mage.cards.m.MoxLotus.class)); cards.add(new SetCardInfo("Plains", 136, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Swamp", 138, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); } diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index 0e9aaf2825..32e57e8d45 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -518,25 +518,46 @@ public class Mana implements Comparable, Serializable, Copyable { if (generic > 0) { sbMana.append('{').append(Integer.toString(generic)).append('}'); } - for (int i = 0; i < colorless; i++) { + if (colorless >= 20) { + sbMana.append(Integer.toString(colorless)).append("{C}"); + } + if (white >= 20) { + sbMana.append(Integer.toString(white)).append("{W}"); + } + if (blue >= 20) { + sbMana.append(Integer.toString(blue)).append("{U}"); + } + if (black >= 20) { + sbMana.append(Integer.toString(black)).append("{B}"); + } + if (red >= 20) { + sbMana.append(Integer.toString(red)).append("{R}"); + } + if (green >= 20) { + sbMana.append(Integer.toString(green)).append("{G}"); + } + if (any >= 20) { + sbMana.append(Integer.toString(any)).append("{Any}"); + } + for (int i = 0; i < colorless && colorless < 20; i++) { sbMana.append("{C}"); } - for (int i = 0; i < white; i++) { + for (int i = 0; i < white && white < 20; i++) { sbMana.append("{W}"); } - for (int i = 0; i < blue; i++) { + for (int i = 0; i < blue && blue < 20; i++) { sbMana.append("{U}"); } - for (int i = 0; i < black; i++) { + for (int i = 0; i < black && black < 20; i++) { sbMana.append("{B}"); } - for (int i = 0; i < red; i++) { + for (int i = 0; i < red && red < 20; i++) { sbMana.append("{R}"); } - for (int i = 0; i < green; i++) { + for (int i = 0; i < green && green < 20; i++) { sbMana.append("{G}"); } - for (int i = 0; i < any; i++) { + for (int i = 0; i < any && any < 20; i++) { sbMana.append("{Any}"); } return sbMana.toString(); diff --git a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java index 093dedb048..0163abd7a6 100644 --- a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java @@ -53,7 +53,7 @@ public class SimpleManaAbility extends ActivatedManaAbilityImpl { * @param zone * @param effect * @param cost - * @param predictable set to false if definig the mana type or amount needs + * @param predictable set to false if defining the mana type or amount needs * to reveal information and can't be predicted */ public SimpleManaAbility(Zone zone, ManaEffect effect, Cost cost, boolean predictable) { diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 5ffce6bc34..f17930b11c 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -33077,3 +33077,4 @@ Urza's Science Fair Project|Unglued|83|U|{6}|Artifact Creature - Construct|4|4| Snow Mercy|Happy Holidays|10|R|{2}{W}{W}|Snow Enchantment|||Whenever a creature deals damage to you, put a globe counter on it.${t},{untap},{t},{untap},{t}: Tap all creatures with globe counters on them.| Fruitcake Elemental|Happy Holidays|6|R|{1}{G}{G}|Creature - Elemental|7|7|Fruitcake Elemental is indestructible.$At the end of your turn, Fruitcake Elemental deals 7 damage to you.${3}: Target player gains control of Fruitcake Elemental.| Season's Beatings|Happy Holidays|9|R|{R}{R}{R}{R}|Sorcery|||Family gathering - Each creature target player controls deals damage equal to its power to another random creature that player controls.| +Mox Lotus|Unhinged|124|R|{15}|Artifact|||{t}: Add infinity to your mana pool.${100}: Add one mana of any color to your mana pool.&You don't lose life due to mana burn.| From 17afa3b3c13cc1e7dd52509ff9f4aeee4860844f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 9 Mar 2018 15:51:31 +0100 Subject: [PATCH 122/127] * Added a test. --- .../CastSplitCardsFromOtherZonesTest.java | 31 +++++++++++++++++++ .../java/mage/abilities/effects/Effect.java | 22 +++++++++++++ 2 files changed, 53 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java index fcec6f5e2c..d6430520c3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java @@ -124,4 +124,35 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Icy Manipulator", 1); } + + /** + * Cast a split card half from exile + */ + @Test + public void testCastSpliHalfFromExile() { + // Fire Instant {1}{R} + // Fire deals 2 damage divided as you choose among one or two target creatures and/or players. + // Ice Instant {1}{U} + // Tap target permanent. + // Draw a card. + addCard(Zone.LIBRARY, playerA, "Fire // Ice", 1); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // Creature 2/2 + + // Whenever Etali, Primal Storm attacks, exile the top card of each player's library, then you may + // cast any number of nonland cards exiled this way without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerB, "Etali, Primal Storm"); // Creature {4}{R} 6/6 + + attack(2, playerB, "Etali, Primal Storm"); + setChoice(playerB, "Yes"); + setChoice(playerB, "Cast Fire"); + addTarget(playerB, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 14); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, "Fire // Ice", 1); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/Effect.java b/Mage/src/main/java/mage/abilities/effects/Effect.java index fc98f40a94..cc7d51c7b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effect.java +++ b/Mage/src/main/java/mage/abilities/effects/Effect.java @@ -46,12 +46,34 @@ public interface Effect extends Serializable { void newId(); + /** + * Some general behaviours for rule text handling: Rule text of effects get + * automatically a full stop "." at the end, if not another effect e.g. with + * a starting "and" follows. So at least for effects of the framework, that + * are used from multiple cards, it's better to set no full stop at the end + * of the rule text of an effect. Also the starting letter of an effect is + * automatically converted to upper case if the rule text starts with this + * text. So There is no need to let the effect text start with upper case, + * even if extracted from a filter message. Also here it's important to use + * only lower cases for effects located in the framework, so if used for a + * triggered abilitiy, the effect text needs to start with lower case after + * the comma. + * + * @param mode the selected mode of the ability (mostly there is only one) + * @return + */ String getText(Mode mode); Effect setText(String staticText); boolean apply(Game game, Ability source); + /** + * The outcome is used for the AI to decide if an effect does bad or good to + * the targets. + * + * @return + */ Outcome getOutcome(); void setOutcome(Outcome outcome); From 9fa63b93ea72f5408fd78a991778cf27176d298f Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 9 Mar 2018 17:36:18 +0000 Subject: [PATCH 123/127] Fixed Dread (#4574) --- Mage.Sets/src/mage/cards/d/Dread.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/Dread.java b/Mage.Sets/src/mage/cards/d/Dread.java index d1bdce0952..a2ec93af04 100644 --- a/Mage.Sets/src/mage/cards/d/Dread.java +++ b/Mage.Sets/src/mage/cards/d/Dread.java @@ -31,7 +31,6 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.FearAbility; @@ -105,11 +104,7 @@ class DreadTriggeredAbility extends TriggeredAbilityImpl { if (event.getPlayerId().equals(this.getControllerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isCreature()) { - for (Effect effect : this.getEffects()) { - if (effect instanceof DestroyTargetEffect) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } - } + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } } From 4e02921f42b1e52b2f3df251d7c40c509afc40ff Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 9 Mar 2018 18:46:20 +0000 Subject: [PATCH 124/127] Pandemonium fix Made the entering creature the source of the damage instead of Pandemonium itself --- Mage.Sets/src/mage/cards/p/Pandemonium.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/Pandemonium.java b/Mage.Sets/src/mage/cards/p/Pandemonium.java index 433002b154..38802297f5 100644 --- a/Mage.Sets/src/mage/cards/p/Pandemonium.java +++ b/Mage.Sets/src/mage/cards/p/Pandemonium.java @@ -107,11 +107,11 @@ class PandemoniumEffect extends OneShotEffect { if (enteringCreature != null) { Permanent targetPermanent = game.getPermanent(source.getTargets().getFirstTarget()); if (targetPermanent != null) { - targetPermanent.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true); + targetPermanent.damage(enteringCreature.getPower().getValue(), enteringCreature.getId(), game, false, true); } else { Player targetPlayer = game.getPlayer(source.getTargets().getFirstTarget()); if (targetPlayer != null) { - targetPlayer.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true); + targetPlayer.damage(enteringCreature.getPower().getValue(), enteringCreature.getId(), game, false, true); } } return true; From c6bff105c61e1c498d3ebe6e4523a0a5dc6968a2 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 9 Mar 2018 20:43:05 +0000 Subject: [PATCH 125/127] Hot Soup fix (#4574) --- Mage.Sets/src/mage/cards/h/HotSoup.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HotSoup.java b/Mage.Sets/src/mage/cards/h/HotSoup.java index 149fc23d68..969b778c72 100644 --- a/Mage.Sets/src/mage/cards/h/HotSoup.java +++ b/Mage.Sets/src/mage/cards/h/HotSoup.java @@ -104,10 +104,7 @@ class HotSoupTriggeredAbility extends TriggeredAbilityImpl { Permanent equipment = game.getPermanent(this.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { if (Objects.equals(event.getTargetId(), equipment.getAttachedTo())) { - for(Effect effect : this.getEffects()) - { - effect.setTargetPointer(new FixedTarget(equipment.getAttachedTo())); - } + this.getEffects().setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game)); return true; } } @@ -118,4 +115,4 @@ class HotSoupTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever equipped creature is dealt damage, " + super.getRule(); } -} \ No newline at end of file +} From dc5220df4f9fa577ab9fbd189eb90d49f9c6c100 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 Mar 2018 15:15:11 -0600 Subject: [PATCH 126/127] - Fixed Archwing Dragon. #4581 --- Mage.Sets/src/mage/cards/a/ArchwingDragon.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ArchwingDragon.java b/Mage.Sets/src/mage/cards/a/ArchwingDragon.java index c2f8751e51..1067f47a1a 100644 --- a/Mage.Sets/src/mage/cards/a/ArchwingDragon.java +++ b/Mage.Sets/src/mage/cards/a/ArchwingDragon.java @@ -29,7 +29,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.events.GameEvent; +import mage.constants.TargetController; /** * @@ -46,7 +46,7 @@ import mage.game.events.GameEvent; public class ArchwingDragon extends CardImpl { public ArchwingDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); this.subtype.add(SubType.DRAGON); this.power = new MageInt(4); @@ -54,8 +54,10 @@ public class ArchwingDragon extends CardImpl { this.addAbility(FlyingAbility.getInstance()); this.addAbility(HasteAbility.getInstance()); + // At the beginning of the end step, return Archwing Dragon to its owner's hand. - this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", new ReturnToHandSourceEffect(true), false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ReturnToHandSourceEffect(true), TargetController.ANY, false)); + } public ArchwingDragon(final ArchwingDragon card) { From 823389a21a72870b8ff6c1c64e3efd5fd15fc90c Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 Mar 2018 15:34:55 -0600 Subject: [PATCH 127/127] - Fixed Heat Shimmer. #4576 --- Mage.Sets/src/mage/cards/h/HeatShimmer.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HeatShimmer.java b/Mage.Sets/src/mage/cards/h/HeatShimmer.java index 68967ad6ab..80fb54d109 100644 --- a/Mage.Sets/src/mage/cards/h/HeatShimmer.java +++ b/Mage.Sets/src/mage/cards/h/HeatShimmer.java @@ -29,10 +29,9 @@ package mage.cards.h; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; -import mage.abilities.effects.Effect; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.cards.CardImpl; @@ -52,7 +51,7 @@ import mage.target.targetpointer.FixedTarget; public class HeatShimmer extends CardImpl { public HeatShimmer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Create a token that's a copy of target creature. That token has haste and "At the beginning of the end step, exile this permanent." this.getSpellAbility().addEffect(new HeatShimmerEffect()); @@ -89,15 +88,15 @@ class HeatShimmerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (controller != null && permanent != null) { + if (controller != null + && permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { - Effect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); - new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfTurnStepPostDelayedTriggeredAbility(exileEffect), false).apply(game, source); - } + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + game.addDelayedTriggeredAbility(delayedAbility, source); return true; } return false;