mirror of
https://github.com/correl/mage.git
synced 2024-12-26 19:16:54 +00:00
Merge pull request #43 from magefree/master
Merge https://github.com/magefree/mage
This commit is contained in:
commit
0a1ae58943
51 changed files with 1437 additions and 378 deletions
|
@ -28,6 +28,7 @@
|
|||
package mage.server;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
@ -37,6 +38,8 @@ import java.util.regex.Pattern;
|
|||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.server.exceptions.UserNotFoundException;
|
||||
import mage.server.game.GameController;
|
||||
import mage.server.game.GameManager;
|
||||
import mage.server.util.SystemUtil;
|
||||
import mage.view.ChatMessage.MessageColor;
|
||||
import mage.view.ChatMessage.MessageType;
|
||||
|
@ -220,6 +223,27 @@ public enum ChatManager {
|
|||
chatSessions.get(chatId).broadcastInfoToUser(user, message);
|
||||
return true;
|
||||
}
|
||||
if (command.startsWith("GAME")) {
|
||||
message += "<br/>" + GameManager.instance.getChatId(chatId);
|
||||
ChatSession session = chatSessions.get(chatId);
|
||||
if (session != null && session.getInfo() != null) {
|
||||
String gameId = session.getInfo();
|
||||
if (gameId.startsWith("Game ")) {
|
||||
UUID id = java.util.UUID.fromString(gameId.substring(5, gameId.length()));
|
||||
for (Entry<UUID, GameController> entry : GameManager.instance.getGameController().entrySet()) {
|
||||
if (entry.getKey().equals(id)) {
|
||||
GameController controller = entry.getValue();
|
||||
if (controller != null) {
|
||||
message += controller.getGameStateDebugMessage();
|
||||
chatSessions.get(chatId).broadcastInfoToUser(user, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (command.startsWith("CARD ")) {
|
||||
Matcher matchPattern = getCardTextPattern.matcher(message.toLowerCase());
|
||||
if (matchPattern.find()) {
|
||||
|
@ -289,18 +313,18 @@ public enum ChatManager {
|
|||
public void sendReconnectMessage(UUID userId) {
|
||||
UserManager.instance.getUser(userId).ifPresent(user
|
||||
-> getChatSessions()
|
||||
.stream()
|
||||
.filter(chat -> chat.hasUser(userId))
|
||||
.forEach(chatSession -> chatSession.broadcast(null, user.getName() + " has reconnected", MessageColor.BLUE, true, MessageType.STATUS, null)));
|
||||
.stream()
|
||||
.filter(chat -> chat.hasUser(userId))
|
||||
.forEach(chatSession -> chatSession.broadcast(null, user.getName() + " has reconnected", MessageColor.BLUE, true, MessageType.STATUS, null)));
|
||||
|
||||
}
|
||||
|
||||
public void sendLostConnectionMessage(UUID userId, DisconnectReason reason) {
|
||||
UserManager.instance.getUser(userId).ifPresent(user
|
||||
-> getChatSessions()
|
||||
.stream()
|
||||
.filter(chat -> chat.hasUser(userId))
|
||||
.forEach(chatSession -> chatSession.broadcast(null, user.getName() + reason.getMessage(), MessageColor.BLUE, true, MessageType.STATUS, null)));
|
||||
.stream()
|
||||
.filter(chat -> chat.hasUser(userId))
|
||||
.forEach(chatSession -> chatSession.broadcast(null, user.getName() + reason.getMessage(), MessageColor.BLUE, true, MessageType.STATUS, null)));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -990,7 +990,7 @@ public class TableController {
|
|||
|| !match.isDoneSideboarding()
|
||||
|| (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) {
|
||||
Optional<User> user = UserManager.instance.getUser(userPlayerEntry.getKey());
|
||||
if (!user.isPresent()) {
|
||||
if (!user.isPresent() || user.get().isActive()) {
|
||||
logger.warn("- Active user of match is missing: " + matchPlayer.getName());
|
||||
logger.warn("-- matchId:" + match.getId());
|
||||
logger.warn("-- userId:" + userPlayerEntry.getKey());
|
||||
|
|
|
@ -50,6 +50,7 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.GameException;
|
||||
import mage.game.GameOptions;
|
||||
import mage.game.GameState;
|
||||
import mage.game.Table;
|
||||
import mage.game.events.Listener;
|
||||
import mage.game.events.PlayerQueryEvent;
|
||||
|
@ -1088,4 +1089,97 @@ public class GameController implements GameCallback {
|
|||
return false;
|
||||
}
|
||||
|
||||
public String getGameStateDebugMessage() {
|
||||
if (game == null) {
|
||||
return "";
|
||||
}
|
||||
GameState state = game.getState();
|
||||
if (state == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<br/>Game State:<br/><font size=-2>");
|
||||
sb.append(state);
|
||||
|
||||
sb.append("<br>Active player is: ");
|
||||
sb.append(game.getPlayer(state.getActivePlayerId()).getName());
|
||||
sb.append("<br>isGameOver: ");
|
||||
sb.append(state.isGameOver());
|
||||
sb.append("<br>Current phase is: ");
|
||||
sb.append(state.getTurn().getPhase());
|
||||
sb.append("<br>getBattlefield: ");
|
||||
sb.append(state.getBattlefield());
|
||||
sb.append("<br>getChoosingPlayerId: ");
|
||||
if (state.getChoosingPlayerId() != null) {
|
||||
sb.append(game.getPlayer(state.getChoosingPlayerId()).getName());
|
||||
} else {
|
||||
sb.append("noone!");
|
||||
}
|
||||
sb.append("<br>getCombat: ");
|
||||
sb.append(state.getCombat());
|
||||
sb.append("<br>getCommand: ");
|
||||
sb.append(state.getCommand());
|
||||
sb.append("<br>getContinuousEffects: ");
|
||||
sb.append(state.getContinuousEffects());
|
||||
sb.append("<br>getCopiedCards: ");
|
||||
sb.append(state.getCopiedCards());
|
||||
sb.append("<br>getDelayed: ");
|
||||
sb.append(state.getDelayed());
|
||||
sb.append("<br>getDesignations: ");
|
||||
sb.append(state.getDesignations());
|
||||
sb.append("<br>getExile: ");
|
||||
sb.append(state.getExile());
|
||||
sb.append("<br>getMonarchId: ");
|
||||
sb.append(state.getMonarchId());
|
||||
sb.append("<br>getNextPermanentOrderNumber: ");
|
||||
sb.append(state.getNextPermanentOrderNumber());
|
||||
sb.append("<br>getPlayerByOrderId: ");
|
||||
if (state.getPlayerByOrderId() != null) {
|
||||
sb.append(game.getPlayer(state.getPlayerByOrderId()).getName());
|
||||
} else {
|
||||
sb.append("noone!");
|
||||
}
|
||||
sb.append("<br>getPlayerList: ");
|
||||
sb.append(state.getPlayerList());
|
||||
sb.append("<br>getPlayers: ");
|
||||
sb.append(state.getPlayers());
|
||||
sb.append("<br>Player with Priority is: ");
|
||||
if (state.getPriorityPlayerId() != null) {
|
||||
sb.append(game.getPlayer(state.getPriorityPlayerId()).getName());
|
||||
} else {
|
||||
sb.append("noone!");
|
||||
}
|
||||
sb.append("<br>getRevealed: ");
|
||||
sb.append(state.getRevealed());
|
||||
sb.append("<br>getSpecialActions: ");
|
||||
sb.append(state.getSpecialActions());
|
||||
sb.append("<br>getStack: ");
|
||||
sb.append(state.getStack());
|
||||
sb.append("<br>getStepNum: ");
|
||||
sb.append(state.getStepNum());
|
||||
sb.append("<br>getTriggers: ");
|
||||
sb.append(state.getTriggers());
|
||||
sb.append("<br>getTurn: ");
|
||||
sb.append(state.getTurn());
|
||||
sb.append("<br>getTurnId: ");
|
||||
sb.append(state.getTurnId());
|
||||
sb.append("<br>getTurnMods: ");
|
||||
sb.append(state.getTurnMods());
|
||||
sb.append("<br>getTurnNum: ");
|
||||
sb.append(state.getTurnNum());
|
||||
sb.append("<br>Future Timeout:");
|
||||
if (futureTimeout != null) {
|
||||
sb.append("Cancelled?=");
|
||||
sb.append(futureTimeout.isCancelled());
|
||||
sb.append(",,,Done?=");
|
||||
sb.append(futureTimeout.isDone());
|
||||
sb.append(",,,GetDelay?=");
|
||||
sb.append((int) futureTimeout.getDelay(TimeUnit.SECONDS));
|
||||
} else {
|
||||
sb.append("Not using future Timeout!");
|
||||
}
|
||||
sb.append("</font>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
|
@ -44,8 +45,6 @@ import mage.game.permanent.Permanent;
|
|||
import mage.target.TargetPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
@ -53,8 +52,7 @@ import java.util.UUID;
|
|||
public class ActOfAuthority extends CardImpl {
|
||||
|
||||
public ActOfAuthority(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}{W}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}");
|
||||
|
||||
// When Act of Authority enters the battlefield, you may exile target artifact or enchantment.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect(), true);
|
||||
|
|
|
@ -103,7 +103,7 @@ class BackFromTheBrinkCost extends CostImpl {
|
|||
if (controller != null) {
|
||||
Card card = controller.getGraveyard().get(targets.getFirstTarget(), game);
|
||||
if (card != null && controller.moveCards(card, Zone.EXILED, ability, game)) {
|
||||
ability.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId()));
|
||||
ability.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId())));
|
||||
paid = card.getManaCost().pay(ability, game, sourceId, controllerId, noMana);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class BarrinsUnmaking extends CardImpl {
|
|||
public BarrinsUnmaking(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
|
||||
|
||||
// Return target permanent to its owner's hand if that permanent shares a color with the most common color among all permanents or a color tied for most common.
|
||||
// Return target permanent to its owner's hand if that permanent shares a color with the most common color among all permanents or a color tied for most common.
|
||||
this.getSpellAbility().addEffect(new BarrinsUnmakingEffect());
|
||||
this.getSpellAbility().addTarget(new TargetPermanent());
|
||||
}
|
||||
|
@ -85,12 +85,12 @@ class BarrinsUnmakingEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
Condition condition = new MostCommonColorCondition(permanent.getColor(game));
|
||||
if (condition.apply(game, source)) {
|
||||
Effect effect = new ReturnToHandTargetEffect();
|
||||
effect.setTargetPointer(new FixedTarget(permanent.getId()));
|
||||
effect.setTargetPointer(new FixedTarget(permanent, game));
|
||||
return effect.apply(game, source);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ package mage.cards.c;
|
|||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.costs.common.DiscardXTargetCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
@ -82,8 +81,8 @@ class ConflagrateVariableValue implements DynamicValue {
|
|||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
int xValue = sourceAbility.getManaCostsToPay().getX();
|
||||
for (Cost cost : sourceAbility.getCosts()) {
|
||||
if (cost instanceof DiscardTargetCost) {
|
||||
xValue = ((DiscardTargetCost) cost).getCards().size();
|
||||
if (cost instanceof DiscardXTargetCost) {
|
||||
xValue = ((DiscardXTargetCost) cost).getAmount();
|
||||
}
|
||||
}
|
||||
return xValue;
|
||||
|
|
|
@ -68,8 +68,7 @@ public class DiamondKaleidoscope extends CardImpl {
|
|||
this.addAbility(ability);
|
||||
|
||||
// Sacrifice a Prism token: Add one mana of any color to your mana pool.
|
||||
ability = new AnyColorManaAbility();
|
||||
ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter)));
|
||||
ability = new AnyColorManaAbility(new SacrificeTargetCost(new TargetControlledPermanent(filter)));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -52,7 +53,7 @@ public class DreamThief extends CardImpl {
|
|||
private static final String rule = "draw a card if you've cast another blue spell this turn";
|
||||
|
||||
public DreamThief(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
|
||||
this.subtype.add(SubType.FAERIE);
|
||||
this.subtype.add(SubType.ROGUE);
|
||||
|
||||
|
@ -84,9 +85,12 @@ class CastBlueSpellThisTurnCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
for (Spell spell : watcher.getSpellsCastThisTurn(source.getControllerId())) {
|
||||
if (!spell.getSourceId().equals(source.getSourceId()) && spell.getColor(game).isBlue()) {
|
||||
return true;
|
||||
List<Spell> spells = watcher.getSpellsCastThisTurn(source.getControllerId());
|
||||
if (spells != null) {
|
||||
for (Spell spell : spells) {
|
||||
if (!spell.getSourceId().equals(source.getSourceId()) && spell.getColor(game).isBlue()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,13 @@
|
|||
*/
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.RegenerateTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
@ -43,10 +46,6 @@ import mage.players.Player;
|
|||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.watchers.common.BlockedAttackerWatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
@ -60,8 +59,7 @@ public class GazeOfTheGorgon extends CardImpl {
|
|||
// Regenerate target creature. At end of combat, destroy all creatures that blocked or were blocked by that creature this turn.
|
||||
this.getSpellAbility().addEffect(new RegenerateTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(
|
||||
new AtTheEndOfCombatDelayedTriggeredAbility(new GazeOfTheGorgonEffect())));
|
||||
this.getSpellAbility().addEffect(new GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect());
|
||||
this.getSpellAbility().addWatcher(new BlockedAttackerWatcher());
|
||||
}
|
||||
|
||||
|
@ -75,15 +73,46 @@ public class GazeOfTheGorgon extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class GazeOfTheGorgonEffect extends OneShotEffect {
|
||||
class GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect extends OneShotEffect {
|
||||
|
||||
public GazeOfTheGorgonEffect() {
|
||||
public GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "At this turn's next end of combat, destroy all creatures that blocked or were blocked by it this turn";
|
||||
}
|
||||
|
||||
public GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect(final GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect copy() {
|
||||
return new GazeOfTheGorgonCreateDelayedTriggeredAbilityEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (!source.getTargets().isEmpty() && source.getFirstTarget() != null) {
|
||||
MageObjectReference mor = new MageObjectReference(source.getFirstTarget(), game);
|
||||
AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new GazeOfTheGorgonEffect(mor));
|
||||
game.addDelayedTriggeredAbility(delayedAbility, source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class GazeOfTheGorgonEffect extends OneShotEffect {
|
||||
|
||||
MageObjectReference targetCreature;
|
||||
|
||||
public GazeOfTheGorgonEffect(MageObjectReference targetCreature) {
|
||||
super(Outcome.DestroyPermanent);
|
||||
this.staticText = "destroy all creatures that blocked or were blocked by that creature this turn";
|
||||
this.targetCreature = targetCreature;
|
||||
}
|
||||
|
||||
public GazeOfTheGorgonEffect(final GazeOfTheGorgonEffect effect) {
|
||||
super(effect);
|
||||
targetCreature = effect.targetCreature;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -94,14 +123,13 @@ class GazeOfTheGorgonEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Permanent targetCreature = game.getPermanentOrLKIBattlefield(source.getTargets().getFirstTarget());
|
||||
if (controller != null && targetCreature != null) {
|
||||
BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
List<Permanent> toDestroy = new ArrayList<>();
|
||||
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (!creature.getId().equals(targetCreature.getId())) {
|
||||
if (watcher.creatureHasBlockedAttacker(creature, targetCreature, game) || watcher.creatureHasBlockedAttacker(targetCreature, creature, game)) {
|
||||
if (!creature.getId().equals(targetCreature.getSourceId())) {
|
||||
if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), targetCreature, game) || watcher.creatureHasBlockedAttacker(targetCreature, new MageObjectReference(creature, game), game)) {
|
||||
toDestroy.add(creature);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,35 +27,34 @@
|
|||
*/
|
||||
package mage.cards.h;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInExile;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
* @author jeffwadsworth & L_J
|
||||
*/
|
||||
public class HellcarverDemon extends CardImpl {
|
||||
|
||||
|
@ -84,9 +83,6 @@ public class HellcarverDemon extends CardImpl {
|
|||
|
||||
class HellcarverDemonEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterControlledPermanent filterPermanents = new FilterControlledPermanent("Permanent");
|
||||
private static FilterNonlandCard filter = new FilterNonlandCard("nonland card exiled with Hellcarver Demon");
|
||||
|
||||
public HellcarverDemonEffect() {
|
||||
super(Outcome.PlayForFree);
|
||||
staticText = "sacrifice all other permanents you control and discard your hand. Exile the top six cards of your library. You may cast any number of nonland cards exiled this way without paying their mana costs.";
|
||||
|
@ -99,41 +95,43 @@ class HellcarverDemonEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Permanent hellcarverDemon = game.getPermanent(source.getSourceId());
|
||||
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanents, source.getControllerId(), game)) {
|
||||
if (!Objects.equals(permanent, hellcarverDemon)) {
|
||||
permanent.sacrifice(source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
|
||||
if (controller != null && !controller.getHand().isEmpty()) {
|
||||
int cardsInHand = controller.getHand().size();
|
||||
controller.discard(cardsInHand, false, source, game);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (controller != null
|
||||
&& controller.getLibrary().hasCards()) {
|
||||
Card topCard = controller.getLibrary().getFromTop(game);
|
||||
topCard.moveToExile(source.getSourceId(), "Cards exiled by Hellcarver Demon", source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
|
||||
while (controller != null
|
||||
&& controller.canRespond()
|
||||
&& controller.chooseUse(Outcome.PlayForFree, controller.getLogName() + " can cast another nonland card exiled with Hellcarver Demon without paying that card's mana cost.", source, game)) {
|
||||
TargetCardInExile target = new TargetCardInExile(filter, source.getSourceId());
|
||||
while (controller.chooseUse(Outcome.PlayForFree, "Cast another spell exiled by Hellcarver Demon?", source, game)) {
|
||||
controller.choose(Outcome.PlayForFree, game.getExile().getExileZone(source.getSourceId()), target, game);
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
if (card != null) {
|
||||
ContinuousEffect effect = new HellcarverDemonCastFromExileEffect();
|
||||
effect.setTargetPointer(new FixedTarget(card.getId()));
|
||||
game.addEffect(effect, source);
|
||||
controller.cast(card.getSpellAbility(), game, true);
|
||||
Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
||||
if (controller != null && sourceObject != null) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) {
|
||||
if (!Objects.equals(permanent, sourceObject)) {
|
||||
permanent.sacrifice(source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
if (!controller.getHand().isEmpty()) {
|
||||
int cardsInHand = controller.getHand().size();
|
||||
controller.discard(cardsInHand, false, source, game);
|
||||
}
|
||||
// move cards from library to exile
|
||||
Set<Card> currentExiledCards = new HashSet<>();
|
||||
currentExiledCards.addAll(controller.getLibrary().getTopCards(game, 6));
|
||||
controller.moveCardsToExile(currentExiledCards, source, game, true, source.getSourceId(), sourceObject.getIdName());
|
||||
|
||||
// cast the possible cards without paying the mana
|
||||
Cards cardsToCast = new CardsImpl();
|
||||
cardsToCast.addAll(currentExiledCards);
|
||||
boolean alreadyCast = false;
|
||||
while (!cardsToCast.isEmpty()
|
||||
&& controller.canRespond()) {
|
||||
if (!controller.chooseUse(outcome, "Cast a" + (alreadyCast ? "nother" : "" ) + " card exiled with " + sourceObject.getLogName() + " without paying its mana cost?", source, game)) {
|
||||
break;
|
||||
}
|
||||
TargetCard targetCard = new TargetCard(1, Zone.EXILED, new FilterCard("nonland card to cast for free"));
|
||||
if (controller.choose(Outcome.PlayForFree, cardsToCast, targetCard, game)) {
|
||||
alreadyCast = true;
|
||||
Card card = game.getCard(targetCard.getFirstTarget());
|
||||
if (card != null) {
|
||||
if (controller.cast(card.getSpellAbility(), game, true)) {
|
||||
cardsToCast.remove(card);
|
||||
} else {
|
||||
game.informPlayer(controller, "You're not able to cast " + card.getIdName() + " or you canceled the casting.");
|
||||
}
|
||||
}
|
||||
}
|
||||
target.clearChosen();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -145,33 +143,3 @@ class HellcarverDemonEffect extends OneShotEffect {
|
|||
return new HellcarverDemonEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
class HellcarverDemonCastFromExileEffect extends AsThoughEffectImpl {
|
||||
|
||||
public HellcarverDemonCastFromExileEffect() {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
|
||||
staticText = "You may play the card from exile without paying its mana cost";
|
||||
}
|
||||
|
||||
public HellcarverDemonCastFromExileEffect(final HellcarverDemonCastFromExileEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HellcarverDemonCastFromExileEffect copy() {
|
||||
return new HellcarverDemonCastFromExileEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (targetPointer.getTargets(game, source).contains(sourceId)) {
|
||||
return game.getState().getZone(sourceId) == Zone.EXILED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,25 +12,24 @@ import mage.filter.common.FilterControlledPermanent;
|
|||
|
||||
public class KaerveksSpite extends CardImpl {
|
||||
|
||||
private FilterControlledPermanent permanentsYouControl = new FilterControlledPermanent("all permanents you control");
|
||||
|
||||
public KaerveksSpite(UUID ownerId, CardSetInfo cardSetInfo) {
|
||||
super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}");
|
||||
|
||||
//As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand.
|
||||
this.getSpellAbility().addCost(new SacrificeAllCost(permanentsYouControl));
|
||||
// As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand.
|
||||
this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("all permanents you control")));
|
||||
this.getSpellAbility().addCost(new DiscardHandCost());
|
||||
|
||||
//Target player loses 5 life.
|
||||
// Target player loses 5 life.
|
||||
Effect effect = new LoseLifeTargetEffect(5);
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
}
|
||||
|
||||
public KaerveksSpite(final KaerveksSpite other){
|
||||
public KaerveksSpite(final KaerveksSpite other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
public KaerveksSpite copy(){
|
||||
@Override
|
||||
public KaerveksSpite copy() {
|
||||
return new KaerveksSpite(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbil
|
|||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
@ -122,7 +122,7 @@ class MimicVatTriggeredAbility extends TriggeredAbilityImpl {
|
|||
&& !(permanent instanceof PermanentToken)
|
||||
&& permanent.isCreature()) {
|
||||
|
||||
getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId()));
|
||||
getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId(), game));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -152,22 +152,22 @@ class MimicVatEffect extends OneShotEffect {
|
|||
if (controller == null || permanent == null) {
|
||||
return false;
|
||||
}
|
||||
// return older cards to graveyard
|
||||
Set<Card> toGraveyard = new HashSet<>();
|
||||
for (UUID imprintedId : permanent.getImprinted()) {
|
||||
Card card = game.getCard(imprintedId);
|
||||
if (card != null) {
|
||||
toGraveyard.add(card);
|
||||
}
|
||||
}
|
||||
controller.moveCards(toGraveyard, Zone.GRAVEYARD, source, game);
|
||||
permanent.clearImprinted(game);
|
||||
|
||||
// Imprint a new one
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
controller.moveCardsToExile(card, source, game, true, source.getSourceId(), permanent.getName() + " (Imprint)");
|
||||
permanent.imprint(card.getId(), game);
|
||||
Card newCard = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (newCard != null) {
|
||||
// return older cards to graveyard
|
||||
Set<Card> toGraveyard = new HashSet<>();
|
||||
for (UUID imprintedId : permanent.getImprinted()) {
|
||||
Card card = game.getCard(imprintedId);
|
||||
if (card != null) {
|
||||
toGraveyard.add(card);
|
||||
}
|
||||
}
|
||||
controller.moveCards(toGraveyard, Zone.GRAVEYARD, source, game);
|
||||
permanent.clearImprinted(game);
|
||||
|
||||
controller.moveCardsToExile(newCard, source, game, true, source.getSourceId(), permanent.getName() + " (Imprint)");
|
||||
permanent.imprint(newCard.getId(), game);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -50,7 +50,7 @@ import mage.game.events.NumberOfTriggersEvent;
|
|||
public class Panharmonicon extends CardImpl {
|
||||
|
||||
public Panharmonicon(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
|
||||
|
||||
// If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PanharmoniconEffect()));
|
||||
|
@ -95,7 +95,9 @@ class PanharmoniconEffect extends ReplacementEffectImpl {
|
|||
if (source.getControllerId().equals(event.getPlayerId())) {
|
||||
GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent();
|
||||
// Only EtB triggers
|
||||
if (sourceEvent.getType() == EventType.ENTERS_THE_BATTLEFIELD && sourceEvent instanceof EntersTheBattlefieldEvent) {
|
||||
if (sourceEvent != null
|
||||
&& sourceEvent.getType() == EventType.ENTERS_THE_BATTLEFIELD
|
||||
&& sourceEvent instanceof EntersTheBattlefieldEvent) {
|
||||
EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) sourceEvent;
|
||||
// Only for entering artifacts or creatures
|
||||
if (entersTheBattlefieldEvent.getTarget().isArtifact()
|
||||
|
@ -116,4 +118,4 @@ class PanharmoniconEffect extends ReplacementEffectImpl {
|
|||
event.setAmount(event.getAmount() + 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
133
Mage.Sets/src/mage/cards/p/PowerLeak.java
Normal file
133
Mage.Sets/src/mage/cards/p/PowerLeak.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.PreventDamageByTargetEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetEnchantmentPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class PowerLeak extends CardImpl {
|
||||
|
||||
public PowerLeak(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}");
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
// Enchant enchantment
|
||||
TargetPermanent auraTarget = new TargetEnchantmentPermanent();
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment));
|
||||
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
|
||||
|
||||
// At the beginning of the upkeep of enchanted enchantment's controller, that player may pay any amount of mana. Power Leak deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new PowerLeakEffect(), TargetController.CONTROLLER_ATTACHED_TO, false, true, "At the beginning of the upkeep of enchanted enchantment's controller, "));
|
||||
}
|
||||
|
||||
public PowerLeak(final PowerLeak card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerLeak copy() {
|
||||
return new PowerLeak(this);
|
||||
}
|
||||
}
|
||||
|
||||
class PowerLeakEffect extends OneShotEffect {
|
||||
|
||||
public PowerLeakEffect() {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = "that player may pay any amount of mana. {this} deals 2 damage to that player. Prevent X of that damage, where X is the amount of mana that player paid this way";
|
||||
}
|
||||
|
||||
public PowerLeakEffect(final PowerLeakEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerLeakEffect copy() {
|
||||
return new PowerLeakEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(game.getActivePlayerId());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
||||
if (player != null && permanent != null) {
|
||||
ManaCosts<ManaCost> cost = new ManaCostsImpl<>("{X}");
|
||||
String message = "Pay {X} to prevent X damage from " + permanent.getLogName() + "?";
|
||||
int xValue = 0;
|
||||
if (player != null && player.chooseUse(Outcome.Neutral, message, source, game)) {
|
||||
xValue = player.announceXMana(0, Integer.MAX_VALUE, "Choose the amount of mana to pay", game, source);
|
||||
cost.add(new GenericManaCost(xValue));
|
||||
if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
|
||||
game.informPlayers(player.getLogName() + " paid {" + xValue + "} for " + permanent.getLogName());
|
||||
} else {
|
||||
game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName());
|
||||
}
|
||||
} else {
|
||||
game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName());
|
||||
}
|
||||
|
||||
PreventDamageByTargetEffect effect = new PreventDamageByTargetEffect(Duration.OneUse, xValue, false);
|
||||
if (xValue != 0 && cost.isPaid()) {
|
||||
effect.setTargetPointer(new FixedTarget(permanent.getId()));
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
player.damage(2, source.getSourceId(), game, false, true);
|
||||
effect.discard();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
112
Mage.Sets/src/mage/cards/p/PutridCyclops.java
Normal file
112
Mage.Sets/src/mage/cards/p/PutridCyclops.java
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
||||
import mage.abilities.effects.keyword.ScryEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class PutridCyclops extends CardImpl {
|
||||
|
||||
public PutridCyclops(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
||||
|
||||
this.subtype.add(SubType.ZOMBIE);
|
||||
this.subtype.add(SubType.CYCLOPS);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// When Putrid Cyclops enters the battlefield, scry 1, then reveal the top card of your library. Putrid Cyclops gets -X/-X until end of turn, where X is that card's converted mana cost.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new PutridCyclopEffect()));
|
||||
}
|
||||
|
||||
public PutridCyclops(final PutridCyclops card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PutridCyclops copy() {
|
||||
return new PutridCyclops(this);
|
||||
}
|
||||
}
|
||||
|
||||
class PutridCyclopEffect extends OneShotEffect {
|
||||
|
||||
public PutridCyclopEffect() {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = "scry 1, then reveal the top card of your library. {this} gets -X/-X until end of turn, where X is that card's converted mana cost"
|
||||
+ " <i>(To scry 1, look at the top card of your library, then you may put that card on the bottom of your library.)</i>";
|
||||
}
|
||||
|
||||
public PutridCyclopEffect(final PutridCyclopEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PutridCyclopEffect copy() {
|
||||
return new PutridCyclopEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (controller != null && sourceObject != null) {
|
||||
new ScryEffect(1).apply(game, source);
|
||||
Card card = controller.getLibrary().getFromTop(game);
|
||||
if (card != null) {
|
||||
controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game);
|
||||
int unboost = card.getConvertedManaCost() * -1;
|
||||
ContinuousEffect effect = new BoostSourceEffect(unboost, unboost, Duration.EndOfTurn);
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
135
Mage.Sets/src/mage/cards/r/ReversePolarity.java
Normal file
135
Mage.Sets/src/mage/cards/r/ReversePolarity.java
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.r;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth, MTGFan & L_J
|
||||
*/
|
||||
public class ReversePolarity extends CardImpl {
|
||||
|
||||
public ReversePolarity(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{W}");
|
||||
|
||||
// You gain X life, where X is twice the damage dealt to you so far this turn by artifacts.
|
||||
this.getSpellAbility().addEffect(new GainLifeEffect(new ReversePolarityAmount(), "You gain X life, where X is twice the damage dealt to you so far this turn by artifacts"));
|
||||
this.getSpellAbility().addWatcher(new ReversePolarityWatcher());
|
||||
}
|
||||
|
||||
public ReversePolarity(final ReversePolarity card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReversePolarity copy() {
|
||||
return new ReversePolarity(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ReversePolarityAmount implements DynamicValue {
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability source, Effect effect) {
|
||||
ReversePolarityWatcher watcher = (ReversePolarityWatcher) game.getState().getWatchers().get(ReversePolarityWatcher.class.getSimpleName());
|
||||
if(watcher != null) {
|
||||
return watcher.getArtifactDamageReceivedThisTurn(source.getControllerId()) * 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReversePolarityAmount copy() {
|
||||
return new ReversePolarityAmount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
class ReversePolarityWatcher extends Watcher {
|
||||
|
||||
private final Map<UUID, Integer> artifactDamageReceivedThisTurn = new HashMap<>();
|
||||
|
||||
public ReversePolarityWatcher() {
|
||||
super(ReversePolarityWatcher.class.getSimpleName(), WatcherScope.GAME);
|
||||
}
|
||||
|
||||
public ReversePolarityWatcher(final ReversePolarityWatcher watcher) {
|
||||
super(watcher);
|
||||
for (Entry<UUID, Integer> entry : watcher.artifactDamageReceivedThisTurn.entrySet()) {
|
||||
artifactDamageReceivedThisTurn.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
|
||||
UUID playerId = event.getTargetId();
|
||||
if (playerId != null) {
|
||||
Permanent permanent = game.getPermanent(event.getSourceId());
|
||||
if (permanent != null && permanent.isArtifact()) {
|
||||
artifactDamageReceivedThisTurn.putIfAbsent(playerId, 0);
|
||||
artifactDamageReceivedThisTurn.compute(playerId, (k, v) -> v + event.getAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getArtifactDamageReceivedThisTurn(UUID playerId) {
|
||||
return artifactDamageReceivedThisTurn.getOrDefault(playerId, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
artifactDamageReceivedThisTurn.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReversePolarityWatcher copy() {
|
||||
return new ReversePolarityWatcher(this);
|
||||
}
|
||||
}
|
142
Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java
Normal file
142
Mage.Sets/src/mage/cards/s/ShahOfNaarIsle.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.EchoAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ShahOfNaarIsle extends CardImpl {
|
||||
|
||||
public ShahOfNaarIsle(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
|
||||
|
||||
this.subtype.add(SubType.EFREET);
|
||||
this.power = new MageInt(6);
|
||||
this.toughness = new MageInt(6);
|
||||
|
||||
// Trample
|
||||
this.addAbility(TrampleAbility.getInstance());
|
||||
|
||||
// Echo {0}
|
||||
this.addAbility(new EchoAbility("{0}"));
|
||||
|
||||
// When Shah of Naar Isle's echo cost is paid, each opponent may draw up to three cards.
|
||||
this.addAbility(new ShahOfNaarIsleTriggeredAbility());
|
||||
}
|
||||
|
||||
public ShahOfNaarIsle(final ShahOfNaarIsle card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShahOfNaarIsle copy() {
|
||||
return new ShahOfNaarIsle(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ShahOfNaarIsleTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public ShahOfNaarIsleTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new ShahOfNaarIsleEffect(), false);
|
||||
}
|
||||
|
||||
public ShahOfNaarIsleTriggeredAbility(final ShahOfNaarIsleTriggeredAbility effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ECHO_PAID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return getSourceId().equals(event.getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShahOfNaarIsleTriggeredAbility copy() {
|
||||
return new ShahOfNaarIsleTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When {this}'s echo cost is paid, " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
||||
class ShahOfNaarIsleEffect extends OneShotEffect {
|
||||
|
||||
public ShahOfNaarIsleEffect() {
|
||||
super(Outcome.DrawCard);
|
||||
this.staticText = "each opponent may draw up to three cards";
|
||||
}
|
||||
|
||||
public ShahOfNaarIsleEffect(final ShahOfNaarIsleEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShahOfNaarIsleEffect copy() {
|
||||
return new ShahOfNaarIsleEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
for (UUID playerId : game.getOpponents(controller.getId())) {
|
||||
Player opponent = game.getPlayer(playerId);
|
||||
if (opponent != null) {
|
||||
int number = opponent.getAmount(0, 3, "Draw how many cards?", game);
|
||||
opponent.drawCards(number, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
@ -48,14 +48,12 @@ import mage.target.common.TargetCreatureOrPlayer;
|
|||
*/
|
||||
public class Soulblast extends CardImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control");
|
||||
|
||||
public Soulblast(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}{R}{R}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}{R}{R}");
|
||||
|
||||
// As an additional cost to cast Soulblast, sacrifice all creatures you control.
|
||||
this.getSpellAbility().addCost(new SacrificeAllCost(filter));
|
||||
this.getSpellAbility().addCost(new SacrificeAllCost(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED));
|
||||
|
||||
// Soulblast deals damage to target creature or player equal to the total power of the sacrificed creatures.
|
||||
this.getSpellAbility().addEffect(new SoulblastEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
|
||||
|
@ -90,7 +88,7 @@ class SoulblastEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int power = 0;
|
||||
for (Cost cost :source.getCosts()) {
|
||||
for (Cost cost : source.getCosts()) {
|
||||
if (cost instanceof SacrificeAllCost) {
|
||||
for (Permanent permanent : ((SacrificeAllCost) cost).getPermanents()) {
|
||||
power += permanent.getPower().getValue();
|
||||
|
|
83
Mage.Sets/src/mage/cards/s/SteamfloggerBoss.java
Normal file
83
Mage.Sets/src/mage/cards/s/SteamfloggerBoss.java
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import static mage.filter.predicate.permanent.ControllerControlsIslandPredicate.filter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SteamfloggerBoss extends CardImpl {
|
||||
|
||||
public SteamfloggerBoss(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
|
||||
|
||||
this.subtype.add(SubType.GOBLIN);
|
||||
this.subtype.add(SubType.RIGGER);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Other Rigger creatures you control get +1/+0 and have haste.
|
||||
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD,
|
||||
new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, new FilterCreaturePermanent(SubType.RIGGER, "Rigger creatures"), true));
|
||||
Effect effect = new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter);
|
||||
effect.setText("and have haste");
|
||||
ability.addEffect(effect);
|
||||
this.addAbility(ability);
|
||||
|
||||
// If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead. (NOT IMPLEMENTED)")));
|
||||
|
||||
}
|
||||
|
||||
public SteamfloggerBoss(final SteamfloggerBoss card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SteamfloggerBoss copy() {
|
||||
return new SteamfloggerBoss(this);
|
||||
}
|
||||
}
|
|
@ -46,8 +46,7 @@ import mage.filter.FilterCard;
|
|||
public class SultaiAscendancy extends CardImpl {
|
||||
|
||||
public SultaiAscendancy(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}{G}{U}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}{G}{U}");
|
||||
|
||||
// At the beginning of your upkeep, look at the top two cards of your library. Put any number of them into your graveyard and the rest on top of your library in any order.
|
||||
Effect effect = new LookLibraryAndPickControllerEffect(
|
||||
|
|
104
Mage.Sets/src/mage/cards/t/ThunderbladeCharge.java
Normal file
104
Mage.Sets/src/mage/cards/t/ThunderbladeCharge.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreatureOrPlayer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ThunderbladeCharge extends CardImpl {
|
||||
|
||||
public ThunderbladeCharge(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}");
|
||||
|
||||
// Thunderblade Charge deals 3 damage to target creature or player.
|
||||
this.getSpellAbility().addEffect(new DamageTargetEffect(3));
|
||||
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
|
||||
|
||||
// Whenever one or more creatures you control deal combat damage to a player, if Thunderblade Charge is in your graveyard, you may pay {2}{R}{R}{R}. If you do, you may cast it without paying its mana cost.
|
||||
this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone.GRAVEYARD,
|
||||
new DoIfCostPaid(new ThunderbladeChargeCastEffect(), new ManaCostsImpl("{2}{R}{R}{R}"))
|
||||
.setText("if {this} is in your graveyard, you may pay {2}{R}{R}{R}. If you do, you may cast it without paying its mana cost")));
|
||||
}
|
||||
|
||||
public ThunderbladeCharge(final ThunderbladeCharge card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThunderbladeCharge copy() {
|
||||
return new ThunderbladeCharge(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ThunderbladeChargeCastEffect extends OneShotEffect {
|
||||
|
||||
public ThunderbladeChargeCastEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.staticText = "you may cast {this} without paying its mana cost";
|
||||
}
|
||||
|
||||
public ThunderbladeChargeCastEffect(final ThunderbladeChargeCastEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThunderbladeChargeCastEffect copy() {
|
||||
return new ThunderbladeChargeCastEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Card sourceCard = game.getCard(source.getSourceId());
|
||||
if (controller != null
|
||||
&& sourceCard != null
|
||||
&& Zone.GRAVEYARD == game.getState().getZone(sourceCard.getId())) {
|
||||
controller.cast(sourceCard.getSpellAbility(), game, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ package mage.cards.t;
|
|||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
|
@ -49,7 +50,7 @@ import mage.target.common.TargetOpponent;
|
|||
public class TreacherousPitDweller extends CardImpl {
|
||||
|
||||
public TreacherousPitDweller(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}");
|
||||
this.subtype.add(SubType.DEMON);
|
||||
|
||||
this.power = new MageInt(4);
|
||||
|
@ -77,24 +78,24 @@ class TreacherousPitDwellerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
private static final String ruleText = "When {this} enters the battlefield from a graveyard, ";
|
||||
|
||||
public TreacherousPitDwellerTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(),false);
|
||||
super(Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(), false);
|
||||
addTarget(new TargetOpponent());
|
||||
}
|
||||
|
||||
public TreacherousPitDwellerTriggeredAbility(final TreacherousPitDwellerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getTargetId().equals(getSourceId()) && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.GRAVEYARD;
|
||||
}
|
||||
|
||||
return event.getTargetId().equals(getSourceId()) && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.GRAVEYARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreacherousPitDwellerTriggeredAbility copy() {
|
||||
return new TreacherousPitDwellerTriggeredAbility(this);
|
||||
|
@ -104,7 +105,7 @@ class TreacherousPitDwellerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
public String getRule() {
|
||||
return ruleText + super.getRule();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class TreacherousPitDwellerEffect extends ContinuousEffectImpl {
|
||||
|
@ -125,10 +126,12 @@ class TreacherousPitDwellerEffect extends ContinuousEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game);
|
||||
MageObject permanent = source.getSourceObjectIfItStillExists(game); // it can also return Card object
|
||||
Player targetOpponent = game.getPlayer(source.getFirstTarget());
|
||||
if (permanent != null && targetOpponent != null) {
|
||||
return permanent.changeControllerId(targetOpponent.getId(), game);
|
||||
if (permanent != null
|
||||
&& (permanent instanceof Permanent)
|
||||
&& targetOpponent != null) {
|
||||
return ((Permanent) permanent).changeControllerId(targetOpponent.getId(), game);
|
||||
} else {
|
||||
discard();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
public class WickedReward extends CardImpl {
|
||||
|
||||
|
@ -19,6 +20,7 @@ public class WickedReward extends CardImpl {
|
|||
|
||||
//Target creature gets +4/+2 until end of turn.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(4, 2, Duration.EndOfTurn));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
}
|
||||
|
||||
public WickedReward(WickedReward other){
|
||||
|
|
|
@ -111,6 +111,7 @@ public class Antiquities extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Primal Clay", 26, Rarity.UNCOMMON, mage.cards.p.PrimalClay.class));
|
||||
cards.add(new SetCardInfo("Rakalite", 27, Rarity.UNCOMMON, mage.cards.r.Rakalite.class));
|
||||
cards.add(new SetCardInfo("Reconstruction", 56, Rarity.COMMON, mage.cards.r.Reconstruction.class));
|
||||
cards.add(new SetCardInfo("Reverse Polarity", 100, Rarity.COMMON, mage.cards.r.ReversePolarity.class));
|
||||
cards.add(new SetCardInfo("Rocket Launcher", 28, Rarity.UNCOMMON, mage.cards.r.RocketLauncher.class));
|
||||
cards.add(new SetCardInfo("Sage of Lat-Nam", 57, Rarity.COMMON, mage.cards.s.SageOfLatNam.class));
|
||||
cards.add(new SetCardInfo("Shapeshifter", 29, Rarity.RARE, mage.cards.s.Shapeshifter.class));
|
||||
|
|
|
@ -142,6 +142,7 @@ public class FourthEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Phantasmal Terrain", 89, Rarity.COMMON, mage.cards.p.PhantasmalTerrain.class));
|
||||
cards.add(new SetCardInfo("Phantom Monster", 90, Rarity.UNCOMMON, mage.cards.p.PhantomMonster.class));
|
||||
cards.add(new SetCardInfo("Pirate Ship", 91, Rarity.RARE, mage.cards.p.PirateShip.class));
|
||||
cards.add(new SetCardInfo("Power Leak", 92, Rarity.COMMON, mage.cards.p.PowerLeak.class));
|
||||
cards.add(new SetCardInfo("Power Sink", 93, Rarity.COMMON, mage.cards.p.PowerSink.class));
|
||||
cards.add(new SetCardInfo("Prodigal Sorcerer", 94, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class));
|
||||
cards.add(new SetCardInfo("Psionic Entity", 95, Rarity.RARE, mage.cards.p.PsionicEntity.class));
|
||||
|
@ -263,6 +264,7 @@ public class FourthEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Giant Strength", 196, Rarity.COMMON, mage.cards.g.GiantStrength.class));
|
||||
cards.add(new SetCardInfo("Goblin Balloon Brigade", 197, Rarity.UNCOMMON, mage.cards.g.GoblinBalloonBrigade.class));
|
||||
cards.add(new SetCardInfo("Goblin King", 198, Rarity.RARE, mage.cards.g.GoblinKing.class));
|
||||
cards.add(new SetCardInfo("Goblin Rock Sled", 199, Rarity.COMMON, mage.cards.g.GoblinRockSled.class));
|
||||
cards.add(new SetCardInfo("Gray Ogre", 200, Rarity.COMMON, mage.cards.g.GrayOgre.class));
|
||||
cards.add(new SetCardInfo("Hill Giant", 201, Rarity.COMMON, mage.cards.h.HillGiant.class));
|
||||
cards.add(new SetCardInfo("Hurloon Minotaur", 202, Rarity.COMMON, mage.cards.h.HurloonMinotaur.class));
|
||||
|
|
|
@ -171,6 +171,7 @@ public class FutureSight extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Petrified Plating", 133, Rarity.COMMON, mage.cards.p.PetrifiedPlating.class));
|
||||
cards.add(new SetCardInfo("Phosphorescent Feast", 149, Rarity.UNCOMMON, mage.cards.p.PhosphorescentFeast.class));
|
||||
cards.add(new SetCardInfo("Pooling Venom", 74, Rarity.UNCOMMON, mage.cards.p.PoolingVenom.class));
|
||||
cards.add(new SetCardInfo("Putrid Cyclops", 75, Rarity.COMMON, mage.cards.p.PutridCyclops.class));
|
||||
cards.add(new SetCardInfo("Pyromancer's Swath", 104, Rarity.RARE, mage.cards.p.PyromancersSwath.class));
|
||||
cards.add(new SetCardInfo("Quagnoth", 150, Rarity.RARE, mage.cards.q.Quagnoth.class));
|
||||
cards.add(new SetCardInfo("Quiet Disrepair", 134, Rarity.COMMON, mage.cards.q.QuietDisrepair.class));
|
||||
|
@ -189,6 +190,7 @@ public class FutureSight extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Scout's Warning", 16, Rarity.RARE, mage.cards.s.ScoutsWarning.class));
|
||||
cards.add(new SetCardInfo("Second Wind", 57, Rarity.UNCOMMON, mage.cards.s.SecondWind.class));
|
||||
cards.add(new SetCardInfo("Seht's Tiger", 31, Rarity.RARE, mage.cards.s.SehtsTiger.class));
|
||||
cards.add(new SetCardInfo("Shah of Naar Isle", 119, Rarity.RARE, mage.cards.s.ShahOfNaarIsle.class));
|
||||
cards.add(new SetCardInfo("Shapeshifter's Marrow", 58, Rarity.RARE, mage.cards.s.ShapeshiftersMarrow.class));
|
||||
cards.add(new SetCardInfo("Shimian Specter", 76, Rarity.RARE, mage.cards.s.ShimianSpecter.class));
|
||||
cards.add(new SetCardInfo("Shivan Sand-Mage", 108, Rarity.UNCOMMON, mage.cards.s.ShivanSandMage.class));
|
||||
|
@ -206,6 +208,7 @@ public class FutureSight extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Spirit en-Dal", 17, Rarity.UNCOMMON, mage.cards.s.SpiritEnDal.class));
|
||||
cards.add(new SetCardInfo("Sporoloth Ancient", 152, Rarity.COMMON, mage.cards.s.SporolothAncient.class));
|
||||
cards.add(new SetCardInfo("Sprout Swarm", 138, Rarity.COMMON, mage.cards.s.SproutSwarm.class));
|
||||
cards.add(new SetCardInfo("Steamflogger Boss", 121, Rarity.RARE, mage.cards.s.SteamfloggerBoss.class));
|
||||
cards.add(new SetCardInfo("Storm Entity", 122, Rarity.UNCOMMON, mage.cards.s.StormEntity.class));
|
||||
cards.add(new SetCardInfo("Street Wraith", 90, Rarity.UNCOMMON, mage.cards.s.StreetWraith.class));
|
||||
cards.add(new SetCardInfo("Stronghold Rats", 79, Rarity.UNCOMMON, mage.cards.s.StrongholdRats.class));
|
||||
|
@ -215,6 +218,7 @@ public class FutureSight extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Tarmogoyf", 153, Rarity.RARE, mage.cards.t.Tarmogoyf.class));
|
||||
cards.add(new SetCardInfo("Tarox Bladewing", 123, Rarity.RARE, mage.cards.t.TaroxBladewing.class));
|
||||
cards.add(new SetCardInfo("Thornweald Archer", 154, Rarity.COMMON, mage.cards.t.ThornwealdArcher.class));
|
||||
cards.add(new SetCardInfo("Thunderblade Charge", 124, Rarity.RARE, mage.cards.t.ThunderbladeCharge.class));
|
||||
cards.add(new SetCardInfo("Tolaria West", 173, Rarity.UNCOMMON, mage.cards.t.TolariaWest.class));
|
||||
cards.add(new SetCardInfo("Tombstalker", 91, Rarity.RARE, mage.cards.t.Tombstalker.class));
|
||||
cards.add(new SetCardInfo("Unblinking Bleb", 45, Rarity.COMMON, mage.cards.u.UnblinkingBleb.class));
|
||||
|
|
|
@ -209,6 +209,7 @@ public class LimitedEditionAlpha extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Plains", 285, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plains", 286, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plateau", 287, Rarity.RARE, mage.cards.p.Plateau.class));
|
||||
cards.add(new SetCardInfo("Power Leak", 72, Rarity.COMMON, mage.cards.p.PowerLeak.class));
|
||||
cards.add(new SetCardInfo("Power Sink", 73, Rarity.COMMON, mage.cards.p.PowerSink.class));
|
||||
cards.add(new SetCardInfo("Power Surge", 168, Rarity.RARE, mage.cards.p.PowerSurge.class));
|
||||
cards.add(new SetCardInfo("Prodigal Sorcerer", 74, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class));
|
||||
|
|
|
@ -214,6 +214,7 @@ public class LimitedEditionBeta extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Plains", 289, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plains", 290, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plateau", 280, Rarity.RARE, mage.cards.p.Plateau.class));
|
||||
cards.add(new SetCardInfo("Power Leak", 72, Rarity.COMMON, mage.cards.p.PowerLeak.class));
|
||||
cards.add(new SetCardInfo("Power Sink", 73, Rarity.COMMON, mage.cards.p.PowerSink.class));
|
||||
cards.add(new SetCardInfo("Power Surge", 168, Rarity.RARE, mage.cards.p.PowerSurge.class));
|
||||
cards.add(new SetCardInfo("Prodigal Sorcerer", 74, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class));
|
||||
|
|
|
@ -27,9 +27,7 @@
|
|||
*/
|
||||
package mage.sets;
|
||||
|
||||
import mage.cards.CardGraphicInfo;
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.FrameStyle;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
|
||||
|
@ -50,7 +48,6 @@ public class MasterpieceSeriesAmonkhet extends ExpansionSet {
|
|||
this.blockName = "Masterpiece Series";
|
||||
this.hasBoosters = false;
|
||||
this.hasBasicLands = false;
|
||||
CardGraphicInfo cardGraphicInfo = new CardGraphicInfo(FrameStyle.KLD_INVENTION, false);
|
||||
|
||||
cards.add(new SetCardInfo("Aggravated Assault", 25, Rarity.SPECIAL, mage.cards.a.AggravatedAssault.class));
|
||||
cards.add(new SetCardInfo("Armageddon", 31, Rarity.SPECIAL, mage.cards.a.Armageddon.class));
|
||||
|
|
|
@ -220,6 +220,7 @@ public class RevisedEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Plains", 294, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plains", 295, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plateau", 296, Rarity.RARE, mage.cards.p.Plateau.class));
|
||||
cards.add(new SetCardInfo("Power Leak", 73, Rarity.COMMON, mage.cards.p.PowerLeak.class));
|
||||
cards.add(new SetCardInfo("Power Sink", 74, Rarity.COMMON, mage.cards.p.PowerSink.class));
|
||||
cards.add(new SetCardInfo("Power Surge", 169, Rarity.RARE, mage.cards.p.PowerSurge.class));
|
||||
cards.add(new SetCardInfo("Primal Clay", 271, Rarity.RARE, mage.cards.p.PrimalClay.class));
|
||||
|
@ -234,6 +235,7 @@ public class RevisedEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Regrowth", 121, Rarity.UNCOMMON, mage.cards.r.Regrowth.class));
|
||||
cards.add(new SetCardInfo("Resurrection", 218, Rarity.UNCOMMON, mage.cards.r.Resurrection.class));
|
||||
cards.add(new SetCardInfo("Reverse Damage", 219, Rarity.RARE, mage.cards.r.ReverseDamage.class));
|
||||
cards.add(new SetCardInfo("Reverse Polarity", 220, Rarity.UNCOMMON, mage.cards.r.ReversePolarity.class));
|
||||
cards.add(new SetCardInfo("Righteousness", 221, Rarity.RARE, mage.cards.r.Righteousness.class));
|
||||
cards.add(new SetCardInfo("Roc of Kher Ridges", 171, Rarity.RARE, mage.cards.r.RocOfKherRidges.class));
|
||||
cards.add(new SetCardInfo("Rock Hydra", 172, Rarity.RARE, mage.cards.r.RockHydra.class));
|
||||
|
|
|
@ -214,6 +214,7 @@ public class UnlimitedEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Plains", 290, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plains", 291, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Plateau", 292, Rarity.RARE, mage.cards.p.Plateau.class));
|
||||
cards.add(new SetCardInfo("Power Leak", 72, Rarity.COMMON, mage.cards.p.PowerLeak.class));
|
||||
cards.add(new SetCardInfo("Power Sink", 73, Rarity.COMMON, mage.cards.p.PowerSink.class));
|
||||
cards.add(new SetCardInfo("Power Surge", 168, Rarity.RARE, mage.cards.p.PowerSurge.class));
|
||||
cards.add(new SetCardInfo("Prodigal Sorcerer", 74, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class));
|
||||
|
|
|
@ -75,6 +75,7 @@ public class Unstable extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
|
||||
cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class));
|
||||
cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class));
|
||||
cards.add(new SetCardInfo("Steamflogger Boss", 93, Rarity.RARE, mage.cards.s.SteamfloggerBoss.class));
|
||||
cards.add(new SetCardInfo("Steel Squirrel", 162, Rarity.UNCOMMON, mage.cards.s.SteelSquirrel.class));
|
||||
cards.add(new SetCardInfo("Summon the Pack", 74, Rarity.MYTHIC, mage.cards.s.SummonThePack.class));
|
||||
cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
*/
|
||||
package org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -39,6 +39,27 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
*/
|
||||
public class FlashbackTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void testNormalWildHunger() {
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
// Target creature gets +3/+1 and gains trample until end of turn.
|
||||
// Flashback {3}{R}
|
||||
addCard(Zone.GRAVEYARD, playerA, "Wild Hunger");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", "Silvercoat Lion");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Silvercoat Lion", 5, 3);
|
||||
assertAbility(playerA, "Silvercoat Lion", TrampleAbility.getInstance(), true);
|
||||
assertExileCount("Wild Hunger", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fracturing Gust is bugged. In a match against Affinity, it worked
|
||||
* properly when cast from hand. When I cast it from graveyard c/o
|
||||
|
@ -219,7 +240,7 @@ public class FlashbackTest extends CardTestPlayerBase {
|
|||
|
||||
// Conflagrate deals X damage divided as you choose among any number of target creatures and/or players.
|
||||
// Flashback-{R}{R}, Discard X cards.
|
||||
addCard(Zone.HAND, playerA, "Conflagrate", 1);
|
||||
addCard(Zone.HAND, playerA, "Conflagrate", 1); // Sorcery {X}{X}{R}
|
||||
|
||||
addCard(Zone.HAND, playerA, "Forest", 4);
|
||||
|
||||
|
@ -307,20 +328,26 @@ public class FlashbackTest extends CardTestPlayerBase {
|
|||
public void testAltarsReap() {
|
||||
|
||||
addCard(Zone.LIBRARY, playerA, "Island", 2);
|
||||
addCard(Zone.GRAVEYARD, playerA, "Altar's Reap", 1);
|
||||
// As an additional cost to cast Altar's Reap, sacrifice a creature.
|
||||
// Draw two cards.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Altar's Reap", 1); // Instant {1}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4);
|
||||
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
|
||||
|
||||
// Flash
|
||||
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
|
||||
// The flashback cost is equal to its mana cost.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
|
||||
setChoice(playerA, "Altar's Reap");
|
||||
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {1}{B}");
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback");
|
||||
setChoice(playerA, "Snapcaster Mage");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Snapcaster Mage", 1);
|
||||
assertExileCount(playerA, "Altar's Reap", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -520,7 +547,6 @@ public class FlashbackTest extends CardTestPlayerBase {
|
|||
* to a spell, and the flashback cost is already an alternative cost.
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSnapcasterMageSpellWithAlternateCost() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
|
|
|
@ -111,4 +111,47 @@ public class MimicVatTest extends CardTestPlayerBase {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Player A has Mimic Vat and plays Sidisi, Undead Vizier and exploits.
|
||||
* Player N responds to Mimic Vat Trigger with Shred Memory, exiling Sidisi.
|
||||
* Sidisi gets exiled but then xmage allows player A to imprint the
|
||||
* creature, which shouldn't be possible.
|
||||
*/
|
||||
@Test
|
||||
public void TestExileFails() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
// Imprint - Whenever a nontoken creature dies, you may exile that card. If you do, return each other card exiled with Mimic Vat to its owner's graveyard.
|
||||
// {3}, {T}: Create a token that's a copy of a card exiled with Mimic Vat. It gains haste. Exile it at the beginning of the next end step.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mimic Vat", 1); // Artifact {3}
|
||||
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
// Exile up to four target cards from a single graveyard.
|
||||
// Transmute {1}{B}{B}
|
||||
addCard(Zone.HAND, playerB, "Shred Memory", 1); // Instant {1}{B}
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion");
|
||||
setChoice(playerA, "Yes");
|
||||
setChoice(playerA, "Silvercoat Lion");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Shred Memory", "Silvercoat Lion", "Whenever a nontoken creature dies");
|
||||
setChoice(playerA, "Yes");
|
||||
|
||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}, {T}: Create a token that's a copy of a card exiled with ");
|
||||
setChoice(playerA, "Yes");
|
||||
setChoice(playerA, "Silvercoat Lion");
|
||||
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Lightning Bolt", 1);
|
||||
assertGraveyardCount(playerB, "Shred Memory", 1);
|
||||
|
||||
assertExileCount(playerB, "Silvercoat Lion", 1);
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,12 +86,12 @@ public class SpreadingSeasTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUtopiaSprawlWithSpreadingSeas(){
|
||||
public void testUtopiaSprawlWithSpreadingSeas() {
|
||||
addCard(Zone.HAND, playerA, "Spreading Seas", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 10);
|
||||
addCard(Zone.HAND, playerA, "Utopia Sprawl");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Utopia Sprawl","Forest");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Utopia Sprawl", "Forest");
|
||||
setChoice(playerA, "Green");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Forest");
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
@ -99,9 +99,8 @@ public class SpreadingSeasTest extends CardTestPlayerBase {
|
|||
assertNotSubtype("Forest", SubType.FOREST);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSpreadingSeasWithUrzaLand(){
|
||||
public void testSpreadingSeasWithUrzaLand() {
|
||||
addCard(Zone.HAND, playerA, "Spreading Seas", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Urza's Tower", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 10);
|
||||
|
@ -111,4 +110,40 @@ public class SpreadingSeasTest extends CardTestPlayerBase {
|
|||
assertNotSubtype("Urza's Tower", SubType.URZAS);
|
||||
assertNotSubtype("Urza's Tower", SubType.TOWER);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/magefree/mage/issues/4529 Some spell effects that
|
||||
* effect the use of mana abilities on lands are inoperative. Example
|
||||
* Spreading Seas transforms enchanted land into an island and it loses all
|
||||
* other abilities. The AI does not recognize this and is able to use all
|
||||
* abilities of the enchanted land including all previous mana abilities and
|
||||
* activated abilities, in addition to now also being an island due to
|
||||
* Spreading Sea's effect.
|
||||
*/
|
||||
@Test
|
||||
public void testSpreadingRemovesOtherAbilities() {
|
||||
|
||||
// Enchant land
|
||||
// When Spreading Seas enters the battlefield, draw a card.
|
||||
// Enchanted land is an Island.
|
||||
addCard(Zone.HAND, playerA, "Spreading Seas", 1); // ENCHANTMENT {1}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
// {T}: Add {C} to your mana pool.
|
||||
// {1}{R}, {T}: Create a 0/1 red Kobold creature token named Kobolds of Kher Keep.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Kher Keep", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Kher Keep");
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{1}{R}"); // Ability should not be available
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Spreading Seas", 1);
|
||||
|
||||
assertPermanentCount(playerB, "Kobolds of Kher Keep", 0);
|
||||
assertTapped("Kher Keep", false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,24 +27,24 @@
|
|||
*/
|
||||
package org.mage.test.serverside.deck;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.cards.decks.DeckValidator;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.deck.Limited;
|
||||
import mage.deck.Modern;
|
||||
import mage.deck.Standard;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mage.test.serverside.base.MageTestBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DeckValidatorTest extends CardTestPlayerBase {
|
||||
public class DeckValidatorTest extends MageTestBase {
|
||||
|
||||
static class CardNameAmount {
|
||||
|
||||
|
@ -84,6 +84,38 @@ public class DeckValidatorTest extends CardTestPlayerBase {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardValid() {
|
||||
ArrayList<CardNameAmount> deck = new ArrayList<>();
|
||||
|
||||
deck.add(new CardNameAmount("MPS-AKH", 28, 4)); // Rhonas the Indomitable
|
||||
deck.add(new CardNameAmount("Built to Smash", 4));
|
||||
deck.add(new CardNameAmount("Heroic Intervention", 4));
|
||||
deck.add(new CardNameAmount("Mountain", 48));
|
||||
|
||||
DeckValidator validator = new Standard();
|
||||
boolean validationSuccessful = testDeckValid(validator, deck);
|
||||
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardNotValid() {
|
||||
ArrayList<CardNameAmount> deck = new ArrayList<>();
|
||||
|
||||
deck.add(new CardNameAmount("MPS-AKH", 28, 4)); // Rhonas the Indomitable
|
||||
deck.add(new CardNameAmount("Built to Smash", 4));
|
||||
deck.add(new CardNameAmount("Heroic Intervention", 4));
|
||||
deck.add(new CardNameAmount("Mountain", 47));
|
||||
|
||||
ArrayList<CardNameAmount> sideboard = new ArrayList<>();
|
||||
sideboard.add(new CardNameAmount("Mountain", 16));
|
||||
|
||||
DeckValidator validator = new Standard();
|
||||
testDeckValid(validator, deck, sideboard);
|
||||
Assert.assertEquals("invalid message not correct",
|
||||
"{Sideboard=Must contain no more than 15 cards : has 16 cards, Deck=Must contain at least 60 cards: has only 59 cards}", validator.getInvalid().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLimitedValid() {
|
||||
ArrayList<CardNameAmount> deck = new ArrayList<>();
|
||||
|
@ -226,37 +258,156 @@ public class DeckValidatorTest extends CardTestPlayerBase {
|
|||
@Test
|
||||
public void testModernBanned() {
|
||||
ArrayList<CardNameAmount> deckList = new ArrayList<>();
|
||||
DeckValidator validator = new Modern();
|
||||
|
||||
deckList.add(new CardNameAmount("Ancestral Vision", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
boolean validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Ancient Den", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.add(new CardNameAmount("Birthing Pod", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Blazing Shoal", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Bloodbraid Elf", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Chrome Mox", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Cloudpost", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Dark Depths", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Deathrite Shaman", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Dig Through Time", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Dread Return", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Glimpse of Nature", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Great Furnace", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Green Sun's Zenith", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Hypergenesis", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Jace, the Mind Sculptor", 4));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
|
||||
deckList.clear();
|
||||
deckList.add(new CardNameAmount("Mental Misstep", 4));
|
||||
Assert.assertFalse("banned cards are not allowed", testDeckValid(new Modern(), deckList));
|
||||
deckList.add(new CardNameAmount("Mountain", 56));
|
||||
validationSuccessful = testDeckValid(validator, deckList);
|
||||
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
|
||||
validator.getInvalid().clear();
|
||||
}
|
||||
|
||||
private boolean testDeckValid(DeckValidator validator, List<CardNameAmount> cards) {
|
||||
return testDeckValid(validator, cards, null);
|
||||
}
|
||||
|
||||
private boolean testDeckValid(DeckValidator validator, List<CardNameAmount> cards, List<CardNameAmount> cardsSideboard) {
|
||||
Deck deckToTest = new Deck();
|
||||
for (CardNameAmount cardNameAmount : cards) {
|
||||
CardInfo cardinfo;
|
||||
if (cardNameAmount.getName().isEmpty()) {
|
||||
cardinfo = CardRepository.instance.findCard(cardNameAmount.getSetCode(), cardNameAmount.getCardNumber());
|
||||
} else {
|
||||
cardinfo = CardRepository.instance.findCard(cardNameAmount.getName());
|
||||
if (cards != null) {
|
||||
for (CardNameAmount cardNameAmount : cards) {
|
||||
CardInfo cardinfo;
|
||||
if (cardNameAmount.getName().isEmpty()) {
|
||||
cardinfo = CardRepository.instance.findCard(cardNameAmount.getSetCode(), cardNameAmount.getCardNumber());
|
||||
} else {
|
||||
cardinfo = CardRepository.instance.findCard(cardNameAmount.getName());
|
||||
}
|
||||
for (int i = 0; i < cardNameAmount.getNumber(); i++) {
|
||||
deckToTest.getCards().add(cardinfo.getCard());
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cardNameAmount.getNumber(); i++) {
|
||||
deckToTest.getCards().add(cardinfo.getCard());
|
||||
}
|
||||
if (cardsSideboard != null) {
|
||||
for (CardNameAmount cardNameAmount : cardsSideboard) {
|
||||
CardInfo cardinfo;
|
||||
if (cardNameAmount.getName().isEmpty()) {
|
||||
cardinfo = CardRepository.instance.findCard(cardNameAmount.getSetCode(), cardNameAmount.getCardNumber());
|
||||
} else {
|
||||
cardinfo = CardRepository.instance.findCard(cardNameAmount.getName());
|
||||
}
|
||||
for (int i = 0; i < cardNameAmount.getNumber(); i++) {
|
||||
deckToTest.getSideboard().add(cardinfo.getCard());
|
||||
}
|
||||
}
|
||||
}
|
||||
return validator.validate(deckToTest);
|
||||
|
|
|
@ -44,7 +44,6 @@ import mage.abilities.effects.Effects;
|
|||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DynamicManaEffect;
|
||||
import mage.abilities.effects.common.ManaEffect;
|
||||
import mage.abilities.keyword.FlashbackAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.SplitCard;
|
||||
|
@ -337,9 +336,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (sourceObject != null && this.getAbilityType() != AbilityType.TRIGGERED) { // triggered abilities check this already in playerImpl.triggerAbility
|
||||
sourceObject.adjustTargets(this, game);
|
||||
}
|
||||
// Flashback abilities haven't made the choices the underlying spell might need for targeting.
|
||||
if (!(this instanceof FlashbackAbility)
|
||||
&& !getTargets().isEmpty()) {
|
||||
if (!getTargets().isEmpty()) {
|
||||
Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome();
|
||||
if (getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game) == false) {
|
||||
if ((variableManaCost != null || announceString != null)) {
|
||||
|
@ -445,8 +442,15 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) {
|
||||
if (this instanceof SpellAbility) {
|
||||
if (((SpellAbility) this).getSpellAbilityCastMode() != SpellAbilityCastMode.NORMAL) {
|
||||
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
|
||||
// So can only use alternate costs if the spell is cast in normal mode
|
||||
return false;
|
||||
}
|
||||
}
|
||||
boolean alternativeCostisUsed = false;
|
||||
if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) {
|
||||
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
||||
Abilities<Ability> abilities = null;
|
||||
if (sourceObject instanceof Card) {
|
||||
abilities = ((Card) sourceObject).getAbilities(game);
|
||||
|
|
|
@ -217,7 +217,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
this.name = "Cast fused " + cardName;
|
||||
break;
|
||||
default:
|
||||
this.name = "Cast " + cardName + (this.spellAbilityCastMode != SpellAbilityCastMode.NORMAL ? " by " + spellAbilityCastMode.toString() : "");
|
||||
this.name = "Cast " + cardName + (this.spellAbilityCastMode != SpellAbilityCastMode.NORMAL ? " using " + spellAbilityCastMode.toString() : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,4 +230,11 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
setSpellName();
|
||||
}
|
||||
|
||||
public SpellAbility getSpellAbilityToResolve(Game game) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setId(UUID idToUse) {
|
||||
this.id = idToUse;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ package mage.abilities.costs;
|
|||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.keyword.FlashbackAbility;
|
||||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.StackObject;
|
||||
|
@ -173,7 +172,6 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
StackObject stackObject = game.getStack().getStackObject(source.getId());
|
||||
if (controller != null
|
||||
&& (source instanceof ManaAbility
|
||||
|| source instanceof FlashbackAbility
|
||||
|| stackObject != null)) {
|
||||
xValue = controller.announceXCost(getMinValue(source, game), getMaxValue(source, game),
|
||||
"Announce the number of " + actionText, game, source, this);
|
||||
|
|
|
@ -55,19 +55,19 @@ public class SacrificeAllCost extends CostImpl {
|
|||
|
||||
public SacrificeAllCost(final SacrificeAllCost cost) {
|
||||
super(cost);
|
||||
for (Permanent permanent: cost.permanents) {
|
||||
this.permanents.add(permanent.copy());
|
||||
}
|
||||
this.permanents.addAll(cost.permanents); // because this are already copied permanents, they can't change, so no copy again is needed
|
||||
this.filter = cost.filter.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) {
|
||||
permanents.add(permanent.copy());
|
||||
permanent.sacrifice(sourceId, game);
|
||||
if (permanent.sacrifice(sourceId, game)) {
|
||||
permanents.add(permanent.copy());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
paid = true;
|
||||
return paid;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,13 +81,13 @@ public class SacrificeAllCost extends CostImpl {
|
|||
activator = controllerId;
|
||||
}
|
||||
}
|
||||
|
||||
for (Permanent permanent :game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) {
|
||||
if(!game.getPlayer(activator).canPaySacrificeCost(permanent, sourceId, controllerId, game)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) {
|
||||
if (!game.getPlayer(activator).canPaySacrificeCost(permanent, sourceId, controllerId, game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -142,42 +142,50 @@ public class MaximumHandSizeControllerEffect extends ContinuousEffectImpl {
|
|||
|
||||
private void setText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
if (handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) {
|
||||
sb.append("All players have no ");
|
||||
} else {
|
||||
sb.append("All players ");
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
if (handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) {
|
||||
sb.append("Each opponent has no ");
|
||||
} else {
|
||||
sb.append("Each opponent's ");
|
||||
}
|
||||
break;
|
||||
case YOU:
|
||||
if (handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) {
|
||||
sb.append("You have no ");
|
||||
} else {
|
||||
sb.append("Your ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
sb.append("maximum hand size");
|
||||
if (handSizeModification == HandSizeModification.INCREASE) {
|
||||
sb.append(" is increased by ");
|
||||
} else if (handSizeModification == HandSizeModification.REDUCE) {
|
||||
sb.append(" is reduced by ");
|
||||
} else if (!((handSize instanceof StaticValue) && ((StaticValue) handSize).getValue() != Integer.MAX_VALUE) || !(handSize instanceof StaticValue)) {
|
||||
sb.append(" is ");
|
||||
}
|
||||
if ((handSize instanceof StaticValue && ((StaticValue) handSize).getValue() != Integer.MAX_VALUE)) {
|
||||
sb.append(CardUtil.numberToText(((StaticValue) handSize).getValue()));
|
||||
} else if (!(handSize instanceof StaticValue)) {
|
||||
sb.append(handSize.getMessage());
|
||||
if(handSize instanceof StaticValue && ((StaticValue) handSize).getValue() == Integer.MAX_VALUE) {
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
sb.append("Players have no maximum hand size");
|
||||
break;
|
||||
case OPPONENT:
|
||||
sb.append("Each opponent has no maximum hand size");
|
||||
break;
|
||||
case YOU:
|
||||
sb.append("You have no maximum hand size");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
sb.append("All players maximum hand size");
|
||||
break;
|
||||
case OPPONENT:
|
||||
sb.append("Each opponent's maximum hand size");
|
||||
break;
|
||||
case YOU:
|
||||
sb.append("Your maximum hand size");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (handSizeModification) {
|
||||
case SET:
|
||||
sb.append(" is ");
|
||||
break;
|
||||
case INCREASE:
|
||||
sb.append(" is increased by ");
|
||||
break;
|
||||
case REDUCE:
|
||||
sb.append(" is reduced by ");
|
||||
break;
|
||||
}
|
||||
|
||||
if (handSize instanceof StaticValue) {
|
||||
sb.append(CardUtil.numberToText(((StaticValue) handSize).getValue()));
|
||||
} else if (!(handSize instanceof StaticValue)) {
|
||||
sb.append(handSize.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (duration == Duration.EndOfGame) {
|
||||
sb.append(" for the rest of the game");
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
|
|||
private final boolean upTo;
|
||||
|
||||
public SpellsCostReductionAllEffect(int amount) {
|
||||
this(new FilterCard("All Spells "), amount);
|
||||
this(new FilterCard("Spells"), amount);
|
||||
}
|
||||
|
||||
public SpellsCostReductionAllEffect(FilterCard filter, int amount) {
|
||||
|
|
|
@ -187,6 +187,7 @@ class EchoEffect extends OneShotEffect {
|
|||
if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) {
|
||||
cost.clearPaid();
|
||||
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ECHO_PAID, source.getSourceId(), source.getSourceId(), source.getControllerId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,14 +32,13 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SpellAbilityCastMode;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
|
@ -66,23 +65,21 @@ import mage.target.targetpointer.FixedTarget;
|
|||
public class FlashbackAbility extends SpellAbility {
|
||||
|
||||
private String abilityName;
|
||||
private SpellAbility spellAbilityToResolve;
|
||||
|
||||
public FlashbackAbility(Cost cost, TimingRule timingRule) {
|
||||
super(null, "", Zone.GRAVEYARD);
|
||||
super(null, "", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE, SpellAbilityCastMode.FLASHBACK);
|
||||
this.setAdditionalCostsRuleVisible(false);
|
||||
this.name = "Flashback " + cost.getText();
|
||||
this.addEffect(new FlashbackEffect());
|
||||
this.addCost(cost);
|
||||
this.timing = timingRule;
|
||||
this.usesStack = false;
|
||||
this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
|
||||
setCostModificationActive(false);
|
||||
}
|
||||
|
||||
public FlashbackAbility(final FlashbackAbility ability) {
|
||||
super(ability);
|
||||
this.spellAbilityType = ability.spellAbilityType;
|
||||
this.abilityName = ability.abilityName;
|
||||
this.spellAbilityToResolve = ability.spellAbilityToResolve;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,6 +105,47 @@ public class FlashbackAbility extends SpellAbility {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility getSpellAbilityToResolve(Game game) {
|
||||
Card card = game.getCard(getSourceId());
|
||||
if (card != null) {
|
||||
if (spellAbilityToResolve == null) {
|
||||
SpellAbility spellAbilityCopy = null;
|
||||
if (card.isSplitCard()) {
|
||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||
}
|
||||
} else {
|
||||
spellAbilityCopy = card.getSpellAbility().copy();
|
||||
}
|
||||
if (spellAbilityCopy == null) {
|
||||
return null;
|
||||
}
|
||||
spellAbilityCopy.setId(this.getId());
|
||||
spellAbilityCopy.getManaCosts().clear();
|
||||
spellAbilityCopy.getManaCostsToPay().clear();
|
||||
spellAbilityCopy.getCosts().addAll(this.getCosts());
|
||||
spellAbilityCopy.addCost(this.getManaCosts());
|
||||
spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode());
|
||||
spellAbilityToResolve = spellAbilityCopy;
|
||||
ContinuousEffect effect = new FlashbackReplacementEffect();
|
||||
effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId())));
|
||||
game.addEffect(effect, this);
|
||||
}
|
||||
}
|
||||
return spellAbilityToResolve;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCosts() {
|
||||
if (spellAbilityToResolve == null) {
|
||||
return super.getCosts();
|
||||
}
|
||||
return spellAbilityToResolve.getCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlashbackAbility copy() {
|
||||
return new FlashbackAbility(this);
|
||||
|
@ -144,102 +182,18 @@ public class FlashbackAbility extends SpellAbility {
|
|||
return sbRule.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpellAbilityType(SpellAbilityType spellAbilityType) {
|
||||
this.spellAbilityType = spellAbilityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbilityType getSpellAbilityType() {
|
||||
return this.spellAbilityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for split card sin PlayerImpl method:
|
||||
* getOtherUseableActivatedAbilities
|
||||
*
|
||||
* @param abilityName
|
||||
*/
|
||||
public void setAbilityName(String abilityName) {
|
||||
this.abilityName = abilityName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FlashbackEffect extends OneShotEffect {
|
||||
|
||||
public FlashbackEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "";
|
||||
}
|
||||
|
||||
public FlashbackEffect(final FlashbackEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlashbackEffect copy() {
|
||||
return new FlashbackEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Card card = (Card) game.getObject(source.getSourceId());
|
||||
if (card != null) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
SpellAbility spellAbility;
|
||||
switch (((FlashbackAbility) source).getSpellAbilityType()) {
|
||||
case SPLIT_LEFT:
|
||||
spellAbility = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||
break;
|
||||
case SPLIT_RIGHT:
|
||||
spellAbility = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||
break;
|
||||
default:
|
||||
spellAbility = card.getSpellAbility().copy();
|
||||
}
|
||||
|
||||
spellAbility.clear();
|
||||
// set the payed flashback costs to the spell ability so abilities like Converge or calculation of {X} values work
|
||||
spellAbility.getManaCostsToPay().clear();
|
||||
spellAbility.getManaCostsToPay().addAll(source.getManaCosts());
|
||||
spellAbility.getManaCosts().clear();
|
||||
spellAbility.getManaCosts().addAll(source.getManaCosts());
|
||||
// needed to get e.g. paid costs from Conflagrate
|
||||
|
||||
for (Cost cost : source.getCosts()) {
|
||||
if (cost instanceof Costs) {
|
||||
Costs<Cost> listOfCosts = (Costs<Cost>) cost;
|
||||
for (Cost singleCost : listOfCosts) {
|
||||
if (singleCost instanceof ManaCost) {
|
||||
singleCost.clearPaid();
|
||||
spellAbility.getManaCosts().add((ManaCost) singleCost);
|
||||
spellAbility.getManaCostsToPay().add((ManaCost) singleCost);
|
||||
} else {
|
||||
spellAbility.getCosts().add(singleCost);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (cost instanceof ManaCost) {
|
||||
spellAbility.getManaCosts().add((ManaCost) cost);
|
||||
spellAbility.getManaCostsToPay().add((ManaCost) cost);
|
||||
} else {
|
||||
spellAbility.getCosts().add(cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(controller.getLogName() + " flashbacks " + card.getLogName());
|
||||
}
|
||||
if (controller.cast(spellAbility, game, false)) {
|
||||
ContinuousEffect effect = new FlashbackReplacementEffect();
|
||||
effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId())));
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FlashbackReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
public FlashbackReplacementEffect() {
|
||||
|
@ -287,7 +241,7 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl {
|
|||
&& ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) {
|
||||
|
||||
int zcc = game.getState().getZoneChangeCounter(source.getSourceId());
|
||||
if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() == zcc) {
|
||||
if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -516,7 +516,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
Card mainCard = getMainCard();
|
||||
ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK);
|
||||
ZoneChangeInfo.Stack info
|
||||
= new ZoneChangeInfo.Stack(event, new Spell(this, ability.copy(), controllerId, event.getFromZone()));
|
||||
= new ZoneChangeInfo.Stack(event, new Spell(this, ability.getSpellAbilityToResolve(game), controllerId, event.getFromZone()));
|
||||
return ZonesHandler.cast(info, game);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ package mage.constants;
|
|||
*/
|
||||
public enum SpellAbilityCastMode {
|
||||
NORMAL("Normal"),
|
||||
MADNESS("Madness");
|
||||
MADNESS("Madness"),
|
||||
FLASHBACK("Flashback");
|
||||
|
||||
private final String text;
|
||||
|
||||
|
|
|
@ -732,42 +732,35 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
|
||||
for (UUID uuid : attackers) {
|
||||
Permanent attacker = game.getPermanent(uuid);
|
||||
// Check if there are enough blockers to have a legal block
|
||||
if (attacker != null && this.blocked && attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) {
|
||||
for (UUID blockerId : blockers) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
if (blocker != null) {
|
||||
blocker.setBlocking(blocker.getBlocking() - 1);
|
||||
if (attacker != null && this.blocked) {
|
||||
// Check if there are enough blockers to have a legal block
|
||||
if (attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) {
|
||||
for (UUID blockerId : new ArrayList<>(blockers)) {
|
||||
game.getCombat().removeBlocker(blockerId, game);
|
||||
}
|
||||
}
|
||||
blockers.clear();
|
||||
blockerOrder.clear();
|
||||
this.blocked = false;
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded.");
|
||||
}
|
||||
blockWasLegal = false;
|
||||
}
|
||||
// Check if there are too many blockers (maxBlockedBy = 0 means no restrictions)
|
||||
if (attacker != null && this.blocked && attacker.getMaxBlockedBy() > 0 && attacker.getMaxBlockedBy() < blockers.size()) {
|
||||
for (UUID blockerId : blockers) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
if (blocker != null) {
|
||||
blocker.setBlocking(blocker.getBlocking() - 1);
|
||||
blockers.clear();
|
||||
blockerOrder.clear();
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded.");
|
||||
}
|
||||
blockWasLegal = false;
|
||||
}
|
||||
blockers.clear();
|
||||
blockerOrder.clear();
|
||||
this.blocked = false;
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(new StringBuilder(attacker.getLogName())
|
||||
.append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy())
|
||||
.append(attacker.getMaxBlockedBy() == 1 ? " creature." : " creatures.")
|
||||
.append(" Blockers discarded.").toString());
|
||||
// Check if there are too many blockers (maxBlockedBy = 0 means no restrictions)
|
||||
if (attacker.getMaxBlockedBy() > 0 && attacker.getMaxBlockedBy() < blockers.size()) {
|
||||
for (UUID blockerId : new ArrayList<>(blockers)) {
|
||||
game.getCombat().removeBlocker(blockerId, game);
|
||||
}
|
||||
blockers.clear();
|
||||
blockerOrder.clear();
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(new StringBuilder(attacker.getLogName())
|
||||
.append(" can't be blocked by more than ").append(attacker.getMaxBlockedBy())
|
||||
.append(attacker.getMaxBlockedBy() == 1 ? " creature." : " creatures.")
|
||||
.append(" Blockers discarded.").toString());
|
||||
}
|
||||
blockWasLegal = false;
|
||||
}
|
||||
blockWasLegal = false;
|
||||
}
|
||||
|
||||
}
|
||||
return blockWasLegal;
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ public class GameEvent implements Serializable {
|
|||
DRAW_CARDS, // applies to an instruction to draw more than one card before any replacement effects apply to individual cards drawn
|
||||
DRAW_CARD, DREW_CARD,
|
||||
EXPLORED,
|
||||
ECHO_PAID,
|
||||
MIRACLE_CARD_REVEALED,
|
||||
MADNESS_CARD_EXILED,
|
||||
INVESTIGATED,
|
||||
|
|
|
@ -1158,7 +1158,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
} else {
|
||||
int bookmark = game.bookmarkState();
|
||||
if (ability.activate(game, ability instanceof FlashbackAbility)) {
|
||||
if (ability.activate(game, false)) {
|
||||
ability.resolve(game);
|
||||
game.removeBookmark(bookmark);
|
||||
resetStoredBookmark(game);
|
||||
|
@ -1219,11 +1219,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
result = playManaAbility((ActivatedManaAbilityImpl) ability.copy(), game);
|
||||
break;
|
||||
case SPELL:
|
||||
if (ability instanceof FlashbackAbility) {
|
||||
result = playAbility(ability.copy(), game);
|
||||
} else {
|
||||
result = cast((SpellAbility) ability, game, false);
|
||||
}
|
||||
result = cast((SpellAbility) ability.copy(), game, false);
|
||||
break;
|
||||
default:
|
||||
result = playAbility(ability.copy(), game);
|
||||
|
|
|
@ -89,4 +89,9 @@ public class BlockedAttackerWatcher extends Watcher {
|
|||
Set<MageObjectReference> blockedAttackers = blockData.get(new MageObjectReference(blocker, game));
|
||||
return blockedAttackers != null && blockedAttackers.contains(new MageObjectReference(attacker, game));
|
||||
}
|
||||
|
||||
public boolean creatureHasBlockedAttacker(MageObjectReference attacker, MageObjectReference blocker, Game game) {
|
||||
Set<MageObjectReference> blockedAttackers = blockData.get(blocker);
|
||||
return blockedAttackers != null && blockedAttackers.contains(attacker);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue