mirror of
https://github.com/correl/mage.git
synced 2025-04-10 17:00:08 -09:00
Merge pull request #6093 from magefree/asThough_and_non_hand_plays_rework
As though and non hand plays rework
This commit is contained in:
commit
71cd4b73b4
41 changed files with 663 additions and 442 deletions
Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human
Mage.Sets/src/mage/cards
b
c
d
g
h
o
p
q
r
s
t
v
Mage.Tests/src/test/java/org/mage/test
AI/basic
cards
abilities/activated
asthough
continuous
cost
single
player
stub
Mage/src/main/java/mage
abilities
cards
constants
players
|
@ -1742,7 +1742,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
if (ability instanceof PlayLandAbility) {
|
||||
return true;
|
||||
}
|
||||
if (!ability.getSourceId().equals(getCastSourceIdWithAlternateMana())
|
||||
if (!getCastSourceIdWithAlternateMana().contains(ability.getSourceId())
|
||||
&& ability.getManaCostsToPay().convertedManaCost() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -96,15 +96,14 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card cardOnTop = game.getCard(objectId);
|
||||
if (cardOnTop == null) {
|
||||
return false;
|
||||
}
|
||||
if (playerId.equals(source.getControllerId())
|
||||
&& cardOnTop.isOwnedBy(source.getControllerId())) {
|
||||
Player controller = game.getPlayer(cardOnTop.getOwnerId());
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
|
||||
if (playerId.equals(source.getControllerId()) && cardToCheck.isOwnedBy(source.getControllerId())) {
|
||||
Player controller = game.getPlayer(cardToCheck.getOwnerId());
|
||||
if (controller != null
|
||||
&& cardOnTop.equals(controller.getLibrary().getFromTop(game))) {
|
||||
&& controller.getLibrary().getFromTop(game) != null
|
||||
&& objectId.equals(controller.getLibrary().getFromTop(game).getId())) {
|
||||
if (affectedAbility instanceof ActivatedAbility) {
|
||||
ActivatedAbility activatedAbility = (ActivatedAbility) affectedAbility;
|
||||
// add the life cost first
|
||||
|
|
|
@ -156,11 +156,12 @@ class CovetousUrgeSpendAnyManaEffect extends AsThoughEffectImpl implements AsTho
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& fixedTarget.getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
|
@ -21,8 +20,9 @@ import mage.target.common.TargetOpponent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Styxo
|
||||
*/
|
||||
public final class CunningAbduction extends CardImpl {
|
||||
|
@ -125,12 +125,8 @@ class CunningAbductionSpendAnyManaEffect extends AsThoughEffectImpl implements A
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
this.discard();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -26,8 +24,10 @@ import mage.players.Player;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class DaxosOfMeletis extends CardImpl {
|
||||
|
@ -178,11 +178,11 @@ class DaxosOfMeletisSpendAnyManaEffect extends AsThoughEffectImpl implements AsT
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DiesAttachedTriggeredAbility;
|
||||
|
@ -15,14 +13,7 @@ import mage.abilities.keyword.EnchantAbility;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.game.Game;
|
||||
|
@ -32,8 +23,10 @@ import mage.players.Player;
|
|||
import mage.target.TargetPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class DeadMansChest extends CardImpl {
|
||||
|
@ -142,9 +135,7 @@ class DeadMansChestCastFromExileEffect extends AsThoughEffectImpl {
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (objectId.equals(getTargetPointer().getFirst(game, source))) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
@ -181,12 +172,8 @@ class DeadMansChestSpendManaEffect extends AsThoughEffectImpl implements AsThoug
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
|
|
@ -1,27 +1,15 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.AsThoughManaEffect;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
|
@ -34,8 +22,10 @@ import mage.players.Player;
|
|||
import mage.target.common.TargetCardInOpponentsGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class DireFleetDaredevil extends CardImpl {
|
||||
|
@ -144,10 +134,11 @@ class DireFleetDaredevilSpendAnyManaEffect extends AsThoughEffectImpl implements
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -25,8 +22,11 @@ import mage.target.common.TargetOpponent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class GontiLordOfLuxury extends CardImpl {
|
||||
|
@ -185,22 +185,18 @@ class GontiLordOfLuxurySpendAnyManaEffect extends AsThoughEffectImpl implements
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Card theCard = game.getCard(objectId);
|
||||
if(theCard == null){
|
||||
if (theCard == null) {
|
||||
return false;
|
||||
}
|
||||
Card mainCard = theCard.getMainCard();
|
||||
if(mainCard == null){
|
||||
if (mainCard == null) {
|
||||
return false;
|
||||
}
|
||||
objectId = mainCard.getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
this.discard();
|
||||
|
@ -238,11 +234,11 @@ class GontiLordOfLuxuryLookEffect extends AsThoughEffectImpl {
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Card theCard = game.getCard(objectId);
|
||||
if(theCard == null){
|
||||
if (theCard == null) {
|
||||
return false;
|
||||
}
|
||||
Card mainCard = theCard.getMainCard();
|
||||
if(mainCard == null){
|
||||
if (mainCard == null) {
|
||||
return false;
|
||||
}
|
||||
objectId = mainCard.getId(); // for split cards
|
||||
|
|
|
@ -233,11 +233,12 @@ class GrenzoHavocRaiserSpendAnyManaEffect extends AsThoughEffectImpl implements
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -106,7 +106,7 @@ class HostageTakerExileEffect extends OneShotEffect {
|
|||
ContinuousEffect effect = new HostageTakerSpendAnyManaEffect();
|
||||
effect.setTargetPointer(new FixedTarget(card.getId(), game));
|
||||
game.addEffect(effect, source);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,11 +173,12 @@ class HostageTakerSpendAnyManaEffect extends AsThoughEffectImpl implements AsTho
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& fixedTarget.getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.o;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
|
@ -19,8 +17,9 @@ import mage.players.ManaPoolItem;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class OathOfNissa extends CardImpl {
|
||||
|
@ -128,13 +127,10 @@ class OathOfNissaSpendAnyManaEffect extends AsThoughEffectImpl implements AsThou
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (source.isControlledBy(affectedControllerId)) {
|
||||
MageObject mageObject = game.getObject(objectId);
|
||||
if (mageObject != null) {
|
||||
if (mageObject.isPlaneswalker()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return mageObject != null && mageObject.isPlaneswalker();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
|
@ -10,12 +9,7 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
@ -25,8 +19,9 @@ import mage.target.common.TargetOpponent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class PsychicIntrusion extends CardImpl {
|
||||
|
@ -149,10 +144,9 @@ class PsychicIntrusionCastFromExileEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(getTargetPointer().getFirst(game, source))) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
@ -189,12 +183,8 @@ class PsychicIntrusionSpendAnyManaEffect extends AsThoughEffectImpl implements A
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.q;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
|
@ -10,28 +8,20 @@ import mage.abilities.common.SimpleStaticAbility;
|
|||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.AsThoughManaEffect;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.ManaPoolItem;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public final class QuicksilverElemental extends CardImpl {
|
||||
|
@ -151,10 +141,9 @@ class QuickSilverElementalBlueManaEffect extends AsThoughEffectImpl implements A
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(getTargetPointer().getFirst(game, source))) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -194,11 +194,12 @@ class RobberOfTheRichSpendAnyManaEffect extends AsThoughEffectImpl implements As
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& fixedTarget.getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
|
@ -13,13 +10,7 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
@ -27,8 +18,10 @@ import mage.players.Player;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class StolenStrategy extends CardImpl {
|
||||
|
@ -165,11 +158,11 @@ class StolenStrategySpendAnyManaEffect extends AsThoughEffectImpl implements AsT
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -12,18 +9,8 @@ import mage.abilities.effects.AsThoughManaEffect;
|
|||
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.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
@ -32,8 +19,11 @@ import mage.target.TargetCard;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class ThiefOfSanity extends CardImpl {
|
||||
|
@ -200,12 +190,8 @@ class ThiefOfSanitySpendAnyManaEffect extends AsThoughEffectImpl implements AsTh
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(authorizedPlayerId)) {
|
||||
// if the card moved from exile to stack the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(authorizedPlayerId);
|
||||
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
this.discard();
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.common.ExileCardsFromTopOfLibraryTargetEffect;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.AsThoughManaEffect;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.BountyAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
|
@ -24,8 +24,10 @@ import mage.target.common.TargetOpponentsCreaturePermanent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author NinthWorld
|
||||
*/
|
||||
public final class TobiasBeckett extends CardImpl {
|
||||
|
@ -75,7 +77,7 @@ class TobiasBeckettEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Permanent bountyTriggered = game.getPermanent(this.getTargetPointer().getFirst(game, source));
|
||||
if(bountyTriggered != null) {
|
||||
if (bountyTriggered != null) {
|
||||
Player opponent = game.getPlayer(bountyTriggered.getControllerId());
|
||||
if (opponent != null) {
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
|
@ -171,11 +173,11 @@ class TobiasBeckettSpendAnyManaEffect extends AsThoughEffectImpl implements AsTh
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -73,6 +73,7 @@ class ViviensInvocationEffect extends OneShotEffect {
|
|||
Zone.LIBRARY,
|
||||
new FilterCreatureCard("creature card to put on the battlefield")
|
||||
);
|
||||
target.setNotTarget(true);
|
||||
if (controller.choose(Outcome.PutCreatureInPlay, cards, target, game)) {
|
||||
Card card = cards.get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.v;
|
||||
|
||||
import mage.MageInt;
|
||||
|
@ -83,12 +82,10 @@ class VizierOfTheMenagerieTopCardCastEffect extends AsThoughEffectImpl {
|
|||
MageObject vizierOfTheMenagerie = game.getObject(source.getSourceId());
|
||||
if (vizierOfTheMenagerie != null
|
||||
&& topCard != null) {
|
||||
if (topCard == card
|
||||
return topCard == card
|
||||
&& topCard.isCreature()
|
||||
&& topCard.getSpellAbility() != null
|
||||
&& topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game)) {
|
||||
return true;
|
||||
}
|
||||
&& topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +117,10 @@ class VizierOfTheMenagerieManaEffect extends AsThoughEffectImpl implements AsTho
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (source.isControlledBy(affectedControllerId)) {
|
||||
MageObject mageObject = game.getObject(objectId);
|
||||
return mageObject != null
|
||||
&& mageObject.isCreature();
|
||||
return mageObject != null && mageObject.isCreature();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.AI.basic;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -8,7 +7,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
||||
|
@ -173,7 +171,7 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
|||
|
||||
/**
|
||||
* Tests that the creature is cast if enough mana is available.
|
||||
*
|
||||
* <p>
|
||||
* Once Ammit Eternal is cast against a computer AI opponent, the AI just
|
||||
* decides to sit there and only play basic lands. I've sat there and decked
|
||||
* it because it just plays lands. It's like it views giving the Ammit
|
||||
|
@ -194,8 +192,11 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ammit Eternal");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerB, "Ammit Eternal", 1);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.abilities.activated;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class LightningStormTest extends CardTestPlayerBase {
|
||||
|
@ -16,10 +14,9 @@ public class LightningStormTest extends CardTestPlayerBase {
|
|||
* So, this just happened to me. My opponent cast Lightning Storm and while
|
||||
* it was on the stack I couldn't use the ability despite having land in
|
||||
* hand which isn't something I've had an issue with before.
|
||||
*
|
||||
* <p>
|
||||
* My opponent had a Leyline of Sanctity in play, so perhaps that was
|
||||
* causing the issue somehow? Does anyone want to try and replicate it?
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void ActivateByBothPlayersTest() {
|
||||
|
@ -31,22 +28,32 @@ public class LightningStormTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Mountain");
|
||||
addCard(Zone.HAND, playerB, "Mountain");
|
||||
|
||||
// A activate
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Storm", playerB);
|
||||
|
||||
// B discard and re-target
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Discard");
|
||||
setChoice(playerB, "playerA");
|
||||
setChoice(playerB, "Mountain");
|
||||
setChoice(playerB, "Yes");
|
||||
addTarget(playerB, playerA);
|
||||
|
||||
// A discard and re-target
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||
setChoice(playerA, "playerB");
|
||||
setChoice(playerA, "Mountain");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Lightning Storm", 1);
|
||||
assertGraveyardCount(playerB, "Mountain", 1);
|
||||
assertGraveyardCount(playerA, "Mountain", 1);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 13);
|
||||
assertLife(playerB, 20 - 3 - 2 - 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
package org.mage.test.cards.asthough;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayTopCardFromLibraryTest extends CardTestPlayerBase {
|
||||
|
||||
/*
|
||||
Bolas's Citadel
|
||||
{3}{B}{B}{B}
|
||||
You may look at the top card of your library any time.
|
||||
You may play the top card of your library. If you cast a spell this way, pay life equal to its converted mana cost rather than pay its mana cost.
|
||||
{T}, Sacrifice ten nonland permanents: Each opponent loses 10 life.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void test_CreaturePlay() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); // 2 CMC
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
assertLife(playerA, 20 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_CreaturePlay2() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Vizier of the Menagerie", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ManaCostmodifications() {
|
||||
//
|
||||
// {5}{B}{B}
|
||||
// You may cast Scourge of Nel Toth from your graveyard by paying {B}{B} and sacrificing two creatures rather than paying its mana cost.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Scourge of Nel Toth", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scourge of Nel Toth");
|
||||
setChoice(playerA, "Kitesail Corsair");
|
||||
setChoice(playerA, "Kitesail Corsair");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Scourge of Nel Toth", 1);
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SplitRightPlay() {
|
||||
// https://github.com/magefree/mage/issues/5912
|
||||
// Bolas's citadel requires you to pay mana instead of life for a split card on top of library.
|
||||
//
|
||||
// Steps to reproduce:
|
||||
//
|
||||
// Bolas's Citadel in play, Revival//Revenge on top of library.
|
||||
// Cast Revenge, choose target
|
||||
// receive prompt to pay 4WB.
|
||||
//
|
||||
// Expected outcome
|
||||
//
|
||||
// No prompt for mana payment, payment of six life instead.
|
||||
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Revival // Revenge", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
|
||||
|
||||
// Double your life total. Target opponent loses half their life, rounded up.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revenge", playerB); // {4}{W}{B} = 6 life
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, (20 - 6) * 2);
|
||||
assertLife(playerB, 20 / 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SplitLeftPlay() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Revival // Revenge", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
|
||||
addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1);
|
||||
|
||||
// Return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revival", "Balduvian Bears"); // {W/B}{W/B} = 2 life
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20 - 2);
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Balduvian Bears", 0);
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.asthough;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SpendOtherManaTest extends CardTestPlayerBase {
|
||||
|
@ -47,7 +45,7 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
|
|||
/**
|
||||
* Tron mana doesn't work with Oath of Nissa. (e.g. can't cast Chandra,
|
||||
* Flamecaller with Urza's Tower, Power Plant, and Mine.)
|
||||
*
|
||||
* <p>
|
||||
* AI don't get the Planeswalker as playable card (probably because of the
|
||||
* as thought effect)
|
||||
*/
|
||||
|
@ -76,7 +74,7 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
|
|||
* I was unable to cast Nissa, Voice of Zendikar using black mana with Oath
|
||||
* of Nissa in play. Pretty sure Oath is working usually, so here were the
|
||||
* conditions in my game:
|
||||
*
|
||||
* <p>
|
||||
* -Cast Dark Petition with spell mastery -Attempt to cast Nissa, Voice of
|
||||
* Zendikar using the triple black mana from Dark Petition
|
||||
*/
|
||||
|
@ -122,20 +120,84 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Hostage Taker"); // {2}{U}{B}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hostage Taker");
|
||||
setChoice(playerA, "Silvercoat Lion");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
|
||||
// red mana must be used as any mana
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R}."); // red mana to pool
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R}."); // red mana to pool
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); // cast it from exile with red mana from pool
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Hostage Taker", 1);
|
||||
assertTappedCount("Mountain", true, 4);
|
||||
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_QuicksilverElemental_Normal() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
// {U}: Quicksilver Elemental gains all activated abilities of target creature until end of turn.
|
||||
// You may spend blue mana as though it were mana of any color to pay the activation costs of Quicksilver Elemental’s abilities.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Quicksilver Elemental"); // Creature {1}{W}
|
||||
// {R}, {T}: Anaba Shaman deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman");
|
||||
|
||||
// gain abilities
|
||||
checkPlayableAbility("must not have", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", false);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}:", "Anaba Shaman");
|
||||
|
||||
// use ability
|
||||
checkPlayableAbility("must have new ability", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", true);
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_QuicksilverElemental_Flicker() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
// {U}: Quicksilver Elemental gains all activated abilities of target creature until end of turn.
|
||||
// You may spend blue mana as though it were mana of any color to pay the activation costs of Quicksilver Elemental’s abilities.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Quicksilver Elemental"); // Creature {1}{W}
|
||||
// {R}, {T}: Anaba Shaman deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman");
|
||||
// Exile target nontoken permanent, then return it to the battlefield under its owner’s control.
|
||||
addCard(Zone.HAND, playerA, "Flicker"); // {1}{W}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
// gain abilities
|
||||
checkPlayableAbility("must not have", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", false);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}:", "Anaba Shaman");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
checkPlayableAbility("must have new ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", true);
|
||||
|
||||
// renew target
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flicker", "Anaba Shaman");
|
||||
|
||||
// use ability
|
||||
checkPlayableAbility("must save ability", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", true);
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Flicker", 1);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.continuous;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
@ -27,16 +25,18 @@ public class PsychicIntrusionTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
|
||||
addCard(Zone.HAND, playerB, "Elspeth, Sun's Champion", 1);
|
||||
addCard(Zone.HAND, playerB, "Elspeth, Sun's Champion", 1); // {4}{W}{W}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Psychic Intrusion", playerB);
|
||||
addTarget(playerA, "Elspeth, Sun's Champion");
|
||||
setChoice(playerA, "Elspeth, Sun's Champion");
|
||||
|
||||
// cast from exile with any mana
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Elspeth, Sun's Champion");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Psychic Intrusion", 1);
|
||||
assertHandCount(playerB, "Elspeth, Sun's Champion", 0);
|
||||
|
|
|
@ -5,7 +5,6 @@ import mage.constants.Zone;
|
|||
import mage.counters.CounterType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -282,7 +281,8 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerB, "Curious Pair");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Psychic Intrusion", playerB);
|
||||
playerA.addChoice("Curious Pair");
|
||||
setChoice(playerA, "Curious Pair");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
|
||||
|
||||
|
@ -462,6 +462,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||
|
||||
showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
|
@ -473,7 +474,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Not yet working correctly.")
|
||||
//@Ignore("Not yet working correctly.")
|
||||
public void testCastTreatsToShareWithWrennAndSixEmblem() {
|
||||
/*
|
||||
* Wrenn and Six {R}{G}
|
||||
|
@ -487,11 +488,16 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Wrenn and Six");
|
||||
addCard(Zone.GRAVEYARD, playerA, "Curious Pair");
|
||||
addCard(Zone.HAND, playerA, "Forest");
|
||||
addCard(Zone.HAND, playerA, "Forest"); // pay for retrace
|
||||
|
||||
addCounters(1, PhaseStep.UPKEEP, playerA, "Wrenn and Six", CounterType.LOYALTY, 5);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-7: You get an emblem");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
// retrace - You may cast this card from your graveyard by discarding a land card as an additional cost to cast it
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||
setChoice(playerA, "Forest");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
@ -523,6 +529,8 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Curious Pair");
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Until your next");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
showAvaileableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA);
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Treats to Share");
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
|
|
@ -32,7 +32,6 @@ public class BolassCitadelTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("This is broken for now.")
|
||||
public void testCastAdventure() {
|
||||
/*
|
||||
* Curious Pair {1}{G}
|
||||
|
@ -44,7 +43,6 @@ public class BolassCitadelTest extends CardTestPlayerBase {
|
|||
* Create a Food token.
|
||||
*/
|
||||
setStrictChooseMode(true);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel");
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||
|
@ -54,7 +52,6 @@ public class BolassCitadelTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertAllCommandsUsed();
|
||||
assertTapped("Forest", false);
|
||||
assertHandCount(playerA, 0);
|
||||
assertPermanentCount(playerA, "Food", 1);
|
||||
assertExileCount(playerA, "Curious Pair", 1);
|
||||
|
|
|
@ -6,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* also tests regenerate and tests that permanents with protection can be
|
||||
* sacrificed
|
||||
*
|
||||
|
@ -46,10 +45,13 @@ public class FiendOfTheShadowsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerB, "Swamp");
|
||||
|
||||
attack(1, playerA, "Fiend of the Shadows");
|
||||
addTarget(playerB, "Swamp");
|
||||
playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Swamp");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 17);
|
||||
|
|
|
@ -2374,20 +2374,25 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UUID getCastSourceIdWithAlternateMana() {
|
||||
public Set<UUID> getCastSourceIdWithAlternateMana() {
|
||||
return computerPlayer.getCastSourceIdWithAlternateMana();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts getCastSourceIdManaCosts() {
|
||||
public Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts() {
|
||||
return computerPlayer.getCastSourceIdManaCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCastSourceIdCosts() {
|
||||
public Map<UUID, Costs<Cost>> getCastSourceIdCosts() {
|
||||
return computerPlayer.getCastSourceIdCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCastSourceIdManaCosts() {
|
||||
computerPlayer.clearCastSourceIdManaCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInPayManaMode() {
|
||||
return computerPlayer.isInPayManaMode();
|
||||
|
|
|
@ -19,8 +19,8 @@ import mage.counters.Counter;
|
|||
import mage.counters.Counters;
|
||||
import mage.designations.Designation;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.Graveyard;
|
||||
import mage.game.Table;
|
||||
|
@ -1208,7 +1208,7 @@ public class PlayerStub implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UUID getCastSourceIdWithAlternateMana() {
|
||||
public Set<UUID> getCastSourceIdWithAlternateMana() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1218,15 +1218,20 @@ public class PlayerStub implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts getCastSourceIdManaCosts() {
|
||||
public Map<UUID, Costs<Cost>> getCastSourceIdCosts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCastSourceIdCosts() {
|
||||
public Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCastSourceIdManaCosts() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermissionToShowHandCards(UUID watcherUserId) {
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
*/
|
||||
public boolean spellCanBeActivatedRegularlyNow(UUID playerId, Game game) {
|
||||
MageObject object = game.getObject(sourceId);
|
||||
if ((Boolean) game.getState().getValue("CastFromExileEnabled" + object.getId()) != null) {
|
||||
if (game.getState().getValue("CastFromExileEnabled" + object.getId()) != null) {
|
||||
return (Boolean) game.getState().getValue("CastFromExileEnabled" + object.getId()); // card like Chandra, Torch of Defiance +1 loyal ability)
|
||||
}
|
||||
return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|
||||
|
@ -98,7 +98,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
if (getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null
|
||||
&& getSourceId().equals(player.getCastSourceIdWithAlternateMana())) {
|
||||
&& player.getCastSourceIdWithAlternateMana().contains(getSourceId())) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
|
@ -8,8 +7,9 @@ import mage.constants.EffectType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements AsThoughEffect {
|
||||
|
@ -29,10 +29,11 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
// affectedControllerId = player to check
|
||||
if (getAsThoughEffectType().equals(AsThoughEffectType.LOOK_AT_FACE_DOWN)) {
|
||||
return applies(objectId, source, playerId, game);
|
||||
} else {
|
||||
return applies(objectId, source, affectedAbility.getControllerId(), game);
|
||||
return applies(objectId, source, playerId, game);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -505,12 +505,19 @@ public class ContinuousEffects implements Serializable {
|
|||
UUID idToCheck;
|
||||
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell
|
||||
&& type != AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& type != AsThoughEffectType.CAST_AS_INSTANT) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
idToCheck = ((AdventureCardSpell) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else {
|
||||
Card card = game.getCard(objectId);
|
||||
if (card != null && card instanceof SplitCardHalf) {
|
||||
if (card instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) card).getParentCard().getId();
|
||||
} else if (card != null && type == AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& card instanceof AdventureCardSpell) {
|
||||
} else if (card instanceof AdventureCardSpell
|
||||
&& type != AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& type != AsThoughEffectType.CAST_AS_INSTANT) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
idToCheck = ((AdventureCardSpell) card).getParentCard().getId();
|
||||
} else {
|
||||
idToCheck = objectId;
|
||||
|
|
|
@ -51,7 +51,7 @@ public class ExileAdventureSpellEffect extends OneShotEffect implements MageSing
|
|||
Spell spell = game.getStack().getSpell(source.getId());
|
||||
if (spell != null && !spell.isCopy()) {
|
||||
Card spellCard = spell.getCard();
|
||||
if (spellCard != null && spellCard instanceof AdventureCardSpell) {
|
||||
if (spellCard instanceof AdventureCardSpell) {
|
||||
UUID exileId = adventureExileId(controller.getId(), game);
|
||||
game.getExile().createZone(exileId, "On an Adventure");
|
||||
AdventureCardSpell adventureSpellCard = (AdventureCardSpell) spellCard;
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
|
@ -15,6 +10,8 @@ import mage.filter.FilterCard;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
|
@ -55,26 +52,18 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card cardOnTop = game.getCard(objectId);
|
||||
Card cardToCheckProperties = cardOnTop;
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
|
||||
// Check each ability individually, as e.g. Adventures and associated creatures may get different results from the filter.
|
||||
if (affectedAbility != null) {
|
||||
MageObject sourceObject = affectedAbility.getSourceObject(game);
|
||||
if (sourceObject != null && sourceObject instanceof Card) {
|
||||
cardToCheckProperties = (Card) sourceObject;
|
||||
}
|
||||
}
|
||||
|
||||
if (cardOnTop != null
|
||||
if (cardToCheck != null
|
||||
&& playerId.equals(source.getControllerId())
|
||||
&& cardOnTop.isOwnedBy(source.getControllerId())
|
||||
&& (!cardToCheckProperties.getManaCost().isEmpty() || cardToCheckProperties.isLand())
|
||||
&& filter.match(cardToCheckProperties, game)) {
|
||||
Player player = game.getPlayer(cardOnTop.getOwnerId());
|
||||
if (player != null && cardOnTop.equals(player.getLibrary().getFromTop(game))) {
|
||||
return true;
|
||||
}
|
||||
&& cardToCheck.isOwnedBy(source.getControllerId())
|
||||
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
|
||||
&& filter.match(cardToCheck, game)) {
|
||||
Player player = game.getPlayer(cardToCheck.getOwnerId());
|
||||
|
||||
UUID needCardID = player.getLibrary().getFromTop(game) == null ? null : player.getLibrary().getFromTop(game).getId();
|
||||
return objectId.equals(needCardID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,7 @@ class HideawayExileEffect extends OneShotEffect {
|
|||
cards.addAll(controller.getLibrary().getTopCards(game, 4));
|
||||
if (!cards.isEmpty()) {
|
||||
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
|
||||
target1.setNotTarget(true);
|
||||
if (controller.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.Iterator;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
@ -23,8 +17,9 @@ import mage.game.stack.Spell;
|
|||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
|
||||
|
@ -91,12 +86,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
|
|||
String times = "";
|
||||
if (additionalCost.isRepeatable()) {
|
||||
int numActivations = additionalCost.getActivateCount();
|
||||
times = Integer.toString(numActivations + 1) + (numActivations == 0 ? " time " : " times ");
|
||||
times = (numActivations + 1) + (numActivations == 0 ? " time " : " times ");
|
||||
}
|
||||
if (additionalCost.canPay(ability, sourceId, controllerId, game)
|
||||
&& player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
|
||||
additionalCost.activate();
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
|
@ -170,7 +165,7 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof ReplicateAbility) {
|
||||
if (((ReplicateAbility) ability).isActivated()) {
|
||||
if (ability.isActivated()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("ReplicateSpell", spell);
|
||||
effect.setValue("ReplicateCount", ((ReplicateAbility) ability).getActivateCount());
|
||||
|
@ -213,7 +208,7 @@ class ReplicateCopyEffect extends OneShotEffect {
|
|||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof ReplicateAbility) {
|
||||
if (((ReplicateAbility) ability).isActivated()) {
|
||||
if (ability.isActivated()) {
|
||||
((ReplicateAbility) ability).resetReplicate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import mage.abilities.Abilities;
|
|||
import mage.abilities.AbilitiesImpl;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -26,7 +27,7 @@ public abstract class AdventureCard extends CardImpl {
|
|||
public AdventureCard(AdventureCard card) {
|
||||
super(card);
|
||||
this.spellCard = card.getSpellCard().copy();
|
||||
((AdventureCardSpell)this.spellCard).setParentCard(this);
|
||||
((AdventureCardSpell) this.spellCard).setParentCard(this);
|
||||
}
|
||||
|
||||
public Card getSpellCard() {
|
||||
|
@ -91,6 +92,11 @@ public abstract class AdventureCard extends CardImpl {
|
|||
return allAbilities;
|
||||
}
|
||||
|
||||
public Abilities<Ability> getSharedAbilities() {
|
||||
// abilities without spellcard
|
||||
return super.getAbilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UUID ownerId) {
|
||||
super.setOwnerId(ownerId);
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author phulin
|
||||
*/
|
||||
public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpell {
|
||||
|
@ -112,7 +111,7 @@ class AdventureCardSpellAbility extends SpellAbility {
|
|||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
ExileZone adventureExileZone = game.getExile().getExileZone(ExileAdventureSpellEffect.adventureExileId(playerId, game));
|
||||
Card spellCard = game.getCard(this.getSourceId());
|
||||
if (spellCard != null && spellCard instanceof AdventureCardSpell) {
|
||||
if (spellCard instanceof AdventureCardSpell) {
|
||||
Card card = ((AdventureCardSpell) spellCard).getParentCard();
|
||||
if (adventureExileZone != null && adventureExileZone.contains(card.getId())) {
|
||||
return ActivationStatus.getFalse();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public enum AsThoughEffectType {
|
||||
|
@ -19,15 +18,29 @@ public enum AsThoughEffectType {
|
|||
BLOCK_FORESTWALK,
|
||||
DAMAGE_NOT_BLOCKED,
|
||||
BE_BLOCKED,
|
||||
PLAY_FROM_NOT_OWN_HAND_ZONE, // do not use dialogs in "applies" method for that type of effect (it calls multiple times)
|
||||
|
||||
// PLAY_FROM_NOT_OWN_HAND_ZONE + CAST_AS_INSTANT:
|
||||
// 1. Do not use dialogs in "applies" method for that type of effect (it calls multiple times and will freeze the game)
|
||||
// 2. All effects in "applies" must checks affectedControllerId.equals(source.getControllerId()) (if not then all players will be able to play it)
|
||||
// 3. Target points to mainCard, but card's characteristics from objectId (split, adventure)
|
||||
// TODO: search all PLAY_FROM_NOT_OWN_HAND_ZONE and CAST_AS_INSTANT effects and add support of mainCard and objectId
|
||||
PLAY_FROM_NOT_OWN_HAND_ZONE,
|
||||
CAST_AS_INSTANT,
|
||||
|
||||
ACTIVATE_AS_INSTANT,
|
||||
DAMAGE,
|
||||
SHROUD,
|
||||
HEXPROOF,
|
||||
PAY_0_ECHO,
|
||||
LOOK_AT_FACE_DOWN,
|
||||
|
||||
// SPEND_OTHER_MANA:
|
||||
// 1. It's uses for mana calcs at any zone, not stack only
|
||||
// 2. Compare zone change counter as "objectZCC <= targetZCC + 1"
|
||||
// 3. Compare zone with original (like exiled) and stack, not stack only
|
||||
// TODO: search all SPEND_ONLY_MANA effects and improve counters compare as SPEND_OTHER_MANA
|
||||
SPEND_OTHER_MANA,
|
||||
|
||||
SPEND_ONLY_MANA,
|
||||
TARGET
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ import mage.counters.Counter;
|
|||
import mage.counters.Counters;
|
||||
import mage.designations.Designation;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.Graveyard;
|
||||
import mage.game.Table;
|
||||
|
@ -851,11 +851,13 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs);
|
||||
|
||||
UUID getCastSourceIdWithAlternateMana();
|
||||
Set<UUID> getCastSourceIdWithAlternateMana();
|
||||
|
||||
ManaCosts<ManaCost> getCastSourceIdManaCosts();
|
||||
Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts();
|
||||
|
||||
Costs<Cost> getCastSourceIdCosts();
|
||||
Map<UUID, Costs<Cost>> getCastSourceIdCosts();
|
||||
|
||||
void clearCastSourceIdManaCosts();
|
||||
|
||||
// permission handling to show hand cards
|
||||
void addPermissionToShowHandCards(UUID watcherUserId);
|
||||
|
|
|
@ -32,8 +32,8 @@ import mage.counters.Counters;
|
|||
import mage.designations.Designation;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.common.FilterCreatureForCombat;
|
||||
import mage.filter.common.FilterCreatureForCombatBlock;
|
||||
|
@ -160,9 +160,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
protected boolean reachedNextTurnAfterLeaving = false;
|
||||
|
||||
// indicates that the spell with the set sourceId can be cast with an alternate mana costs (can also be no mana costs)
|
||||
protected UUID castSourceIdWithAlternateMana;
|
||||
protected ManaCosts<ManaCost> castSourceIdManaCosts;
|
||||
protected Costs<Cost> castSourceIdCosts;
|
||||
// support multiple cards with alternative mana cost
|
||||
protected Set<UUID> castSourceIdWithAlternateMana = new HashSet<>();
|
||||
protected Map<UUID, ManaCosts<ManaCost>> castSourceIdManaCosts = new HashMap<>();
|
||||
protected Map<UUID, Costs<Cost>> castSourceIdCosts = new HashMap<>();
|
||||
|
||||
// indicates that the player is in mana payment phase
|
||||
protected boolean payManaMode = false;
|
||||
|
@ -271,9 +272,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.priorityTimeLeft = player.getPriorityTimeLeft();
|
||||
this.reachedNextTurnAfterLeaving = player.reachedNextTurnAfterLeaving;
|
||||
|
||||
this.castSourceIdWithAlternateMana = player.castSourceIdWithAlternateMana;
|
||||
this.castSourceIdManaCosts = player.castSourceIdManaCosts;
|
||||
this.castSourceIdCosts = player.castSourceIdCosts;
|
||||
this.castSourceIdWithAlternateMana.addAll(player.castSourceIdWithAlternateMana);
|
||||
this.castSourceIdManaCosts.putAll(player.castSourceIdManaCosts);
|
||||
this.castSourceIdCosts.putAll(player.castSourceIdCosts);
|
||||
|
||||
this.payManaMode = player.payManaMode;
|
||||
this.phyrexianColors = player.phyrexianColors.copy();
|
||||
|
||||
|
@ -340,9 +342,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.turnControllers.clear();
|
||||
this.turnControllers.addAll(player.getTurnControllers());
|
||||
this.reachedNextTurnAfterLeaving = player.hasReachedNextTurnAfterLeaving();
|
||||
this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana();
|
||||
this.castSourceIdManaCosts = player.getCastSourceIdManaCosts();
|
||||
this.castSourceIdCosts = player.getCastSourceIdCosts();
|
||||
|
||||
this.clearCastSourceIdManaCosts();
|
||||
this.castSourceIdWithAlternateMana.addAll(player.getCastSourceIdWithAlternateMana());
|
||||
this.castSourceIdManaCosts.putAll(player.getCastSourceIdManaCosts());
|
||||
this.castSourceIdCosts.putAll(player.getCastSourceIdCosts());
|
||||
|
||||
this.phyrexianColors = player.getPhyrexianColors().copy();
|
||||
|
||||
this.designations.clear();
|
||||
|
@ -417,9 +422,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.setLife(game.getLife(), game, (UUID) null);
|
||||
this.setReachedNextTurnAfterLeaving(false);
|
||||
|
||||
this.castSourceIdWithAlternateMana = null;
|
||||
this.castSourceIdManaCosts = null;
|
||||
this.castSourceIdCosts = null;
|
||||
this.clearCastSourceIdManaCosts();
|
||||
|
||||
this.getManaPool().init(); // needed to remove mana that not empties on step change from previous game if left
|
||||
this.phyrexianColors = new FilterMana();
|
||||
|
||||
|
@ -444,9 +448,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.canPlayCardsFromGraveyard = false;
|
||||
this.topCardRevealed = false;
|
||||
this.alternativeSourceCosts.clear();
|
||||
this.castSourceIdWithAlternateMana = null;
|
||||
this.castSourceIdManaCosts = null;
|
||||
this.castSourceIdCosts = null;
|
||||
this.clearCastSourceIdManaCosts();
|
||||
this.getManaPool().clearEmptyManaPoolRules();
|
||||
this.phyrexianColors = new FilterMana();
|
||||
}
|
||||
|
@ -1061,26 +1063,33 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs) {
|
||||
castSourceIdWithAlternateMana = sourceId;
|
||||
castSourceIdManaCosts = manaCosts;
|
||||
castSourceIdCosts = costs;
|
||||
castSourceIdWithAlternateMana.add(sourceId);
|
||||
castSourceIdManaCosts.put(sourceId, manaCosts);
|
||||
castSourceIdCosts.put(sourceId, costs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getCastSourceIdWithAlternateMana() {
|
||||
public Set<UUID> getCastSourceIdWithAlternateMana() {
|
||||
return castSourceIdWithAlternateMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCastSourceIdCosts() {
|
||||
public Map<UUID, Costs<Cost>> getCastSourceIdCosts() {
|
||||
return castSourceIdCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts getCastSourceIdManaCosts() {
|
||||
public Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts() {
|
||||
return castSourceIdManaCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCastSourceIdManaCosts() {
|
||||
this.castSourceIdCosts.clear();
|
||||
this.castSourceIdManaCosts.clear();
|
||||
this.castSourceIdWithAlternateMana.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPayManaMode(boolean payManaMode) {
|
||||
this.payManaMode = payManaMode;
|
||||
|
@ -1144,11 +1153,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
// Update the zcc to the stack
|
||||
ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId()));
|
||||
|
||||
// ALTERNATIVE COST from dynamic effects
|
||||
// some effects set sourceId to cast without paying mana costs or other costs
|
||||
if (ability.getSourceId().equals(getCastSourceIdWithAlternateMana())) {
|
||||
if (getCastSourceIdWithAlternateMana().contains(ability.getSourceId())) {
|
||||
Ability spellAbility = spell.getSpellAbility();
|
||||
ManaCosts alternateCosts = getCastSourceIdManaCosts();
|
||||
Costs<Cost> costs = getCastSourceIdCosts();
|
||||
ManaCosts alternateCosts = getCastSourceIdManaCosts().get(ability.getSourceId());
|
||||
Costs<Cost> costs = getCastSourceIdCosts().get(ability.getSourceId());
|
||||
if (alternateCosts == null) {
|
||||
noMana = true;
|
||||
} else {
|
||||
|
@ -1162,7 +1173,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
spellAbility.getCosts().addAll(costs);
|
||||
}
|
||||
}
|
||||
setCastSourceIdWithAlternateMana(null, null, null);
|
||||
clearCastSourceIdManaCosts(); // TODO: test multiple alternative cost for different cards as same time
|
||||
|
||||
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL,
|
||||
spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
|
||||
game.fireEvent(event);
|
||||
|
@ -3046,7 +3058,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
game.getContinuousEffects().costModification(copy, game);
|
||||
}
|
||||
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
Card card = game.getCard(copy.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability ability0 : card.getAbilities()) {
|
||||
if (ability0 instanceof AdjustingSourceCosts) {
|
||||
|
@ -3072,10 +3084,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (available == null) {
|
||||
return true;
|
||||
}
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(),
|
||||
AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(copy.getSourceId(),
|
||||
AsThoughEffectType.SPEND_OTHER_MANA, copy, copy.getControllerId(), game);
|
||||
for (Mana mana : abilityOptions) {
|
||||
for (Mana avail : available) {
|
||||
// TODO: SPEND_OTHER_MANA effects with getAsThoughManaType can change mana type to pay,
|
||||
// but that code processing it as any color, need to test and fix another use cases
|
||||
// (example: Sunglasses of Urza - may spend white mana as though it were red mana)
|
||||
|
||||
//
|
||||
// add tests for non any color like Sunglasses of Urza
|
||||
if (permittingObject != null && mana.count() <= avail.count()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -3087,14 +3105,35 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
// ALTERNATIVE COST from source card (AlternativeCostSourceAbility)
|
||||
for (Ability objectAbility : sourceObject.getAbilities()) {
|
||||
if (objectAbility instanceof AlternativeCostSourceAbility) {
|
||||
if (objectAbility.getCosts().canPay(ability, ability.getSourceId(), playerId, game)) {
|
||||
if (objectAbility.getCosts().canPay(copy, copy.getSourceId(), playerId, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return canPlayCardByAlternateCost(card, available, ability, game);
|
||||
|
||||
// ALTERNATIVE COST FROM dynamic effects
|
||||
if (getCastSourceIdWithAlternateMana().contains(copy.getSourceId())) {
|
||||
ManaCosts alternateCosts = getCastSourceIdManaCosts().get(copy.getSourceId());
|
||||
Costs<Cost> costs = getCastSourceIdCosts().get(copy.getSourceId());
|
||||
|
||||
boolean canPutToPlay = true;
|
||||
if (alternateCosts != null && !alternateCosts.canPay(copy, copy.getSourceId(), playerId, game)) {
|
||||
canPutToPlay = false;
|
||||
}
|
||||
if (costs != null && !costs.canPay(copy, copy.getSourceId(), playerId, game)) {
|
||||
canPutToPlay = false;
|
||||
}
|
||||
|
||||
if (canPutToPlay) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ALTERNATIVE COST from source card (any AlternativeSourceCosts)
|
||||
return canPlayCardByAlternateCost(card, available, copy, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3215,36 +3254,86 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
private List<Ability> cardPlayableAbilities(Game game, Card card, boolean setControllerId) {
|
||||
List<Ability> playable = new ArrayList();
|
||||
if (card != null) {
|
||||
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
|
||||
if (!ability.canActivate(playerId, game).canActivate()) {
|
||||
continue;
|
||||
private void getPlayableFromNonHandCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<Ability> output) {
|
||||
if (fromZone == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID savedControllerId = null;
|
||||
if (setControllerId) {
|
||||
// For when owner != caster, e.g. with Psychic Intrusion and similar effects.
|
||||
savedControllerId = getId();
|
||||
ability.setControllerId(getId());
|
||||
// BASIC abilities
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(), availableMana, output);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
// adventure must use different card characteristics for different spells (main or adventure)
|
||||
AdventureCard adventureCard = (AdventureCard) card;
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(), availableMana, output);
|
||||
} else {
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, card, card.getAbilities(), availableMana, output);
|
||||
}
|
||||
if (ability instanceof SpellAbility
|
||||
&& null != game.getContinuousEffects().asThough(card.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, getId(), game)) {
|
||||
playable.add(ability);
|
||||
} else if (ability instanceof PlayLandAbility
|
||||
&& null != game.getContinuousEffects().asThough(card.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), getId(), game)) {
|
||||
playable.add(ability);
|
||||
|
||||
// DYNAMIC ADDED abilities
|
||||
if (fromZone != Zone.ALL) { // TODO: test revealed cards with dynamic added abilities
|
||||
// Other activated abilities (added dynamic by effects)
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable;
|
||||
if (card instanceof AdventureCard) {
|
||||
// adventure cards (contains two different cards: main and adventure spell)
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(((AdventureCard) card).getSpellCard(), fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
} else {
|
||||
// all other cards (TODO: check split cards with dynamic added abilities)
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
}
|
||||
if (setControllerId) {
|
||||
}
|
||||
}
|
||||
|
||||
private void getPlayableFromNonHandCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
|
||||
|
||||
// check "can play from hand" condition as original controller (effects checks affected controller with source controller)
|
||||
// TODO: remove card.getSpellAbility() ?
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game);
|
||||
boolean canActivateAsHandZone = permittingObject != null
|
||||
|| (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
|
||||
|
||||
// check "can play" condition as affected controller
|
||||
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
|
||||
UUID savedControllerId = ability.getControllerId();
|
||||
ability.setControllerId(this.getId());
|
||||
try {
|
||||
boolean possibleToPlay = false;
|
||||
|
||||
// spell/hand abilities (play from all zones)
|
||||
// need permitingObject or canPlayCardsFromGraveyard
|
||||
if (canActivateAsHandZone
|
||||
&& ability.getZone().match(Zone.HAND)
|
||||
&& (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) {
|
||||
possibleToPlay = true;
|
||||
}
|
||||
|
||||
// zone's abilities (play from specific zone)
|
||||
// no need in permitingObject
|
||||
if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) {
|
||||
possibleToPlay = true;
|
||||
}
|
||||
|
||||
if (possibleToPlay && canPlay(ability, availableMana, card, game)) {
|
||||
output.add(ability);
|
||||
}
|
||||
} finally {
|
||||
ability.setControllerId(savedControllerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return playable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
||||
|
@ -3262,11 +3351,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
boolean fromAll = fromZone.equals(Zone.ALL);
|
||||
Collection<Card> cards;
|
||||
|
||||
if (hidden && (fromAll || fromZone == Zone.HAND)) {
|
||||
cards = hideDuplicatedAbilities ? hand.getUniqueCards(game) : hand.getCards(game);
|
||||
for (Card card : cards) {
|
||||
for (Card card : hand.getCards(game)) {
|
||||
for (Ability ability : card.getAbilities(game)) { // gets this activated ability from hand? (Morph?)
|
||||
if (ability.getZone().match(Zone.HAND)) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
|
@ -3297,37 +3384,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
if (fromAll || fromZone == Zone.GRAVEYARD) {
|
||||
cards = hideDuplicatedAbilities ? graveyard.getUniqueCards(game) : graveyard.getCards(game);
|
||||
for (Card card : cards) {
|
||||
// Handle split cards in graveyard to support Aftermath
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
getPlayableFromGraveyardCard(game, splitCard.getLeftHalfCard(),
|
||||
splitCard.getLeftHalfCard().getAbilities(), availableMana, playable);
|
||||
getPlayableFromGraveyardCard(game, splitCard.getRightHalfCard(),
|
||||
splitCard.getRightHalfCard().getAbilities(), availableMana, playable);
|
||||
getPlayableFromGraveyardCard(game, splitCard, splitCard.getSharedAbilities(),
|
||||
availableMana, playable);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
AdventureCard adventureCard = (AdventureCard) card;
|
||||
getPlayableFromGraveyardCard(game, adventureCard.getSpellCard(),
|
||||
adventureCard.getSpellCard().getAbilities(), availableMana, playable);
|
||||
getPlayableFromGraveyardCard(game, adventureCard, adventureCard.getAbilities(), availableMana, playable);
|
||||
} else {
|
||||
getPlayableFromGraveyardCard(game, card, card.getAbilities(), availableMana, playable);
|
||||
}
|
||||
|
||||
// Other activated abilities
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, Zone.GRAVEYARD, game, useable);
|
||||
playable.addAll(useable.values());
|
||||
for (Card card : graveyard.getCards(game)) {
|
||||
getPlayableFromNonHandCardAll(game, Zone.GRAVEYARD, card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
|
||||
if (fromAll || fromZone == Zone.EXILED) {
|
||||
for (ExileZone exile : game.getExile().getExileZones()) {
|
||||
for (Card card : exile.getCards(game)) {
|
||||
playable.addAll(cardPlayableAbilities(game, card, true));
|
||||
getPlayableFromNonHandCardAll(game, Zone.EXILED, card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3336,7 +3401,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (fromAll) {
|
||||
for (Cards revealedCards : game.getState().getRevealed().values()) {
|
||||
for (Card card : revealedCards.getCards(game)) {
|
||||
playable.addAll(cardPlayableAbilities(game, card, false));
|
||||
// revealed cards can be from any zones
|
||||
getPlayableFromNonHandCardAll(game, game.getState().getZone(card.getId()), card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3348,7 +3414,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (player != null) {
|
||||
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
|
||||
Card card = player.getLibrary().getFromTop(game);
|
||||
playable.addAll(cardPlayableAbilities(game, card, false));
|
||||
getPlayableFromNonHandCardAll(game, Zone.LIBRARY, card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3958,7 +4024,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// identify cards from one owner
|
||||
Cards cards = new CardsImpl();
|
||||
UUID ownerId = null;
|
||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
|
||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
|
||||
Card card = it.next();
|
||||
if (cards.isEmpty()) {
|
||||
ownerId = card.getOwnerId();
|
||||
|
|
Loading…
Add table
Reference in a new issue