Merge pull request #1118 from glerman/master

Chronozoa + Dust of Moments cards
This commit is contained in:
LevelX2 2015-07-15 23:15:52 +02:00
commit 88d6d0fa0f
12 changed files with 774 additions and 62 deletions

View file

@ -1,20 +0,0 @@
#
# Rename this file to init.txt if you want to start using it
#
# You may add any card to any zone here
#
# Format: <zone>:<nickname>:<card name>:<amount>
#
# zone ::= hand | battlefield | graveyard | library
# nickname - Player's name you connect to the game with
#
#
battlefield:player:Forest:3
graveyard:player:Plains:1
battlefield:player:Snapsail Glider:1
battlefield:computer:Island:2
battlefield:player:Plains:3
hand:player:Whispersilk Cloak:1
hand:computer:Lightning Bolt:1
library:player:Shock:2

View file

@ -0,0 +1,249 @@
/*
* 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.futuresight;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.filter.Filter;
import mage.filter.FilterCard;
import mage.filter.predicate.permanent.CardCounterPredicate;
import mage.filter.predicate.permanent.CounterPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author Gal Lerman
*/
public class DustOfMoments extends CardImpl {
public DustOfMoments(UUID ownerId) {
super(ownerId, 5, "Dust of Moments", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{W}");
this.expansionSetCode = "FUT";
// Choose one - Remove two time counters from each permanent and each suspended card
this.getSpellAbility().addEffect(new RemoveCountersEffect());
// Or put two time counters on each permanent with a time counter on it and each suspended card
Mode mode = new Mode();
mode.getEffects().add(new AddCountersEffect());
this.getSpellAbility().addMode(mode);
}
public DustOfMoments(final DustOfMoments card) {
super(card);
}
@Override
public DustOfMoments copy() {
return new DustOfMoments(this);
}
//TODO: PermanentImpl.getCounters() and CardImpl.getCounters(game) don't return the same value for the same Card
//TODO: This means I can't use a Card generic for Permanents and Exiled cards and use Card.getCounters(game)
//TODO: This is the reason i've copy pasted some logic in DustOfMomentsEffect
//TODO: After this issue is fixed/explained i'll refactor the code
public abstract static class DustOfMomentsEffect extends OneShotEffect {
private final Counter counter;
private final Filter<Card> permFilter;
private final Filter<Card> exiledFilter;
public DustOfMomentsEffect() {
super(Outcome.Benefit);
this.counter = new Counter(CounterType.TIME.getName(), 2);
this.permFilter = new FilterCard("permanent and each suspended card");
permFilter.add(new CounterPredicate(CounterType.TIME));
this.exiledFilter = new FilterCard("permanent and each suspended card");
exiledFilter.add(new CardCounterPredicate(CounterType.TIME));
setText();
}
public DustOfMomentsEffect(final DustOfMomentsEffect effect) {
super(effect);
this.counter = effect.counter.copy();
this.permFilter = effect.permFilter.copy();
this.exiledFilter = effect.exiledFilter.copy();
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (controller != null && sourceObject != null) {
updatePermanents(game, controller, sourceObject);
updateSuspended(game, controller, sourceObject);
return true;
}
return false;
}
private void updateSuspended(final Game game, final Player controller, final MageObject sourceObject) {
final List<Card> exiledCards = game.getExile().getAllCards(game);
execute(game, controller, sourceObject, exiledCards);
}
private void updatePermanents(final Game game, final Player controller, final MageObject sourceObject) {
List<Permanent> permanents = game.getBattlefield().getAllActivePermanents();
executeP(game, controller, sourceObject, permanents);
}
private void executeP(final Game game, final Player controller, final MageObject sourceObject, final List<Permanent> cards) {
if (cards == null || cards.isEmpty()) {
return;
}
for (Permanent card : cards) {
if (permFilter.match(card, game)) {
final String counterName = counter.getName();
if (shouldRemoveCounters()) {
final Counter existingCounterOfSameType = card.getCounters().get(counterName);
final int countersToRemove = Math.min(existingCounterOfSameType.getCount(), counter.getCount());
final Counter modifiedCounter = new Counter(counterName, countersToRemove);
card.removeCounters(modifiedCounter, game);
} else {
card.addCounters(counter, game);
}
if (!game.isSimulation())
game.informPlayers(new StringBuilder(sourceObject.getName()).append(": ")
.append(controller.getLogName()).append(getActionStr()).append("s")
.append(counter.getCount()).append(" ").append(counterName.toLowerCase())
.append(" counter on ").append(card.getName()).toString());
}
}
}
private void execute(final Game game, final Player controller, final MageObject sourceObject, final List<Card> cards) {
if (cards == null || cards.isEmpty()) {
return;
}
for (Card card : cards) {
if (exiledFilter.match(card, game)) {
final String counterName = counter.getName();
if (shouldRemoveCounters()) {
final Counter existingCounterOfSameType = card.getCounters(game).get(counterName);
final int countersToRemove = Math.min(existingCounterOfSameType.getCount(), counter.getCount());
final Counter modifiedCounter = new Counter(counterName, countersToRemove);
card.removeCounters(modifiedCounter, game);
} else {
card.addCounters(counter, game);
}
if (!game.isSimulation())
game.informPlayers(new StringBuilder(sourceObject.getName()).append(": ")
.append(controller.getLogName()).append(getActionStr()).append("s ")
.append(counter.getCount()).append(" ").append(counterName.toLowerCase())
.append(" counter on ").append(card.getName()).toString());
}
}
}
protected abstract boolean shouldRemoveCounters();
protected abstract String getActionStr();
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append(getActionStr());
if (counter.getCount() > 1) {
sb.append(Integer.toString(counter.getCount())).append(" ").append(counter.getName().toLowerCase()).append(" counters on each ");
} else {
sb.append("a ").append(counter.getName().toLowerCase()).append(" counter on each ");
}
sb.append(permFilter.getMessage());
staticText = sb.toString();
}
}
public static class AddCountersEffect extends DustOfMomentsEffect {
public AddCountersEffect() {
super();
}
public AddCountersEffect(final DustOfMomentsEffect effect) {
super(effect);
}
@Override
protected boolean shouldRemoveCounters() {
return false;
}
@Override
protected String getActionStr() {
return "add";
}
@Override
public Effect copy() {
return new AddCountersEffect(this);
}
}
public static class RemoveCountersEffect extends DustOfMomentsEffect {
public RemoveCountersEffect() {
super();
}
public RemoveCountersEffect(final DustOfMomentsEffect effect) {
super(effect);
}
@Override
protected boolean shouldRemoveCounters() {
return true;
}
@Override
protected String getActionStr() {
return "remove";
}
@Override
public Effect copy() {
return new RemoveCountersEffect(this);
}
}
}

View file

@ -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 mage.sets.planarchaos;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.condition.common.LastTimeCounterRemovedCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.CopyCardEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.VanishingSacrificeAbility;
import mage.abilities.keyword.VanishingUpkeepAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.counters.CounterType;
/**
*
* @author Gal Lerman
*/
public class Chronozoa extends CardImpl {
private static final int timeCounters = 3;
private static final int numCopies = 2;
public Chronozoa(UUID ownerId) {
super(ownerId, 37, "Chronozoa", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}");
this.expansionSetCode = "PLC";
this.subtype.add("Illusion");
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Vanishing 3
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIME.createInstance(timeCounters))));
this.addAbility(new VanishingUpkeepAbility(timeCounters));
this.addAbility(new VanishingSacrificeAbility());
// When Chronozoa is put into a graveyard from play, if it had no time counters on it, put two tokens into play that are copies of it.
this.addAbility(new ConditionalTriggeredAbility(new DiesCreatureTriggeredAbility(new CopyCardEffect(this, numCopies), false),
new LastTimeCounterRemovedCondition(),
"When Chronozoa is put into a graveyard from play, if it had no time counters on it, put two tokens into play that are copies of it."));
}
public Chronozoa(final Chronozoa card) {
super(card);
}
@Override
public Chronozoa copy() {
return new Chronozoa(this);
}
}

View file

@ -29,6 +29,7 @@ package mage.sets.returntoravnica;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.CopyCardEffect;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Rarity; import mage.constants.Rarity;
import mage.constants.Zone; import mage.constants.Zone;
@ -39,18 +40,11 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.DiscardCardCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken;
import mage.util.CardUtil;
/** /**
* *
@ -89,7 +83,7 @@ public class PackRat extends CardImpl {
// Pack Rat's power and toughness are each equal to the number of Rats you control. // Pack Rat's power and toughness are each equal to the number of Rats you control.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame))); this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame)));
// {2}{B}, Discard a card: Put a token onto the battlefield that's a copy of Pack Rat. // {2}{B}, Discard a card: Put a token onto the battlefield that's a copy of Pack Rat.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PackRatEffect(this), new ManaCostsImpl("{2}{B}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyCardEffect(this, 1), new ManaCostsImpl("{2}{B}"));
ability.addCost(new DiscardCardCost()); ability.addCost(new DiscardCardCost());
this.addAbility(ability); this.addAbility(ability);
} }
@ -104,37 +98,3 @@ public class PackRat extends CardImpl {
} }
} }
class PackRatEffect extends OneShotEffect {
private Card card;
public PackRatEffect(Card card) {
super(Outcome.PutCreatureInPlay);
this.card = card;
staticText = "Put a token onto the battlefield that's a copy of {this}";
}
public PackRatEffect(final PackRatEffect effect) {
super(effect);
this.card = effect.card;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
}
if (permanent != null) {
EmptyToken newToken = new EmptyToken();
CardUtil.copyTo(newToken).from(permanent);
return newToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
}
return false;
}
@Override
public PackRatEffect copy() {
return new PackRatEffect(this);
}
}

View file

@ -0,0 +1,101 @@
package org.mage.test.cards.single;
import java.util.List;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.counters.Counters;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Created by glerman on 22/6/15.
*/
public class ChronozoaTest extends CardTestPlayerBase {
// Flying
// Vanishing 3
// When Chronozoa dies, if it had no time counters on it, put two tokens that are copies of it onto the battlefield.
/**
* Test that time counters are removed before the draw phase
*/
@Test
public void testVanishing() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.HAND, playerA, "Chronozoa");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chronozoa");
setStopAt(5, PhaseStep.DRAW);
execute();
// Make sure one time counter was removed at beginning of playerA turn num 3
assertCounterCount("Chronozoa", CounterType.TIME, 1);
}
/**
* Test that the tokens are put to battlefield if the last time counter is removed
*/
@Test
public void testDuplicationEffect() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.HAND, playerA, "Chronozoa");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chronozoa");
setStopAt(9, PhaseStep.PRECOMBAT_MAIN);
execute();
// The original Chronozoa card should be in graveyard
assertGraveyardCount(playerA, 1);
final List<Permanent> creatures = currentGame.getBattlefield().getAllActivePermanents(CardType.CREATURE);
Assert.assertEquals(2, creatures.size());
for (final Permanent creature : creatures) {
// Make sure the creatures are Chronozoa tokens
Assert.assertEquals("Chronozoa", creature.getName());
Assert.assertEquals("Chronozoa has to be a token", true, creature instanceof PermanentToken);
// Make sure each token has 2 time counters
final Counters counters = creature.getCounters();
Assert.assertEquals(1, counters.size());
for(final Counter counter : counters.values()) {
Assert.assertEquals(CounterType.TIME.getName(), counter.getName());
Assert.assertEquals(2, counter.getCount());
}
}
}
@Test
public void testChronozoaDestroyedWithTimeCounters() throws Exception {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.HAND, playerA, "Chronozoa");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
addCard(Zone.HAND, playerB, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chronozoa");
// Destroy Chronozoa the same phase it should duplicate -> due to stack Chronozoa is destroyed before duplication
castSpell(7, PhaseStep.UPKEEP, playerB, "Lightning Bolt", "Chronozoa");
setStopAt(7, PhaseStep.PRECOMBAT_MAIN);
execute();
// Chronozoa in gy
assertGraveyardCount(playerA, 1);
// Lightning Bolt in gt
assertGraveyardCount(playerB, 1);
// Chronozoa shouldn't duplicate
final List<Permanent> creatures = currentGame.getBattlefield().getAllActivePermanents(CardType.CREATURE);
Assert.assertTrue(creatures.isEmpty());
}
}

View file

@ -0,0 +1,61 @@
package org.mage.test.cards.single;
import java.util.List;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Created by glerman on 29/6/15.
*/
public class DustOfMomentsTest extends CardTestPlayerBase {
@Test
public void testRemoveCounters() throws Exception {
initGame();
setModeChoice(playerA, "1"); // Chose the remove 2 time counters option
setStopAt(3, PhaseStep.END_TURN);
execute();
// Chronozoa should have duplicated
final List<Permanent> activeCreatures = currentGame.getBattlefield().getAllActivePermanents(CardType.CREATURE);
Assert.assertEquals(2, activeCreatures.size());
for (final Permanent creature : activeCreatures) {
Assert.assertEquals("Chronozoa", creature.getName());
Assert.assertEquals(3, creature.getCounters().getCount(CounterType.TIME));
}
// Check time counters on kraken
assertCounterOnExiledCardCount("Deep-Sea Kraken", CounterType.TIME, 6);
}
@Test
public void testAddCounters() throws Exception {
initGame();
setModeChoice(playerA, "2"); // Chose the add 2 time counters option
setStopAt(3, PhaseStep.END_TURN);
execute();
assertCounterCount("Chronozoa", CounterType.TIME, 4);
assertCounterOnExiledCardCount("Deep-Sea Kraken", CounterType.TIME, 10);
}
private void initGame() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 7);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.HAND, playerA, "Chronozoa");
addCard(Zone.HAND, playerA, "Deep-Sea Kraken");
addCard(Zone.HAND, playerA, "Dust of Moments");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chronozoa");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Suspend"); // Casts Deep-Sea Kraken as Suspend
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Dust of Moments");
}
}

View file

@ -0,0 +1,27 @@
package org.mage.test.cards.single;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Created by glerman on 23/6/15.
*/
@Ignore
public class NornsAnnexTest extends CardTestPlayerBase{
@Test
@Ignore
public void testNornsAnnex() {
addCard(Zone.BATTLEFIELD, playerA, "Norn's Annex");
addCard(Zone.BATTLEFIELD, playerB, "Brindle Boar");
attack(2, playerB, "Brindle Boar", playerA);
setStopAt(2, PhaseStep.END_TURN);
execute();
}
}

View file

@ -0,0 +1,35 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
* Created by glerman on 20/6/15.
*/
public class LastTimeCounterRemovedCondition implements Condition{
private static final LastTimeCounterRemovedCondition fInstance = new LastTimeCounterRemovedCondition();
public static LastTimeCounterRemovedCondition getInstance() {
return fInstance;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
}
if (permanent != null) {
final int timeCounters = permanent.getCounters().getCount(CounterType.TIME);
return timeCounters == 0;
} else {
return false;
}
}
}

View file

