mirror of
https://github.com/correl/mage.git
synced 2024-12-25 11:11:16 +00:00
Merge origin/master
This commit is contained in:
commit
bb4714eb32
32 changed files with 618 additions and 78 deletions
|
@ -32,6 +32,7 @@ import java.awt.event.*;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -726,6 +727,12 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
currentConnection.setPassword(password);
|
||||
currentConnection.setHost(server);
|
||||
currentConnection.setPort(port);
|
||||
String allMAC = "";
|
||||
try {
|
||||
allMAC = currentConnection.getMAC();
|
||||
} catch (SocketException ex) {
|
||||
}
|
||||
currentConnection.setUserIdStr(System.getProperty("user.name") + ":" + System.getProperty("os.name") + ":" + MagePreferences.getUserNames() + ":" + allMAC);
|
||||
currentConnection.setProxyType(proxyType);
|
||||
currentConnection.setProxyHost(proxyServer);
|
||||
currentConnection.setProxyPort(proxyPort);
|
||||
|
|
|
@ -54,6 +54,7 @@ import mage.client.util.GUISizeHelper;
|
|||
import mage.client.util.audio.AudioManager;
|
||||
import mage.client.util.gui.TableUtil;
|
||||
import mage.client.util.gui.countryBox.CountryCellRenderer;
|
||||
import mage.players.PlayerType;
|
||||
import mage.remote.Session;
|
||||
import mage.view.SeatView;
|
||||
import mage.view.TableView;
|
||||
|
@ -437,6 +438,7 @@ class UpdateSeatsTask extends SwingWorker<Void, TableView> {
|
|||
AudioManager.playPlayerJoinedTable();
|
||||
} else {
|
||||
MageTray.instance.displayMessage("A player left your game.");
|
||||
AudioManager.playPlayerLeft();
|
||||
}
|
||||
MageTray.instance.blink();
|
||||
}
|
||||
|
@ -450,7 +452,7 @@ class UpdateSeatsTask extends SwingWorker<Void, TableView> {
|
|||
int playerCount = 0;
|
||||
if (tableView != null) {
|
||||
for (SeatView seatView : tableView.getSeats()) {
|
||||
if (seatView.getPlayerId() != null && seatView.getPlayerType().equals("Human")) {
|
||||
if (seatView.getPlayerId() != null && seatView.getPlayerType() == PlayerType.HUMAN) {
|
||||
playerCount++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.deck;
|
||||
|
||||
import java.util.*;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.CanBeYourCommanderAbility;
|
||||
|
@ -41,8 +42,6 @@ import mage.constants.SetType;
|
|||
import mage.filter.FilterMana;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Plopman
|
||||
|
@ -71,6 +70,7 @@ public class Commander extends Constructed {
|
|||
banned.add("Gifts Ungiven");
|
||||
banned.add("Griselbrand");
|
||||
banned.add("Karakas");
|
||||
banned.add("Leovold, Emissary of Trest");
|
||||
banned.add("Library of Alexandria");
|
||||
banned.add("Limited Resources");
|
||||
banned.add("Mox Emerald");
|
||||
|
@ -82,7 +82,6 @@ public class Commander extends Constructed {
|
|||
banned.add("Panoptic Mirror");
|
||||
banned.add("Primeval Titan");
|
||||
banned.add("Prophet of Kruphix");
|
||||
banned.add("Protean Hulk");
|
||||
banned.add("Recurring Nightmare");
|
||||
banned.add("Rofellos, Llanowar Emissary");
|
||||
banned.add("Sundering Titan");
|
||||
|
|
|
@ -97,6 +97,7 @@ public class Legacy extends Constructed {
|
|||
banned.add("Rebirth");
|
||||
banned.add("Secret Summoning");
|
||||
banned.add("Secrets of Paradise");
|
||||
banned.add("Sensei's Divining Top");
|
||||
banned.add("Sentinel Dispatch");
|
||||
banned.add("Shahrazad");
|
||||
banned.add("Skullclamp");
|
||||
|
|
|
@ -82,6 +82,8 @@ public class Vintage extends Constructed {
|
|||
restricted.add("Dig Through Time");
|
||||
restricted.add("Fastbond");
|
||||
restricted.add("Flash");
|
||||
restricted.add("Gitaxian Probe");
|
||||
restricted.add("Gush");
|
||||
restricted.add("Imperial Seal");
|
||||
restricted.add("Library of Alexandria");
|
||||
restricted.add("Lion’s Eye Diamond");
|
||||
|
|
|
@ -181,11 +181,14 @@ public enum ChatManager {
|
|||
+ "<br/>\\me - shows the history of the current player"
|
||||
+ "<br/>\\list or \\l - Show a list of commands"
|
||||
+ "<br/>\\whisper or \\w [player name] [text] - whisper to the player with the given name"
|
||||
+ "<br/>\\card Card Name - Print oracle text for card"
|
||||
+ "<br/>[Card Name] - Show a highlighted card name"
|
||||
+ "<br/>\\ignore - shows current ignore list on this server."
|
||||
+ "<br/>\\ignore [username] - add a username to your ignore list on this server."
|
||||
+ "<br/>\\unignore [username] - remove a username from your ignore list on this server.";
|
||||
|
||||
final Pattern getCardTextPattern = Pattern.compile("^.card *(.*)");
|
||||
|
||||
private boolean performUserCommand(User user, String message, UUID chatId, boolean doError) {
|
||||
String command = message.substring(1).trim().toUpperCase(Locale.ENGLISH);
|
||||
if (doError) {
|
||||
|
@ -205,6 +208,25 @@ public enum ChatManager {
|
|||
chatSessions.get(chatId).broadcastInfoToUser(user, message);
|
||||
return true;
|
||||
}
|
||||
if (command.startsWith("CARD ")) {
|
||||
Matcher matchPattern = getCardTextPattern.matcher(message.toLowerCase());
|
||||
if (matchPattern.find()) {
|
||||
String cardName = matchPattern.group(1);
|
||||
CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, true);
|
||||
if (cardInfo != null) {
|
||||
cardInfo.getRules();
|
||||
message = "<font color=orange>" + cardInfo.getName() + "</font>: Cost:" + cardInfo.getManaCosts().toString() + ", Types:" + cardInfo.getTypes().toString() + ", ";
|
||||
for (String rule : cardInfo.getRules()) {
|
||||
message = message + rule;
|
||||
}
|
||||
} else {
|
||||
message = "Couldn't find: " + cardName;
|
||||
|
||||
}
|
||||
}
|
||||
chatSessions.get(chatId).broadcastInfoToUser(user, message);
|
||||
return true;
|
||||
}
|
||||
if (command.startsWith("W ") || command.startsWith("WHISPER ")) {
|
||||
String rest = message.substring(command.startsWith("W ") ? 3 : 9);
|
||||
int first = rest.indexOf(' ');
|
||||
|
|
|
@ -636,7 +636,8 @@ public class MageServerImpl implements MageServer {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
logger.error("table not found : " + tableId);
|
||||
// this can happen if a game ends and a player quits XMage or a match nearly at the same time as the game ends
|
||||
logger.trace("table not found : " + tableId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1119,12 +1120,12 @@ public class MageServerImpl implements MageServer {
|
|||
public void toggleActivation(final String sessionId, final String userName) throws MageException {
|
||||
execute("toggleActivation", sessionId, ()
|
||||
-> UserManager.instance.getUserByName(userName).ifPresent(user
|
||||
-> {
|
||||
user.setActive(!user.isActive());
|
||||
if (!user.isActive() && user.isConnected()) {
|
||||
SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
|
||||
}
|
||||
}));
|
||||
-> {
|
||||
user.setActive(!user.isActive());
|
||||
if (!user.isActive() && user.isConnected()) {
|
||||
SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1159,8 +1160,8 @@ public class MageServerImpl implements MageServer {
|
|||
if (title != null && message != null) {
|
||||
execute("sendFeedbackMessage", sessionId, ()
|
||||
-> SessionManager.instance.getSession(sessionId).ifPresent(
|
||||
session -> FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.getHost())
|
||||
));
|
||||
session -> FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.getHost())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
*/
|
||||
package mage.server;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import mage.MageException;
|
||||
import mage.constants.Constants;
|
||||
import mage.interfaces.callback.ClientCallback;
|
||||
|
@ -44,12 +49,6 @@ import org.jboss.remoting.callback.Callback;
|
|||
import org.jboss.remoting.callback.HandleCallbackException;
|
||||
import org.jboss.remoting.callback.InvokerCallbackHandler;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
@ -218,8 +217,8 @@ public class Session {
|
|||
if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) {
|
||||
return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
|
||||
} else {
|
||||
UserManager.instance.createUser(userName, host, authorizedUser).ifPresent(user ->
|
||||
user.setLockedUntil(null)
|
||||
UserManager.instance.createUser(userName, host, authorizedUser).ifPresent(user
|
||||
-> user.setLockedUntil(null)
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -263,7 +262,6 @@ public class Session {
|
|||
ChatManager.instance.sendReconnectMessage(userId);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
@ -337,7 +335,7 @@ public class Session {
|
|||
lockSet = true;
|
||||
logger.debug("SESSION LOCK SET sessionId: " + sessionId);
|
||||
} else {
|
||||
logger.error("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount());
|
||||
logger.warn("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount());
|
||||
}
|
||||
Optional<User> _user = UserManager.instance.getUser(userId);
|
||||
if (!_user.isPresent()) {
|
||||
|
@ -393,7 +391,7 @@ public class Session {
|
|||
call.setMessageId(messageId++);
|
||||
callbackHandler.handleCallbackOneway(new Callback(call));
|
||||
} catch (HandleCallbackException ex) {
|
||||
ex.printStackTrace();
|
||||
// ex.printStackTrace();
|
||||
UserManager.instance.getUser(userId).ifPresent(user -> {
|
||||
logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId);
|
||||
logger.warn(" - method: " + call.getMethod());
|
||||
|
|
|
@ -144,8 +144,10 @@ public enum SessionManager {
|
|||
case LostConnection: // user lost connection - session expires countdaoun starts
|
||||
session.userLostConnection();
|
||||
break;
|
||||
case ConnectingOtherInstance:
|
||||
break;
|
||||
default:
|
||||
logger.error("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId);
|
||||
logger.trace("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId);
|
||||
}
|
||||
} else {
|
||||
sessions.remove(sessionId);
|
||||
|
|
|
@ -41,9 +41,8 @@ import mage.constants.CardType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.common.FilterSpellOrPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
|
@ -59,7 +58,7 @@ public class CommitMemory extends SplitCard {
|
|||
private static final FilterSpellOrPermanent filter = new FilterSpellOrPermanent("spell or nonland permanent");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.not(new CardTypePredicate(CardType.LAND)));
|
||||
filter.setPermanentFilter(new FilterNonlandPermanent());
|
||||
}
|
||||
|
||||
public CommitMemory(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
71
Mage.Sets/src/mage/cards/d/Dogpile.java
Normal file
71
Mage.Sets/src/mage/cards/d/Dogpile.java
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.common.FilterAttackingCreature;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.target.common.TargetCreatureOrPlayer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class Dogpile extends CardImpl {
|
||||
|
||||
private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures you control");
|
||||
|
||||
static {
|
||||
filter.add(new ControllerPredicate(TargetController.YOU));
|
||||
}
|
||||
|
||||
public Dogpile(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}");
|
||||
|
||||
// Dogpile deals damage to target creature or player equal to the number of attacking creatures you control.
|
||||
this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)).
|
||||
setText("{this} deals damage to target creature or player equal to the number of attacking creatures you control"));
|
||||
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
|
||||
|
||||
}
|
||||
|
||||
public Dogpile(final Dogpile card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dogpile copy() {
|
||||
return new Dogpile(this);
|
||||
}
|
||||
}
|
|
@ -31,9 +31,10 @@ import java.util.UUID;
|
|||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.RevealTargetFromHandCost;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.keyword.DefenderAbility;
|
||||
|
@ -47,6 +48,7 @@ import mage.constants.Zone;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
|
@ -65,7 +67,7 @@ public class OratorOfOjutai extends CardImpl {
|
|||
}
|
||||
|
||||
public OratorOfOjutai(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||
this.subtype.add("Bird");
|
||||
this.subtype.add("Monk");
|
||||
this.power = new MageInt(0);
|
||||
|
@ -79,7 +81,7 @@ public class OratorOfOjutai extends CardImpl {
|
|||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("As an additional cost to cast {this}, you may reveal a Dragon card from your hand")));
|
||||
|
||||
// When Orator of Ojutai enters the battlefield, if you revealed a Dragon card or controlled a Dragon as you cast Orator of Ojutai, draw a card.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new OratorOfOjutaiEffect()), new DragonOnTheBattlefieldWhileSpellWasCastWatcher());
|
||||
this.addAbility(new OratorOfOjutaiTriggeredAbility(new OratorOfOjutaiEffect()), new DragonOnTheBattlefieldWhileSpellWasCastWatcher());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,7 +90,7 @@ public class OratorOfOjutai extends CardImpl {
|
|||
Player controller = game.getPlayer(ability.getControllerId());
|
||||
if (controller != null) {
|
||||
if (controller.getHand().count(filter, game) > 0) {
|
||||
ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0,1, filter)));
|
||||
ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +106,42 @@ public class OratorOfOjutai extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class OratorOfOjutaiTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public OratorOfOjutaiTriggeredAbility(Effect effect) {
|
||||
super(Zone.BATTLEFIELD, effect, false);
|
||||
}
|
||||
|
||||
public OratorOfOjutaiTriggeredAbility(final OratorOfOjutaiTriggeredAbility 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) {
|
||||
//Intervening if must be checked
|
||||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId());
|
||||
DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = (DragonOnTheBattlefieldWhileSpellWasCastWatcher) game.getState().getWatchers().get("DragonOnTheBattlefieldWhileSpellWasCastWatcher");
|
||||
return event.getTargetId().equals(getSourceId())
|
||||
&& watcher != null
|
||||
&& watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When {this} enters the battlefield, " + super.getRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OratorOfOjutaiTriggeredAbility copy() {
|
||||
return new OratorOfOjutaiTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
||||
class OratorOfOjutaiEffect extends OneShotEffect {
|
||||
|
||||
public OratorOfOjutaiEffect() {
|
||||
|
@ -122,6 +160,7 @@ class OratorOfOjutaiEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
//Intervening if is checked again on resolution
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
||||
|
@ -129,9 +168,9 @@ class OratorOfOjutaiEffect extends OneShotEffect {
|
|||
DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = (DragonOnTheBattlefieldWhileSpellWasCastWatcher) game.getState().getWatchers().get("DragonOnTheBattlefieldWhileSpellWasCastWatcher");
|
||||
if (watcher != null && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId())) {
|
||||
controller.drawCards(1, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
81
Mage.Sets/src/mage/cards/s/SabertoothAlleyCat.java
Normal file
81
Mage.Sets/src/mage/cards/s/SabertoothAlleyCat.java
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.AttacksEachTurnStaticAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect;
|
||||
import mage.abilities.keyword.DefenderAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SabertoothAlleyCat extends CardImpl {
|
||||
|
||||
public SabertoothAlleyCat(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}");
|
||||
|
||||
this.subtype.add("Cat");
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Sabertooth Alley Cat attacks each turn if able.
|
||||
this.addAbility(new AttacksEachTurnStaticAbility());
|
||||
|
||||
// {1}{R}: Creatures without defender can't block Sabertooth Alley Cat this turn.
|
||||
this.addAbility(new SimpleActivatedAbility(
|
||||
Zone.BATTLEFIELD,
|
||||
new CantBeBlockedByCreaturesSourceEffect(
|
||||
(FilterCreaturePermanent) new FilterCreaturePermanent().add(Predicates.not(new AbilityPredicate(DefenderAbility.class))),
|
||||
Duration.EndOfTurn
|
||||
)
|
||||
.setText("Creatures without defender can't block {this} this turn"),
|
||||
new ManaCostsImpl<>("{1}{R}")));
|
||||
}
|
||||
|
||||
public SabertoothAlleyCat(final SabertoothAlleyCat card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SabertoothAlleyCat copy() {
|
||||
return new SabertoothAlleyCat(this);
|
||||
}
|
||||
}
|
74
Mage.Sets/src/mage/cards/s/SeedsOfStrength.java
Normal file
74
Mage.Sets/src/mage/cards/s/SeedsOfStrength.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.SecondTargetPointer;
|
||||
import mage.target.targetpointer.ThirdTargetPointer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SeedsOfStrength extends CardImpl {
|
||||
|
||||
public SeedsOfStrength(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{W}");
|
||||
|
||||
// Target creature gets +1/+1 until end of turn.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (1st)")));
|
||||
// Target creature gets +1/+1 until end of turn.
|
||||
Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn).setText("<br>Target creature gets +1/+1 until end of turn.");
|
||||
effect.setTargetPointer(new SecondTargetPointer());
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (2nd)")));
|
||||
// Target creature gets +1/+1 until end of turn.
|
||||
effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn).setText("<br>Target creature gets +1/+1 until end of turn.");
|
||||
effect.setTargetPointer(new ThirdTargetPointer());
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (3rd)")));
|
||||
}
|
||||
|
||||
public SeedsOfStrength(final SeedsOfStrength card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeedsOfStrength copy() {
|
||||
return new SeedsOfStrength(this);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AsEntersBattlefieldAbility;
|
||||
|
@ -37,13 +38,13 @@ import mage.cards.*;
|
|||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
|
@ -51,7 +52,7 @@ import java.util.UUID;
|
|||
public class TheMimeoplasm extends CardImpl {
|
||||
|
||||
public TheMimeoplasm(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{U}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}{B}");
|
||||
addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add("Ooze");
|
||||
|
||||
|
@ -95,10 +96,12 @@ class TheMimeoplasmEffect extends OneShotEffect {
|
|||
if (new CardsInAllGraveyardsCount(new FilterCreatureCard()).calculate(game, source, this) >= 2) {
|
||||
if (controller.chooseUse(Outcome.Benefit, "Do you want to exile two creature cards from graveyards?", source, game)) {
|
||||
TargetCardInGraveyard targetCopy = new TargetCardInGraveyard(new FilterCreatureCard("creature card to become a copy of"));
|
||||
TargetCardInGraveyard targetCounters = new TargetCardInGraveyard(new FilterCreatureCard("creature card to determine amount of additional +1/+1 counters"));
|
||||
if (controller.choose(Outcome.Copy, targetCopy, source.getSourceId(), game)) {
|
||||
Card cardToCopy = game.getCard(targetCopy.getFirstTarget());
|
||||
if (cardToCopy != null) {
|
||||
FilterCreatureCard filter = new FilterCreatureCard("creature card to determine amount of additional +1/+1 counters");
|
||||
filter.add(Predicates.not(new CardIdPredicate(cardToCopy.getId())));
|
||||
TargetCardInGraveyard targetCounters = new TargetCardInGraveyard(filter);
|
||||
if (controller.choose(Outcome.Copy, targetCounters, source.getSourceId(), game)) {
|
||||
Card cardForCounters = game.getCard(targetCounters.getFirstTarget());
|
||||
if (cardForCounters != null) {
|
||||
|
|
|
@ -50,6 +50,7 @@ import mage.game.events.ZoneChangeEvent;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -64,7 +65,7 @@ public class TorrentialGearhulk extends CardImpl {
|
|||
}
|
||||
|
||||
public TorrentialGearhulk(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{U}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{U}{U}");
|
||||
this.subtype.add("Construct");
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(6);
|
||||
|
@ -111,7 +112,7 @@ class TorrentialGearhulkEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
if (card != null && card.getSpellAbility() != null) {
|
||||
if (controller.chooseUse(outcome, "Cast " + card.getLogName() + '?', source, game)) {
|
||||
if (controller.cast(card.getSpellAbility(), game, true)) {
|
||||
ContinuousEffect effect = new TorrentialGearhulkReplacementEffect(card.getId());
|
||||
|
@ -119,6 +120,9 @@ class TorrentialGearhulkEffect extends OneShotEffect {
|
|||
game.addEffect(effect, source);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger.getLogger(TorrentialGearhulkEffect.class).error("Torrential Gearhulk - Instant card without spellAbility : " + card.getName());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import mage.abilities.costs.mana.GenericManaCost;
|
|||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.DestroyAllEffect;
|
||||
import mage.abilities.effects.common.FlipCoinEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
@ -74,12 +75,13 @@ public class WireflyHive extends CardImpl {
|
|||
class WireflyToken extends Token {
|
||||
|
||||
WireflyToken() {
|
||||
super("Wirefly", "2/2 colorless Insect artifact creature token named Wirefly");
|
||||
super("Wirefly", "2/2 colorless Insect artifact creature token with flying named Wirefly");
|
||||
this.setOriginalExpansionSetCode("DST");
|
||||
this.getPower().modifyBaseValue(2);
|
||||
this.getToughness().modifyBaseValue(2);
|
||||
this.getSubtype(null).add("Insect");
|
||||
this.addCardType(CardType.ARTIFACT);
|
||||
this.addCardType(CardType.CREATURE);
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public class MasterpieceSeriesAmonkhet extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Attrition", 19, Rarity.SPECIAL, mage.cards.a.Attrition.class));
|
||||
cards.add(new SetCardInfo("Austere Command", 1, Rarity.SPECIAL, mage.cards.a.AustereCommand.class));
|
||||
cards.add(new SetCardInfo("Aven Mindcensor", 2, Rarity.SPECIAL, mage.cards.a.AvenMindcensor.class));
|
||||
cards.add(new SetCardInfo("Bontu the Glorified", 20, Rarity.SPECIAL, mage.cards.b.BontuTheGlorified.class));
|
||||
cards.add(new SetCardInfo("Chain Lightning", 26, Rarity.SPECIAL, mage.cards.c.ChainLightning.class));
|
||||
cards.add(new SetCardInfo("Consecrated Sphinx", 8, Rarity.SPECIAL, mage.cards.c.ConsecratedSphinx.class));
|
||||
cards.add(new SetCardInfo("Containment Priest", 3, Rarity.SPECIAL, mage.cards.c.ContainmentPriest.class));
|
||||
|
@ -75,6 +76,7 @@ public class MasterpieceSeriesAmonkhet extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Mind Twist", 24, Rarity.SPECIAL, mage.cards.m.MindTwist.class));
|
||||
cards.add(new SetCardInfo("Oketra the True", 5, Rarity.SPECIAL, mage.cards.o.OketraTheTrue.class));
|
||||
cards.add(new SetCardInfo("Pact of Negation", 16, Rarity.SPECIAL, mage.cards.p.PactOfNegation.class));
|
||||
cards.add(new SetCardInfo("Rhonas the Indomitable", 28, Rarity.SPECIAL, mage.cards.r.RhonasTheIndomitable.class));
|
||||
cards.add(new SetCardInfo("Spell Pierce", 17, Rarity.SPECIAL, mage.cards.s.SpellPierce.class));
|
||||
cards.add(new SetCardInfo("Stifle", 18, Rarity.SPECIAL, mage.cards.s.Stifle.class));
|
||||
cards.add(new SetCardInfo("Vindicate", 30, Rarity.SPECIAL, mage.cards.v.Vindicate.class));
|
||||
|
|
|
@ -117,6 +117,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Disembowel", 85, Rarity.COMMON, mage.cards.d.Disembowel.class));
|
||||
cards.add(new SetCardInfo("Divebomber Griffin", 14, Rarity.UNCOMMON, mage.cards.d.DivebomberGriffin.class));
|
||||
cards.add(new SetCardInfo("Dizzy Spell", 43, Rarity.COMMON, mage.cards.d.DizzySpell.class));
|
||||
cards.add(new SetCardInfo("Dogpile", 120, Rarity.COMMON, mage.cards.d.Dogpile.class));
|
||||
cards.add(new SetCardInfo("Doubling Season", 158, Rarity.RARE, mage.cards.d.DoublingSeason.class));
|
||||
cards.add(new SetCardInfo("Dowsing Shaman", 159, Rarity.UNCOMMON, mage.cards.d.DowsingShaman.class));
|
||||
cards.add(new SetCardInfo("Drake Familiar", 44, Rarity.COMMON, mage.cards.d.DrakeFamiliar.class));
|
||||
|
@ -252,6 +253,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Rolling Spoil", 179, Rarity.UNCOMMON, mage.cards.r.RollingSpoil.class));
|
||||
cards.add(new SetCardInfo("Roofstalker Wight", 102, Rarity.COMMON, mage.cards.r.RoofstalkerWight.class));
|
||||
cards.add(new SetCardInfo("Root-Kin Ally", 180, Rarity.UNCOMMON, mage.cards.r.RootKinAlly.class));
|
||||
cards.add(new SetCardInfo("Sabertooth Alley Cat", 140, Rarity.COMMON, mage.cards.s.SabertoothAlleyCat.class));
|
||||
cards.add(new SetCardInfo("Sacred Foundry", 280, Rarity.RARE, mage.cards.s.SacredFoundry.class));
|
||||
cards.add(new SetCardInfo("Sadistic Augermage", 103, Rarity.COMMON, mage.cards.s.SadisticAugermage.class));
|
||||
cards.add(new SetCardInfo("Sandsower", 28, Rarity.UNCOMMON, mage.cards.s.Sandsower.class));
|
||||
|
@ -261,6 +263,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Scion of the Wild", 182, Rarity.RARE, mage.cards.s.ScionOfTheWild.class));
|
||||
cards.add(new SetCardInfo("Searing Meditation", 226, Rarity.RARE, mage.cards.s.SearingMeditation.class));
|
||||
cards.add(new SetCardInfo("Seed Spark", 30, Rarity.UNCOMMON, mage.cards.s.SeedSpark.class));
|
||||
cards.add(new SetCardInfo("Seeds of Strength", 227, Rarity.COMMON, mage.cards.s.SeedsOfStrength.class));
|
||||
cards.add(new SetCardInfo("Seismic Spike", 141, Rarity.COMMON, mage.cards.s.SeismicSpike.class));
|
||||
cards.add(new SetCardInfo("Selesnya Evangel", 228, Rarity.COMMON, mage.cards.s.SelesnyaEvangel.class));
|
||||
cards.add(new SetCardInfo("Selesnya Guildmage", 252, Rarity.UNCOMMON, mage.cards.s.SelesnyaGuildmage.class));
|
||||
|
|
|
@ -88,4 +88,31 @@ public class ExertTest extends CardTestPlayerBase {
|
|||
assertLife(playerB, 24);
|
||||
assertTapped(gbInitiate, false); // stolen creature exerted does untap during owner's untap step
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combatCelebrantExertedCannotAttackDuringNextCombatPhase() {
|
||||
/*
|
||||
Combat Celebrant 2R
|
||||
Creature - Human Warrior 4/1
|
||||
If Combat Celebrant hasn't been exerted this turn, you may exert it as it attacks. When you do, untap all other creatures you control and after this phase, there is an additional combat phase.
|
||||
*/
|
||||
String cCelebrant = "Combat Celebrant";
|
||||
String memnite = "Memnite"; // {0} 1/1
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, cCelebrant);
|
||||
addCard(Zone.BATTLEFIELD, playerA, memnite);
|
||||
|
||||
attack(1, playerA, cCelebrant);
|
||||
attack(1, playerA, memnite);
|
||||
setChoice(playerA, "Yes"); // exert for extra turn and untap all creatures
|
||||
attack(1, playerA, cCelebrant); // should not be able to attack again due to "if has not been exerted this turn"
|
||||
attack(1, playerA, memnite);
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 14); // 4 + 1 + 1 (Celebrant once, Memnite twice)
|
||||
assertTapped(cCelebrant, true);
|
||||
assertTapped(memnite, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ package org.mage.test.cards.abilities.other;
|
|||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -46,9 +47,9 @@ public class AuratouchedMageTest extends CardTestPlayerBase {
|
|||
* card and put it into your hand. Then shuffle your library.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
@Ignore //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice.
|
||||
@Test
|
||||
|
||||
//If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice.
|
||||
@Ignore
|
||||
public void testAuratouchedMageEffectHasMadeIntoTypeArtifact() {
|
||||
//Any Aura card you find must be able to enchant Auratouched Mage as it currently exists, or as it most recently existed on the battlefield if it’s no
|
||||
//longer on the battlefield. If an effect has made the Mage an artifact, for example, you could search for an Aura with “enchant artifact.”
|
||||
|
@ -70,7 +71,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "Relic Ward", 1);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testGainsLegalAura() {
|
||||
// Expected result: Brainwash gets placed on Auratouched Mage
|
||||
|
@ -89,8 +90,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase {
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
@Ignore //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice.
|
||||
//If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice.
|
||||
@Test
|
||||
public void testAuratouchedMageNotOnBattlefield() {
|
||||
// Expected result: Auratouched Mage is exiled immediately after entering the battlefield, the legal aura (Brainwash) gets put into controller's hand
|
||||
|
@ -113,5 +113,4 @@ public class AuratouchedMageTest extends CardTestPlayerBase {
|
|||
assertLibraryCount(playerA, "Animate Wall", 1);
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -108,4 +108,37 @@ public class EndTurnEffectTest extends CardTestPlayerBase {
|
|||
assertHandCount(playerB, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to remove a Aftermath card from spell
|
||||
*/
|
||||
@Test
|
||||
public void testSpellAftermath() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
// Insult Sorcery {2}{R}
|
||||
// Damage can't be prevented this turn. If a source you control would deal damage this turn, it deals double that damage instead.
|
||||
// Injury Sorcery {2}{R}
|
||||
// Aftermath (Cast this spell only from your graveyard. Then exile it.)
|
||||
// Injury deals 2 damage to target creature and 2 damage to target player.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Insult // Injury");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);
|
||||
// End the turn.
|
||||
// At the beginning of your next end step, you lose the game.
|
||||
addCard(Zone.HAND, playerB, "Glorious End"); //Instant {2}{R}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Injury", "Silvercoat Lion");
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glorious End", NO_TARGET, "Injury");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertExileCount(playerA, "Insult // Injury", 1);
|
||||
assertGraveyardCount(playerB, "Glorious End", 0);
|
||||
assertHandCount(playerA, 0);
|
||||
assertHandCount(playerB, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class LayerTests extends CardTestPlayerBase {
|
|||
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Ignore //Works fine in the game. Test fails, though.
|
||||
public void complexExampleFromLayersArticle() {
|
||||
/*In play there is a Grizzly Bears which has already been Giant Growthed,
|
||||
a Bog Wraith enchanted by a Lignify, and Figure of Destiny with its 3rd ability activated.
|
||||
|
@ -79,6 +79,7 @@ public class LayerTests extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", "Grizzly Bears");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lignify", "Bog Wrath");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}:");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}:");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}{R/W}{R/W}{R/W}:");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mirrorweave", "Figure of Destiny");
|
||||
|
@ -92,4 +93,56 @@ public class LayerTests extends CardTestPlayerBase {
|
|||
assertPowerToughness(playerA, "Figure of Destiny", 0, 4, Filter.ComparisonScope.All);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUrborgWithAnimateLandAndOvinize() {
|
||||
// Animate Land: target land is a 3/3 until end of turn and is still a land.
|
||||
// Ovinize: target creature becomes 0/1 and loses all abilities until end of turn.
|
||||
// Urborg, Tomb of Yawgmoth : Each land is a Swamp in addition to its other types.
|
||||
// Expected behavior: Urborg loses all abilities and becomes a 0/1 creature.
|
||||
addCard(Zone.HAND, playerA, "Animate Land", 1);
|
||||
addCard(Zone.HAND, playerA, "Ovinize", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Land", "Urborg, Tomb of Yawgmoth");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Urborg, Tomb of Yawgmoth");
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertType("Urborg, Tomb of Yawgmoth", CardType.CREATURE, "Swamp"); // Urborg is a creature
|
||||
assertPowerToughness(playerA, "Urborg, Tomb of Yawgmoth", 0, 1); // Urborg is a 0/1 creature
|
||||
|
||||
}
|
||||
|
||||
@Ignore //This works fine in the game. Test fails.
|
||||
public void testFromAnArticle() {
|
||||
/*
|
||||
Aiden has a Battlegate Mimic on the battlefield. Nick controls two Wilderness Hypnotists.
|
||||
Aiden casts a Scourge of the Nobilis, targeting the Mimic; after that resolves Nick activates
|
||||
one of his Hypnotist's abilities, targeting the Mimic. Aiden attacks with the Mimic, and
|
||||
casts Inside Out before the damage step. Once Inside Out resolves, Nick activates the ability
|
||||
of his other Hypnotist. How much damage will the Mimic deal?
|
||||
*/
|
||||
addCard(Zone.HAND, playerA, "Scourge of the Nobilis", 1);
|
||||
addCard(Zone.HAND, playerA, "Inside Out", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Battlegate Mimic", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 8);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Wilderness Hypnotist", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scourge of the Nobilis", "Battlegate Mimic");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inside Out", "Battlegate Mimic");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic");
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Battlegate Mimic", 4, 2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.abilities.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
|
@ -144,14 +145,16 @@ class LicidContinuousEffect extends ContinuousEffectImpl {
|
|||
licid.getSubtype(game).add("Aura");
|
||||
break;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
ArrayList<Ability> toRemove = new ArrayList<>();
|
||||
for (Ability ability : licid.getAbilities(game)) {
|
||||
for (Effect effect : ability.getEffects()) {
|
||||
if (effect instanceof LicidEffect) {
|
||||
licid.getAbilities(game).remove(ability);
|
||||
toRemove.add(ability);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
licid.getAbilities(game).removeAll(toRemove);
|
||||
Ability ability = new EnchantAbility("creature");
|
||||
ability.setRuleAtTheTop(true);
|
||||
licid.addAbility(ability, source.getSourceId(), game);
|
||||
|
|
|
@ -1218,7 +1218,10 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
logger.error("Replacement effect without ability: " + entry.getKey().toString());
|
||||
if (!(entry.getKey() instanceof AuraReplacementEffect)
|
||||
&& !(entry.getKey() instanceof PlaneswalkerRedirectionEffect)) {
|
||||
logger.error("Replacement effect without ability: " + entry.getKey().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return texts;
|
||||
|
|
|
@ -1,7 +1,29 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.effects.common.combat;
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.abilities.effects.common.counter;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
@ -47,8 +46,8 @@ import mage.util.CardUtil;
|
|||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class RemoveCounterTargetEffect extends OneShotEffect {
|
||||
|
||||
private final Counter counter;
|
||||
|
||||
public RemoveCounterTargetEffect() {
|
||||
|
@ -69,23 +68,25 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent p = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
if(p != null) {
|
||||
if (p != null) {
|
||||
Counter toRemove = (counter == null ? selectCounterType(game, source, p) : counter);
|
||||
if(toRemove != null && p.getCounters(game).getCount(toRemove.getName()) >= toRemove.getCount()) {
|
||||
if (toRemove != null && p.getCounters(game).getCount(toRemove.getName()) >= toRemove.getCount()) {
|
||||
p.removeCounters(toRemove.getName(), toRemove.getCount(), game);
|
||||
if(!game.isSimulation())
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers("Removed " + toRemove.getCount() + ' ' + toRemove.getName()
|
||||
+ " counter from " + p.getName());
|
||||
+ " counter from " + p.getName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Card c = game.getCard(targetPointer.getFirst(game, source));
|
||||
if (c != null && counter != null && c.getCounters(game).getCount(counter.getName()) >= counter.getCount()) {
|
||||
c.removeCounters(counter.getName(), counter.getCount(), game);
|
||||
if (!game.isSimulation())
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(new StringBuilder("Removed ").append(counter.getCount()).append(' ').append(counter.getName())
|
||||
.append(" counter from ").append(c.getName())
|
||||
.append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString());
|
||||
.append(" counter from ").append(c.getName())
|
||||
.append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -93,12 +94,12 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
|
|||
|
||||
private Counter selectCounterType(Game game, Ability source, Permanent permanent) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if(controller != null && !permanent.getCounters(game).isEmpty()) {
|
||||
if (controller != null && !permanent.getCounters(game).isEmpty()) {
|
||||
String counterName = null;
|
||||
if(permanent.getCounters(game).size() > 1) {
|
||||
if (permanent.getCounters(game).size() > 1) {
|
||||
Choice choice = new ChoiceImpl(true);
|
||||
Set<String> choices = new HashSet<>();
|
||||
for(Counter counter : permanent.getCounters(game).values()) {
|
||||
for (Counter counter : permanent.getCounters(game).values()) {
|
||||
if (permanent.getCounters(game).getCount(counter.getName()) > 0) {
|
||||
choices.add(counter.getName());
|
||||
}
|
||||
|
@ -108,8 +109,8 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
|
|||
controller.choose(Outcome.Detriment, choice, game);
|
||||
counterName = choice.getChoice();
|
||||
} else {
|
||||
for(Counter counter : permanent.getCounters(game).values()) {
|
||||
if(counter.getCount() > 0) {
|
||||
for (Counter counter : permanent.getCounters(game).values()) {
|
||||
if (counter.getCount() > 0) {
|
||||
counterName = counter.getName();
|
||||
}
|
||||
}
|
||||
|
@ -131,14 +132,13 @@ public class RemoveCounterTargetEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
String text = "remove ";
|
||||
if(counter == null) {
|
||||
if (counter == null) {
|
||||
text += "a counter";
|
||||
} else {
|
||||
text += CardUtil.numberToText(counter.getCount(), "a") + ' ' + counter.getName();
|
||||
text += counter.getCount() > 1 ? " counters" : " counter";
|
||||
}
|
||||
else {
|
||||
text += CardUtil.numberToText(counter.getCount(), "a") + ' ' + counter.getName();
|
||||
text += counter.getCount() > 1 ? " counters" : " counter";
|
||||
}
|
||||
text += " from target " + mode.getTargets().get(0).getTargetName();
|
||||
text += " from target " + (mode.getTargets().isEmpty() ? " object" : mode.getTargets().get(0).getTargetName());
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,11 +27,10 @@
|
|||
*/
|
||||
package mage.filter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @param <E>
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
@ -45,7 +44,7 @@ public interface Filter<E> extends Serializable {
|
|||
|
||||
boolean match(E o, Game game);
|
||||
|
||||
void add(Predicate predicate);
|
||||
Filter<E> add(Predicate predicate);
|
||||
|
||||
boolean checkObjectClass(Object object);
|
||||
|
||||
|
|
|
@ -65,8 +65,9 @@ public abstract class FilterImpl<E> implements Filter<E> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public final void add(Predicate predicate) {
|
||||
public final Filter add(Predicate predicate) {
|
||||
predicates.add(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -971,6 +971,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
}
|
||||
game.fireEvent(GameEvent.getEvent(EventType.SACRIFICED_PERMANENT, objectId, sourceId, controllerId));
|
||||
game.checkStateAndTriggered();
|
||||
game.applyEffects();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -276,7 +276,7 @@ public class Turn implements Serializable {
|
|||
|
||||
setEndTurnRequested(true);
|
||||
|
||||
// 1) All spells and abilities on the stack are exiled. This includes Time Stop, though it will continue to resolve.
|
||||
// 1) All spells and abilities on the stack are exiled. This includes (e.g.) Time Stop, though it will continue to resolve.
|
||||
// It also includes spells and abilities that can't be countered.
|
||||
while (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().peekFirst();
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.target.targetpointer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ludwig.Hirth
|
||||
*/
|
||||
public class ThirdTargetPointer implements TargetPointer {
|
||||
|
||||
private Map<UUID, Integer> zoneChangeCounter = new HashMap<>();
|
||||
|
||||
public static ThirdTargetPointer getInstance() {
|
||||
return new ThirdTargetPointer();
|
||||
}
|
||||
|
||||
public ThirdTargetPointer() {
|
||||
}
|
||||
|
||||
public ThirdTargetPointer(ThirdTargetPointer targetPointer) {
|
||||
this.zoneChangeCounter = new HashMap<>();
|
||||
for (Map.Entry<UUID, Integer> entry : targetPointer.zoneChangeCounter.entrySet()) {
|
||||
this.zoneChangeCounter.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Game game, Ability source) {
|
||||
if (source.getTargets().size() > 2) {
|
||||
for (UUID target : source.getTargets().get(2).getTargets()) {
|
||||
Card card = game.getCard(target);
|
||||
if (card != null) {
|
||||
this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getTargets(Game game, Ability source) {
|
||||
ArrayList<UUID> target = new ArrayList<>();
|
||||
if (source.getTargets().size() > 2) {
|
||||
for (UUID targetId : source.getTargets().get(2).getTargets()) {
|
||||
Card card = game.getCard(targetId);
|
||||
if (card != null && zoneChangeCounter.containsKey(targetId)
|
||||
&& card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) {
|
||||
continue;
|
||||
}
|
||||
target.add(targetId);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getFirst(Game game, Ability source) {
|
||||
if (source.getTargets().size() > 2) {
|
||||
UUID targetId = source.getTargets().get(2).getFirstTarget();
|
||||
if (zoneChangeCounter.containsKey(targetId)) {
|
||||
Card card = game.getCard(targetId);
|
||||
if (card != null && zoneChangeCounter.containsKey(targetId)
|
||||
&& card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return targetId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetPointer copy() {
|
||||
return new ThirdTargetPointer(this);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue