mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
Merge branch 'master' into Zzooouhh-rix
This commit is contained in:
commit
ce433f7761
32 changed files with 1399 additions and 440 deletions
|
@ -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.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -46,14 +45,17 @@ import mage.constants.*;
|
|||
public class ArdentRecruit extends CardImpl {
|
||||
|
||||
public ArdentRecruit(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.SOLDIER);
|
||||
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Metalcraft — Ardent Recruit gets +2/+2 as long as you control three or more artifacts.
|
||||
ContinuousEffect boostSource = new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield);
|
||||
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(boostSource, MetalcraftCondition.instance, "Ardent Recruit gets +2/+2 as long as you control three or more artifacts");
|
||||
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(boostSource, MetalcraftCondition.instance,
|
||||
"{this} gets +2/+2 as long as you control three or more artifacts");
|
||||
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect);
|
||||
ability.setAbilityWord(AbilityWord.METALCRAFT);
|
||||
this.addAbility(ability);
|
||||
|
|
78
Mage.Sets/src/mage/cards/d/DuskCharger.java
Normal file
78
Mage.Sets/src/mage/cards/d/DuskCharger.java
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.CitysBlessingCondition;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
||||
import mage.abilities.keyword.AscendAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DuskCharger extends CardImpl {
|
||||
|
||||
public DuskCharger(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
|
||||
|
||||
this.subtype.add(SubType.HORSE);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Ascend
|
||||
this.addAbility(new AscendAbility());
|
||||
|
||||
// Dusk Charger gets +2/+2 as long as you have the city's blessing.
|
||||
ContinuousEffect boostSource = new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield);
|
||||
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(boostSource, CitysBlessingCondition.instance,
|
||||
"{this} gets +2/+2 as long as you have the city's blessing");
|
||||
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public DuskCharger(final DuskCharger card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DuskCharger copy() {
|
||||
return new DuskCharger(this);
|
||||
}
|
||||
}
|
|
@ -28,18 +28,15 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.condition.common.LiveLostLastTurnCondition;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.token.SoldierToken;
|
||||
import mage.watchers.common.PlayerLostLifeWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -51,8 +48,10 @@ public class FirstResponse extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}");
|
||||
|
||||
// At the beginning of each upkeep, if you lost life last turn, create a 1/1 white Soldier creature token.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new FirstResponseEffect(), TargetController.ANY, false), new PlayerLostLifeWatcher());
|
||||
|
||||
this.addAbility(new ConditionalTriggeredAbility(
|
||||
new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new SoldierToken()), TargetController.ANY, false),
|
||||
LiveLostLastTurnCondition.instance,
|
||||
"At the beginning of each upkeep, if you lost life last turn, create a 1/1 white Soldier creature token."));
|
||||
}
|
||||
|
||||
public FirstResponse(final FirstResponse card) {
|
||||
|
@ -64,32 +63,3 @@ public class FirstResponse extends CardImpl {
|
|||
return new FirstResponse(this);
|
||||
}
|
||||
}
|
||||
|
||||
class FirstResponseEffect extends OneShotEffect {
|
||||
|
||||
public FirstResponseEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.staticText = "if you lost life last turn, create a 1/1 white Soldier creature token";
|
||||
}
|
||||
|
||||
public FirstResponseEffect(final FirstResponseEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FirstResponseEffect copy() {
|
||||
return new FirstResponseEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
PlayerLostLifeWatcher watcher = (PlayerLostLifeWatcher) game.getState().getWatchers().get(PlayerLostLifeWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
if (watcher.getLiveLostLastTurn(source.getControllerId()) > 0) {
|
||||
return new CreateTokenEffect(new SoldierToken()).apply(game, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
92
Mage.Sets/src/mage/cards/g/GloriousDestiny.java
Normal file
92
Mage.Sets/src/mage/cards/g/GloriousDestiny.java
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.common.AsEntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.CitysBlessingCondition;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostAllOfChosenSubtypeEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAllOfChosenSubtypeEffect;
|
||||
import mage.abilities.keyword.AscendAbility;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class GloriousDestiny extends CardImpl {
|
||||
|
||||
private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control of the chosen type");
|
||||
|
||||
static {
|
||||
filter.add(new ControllerPredicate(TargetController.YOU));
|
||||
}
|
||||
|
||||
public GloriousDestiny(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
|
||||
|
||||
// Ascend
|
||||
this.addAbility(new AscendAbility());
|
||||
|
||||
// As Glorious Destiny enters the battlefield, choose a creature type.
|
||||
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.BoostCreature)));
|
||||
|
||||
// Creatures you control of the chosen type get +1/+1. They have vigilance as long as you have the city's blessing.
|
||||
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllOfChosenSubtypeEffect(1, 1, Duration.WhileOnBattlefield, filter, true));
|
||||
ContinuousEffect effect = new ConditionalContinuousEffect(
|
||||
new GainAbilityAllOfChosenSubtypeEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, FILTER_PERMANENT_CREATURES_CONTROLLED),
|
||||
CitysBlessingCondition.instance,
|
||||
"They have vigilance as long as you have the city's blessing");
|
||||
ability.addEffect(effect);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public GloriousDestiny(final GloriousDestiny card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GloriousDestiny copy() {
|
||||
return new GloriousDestiny(this);
|
||||
}
|
||||
}
|
79
Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java
Normal file
79
Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.common.DiesTriggeredAbility;
|
||||
import mage.abilities.condition.common.LiveLostLastTurnCondition;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class PaladinOfAtonement extends CardImpl {
|
||||
|
||||
public PaladinOfAtonement(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||
|
||||
this.subtype.add(SubType.VAMPIRE);
|
||||
this.subtype.add(SubType.KNIGHT);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// At the beginning of each upkeep, if you lost life last turn, put a +1/+1 counter on Paladin of Atonement.
|
||||
this.addAbility(new ConditionalTriggeredAbility(
|
||||
new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.ANY, false),
|
||||
LiveLostLastTurnCondition.instance,
|
||||
"At the beginning of each upkeep, if you lost life last turn, put a +1/+1 counter on {this}"));
|
||||
|
||||
// When Paladin of Atonement dies, you gain life equal to it's toughness.
|
||||
this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(SourcePermanentToughnessValue.getInstance(),
|
||||
"you gain life equal to it's toughness")));
|
||||
}
|
||||
|
||||
public PaladinOfAtonement(final PaladinOfAtonement card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaladinOfAtonement copy() {
|
||||
return new PaladinOfAtonement(this);
|
||||
}
|
||||
}
|
150
Mage.Sets/src/mage/cards/s/SpellweaverVolute.java
Normal file
150
Mage.Sets/src/mage/cards/s/SpellweaverVolute.java
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.cards.Card;
|
||||
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.filter.FilterCard;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SpellweaverVolute extends CardImpl {
|
||||
|
||||
public SpellweaverVolute(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
|
||||
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
// Enchant instant card in a graveyard
|
||||
FilterCard filter = new FilterCard("instant card in a graveyard");
|
||||
filter.add(new CardTypePredicate(CardType.INSTANT));
|
||||
TargetCardInGraveyard auraTarget = new TargetCardInGraveyard(filter);
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment));
|
||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost.
|
||||
// If you do, exile the enchanted card and attach Spellweaver Volute to another instant card in a graveyard.
|
||||
FilterSpell filterSpell = new FilterSpell("a sorcery spell");
|
||||
filterSpell.add(new CardTypePredicate(CardType.SORCERY));
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(new SpellweaverVoluteEffect(), filterSpell, false));
|
||||
}
|
||||
|
||||
public SpellweaverVolute(final SpellweaverVolute card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellweaverVolute copy() {
|
||||
return new SpellweaverVolute(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SpellweaverVoluteEffect extends OneShotEffect {
|
||||
|
||||
public SpellweaverVoluteEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "copy the enchanted instant card. You may cast the copy without paying its mana cost. \n"
|
||||
+ "If you do, exile the enchanted card and attach {this} to another instant card in a graveyard";
|
||||
}
|
||||
|
||||
public SpellweaverVoluteEffect(final SpellweaverVoluteEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellweaverVoluteEffect copy() {
|
||||
return new SpellweaverVoluteEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (sourcePermanent != null && sourcePermanent.getAttachedTo() != null) {
|
||||
Card enchantedCard = game.getCard(sourcePermanent.getAttachedTo());
|
||||
if (enchantedCard != null && game.getState().getZone(enchantedCard.getId()) == Zone.GRAVEYARD) {
|
||||
Player ownerEnchanted = game.getPlayer(enchantedCard.getOwnerId());
|
||||
if (ownerEnchanted != null
|
||||
&& controller.chooseUse(outcome, "Create a copy of " + enchantedCard.getName() + '?', source, game)) {
|
||||
Card copiedCard = game.copyCard(enchantedCard, source, source.getControllerId());
|
||||
if (copiedCard != null) {
|
||||
ownerEnchanted.getGraveyard().add(copiedCard);
|
||||
game.getState().setZone(copiedCard.getId(), Zone.GRAVEYARD);
|
||||
if (controller.chooseUse(outcome, "Cast the copied card without paying mana cost?", source, game)) {
|
||||
if (copiedCard.getSpellAbility() != null) {
|
||||
controller.cast(copiedCard.getSpellAbility(), game, true);
|
||||
}
|
||||
if (controller.moveCards(enchantedCard, Zone.EXILED, source, game)) {
|
||||
FilterCard filter = new FilterCard("instant card in a graveyard");
|
||||
filter.add(new CardTypePredicate(CardType.INSTANT));
|
||||
TargetCardInGraveyard auraTarget = new TargetCardInGraveyard(filter);
|
||||
if (auraTarget.canChoose(source.getSourceId(), controller.getId(), game)) {
|
||||
controller.choose(outcome, auraTarget, source.getSourceId(), game);
|
||||
Card newAuraTarget = game.getCard(auraTarget.getFirstTarget());
|
||||
if (newAuraTarget != null) {
|
||||
if (enchantedCard.getId().equals(newAuraTarget.getId())) {
|
||||
} else if (newAuraTarget.addAttachment(sourcePermanent.getId(), game)) {
|
||||
game.informPlayers(sourcePermanent.getLogName() + " was attached to " + newAuraTarget.getLogName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ public class TheImmortalSun extends CardImpl {
|
|||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
|
||||
// Players can't activate loyalty abilities of planeswalkers.
|
||||
// Players can't activate planeswalkers' loyalty abilities.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TheImmortalSunCantActivateEffect()));
|
||||
// At the beginning of your draw step, draw an additional card.
|
||||
this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1), TargetController.YOU, false));
|
||||
|
@ -86,7 +86,7 @@ class TheImmortalSunCantActivateEffect extends ContinuousRuleModifyingEffectImpl
|
|||
|
||||
public TheImmortalSunCantActivateEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
staticText = "Players can't activate loyalty abilities of planeswalkers";
|
||||
staticText = "Players can't activate planeswalkers' loyalty abilities";
|
||||
}
|
||||
|
||||
public TheImmortalSunCantActivateEffect(final TheImmortalSunCantActivateEffect effect) {
|
||||
|
|
|
@ -200,6 +200,7 @@ public class FutureSight extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Snake Cult Initiation", 89, Rarity.UNCOMMON, mage.cards.s.SnakeCultInitiation.class));
|
||||
cards.add(new SetCardInfo("Soultether Golem", 164, Rarity.UNCOMMON, mage.cards.s.SoultetherGolem.class));
|
||||
cards.add(new SetCardInfo("Sparkspitter", 109, Rarity.UNCOMMON, mage.cards.s.Sparkspitter.class));
|
||||
cards.add(new SetCardInfo("Spellweaver Volute", 59, Rarity.RARE, mage.cards.s.SpellweaverVolute.class));
|
||||
cards.add(new SetCardInfo("Spellwild Ouphe", 151, Rarity.UNCOMMON, mage.cards.s.SpellwildOuphe.class));
|
||||
cards.add(new SetCardInfo("Spin into Myth", 60, Rarity.UNCOMMON, mage.cards.s.SpinIntoMyth.class));
|
||||
cards.add(new SetCardInfo("Spirit en-Dal", 17, Rarity.UNCOMMON, mage.cards.s.SpiritEnDal.class));
|
||||
|
|
|
@ -61,9 +61,12 @@ public class RivalsOfIxalan extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Brass's Bounty", 94, Rarity.RARE, mage.cards.b.BrasssBounty.class));
|
||||
cards.add(new SetCardInfo("Captain's Hook", 177, Rarity.RARE, mage.cards.c.CaptainsHook.class));
|
||||
cards.add(new SetCardInfo("Cinder Barrens", 205, Rarity.RARE, mage.cards.c.CinderBarrens.class));
|
||||
cards.add(new SetCardInfo("Elenda, the Dusk Rose", 157, Rarity.MYTHIC, mage.cards.e.ElendaTheDuskRose.class));
|
||||
cards.add(new SetCardInfo("Dusk Charger", 69, Rarity.COMMON, mage.cards.d.DuskCharger.class));
|
||||
cards.add(new SetCardInfo("Elenda, the Dusk Rose", 157, Rarity.MYTHIC, mage.cards.e.ElendaTheDuskRose.class));
|
||||
cards.add(new SetCardInfo("Evolving Wilds", 186, Rarity.RARE, mage.cards.e.EvolvingWilds.class));
|
||||
cards.add(new SetCardInfo("Ghalta, Primal Hunger", 130, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class));
|
||||
cards.add(new SetCardInfo("Glorious Destiny", 18, Rarity.RARE, mage.cards.g.GloriousDestiny.class));
|
||||
cards.add(new SetCardInfo("Paladin of Atonement", 16, Rarity.RARE, mage.cards.p.PaladinOfAtonement.class));
|
||||
cards.add(new SetCardInfo("Silvergill Adept", 53, Rarity.UNCOMMON, mage.cards.s.SilvergillAdept.class));
|
||||
cards.add(new SetCardInfo("Storm the Vault", 173, Rarity.RARE, mage.cards.s.StormTheVault.class));
|
||||
cards.add(new SetCardInfo("Swab Goblin", 203, Rarity.COMMON, mage.cards.s.SwabGoblin.class));
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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.enchantments;
|
||||
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EnchantingGraveyardCardsTest extends CardTestPlayerBase {
|
||||
|
||||
static final String LIGHTNING_BOLT = "Lightning Bolt";
|
||||
static final String SPELLWEAVER_VOLUTE = "Spellweaver Volute";
|
||||
|
||||
/**
|
||||
* Test that a card in the graveyard can be enchanted
|
||||
*/
|
||||
@Test
|
||||
public void testSpellwaeverVoluteNormal() {
|
||||
// Enchant instant card in a graveyard
|
||||
// Whenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost.
|
||||
// If you do, exile the enchanted card and attach Spellweaver Volute to another instant card in a graveyard.
|
||||
addCard(Zone.HAND, playerA, SPELLWEAVER_VOLUTE, 1); // Enchantment Aura {3}{U}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
|
||||
|
||||
// Lightning Bolt deals 3 damage to target creature or player.
|
||||
addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); // Instant {R}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, SPELLWEAVER_VOLUTE, LIGHTNING_BOLT);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
assertHandCount(playerA, 0);
|
||||
assertPermanentCount(playerA, SPELLWEAVER_VOLUTE, 1);
|
||||
assertGraveyardCount(playerB, LIGHTNING_BOLT, 1);
|
||||
|
||||
Permanent spellweaver = getPermanent(SPELLWEAVER_VOLUTE);
|
||||
Card attachedToCard = null;
|
||||
if (spellweaver != null) {
|
||||
attachedToCard = playerB.getGraveyard().get(spellweaver.getAttachedTo(), currentGame);
|
||||
}
|
||||
Assert.assertTrue(SPELLWEAVER_VOLUTE + " has to be attached to Lightning Bolt in graveyard", attachedToCard != null && attachedToCard.getName().equals(LIGHTNING_BOLT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a card in the graveyard can be enchanted and the enchanted card
|
||||
* switches to a new one
|
||||
*/
|
||||
@Test
|
||||
public void testSpellwaeverVoluteAndSorcery() {
|
||||
|
||||
// Enchant instant card in a graveyard
|
||||
// Whenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost.
|
||||
// If you do, exile the enchanted card and attach Spellweaver Volute to another instant card in a graveyard.
|
||||
addCard(Zone.HAND, playerA, SPELLWEAVER_VOLUTE, 1); // Enchantment Aura {3}{U}{U}
|
||||
addCard(Zone.HAND, playerA, "Cloak of Feathers"); // Sorcery {U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
|
||||
addCard(Zone.GRAVEYARD, playerA, "Aerial Volley", 1); // Instant {G}
|
||||
|
||||
// Lightning Bolt deals 3 damage to target creature or player.
|
||||
addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); // Instant {R}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, SPELLWEAVER_VOLUTE, LIGHTNING_BOLT);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloak of Feathers", "Silvercoat Lion");
|
||||
setChoice(playerA, "Yes"); // play the L. Bold
|
||||
addTarget(playerA, playerB); // Add Target for the L. Bold
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 17);
|
||||
|
||||
assertHandCount(playerA, 1);
|
||||
assertGraveyardCount(playerA, "Cloak of Feathers", 1);
|
||||
assertPermanentCount(playerA, SPELLWEAVER_VOLUTE, 1);
|
||||
assertGraveyardCount(playerA, "Aerial Volley", 1);
|
||||
assertExileCount(playerB, LIGHTNING_BOLT, 1);
|
||||
assertAbility(playerA, "Silvercoat Lion", FlyingAbility.getInstance(), true);
|
||||
Permanent spellweaver = getPermanent(SPELLWEAVER_VOLUTE);
|
||||
Card attachedToCard = null;
|
||||
if (spellweaver != null) {
|
||||
attachedToCard = playerA.getGraveyard().get(spellweaver.getAttachedTo(), currentGame);
|
||||
}
|
||||
Assert.assertTrue(SPELLWEAVER_VOLUTE + " has to be attached to Aerial Volley in graveyard", attachedToCard != null && attachedToCard.getName().equals("Aerial Volley"));
|
||||
|
||||
assertHandCount(playerA, 1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a card in the graveyard can be enchanted and the enchanted card
|
||||
* switches to a new one
|
||||
*/
|
||||
@Test
|
||||
public void testSpellwaeverVoluteAndSorceryWithoutNewTarget() {
|
||||
|
||||
// Enchant instant card in a graveyard
|
||||
// Whenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost.
|
||||
// If you do, exile the enchanted card and attach Spellweaver Volute to another instant card in a graveyard.
|
||||
addCard(Zone.HAND, playerA, SPELLWEAVER_VOLUTE, 1); // Enchantment Aura {3}{U}{U}
|
||||
addCard(Zone.HAND, playerA, "Cloak of Feathers"); // Sorcery {U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
|
||||
|
||||
// Lightning Bolt deals 3 damage to target creature or player.
|
||||
addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); // Instant {R}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, SPELLWEAVER_VOLUTE, LIGHTNING_BOLT);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloak of Feathers", "Silvercoat Lion");
|
||||
setChoice(playerA, "Yes"); // play the L. Bold
|
||||
addTarget(playerA, playerB); // Add Target for the L. Bold
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 17);
|
||||
|
||||
assertHandCount(playerA, 1);
|
||||
assertGraveyardCount(playerA, "Cloak of Feathers", 1);
|
||||
assertPermanentCount(playerA, SPELLWEAVER_VOLUTE, 0);
|
||||
assertGraveyardCount(playerA, SPELLWEAVER_VOLUTE, 1);
|
||||
assertExileCount(playerB, LIGHTNING_BOLT, 1);
|
||||
assertAbility(playerA, "Silvercoat Lion", FlyingAbility.getInstance(), true);
|
||||
|
||||
assertGraveyardCount(playerA, SPELLWEAVER_VOLUTE, 1);
|
||||
|
||||
for (Card card : currentGame.getExile().getAllCards(currentGame)) {
|
||||
if (card.getName().equals(LIGHTNING_BOLT)) {
|
||||
Assert.assertTrue(LIGHTNING_BOLT + " may not have any attachments", card.getAttachments().isEmpty());
|
||||
|
||||
}
|
||||
}
|
||||
assertHandCount(playerA, 1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a card in the graveyard can be enchanted and if the Enchantment
|
||||
* returns to hand, the enchanting ends
|
||||
*/
|
||||
@Test
|
||||
public void testSpellwaeverVoluteAndReturnToHand() {
|
||||
|
||||
// Enchant instant card in a graveyard
|
||||
// Whenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost.
|
||||
// If you do, exile the enchanted card and attach Spellweaver Volute to another instant card in a graveyard.
|
||||
addCard(Zone.HAND, playerA, SPELLWEAVER_VOLUTE, 1); // Enchantment Aura {3}{U}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
|
||||
|
||||
// Lightning Bolt deals 3 damage to target creature or player.
|
||||
addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); // Instant {R}
|
||||
|
||||
// Return target permanent to its owner's hand.
|
||||
addCard(Zone.HAND, playerB, "Boomerang", 1); // Instant {U}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, SPELLWEAVER_VOLUTE, LIGHTNING_BOLT);
|
||||
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Boomerang", SPELLWEAVER_VOLUTE);
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
assertHandCount(playerA, SPELLWEAVER_VOLUTE, 1);
|
||||
assertGraveyardCount(playerB, "Boomerang", 1);
|
||||
assertPermanentCount(playerA, SPELLWEAVER_VOLUTE, 0);
|
||||
|
||||
for (Card card : playerB.getGraveyard().getCards(currentGame)) {
|
||||
if (card.getName().equals(LIGHTNING_BOLT)) {
|
||||
Assert.assertTrue(LIGHTNING_BOLT + " may not have any attachments", card.getAttachments().isEmpty());
|
||||
|
||||
}
|
||||
}
|
||||
assertHandCount(playerA, 1);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.WatcherUtils;
|
||||
import mage.watchers.common.PlayerLostLifeWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX
|
||||
*/
|
||||
public enum LiveLostLastTurnCondition implements Condition {
|
||||
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
PlayerLostLifeWatcher watcher = (PlayerLostLifeWatcher) game.getState().getWatchers().get(PlayerLostLifeWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
return watcher.getLiveLostLastTurn(source.getControllerId()) > 0;
|
||||
} else {
|
||||
WatcherUtils.logMissingWatcher(game, source, PlayerLostLifeWatcher.class, this.getClass());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
protected TriggeredAbility ability;
|
||||
protected Condition condition;
|
||||
protected String text;
|
||||
protected String abilityText;
|
||||
|
||||
/**
|
||||
* Triggered ability with a condition. Set the optionality for the trigger
|
||||
|
@ -27,22 +27,22 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
*
|
||||
* @param ability
|
||||
* @param condition
|
||||
* @param text if null or empty, the rule text of the triggered ability
|
||||
* itself is used.
|
||||
* @param text explicit rule text for the ability, if null or empty, the
|
||||
* rule text generated by the triggered ability itself is used.
|
||||
*/
|
||||
public ConditionalTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
|
||||
super(ability.getZone(), null);
|
||||
this.ability = ability;
|
||||
this.modes = ability.getModes();
|
||||
this.condition = condition;
|
||||
this.text = text;
|
||||
this.abilityText = text;
|
||||
}
|
||||
|
||||
public ConditionalTriggeredAbility(final ConditionalTriggeredAbility triggered) {
|
||||
super(triggered);
|
||||
this.ability = triggered.ability.copy();
|
||||
this.condition = triggered.condition;
|
||||
this.text = triggered.text;
|
||||
this.abilityText = triggered.abilityText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,10 +69,10 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
if (text == null || text.isEmpty()) {
|
||||
if (abilityText == null || abilityText.isEmpty()) {
|
||||
return ability.getRule();
|
||||
}
|
||||
return text;
|
||||
return abilityText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,10 +29,12 @@ package mage.abilities.effects.common;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -72,9 +74,16 @@ public class AttachEffect extends OneShotEffect {
|
|||
if (player != null) {
|
||||
return player.addAttachment(source.getSourceId(), game);
|
||||
}
|
||||
if (source.getTargets().get(0) instanceof TargetCard) { // e.g. Spellweaver Volute
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
return card.addAttachment(source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,14 +27,11 @@
|
|||
*/
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceCreatureType;
|
||||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.game.Game;
|
||||
|
|
|
@ -29,12 +29,9 @@ package mage.abilities.effects.keyword;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.AscendAbility;
|
||||
import mage.constants.Outcome;
|
||||
import mage.designations.CitysBlessing;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -44,7 +41,7 @@ public class AscendEffect extends OneShotEffect {
|
|||
|
||||
public AscendEffect() {
|
||||
super(Outcome.Detriment);
|
||||
staticText = "Ascend (If you control ten or more permanents, you get the city's blessing for the rest of the game.)<br>";
|
||||
staticText = AscendAbility.ASCEND_RULE + "<br>";
|
||||
}
|
||||
|
||||
public AscendEffect(final AscendEffect effect) {
|
||||
|
@ -58,20 +55,6 @@ public class AscendEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
if (game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE_ENCHANTMENT_OR_LAND, controller.getId(), game) > 9) {
|
||||
if (!controller.hasDesignation(DesignationType.CITYS_BLESSING)) {
|
||||
controller.addDesignation(new CitysBlessing());
|
||||
game.informPlayers(controller.getLogName() + " gets the city's blessing for the rest of the game.");
|
||||
} else {
|
||||
game.informPlayers(controller.getLogName() + " already has the city's blessing.");
|
||||
}
|
||||
} else {
|
||||
game.informPlayers(controller.getLogName() + " does not get the city's blessing.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return AscendAbility.checkAscend(game, source, true);
|
||||
}
|
||||
}
|
||||
|
|
115
Mage/src/main/java/mage/abilities/keyword/AscendAbility.java
Normal file
115
Mage/src/main/java/mage/abilities/keyword/AscendAbility.java
Normal file
|
@ -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.abilities.keyword;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import static mage.abilities.keyword.AscendAbility.ASCEND_RULE;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.Zone;
|
||||
import mage.designations.CitysBlessing;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class AscendAbility extends SimpleStaticAbility {
|
||||
|
||||
public static String ASCEND_RULE = "Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>";
|
||||
|
||||
public AscendAbility() {
|
||||
super(Zone.BATTLEFIELD, new AscendContinuousEffect());
|
||||
}
|
||||
|
||||
public AscendAbility(final AscendAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AscendAbility copy() {
|
||||
return new AscendAbility(this);
|
||||
}
|
||||
|
||||
public static boolean checkAscend(Game game, Ability source, boolean verbose) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
if (!controller.hasDesignation(DesignationType.CITYS_BLESSING)) {
|
||||
if (game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE_ENCHANTMENT_OR_LAND, controller.getId(), game) > 9) {
|
||||
controller.addDesignation(new CitysBlessing());
|
||||
game.informPlayers(controller.getLogName() + " gets the city's blessing for the rest of the game.");
|
||||
} else {
|
||||
if (verbose) {
|
||||
game.informPlayers(controller.getLogName() + " does not get the city's blessing.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (verbose) {
|
||||
game.informPlayers(controller.getLogName() + " already has the city's blessing.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return ASCEND_RULE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AscendContinuousEffect extends ContinuousEffectImpl {
|
||||
|
||||
public AscendContinuousEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
|
||||
staticText = ASCEND_RULE;
|
||||
}
|
||||
|
||||
public AscendContinuousEffect(final AscendContinuousEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return AscendAbility.checkAscend(game, source, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AscendContinuousEffect copy() {
|
||||
return new AscendContinuousEffect(this);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@
|
|||
*/
|
||||
package mage.cards;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.ObjectColor;
|
||||
|
@ -42,19 +44,14 @@ import mage.game.Game;
|
|||
import mage.game.GameState;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Card extends MageObject {
|
||||
|
||||
|
||||
final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*";
|
||||
final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*";
|
||||
final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*";
|
||||
final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*";
|
||||
final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*";
|
||||
|
||||
|
||||
UUID getOwnerId();
|
||||
|
||||
String getCardNumber();
|
||||
|
@ -248,4 +245,9 @@ public interface Card extends MageObject {
|
|||
return mana;
|
||||
}
|
||||
|
||||
List<UUID> getAttachments();
|
||||
|
||||
boolean addAttachment(UUID permanentId, Game game);
|
||||
|
||||
boolean removeAttachment(UUID permanentId, Game game);
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
protected boolean morphCard;
|
||||
protected boolean allCreatureTypes;
|
||||
|
||||
protected List<UUID> attachments = new ArrayList<>();
|
||||
|
||||
public CardImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) {
|
||||
this(ownerId, setInfo, cardTypes, costs, SpellAbilityType.BASE);
|
||||
}
|
||||
|
@ -169,6 +171,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
flipCardName = card.flipCardName;
|
||||
splitCard = card.splitCard;
|
||||
usesVariousArt = card.usesVariousArt;
|
||||
this.attachments.addAll(card.attachments);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -840,11 +843,53 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
return super.getSubtype(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllCreatureTypes() {
|
||||
return allCreatureTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsAllCreatureTypes(boolean value) {
|
||||
allCreatureTypes = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAttachment(UUID permanentId, Game game) {
|
||||
if (!this.attachments.contains(permanentId)) {
|
||||
Permanent attachment = game.getPermanent(permanentId);
|
||||
if (attachment == null) {
|
||||
attachment = game.getPermanentEntering(permanentId);
|
||||
}
|
||||
if (attachment != null) {
|
||||
if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ATTACH, objectId, permanentId, attachment.getControllerId()))) {
|
||||
this.attachments.add(permanentId);
|
||||
attachment.attachTo(objectId, game);
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.ATTACHED, objectId, permanentId, attachment.getControllerId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAttachment(UUID permanentId, Game game) {
|
||||
if (this.attachments.contains(permanentId)) {
|
||||
Permanent attachment = game.getPermanent(permanentId);
|
||||
if (attachment != null) {
|
||||
attachment.unattach(game);
|
||||
}
|
||||
if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, objectId, permanentId, attachment != null ? attachment.getControllerId() : null))) {
|
||||
this.attachments.remove(permanentId);
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, objectId, permanentId, attachment != null ? attachment.getControllerId() : null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class DckDeckImporter extends DeckImporter {
|
|||
|
||||
private static final Pattern layoutStackPattern = Pattern.compile("\\(([^)]*)\\)");
|
||||
|
||||
private static final Pattern layoutStackEntryPattern = Pattern.compile("\\[(\\w+):(\\w+)]");
|
||||
private static final Pattern layoutStackEntryPattern = Pattern.compile("\\[(\\w+[^:]\\w+):(\\d+)]"); // [MPSAK1321:43],[MPSAKH:9],[MPS123-AKH:32],[MPS-13AKH:30],[MPS-AKH:49],[MPS-AKH:11]
|
||||
|
||||
@Override
|
||||
protected void readLine(String line, DeckCardLists deckList) {
|
||||
|
@ -67,22 +67,47 @@ public class DckDeckImporter extends DeckImporter {
|
|||
int count = Integer.parseInt(m.group(2));
|
||||
String setCode = m.group(3);
|
||||
String cardNum = m.group(4);
|
||||
String cardName = m.group(5);
|
||||
|
||||
cardNum = cardNum == null ? "" : cardNum.trim();
|
||||
setCode = setCode == null ? "" : setCode.trim();
|
||||
cardName = cardName == null ? "" : cardName.trim();
|
||||
|
||||
// search priority: set/code -> name
|
||||
// with bulletproof on card number or name changes
|
||||
|
||||
DeckCardInfo deckCardInfo = null;
|
||||
CardInfo cardInfo = CardRepository.instance.findCard(setCode, cardNum);
|
||||
if (cardInfo == null) {
|
||||
// Try alternate based on name
|
||||
String cardName = m.group(5);
|
||||
if (cardName != null && !cardName.isEmpty()) {
|
||||
cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, false);
|
||||
sbMessage.append("Could not find card '").append(cardName).append("' in set ").append(setCode).append(" of number ").append(cardNum).append(".\n");
|
||||
if (cardInfo != null) {
|
||||
sbMessage.append("Made substitution of ").append(cardInfo.getCardNumber()).append(", ").append(cardInfo.getCard().getExpansionSetCode()).append(" instead.\n");
|
||||
}
|
||||
|
||||
// search by number
|
||||
CardInfo foundedCard = CardRepository.instance.findCard(setCode, cardNum);
|
||||
boolean wasOutdated = false;
|
||||
if ((foundedCard != null) && !foundedCard.getName().equals(cardName)){
|
||||
sbMessage.append("Line ").append(lineCount).append(": ").append("founded outdated card number or name, will try to replace: ").append(line).append('\n');
|
||||
wasOutdated = true;
|
||||
foundedCard = null;
|
||||
}
|
||||
|
||||
// search by name
|
||||
if (foundedCard == null) {
|
||||
if(!wasOutdated){
|
||||
sbMessage.append("Line ").append(lineCount).append(": ").append("can't find card by number, will try ro replace: ").append(line).append('\n');
|
||||
}
|
||||
|
||||
if (!cardName.equals("")) {
|
||||
foundedCard = CardRepository.instance.findPreferedCoreExpansionCard(cardName, false, setCode);
|
||||
}
|
||||
|
||||
if (foundedCard != null) {
|
||||
sbMessage.append("Line ").append(lineCount).append(": ")
|
||||
.append("replaced to [").append(foundedCard.getSetCode()).append(":").append(foundedCard.getCardNumberAsInt()).append("] ")
|
||||
.append(foundedCard.getName()).append('\n');
|
||||
}else{
|
||||
sbMessage.append("Line ").append(lineCount).append(": ").append("ERROR, can't find card [").append(cardName).append("]").append('\n');
|
||||
}
|
||||
}
|
||||
if (cardInfo != null) {
|
||||
deckCardInfo = new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode());
|
||||
|
||||
if (foundedCard != null) {
|
||||
deckCardInfo = new DeckCardInfo(foundedCard.getName(), foundedCard.getCardNumber(), foundedCard.getSetCode());
|
||||
}
|
||||
if (deckCardInfo != null) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
@ -92,8 +117,6 @@ public class DckDeckImporter extends DeckImporter {
|
|||
deckList.getSideboard().add(deckCardInfo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sbMessage.append("Could not find card '").append("' at line ").append(lineCount).append(": ").append(line).append('\n');
|
||||
}
|
||||
} else if (line.startsWith("NAME:")) {
|
||||
deckList.setName(line.substring(5, line.length()));
|
||||
|
|
|
@ -327,12 +327,19 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
public CardInfo findPreferedCoreExpansionCard(String name, boolean caseInsensitive) {
|
||||
return findPreferedCoreExpansionCard(name, caseInsensitive, null);
|
||||
}
|
||||
|
||||
|
||||
public CardInfo findPreferedCoreExpansionCard(String name, boolean caseInsensitive, String preferedSetCode) {
|
||||
|
||||
List<CardInfo> cards;
|
||||
if (caseInsensitive) {
|
||||
cards = findCardsCaseInsensitive(name);
|
||||
} else {
|
||||
cards = findCards(name);
|
||||
}
|
||||
|
||||
if (!cards.isEmpty()) {
|
||||
Date lastReleaseDate = null;
|
||||
Date lastExpansionDate = null;
|
||||
|
@ -340,6 +347,11 @@ public enum CardRepository {
|
|||
for (CardInfo cardinfo : cards) {
|
||||
ExpansionInfo set = ExpansionRepository.instance.getSetByCode(cardinfo.getSetCode());
|
||||
if (set != null) {
|
||||
|
||||
if ((preferedSetCode != null) && (preferedSetCode.equals(set.getCode()))){
|
||||
return cardinfo;
|
||||
}
|
||||
|
||||
if ((set.getType() == SetType.EXPANSION || set.getType() == SetType.CORE)
|
||||
&& (lastExpansionDate == null || set.getReleaseDate().after(lastExpansionDate))) {
|
||||
cardToUse = cardinfo;
|
||||
|
|
|
@ -61,6 +61,7 @@ public final class StaticFilters {
|
|||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE = new FilterCreaturePermanent();
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_A_CREATURE = new FilterCreaturePermanent("a creature");
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES = new FilterCreaturePermanent("creatures");
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES_CONTROLLED = new FilterCreaturePermanent("creatures you control");
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_GOBLINS = new FilterCreaturePermanent(SubType.GOBLIN, "Goblin creatures");
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_SLIVERS = new FilterCreaturePermanent(SubType.SLIVER, "all Sliver creatures");
|
||||
public static final FilterPlaneswalkerPermanent FILTER_PERMANENT_PLANESWALKER = new FilterPlaneswalkerPermanent();
|
||||
|
@ -108,6 +109,8 @@ public final class StaticFilters {
|
|||
|
||||
FILTER_ATTACKING_CREATURES.add(new AttackingPredicate());
|
||||
|
||||
FILTER_PERMANENT_CREATURES_CONTROLLED.add(new ControllerPredicate(TargetController.YOU));
|
||||
|
||||
FILTER_PERMANENT_ARTIFACT_OR_CREATURE.add(Predicates.or(
|
||||
new CardTypePredicate(CardType.ARTIFACT),
|
||||
new CardTypePredicate(CardType.CREATURE)
|
||||
|
|
|
@ -1901,6 +1901,15 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (target instanceof TargetCard) {
|
||||
Card attachedTo = getCard(perm.getAttachedTo());
|
||||
if (attachedTo == null
|
||||
|| !((TargetCard) spellAbility.getTargets().get(0)).canTarget(perm.getControllerId(), perm.getAttachedTo(), spellAbility, this)) {
|
||||
if (movePermanentToGraveyardWithInfo(perm)) {
|
||||
attachedTo.removeAttachment(perm.getId(), this);
|
||||
somethingHappened = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,7 +233,8 @@ public class GameEvent implements Serializable {
|
|||
PAID_CUMULATIVE_UPKEEP,
|
||||
DIDNT_PAY_CUMULATIVE_UPKEEP,
|
||||
//permanent events
|
||||
ENTERS_THE_BATTLEFIELD_SELF, // 616.1a If any of the replacement and/or prevention effects are self-replacement effects (see rule 614.15), one of them must be chosen. If not, proceed to rule 616.1b.
|
||||
ENTERS_THE_BATTLEFIELD_SELF, /* 616.1a If any of the replacement and/or prevention effects are self-replacement effects (see rule 614.15),
|
||||
one of them must be chosen. If not, proceed to rule 616.1b. */
|
||||
ENTERS_THE_BATTLEFIELD_CONTROL, // 616.1b
|
||||
ENTERS_THE_BATTLEFIELD_COPY, // 616.1c
|
||||
ENTERS_THE_BATTLEFIELD, // 616.1d
|
||||
|
|
|
@ -116,10 +116,11 @@ public interface Permanent extends Card, Controllable {
|
|||
|
||||
void attachTo(UUID permanentId, Game game);
|
||||
|
||||
boolean addAttachment(UUID permanentId, Game game);
|
||||
|
||||
boolean removeAttachment(UUID permanentId, Game game);
|
||||
void unattach(Game game);
|
||||
|
||||
// boolean addAttachment(UUID permanentId, Game game);
|
||||
//
|
||||
// boolean removeAttachment(UUID permanentId, Game game);
|
||||
boolean canBeTargetedBy(MageObject source, UUID controllerId, Game game);
|
||||
|
||||
boolean hasProtectionFrom(MageObject source, Game game);
|
||||
|
|
|
@ -56,6 +56,7 @@ import mage.game.permanent.token.SquirrelToken;
|
|||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.ThreadLocalStringBuilder;
|
||||
|
@ -103,7 +104,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
protected int maxBlockedBy = 0;
|
||||
protected boolean removedFromCombat;
|
||||
protected boolean deathtouched;
|
||||
protected List<UUID> attachments = new ArrayList<>();
|
||||
|
||||
protected Map<String, List<UUID>> connectedCards = new HashMap<>();
|
||||
protected Set<MageObjectReference> dealtDamageByThisTurn;
|
||||
protected UUID attachedTo;
|
||||
|
@ -147,7 +148,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.blocking = permanent.blocking;
|
||||
this.maxBlocks = permanent.maxBlocks;
|
||||
this.deathtouched = permanent.deathtouched;
|
||||
this.attachments.addAll(permanent.attachments);
|
||||
// this.attachments.addAll(permanent.attachments);
|
||||
for (Map.Entry<String, List<UUID>> entry : permanent.connectedCards.entrySet()) {
|
||||
this.connectedCards.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
@ -626,47 +627,46 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public List<UUID> getAttachments() {
|
||||
// return attachments;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public List<UUID> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAttachment(UUID permanentId, Game game) {
|
||||
if (!this.attachments.contains(permanentId)) {
|
||||
if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ATTACH, objectId, permanentId, controllerId))) {
|
||||
this.attachments.add(permanentId);
|
||||
Permanent attachment = game.getPermanent(permanentId);
|
||||
if (attachment == null) {
|
||||
attachment = game.getPermanentEntering(permanentId);
|
||||
}
|
||||
if (attachment != null) {
|
||||
attachment.attachTo(objectId, game);
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.ATTACHED, objectId, permanentId, controllerId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAttachment(UUID permanentId, Game game) {
|
||||
if (this.attachments.contains(permanentId)) {
|
||||
if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, objectId, permanentId, controllerId))) {
|
||||
this.attachments.remove(permanentId);
|
||||
Permanent attachment = game.getPermanent(permanentId);
|
||||
if (attachment != null) {
|
||||
attachment.attachTo(null, game);
|
||||
}
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, objectId, permanentId, controllerId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean addAttachment(UUID permanentId, Game game) {
|
||||
// if (!this.attachments.contains(permanentId)) {
|
||||
// if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ATTACH, objectId, permanentId, controllerId))) {
|
||||
// this.attachments.add(permanentId);
|
||||
// Permanent attachment = game.getPermanent(permanentId);
|
||||
// if (attachment == null) {
|
||||
// attachment = game.getPermanentEntering(permanentId);
|
||||
// }
|
||||
// if (attachment != null) {
|
||||
// attachment.attachTo(objectId, game);
|
||||
// game.fireEvent(new GameEvent(GameEvent.EventType.ATTACHED, objectId, permanentId, controllerId));
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean removeAttachment(UUID permanentId, Game game) {
|
||||
// if (this.attachments.contains(permanentId)) {
|
||||
// if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, objectId, permanentId, controllerId))) {
|
||||
// this.attachments.remove(permanentId);
|
||||
// Permanent attachment = game.getPermanent(permanentId);
|
||||
// if (attachment != null) {
|
||||
// attachment.attachTo(null, game);
|
||||
// }
|
||||
// game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, objectId, permanentId, controllerId));
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
@Override
|
||||
public UUID getAttachedTo() {
|
||||
return attachedTo;
|
||||
|
@ -705,15 +705,27 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void attachTo(UUID permanentId, Game game) {
|
||||
if (this.attachedTo != null && !Objects.equals(this.attachedTo, permanentId)) {
|
||||
Permanent attachment = game.getPermanent(this.attachedTo);
|
||||
if (attachment != null) {
|
||||
attachment.removeAttachment(this.objectId, game);
|
||||
public void unattach(Game game) {
|
||||
this.attachedTo = null;
|
||||
this.addInfo("attachedToCard", null, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachTo(UUID attachToObjectId, Game game) {
|
||||
if (this.attachedTo != null && !Objects.equals(this.attachedTo, attachToObjectId)) {
|
||||
Permanent attachedToUntilNowObject = game.getPermanent(this.attachedTo);
|
||||
if (attachedToUntilNowObject != null) {
|
||||
attachedToUntilNowObject.removeAttachment(this.objectId, game);
|
||||
} else {
|
||||
Card attachedToUntilNowCard = game.getCard(this.attachedTo);
|
||||
if (attachedToUntilNowCard != null) {
|
||||
attachedToUntilNowCard.removeAttachment(this.objectId, game);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this.attachedTo = permanentId;
|
||||
this.attachedToZoneChangeCounter = game.getState().getZoneChangeCounter(permanentId);
|
||||
this.attachedTo = attachToObjectId;
|
||||
this.attachedToZoneChangeCounter = game.getState().getZoneChangeCounter(attachToObjectId);
|
||||
for (Ability ability : this.getAbilities()) {
|
||||
for (Iterator<Effect> ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext();) {
|
||||
ContinuousEffect effect = (ContinuousEffect) ite.next();
|
||||
|
@ -726,6 +738,13 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!getSpellAbility().getTargets().isEmpty() && (getSpellAbility().getTargets().get(0) instanceof TargetCard)) {
|
||||
Card attachedToCard = game.getCard(this.getAttachedTo());
|
||||
if (attachedToCard != null) {
|
||||
// Because cards are not on the battlefield, the relation has to be shown in the card tooltip (e.g. the enchanted card in graveyard)
|
||||
this.addInfo("attachedToCard", CardUtil.addToolTipMarkTags("Enchanted card: " + attachedToCard.getIdName()), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -955,4 +955,19 @@ public class Spell extends StackObjImpl implements Card {
|
|||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAttachments() {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAttachment(UUID permanentId, Game game) {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAttachment(UUID permanentId, Game game) {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -817,6 +817,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
Player attachedToPlayer = game.getPlayer(permanent.getAttachedTo());
|
||||
if (attachedToPlayer != null) {
|
||||
attachedToPlayer.removeAttachment(permanent, game);
|
||||
} else {
|
||||
Card attachedToCard = game.getCard(permanent.getAttachedTo());
|
||||
if (attachedToCard != null) {
|
||||
attachedToCard.removeAttachment(permanent.getId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2326,7 +2331,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
newTarget.setCardLimit(Math.min(librarySearchLimit, cardsFromTop.size()));
|
||||
count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit);
|
||||
}
|
||||
|
||||
|
||||
if (count < target.getNumberOfTargets()) {
|
||||
newTarget.setMinNumberOfTargets(count);
|
||||
}
|
||||
|
@ -3278,9 +3283,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCards(Card card, Zone toZone,
|
||||
Ability source, Game game
|
||||
) {
|
||||
public boolean moveCards(Card card, Zone toZone, Ability source, Game game) {
|
||||
return moveCards(card, toZone, source, game, false, false, false, null);
|
||||
}
|
||||
|
||||
|
|
46
Mage/src/main/java/mage/watchers/WatcherUtils.java
Normal file
46
Mage/src/main/java/mage/watchers/WatcherUtils.java
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.watchers;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class WatcherUtils {
|
||||
|
||||
public static void logMissingWatcher(Game game, Ability source, Class watcherClass, Class usingClass) {
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
Logger.getLogger(usingClass).error("Needed watcher is not started " + watcherClass.getSimpleName()
|
||||
+ " - " + (sourceObject == null ? " no source object" : sourceObject.getName()));
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ import mage.watchers.Watcher;
|
|||
|
||||
/*
|
||||
* Counts amount of life lost current or last turn by players.
|
||||
* This watcher is always added in gameImpl.init
|
||||
* This watcher is automatically started in gameImpl.init for each game
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
|
|
@ -1,288 +1,298 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
#author: North
|
||||
|
||||
use Text::Template;
|
||||
use strict;
|
||||
|
||||
|
||||
my $authorFile = 'author.txt';
|
||||
my $dataFile = "mtg-cards-data.txt";
|
||||
my $setsFile = "mtg-sets-data.txt";
|
||||
my $knownSetsFile = "known-sets.txt";
|
||||
|
||||
|
||||
my %cards;
|
||||
my %sets;
|
||||
my %knownSets;
|
||||
|
||||
my @setCards;
|
||||
|
||||
# gets the set name
|
||||
my $setName = $ARGV[0];
|
||||
if(!$setName) {
|
||||
print 'Enter a set name: ';
|
||||
$setName = <STDIN>;
|
||||
chomp $setName;
|
||||
}
|
||||
|
||||
my $template = Text::Template->new(TYPE => 'FILE', SOURCE => 'cardExtendedClass.tmpl', DELIMITERS => [ '[=', '=]' ]);
|
||||
my $templateBasicLand = Text::Template->new(TYPE => 'FILE', SOURCE => 'cardExtendedLandClass.tmpl', DELIMITERS => [ '[=', '=]' ]);
|
||||
|
||||
sub toCamelCase {
|
||||
my $string = $_[0];
|
||||
$string =~ s/\b([\w']+)\b/ucfirst($1)/ge;
|
||||
$string =~ s/[-,\s\'\/]//g;
|
||||
$string;
|
||||
}
|
||||
|
||||
my $author;
|
||||
if (-e $authorFile) {
|
||||
open (DATA, $authorFile);
|
||||
$author = <DATA>;
|
||||
close(DATA);
|
||||
} else {
|
||||
$author = 'anonymous';
|
||||
}
|
||||
|
||||
my $cardsFound = 0;
|
||||
my %all_sets;
|
||||
|
||||
print ("Opening $dataFile\n");
|
||||
open (DATA, $dataFile) || die "can't open $dataFile";
|
||||
while(my $line = <DATA>) {
|
||||
my @data = split('\\|', $line);
|
||||
$cards{$data[0]}{$data[1]} = \@data;
|
||||
|
||||
if ($data[1] eq $setName) {
|
||||
my $cardInfo = "$data[0],,,$data[2]";
|
||||
|
||||
push(@setCards, $cardInfo);
|
||||
} else {
|
||||
$all_sets {$data[1]} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Fix up split cards
|
||||
my $potentialSideA;
|
||||
my @tempSetCards;
|
||||
|
||||
foreach $potentialSideA (sort @setCards) {
|
||||
#print (">>$potentialSideA\n");
|
||||
if ($potentialSideA =~ m/.*,,,(\d+)(a)$/) {
|
||||
my $cardNumSideB = $1 . "b";
|
||||
my $orig_cardNumSideB = $1 . "b";
|
||||
my $val;
|
||||
foreach $val (sort @setCards) {
|
||||
if ($val =~ m/$cardNumSideB$/) {
|
||||
$potentialSideA =~ s/,,,.*//;
|
||||
$val =~ s/,,,.*//;
|
||||
|
||||
# Add 'SideaSideb' to %cards
|
||||
my $ds = $cards{$val}{$setName};
|
||||
print ("$potentialSideA$val,,,$cardNumSideB\n");
|
||||
my @newCard;
|
||||
push (@newCard, "$potentialSideA$val");
|
||||
push (@newCard, "$setName");
|
||||
push (@newCard, "SPLIT");
|
||||
push (@newCard, @$ds[3]);
|
||||
push (@newCard, "$potentialSideA // $val");
|
||||
$cards{$newCard[0]}{$newCard[1]} = \@newCard;
|
||||
|
||||
$cardNumSideB =~ s/.$//;
|
||||
push (@tempSetCards, "$potentialSideA$val,,,$cardNumSideB");
|
||||
|
||||
print ("Adding in: $potentialSideA \/\/ $val,,,$cardNumSideB\n");
|
||||
$cardsFound = $cardsFound - 1;
|
||||
$cardNumSideB = $orig_cardNumSideB;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($potentialSideA =~ m/.*,,,(\d+)(b)$/) {
|
||||
next;
|
||||
}
|
||||
else {
|
||||
$cardsFound = $cardsFound + 1;
|
||||
push (@tempSetCards, $potentialSideA);
|
||||
}
|
||||
}
|
||||
@setCards = @tempSetCards;
|
||||
|
||||
close(DATA);
|
||||
print "Number of cards found for set " . $setName . ": " . $cardsFound . "\n";
|
||||
|
||||
|
||||
if ($cardsFound == 0) {
|
||||
$setName =~ s/^(...).*/$1/;
|
||||
my $poss;
|
||||
my $foundPossibleSet = 0;
|
||||
my $numPossibleSets = 0;
|
||||
foreach $poss (sort keys (%all_sets)) {
|
||||
$numPossibleSets++;
|
||||
if ($poss =~ m/^$setName/i) {
|
||||
print ("Did you possibly mean: $poss ?\n");
|
||||
$foundPossibleSet = 1;
|
||||
}
|
||||
}
|
||||
if (!$foundPossibleSet) {
|
||||
print ("Couldn't find any matching set for '$setName'. \n");
|
||||
}
|
||||
|
||||
print "(Note: Looked at $numPossibleSets sets in total).\nPress the enter key to exit.";
|
||||
$setName = <STDIN>;
|
||||
exit;
|
||||
}
|
||||
|
||||
open (DATA, $setsFile) || die "can't open $setsFile";
|
||||
|
||||
while(my $line = <DATA>) {
|
||||
my @data = split('\\|', $line);
|
||||
$sets{$data[0]}= $data[1];
|
||||
}
|
||||
close(DATA);
|
||||
|
||||
open (DATA, $knownSetsFile) || die "can't open $knownSetsFile";
|
||||
while(my $line = <DATA>) {
|
||||
my @data = split('\\|', $line);
|
||||
$knownSets{$data[0]}= $data[1];
|
||||
}
|
||||
close(DATA);
|
||||
|
||||
my %raritiesConversion;
|
||||
$raritiesConversion{'C'} = 'COMMON';
|
||||
$raritiesConversion{'U'} = 'UNCOMMON';
|
||||
$raritiesConversion{'R'} = 'RARE';
|
||||
$raritiesConversion{'M'} = 'MYTHIC';
|
||||
$raritiesConversion{'Special'} = 'SPECIAL';
|
||||
$raritiesConversion{'Bonus'} = 'BONUS';
|
||||
sub getRarity
|
||||
{
|
||||
my $val = $_ [0];
|
||||
if (exists ($raritiesConversion {$val}))
|
||||
{
|
||||
return $raritiesConversion {$val};
|
||||
}
|
||||
print ("ERROR DETECTED! - Incorrect rarity.. --- $val,,,$_[1]\n");
|
||||
sleep (10);
|
||||
print "Press the enter key to exit.";
|
||||
$setName = <STDIN>;
|
||||
exit;
|
||||
}
|
||||
|
||||
# Generate the cards
|
||||
|
||||
my %vars;
|
||||
$vars{'author'} = $author;
|
||||
$vars{'set'} = $knownSets{$setName};
|
||||
$vars{'expansionSetCode'} = $sets{$setName};
|
||||
|
||||
my $landForest = 0;
|
||||
my $landMountain = 0;
|
||||
my $landSwamp = 0;
|
||||
my $landPlains = 0;
|
||||
my $landIsland = 0;
|
||||
|
||||
print ("Reading in existing cards in set\n");
|
||||
open (SET_FILE, "../../mage/Mage.Sets/src/mage/sets/$knownSets{$setName}.java") || die "can't open $dataFile";
|
||||
my %alreadyIn;
|
||||
while (<SET_FILE>) {
|
||||
my $line = $_;
|
||||
if ($line =~ m/SetCardInfo.*\("(.*)", (\d+).*/)
|
||||
{
|
||||
$alreadyIn {$2} = $1;
|
||||
}
|
||||
}
|
||||
close SET_FILE;
|
||||
|
||||
my $name_collectorid;
|
||||
my %implemented;
|
||||
my %implementedButNotInSetYet;
|
||||
my %unimplemented;
|
||||
|
||||
|
||||
my %githubTask;
|
||||
|
||||
foreach $name_collectorid (sort @setCards)
|
||||
{
|
||||
my $cardName;
|
||||
my $cardNr;
|
||||
$name_collectorid =~ m/^(.*),,,(.*)$/;
|
||||
$cardName = $1;
|
||||
$cardNr = $2;
|
||||
{
|
||||
if($cardName eq "Forest" || $cardName eq "Island" || $cardName eq "Plains" || $cardName eq "Swamp" || $cardName eq "Mountain") {
|
||||
my $found = 0;
|
||||
if ($cardName eq "Forest") {
|
||||
$landForest++;
|
||||
}
|
||||
if ($cardName eq "Mountain") {
|
||||
$landMountain++;
|
||||
}
|
||||
if ($cardName eq "Swamp") {
|
||||
$landSwamp++;
|
||||
}
|
||||
if ($cardName eq "Plains") {
|
||||
$landPlains++;
|
||||
}
|
||||
if ($cardName eq "Island") {
|
||||
$landIsland++;
|
||||
}
|
||||
if (!exists ($alreadyIn{$cardNr})) {
|
||||
print (" cards.add(new SetCardInfo(\"$cardName\", $cardNr, Rarity.LAND, mage.cards.basiclands.$cardName.class, USE_RANDOM_ART));\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $ds;
|
||||
$ds = $cards{$cardName}{$setName};
|
||||
my $className = toCamelCase($cardName);
|
||||
my $setId = lc($cardName);
|
||||
$setId =~ s/^(.).*/$1/;
|
||||
my $googleSetName = $setName;
|
||||
$googleSetName =~ s/ /+/img;
|
||||
my $fn = "..\\Mage.Sets\\src\\mage\\cards\\$setId\\$className.java";
|
||||
my $str = " cards.add(new SetCardInfo(\"$cardName\", $cardNr, Rarity." . getRarity ($cards{$cardName}{$setName}[3], $cardName) . ", mage.cards.$setId.$className.class));\n";
|
||||
|
||||
if (@$ds[2] eq "SPLIT") {
|
||||
my $oldCardName = $cardName;
|
||||
$cardName = @$ds[4];
|
||||
$str = " cards.add(new SetCardInfo(\"$cardName\", $cardNr, Rarity." . getRarity ($cards{$oldCardName}{$setName}[3], $oldCardName) . ", mage.cards.$setId.$className.class));\n";
|
||||
}
|
||||
|
||||
my $plus_cardName = $cardName;
|
||||
$plus_cardName =~ s/ /+/img;
|
||||
$plus_cardName =~ s/,/+/img;
|
||||
$plus_cardName = "intext:\"$plus_cardName\"";
|
||||
|
||||
if (!exists ($alreadyIn{$cardNr})) {
|
||||
# Go Looking for the existing implementation..
|
||||
if (-e $fn) {
|
||||
$implementedButNotInSetYet {$str} = 1;
|
||||
$githubTask {"- [ ] Implemented but have to add to set -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
} else {
|
||||
$unimplemented {"$str"} = 1;
|
||||
$githubTask {"- [ ] Not done -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
}
|
||||
} else {
|
||||
if (-e $fn) {
|
||||
$implemented {$str} = 1;
|
||||
$githubTask {"- [x] Done -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
} else {
|
||||
$unimplemented {$str} = 1;
|
||||
$githubTask {"- [ ] Not done -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "Implemented cards:\n";
|
||||
print (join ("", sort keys (%implemented)));
|
||||
print "\n\n\nImplemented but-not-yet-added-to-set cards:\n";
|
||||
print (join ("", sort keys (%implementedButNotInSetYet)));
|
||||
print "\n\n\nUnimplemented cards:\n";
|
||||
print (join ("", sort keys (%unimplemented)));
|
||||
print "\n\n\nGithub Task:\n";
|
||||
print (join ("", sort keys (%githubTask)));
|
||||
print ("\nData from reading: ../../mage/Mage.Sets/src/mage/sets/$knownSets{$setName}.java\n");
|
||||
print "\n\nYou are done. Press the enter key to exit.";
|
||||
$setName = <STDIN>;
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
#author: North
|
||||
|
||||
use Text::Template;
|
||||
use strict;
|
||||
|
||||
|
||||
my $authorFile = 'author.txt';
|
||||
my $dataFile = "mtg-cards-data.txt";
|
||||
my $setsFile = "mtg-sets-data.txt";
|
||||
my $knownSetsFile = "known-sets.txt";
|
||||
|
||||
|
||||
my %cards;
|
||||
my %sets;
|
||||
my %knownSets;
|
||||
|
||||
my @setCards;
|
||||
|
||||
# gets the set name
|
||||
my $setName = $ARGV[0];
|
||||
if(!$setName) {
|
||||
print 'Enter a set name: ';
|
||||
$setName = <STDIN>;
|
||||
chomp $setName;
|
||||
}
|
||||
|
||||
my $template = Text::Template->new(TYPE => 'FILE', SOURCE => 'cardExtendedClass.tmpl', DELIMITERS => [ '[=', '=]' ]);
|
||||
my $templateBasicLand = Text::Template->new(TYPE => 'FILE', SOURCE => 'cardExtendedLandClass.tmpl', DELIMITERS => [ '[=', '=]' ]);
|
||||
|
||||
sub toCamelCase {
|
||||
my $string = $_[0];
|
||||
$string =~ s/\b([\w']+)\b/ucfirst($1)/ge;
|
||||
$string =~ s/[-,\s\'\/]//g;
|
||||
$string;
|
||||
}
|
||||
|
||||
my $author;
|
||||
if (-e $authorFile) {
|
||||
open (DATA, $authorFile);
|
||||
$author = <DATA>;
|
||||
close(DATA);
|
||||
} else {
|
||||
$author = 'anonymous';
|
||||
}
|
||||
|
||||
my $cardsFound = 0;
|
||||
my %all_sets;
|
||||
|
||||
print ("Opening $dataFile\n");
|
||||
open (DATA, $dataFile) || die "can't open $dataFile";
|
||||
while(my $line = <DATA>) {
|
||||
my @data = split('\\|', $line);
|
||||
$cards{$data[0]}{$data[1]} = \@data;
|
||||
|
||||
if ($data[1] eq $setName) {
|
||||
my $cardInfo = "$data[0],,,$data[2]";
|
||||
|
||||
push(@setCards, $cardInfo);
|
||||
} else {
|
||||
$all_sets {$data[1]} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Fix up split cards
|
||||
my $potentialSideA;
|
||||
my @tempSetCards;
|
||||
|
||||
foreach $potentialSideA (sort @setCards) {
|
||||
#print (">>$potentialSideA\n");
|
||||
if ($potentialSideA =~ m/.*,,,(\d+)(a)$/) {
|
||||
my $cardNumSideB = $1 . "b";
|
||||
my $orig_cardNumSideB = $1 . "b";
|
||||
my $val;
|
||||
foreach $val (sort @setCards) {
|
||||
if ($val =~ m/$cardNumSideB$/) {
|
||||
$potentialSideA =~ s/,,,.*//;
|
||||
$val =~ s/,,,.*//;
|
||||
|
||||
# Add 'SideaSideb' to %cards
|
||||
my $ds = $cards{$val}{$setName};
|
||||
print ("$potentialSideA$val,,,$cardNumSideB\n");
|
||||
my @newCard;
|
||||
push (@newCard, "$potentialSideA$val");
|
||||
push (@newCard, "$setName");
|
||||
push (@newCard, "SPLIT");
|
||||
push (@newCard, @$ds[3]);
|
||||
push (@newCard, "$potentialSideA // $val");
|
||||
$cards{$newCard[0]}{$newCard[1]} = \@newCard;
|
||||
|
||||
$cardNumSideB =~ s/.$//;
|
||||
push (@tempSetCards, "$potentialSideA$val,,,$cardNumSideB");
|
||||
|
||||
print ("Adding in: $potentialSideA \/\/ $val,,,$cardNumSideB\n");
|
||||
$cardsFound = $cardsFound - 1;
|
||||
$cardNumSideB = $orig_cardNumSideB;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($potentialSideA =~ m/.*,,,(\d+)(b)$/) {
|
||||
next;
|
||||
}
|
||||
else {
|
||||
$cardsFound = $cardsFound + 1;
|
||||
push (@tempSetCards, $potentialSideA);
|
||||
}
|
||||
}
|
||||
@setCards = @tempSetCards;
|
||||
|
||||
close(DATA);
|
||||
print "Number of cards found for set " . $setName . ": " . $cardsFound . "\n";
|
||||
|
||||
|
||||
if ($cardsFound == 0) {
|
||||
$setName =~ s/^(...).*/$1/;
|
||||
my $poss;
|
||||
my $foundPossibleSet = 0;
|
||||
my $numPossibleSets = 0;
|
||||
foreach $poss (sort keys (%all_sets)) {
|
||||
$numPossibleSets++;
|
||||
if ($poss =~ m/^$setName/i) {
|
||||
print ("Did you possibly mean: $poss ?\n");
|
||||
$foundPossibleSet = 1;
|
||||
}
|
||||
}
|
||||
if (!$foundPossibleSet) {
|
||||
print ("Couldn't find any matching set for '$setName'. \n");
|
||||
}
|
||||
|
||||
print "(Note: Looked at $numPossibleSets sets in total).\nPress the enter key to exit.";
|
||||
$setName = <STDIN>;
|
||||
exit;
|
||||
}
|
||||
|
||||
open (DATA, $setsFile) || die "can't open $setsFile";
|
||||
|
||||
while(my $line = <DATA>) {
|
||||
my @data = split('\\|', $line);
|
||||
$sets{$data[0]}= $data[1];
|
||||
}
|
||||
close(DATA);
|
||||
|
||||
open (DATA, $knownSetsFile) || die "can't open $knownSetsFile";
|
||||
while(my $line = <DATA>) {
|
||||
my @data = split('\\|', $line);
|
||||
$knownSets{$data[0]}= $data[1];
|
||||
}
|
||||
close(DATA);
|
||||
|
||||
my %raritiesConversion;
|
||||
$raritiesConversion{'C'} = 'COMMON';
|
||||
$raritiesConversion{'U'} = 'UNCOMMON';
|
||||
$raritiesConversion{'R'} = 'RARE';
|
||||
$raritiesConversion{'M'} = 'MYTHIC';
|
||||
$raritiesConversion{'Special'} = 'SPECIAL';
|
||||
$raritiesConversion{'Bonus'} = 'BONUS';
|
||||
sub getRarity
|
||||
{
|
||||
my $val = $_ [0];
|
||||
if (exists ($raritiesConversion {$val}))
|
||||
{
|
||||
return $raritiesConversion {$val};
|
||||
}
|
||||
print ("ERROR DETECTED! - Incorrect rarity.. --- $val,,,$_[1]\n");
|
||||
sleep (10);
|
||||
print "Press the enter key to exit.";
|
||||
$setName = <STDIN>;
|
||||
exit;
|
||||
}
|
||||
|
||||
# Generate the cards
|
||||
|
||||
my %vars;
|
||||
$vars{'author'} = $author;
|
||||
$vars{'set'} = $knownSets{$setName};
|
||||
$vars{'expansionSetCode'} = $sets{$setName};
|
||||
|
||||
my $landForest = 0;
|
||||
my $landMountain = 0;
|
||||
my $landSwamp = 0;
|
||||
my $landPlains = 0;
|
||||
my $landIsland = 0;
|
||||
|
||||
print ("Reading in existing cards in set\n");
|
||||
open (SET_FILE, "../../mage/Mage.Sets/src/mage/sets/$knownSets{$setName}.java") || die "can't open $dataFile";
|
||||
my %alreadyIn;
|
||||
while (<SET_FILE>) {
|
||||
my $line = $_;
|
||||
if ($line =~ m/SetCardInfo.*\("(.*)", (\d+).*/)
|
||||
{
|
||||
$alreadyIn {$2} = $1;
|
||||
}
|
||||
}
|
||||
close SET_FILE;
|
||||
|
||||
my $name_collectorid;
|
||||
my %implemented;
|
||||
my %implementedButNotInSetYet;
|
||||
my %unimplemented;
|
||||
|
||||
|
||||
my %githubTask;
|
||||
|
||||
foreach $name_collectorid (sort @setCards)
|
||||
{
|
||||
my $cardName;
|
||||
my $cardNr;
|
||||
$name_collectorid =~ m/^(.*),,,(.*)$/;
|
||||
$cardName = $1;
|
||||
$cardNr = $2;
|
||||
{
|
||||
if($cardName eq "Forest" || $cardName eq "Island" || $cardName eq "Plains" || $cardName eq "Swamp" || $cardName eq "Mountain") {
|
||||
my $found = 0;
|
||||
if ($cardName eq "Forest") {
|
||||
$landForest++;
|
||||
}
|
||||
if ($cardName eq "Mountain") {
|
||||
$landMountain++;
|
||||
}
|
||||
if ($cardName eq "Swamp") {
|
||||
$landSwamp++;
|
||||
}
|
||||
if ($cardName eq "Plains") {
|
||||
$landPlains++;
|
||||
}
|
||||
if ($cardName eq "Island") {
|
||||
$landIsland++;
|
||||
}
|
||||
if (!exists ($alreadyIn{$cardNr})) {
|
||||
print (" cards.add(new SetCardInfo(\"$cardName\", $cardNr, Rarity.LAND, mage.cards.basiclands.$cardName.class, USE_RANDOM_ART));\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $ds;
|
||||
$ds = $cards{$cardName}{$setName};
|
||||
my $className = toCamelCase($cardName);
|
||||
my $setId = lc($cardName);
|
||||
$setId =~ s/^(.).*/$1/;
|
||||
my $googleSetName = $setName;
|
||||
$googleSetName =~ s/ /+/img;
|
||||
my $fn = "..\\Mage.Sets\\src\\mage\\cards\\$setId\\$className.java";
|
||||
my $str = " cards.add(new SetCardInfo(\"$cardName\", $cardNr, Rarity." . getRarity ($cards{$cardName}{$setName}[3], $cardName) . ", mage.cards.$setId.$className.class));\n";
|
||||
|
||||
if (@$ds[2] eq "SPLIT") {
|
||||
my $oldCardName = $cardName;
|
||||
$cardName = @$ds[4];
|
||||
$str = " cards.add(new SetCardInfo(\"$cardName\", $cardNr, Rarity." . getRarity ($cards{$oldCardName}{$setName}[3], $oldCardName) . ", mage.cards.$setId.$className.class));\n";
|
||||
}
|
||||
|
||||
my $plus_cardName = $cardName;
|
||||
$plus_cardName =~ s/ /+/img;
|
||||
$plus_cardName =~ s/,/+/img;
|
||||
$plus_cardName = "intext:\"$plus_cardName\"";
|
||||
|
||||
if (!exists ($alreadyIn{$cardNr})) {
|
||||
# Go Looking for the existing implementation..
|
||||
if (-e $fn) {
|
||||
$implementedButNotInSetYet {$str} = 1;
|
||||
$githubTask {"- [ ] Implemented but have to add to set -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
} else {
|
||||
$unimplemented {"$str"} = 1;
|
||||
$githubTask {"- [ ] Not done -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
}
|
||||
} else {
|
||||
if (-e $fn) {
|
||||
$implemented {$str} = 1;
|
||||
$githubTask {"- [x] Done -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
} else {
|
||||
$unimplemented {$str} = 1;
|
||||
$githubTask {"- [ ] Not done -- [$cardName](https://www.google.com.au/search?q=$plus_cardName+$googleSetName+mtg&source=lnms&tbm=isch)\n"} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add logic to add the missing card lines to set file automatically
|
||||
#my $setFileName = "../Mage.Sets/src/mage/sets/".$knownSets{$setName}.".java";
|
||||
#print (join("","Add already implemented cards to set file: ", $setFileName,"\n"));
|
||||
#foreach my $line (sort keys (%implementedButNotInSetYet)) {
|
||||
# - Do action to add the line
|
||||
# print $line;
|
||||
#}
|
||||
|
||||
|
||||
print "Implemented cards:\n";
|
||||
print (join ("", sort keys (%implemented)));
|
||||
print "\n\n\nImplemented but-not-yet-added-to-set cards:\n";
|
||||
print (join ("", sort keys (%implementedButNotInSetYet)));
|
||||
print "\n\n\nUnimplemented cards:\n";
|
||||
print (join ("", sort keys (%unimplemented)));
|
||||
print "\n\n\nGithub Task:\n";
|
||||
print (join ("", sort keys (%githubTask)));
|
||||
print ("\nData from reading: ../../mage/Mage.Sets/src/mage/sets/$knownSets{$setName}.java\n");
|
||||
|
||||
print "\n\nYou are done. Press the enter key to exit.";
|
||||
$setName = <STDIN>;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
Afflict|number|
|
||||
Annihilator|number|
|
||||
Ascend|new|
|
||||
Basic landcycling|cost|
|
||||
Battle cry|new|
|
||||
Bestow|card, manaString|
|
||||
|
|
|
@ -32696,36 +32696,52 @@ Forest|Duel Decks: Mind vs. Might|65|L||Basic Land - Forest|||{T}: Add {G} to yo
|
|||
Chandra, Gremlin Wrangler|Heroes of the Realm|1|M|{2}{R}{R}|Legendary Planeswalker - Chandra|3||+1: Create a 2/2 red Gremlin creature token.$-2:Chandra, Gremlin Wrangler deals X damage to target creature or player, where X is the number of Gremlins you control.|
|
||||
Dungeon Master|Heroes of the Realm|1|M|{2}{W}{U}|Legendary Planeswalker - Dungeon Master|||+1: Target opponent creates a 1/1 black Skeleton creature token with “When this creature dies, each opponent loses 2 life.”$+1: Roll a d20. If you roll a 1, skip your next turn. If you roll a 12 or higher, draw a card.$-6: You get an adventuring party. (Your party is a 3/3 red Fighter with first strike, a 1/1 white Cleric with lifelink, a 2/2 black Rogue with hexproof, and a 1/1 blue Wizard with flying.)|
|
||||
Nira, Hellkite Duelist|Heroes of the Realm|3|M|{W}{U}{B}{R}{G}|Legendary Creature — Dragon|6|6|Flash$Flying, trample, haste$When Nira, Hellkite Duelist enters the battlefield, the next time you would lose the game this turn, instead draw three cards and your life total becomes 5.|
|
||||
Famished Paladin|Rivals of Ixalan|8|U|{1}{W}|Creature - Vampire Knight|3|3|Famished Paladin does not untap during your untap step.$Whenever you gain life, untap Famished Paladin.|
|
||||
Paladin of Atonement|Rivals of Ixalan|16|R|{1}{W}|Creature - Vampire Knight|1|1|At the beginning of each upkeep, if you lost life last turn, put a +1/+1 counter on Paladin of Atonement.$When Paladin of Atonement dies, you gain life equal to it's toughness.|
|
||||
Glorious Destiny|Rivals of Ixalan|18|R|{2}{W}|Enchantment|||Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>$As Glorious Destiny enters the battlefield, choose a creature type.$Creatures you control of the chosen type get +1/+1. They have vigilance as long as you have the city's blessing.|
|
||||
Shrine Altisaur|Rivals of Ixalan|28|R|{4}{W}|Creature - Dinosaur|3|4|If a source would deal damage to another Dinosaur you control, prevent all but 1 of that damage.|
|
||||
Skynarcher Aspirant|Rivals of Ixalan|21|U|{W}|Creature - Vampire Soldier|2|1|Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>$Skymarcher Aspirant has flying as long as you have the city's blessing|
|
||||
Temple Altisaur|Rivals of Ixalan|28|R|{4}{W}|Creature - Dinosaur|3|4|If a source would deal damage to another Dinosaur you control, prevent all but 1 of that damage.|
|
||||
Zetalpa, Primal Dawn|Rivals of Ixalan|30|R|{6}{W}{W}|Legendary Creature - Elder Dinosaur|4|8|Flying, double strike, vigilance, trample, indestructible|
|
||||
Admiral's Order|Rivals of Ixalan|31|R|{1}{U}{U}|Instant|||<i>Raid</i> — If you attacked with a creature this turn, you may pay {U} rather than pay this spell's mana cost.$Counter target spell.|
|
||||
Aquatic Incursion|Rivals of Ixalan|32|U|{3}{U}|Enchantment|||When Aquatic Incursion enters the battlefield, create two 1/1 blue Merfolk creature tokens with hexproof.${3}{U}: Target Merfolk can't be blocked this turn.|
|
||||
Induced Amnesia|Rivals of Ixalan|40|R|{2}{U}|Enchantment|||When Induced Amnesia enters the battlefield, target player exiles all the cards in his or her hand face down, then draws that many cards.$When Induced Amnesia is put into a graveyard from the battlefield, return the exiled cards to their owner's hand.|
|
||||
Kumena's Awakening|Rivals of Ixalan|42|R|{2}{U}{U}|Enchantment|||Ascend (If you control ten or more permenants, you get the city's blessing for the rest of the game.)$At the beginning of your upkeep, each player draws a card. If you have the city's blessing, instead only you draw a card.|
|
||||
River Darter|Rivals of Ixalan|47|C|{2}{U}|Creature - Merfolk Warrior|2|3|River Darter can't be blocked by Dinosaurs.|
|
||||
Seafloor Oracle|Rivals of Ixalan|51|R|{2}{W}{W}|Creature - Merfolk Wizard|2|3|Whenever a Merfolk you controls deals combat damage to a player, draw a card.|
|
||||
Secrets of the Golden City|Rivals of Ixalan|52|C|{1}{U}{U}|Sorcery|||Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>$Draw two cards. If you have the city's blessing, draw three cards instead.|
|
||||
Silvergill Adept|Rivals of Ixalan|53|U|{1}{U}|Creature - Merfolk Wizard|2|1|As an additional cost to cast Silvergill Adept, reveal a Merfolk card from your hand or pay {3}.$When Silvergill Adept enters the battlefield, draw a card.|
|
||||
Timestream Navigator|Rivals of Ixalan|59|M|{1}{U}|Creature - Human Pirate Wizard|1|1|Ascend <i>(If you control ten or more permanents, you gain The City's Blessing for the rest of the game.)</i>${2}{U}{U}, {T}, Put Timestream Navigator at the bottom of its owner's library: Take an extra turn after this one. Activate this ability only if you have The City's Blessing.|
|
||||
Dusk Charger|Rivals of Ixalan|69|C|{3}{B}|Creature - Horse|3|3|Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>$Dusk Charger gets +2/+2 as long as you have the city's blessing.|
|
||||
Ravenous Chupacabra|Rivals of Ixalan|82|U|{2}{B}{B}|Creature - Beast Horror|2|2|When Ravenous Chupacabra enters the battlefield, destroy target creature an opponent controls.|
|
||||
Tetzimoc, Primal Death|Rivals of Ixalan|86|R|{4}{B}{B}|Legendary Creature - Elder Dinosaur|6|6|Deathtouch${B}, Reveal Tetzimoc, Primal Death from your hand: Put a prey counter on target creature. Activate this ability only during your turn.$When Tetzimoc, Primal Death enters the battlefield, destroy each creature your opponents control with a prey counter on it.|
|
||||
Vona's Hunger|Rivals of Ixalan|90|R|{2}{B}|Instant|||Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>$Each opponent sacrifices a creature. If you have the city's blessing, instead each opponent sacrifices half the creatures he or she controls rounded up.|
|
||||
Brass's Bounty|Rivals of Ixalan|94|R|{6}{R}|Sorcery|||For each land you control, create a colorless Treasure artifact token with "{t}, Sacrifice this artifact: Add one mana of any color to your mana pool."|
|
||||
Daring Buccaneer|Rivals of Ixalan|98|U|{R}|Creature - Human Pirate|2|2|As an additional cost to cast Daring Buccaneer, reveal a Pirate card from your hand or pay 2.|
|
||||
Etali, Primal Storm|Rivals of Ixalan|100|R|{4}{R}{R}|Legendary Creature - Elder Dinosaur|6|6|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.|
|
||||
Form of the Dinosaur|Rivals of Ixalan|103|R|{4}{R}{R}|Enchantment|||When Form of the Dinosaur enters the battlefield, your life total becomes 15.$At the beginning of your upkeep, Form of the Dinosaur deals 15 damage to target creature an opponent controls and that creature deals damage equal to its power to you.|
|
||||
Frilled Deathspitter|Rivals of Ixalan|104|C|{2}{R}|Creature - Dinosaur|3|2|<i>Enrage</i> — Whenever Frilled Deathspitter is dealt damage, it deals 2 damage to target opponent.|
|
||||
Swaggering Corsair|Rivals of Ixalan|119|C|{2}{R}|Creature - Human Pirate|2|2|<i>Raid</i> — Swaggering Corsair enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn.|
|
||||
Tilonalli's Summoner|Rivals of Ixalan|121|R|{1}{R}|Creature - Human Shaman|1|1|Ascend (If you control ten or more permanents, you get the city's blessing for the rest of the game.)$Whenever Tilonalli's Summoner attacks, you may pay {X}{R}. If you do, create X 1/1 Elemental creature tokens tapped and attacking. Exile them at the beginning of the next end step, unless you have the City's blessing.|
|
||||
Enter the Unknown|Rivals of Ixalan|128|U|{G}|Sorcery|||Target creature you control explores. <i>(Reveal the top card of your library. Put that card into your hand if it's a land. Otherwise, put a +1/+1 counter on this creature, then put the card back or put it into your graveyard.) </i>$You may play an additional land this turn.|
|
||||
Ghalta, Primal Hunger|Rivals of Ixalan|130|R|{1}{0}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control.$Trample|
|
||||
Jadelight Ranger|Rivals of Ixalan|136|R|{1}{G}{G}|Creature - Merfolk Scout|2|1|When Jadelight Ranger enters the battlefield it explores, then it explores again. <i> (Reveal the top card of your library. Put that card into your hand if it's a land. Otherwise, put a +1/+1 counter on this creature, then put the card back or put it into your graveyard. Then repeat this process) </i>|
|
||||
Path to Discovery|Rivals of Ixalan|142|R|{3}{G}|Enchantment|||Whenever a creature enters the battlefield under your control, it explores. (Reveal the top card of your library. Put that card into your hand if it's a land. Otherwise, put a +1/+1 counter on the creature, then put the card back or put it into your graveyard.)|
|
||||
Swift Warden|Rivals of Ixalan|146|U|{1}{G}{G}|Creature - Merfolk Warrior|3|3|When Swift Warden enters the battlefield, target Merfolk you control gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control)|
|
||||
Tendershoot Dryad|Rivals of Ixalan|147|R|{4}{G}|Creature - Dryad|2|2|Ascend <i>(If you control ten or more permanents, you get the city's blessing for the rest of the game.)</i>$At the beginning of each upkeep, create a 1/1 green Saproling creature token.$Saprolings you control get +2/+2 as long as you have the city's blessing.|
|
||||
Thrashing Brontodon|Rivals of Ixalan|148|U|{1}{G}{G}|Creature - Dinosaur|3|4|{1}, Sacrificing Thrashing Brontodon: Destroy target artifact or enchantment.|
|
||||
World Shaper|Rivals of Ixalan|151|R|{3}{G}|Creature - Merfolk Shaman|3|3|Whenever World Shaper attacks, you may put the top three cards of your library into your graveyard.$When World Shaper dies, put all land cards from your graveyard onto the battlefield tapped.|
|
||||
Angrath, the Flame-Chained|Rivals of Ixalan|152|M|{3}{B}{R}|Legendary Planeswalker - Angrath|4|+1: Each opponent discards a card and loses 2 life.$-3: Gain control of target creature until end of turn. Untap it. It gains haste until end of turn. Sacrifice it at the beginning of the next end step if it has converted mana cost 3 or less.$-8: Each opponent loses life equal to the number of cards in his or her graveyard.|
|
||||
Atzocan Seer|Rivals of Ixalan|153|U|{1}{G}{W}|Creature - Human Druid|2|3|{t}: Add one mana of any color to your manan pool.$Sacrifice Atzocan Seer: Return target Dinosaur card from your graveyard to your hand.|
|
||||
Elenda, the Dusk Rose|Rivals of Ixalan|157|M|{2}{W}{B}|Legendary Creature - Vampire Knight|1|1|Lifelink$Whenever another creature dies, put a +1/+1 counter on Elenda, the Dusk Rose.$When Elenda dies, create X 1/1 white Vampire creature tokens with lifelink, where X is Elenda's power|
|
||||
Huatli, Radiant Champion|Rivals of Ixalan|159|M|{2}{G}{W}|Legendary Planeswalker - Huatli|3|+1: Put a loyalty counter on Huatli, Radiant Champion for each creature you control.$-1: Target creature gets +X/+X until end of turn, where X is the number of creatures you control.$-8: You get an emblem with "Whenever a creature enters the battlefield under your control, you may draw a card."|
|
||||
Journey to Eternity|Rivals of Ixalan|160|R|{1}{B}{G}|Legendary Enchantment - Aura|||Enchant creature you control$When enchanted creature dies, return it to the battlefield under your control, then return Journey to Eternity to the battlefield transformed under your control.|
|
||||
Atzal, Cave of Eternity|Rivals of Ixalan|160|R||Legendary Land|||<i>(Transforms from Journey to Eternity.)</i>${t}: Add one mana of any color to your mana pool.${3}{B}{G}, {t}: Return target creature card from your graveyard to the battlefield.|
|
||||
Kumena, Tyrant of Orzca|Rivals of Ixalan|162|M|{1}{G}{U}|Legendary Creature - Merfolk Shaman|2|4|Tap another untapped Merfolk you control: Kumena, Tyrant of Orzca can't be blocked this turn.$Tap three untapped Merfolk you control: Draw a card.$Tap five untapped Merfolk you control: Put a +1/+1 counter on each Merfolk you control.|
|
||||
Legion Lieutenant|Rivals of Ixalan|163|U|{W}{B}|Creature - Vampire Knight|2|2|Other Vampires you control get +1/+1.|
|
||||
Merfolk Mistbender|Rivals of Ixalan|164|U|{G}{U}|Creature - Merfolk Shaman|2|2|Other Merfolk you control get +1/+1.|
|
||||
Storm the Vault|Rivals of Ixalan|173|R|{2}{U}{R}|Legendary Enchantment|||Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."$At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault.|
|
||||
Vault of Catlacan|Rivals of Ixalan|173|R||Legendary Land|||<i>(Transforms from Storm the Vault.)</i>${T}: Add one mana of any color to your mana pool.${T}: Add {U} to your mana pool for each artifact you control.|
|
||||
Awakened Amalgamation|Rivals of Ixalan|175|R|{4}|Artifact Creature - Golem|0|0|Awakened Amalgamation's power and toughness are each equal to the number of lands you control with different names.|
|
||||
Awakened Amalgam|Rivals of Ixalan|175|R|{4}|Artifact Creature - Golem|0|0|Awakened Amalgam's power and toughness are each equal to the number of differently named lands you control.|
|
||||
Captain's Hook|Rivals of Ixalan|177|R|{3}|Artifact - Equipment|||Equipped creature gets +2/+0, has menace, and is a Pirate in addition to its other creature types.$Whenever Captain's Hook becomes unattached from a permanent, destroy that permanent.$Equip {1}|
|
||||
Gleaming Barrier|Rivals of Ixalan|178|C|{2}|Artifact Creature - Wall|0|4|Defender$When Gleaming Barrier dies, create a colorless Treasure artifact token with "{t}, Sacrifice this artifact: Add one mana of any color to your mana pool."|
|
||||
The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate planeswalkers' loyalty abilities.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.|
|
||||
|
|
Loading…
Reference in a new issue