@ -0,0 +1,51 @@
package mage.abilities.effects;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken;
import mage.util.CardUtil;
/**
* Created by glerman on 20/6/15.
*/
public class CopyCardEffect extends OneShotEffect {
private final Card card;
private final int copies;
public CopyCardEffect(Card card, int copies) {
super(Outcome.PutCreatureInPlay);
this.card = card;
this.copies = copies;
staticText = "Put a token onto the battlefield that's a copy of {this}";
}
public CopyCardEffect(final CopyCardEffect effect) {
super(effect);
this.card = effect.card;
this.copies = effect.copies;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
}
if (permanent != null) {
EmptyToken newToken = new EmptyToken();
CardUtil.copyTo(newToken).from(permanent);
return newToken.putOntoBattlefield(copies, game, source.getSourceId(), source.getControllerId());
}
return false;
}
@Override
public CopyCardEffect copy() {
return new CopyCardEffect(this);
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common.counter;
import java.util.ArrayList;
import java.util.List;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.filter.Filter;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author Gal Lerman
*/
public class AddRemoveAllTimeSuspentCountersEffect extends OneShotEffect {
private final Counter counter;
private final Filter<Card> filter;
private final boolean removeCounter;
private final String actionStr;
public AddRemoveAllTimeSuspentCountersEffect(Counter counter, Filter<Card> filter, boolean removeCounter) {
super(Outcome.Benefit);
this.counter = counter;
this.filter = filter;
this.removeCounter= removeCounter;
actionStr = removeCounter ? " removes " : " puts ";
setText();
}
public AddRemoveAllTimeSuspentCountersEffect(final AddRemoveAllTimeSuspentCountersEffect effect) {
super(effect);
this.counter = effect.counter.copy();
this.filter = effect.filter.copy();
this.removeCounter = effect.removeCounter;
this.actionStr = effect.actionStr;
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (controller != null && sourceObject != null) {
if (counter != null) {
List<Card> permanents = new ArrayList<Card>(game.getBattlefield().getAllActivePermanents());
execute(game, controller, sourceObject, permanents, removeCounter);
final List<Card> exiledCards = game.getExile().getAllCards(game);
execute(game, controller, sourceObject, exiledCards, removeCounter);
}
return true;
}
return false;
}
private void execute(final Game game, final Player controller, final MageObject sourceObject, final List<Card> cards, final boolean removeCounter) {
for (Card card : cards) {
if (filter.match(card, game)) {
final String counterName = counter.getName();
if (removeCounter) {
final Counter existingCounterOfSameType = card.getCounters(game).get(counterName);
final int countersToRemove = Math.min(existingCounterOfSameType.getCount(), counter.getCount());
final Counter modifiedCounter = new Counter(counterName, countersToRemove);
card.removeCounters(modifiedCounter, game);
} else {
card.addCounters(counter, game);
}
if (!game.isSimulation())
game.informPlayers(new StringBuilder(sourceObject.getName()).append(": ")
.append(controller.getLogName()).append(actionStr)
.append(counter.getCount()).append(" ").append(counterName.toLowerCase())
.append(" counter on ").append(card.getName()).toString());
}
}
}
private void setText() {
StringBuilder sb = new StringBuilder();
final String actionsStr2 = removeCounter ? "remove " : " put ";
sb.append(actionsStr2);
if (counter.getCount() > 1) {
sb.append(Integer.toString(counter.getCount())).append(" ").append(counter.getName().toLowerCase()).append(" counters on each ");
} else {
sb.append("a ").append(counter.getName().toLowerCase()).append(" counter on each ");
}
sb.append(filter.getMessage());
staticText = sb.toString();
}
@Override
public AddRemoveAllTimeSuspentCountersEffect copy() {
return new AddRemoveAllTimeSuspentCountersEffect(this);
}
}

View file

@ -0,0 +1,36 @@
package mage.filter.predicate.permanent;
import mage.cards.Card;
import mage.counters.CounterType;
import mage.filter.predicate.Predicate;
import mage.game.Game;
/**
* Created by glerman on 3/7/15.
*/
public class CardCounterPredicate implements Predicate<Card>{
private final CounterType counter;
/**
*
* @param counter if null any counter selects the permanent
*/
public CardCounterPredicate(CounterType counter) {
this.counter = counter;
}
@Override
public boolean apply(Card input, Game game) {
if (counter == null) {
return !input.getCounters(game).keySet().isEmpty();
} else {
return input.getCounters(game).containsKey(counter);
}
}
@Override
public String toString() {
return "CounterType(" + counter.getName() + ')';
}
}

3
clean_dbs.sh Normal file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
# Cleans the DB from Server, Client and Test modules
find . -type f | grep -i cards.h2*.db | xargs rm -v