Merge branch 'master' into changeToFinal

This commit is contained in:
LevelX2 2018-05-29 00:06:56 +02:00 committed by GitHub
commit e7a5fd9979
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
573 changed files with 11915 additions and 2778 deletions

View file

@ -157,7 +157,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
return stream()
.filter(ability -> ability instanceof ActivatedManaAbilityImpl)
.filter(ability -> ability.getZone().match(zone))
.filter(ability -> (((ActivatedManaAbilityImpl) ability).canActivate(ability.getControllerId(), game)))
.filter(ability -> (((ActivatedManaAbilityImpl) ability).canActivate(ability.getControllerId(), game).canActivate()))
.map(ability -> (ActivatedManaAbilityImpl) ability)
.collect(Collectors.toCollection(AbilitiesImpl::new));

View file

@ -28,6 +28,7 @@
package mage.abilities;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.mana.ManaOptions;
import mage.constants.TargetController;
import mage.game.Game;
@ -38,9 +39,36 @@ import mage.game.Game;
*/
public interface ActivatedAbility extends Ability {
boolean canActivate(UUID playerId, Game game);
final public class ActivationStatus {
public void setMayActivate(TargetController mayActivate);
private final boolean canActivate;
private final MageObjectReference permittingObject;
public ActivationStatus(boolean canActivate, MageObjectReference permittingObject) {
this.canActivate = canActivate;
this.permittingObject = permittingObject;
}
public boolean canActivate() {
return canActivate;
}
public MageObjectReference getPermittingObject() {
return permittingObject;
}
public static ActivationStatus getFalse() {
return new ActivationStatus(false, null);
}
public static ActivationStatus getTrue() {
return new ActivationStatus(true, null);
}
}
ActivationStatus canActivate(UUID playerId, Game game); // has to return a reference to the permitting ability/source
void setMayActivate(TargetController mayActivate);
/**
* Returns the minimal possible cost for what the ability can be activated

View file

@ -29,6 +29,7 @@ package mage.abilities;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.condition.Condition;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
@ -177,10 +178,10 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
public abstract ActivatedAbilityImpl copy();
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
//20091005 - 602.2
if (!(hasMoreActivationsThisTurn(game) && (condition == null || condition.apply(game, this)))) {
return false;
return ActivationStatus.getFalse();
}
switch (mayActivate) {
case ANY:
@ -188,24 +189,28 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
case NOT_YOU:
if (controlsAbility(playerId, game)) {
return false;
return ActivationStatus.getFalse();
}
break;
case TEAM:
if (game.getPlayer(controllerId).hasOpponent(playerId, game)) {
return ActivationStatus.getFalse();
}
break;
case OPPONENT:
if (!game.getPlayer(controllerId).hasOpponent(playerId, game)) {
return false;
return ActivationStatus.getFalse();
}
break;
case OWNER:
Permanent permanent = game.getPermanent(getSourceId());
if (!permanent.getOwnerId().equals(playerId)) {
return false;
return ActivationStatus.getFalse();
}
break;
case YOU:
if (!controlsAbility(playerId, game)) {
return false;
return ActivationStatus.getFalse();
}
break;
case CONTROLLER_ATTACHED_TO:
@ -216,17 +221,17 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
break;
}
}
return false;
return ActivationStatus.getFalse();
}
//20091005 - 602.5d/602.5e
if (timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)
|| null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
MageObjectReference permittingObject = game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game);
if (timing == TimingRule.INSTANT || game.canPlaySorcery(playerId) || null != permittingObject) {
if (costs.canPay(this, sourceId, playerId, game) && canChooseTarget(game)) {
this.activatorId = playerId;
return true;
return new ActivationStatus(true, permittingObject);
}
}
return false;
return ActivationStatus.getFalse();
}
@Override
@ -307,10 +312,12 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
return false;
}
@Override
public void setMaxActivationsPerTurn(int maxActivationsPerTurn) {
this.maxActivationsPerTurn = maxActivationsPerTurn;
}
@Override
public int getMaxActivationsPerTurn(Game game) {
return maxActivationsPerTurn;
}

View file

@ -28,6 +28,7 @@
package mage.abilities;
import java.util.UUID;
import mage.MageObjectReference;
import mage.constants.AbilityType;
import mage.constants.AsThoughEffectType;
import mage.constants.Zone;
@ -50,13 +51,13 @@ public class PlayLandAbility extends ActivatedAbilityImpl {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
if (!controlsAbility(playerId, game)
&& null == game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, playerId, game)) {
return false;
public ActivationStatus canActivate(UUID playerId, Game game) {
MageObjectReference permittingObject = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, playerId, game);
if (!controlsAbility(playerId, game) && null == permittingObject) {
return ActivationStatus.getFalse();
}
//20091005 - 114.2a
return game.getActivePlayerId().equals(playerId) && game.getPlayer(playerId).canPlayLand() && game.canPlaySorcery(playerId);
return new ActivationStatus(game.getActivePlayerId().equals(playerId) && game.getPlayer(playerId).canPlayLand() && game.canPlaySorcery(playerId), permittingObject);
}
@Override

View file

@ -29,6 +29,7 @@ package mage.abilities;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.costs.Cost;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost;
@ -91,48 +92,49 @@ public class SpellAbility extends ActivatedAbilityImpl {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
if (null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|| this.spellCanBeActivatedRegularlyNow(playerId, game)) {
if (spellAbilityType == SpellAbilityType.SPLIT || spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) {
return false;
return ActivationStatus.getFalse();
}
// fix for Gitaxian Probe and casting opponent's spells
if (null == game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, playerId, game)) {
MageObjectReference permittingSource = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, playerId, game);
if (permittingSource == null) {
Card card = game.getCard(sourceId);
if (!(card != null && card.getOwnerId().equals(playerId))) {
return false;
return ActivationStatus.getFalse();
}
}
// Check if rule modifying events prevent to cast the spell in check playable mode
if (this.isCheckPlayableMode()) {
if (game.getContinuousEffects().preventedByRuleModification(
GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, this.getId(), this.getSourceId(), playerId), this, game, true)) {
return false;
return ActivationStatus.getFalse();
}
}
// Alternate spell abilities (Flashback, Overload) can't be cast with no mana to pay option
if (getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
Player player = game.getPlayer(playerId);
if (player != null && getSourceId().equals(player.getCastSourceIdWithAlternateMana())) {
return false;
return ActivationStatus.getFalse();
}
}
if (costs.canPay(this, sourceId, controllerId, game)) {
if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
SplitCard splitCard = (SplitCard) game.getCard(getSourceId());
if (splitCard != null) {
return (splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game)
&& splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game));
return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game)
&& splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null);
}
return false;
return ActivationStatus.getFalse();
} else {
return canChooseTarget(game);
return new ActivationStatus(canChooseTarget(game), permittingSource);
}
}
}
return false;
return ActivationStatus.getFalse();
}
@Override

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,18 +20,17 @@
* 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.common;
import java.util.UUID;
import mage.constants.Zone;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.effects.common.PassEffect;
import mage.constants.Zone;
import mage.game.Game;
/**
@ -55,8 +54,8 @@ public class PassAbility extends ActivatedAbilityImpl {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
return true;
public ActivationStatus canActivate(UUID playerId, Game game) {
return ActivationStatus.getTrue();
}
@Override

View file

@ -0,0 +1,69 @@
/*
* 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.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author TheElk801
*/
public class WinsCoinFlipTriggeredAbility extends TriggeredAbilityImpl {
public WinsCoinFlipTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
}
public WinsCoinFlipTriggeredAbility(final WinsCoinFlipTriggeredAbility ability) {
super(ability);
}
@Override
public WinsCoinFlipTriggeredAbility copy() {
return new WinsCoinFlipTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COIN_FLIPPED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getFlag();
}
@Override
public String getRule() {
return "Whenever a player wins a coin flip, " + super.getRule();
}
}

View file

@ -63,7 +63,7 @@ public class TapSourceCost extends CostImpl {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null) {
return !permanent.isTapped()
&& (permanent.canTap() || null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_HASTE, controllerId, game));
&& (permanent.canTap() || null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_HASTE, ability, controllerId, game));
}
return false;
}

View file

@ -62,7 +62,7 @@ public class UntapSourceCost extends CostImpl {
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null) {
return permanent.isTapped() && (permanent.canTap() || null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_HASTE, controllerId, game));
return permanent.isTapped() && (permanent.canTap() || null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_HASTE, ability, controllerId, game));
}
return false;
}

View file

@ -82,9 +82,9 @@ public class ConditionalGainActivatedAbility extends ActivatedAbilityImpl {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
if (!condition.apply(game, this)) {
return false;
return ActivationStatus.getFalse();
}
return super.canActivate(playerId, game);
}

View file

@ -45,7 +45,7 @@ public class ManaTypeInManaPoolCount implements DynamicValue {
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder("unspent ");
switch (manaType) {
case BLACK:
sb.append("black");
@ -66,7 +66,7 @@ public class ManaTypeInManaPoolCount implements DynamicValue {
sb.append("colorless");
break;
}
sb.append(" mana in your mana pool");
sb.append(" mana you have");
return sb.toString();
}
}

View file

@ -51,6 +51,8 @@ public interface ContinuousEffect extends Effect {
void discard();
void setDuration(Duration duration);
Duration getDuration();
long getOrder();

View file

@ -112,6 +112,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
this.characterDefining = effect.characterDefining;
}
@Override
public void setDuration(Duration duration) {
this.duration = duration;
}
@Override
public Duration getDuration() {
return duration;

View file

@ -32,6 +32,7 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.*;
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
@ -347,7 +348,8 @@ public class ContinuousEffects implements Serializable {
}
// boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
//get all applicable transient Replacement effects
for (ReplacementEffect effect : replacementEffects) {
for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
ReplacementEffect effect = iterator.next();
if (!effect.checksEventType(event, game)) {
continue;
}
@ -378,7 +380,8 @@ public class ContinuousEffects implements Serializable {
replaceEffects.put(effect, applicableAbilities);
}
}
for (PreventionEffect effect : preventionEffects) {
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
PreventionEffect effect = iterator.next();
if (!effect.checksEventType(event, game)) {
continue;
}
@ -500,19 +503,6 @@ public class ContinuousEffects implements Serializable {
return spliceEffects;
}
/**
*
* @param objectId
* @param type
* @param controllerId
* @param game
* @return sourceId of the permitting effect if any exists otherwise returns
* null
*/
public UUID asThough(UUID objectId, AsThoughEffectType type, UUID controllerId, Game game) {
return asThough(objectId, type, null, controllerId, game);
}
/**
*
* @param objectId
@ -523,17 +513,17 @@ public class ContinuousEffects implements Serializable {
* @return sourceId of the permitting effect if any exists otherwise returns
* null
*/
public UUID asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
public MageObjectReference asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
for (AsThoughEffect effect : asThoughEffectsList) {
Set<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
for (Ability ability : abilities) {
if (affectedAbility == null) {
if (effect.applies(objectId, ability, controllerId, game)) {
return ability.getSourceId();
return new MageObjectReference(ability.getSourceObject(game), game);
}
} else if (effect.applies(objectId, affectedAbility, ability, game)) {
return ability.getSourceId();
return new MageObjectReference(ability.getSourceObject(game), game);
}
}
}
@ -849,6 +839,9 @@ public class ContinuousEffects implements Serializable {
if (rEffect != null) {
event.getAppliedEffects().add(rEffect.getId());
caught = rEffect.replaceEvent(event, rAbility, game);
if (Duration.OneUse.equals(rEffect.getDuration())) {
rEffect.discard();
}
}
if (caught) { // Event was completely replaced -> stop applying effects to it
break;
@ -932,8 +925,7 @@ public class ContinuousEffects implements Serializable {
System.out.println(game.getTurn() + ", " + game.getPhase() + ": " + "need apply " + layer.stream()
.map((eff) -> {return eff.getClass().getName().replaceAll(".+\\.(.+)", "$1");})
.collect(Collectors.joining(", ")));
*/
*/
for (ContinuousEffect effect : layer) {
if (activeLayerEffects.contains(effect) && !appliedEffects.contains(effect.getId())) { // Effect does still exist and was not applied yet
Set<UUID> dependentTo = effect.isDependentTo(layer);

View file

@ -28,6 +28,7 @@
package mage.abilities.effects.common;
import java.util.Set;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
@ -88,7 +89,7 @@ public class CastCardFromOutsideTheGameEffect extends OneShotEffect {
}
return false;
}
Cards filteredCards = new CardsImpl();
for (Card card : filtered) {
filteredCards.add(card.getId());
@ -98,7 +99,7 @@ public class CastCardFromOutsideTheGameEffect extends OneShotEffect {
if (player.choose(Outcome.Benefit, filteredCards, target, game)) {
Card card = player.getSideboard().get(target.getFirstTarget(), game);
if (card != null) {
player.cast(card.getSpellAbility(), game, true);
player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
}
}
}
@ -106,4 +107,4 @@ public class CastCardFromOutsideTheGameEffect extends OneShotEffect {
return true;
}
}
}

View file

@ -28,6 +28,7 @@
package mage.abilities.effects.common;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
@ -151,7 +152,7 @@ class CipherStoreEffect extends OneShotEffect {
}
ability.getEffects().remove(cipherEffect);
if (ability instanceof SpellAbility) {
controller.cast(ability, game, true);
controller.cast(ability, game, true, new MageObjectReference(source.getSourceObject(game), game));
}
}

View file

@ -43,20 +43,27 @@ import mage.players.Player;
*/
public class CopyTargetSpellEffect extends OneShotEffect {
private boolean useLKI = false;
private final boolean useController;
private final boolean useLKI;
public CopyTargetSpellEffect() {
super(Outcome.Copy);
this(false);
}
public CopyTargetSpellEffect(boolean useLKI) {
this(false, useLKI);
}
public CopyTargetSpellEffect(boolean useController, boolean useLKI) {
super(Outcome.Copy);
this.useController = useController;
this.useLKI = useLKI;
}
public CopyTargetSpellEffect(final CopyTargetSpellEffect effect) {
super(effect);
this.useLKI = effect.useLKI;
this.useController = effect.useController;
}
@Override
@ -71,7 +78,7 @@ public class CopyTargetSpellEffect extends OneShotEffect {
spell = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK);
}
if (spell != null) {
StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true);
StackObject newStackObject = spell.createCopyOnStack(game, source, useController ? spell.getControllerId() : source.getControllerId(), true);
Player player = game.getPlayer(source.getControllerId());
if (player != null && newStackObject != null && newStackObject instanceof Spell) {
String activateMessage = ((Spell) newStackObject).getActivatedMessage(game);

View file

@ -81,19 +81,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
}
public CreateTokenCopyTargetEffect() {
super(Outcome.PutCreatureInPlay);
this.playerId = null;
this.additionalCardType = null;
this.addedTokenPermanents = new ArrayList<>();
this.number = 1;
this.additionalSubType = null;
this.onlySubType = null;
this.attackedPlayer = null;
this.tokenPower = Integer.MIN_VALUE;
this.tokenToughness = Integer.MIN_VALUE;
this.gainsFlying = false;
this.becomesArtifact = false;
this.color = null;
this((UUID) null);
}
public CreateTokenCopyTargetEffect(UUID playerId) {

View file

@ -115,7 +115,7 @@ public class DrawCardTargetEffect extends OneShotEffect {
} else {
target = mode.getTargets().get(0);
}
sb.append("Target ").append(target.getTargetName());
sb.append("target ").append(target.getTargetName());
} else {
sb.append("that player");
}

View file

@ -29,68 +29,40 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterLandCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCardInHand;
/**
*
* @author LevelX2
* @author TheElk801
*/
public class PutLandFromHandOntoBattlefieldEffect extends OneShotEffect {
public class FlipUntilLoseEffect extends OneShotEffect {
private FilterCard filter;
private boolean tapped;
public PutLandFromHandOntoBattlefieldEffect() {
this(false);
public FlipUntilLoseEffect() {
super(Outcome.Benefit);
this.staticText = "flip a coin until you lose a flip";
}
public PutLandFromHandOntoBattlefieldEffect(boolean tapped) {
this(tapped, new FilterLandCard());
}
public PutLandFromHandOntoBattlefieldEffect(boolean tapped, FilterCard filter) {
super(Outcome.PutLandInPlay);
this.tapped = tapped;
this.filter = filter;
staticText = "you may put a " + filter.getMessage() + " from your hand onto the battlefield" + (tapped ? " tapped" : "");
}
public PutLandFromHandOntoBattlefieldEffect(final PutLandFromHandOntoBattlefieldEffect effect) {
public FlipUntilLoseEffect(final FlipUntilLoseEffect effect) {
super(effect);
this.tapped = effect.tapped;
this.filter = effect.filter;
}
@Override
public FlipUntilLoseEffect copy() {
return new FlipUntilLoseEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Target target = new TargetCardInHand(filter);
if (target.canChoose(source.getSourceId(), source.getControllerId(), game)
&& controller.chooseUse(outcome, "Put land onto battlefield?", source, game)
&& controller.choose(outcome, target, source.getSourceId(), game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
}
}
return true;
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
while (true) {
if (!player.flipCoin(game)) {
return true;
}
}
return false;
}
@Override
public PutLandFromHandOntoBattlefieldEffect copy() {
return new PutLandFromHandOntoBattlefieldEffect(this);
}
}

View file

@ -27,6 +27,8 @@
*/
package mage.abilities.effects.common;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
@ -36,8 +38,6 @@ import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author LevelX2
*
@ -82,7 +82,7 @@ public class HideawayPlayEffect extends OneShotEffect {
}
}
if (!controller.playCard(card, game, true, true)) {
if (!controller.playCard(card, game, true, true, new MageObjectReference(source.getSourceObject(game), game))) {
if (card.getZoneChangeCounter(game) == zcc) {
card.setFaceDown(true, game);
}

View file

@ -24,16 +24,16 @@
* 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;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
@ -62,7 +62,7 @@ public class PlayTargetWithoutPayingManaEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
Card target = (Card) game.getObject(source.getFirstTarget());
if (controller != null && target != null) {
return controller.cast(target.getSpellAbility(), game, true);
return controller.cast(target.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
}
return false;
}
@ -74,8 +74,7 @@ public class PlayTargetWithoutPayingManaEffect extends OneShotEffect {
Target target = mode.getTargets().get(0);
if (mode.getTargets().get(0).getZone() == Zone.HAND) {
sb.append("you may put ").append(target.getTargetName()).append(" from your hand onto the battlefield");
}
else {
} else {
sb.append("you may cast target ").append(target.getTargetName()).append(" without paying its mana cost");
}
}

View file

@ -15,34 +15,41 @@ import mage.target.common.TargetCardInHand;
/**
* @author magenoxx_at_gmail.com
*/
public class PutPermanentOnBattlefieldEffect extends OneShotEffect {
public class PutCardFromHandOntoBattlefieldEffect extends OneShotEffect {
private final FilterCard filter;
private final boolean useTargetController;
private final boolean tapped;
public PutPermanentOnBattlefieldEffect() {
public PutCardFromHandOntoBattlefieldEffect() {
this(new FilterPermanentCard("a permanent card"), false);
}
public PutPermanentOnBattlefieldEffect(FilterCard filter) {
public PutCardFromHandOntoBattlefieldEffect(FilterCard filter) {
this(filter, false);
}
public PutPermanentOnBattlefieldEffect(FilterCard filter, boolean useTargetController) {
public PutCardFromHandOntoBattlefieldEffect(FilterCard filter, boolean useTargetController) {
this(filter, useTargetController, false);
}
public PutCardFromHandOntoBattlefieldEffect(FilterCard filter, boolean useTargetController, boolean tapped) {
super(Outcome.PutCardInPlay);
this.filter = filter;
this.useTargetController = useTargetController;
this.tapped = tapped;
}
public PutPermanentOnBattlefieldEffect(final PutPermanentOnBattlefieldEffect effect) {
public PutCardFromHandOntoBattlefieldEffect(final PutCardFromHandOntoBattlefieldEffect effect) {
super(effect);
this.filter = effect.filter.copy();
this.useTargetController = effect.useTargetController;
this.tapped = effect.tapped;
}
@Override
public PutPermanentOnBattlefieldEffect copy() {
return new PutPermanentOnBattlefieldEffect(this);
public PutCardFromHandOntoBattlefieldEffect copy() {
return new PutCardFromHandOntoBattlefieldEffect(this);
}
@Override
@ -58,10 +65,10 @@ public class PutPermanentOnBattlefieldEffect extends OneShotEffect {
}
if (player.chooseUse(Outcome.PutCardInPlay, "Put " + filter.getMessage() + " from your hand onto the battlefield?", source, game)) {
TargetCardInHand target = new TargetCardInHand(filter);
if (player.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) {
if (player.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
return player.moveCards(card, Zone.BATTLEFIELD, source, game);
return player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
}
}
}

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,22 +20,21 @@
* 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;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
@ -59,12 +58,15 @@ public class ReturnToBattlefieldUnderOwnerControlAttachedEffect extends OneShotE
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Object object = getValue("attachedTo");
if (object != null && object instanceof Permanent) {
Card card = game.getCard(((Permanent) object).getId());
if (card != null) {
Zone currentZone = game.getState().getZone(card.getId());
if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), card.getOwnerId())) {
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null)) {
return true;
}
}

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,22 +20,22 @@
* 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;
import java.util.UUID;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
@ -60,11 +60,16 @@ public class ReturnToBattlefieldUnderYourControlSourceEffect extends OneShotEffe
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
ExileZone exileZone = game.getExile().getExileZone(exileZoneId);
if (exileZone != null && exileZone.contains(source.getSourceId())) {
Card card = game.getCard(source.getSourceId());
if (card != null && card.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), source.getControllerId())) {
Card card = game.getCard(source.getSourceId());
if (card != null
&& controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
return true;
}
}

View file

@ -0,0 +1,45 @@
package mage.abilities.effects.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ShuffleHandGraveyardAllEffect extends OneShotEffect {
public ShuffleHandGraveyardAllEffect() {
super(Outcome.Neutral);
staticText = "each player shuffles their hand and graveyard into their library";
}
public ShuffleHandGraveyardAllEffect(final ShuffleHandGraveyardAllEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
player.moveCards(player.getHand(), Zone.LIBRARY, source, game);
player.moveCards(player.getGraveyard(), Zone.LIBRARY, source, game);
player.shuffleLibrary(source, game);
}
}
return true;
}
@Override
public ShuffleHandGraveyardAllEffect copy() {
return new ShuffleHandGraveyardAllEffect(this);
}
}

View file

@ -50,6 +50,7 @@ import mage.game.events.ManaEvent;
* Commander rule #4 was removed Jan. 18, 2016
*
*/
@Deprecated
public class CommanderManaReplacementEffect extends ReplacementEffectImpl {
private final UUID playerId;

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.effects.common.cost;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
@ -101,7 +102,7 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
}
}
if (cardToCast != null) {
controller.cast(cardToCast.getSpellAbility(), game, true);
controller.cast(cardToCast.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
}
}
return true;

View file

@ -29,6 +29,7 @@ package mage.abilities.effects.common.cost;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
@ -54,6 +55,7 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
private int amount;
private final boolean upTo;
private boolean onlyControlled;
private UUID controllerId;
public SpellsCostReductionAllEffect(int amount) {
this(new FilterCard("Spells"), amount);
@ -74,6 +76,7 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
this.upTo = upTo;
this.onlyControlled = onlyControlled;
this.staticText = filter.getMessage() + " cost " + (upTo ? "up to " : "") + '{' + amount + "} less to cast";
this.controllerId = null;
}
protected SpellsCostReductionAllEffect(final SpellsCostReductionAllEffect effect) {
@ -82,6 +85,7 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
this.amount = effect.amount;
this.upTo = effect.upTo;
this.onlyControlled = effect.onlyControlled;
this.controllerId = effect.controllerId;
}
@Override
@ -145,6 +149,9 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
if (onlyControlled && !abilityToModify.getControllerId().equals(source.getControllerId())) {
return false;
}
if (controllerId != null && !abilityToModify.getControllerId().equals(controllerId)) {
return false;
}
if (abilityToModify instanceof SpellAbility) {
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
if (spell != null) {
@ -158,6 +165,10 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
return false;
}
public void setControllerId(UUID controllerId) {
this.controllerId = controllerId;
}
@Override
public SpellsCostReductionAllEffect copy() {
return new SpellsCostReductionAllEffect(this);

View file

@ -38,12 +38,15 @@ import mage.choices.ChoiceColor;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import org.apache.log4j.Logger;
/**
* @author noxx
*/
public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
private static final Logger logger = Logger.getLogger(AddConditionalManaOfAnyColorEffect.class);
private final DynamicValue amount;
private final ConditionalManaBuilder manaBuilder;
private final boolean oneChoice;
@ -87,8 +90,13 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
checkToFirePossibleEvents(getMana(game, source), game, source);
controller.getManaPool().addMana(getMana(game, source), game, source);
Mana mana = getMana(game, source);
if (mana != null) {
checkToFirePossibleEvents(mana, game, source);
controller.getManaPool().addMana(mana, game, source);
} else {
logger.error("There was no mana created: " + source.getSourceObject(game).getName() + " - Ability: " + source.getRule());
}
return true;
}
return false;

View file

@ -45,7 +45,7 @@ public class AddManaAnyColorAttachedControllerEffect extends ManaEffect {
public AddManaAnyColorAttachedControllerEffect() {
super();
staticText = "its controller adds one mana of any color to their mana pool";
staticText = "its controller adds one mana of any color";
}
public AddManaAnyColorAttachedControllerEffect(final AddManaAnyColorAttachedControllerEffect effect) {

View file

@ -44,7 +44,7 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
public AddManaOfAnyTypeProducedEffect() {
super();
staticText = "that player adds one mana to their mana pool of any type that land produced";
staticText = "that player adds one mana of any type that land produced";
}
public AddManaOfAnyTypeProducedEffect(final AddManaOfAnyTypeProducedEffect effect) {

View file

@ -38,7 +38,7 @@ public class AddManaToManaPoolTargetControllerEffect extends ManaEffect {
super();
this.mana = mana;
this.emptyOnlyOnTurnsEnd = emptyOnTurnsEnd;
this.staticText = (textManaPoolOwner.equals("their") ? "that player adds " : "add ") + mana.toString() + " to " + textManaPoolOwner + " mana pool";
this.staticText = (textManaPoolOwner.equals("their") ? "that player adds " : "add ") + mana.toString();
}
public AddManaToManaPoolTargetControllerEffect(final AddManaToManaPoolTargetControllerEffect effect) {

View file

@ -0,0 +1,159 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.SpecialAction;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterPlayer;
import mage.filter.predicate.other.PlayerPredicate;
import mage.game.Game;
import mage.players.ManaPool;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPlayer;
/*
* @author emerald000
*/
public class AssistAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
private static final FilterPlayer filter = new FilterPlayer("another player");
static {
filter.add(new PlayerPredicate(TargetController.NOT_YOU));
}
public AssistAbility() {
super(Zone.STACK, null);
this.setRuleAtTheTop(true);
}
public AssistAbility(final AssistAbility ability) {
super(ability);
}
@Override
public AssistAbility copy() {
return new AssistAbility(this);
}
@Override
public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null
&& source.getAbilityType() == AbilityType.SPELL
&& unpaid.getMana().getGeneric() >= 1
&& game.getState().getValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId())) == null) {
SpecialAction specialAction = new AssistSpecialAction(unpaid);
specialAction.setControllerId(source.getControllerId());
specialAction.setSourceId(source.getSourceId());
Target target = new TargetPlayer(1, 1, true, filter);
specialAction.addTarget(target);
if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
game.getState().getSpecialActions().add(specialAction);
}
}
}
@Override
public String getRule() {
return "Assist <i>(Another player can help pay the generic mana of this spell's cost.)</i>";
}
}
class AssistSpecialAction extends SpecialAction {
AssistSpecialAction(ManaCost unpaid) {
super(Zone.ALL, true);
setRuleVisible(false);
this.addEffect(new AssistEffect(unpaid));
}
AssistSpecialAction(final AssistSpecialAction ability) {
super(ability);
}
@Override
public AssistSpecialAction copy() {
return new AssistSpecialAction(this);
}
}
class AssistEffect extends OneShotEffect {
private final ManaCost unpaid;
AssistEffect(ManaCost unpaid) {
super(Outcome.Benefit);
this.unpaid = unpaid;
this.staticText = "Assist <i>(Another player can help pay the generic mana of this spell's cost.)</i>";
}
AssistEffect(final AssistEffect effect) {
super(effect);
this.unpaid = effect.unpaid;
}
@Override
public AssistEffect copy() {
return new AssistEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
if (controller != null && targetPlayer != null) {
int amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(), "How much mana to pay?", game, source);
if (amountToPay > 0) {
Cost cost = new GenericManaCost(amountToPay);
if (cost.pay(source, game, source.getSourceId(), targetPlayer.getId(), false)) {
ManaPool manaPool = controller.getManaPool();
manaPool.addMana(Mana.ColorlessMana(amountToPay), game, source);
manaPool.unlockManaType(ManaType.COLORLESS);
game.informPlayers(targetPlayer.getLogName() + " paid " + amountToPay + " mana.");
game.getState().setValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), true);
}
}
return true;
}
return false;
}
}

View file

@ -103,11 +103,11 @@ class AuraSwapEffect extends OneShotEffect {
Card auraInHand = game.getCard(target.getFirstTarget());
if (auraInHand != null) {
game.getState().setValue("attachTo:" + auraInHand.getId(), enchantedPermanent);
auraInHand.putOntoBattlefield(game, Zone.HAND, source.getSourceId(), controller.getId());
controller.moveCards(auraInHand, Zone.BATTLEFIELD, source, game);
enchantedPermanent.addAttachment(auraInHand.getId(), game);
game.informPlayers(controller.getLogName() + " put " + auraInHand.getLogName() + " on the battlefield attached to " + enchantedPermanent.getLogName() + '.');
enchantedPermanent.removeAttachment(auraSourcePermanent.getId(), game);
return controller.moveCards(game.getCard(source.getSourceId()), Zone.HAND, source, game);
return controller.moveCards(auraSourcePermanent, Zone.HAND, source, game);
}
}
}

View file

@ -39,7 +39,7 @@ public class BasicLandcyclingAbility extends CyclingAbility{
private static final String text = "Basic landcycling";
public BasicLandcyclingAbility(ManaCosts costs) {
super(costs, StaticFilters.FILTER_BASIC_LAND_CARD, text);
super(costs, StaticFilters.FILTER_CARD_BASIC_LAND, text);
}
public BasicLandcyclingAbility(final BasicLandcyclingAbility ability) {

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.keyword;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
@ -124,7 +125,7 @@ class CascadeEffect extends OneShotEffect {
if (card != null) {
if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + '?', source, game)) {
controller.cast(card.getSpellAbility(), game, true);
controller.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
}
}
// Move the remaining cards to the buttom of the library in a random order

View file

@ -145,7 +145,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
target.setTargetName("creature to convoke");
specialAction.addTarget(target);
if (specialAction.canActivate(source.getControllerId(), game)) {
if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
game.getState().getSpecialActions().add(specialAction);
}
}

View file

@ -110,7 +110,7 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa
}
specialAction.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(
0, Math.min(controller.getGraveyard().size(), unpaidAmount), new FilterCard(), true)));
if (specialAction.canActivate(source.getControllerId(), game)) {
if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
game.getState().getSpecialActions().add(specialAction);
}
}

View file

@ -1,140 +1,140 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import java.util.UUID;
import mage.Mana;
import mage.abilities.SpellAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.CardIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.util.CardUtil;
/**
*
* @author emerald000
*/
public class EmergeAbility extends SpellAbility {
private final ManaCosts<ManaCost> emergeCost;
public EmergeAbility(Card card, ManaCosts<ManaCost> emergeCost) {
super(emergeCost, card.getName() + " with emerge", Zone.HAND, SpellAbilityType.BASE_ALTERNATE);
this.getCosts().addAll(card.getSpellAbility().getCosts().copy());
this.getEffects().addAll(card.getSpellAbility().getEffects().copy());
this.getTargets().addAll(card.getSpellAbility().getTargets().copy());
this.timing = card.getSpellAbility().getTiming();
this.setRuleAtTheTop(true);
this.emergeCost = emergeCost.copy();
}
public EmergeAbility(final EmergeAbility ability) {
super(ability);
this.emergeCost = ability.emergeCost.copy();
}
@Override
public boolean canActivate(UUID playerId, Game game) {
if (super.canActivate(playerId, game)) {
Player controller = game.getPlayer(this.getControllerId());
if (controller != null) {
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) {
ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getConvertedManaCost());
if (costToPay.canPay(this, this.getSourceId(), this.getControllerId(), game)) {
return true;
}
}
}
}
return false;
}
@Override
public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) {
int maxCMC = 0;
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), playerId, this.getSourceId(), game)) {
int cmc = creature.getConvertedManaCost();
if (cmc > maxCMC) {
maxCMC = cmc;
}
}
ManaOptions manaOptions = super.getMinimumCostToActivate(playerId, game);
for (Mana mana : manaOptions) {
if (mana.getGeneric() > maxCMC) {
mana.setGeneric(mana.getGeneric() - maxCMC);
} else {
mana.setGeneric(0);
}
}
return manaOptions;
}
@Override
public boolean activate(Game game, boolean noMana) {
Player controller = game.getPlayer(this.getControllerId());
if (controller != null) {
TargetPermanent target = new TargetControlledCreaturePermanent(new FilterControlledCreaturePermanent("creature to sacrifice for emerge"));
if (controller.choose(Outcome.Sacrifice, target, this.getSourceId(), game)) {
Permanent creature = game.getPermanent(target.getFirstTarget());
CardUtil.reduceCost(this, creature.getConvertedManaCost());
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(creature.getLogName());
filter.add(new CardIdPredicate(creature.getId()));
this.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(filter)));
return super.activate(game, false);
}
}
return false;
}
@Override
public EmergeAbility copy() {
return new EmergeAbility(this);
}
@Override
public String getRule(boolean all) {
return getRule();
}
@Override
public String getRule() {
return "Emerge " + emergeCost.getText() + " <i>(You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost.)</i>";
}
}
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import java.util.UUID;
import mage.Mana;
import mage.abilities.SpellAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.CardIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.util.CardUtil;
/**
*
* @author emerald000
*/
public class EmergeAbility extends SpellAbility {
private final ManaCosts<ManaCost> emergeCost;
public EmergeAbility(Card card, ManaCosts<ManaCost> emergeCost) {
super(emergeCost, card.getName() + " with emerge", Zone.HAND, SpellAbilityType.BASE_ALTERNATE);
this.getCosts().addAll(card.getSpellAbility().getCosts().copy());
this.getEffects().addAll(card.getSpellAbility().getEffects().copy());
this.getTargets().addAll(card.getSpellAbility().getTargets().copy());
this.timing = card.getSpellAbility().getTiming();
this.setRuleAtTheTop(true);
this.emergeCost = emergeCost.copy();
}
public EmergeAbility(final EmergeAbility ability) {
super(ability);
this.emergeCost = ability.emergeCost.copy();
}
@Override
public ActivationStatus canActivate(UUID playerId, Game game) {
if (super.canActivate(playerId, game).canActivate()) {
Player controller = game.getPlayer(this.getControllerId());
if (controller != null) {
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) {
ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getConvertedManaCost());
if (costToPay.canPay(this, this.getSourceId(), this.getControllerId(), game)) {
return ActivationStatus.getTrue();
}
}
}
}
return ActivationStatus.getFalse();
}
@Override
public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) {
int maxCMC = 0;
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), playerId, this.getSourceId(), game)) {
int cmc = creature.getConvertedManaCost();
if (cmc > maxCMC) {
maxCMC = cmc;
}
}
ManaOptions manaOptions = super.getMinimumCostToActivate(playerId, game);
for (Mana mana : manaOptions) {
if (mana.getGeneric() > maxCMC) {
mana.setGeneric(mana.getGeneric() - maxCMC);
} else {
mana.setGeneric(0);
}
}
return manaOptions;
}
@Override
public boolean activate(Game game, boolean noMana) {
Player controller = game.getPlayer(this.getControllerId());
if (controller != null) {
TargetPermanent target = new TargetControlledCreaturePermanent(new FilterControlledCreaturePermanent("creature to sacrifice for emerge"));
if (controller.choose(Outcome.Sacrifice, target, this.getSourceId(), game)) {
Permanent creature = game.getPermanent(target.getFirstTarget());
CardUtil.reduceCost(this, creature.getConvertedManaCost());
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(creature.getLogName());
filter.add(new CardIdPredicate(creature.getId()));
this.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(filter)));
return super.activate(game, false);
}
}
return false;
}
@Override
public EmergeAbility copy() {
return new EmergeAbility(this);
}
@Override
public String getRule(boolean all) {
return getRule();
}
@Override
public String getRule() {
return "Emerge " + emergeCost.getText() + " <i>(You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost.)</i>";
}
}

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.keyword;
import java.util.UUID;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.effects.common.AttachEffect;
@ -39,8 +40,6 @@ import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -57,14 +56,15 @@ public class EquipAbility extends ActivatedAbilityImpl {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
if (super.canActivate(playerId, game)) {
public ActivationStatus canActivate(UUID playerId, Game game) {
ActivationStatus activationStatus = super.canActivate(playerId, game);
if (activationStatus.canActivate()) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game)) {
return true;
return activationStatus;
}
}
return false;
return activationStatus;
}
public EquipAbility(final EquipAbility ability) {

View file

@ -1,96 +1,95 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.effects.common.AttachEffect;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
import mage.constants.SuperType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.SupertypePredicate;
/**
* @author Rystan
*/
public class EquipLegendaryAbility extends ActivatedAbilityImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("legendary creature you control");
static {
filter.add(new SupertypePredicate(SuperType.LEGENDARY));
}
public EquipLegendaryAbility(Outcome outcome, Cost cost) {
this(outcome, cost, new TargetControlledCreaturePermanent(filter));
}
public EquipLegendaryAbility(Outcome outcome, Cost cost, Target target) {
super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Equip"), cost);
this.addTarget(target);
this.timing = TimingRule.SORCERY;
}
@Override
public boolean canActivate(UUID playerId, Game game) {
if (super.canActivate(playerId, game)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game)) {
return true;
}
}
return false;
}
public EquipLegendaryAbility(final EquipLegendaryAbility ability) {
super(ability);
}
@Override
public EquipLegendaryAbility copy() {
return new EquipLegendaryAbility(this);
}
@Override
public String getRule() {
return "Equip legendary creature " + costs.getText() +
manaCosts.getText() + " (" + manaCosts.getText() +
": <i>Attach to target legendary creature you control. Equip only as a sorcery.)</i>";
}
}
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import java.util.UUID;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.effects.common.AttachEffect;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
/**
* @author Rystan
*/
public class EquipLegendaryAbility extends ActivatedAbilityImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("legendary creature you control");
static {
filter.add(new SupertypePredicate(SuperType.LEGENDARY));
}
public EquipLegendaryAbility(Outcome outcome, Cost cost) {
this(outcome, cost, new TargetControlledCreaturePermanent(filter));
}
public EquipLegendaryAbility(Outcome outcome, Cost cost, Target target) {
super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Equip"), cost);
this.addTarget(target);
this.timing = TimingRule.SORCERY;
}
@Override
public ActivationStatus canActivate(UUID playerId, Game game) {
ActivationStatus activationStatus = super.canActivate(playerId, game);
if (activationStatus.canActivate()) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game)) {
return activationStatus;
}
}
return activationStatus;
}
public EquipLegendaryAbility(final EquipLegendaryAbility ability) {
super(ability);
}
@Override
public EquipLegendaryAbility copy() {
return new EquipLegendaryAbility(this);
}
@Override
public String getRule() {
return "Equip legendary creature " + costs.getText()
+ manaCosts.getText() + " (" + manaCosts.getText()
+ ": <i>Attach to target legendary creature you control. Equip only as a sorcery.)</i>";
}
}

View file

@ -83,13 +83,14 @@ public class FlashbackAbility extends SpellAbility {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
if (super.canActivate(playerId, game)) {
public ActivationStatus canActivate(UUID playerId, Game game) {
ActivationStatus activationStatus = super.canActivate(playerId, game);
if (activationStatus.canActivate()) {
Card card = game.getCard(getSourceId());
if (card != null) {
// Cards with no Mana Costs cant't be flashbacked (e.g. Ancestral Vision)
if (card.getManaCost().isEmpty()) {
return false;
return ActivationStatus.getFalse();
}
// Flashback can never cast a split card by Fuse, because Fuse only works from hand
if (card.isSplitCard()) {
@ -102,7 +103,7 @@ public class FlashbackAbility extends SpellAbility {
return card.getSpellAbility().canActivate(playerId, game);
}
}
return false;
return activationStatus;
}
@Override

View file

@ -43,10 +43,10 @@ import mage.game.Game;
*
* 702.56b A forecast ability may be activated only during the upkeep step of
* the card's owner and only once each turn. The controller of the forecast
* ability reveals the card with that ability from their hand as the
* ability is activated. That player plays with that card revealed in their
* hand until it leaves the player's hand or until a step or phase that isn't an
* upkeep step begins, whichever comes first.
* ability reveals the card with that ability from their hand as the ability is
* activated. That player plays with that card revealed in their hand until it
* leaves the player's hand or until a step or phase that isn't an upkeep step
* begins, whichever comes first.
*
* @author LevelX2
*
@ -68,11 +68,11 @@ public class ForecastAbility extends LimitedTimesPerTurnActivatedAbility {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
// May be activated only during the upkeep step of the card's owner
// Because it can only be activated from a players hand it should be ok to check here with controllerId instead of card.getOwnerId().
if (!game.getActivePlayerId().equals(controllerId) || PhaseStep.UPKEEP != game.getStep().getType()) {
return false;
return ActivationStatus.getFalse();
}
return super.canActivate(playerId, game);
}

View file

@ -6,7 +6,6 @@
package mage.abilities.keyword;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.SpecialAction;
@ -69,7 +68,7 @@ public class ImproviseAbility extends SimpleStaticAbility implements AlternateMa
Target target = new TargetControlledPermanent(1, unpaid.getMana().getGeneric(), filter, true);
target.setTargetName("artifact to Improvise");
specialAction.addTarget(target);
if (specialAction.canActivate(source.getControllerId(), game)) {
if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
game.getState().getSpecialActions().add(specialAction);
}
}

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.keyword;
import java.util.*;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
@ -41,8 +42,6 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import java.util.*;
/**
* 20121001 702.31. Kicker 702.31a Kicker is a static ability that functions
* while the spell with kicker is on the stack. "Kicker [cost]" means "You may
@ -134,7 +133,8 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
cost.reset();
}
String key = getActivationKey(source, "", game);
for (String activationKey : activations.keySet()) {
for (Iterator<String> iterator = activations.keySet().iterator(); iterator.hasNext();) {
String activationKey = iterator.next();
if (activationKey.startsWith(key) && activations.get(activationKey) > 0) {
activations.put(key, 0);
}
@ -212,10 +212,10 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
&& player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) {
this.activateKicker(kickerCost, ability, game);
if (kickerCost instanceof Costs) {
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) {
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) {
Object kickerCostObject = itKickerCost.next();
if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) {
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext(); ) {
for (@SuppressWarnings("unchecked") Iterator<Cost> itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) {
addKickerCostsToAbility(itDetails.next(), ability, game);
}
} else {

View file

@ -2,6 +2,7 @@ package mage.abilities.keyword;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
@ -33,10 +34,10 @@ import mage.players.Player;
* first ability is applied.
*
* "Madness [cost]" means "If a player would discard this card, that player
* discards it, but may exile it instead of putting it into their
* graveyard" and "When this card is exiled this way, its owner may cast it by
* paying [cost] rather than paying its mana cost. If that player doesn't, he or
* she puts this card into their graveyard.
* discards it, but may exile it instead of putting it into their graveyard" and
* "When this card is exiled this way, its owner may cast it by paying [cost]
* rather than paying its mana cost. If that player doesn't, he or she puts this
* card into their graveyard.
*
* 702.33b. Casting a spell using its madness ability follows the rules for
* paying alternative costs in rules 601.2b and 601.2e-g.
@ -219,7 +220,7 @@ class MadnessCastEffect extends OneShotEffect {
castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS);
costRef.clear();
costRef.add(madnessCost);
boolean result = owner.cast(castByMadness, game, false);
boolean result = owner.cast(castByMadness, game, false, new MageObjectReference(source.getSourceObject(game), game));
return result;
}

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.keyword;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbilityImpl;
@ -52,8 +53,8 @@ import mage.watchers.common.MiracleWatcher;
* cost."
*
* 702.92b If a player chooses to reveal a card using its miracle ability, he or
* she plays with that card revealed until that card leaves their hand,
* that ability resolves, or that ability otherwise leaves the stack.
* she plays with that card revealed until that card leaves their hand, that
* ability resolves, or that ability otherwise leaves the stack.
*
* You can cast a card for its miracle cost only as the miracle triggered
* ability resolves. If you don't want to cast it at that time (or you can't
@ -172,7 +173,7 @@ class MiracleEffect extends OneShotEffect {
// replace with the new cost
costRef.clear();
costRef.add(miracleCosts);
controller.cast(abilityToCast, game, false);
controller.cast(abilityToCast, game, false, new MageObjectReference(source.getSourceObject(game), game));
return true;
}
return false;

View file

@ -120,9 +120,13 @@ class NinjutsuEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Card card = game.getCard(source.getSourceId());
if (card != null) {
card.putOntoBattlefield(game, Zone.HAND, source.getSourceId(), source.getControllerId());
controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
UUID defendingPlayerId = null;
@ -133,7 +137,6 @@ class NinjutsuEffect extends OneShotEffect {
}
if (defendingPlayerId != null) {
game.getCombat().addAttackerToCombat(permanent.getId(), defendingPlayerId, game);
permanent.setTapped(true);
return true;
}
}

View file

@ -0,0 +1,162 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPlayer;
import mage.target.common.TargetCardInLibrary;
/**
*
* @author TheElk801
*/
public class PartnerWithAbility extends EntersBattlefieldTriggeredAbility {
private final String partnerName;
private final String shortName;
private final boolean hasReminderText;
public PartnerWithAbility(String partnerName) {
this(partnerName, false);
}
public PartnerWithAbility(String partnerName, boolean isLegendary) {
this(partnerName, isLegendary, true);
}
public PartnerWithAbility(String partnerName, boolean isLegendary, boolean hasReminderText) {
super(new PartnersWithSearchEffect(partnerName), false);
this.addTarget(new TargetPlayer());
this.partnerName = partnerName;
this.hasReminderText = hasReminderText;
if (isLegendary) {
this.shortName = shortenName(partnerName);
} else {
this.shortName = partnerName;
}
}
public PartnerWithAbility(final PartnerWithAbility ability) {
super(ability);
this.partnerName = ability.partnerName;
this.shortName = ability.shortName;
this.hasReminderText = ability.hasReminderText;
}
@Override
public PartnerWithAbility copy() {
return new PartnerWithAbility(this);
}
@Override
public String getRule() {
if (hasReminderText) {
return "Partner with " + partnerName
+ " <i>(When this creature enters the battlefield, target player may put " + shortName
+ " into their hand from their library, then shuffle.)</i>";
} else {
return "Partner with " + partnerName;
}
}
public String getPartnerName() {
return partnerName;
}
public static String shortenName(String st) {
StringBuilder sb = new StringBuilder();
for (char s : st.toCharArray()) {
if (s == ' ' || s == ',') {
break;
} else {
sb.append(s);
}
}
return sb.toString();
}
}
class PartnersWithSearchEffect extends OneShotEffect {
private final String partnerName;
public PartnersWithSearchEffect(String partnerName) {
super(Outcome.DrawCard);
this.partnerName = partnerName;
this.staticText = "";
}
public PartnersWithSearchEffect(final PartnersWithSearchEffect effect) {
super(effect);
this.partnerName = effect.partnerName;
}
@Override
public PartnersWithSearchEffect copy() {
return new PartnersWithSearchEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Player player = game.getPlayer(source.getFirstTarget());
if (player != null) {
FilterCard filter = new FilterCard("card named " + partnerName);
filter.add(new NamePredicate(partnerName));
TargetCardInLibrary target = new TargetCardInLibrary(filter);
if (player.chooseUse(Outcome.Benefit, "Search your library for a card named " + partnerName + " and put it into your hand?", source, game)) {
player.searchLibrary(target, game);
for (UUID cardId : target.getTargets()) {
Card card = player.getLibrary().getCard(cardId, game);
if (card != null) {
player.revealCards(source, new CardsImpl(card), game);
player.moveCards(card, Zone.HAND, source, game);
}
}
player.shuffleLibrary(source, game);
}
}
// prevent undo
controller.resetStoredBookmark(game);
return true;
}
return false;
}
}

View file

@ -28,6 +28,7 @@
package mage.abilities.keyword;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.SpellAbility;
@ -206,7 +207,7 @@ class ReboundCastSpellFromExileEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
if (player != null && reboundCard != null) {
SpellAbility ability = reboundCard.getSpellAbility();
player.cast(ability, game, true);
player.cast(ability, game, true, new MageObjectReference(source.getSourceObject(game), game));
zone.remove(reboundCard.getId());
return true;
}

View file

@ -1,6 +1,7 @@
package mage.abilities.keyword;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
@ -103,7 +104,7 @@ class RippleEffect extends OneShotEffect {
while (player.canRespond() && cards.count(sameNameFilter, game) > 0 && player.choose(Outcome.PlayForFree, cards, target1, game)) {
Card card = cards.get(target1.getFirstTarget(), game);
if (card != null) {
player.cast(card.getSpellAbility(), game, true);
player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
cards.remove(card);
}
target1.clearChosen();

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.keyword;
import java.util.Iterator;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
@ -42,63 +43,66 @@ import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import java.util.Iterator;
/**
* 702.45. Splice
*
* 702.45a Splice is a static ability that functions while a card is in your hand.
* "Splice onto [subtype] [cost]" means "You may reveal this card from your hand
* as you cast a [subtype] spell. If you do, copy this card's text box onto that
* spell and pay [cost] as an additional cost to cast that spell." Paying a card's
* splice cost follows the rules for paying additional costs in rules 601.2b and
* 601.2e-g.
* 702.45a Splice is a static ability that functions while a card is in your
* hand. "Splice onto [subtype] [cost]" means "You may reveal this card from
* your hand as you cast a [subtype] spell. If you do, copy this card's text box
* onto that spell and pay [cost] as an additional cost to cast that spell."
* Paying a card's splice cost follows the rules for paying additional costs in
* rules 601.2b and 601.2e-g.
*
* Example: Since the card with splice remains in the player's hand, it can later
* be cast normally or spliced onto another spell. It can even be discarded to pay
* a "discard a card" cost of the spell it's spliced onto.
* Example: Since the card with splice remains in the player's hand, it can
* later be cast normally or spliced onto another spell. It can even be
* discarded to pay a "discard a card" cost of the spell it's spliced onto.
*
* 702.45b You can't choose to use a splice ability if you can't make the required
* choices (targets, etc.) for that card's instructions. You can't splice any one
* card onto the same spell more than once. If you're splicing more than one card
* onto a spell, reveal them all at once and choose the order in which their
* instructions will be followed. The instructions on the main spell have to be
* followed first.
* 702.45b You can't choose to use a splice ability if you can't make the
* required choices (targets, etc.) for that card's instructions. You can't
* splice any one card onto the same spell more than once. If you're splicing
* more than one card onto a spell, reveal them all at once and choose the order
* in which their instructions will be followed. The instructions on the main
* spell have to be followed first.
*
* 702.45c The spell has the characteristics of the main spell, plus the text boxes
* of each of the spliced cards. The spell doesn't gain any other characteristics
* (name, mana cost, color, supertypes, card types, subtypes, etc.) of the spliced
* cards. Text copied onto the spell that refers to a card by name refers to the spell
* on the stack, not the card from which the text was copied.
* 702.45c The spell has the characteristics of the main spell, plus the text
* boxes of each of the spliced cards. The spell doesn't gain any other
* characteristics (name, mana cost, color, supertypes, card types, subtypes,
* etc.) of the spliced cards. Text copied onto the spell that refers to a card
* by name refers to the spell on the stack, not the card from which the text
* was copied.
*
* Example: Glacial Ray is a red card with splice onto Arcane that reads, "Glacial
* Ray deals 2 damage to any target." Suppose Glacial Ray is spliced
* onto Reach Through Mists, a blue spell. The spell is still blue, and Reach Through
* Mists deals the damage. This means that the ability can target a creature with
* protection from red and deal 2 damage to that creature.
* Example: Glacial Ray is a red card with splice onto Arcane that reads,
* "Glacial Ray deals 2 damage to any target." Suppose Glacial Ray is spliced
* onto Reach Through Mists, a blue spell. The spell is still blue, and Reach
* Through Mists deals the damage. This means that the ability can target a
* creature with protection from red and deal 2 damage to that creature.
*
* 702.45d Choose targets for the added text normally (see rule 601.2c). Note that a
* spell with one or more targets will be countered if all of its targets are illegal
* on resolution.
* 702.45d Choose targets for the added text normally (see rule 601.2c). Note
* that a spell with one or more targets will be countered if all of its targets
* are illegal on resolution.
*
* 702.45e The spell loses any splice changes once it leaves the stack (for example,
* when it's countered, it's exiled, or it resolves).
* 702.45e The spell loses any splice changes once it leaves the stack (for
* example, when it's countered, it's exiled, or it resolves).
*
* Rulings
* Rulings
*
* You must reveal all of the cards you intend to splice at the same time. Each individual card can only be spliced once onto a spell.
* If you have more than one card with the same name in your hand, you may splice both of them onto the spell.
* A card with a splice ability can't be spliced onto itself because the spell is on the stack (and not in your hand) when you reveal the cards you want to splice onto it.
* The target for a card that's spliced onto a spell may be the same as the target chosen for the original spell or for another spliced-on card. (A recent change to the targeting rules allows this, but most other cards are unaffected by the change.)
* If you splice a targeted card onto an untargeted spell, the entire spell will be countered if the target isn't legal when the spell resolves.
* If you splice an untargeted card onto a targeted spell, the entire spell will be countered if the target isn't legal when the spell resolves.
* A spell is countered on resolution only if *all* of its targets are illegal (or the spell is countered by an effect).
* You must reveal all of the cards you intend to splice at the same time. Each
* individual card can only be spliced once onto a spell. If you have more than
* one card with the same name in your hand, you may splice both of them onto
* the spell. A card with a splice ability can't be spliced onto itself because
* the spell is on the stack (and not in your hand) when you reveal the cards
* you want to splice onto it. The target for a card that's spliced onto a spell
* may be the same as the target chosen for the original spell or for another
* spliced-on card. (A recent change to the targeting rules allows this, but
* most other cards are unaffected by the change.) If you splice a targeted card
* onto an untargeted spell, the entire spell will be countered if the target
* isn't legal when the spell resolves. If you splice an untargeted card onto a
* targeted spell, the entire spell will be countered if the target isn't legal
* when the spell resolves. A spell is countered on resolution only if *all* of
* its targets are illegal (or the spell is countered by an effect).
*
* @author LevelX2
*/
public class SpliceOntoArcaneAbility extends SimpleStaticAbility {
private static final String KEYWORD_TEXT = "Splice onto Arcane";
@ -134,8 +138,8 @@ public class SpliceOntoArcaneAbility extends SimpleStaticAbility {
@Override
public String getRule() {
StringBuilder sb = new StringBuilder();
sb.append(KEYWORD_TEXT).append(nonManaCosts?"-":" ");
sb.append(spliceCosts.getText()).append(nonManaCosts?". ":" ");
sb.append(KEYWORD_TEXT).append(nonManaCosts ? "-" : " ");
sb.append(spliceCosts.getText()).append(nonManaCosts ? ". " : " ");
sb.append("<i>(As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.)</i>");
return sb.toString();
}
@ -193,7 +197,7 @@ class SpliceOntoArcaneEffect extends SpliceCardEffectImpl {
if (card.getManaCost().isEmpty()) { // e.g. Evermind
return card.getSpellAbility().spellCanBeActivatedRegularlyNow(source.getControllerId(), game);
} else {
return card.getSpellAbility().canActivate(source.getControllerId(), game);
return card.getSpellAbility().canActivate(source.getControllerId(), game).canActivate();
}
}
return false;

View file

@ -66,7 +66,7 @@ public class SurgeAbility extends SpellAbility {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
// check if controller or teammate has already cast a spell this turn
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getSimpleName());
if (watcher != null) {
@ -81,7 +81,7 @@ public class SurgeAbility extends SpellAbility {
}
}
}
return false;
return ActivationStatus.getFalse();
}
@Override

View file

@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpecialAction;
import mage.abilities.TriggeredAbilityImpl;
@ -228,16 +229,16 @@ public class SuspendAbility extends SpecialAction {
}
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
if (game.getState().getZone(getSourceId()) != Zone.HAND) {
// Supend can only be activated from hand
return false;
return ActivationStatus.getFalse();
}
MageObject object = game.getObject(sourceId);
return (object.isInstant()
return new ActivationStatus(object.isInstant()
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|| null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|| game.canPlaySorcery(playerId));
|| game.canPlaySorcery(playerId), null);
}
@Override
@ -376,7 +377,7 @@ class SuspendPlayCardEffect extends OneShotEffect {
card.getAbilities().removeAll(abilitiesToRemove);
}
// cast the card for free
if (player.cast(card.getSpellAbility(), game, true)) {
if (player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game))) {
if (card.isCreature()) {
ContinuousEffect effect = new GainHasteEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1));

View file

@ -58,7 +58,7 @@ public class ActivateOncePerTurnManaAbility extends ActivatedManaAbilityImpl {
@Override
public boolean activate(Game game, boolean noMana) {
if (canActivate(this.controllerId, game)) {
if (canActivate(this.controllerId, game).canActivate()) {
return super.activate(game, noMana);
}
return false;

View file

@ -69,21 +69,21 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
}
@Override
public boolean canActivate(UUID playerId, Game game) {
public ActivationStatus canActivate(UUID playerId, Game game) {
if (!super.hasMoreActivationsThisTurn(game) || !(condition == null || condition.apply(game, this))) {
return false;
return ActivationStatus.getFalse();
}
if (!controlsAbility(playerId, game)) {
return false;
return ActivationStatus.getFalse();
}
if (timing == TimingRule.SORCERY
&& !game.canPlaySorcery(playerId)
&& null == game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
return false;
return ActivationStatus.getFalse();
}
// check if player is in the process of playing spell costs and he is no longer allowed to use activated mana abilities (e.g. because he started to use improvise)
//20091005 - 605.3a
return costs.canPay(this, sourceId, controllerId, game);
return new ActivationStatus(costs.canPay(this, sourceId, controllerId, game), null);
}

View file

@ -142,7 +142,7 @@ public class Deck implements Serializable {
cardInfo = CardRepository.instance.findCard("Silvercoat Lion");
Logger.getLogger(Deck.class).error("Tried to restart the DB: " + (cardInfo == null ? "not successful" : "successful"));
}
return new GameException("Card not found - " + deckCardInfo.getCardName() + " - " + deckCardInfo.getSetCode() + " for deck - " + deckName + '\n'
return new GameException("Card not found - " + deckCardInfo.getCardName() + " - " + deckCardInfo.getSetCode() + "/" + deckCardInfo.getCardNum() + " for deck - " + deckName + '\n'
+ "Possible reason is, that you use cards in your deck, that are only supported in newer versions of the server.\n"
+ "So it can help to use the same card from another set, that's already supported from this server.");

View file

@ -58,7 +58,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 51;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 109;
private static final long CARD_CONTENT_VERSION = 111;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;

View file

@ -29,7 +29,7 @@ public enum ExpansionRepository {
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE";
private static final String VERSION_ENTITY_NAME = "expansion";
private static final long EXPANSION_DB_VERSION = 5;
private static final long EXPANSION_CONTENT_VERSION = 14;
private static final long EXPANSION_CONTENT_VERSION = 15;
private Dao<ExpansionInfo, Object> expansionDao;
@ -88,10 +88,10 @@ public enum ExpansionRepository {
try {
// only with boosters and cards
GenericRawResults<ExpansionInfo> setsList = expansionDao.queryRaw(
"select * from expansion e " +
" where e.boosters = 1 " +
" and exists(select (1) from card c where c.setcode = e.code) " +
" order by e.releasedate desc",
"select * from expansion e "
+ " where e.boosters = 1 "
+ " and exists(select (1) from card c where c.setcode = e.code) "
+ " order by e.releasedate desc",
expansionDao.getRawRowMapper());
List<ExpansionInfo> resList = new ArrayList<>();

View file

@ -0,0 +1,51 @@
/*
* 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.choices;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author TheElk801
*/
public class ChooseFriendsAndFoes {
private List<Player> friends = new ArrayList<>();
private List<Player> foes = new ArrayList<>();
public boolean chooseFriendOrFoe(Player playerChoosing, Ability source, Game game) {
if (playerChoosing == null) {
return false;
}
friends.clear();
foes.clear();
for (UUID playerId : game.getState().getPlayersInRange(playerChoosing.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
if (playerChoosing.chooseUse(Outcome.Vote, "Is " + player.getName() + " friend or foe?", null, "Friend", "Foe", source, game)) {
friends.add(player);
} else {
foes.add(player);
}
}
}
return true;
}
public List<Player> getFriends() {
return friends;
}
public List<Player> getFoes() {
return foes;
}
}

View file

@ -61,6 +61,7 @@ public enum SubType {
AUROCHS("Aurochs", SubTypeSet.CreatureType),
AUTOBOT("Autobot", SubTypeSet.CreatureType, true), // H17, Grimlock
AVATAR("Avatar", SubTypeSet.CreatureType),
AZRA("Azra", SubTypeSet.CreatureType),
// B
BADGER("Badger", SubTypeSet.CreatureType),
BARBARIAN("Barbarian", SubTypeSet.CreatureType),
@ -389,6 +390,7 @@ public enum SubType {
NIXILIS("Nixilis", SubTypeSet.PlaneswalkerType),
OBI_WAN("Obi-Wan", SubTypeSet.PlaneswalkerType, true), // Star Wars
RAL("Ral", SubTypeSet.PlaneswalkerType),
ROWAN("Rowan", SubTypeSet.PlaneswalkerType),
SAHEELI("Saheeli", SubTypeSet.PlaneswalkerType),
SAMUT("Samut", SubTypeSet.PlaneswalkerType),
SARKHAN("Sarkhan", SubTypeSet.PlaneswalkerType),
@ -401,6 +403,7 @@ public enum SubType {
UGIN("Ugin", SubTypeSet.PlaneswalkerType),
VENSER("Venser", SubTypeSet.PlaneswalkerType),
VRASKA("Vraska", SubTypeSet.PlaneswalkerType),
WILL("Will", SubTypeSet.PlaneswalkerType),
XENAGOS("Xenagos", SubTypeSet.PlaneswalkerType),
YANGGU("Yanggu", SubTypeSet.PlaneswalkerType),
YANLING("Yanling", SubTypeSet.PlaneswalkerType),

View file

@ -6,5 +6,5 @@ package mage.constants;
*/
public enum TargetController {
ACTIVE, ANY, YOU, NOT_YOU, OPPONENT, OWNER, CONTROLLER_ATTACHED_TO, NEXT
ACTIVE, ANY, YOU, NOT_YOU, OPPONENT, TEAM, OWNER, CONTROLLER_ATTACHED_TO, NEXT
}

View file

@ -104,7 +104,7 @@ public class FilterMana implements Serializable {
}
public boolean isColorless() {
return colorless;
return !(white || blue || black || red || green) || colorless;
}
public void setColorless(boolean colorless) {

View file

@ -71,6 +71,13 @@ public final class StaticFilters {
static {
FILTER_CARD_CREATURE.setLockedFilter(true);
}
public static final FilterCreatureCard FILTER_CARD_CREATURE_A = new FilterCreatureCard("a creature card");
static {
FILTER_CARD_CREATURE_A.setLockedFilter(true);
}
public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_GRAVEYARD = new FilterCreatureCard("creature card from your graveyard");
static {
@ -88,6 +95,25 @@ public final class StaticFilters {
static {
FILTER_CARD_LAND.setLockedFilter(true);
}
public static final FilterLandCard FILTER_CARD_LAND_A = new FilterLandCard("a land card");
static {
FILTER_CARD_LAND_A.setLockedFilter(true);
}
public static final FilterBasicLandCard FILTER_CARD_BASIC_LAND = new FilterBasicLandCard();
static {
FILTER_CARD_BASIC_LAND.setLockedFilter(true);
}
public static final FilterBasicLandCard FILTER_CARD_BASIC_LAND_A = new FilterBasicLandCard("a basic land card");
static {
FILTER_CARD_BASIC_LAND_A.setLockedFilter(true);
}
public static final FilterNonlandCard FILTER_CARD_NON_LAND = new FilterNonlandCard();
static {
@ -286,12 +312,6 @@ public final class StaticFilters {
FILTER_LANDS_NONBASIC.setLockedFilter(true);
}
public static final FilterBasicLandCard FILTER_BASIC_LAND_CARD = new FilterBasicLandCard();
static {
FILTER_BASIC_LAND_CARD.setLockedFilter(true);
}
// Used for sacrifice targets that don't need the "you control" text
public static final FilterControlledLandPermanent FILTER_CONTROLLED_LAND_SHORT_TEXT = new FilterControlledLandPermanent("a land");
@ -357,6 +377,12 @@ public final class StaticFilters {
FILTER_PERMANENT_NON_LAND.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENTS_NON_LAND = new FilterNonlandPermanent("nonland permanents");
static {
FILTER_PERMANENTS_NON_LAND.setLockedFilter(true);
}
public static final FilterCreatureSpell FILTER_SPELL_A_CREATURE = new FilterCreatureSpell("a creature spell");
static {

View file

@ -61,7 +61,7 @@ class BlockTappedPredicate implements Predicate<Permanent> {
@Override
public boolean apply(Permanent input, Game game) {
return !input.isTapped() || null != game.getState().getContinuousEffects().asThough(input.getId(), AsThoughEffectType.BLOCK_TAPPED, input.getControllerId(), game);
return !input.isTapped() || null != game.getState().getContinuousEffects().asThough(input.getId(), AsThoughEffectType.BLOCK_TAPPED, null, input.getControllerId(), game);
}
@Override

View file

@ -0,0 +1,41 @@
package mage.filter.common;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.predicate.permanent.ControllerPredicate;
/*
* 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.
*/
/**
*
* @author TheElk801
*/
public class FilterTeamCreaturePermanent extends FilterCreaturePermanent {
public FilterTeamCreaturePermanent() {
this("creature your team controls");
}
public FilterTeamCreaturePermanent(String name) {
super(name);
this.add(new ControllerPredicate(TargetController.TEAM));
}
public FilterTeamCreaturePermanent(SubType subtype, String name) {
super(subtype, name);
this.add(new ControllerPredicate(TargetController.TEAM));
}
public FilterTeamCreaturePermanent(final FilterTeamCreaturePermanent filter) {
super(filter);
}
@Override
public FilterTeamCreaturePermanent copy() {
return new FilterTeamCreaturePermanent(this);
}
}

View file

@ -0,0 +1,42 @@
package mage.filter.common;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
/*
* 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.
*/
/**
*
* @author TheElk801
*/
public class FilterTeamPermanent extends FilterPermanent {
public FilterTeamPermanent() {
this("permanent your team controls");
}
public FilterTeamPermanent(String name) {
super(name);
this.add(new ControllerPredicate(TargetController.TEAM));
}
public FilterTeamPermanent(SubType subtype, String name) {
super(subtype, name);
this.add(new ControllerPredicate(TargetController.TEAM));
}
public FilterTeamPermanent(final FilterTeamPermanent filter) {
super(filter);
}
@Override
public FilterTeamPermanent copy() {
return new FilterTeamPermanent(this);
}
}

View file

@ -0,0 +1,56 @@
/*
* 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.filter.predicate.mageobject;
import mage.MageObject;
import mage.MageObjectReference;
import mage.filter.predicate.Predicate;
import mage.game.Game;
/**
*
* @author TheElk801
*/
public class MageObjectReferencePredicate implements Predicate<MageObject> {
private final MageObjectReference mor;
public MageObjectReferencePredicate(MageObjectReference mor) {
this.mor = mor;
}
@Override
public boolean apply(MageObject input, Game game) {
return mor.refersTo(input, game);
}
@Override
public String toString() {
return "MageObjectReference(" + mor.toString() + ')';
}
}

View file

@ -58,9 +58,14 @@ public class ControllerPredicate implements ObjectPlayerPredicate<ObjectPlayer<C
return true;
}
break;
case TEAM:
if (!game.getPlayer(playerId).hasOpponent(object.getControllerId(), game)) {
return true;
}
break;
case OPPONENT:
if (!object.getControllerId().equals(playerId) &&
game.getPlayer(playerId).hasOpponent(object.getControllerId(), game)) {
if (!object.getControllerId().equals(playerId)
&& game.getPlayer(playerId).hasOpponent(object.getControllerId(), game)) {
return true;
}
break;

View file

@ -29,7 +29,6 @@ package mage.game.combat;
import java.io.Serializable;
import java.util.*;
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
import mage.abilities.common.ControllerDivideCombatDamageAbility;
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
@ -178,7 +177,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
if (attackers.size() != 1) {
multiAttackerDamage(first, game);
// } else {
// } else {
// singleAttackerDamage(first, game);
}
}
@ -334,7 +333,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
defenderDamage(attacker, damage, game);
} else if (!blockerOrder.isEmpty()) {
// Assign the damage left to first blocker
assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) + damage);
assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) == null ? 0 : assigned.get(blockerOrder.get(0)) + damage);
}
}
for (UUID blockerId : blockerOrder) {
@ -430,7 +429,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
}
public boolean checkSoleBlockerAfter (Permanent blocker, Game game) {
public boolean checkSoleBlockerAfter(Permanent blocker, Game game) {
// this solves some corner cases (involving banding) when finding out whether a blocker is blocking alone or not
if (blocker.getBlocking() == 1) {
if (game.getCombat().blockingGroups.get(blocker.getId()) == null) {
@ -454,9 +453,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
* {@link #singleBlockerDamage}.
*
* Handles abilities like "{this} an block any number of creatures.".
*
* Blocker damage for blockers blocking single creatures is handled in
* the single/multi blocker methods, so this shouldn't be used anymore.
*
* Blocker damage for blockers blocking single creatures is handled in the
* single/multi blocker methods, so this shouldn't be used anymore.
*
* @param first
* @param game
@ -782,7 +781,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
/**
* There are effects, that set an attacker to be blocked. Therefore this
* setter can be used.
*
*
* This method lacks a band check, use setBlocked(blocked, game) instead.
*
* @param blocked
@ -870,8 +869,8 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
/**
* Decides damage distribution for blocking creatures with banding or
* if defending player controls the Defensive Formation enchantment.
* Decides damage distribution for blocking creatures with banding or if
* defending player controls the Defensive Formation enchantment.
*
* @param game
*/

View file

@ -0,0 +1,124 @@
/*
* 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.game.command.emblems;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.mana.ManaAbility;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.command.Emblem;
import mage.game.events.GameEvent;
import mage.game.stack.StackAbility;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author TheElk801
*/
public class RowanKenrithEmblem extends Emblem {
// Target player gets an emblem with "Whenever you activate an ability that isn't a mana ability, copy it. You may choose new targets for the copy."
public RowanKenrithEmblem() {
this.setName("Emblem Rowan Kenrith");
this.getAbilities().add(new RowanKenrithEmblemTriggeredAbility());
}
}
class RowanKenrithEmblemTriggeredAbility extends TriggeredAbilityImpl {
public RowanKenrithEmblemTriggeredAbility() {
super(Zone.COMMAND, new RowanKenrithEmblemCopyEffect(), false);
}
public RowanKenrithEmblemTriggeredAbility(final RowanKenrithEmblemTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(this.getControllerId())) {
StackObject ability = game.getStack().getStackObject(event.getTargetId());
if (ability != null && !(ability instanceof ManaAbility)) {
this.getEffects().get(0).setTargetPointer(new FixedTarget(ability.getId()));
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever you activate an ability that isn't a mana ability, copy it. You may choose new targets for the copy.";
}
@Override
public RowanKenrithEmblemTriggeredAbility copy() {
return new RowanKenrithEmblemTriggeredAbility(this);
}
}
class RowanKenrithEmblemCopyEffect extends OneShotEffect {
public RowanKenrithEmblemCopyEffect() {
super(Outcome.Copy);
this.staticText = "copy it. You may choose new targets for the copy.";
}
public RowanKenrithEmblemCopyEffect(final RowanKenrithEmblemCopyEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source));
if (stackAbility != null) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
stackAbility.createCopyOnStack(game, source, source.getControllerId(), true);
return true;
}
}
return false;
}
@Override
public RowanKenrithEmblemCopyEffect copy() {
return new RowanKenrithEmblemCopyEffect(this);
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.game.command.emblems;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.command.Emblem;
/**
*
* @author TheElk801
*/
public class WillKenrithEmblem extends Emblem {
// Target player gets an emblem with "Whenever you cast an instant or sorcery spell, copy it. You may choose new targets for the copy."
public WillKenrithEmblem() {
this.setName("Emblem Will Kenrith");
this.getAbilities().add(new SpellCastControllerTriggeredAbility(
Zone.COMMAND,
new CopyTargetSpellEffect(true)
.setText("copy that spell. You may choose new targets for the copy"),
StaticFilters.FILTER_INSTANT_OR_SORCERY_SPELL,
false,
true
));
}
}

View file

@ -31,6 +31,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageObjectReference;
import mage.constants.Zone;
/**
@ -48,6 +49,7 @@ public class GameEvent implements Serializable {
protected String data;
protected Zone zone;
protected List<UUID> appliedEffects = new ArrayList<>();
protected MageObjectReference reference; // e.g. the permitting object for casting a spell from non hand zone
protected UUID customEventType = null;
public enum EventType {
@ -232,7 +234,7 @@ public class GameEvent implements Serializable {
FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL,
ROLL_DICE, DICE_ROLLED,
ROLL_PLANAR_DIE, PLANAR_DIE_ROLLED,
PLANESWALK, PLANESWALKED,
PLANESWALK, PLANESWALKED,
PAID_CUMULATIVE_UPKEEP,
DIDNT_PAY_CUMULATIVE_UPKEEP,
//permanent events
@ -334,21 +336,14 @@ public class GameEvent implements Serializable {
CUSTOM_EVENT
}
private GameEvent(EventType type, UUID customEventType,
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this.type = type;
this.customEventType = customEventType;
this.targetId = targetId;
this.sourceId = sourceId;
this.amount = amount;
this.playerId = playerId;
this.flag = flag;
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) {
this(type, null, targetId, sourceId, playerId, 0, false);
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, MageObjectReference reference) {
this(type, null, targetId, sourceId, playerId, 0, false, reference);
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(type, null, targetId, sourceId, playerId, amount, flag);
}
@ -369,6 +364,10 @@ public class GameEvent implements Serializable {
return new GameEvent(type, targetId, sourceId, playerId);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, MageObjectReference reference) {
return new GameEvent(type, targetId, sourceId, playerId, reference);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID playerId) {
return new GameEvent(type, targetId, null, playerId);
}
@ -399,6 +398,23 @@ public class GameEvent implements Serializable {
return event;
}
private GameEvent(EventType type, UUID customEventType,
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(type, customEventType, targetId, sourceId, playerId, amount, flag, null);
}
private GameEvent(EventType type, UUID customEventType,
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, MageObjectReference reference) {
this.type = type;
this.customEventType = customEventType;
this.targetId = targetId;
this.sourceId = sourceId;
this.amount = amount;
this.playerId = playerId;
this.flag = flag;
this.reference = reference;
}
public EventType getType() {
return type;
}
@ -455,6 +471,14 @@ public class GameEvent implements Serializable {
this.zone = zone;
}
public MageObjectReference getAdditionalReference() {
return reference;
}
public void setAdditionalReference(MageObjectReference additionalReference) {
this.reference = additionalReference;
}
/**
* used to store which replacement effects were already applied to an event
* or or any modified events that may replace it

View file

@ -152,7 +152,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.blocking = permanent.blocking;
this.maxBlocks = permanent.maxBlocks;
this.deathtouched = permanent.deathtouched;
// this.attachments.addAll(permanent.attachments);
for (Map.Entry<String, List<UUID>> entry : permanent.connectedCards.entrySet()) {
this.connectedCards.put(entry.getKey(), entry.getValue());
}
@ -696,7 +696,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
for (Iterator<Effect> ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext();) {
ContinuousEffect effect = (ContinuousEffect) ite.next();
game.getContinuousEffects().setOrder(effect);
// It's important is to update timestamp of the copied effect in ContinuousEffects because it does the action
// It's important to update the timestamp of the copied effect in ContinuousEffects because it does the action
for (ContinuousEffect conEffect : game.getContinuousEffects().getLayeredEffects(game)) {
if (conEffect.getId().equals(effect.getId())) {
game.getContinuousEffects().setOrder(conEffect);
@ -935,20 +935,20 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public boolean canBeTargetedBy(MageObject source, UUID sourceControllerId, Game game) {
if (source != null) {
if (abilities.containsKey(ShroudAbility.getInstance().getId())) {
if (null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.SHROUD, sourceControllerId, game)) {
if (null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.SHROUD, null, sourceControllerId, game)) {
return false;
}
}
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, sourceControllerId, game)) {
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) {
return false;
}
}
if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) {
if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
&& source.getColor(game).isBlack()) {
return false;
}
@ -956,7 +956,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) {
if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
&& source.getColor(game).isWhite()) {
return false;
}
@ -1096,7 +1096,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean canAttackInPrinciple(UUID defenderId, Game game) {
if (hasSummoningSickness()
&& null == game.getContinuousEffects().asThough(this.objectId, AsThoughEffectType.ATTACK_AS_HASTE, this.getControllerId(), game)) {
&& null == game.getContinuousEffects().asThough(this.objectId, AsThoughEffectType.ATTACK_AS_HASTE, null, this.getControllerId(), game)) {
return false;
}
//20101001 - 508.1c
@ -1116,7 +1116,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
return !abilities.containsKey(DefenderAbility.getInstance().getId())
|| null != game.getContinuousEffects().asThough(this.objectId, AsThoughEffectType.ATTACK, this.getControllerId(), game);
|| null != game.getContinuousEffects().asThough(this.objectId, AsThoughEffectType.ATTACK, null, this.getControllerId(), game);
}
private boolean canAttackCheckRestrictionEffects(UUID defenderId, Game game) {
@ -1136,7 +1136,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean canBlock(UUID attackerId, Game game) {
if (tapped && null == game.getState().getContinuousEffects().asThough(this.getId(), AsThoughEffectType.BLOCK_TAPPED, this.getControllerId(), game)) {
if (tapped && null == game.getState().getContinuousEffects().asThough(this.getId(), AsThoughEffectType.BLOCK_TAPPED, null, this.getControllerId(), game)) {
return false;
}
Permanent attacker = game.getPermanent(attackerId);
@ -1169,7 +1169,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean canBlockAny(Game game) {
if (tapped && null == game.getState().getContinuousEffects().asThough(this.getId(), AsThoughEffectType.BLOCK_TAPPED, this.getControllerId(), game)) {
if (tapped && null == game.getState().getContinuousEffects().asThough(this.getId(), AsThoughEffectType.BLOCK_TAPPED, null, this.getControllerId(), game)) {
return false;
}

View file

@ -36,6 +36,7 @@ import java.util.Set;
import java.util.UUID;
import mage.MageItem;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
@ -359,7 +360,7 @@ public interface Player extends MageItem, Copyable<Player> {
int drawCards(int num, Game game, List<UUID> appliedEffects);
boolean cast(SpellAbility ability, Game game, boolean noMana);
boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference reference);
SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana);
@ -399,7 +400,7 @@ public interface Player extends MageItem, Copyable<Player> {
* to be the turn of the player playing that card.
* @return
*/
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming);
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference);
/**
*

View file

@ -33,8 +33,10 @@ import java.util.*;
import java.util.Map.Entry;
import mage.ConditionalMana;
import mage.MageObject;
import mage.MageObjectReference;
import mage.Mana;
import mage.abilities.*;
import mage.abilities.ActivatedAbility.ActivationStatus;
import mage.abilities.common.PassAbility;
import mage.abilities.common.WhileSearchingPlayFromLibraryAbility;
import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility;
@ -610,7 +612,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, this.getId(), game)) {
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, this.getId(), game)) {
return false;
}
}
@ -1010,7 +1012,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming) {
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference) {
if (card == null) {
return false;
}
@ -1018,7 +1020,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (card.isLand()) {
result = playLand(card, game, ignoreTiming);
} else {
result = cast(card.getSpellAbility(), game, noMana);
result = cast(card.getSpellAbility(), game, noMana, reference);
}
if (!result) {
game.informPlayer(this, "You can't play " + card.getIdName() + '.');
@ -1027,7 +1029,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean cast(SpellAbility ability, Game game, boolean noMana) {
public boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference permittingObject) {
if (game == null || ability == null) {
return false;
}
@ -1046,7 +1048,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
Card card = game.getCard(ability.getSourceId());
if (card != null) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId), ability)) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId, permittingObject), ability)) {
int bookmark = game.bookmarkState();
Zone fromZone = game.getState().getZone(card.getMainCard().getId());
card.cast(game, fromZone, ability, playerId);
@ -1074,10 +1076,10 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
setCastSourceIdWithAlternateMana(null, null, null);
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId);
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
game.fireEvent(event);
if (spell.activate(game, noMana)) {
event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId);
event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
event.setZone(fromZone);
game.fireEvent(event);
if (!game.isSimulation()) {
@ -1116,7 +1118,7 @@ public abstract class PlayerImpl implements Player, Serializable {
SpellAbility spellAbility = new SpellAbility(null, "", game.getState().getZone(card.getId()), SpellAbilityType.FACE_DOWN_CREATURE);
spellAbility.setControllerId(this.getId());
spellAbility.setSourceId(card.getId());
if (cast(spellAbility, game, false)) {
if (cast(spellAbility, game, false, null)) {
return true;
}
}
@ -1124,17 +1126,18 @@ public abstract class PlayerImpl implements Player, Serializable {
return false;
}
//20091005 - 114.2a
if (!ignoreTiming && !playLandAbility.canActivate(this.playerId, game)) {
ActivationStatus activationStatus = playLandAbility.canActivate(this.playerId, game);
if (!ignoreTiming && !activationStatus.canActivate()) {
return false;
}
//20091005 - 305.1
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
// int bookmark = game.bookmarkState();
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
landsPlayed++;
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
// game.removeBookmark(bookmark);
resetStoredBookmark(game); // prevent undo after playing a land
@ -1239,7 +1242,8 @@ public abstract class PlayerImpl implements Player, Serializable {
Card card = game.getCard(ability.getSourceId());
result = playLand(card, game, false);
} else {
if (!ability.canActivate(this.playerId, game)) {
ActivationStatus activationStatus = ability.canActivate(this.playerId, game);
if (!activationStatus.canActivate()) {
return false;
}
@ -1251,7 +1255,7 @@ public abstract class PlayerImpl implements Player, Serializable {
result = playManaAbility((ActivatedManaAbilityImpl) ability.copy(), game);
break;
case SPELL:
result = cast((SpellAbility) ability.copy(), game, false);
result = cast((SpellAbility) ability.copy(), game, false, activationStatus.getPermittingObject());
break;
default:
result = playAbility(ability.copy(), game);
@ -1357,7 +1361,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (ability.getZone().match(zone)) {
if (ability instanceof ActivatedAbility) {
if (ability instanceof ActivatedManaAbilityImpl) {
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
output.put(ability.getId(), (ActivatedAbility) ability);
}
} else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) {
@ -1379,19 +1383,19 @@ public abstract class PlayerImpl implements Player, Serializable {
if (Zone.GRAVEYARD == zone && canPlayCardsFromGraveyard()) {
for (ActivatedAbility ability : candidateAbilites.getPlayableAbilities(Zone.HAND)) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.canActivate(playerId, game)) {
if (ability.canActivate(playerId, game).canActivate()) {
output.put(ability.getId(), ability);
}
}
}
}
if (zone != Zone.BATTLEFIELD
&& null != game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
&& null != game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
for (Ability ability : candidateAbilites) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
ability.setControllerId(this.getId());
if (ability instanceof ActivatedAbility && ability.getZone().match(Zone.HAND)
&& ((ActivatedAbility) ability).canActivate(playerId, game)) {
&& ((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
output.put(ability.getId(), (ActivatedAbility) ability);
}
}
@ -1439,7 +1443,7 @@ public abstract class PlayerImpl implements Player, Serializable {
flashbackAbility.setControllerId(card.getOwnerId());
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT);
flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName());
if (flashbackAbility.canActivate(playerId, game)) {
if (flashbackAbility.canActivate(playerId, game).canActivate()) {
useable.put(flashbackAbility.getId(), flashbackAbility);
}
// Right Half
@ -1452,7 +1456,7 @@ public abstract class PlayerImpl implements Player, Serializable {
flashbackAbility.setControllerId(card.getOwnerId());
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT);
flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName());
if (flashbackAbility.canActivate(playerId, game)) {
if (flashbackAbility.canActivate(playerId, game).canActivate()) {
useable.put(flashbackAbility.getId(), flashbackAbility);
}
@ -1469,7 +1473,7 @@ public abstract class PlayerImpl implements Player, Serializable {
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
for (ActivatedManaAbilityImpl ability : object.getAbilities().getActivatedManaAbilities(zone)) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.canActivate(playerId, game)) {
if (ability.canActivate(playerId, game).canActivate()) {
useable.put(ability.getId(), ability);
}
}
@ -2477,7 +2481,7 @@ public abstract class PlayerImpl implements Player, Serializable {
Card card = game.getCard(entry.getKey());
if (card != null) {
// TODO: fix costs (why is Panglacial Wurm automatically accepting payment?)
player.cast(card.getSpellAbility(), game, false);
player.cast(card.getSpellAbility(), game, false, null);
}
chooseCard.clearChoice();
libraryCastableCardTracker.clear();
@ -2633,7 +2637,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game);
}
if (canUse && ability.canActivate(playerId, game)) {
if (canUse && ability.canActivate(playerId, game).canActivate()) {
canAdd = true;
if (!ability.getManaCosts().isEmpty()) {
withCost = true;
@ -2677,7 +2681,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game);
}
if (canUse && ability.canActivate(playerId, game)) {
if (canUse && ability.canActivate(playerId, game).canActivate()) {
canAdd = true;
}
}
@ -2692,7 +2696,7 @@ public abstract class PlayerImpl implements Player, Serializable {
canAdd = false;
break;
}
if (ability.canActivate(playerId, game)) {
if (ability.canActivate(playerId, game).canActivate()) {
canAdd = true;
}
}
@ -2712,7 +2716,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game);
}
if (canUse && ability.canActivate(playerId, game) && !ability.getManaCosts().isEmpty()) {
if (canUse && ability.canActivate(playerId, game).canActivate() && !ability.getManaCosts().isEmpty()) {
result.add(permanent);
break;
}
@ -2732,7 +2736,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (!(ability instanceof ActivatedManaAbilityImpl)) {
ActivatedAbility copy = ability.copy();
copy.setCheckPlayableMode(); // prevents from endless loops for asking player to use effects by checking this mode
if (!copy.canActivate(playerId, game)) {
if (!copy.canActivate(playerId, game).canActivate()) {
return false;
}
if (available != null) {
@ -2765,10 +2769,10 @@ public abstract class PlayerImpl implements Player, Serializable {
if (available == null) {
return true;
}
UUID spendAnyManaSourceId = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
for (Mana mana : abilityOptions) {
for (Mana avail : available) {
if (spendAnyManaSourceId != null && mana.count() <= avail.count()) {
if (permittingObject != null && mana.count() <= avail.count()) {
return true;
}
if (mana.enough(avail)) { // here we need to check if spend mana as though allow to pay the mana cost
@ -2893,13 +2897,13 @@ public abstract class PlayerImpl implements Player, Serializable {
}
private void getPlayableFromGraveyardCard(Game game, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
UUID asThoughtCastSourceId = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game);
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game);
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
boolean possible = false;
if (ability.getZone().match(Zone.GRAVEYARD)) {
possible = true;
} else if (ability.getZone().match(Zone.HAND) && (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) {
if (asThoughtCastSourceId != null || canPlayCardsFromGraveyard()) {
if (permittingObject != null || canPlayCardsFromGraveyard()) {
possible = true;
}
}
@ -2968,12 +2972,12 @@ public abstract class PlayerImpl implements Player, Serializable {
}
for (ExileZone exile : game.getExile().getExileZones()) {
for (Card card : exile.getCards(game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
for (Ability ability : card.getAbilities()) {
if (ability.getZone().match(Zone.HAND)) {
ability.setControllerId(this.getId()); // controller must be set for case owner != caster
if (ability instanceof ActivatedAbility) {
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
playable.add(ability);
}
}
@ -2986,7 +2990,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// Check to play revealed cards
for (Cards cards : game.getState().getRevealed().values()) {
for (Card card : cards.getCards(game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
playable.add(ability);
@ -3001,7 +3005,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (player != null) {
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
Card card = player.getLibrary().getFromTop(game);
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, getId(), game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
playable.add(ability);
@ -3354,7 +3358,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean lookAtFaceDownCard(Card card, Game game
) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, this.getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, card.getSpellAbility(), this.getId(), game)) {
if (chooseUse(Outcome.Benefit, "Look at that card?", null, game)) {
Cards cards = new CardsImpl(card);
this.lookAtCards(getName() + " - " + sdf.format(System.currentTimeMillis()), cards, game);

View file

@ -24,8 +24,7 @@
* 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.target.common;
import mage.constants.Zone;
@ -36,6 +35,7 @@ import mage.game.Game;
import mage.target.TargetCard;
import java.util.UUID;
import mage.filter.StaticFilters;
import mage.filter.predicate.other.OwnerIdPredicate;
/**
@ -47,7 +47,7 @@ public class TargetDiscard extends TargetCard {
private final UUID playerId;
public TargetDiscard(UUID playerId) {
this(1, 1, new FilterCard(), playerId);
this(1, 1, StaticFilters.FILTER_CARD, playerId);
}
public TargetDiscard(FilterCard filter, UUID playerId) {

View file

@ -8,8 +8,12 @@ import java.util.concurrent.ThreadLocalRandom;
*/
public final class RandomUtil {
private RandomUtil() {
}
public static Random getRandom() {return ThreadLocalRandom.current();}
public static Random getRandom() {
return ThreadLocalRandom.current();
}
public static int nextInt() {
return ThreadLocalRandom.current().nextInt();

View file

@ -34,6 +34,7 @@ import java.util.UUID;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.watchers.Watcher;
/*
@ -81,8 +82,9 @@ public class PlayerLostLifeWatcher extends Watcher {
public int getAllOppLifeLost(UUID playerId, Game game) {
int amount = 0;
for (UUID opponentId : this.amountOfLifeLostThisTurn.keySet()) {
if (game.getOpponents(playerId).contains(opponentId)) {
amount += this.amountOfLifeLostThisTurn.get(playerId);
Player opponent = game.getPlayer(opponentId);
if (opponent != null && opponent.hasOpponent(playerId, game)) {
amount += this.amountOfLifeLostThisTurn.getOrDefault(opponentId, 0);
}
}
return amount;