Fixed a problem with casting split cards from non hand zone. Added some generic cast from non hand zone effects and started replacing card specific effects by the generic ones (fixes #5356 and fixes #4493).

This commit is contained in:
LevelX2 2018-10-03 13:44:01 +02:00
parent c097ec053f
commit c41c6e1fe9
25 changed files with 304 additions and 476 deletions

View file

@ -13,6 +13,7 @@ import mage.abilities.keyword.SpliceOntoArcaneAbility;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.cards.SplitCardHalf;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicate;
@ -488,15 +489,23 @@ public class ContinuousEffects implements Serializable {
*/
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)) {
if (!asThoughEffectsList.isEmpty()) {
UUID idToCheck;
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
} else {
idToCheck = objectId;
}
for (AsThoughEffect effect : asThoughEffectsList) {
Set<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
for (Ability ability : abilities) {
if (affectedAbility == null) {
if (effect.applies(idToCheck, ability, controllerId, game)) {
return new MageObjectReference(ability.getSourceObject(game), game);
}
} else if (effect.applies(idToCheck, affectedAbility, ability, game, controllerId)) {
return new MageObjectReference(ability.getSourceObject(game), game);
}
} else if (effect.applies(objectId, affectedAbility, ability, game, controllerId)) {
return new MageObjectReference(ability.getSourceObject(game), game);
}
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
@ -39,7 +38,9 @@ public class AttachEffect extends OneShotEffect {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent != null) {
int zcc = game.getState().getZoneChangeCounter(sourcePermanent.getId());
if (zcc == source.getSourceObjectZoneChangeCounter() || zcc == source.getSourceObjectZoneChangeCounter() + 1) {
if (zcc == source.getSourceObjectZoneChangeCounter()
|| zcc == source.getSourceObjectZoneChangeCounter() + 1
|| zcc == source.getSourceObjectZoneChangeCounter() + 2) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent != null) {
return permanent.addAttachment(source.getSourceId(), game);

View file

@ -0,0 +1,74 @@
package mage.abilities.effects.common.asthought;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.cards.Card;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
/**
*
* @author LevelX2
*/
public class PlayFromNotOwnHandZoneAllEffect extends AsThoughEffectImpl {
private final FilterCard filter;
private final Zone fromZone;
private final boolean onlyOwnedCards;
private final TargetController allowedCaster;
public PlayFromNotOwnHandZoneAllEffect(FilterCard filter, Zone fromZone, boolean onlyOwnedCards, TargetController allowedCaster, Duration duration) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.filter = filter;
this.fromZone = fromZone;
this.onlyOwnedCards = onlyOwnedCards;
this.allowedCaster = allowedCaster;
}
public PlayFromNotOwnHandZoneAllEffect(final PlayFromNotOwnHandZoneAllEffect effect) {
super(effect);
this.filter = effect.filter;
this.fromZone = effect.fromZone;
this.onlyOwnedCards = effect.onlyOwnedCards;
this.allowedCaster = effect.allowedCaster;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public PlayFromNotOwnHandZoneAllEffect copy() {
return new PlayFromNotOwnHandZoneAllEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Card card = game.getCard(objectId);
if (card != null) {
switch (allowedCaster) {
case YOU:
if (affectedControllerId != source.getControllerId()) {
return false;
}
break;
case OPPONENT:
if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
return false;
}
break;
}
return !onlyOwnedCards || card.getOwnerId().equals(source.getControllerId())
&& filter.match(card, game)
&& game.getState().getZone(card.getId()).match(fromZone);
}
return false;
}
}

View file

@ -0,0 +1,82 @@
package mage.abilities.effects.common.asthought;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.game.Game;
/**
*
* @author LevelX2
*/
public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
private final Zone fromZone;
private final TargetController allowedCaster;
public PlayFromNotOwnHandZoneTargetEffect() {
this(Duration.EndOfTurn);
}
public PlayFromNotOwnHandZoneTargetEffect(Duration duration) {
this(Zone.ALL, TargetController.YOU, duration);
}
public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, Duration duration) {
this(fromZone, TargetController.YOU, duration);
}
public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.fromZone = fromZone;
this.allowedCaster = allowedCaster;
}
public PlayFromNotOwnHandZoneTargetEffect(final PlayFromNotOwnHandZoneTargetEffect effect) {
super(effect);
this.fromZone = effect.fromZone;
this.allowedCaster = effect.allowedCaster;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public PlayFromNotOwnHandZoneTargetEffect copy() {
return new PlayFromNotOwnHandZoneTargetEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
switch (allowedCaster) {
case YOU:
if (affectedControllerId != source.getControllerId()) {
return false;
}
break;
case OPPONENT:
if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
return false;
}
break;
case ANY:
break;
}
List<UUID> targets = getTargetPointer().getTargets(game, source);
if (targets.isEmpty()) {
this.discard();
return false;
}
return targets.contains(objectId)
&& affectedControllerId.equals(source.getControllerId())
&& game.getState().getZone(objectId).match(fromZone);
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.keyword;
import java.util.UUID;
@ -68,16 +67,19 @@ class AftermathCastFromGraveyard extends AsThoughEffectImpl {
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (objectId.equals(source.getSourceId())
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID affectedControllerId) {
if (affectedAbility != null && affectedAbility.getSourceId().equals(source.getSourceId())
&& affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(source.getSourceId());
if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
return true;
}
return game.getState().getZone(objectId).equals(Zone.GRAVEYARD);
}
return false;
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return false;
}
}
class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl {
@ -168,17 +170,14 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
UUID sourceId = source.getSourceId();
Card sourceCard = game.getCard(source.getSourceId());
if (sourceCard instanceof SplitCardHalf) {
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
sourceId = sourceCard.getId();
}
if (sourceCard != null) {
Player player = game.getPlayer(sourceCard.getOwnerId());
if (player != null) {
return player.moveCardToExileWithInfo(sourceCard, null, "", sourceId, game, ((ZoneChangeEvent) event).getFromZone(), true);
return player.moveCards(sourceCard, Zone.EXILED, source, game);
}
}
return false;

View file

@ -1,5 +1,9 @@
package mage.players;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import mage.ConditionalMana;
import mage.MageObject;
import mage.MageObjectReference;
@ -68,11 +72,6 @@ import mage.util.GameLog;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
public abstract class PlayerImpl implements Player, Serializable {
private static final Logger logger = Logger.getLogger(PlayerImpl.class);
@ -2563,7 +2562,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param game
* @param appliedEffects
* @param numSides Number of sides the dice has
* @param numSides Number of sides the dice has
* @return the number that the player rolled
*/
@Override
@ -2597,10 +2596,10 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param game
* @param appliedEffects
* @param numberChaosSides The number of chaos sides the planar die
* currently has (normally 1 but can be 5)
* @param numberChaosSides The number of chaos sides the planar die
* currently has (normally 1 but can be 5)
* @param numberPlanarSides The number of chaos sides the planar die
* currently has (normally 1)
* currently has (normally 1)
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
* or NilRoll
*/
@ -2757,7 +2756,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param ability
* @param available if null, it won't be checked if enough mana is available
* @param available if null, it won't be checked if enough mana is available
* @param sourceObject
* @param game
* @return
@ -2927,7 +2926,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
private void getPlayableFromGraveyardCard(Game game, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game);
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game);
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
boolean possible = false;
if (ability.getZone().match(Zone.GRAVEYARD)) {
@ -3033,7 +3032,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, null, getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), getId(), game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
playable.add(ability);
@ -3309,7 +3308,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
UUID controllerId, Game game
UUID controllerId, Game game
) {
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
}
@ -3457,8 +3456,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Card card, Zone toZone,
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) {
Set<Card> cardList = new HashSet<>();
if (card != null) {
@ -3469,22 +3468,22 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Cards cards, Zone toZone,
Ability source, Game game
Ability source, Game game
) {
return moveCards(cards.getCards(game), toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game
Ability source, Game game
) {
return moveCards(cards, toZone, source, game, false, false, false, null);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) {
if (cards.isEmpty()) {
return true;
@ -3570,8 +3569,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardsToExile(Card card, Ability source,
Game game, boolean withName, UUID exileId,
String exileZoneName
Game game, boolean withName, UUID exileId,
String exileZoneName
) {
Set<Card> cards = new HashSet<>();
cards.add(card);
@ -3580,8 +3579,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardsToExile(Set<Card> cards, Ability source,
Game game, boolean withName, UUID exileId,
String exileZoneName
Game game, boolean withName, UUID exileId,
String exileZoneName
) {
if (cards.isEmpty()) {
return true;
@ -3596,14 +3595,14 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
Game game
Game game
) {
return this.moveCardToHandWithInfo(card, sourceId, game, true);
}
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
Game game, boolean withName
Game game, boolean withName
) {
boolean result = false;
Zone fromZone = game.getState().getZone(card.getId());
@ -3628,7 +3627,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
Game game, Zone fromZone
Game game, Zone fromZone
) {
UUID sourceId = source == null ? null : source.getSourceId();
Set<Card> movedCards = new LinkedHashSet<>();
@ -3636,7 +3635,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// identify cards from one owner
Cards cards = new CardsImpl();
UUID ownerId = null;
for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
Card card = it.next();
if (cards.isEmpty()) {
ownerId = card.getOwnerId();
@ -3697,7 +3696,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
Game game, Zone fromZone
Game game, Zone fromZone
) {
if (card == null) {
return false;
@ -3726,8 +3725,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
Game game, Zone fromZone,
boolean toTop, boolean withName
Game game, Zone fromZone,
boolean toTop, boolean withName
) {
if (card == null) {
return false;
@ -3761,7 +3760,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
Game game, Zone fromZone, boolean withName) {
Game game, Zone fromZone, boolean withName) {
if (card == null) {
return false;
}

View file

@ -21,6 +21,12 @@ public class FixedTarget implements TargetPointer {
this.initialized = false;
}
public FixedTarget(Card card, Game game) {
this.targetId = card.getId();
this.zoneChangeCounter = card.getZoneChangeCounter(game);
this.initialized = true;
}
public FixedTarget(Permanent permanent, Game game) {
this.targetId = permanent.getId();
this.zoneChangeCounter = permanent.getZoneChangeCounter(game);