mirror of
https://github.com/magefree/mage.git
synced 2025-12-27 05:52:06 -08:00
Added modal double faces cards implementation (MDF cards, #7012)
This commit is contained in:
parent
bbed5a16b8
commit
8ac78b4b9e
60 changed files with 1128 additions and 764 deletions
|
|
@ -3,13 +3,13 @@ package mage.abilities.effects;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -22,7 +22,7 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome) {
|
||||
this(type, duration, outcome, false);
|
||||
}
|
||||
|
||||
|
||||
public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome, boolean consumable) {
|
||||
super(duration, outcome);
|
||||
this.type = type;
|
||||
|
|
@ -74,12 +74,12 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
* Internal method to do the neccessary to allow the card from objectId to be cast or played (if it's a land) without paying any mana.
|
||||
* Additional costs (like sacrificing or discarding) have still to be payed.
|
||||
* Checks if the card is of the correct type or in the correct zone have to be done before.
|
||||
*
|
||||
* @param objectId sourceId of the card to play
|
||||
* @param source source ability that allows this effect
|
||||
*
|
||||
* @param objectId sourceId of the card to play
|
||||
* @param source source ability that allows this effect
|
||||
* @param affectedControllerId player allowed to play the card
|
||||
* @param game
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Player player = game.getPlayer(affectedControllerId);
|
||||
|
|
@ -89,9 +89,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
}
|
||||
if (!card.isLand()) {
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
Card leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());
|
||||
SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
Card rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts());
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
Card leftCard = ((ModalDoubleFacesCard) card).getLeftHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());
|
||||
Card rightCard = ((ModalDoubleFacesCard) card).getRightHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts());
|
||||
} else {
|
||||
player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.MageSingleton;
|
||||
|
|
@ -7,6 +8,8 @@ import mage.abilities.StaticAbility;
|
|||
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
||||
import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
|
||||
import mage.cards.*;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -29,9 +32,6 @@ import java.io.Serializable;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.ApprovingObject;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceImpl;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -334,7 +334,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @param event
|
||||
* @param game
|
||||
* @return a list of all {@link ReplacementEffect} that apply to the current
|
||||
* event
|
||||
* event
|
||||
*/
|
||||
private Map<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
|
||||
Map<ReplacementEffect, Set<Ability>> replaceEffects = new HashMap<>();
|
||||
|
|
@ -343,7 +343,7 @@ 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 (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext(); ) {
|
||||
ReplacementEffect effect = iterator.next();
|
||||
if (!effect.checksEventType(event, game)) {
|
||||
continue;
|
||||
|
|
@ -376,7 +376,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
|
||||
PreventionEffect effect = iterator.next();
|
||||
if (!effect.checksEventType(event, game)) {
|
||||
continue;
|
||||
|
|
@ -507,7 +507,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @param controllerId
|
||||
* @param game
|
||||
* @return sourceId of the permitting effect if any exists otherwise returns
|
||||
* null
|
||||
* null
|
||||
*/
|
||||
public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
|
||||
|
|
@ -515,6 +515,8 @@ public class ContinuousEffects implements Serializable {
|
|||
UUID idToCheck;
|
||||
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof ModalDoubleFacesCardHalf) {
|
||||
idToCheck = ((ModalDoubleFacesCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell
|
||||
&& !type.needPlayCardAbility()) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
|
|
@ -523,6 +525,8 @@ public class ContinuousEffects implements Serializable {
|
|||
Card card = game.getCard(objectId);
|
||||
if (card instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof ModalDoubleFacesCardHalf) {
|
||||
idToCheck = ((ModalDoubleFacesCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof AdventureCardSpell
|
||||
&& !type.needPlayCardAbility()) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
|
|
@ -568,24 +572,24 @@ public class ContinuousEffects implements Serializable {
|
|||
} else if (possibleApprovingObjects.size() > 1) {
|
||||
// Select the ability that you use to permit the action
|
||||
Map<String, String> keyChoices = new HashMap<>();
|
||||
for(ApprovingObject approvingObject :possibleApprovingObjects) {
|
||||
MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId());
|
||||
keyChoices.put(approvingObject.getApprovingAbility().getId().toString(),
|
||||
for (ApprovingObject approvingObject : possibleApprovingObjects) {
|
||||
MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId());
|
||||
keyChoices.put(approvingObject.getApprovingAbility().getId().toString(),
|
||||
(approvingObject.getApprovingAbility().getRule(mageObject == null ? "" : mageObject.getName()))
|
||||
+ (mageObject == null ? "" : " (" + mageObject.getIdName() + ")"));
|
||||
+ (mageObject == null ? "" : " (" + mageObject.getIdName() + ")"));
|
||||
}
|
||||
Choice choicePermitting = new ChoiceImpl(true);
|
||||
choicePermitting.setMessage("Choose the permitting object");
|
||||
choicePermitting.setKeyChoices(keyChoices);
|
||||
Player player = game.getPlayer(controllerId);
|
||||
player.choose(Outcome.Detriment, choicePermitting, game);
|
||||
for(ApprovingObject approvingObject: possibleApprovingObjects) {
|
||||
for (ApprovingObject approvingObject : possibleApprovingObjects) {
|
||||
if (approvingObject.getApprovingAbility().getId().toString().equals(choicePermitting.getChoiceKey())) {
|
||||
return approvingObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return null;
|
||||
|
||||
|
|
@ -833,7 +837,7 @@ public class ContinuousEffects implements Serializable {
|
|||
do {
|
||||
Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
|
||||
// Remove all consumed effects (ability dependant)
|
||||
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
|
||||
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
|
||||
ReplacementEffect entry = it1.next();
|
||||
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
|
||||
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
|
||||
|
|
@ -1018,7 +1022,7 @@ public class ContinuousEffects implements Serializable {
|
|||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> dependentTo.contains(entry.getKey().getId())
|
||||
&& entry.getValue().contains(effect.getId()))
|
||||
&& entry.getValue().contains(effect.getId()))
|
||||
.forEach(entry -> {
|
||||
entry.getValue().remove(effect.getId());
|
||||
dependentTo.remove(entry.getKey().getId());
|
||||
|
|
@ -1052,7 +1056,7 @@ public class ContinuousEffects implements Serializable {
|
|||
continue;
|
||||
}
|
||||
// check if waiting effects can be applied now
|
||||
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
|
||||
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
|
||||
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
|
||||
if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import mage.abilities.condition.common.SourceIsSpellCondition;
|
|||
import mage.abilities.costs.AlternativeCostSourceAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCardHalf;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
|
|
@ -21,7 +22,7 @@ import java.util.UUID;
|
|||
public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl {
|
||||
|
||||
private final AlternativeCostSourceAbility alternativeCastingCostAbility;
|
||||
|
||||
|
||||
public CastFromHandWithoutPayingManaCostEffect() {
|
||||
this(StaticFilters.FILTER_CARDS_NON_LAND, true);
|
||||
}
|
||||
|
|
@ -37,7 +38,7 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
|
|||
condition = new CompoundCondition(SourceIsSpellCondition.instance, IsBeingCastFromHandCondition.instance);
|
||||
} else {
|
||||
condition = SourceIsSpellCondition.instance;
|
||||
}
|
||||
}
|
||||
this.alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, condition, null, filter, true);
|
||||
this.staticText = "You may cast " + filter.getMessage()
|
||||
+ (fromHand ? " from your hand" : "")
|
||||
|
|
@ -87,9 +88,9 @@ enum IsBeingCastFromHandCondition implements Condition {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
MageObject object = game.getObject(source.getSourceId());
|
||||
if (object instanceof SplitCardHalf) {
|
||||
UUID splitCardId = ((Card) object).getMainCard().getId();
|
||||
object = game.getObject(splitCardId);
|
||||
if (object instanceof SplitCardHalf || object instanceof ModalDoubleFacesCardHalf) {
|
||||
UUID mainCardId = ((Card) object).getMainCard().getId();
|
||||
object = game.getObject(mainCardId);
|
||||
}
|
||||
if (object instanceof Spell) { // needed to check if it can be cast by alternate cost
|
||||
Spell spell = (Spell) object;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,31 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCardHalf;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.*;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Aftermath
|
||||
*
|
||||
* <p>
|
||||
* TODO: Implement once we get details on the comprehensive rules meaning of the
|
||||
* ability
|
||||
*
|
||||
* <p>
|
||||
* Current text is a shell copied from Flashback
|
||||
*
|
||||
* @author stravant
|
||||
|
|
@ -113,9 +120,7 @@ class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl {
|
|||
Card card = game.getCard(event.getSourceId());
|
||||
if (card != null && card.getId().equals(source.getSourceId())) {
|
||||
Zone zone = game.getState().getZone(card.getId());
|
||||
if (zone != null && (zone != Zone.GRAVEYARD)) {
|
||||
return true;
|
||||
}
|
||||
return zone != null && (zone != Zone.GRAVEYARD);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -155,14 +160,16 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
|
|||
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
|
||||
sourceId = sourceCard.getId();
|
||||
}
|
||||
if (sourceCard instanceof ModalDoubleFacesCardHalf) {
|
||||
sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard();
|
||||
sourceId = sourceCard.getId();
|
||||
}
|
||||
|
||||
if (event.getTargetId().equals(sourceId)) {
|
||||
// Moving this spell from stack to yard
|
||||
Spell spell = game.getStack().getSpell(source.getSourceId());
|
||||
if (spell != null && spell.getFromZone() == Zone.GRAVEYARD) {
|
||||
// And this spell was cast from the graveyard, so we need to exile it
|
||||
return true;
|
||||
}
|
||||
// And this spell was cast from the graveyard, so we need to exile it
|
||||
return spell != null && spell.getFromZone() == Zone.GRAVEYARD;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -174,6 +181,9 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
|
|||
if (sourceCard instanceof SplitCardHalf) {
|
||||
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
|
||||
}
|
||||
if (sourceCard instanceof ModalDoubleFacesCardHalf) {
|
||||
sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard();
|
||||
}
|
||||
if (sourceCard != null) {
|
||||
Player player = game.getPlayer(sourceCard.getOwnerId());
|
||||
if (player != null) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.abilities.costs.Costs;
|
|||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
|
|
@ -68,12 +69,18 @@ public class FlashbackAbility extends SpellAbility {
|
|||
}
|
||||
// Flashback can never cast a split card by Fuse, because Fuse only works from hand
|
||||
// https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
return ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
} else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
return ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
}
|
||||
return card.getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
|
|
@ -87,12 +94,18 @@ public class FlashbackAbility extends SpellAbility {
|
|||
if (card != null) {
|
||||
if (spellAbilityToResolve == null) {
|
||||
SpellAbility spellAbilityCopy = null;
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||
}
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||
} else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||
}
|
||||
} else {
|
||||
spellAbilityCopy = card.getSpellAbility().copy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* 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.cards;
|
||||
|
||||
import mage.abilities.Modes;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Abilities;
|
||||
|
|
@ -16,6 +14,9 @@ import mage.game.Game;
|
|||
import mage.game.GameState;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Card extends MageObject {
|
||||
|
||||
UUID getOwnerId();
|
||||
|
|
@ -29,8 +30,9 @@ public interface Card extends MageObject {
|
|||
/**
|
||||
* For cards: return all basic and dynamic abilities
|
||||
* For permanents: return all basic and dynamic abilities
|
||||
*
|
||||
* @param game
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
Abilities<Ability> getAbilities(Game game);
|
||||
|
||||
|
|
@ -62,8 +64,6 @@ public interface Card extends MageObject {
|
|||
|
||||
String getFlipCardName();
|
||||
|
||||
boolean isSplitCard();
|
||||
|
||||
boolean isTransformable();
|
||||
|
||||
void setTransformable(boolean transformable);
|
||||
|
|
@ -76,21 +76,23 @@ public interface Card extends MageObject {
|
|||
|
||||
void addInfo(String key, String value, Game game);
|
||||
|
||||
// WARNING, don't add new move/remove methods (if you add then you must override it in all multi-part cards like Split or MDF)
|
||||
|
||||
/**
|
||||
* Moves the card to the specified zone
|
||||
*
|
||||
* @param zone
|
||||
* @param sourceId
|
||||
* @param game
|
||||
* @param flag If zone
|
||||
* <ul>
|
||||
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
|
||||
* bottom</li></ul></li>
|
||||
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
|
||||
* untapped</li></ul></li>
|
||||
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
|
||||
* Battlefield</li></ul></li>
|
||||
* </ul>
|
||||
* @param flag If zone
|
||||
* <ul>
|
||||
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
|
||||
* bottom</li></ul></li>
|
||||
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
|
||||
* untapped</li></ul></li>
|
||||
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
|
||||
* Battlefield</li></ul></li>
|
||||
* </ul>
|
||||
* @return true if card was moved to zone
|
||||
*/
|
||||
boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag);
|
||||
|
|
@ -100,8 +102,8 @@ public interface Card extends MageObject {
|
|||
/**
|
||||
* Moves the card to an exile zone
|
||||
*
|
||||
* @param exileId set to null for generic exile zone
|
||||
* @param name used for exile zone with the specified exileId
|
||||
* @param exileId set to null for generic exile zone
|
||||
* @param name used for exile zone with the specified exileId
|
||||
* @param sourceId
|
||||
* @param game
|
||||
* @return true if card was moved to zone
|
||||
|
|
@ -112,6 +114,7 @@ public interface Card extends MageObject {
|
|||
|
||||
boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId);
|
||||
|
||||
// WARNING, don't add new move/remove methods (if you add then you must override it in all multi-parts card like Split Half or MDF Half)
|
||||
boolean removeFromZone(Game game, Zone fromZone, UUID sourceId);
|
||||
|
||||
boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
protected boolean flipCard;
|
||||
protected String flipCardName;
|
||||
protected boolean usesVariousArt = false;
|
||||
protected boolean splitCard;
|
||||
protected boolean morphCard;
|
||||
protected boolean modalDFC; // modal double faces card
|
||||
|
||||
|
|
@ -139,7 +138,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
flipCard = card.flipCard;
|
||||
flipCardName = card.flipCardName;
|
||||
usesVariousArt = card.usesVariousArt;
|
||||
splitCard = card.splitCard;
|
||||
morphCard = card.morphCard;
|
||||
modalDFC = card.modalDFC;
|
||||
|
||||
|
|
@ -563,13 +561,22 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
stackObject = game.getStack().getSpell(this.getId(), false);
|
||||
}
|
||||
|
||||
if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack
|
||||
// handle half of Split Cards on stack
|
||||
if (stackObject == null && (this instanceof SplitCard)) {
|
||||
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false);
|
||||
if (stackObject == null) {
|
||||
stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
// handle half of Modal Double Faces Cards on stack
|
||||
if (stackObject == null && (this instanceof ModalDoubleFacesCard)) {
|
||||
stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getLeftHalfCard().getId(), false);
|
||||
if (stackObject == null) {
|
||||
stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getRightHalfCard().getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (stackObject == null && (this instanceof AdventureCard)) {
|
||||
stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false);
|
||||
}
|
||||
|
|
@ -687,10 +694,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
@Override
|
||||
public final Card getSecondCardFace() {
|
||||
// TODO: remove when MDFCs are implemented
|
||||
if (modalDFC) {
|
||||
return null;
|
||||
}
|
||||
// init second side card on first call
|
||||
if (secondSideCardClazz == null && secondSideCard == null) {
|
||||
return null;
|
||||
|
|
@ -726,11 +729,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
return flipCardName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSplitCard() {
|
||||
return splitCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUsesVariousArt() {
|
||||
return usesVariousArt;
|
||||
|
|
|
|||
184
Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java
Normal file
184
Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.AbilitiesImpl;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public abstract class ModalDoubleFacesCard extends CardImpl {
|
||||
|
||||
protected Card leftHalfCard;
|
||||
protected Card rightHalfCard;
|
||||
|
||||
public ModalDoubleFacesCard(UUID ownerId, CardSetInfo setInfo,
|
||||
CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft,
|
||||
String secondSideName, CardType[] typesRight, SubType[] subTypesRight, String costsRight) {
|
||||
super(ownerId, setInfo, typesLeft, costsLeft + costsRight, SpellAbilityType.MODAL);
|
||||
// main card name must be same as left side
|
||||
leftHalfCard = new ModalDoubleFacesCardHalfImpl(this.getOwnerId(), new CardSetInfo(setInfo.getName(), setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()),
|
||||
typesLeft, subTypesLeft, costsLeft, this, SpellAbilityType.MODAL_LEFT);
|
||||
rightHalfCard = new ModalDoubleFacesCardHalfImpl(this.getOwnerId(), new CardSetInfo(secondSideName, setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()),
|
||||
typesRight, subTypesRight, costsRight, this, SpellAbilityType.MODAL_RIGHT);
|
||||
this.modalDFC = true;
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCard(ModalDoubleFacesCard card) {
|
||||
super(card);
|
||||
this.leftHalfCard = card.getLeftHalfCard().copy();
|
||||
((ModalDoubleFacesCardHalf) leftHalfCard).setParentCard(this);
|
||||
this.rightHalfCard = card.rightHalfCard.copy();
|
||||
((ModalDoubleFacesCardHalf) rightHalfCard).setParentCard(this);
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCardHalf getLeftHalfCard() {
|
||||
return (ModalDoubleFacesCardHalf) leftHalfCard;
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCardHalf getRightHalfCard() {
|
||||
return (ModalDoubleFacesCardHalf) rightHalfCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignNewId() {
|
||||
super.assignNewId();
|
||||
leftHalfCard.assignNewId();
|
||||
rightHalfCard.assignNewId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCopy(boolean isCopy, MageObject copiedFrom) {
|
||||
super.setCopy(isCopy, copiedFrom);
|
||||
leftHalfCard.setCopy(isCopy, copiedFrom);
|
||||
rightHalfCard.setCopy(isCopy, copiedFrom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
|
||||
if (super.moveToZone(toZone, sourceId, game, flag, appliedEffects)) {
|
||||
game.getState().setZone(getLeftHalfCard().getId(), toZone);
|
||||
game.getState().setZone(getRightHalfCard().getId(), toZone);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(Zone zone, Game game) {
|
||||
super.setZone(zone, game);
|
||||
game.setZone(getLeftHalfCard().getId(), zone);
|
||||
game.setZone(getRightHalfCard().getId(), zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
|
||||
if (super.moveToExile(exileId, name, sourceId, game, appliedEffects)) {
|
||||
Zone currentZone = game.getState().getZone(getId());
|
||||
game.getState().setZone(getLeftHalfCard().getId(), currentZone);
|
||||
game.getState().setZone(getRightHalfCard().getId(), currentZone);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
|
||||
// zone contains only one main card
|
||||
return super.removeFromZone(game, fromZone, sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
|
||||
if (isCopy()) { // same as meld cards
|
||||
super.updateZoneChangeCounter(game, event);
|
||||
return;
|
||||
}
|
||||
super.updateZoneChangeCounter(game, event);
|
||||
getLeftHalfCard().updateZoneChangeCounter(game, event);
|
||||
getRightHalfCard().updateZoneChangeCounter(game, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
|
||||
switch (ability.getSpellAbilityType()) {
|
||||
case MODAL_LEFT:
|
||||
return this.getLeftHalfCard().cast(game, fromZone, ability, controllerId);
|
||||
case MODAL_RIGHT:
|
||||
return this.getRightHalfCard().cast(game, fromZone, ability, controllerId);
|
||||
default:
|
||||
this.getLeftHalfCard().getSpellAbility().setControllerId(controllerId);
|
||||
this.getRightHalfCard().getSpellAbility().setControllerId(controllerId);
|
||||
return super.cast(game, fromZone, ability, controllerId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities() {
|
||||
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
|
||||
allAbilites.addAll(super.getAbilities());
|
||||
allAbilites.addAll(leftHalfCard.getAbilities());
|
||||
allAbilites.addAll(rightHalfCard.getAbilities());
|
||||
return allAbilites;
|
||||
}
|
||||
|
||||
public Abilities<Ability> getSharedAbilities(Game game) {
|
||||
return super.getAbilities(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities(Game game) {
|
||||
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
|
||||
|
||||
// ignore default spell ability from main card (only halfes are actual)
|
||||
for (Ability ability : super.getAbilities(game)) {
|
||||
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) {
|
||||
continue;
|
||||
}
|
||||
allAbilites.add(ability);
|
||||
}
|
||||
|
||||
allAbilites.addAll(leftHalfCard.getAbilities(game));
|
||||
allAbilites.addAll(rightHalfCard.getAbilities(game));
|
||||
return allAbilites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UUID ownerId) {
|
||||
super.setOwnerId(ownerId);
|
||||
abilities.setControllerId(ownerId);
|
||||
leftHalfCard.getAbilities().setControllerId(ownerId);
|
||||
leftHalfCard.setOwnerId(ownerId);
|
||||
rightHalfCard.getAbilities().setControllerId(ownerId);
|
||||
rightHalfCard.setOwnerId(ownerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConvertedManaCost() {
|
||||
// Rules:
|
||||
// The converted mana cost of a modal double-faced card is based on the characteristics of the
|
||||
// face that’s being considered. On the stack and battlefield, consider whichever face is up.
|
||||
// In all other zones, consider only the front face. This is different than how the converted
|
||||
// mana cost of a transforming double-faced card is determined.
|
||||
|
||||
// on stack or battlefield it must be half card with own cost
|
||||
return getLeftHalfCard().getConvertedManaCost();
|
||||
}
|
||||
}
|
||||
18
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java
Normal file
18
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.MageInt;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public interface ModalDoubleFacesCardHalf extends Card {
|
||||
|
||||
@Override
|
||||
ModalDoubleFacesCardHalf copy();
|
||||
|
||||
void setParentCard(ModalDoubleFacesCard card);
|
||||
|
||||
ModalDoubleFacesCard getParentCard();
|
||||
|
||||
void setPT(MageInt power, MageInt toughtness);
|
||||
}
|
||||
101
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java
Normal file
101
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubleFacesCardHalf {
|
||||
|
||||
ModalDoubleFacesCard parentCard;
|
||||
|
||||
public ModalDoubleFacesCardHalfImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, SubType[] cardSubTypes,
|
||||
String costs, ModalDoubleFacesCard parentCard, SpellAbilityType spellAbilityType) {
|
||||
super(ownerId, setInfo, cardTypes, costs, spellAbilityType);
|
||||
this.parentCard = parentCard;
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCardHalfImpl(final ModalDoubleFacesCardHalfImpl card) {
|
||||
super(card);
|
||||
this.parentCard = card.parentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOwnerId() {
|
||||
return parentCard.getOwnerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageName() {
|
||||
// TODO: own name?
|
||||
return parentCard.getImageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpansionSetCode() {
|
||||
// TODO: own set code?
|
||||
return parentCard.getExpansionSetCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCardNumber() {
|
||||
// TODO: own card number?
|
||||
return parentCard.getCardNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
|
||||
return parentCard.moveToZone(toZone, sourceId, game, flag, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
|
||||
return parentCard.moveToExile(exileId, name, sourceId, game, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
|
||||
return parentCard.removeFromZone(game, fromZone, sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModalDoubleFacesCard getMainCard() {
|
||||
return parentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(Zone zone, Game game) {
|
||||
game.setZone(parentCard.getId(), zone);
|
||||
game.setZone(parentCard.getLeftHalfCard().getId(), zone);
|
||||
game.setZone(parentCard.getRightHalfCard().getId(), zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModalDoubleFacesCardHalfImpl copy() {
|
||||
return new ModalDoubleFacesCardHalfImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentCard(ModalDoubleFacesCard card) {
|
||||
this.parentCard = card;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModalDoubleFacesCard getParentCard() {
|
||||
return this.parentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPT(MageInt power, MageInt toughness) {
|
||||
this.power = power;
|
||||
this.toughness = toughness;
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,6 @@ public abstract class SplitCard extends CardImpl {
|
|||
String[] names = setInfo.getName().split(" // ");
|
||||
leftHalfCard = new SplitCardHalfImpl(this.getOwnerId(), new CardSetInfo(names[0], setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), typesLeft, costsLeft, this, SpellAbilityType.SPLIT_LEFT);
|
||||
rightHalfCard = new SplitCardHalfImpl(this.getOwnerId(), new CardSetInfo(names[1], setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), typesRight, costsRight, this, SpellAbilityType.SPLIT_RIGHT);
|
||||
this.splitCard = true;
|
||||
}
|
||||
|
||||
public SplitCard(SplitCard card) {
|
||||
|
|
@ -187,4 +186,15 @@ public abstract class SplitCard extends CardImpl {
|
|||
rightHalfCard.getAbilities().setControllerId(ownerId);
|
||||
rightHalfCard.setOwnerId(ownerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConvertedManaCost() {
|
||||
// 202.3d The converted mana cost of a split card not on the stack or of a fused split spell on the
|
||||
// stack is determined from the combined mana costs of its halves. Otherwise, while a split card is
|
||||
// on the stack, the converted mana cost of the spell is determined by the mana cost of the half
|
||||
// that was chosen to be cast. See rule 708, “Split Cards.”
|
||||
|
||||
// split card and it's halfes contains own mana costs, so no need to rewrite logic
|
||||
return super.getConvertedManaCost();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
/*
|
||||
* 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.cards;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface SplitCardHalf extends Card {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* 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.cards;
|
||||
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -14,7 +9,6 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SplitCardHalfImpl extends CardImpl implements SplitCardHalf {
|
||||
|
|
@ -61,6 +55,11 @@ public class SplitCardHalfImpl extends CardImpl implements SplitCardHalf {
|
|||
return splitCardParent.moveToExile(exileId, name, sourceId, game, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
|
||||
return splitCardParent.removeFromZone(game, fromZone, sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SplitCard getMainCard() {
|
||||
return splitCardParent;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
|||
public class MockCard extends CardImpl {
|
||||
|
||||
static public String ADVENTURE_NAME_SEPARATOR = " // ";
|
||||
static public String MODAL_DOUBLE_FACES_NAME_SEPARATOR = " // ";
|
||||
|
||||
// Needs to be here, as it is normally calculated from the
|
||||
// PlaneswalkerEntersWithLoyaltyAbility of the card... but the MockCard
|
||||
|
|
@ -30,6 +31,7 @@ public class MockCard extends CardImpl {
|
|||
protected ManaCosts<ManaCost> manaCostLeft;
|
||||
protected ManaCosts<ManaCost> manaCostRight;
|
||||
protected String adventureSpellName;
|
||||
protected String modalDoubleFacesSecondSideName;
|
||||
|
||||
public MockCard(CardInfo card) {
|
||||
super(null, card.getName());
|
||||
|
|
@ -53,7 +55,6 @@ public class MockCard extends CardImpl {
|
|||
this.frameColor = card.getFrameColor();
|
||||
this.frameStyle = card.getFrameStyle();
|
||||
|
||||
this.splitCard = card.isSplitCard();
|
||||
this.flipCard = card.isFlipCard();
|
||||
|
||||
this.transformable = card.isDoubleFaced();
|
||||
|
|
@ -66,6 +67,10 @@ public class MockCard extends CardImpl {
|
|||
this.adventureSpellName = card.getAdventureSpellName();
|
||||
}
|
||||
|
||||
if (card.isModalDoubleFacesCard()) {
|
||||
this.modalDoubleFacesSecondSideName = card.getModalDoubleFacesSecondSideName();
|
||||
}
|
||||
|
||||
if (this.isPlaneswalker()) {
|
||||
String startingLoyaltyString = card.getStartingLoyalty();
|
||||
if (startingLoyaltyString.isEmpty()) {
|
||||
|
|
@ -117,8 +122,14 @@ public class MockCard extends CardImpl {
|
|||
}
|
||||
|
||||
public String getFullName(boolean showSecondName) {
|
||||
if (!showSecondName) {
|
||||
return getName();
|
||||
}
|
||||
|
||||
if (adventureSpellName != null) {
|
||||
return getName() + ADVENTURE_NAME_SEPARATOR + adventureSpellName;
|
||||
} else if (modalDoubleFacesSecondSideName != null) {
|
||||
return getName() + MODAL_DOUBLE_FACES_NAME_SEPARATOR + modalDoubleFacesSecondSideName;
|
||||
} else {
|
||||
return getName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ public class MockSplitCard extends SplitCard {
|
|||
this.usesVariousArt = card.usesVariousArt();
|
||||
|
||||
this.color = card.getColor();
|
||||
this.splitCard = card.isSplitCard();
|
||||
this.flipCard = card.isFlipCard();
|
||||
|
||||
this.transformable = card.isDoubleFaced();
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package mage.cards.repository;
|
|||
import com.j256.ormlite.field.DataType;
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
|
|
@ -17,6 +15,9 @@ import mage.util.CardUtil;
|
|||
import mage.util.SubTypeList;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author North
|
||||
*/
|
||||
|
|
@ -103,6 +104,10 @@ public class CardInfo {
|
|||
protected boolean adventureCard;
|
||||
@DatabaseField
|
||||
protected String adventureSpellName;
|
||||
@DatabaseField
|
||||
protected boolean modalDoubleFacesCard;
|
||||
@DatabaseField
|
||||
protected String modalDoubleFacesSecondSideName;
|
||||
|
||||
public enum ManaCostSide {
|
||||
LEFT, RIGHT, ALL
|
||||
|
|
@ -121,7 +126,7 @@ public class CardInfo {
|
|||
this.toughness = card.getToughness().toString();
|
||||
this.convertedManaCost = card.getConvertedManaCost();
|
||||
this.rarity = card.getRarity();
|
||||
this.splitCard = card.isSplitCard();
|
||||
this.splitCard = card instanceof SplitCard;
|
||||
this.splitCardFuse = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED;
|
||||
this.splitCardAftermath = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH;
|
||||
|
||||
|
|
@ -140,6 +145,11 @@ public class CardInfo {
|
|||
this.adventureSpellName = ((AdventureCard) card).getSpellCard().getName();
|
||||
}
|
||||
|
||||
if (card instanceof ModalDoubleFacesCard) {
|
||||
this.modalDoubleFacesCard = true;
|
||||
this.modalDoubleFacesSecondSideName = ((ModalDoubleFacesCard) card).getRightHalfCard().getName();
|
||||
}
|
||||
|
||||
this.frameStyle = card.getFrameStyle().toString();
|
||||
this.frameColor = card.getFrameColor(null).toString();
|
||||
this.variousArt = card.getUsesVariousArt();
|
||||
|
|
@ -153,13 +163,17 @@ public class CardInfo {
|
|||
this.setSubtypes(card.getSubtype(null).stream().map(SubType::toString).collect(Collectors.toList()));
|
||||
this.setSuperTypes(card.getSuperType());
|
||||
|
||||
// mana cost can contains multiple cards (split left/right, card/adventure)
|
||||
// mana cost can contains multiple cards (split left/right, modal double faces, card/adventure)
|
||||
if (card instanceof SplitCard) {
|
||||
List<String> manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols();
|
||||
List<String> manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols();
|
||||
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
List<String> manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().getSymbols();
|
||||
List<String> manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().getSymbols();
|
||||
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
|
||||
} else if (card instanceof AdventureCard) {
|
||||
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); // Spell from left like MTGA
|
||||
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols();
|
||||
List<String> manaCostRight = card.getManaCost().getSymbols();
|
||||
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
|
||||
} else {
|
||||
|
|
@ -181,6 +195,19 @@ public class CardInfo {
|
|||
length += rule.length();
|
||||
rulesList.add(rule);
|
||||
}
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
for (String rule : ((ModalDoubleFacesCard) card).getLeftHalfCard().getRules()) {
|
||||
length += rule.length();
|
||||
rulesList.add(rule);
|
||||
}
|
||||
for (String rule : ((ModalDoubleFacesCard) card).getRightHalfCard().getRules()) {
|
||||
length += rule.length();
|
||||
rulesList.add(rule);
|
||||
}
|
||||
for (String rule : card.getRules()) {
|
||||
length += rule.length();
|
||||
rulesList.add(rule);
|
||||
}
|
||||
} else {
|
||||
for (String rule : card.getRules()) {
|
||||
length += rule.length();
|
||||
|
|
@ -222,7 +249,6 @@ public class CardInfo {
|
|||
}
|
||||
}
|
||||
if (this.startingLoyalty == null) {
|
||||
//Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty");
|
||||
this.startingLoyalty = "";
|
||||
}
|
||||
} else {
|
||||
|
|
@ -447,4 +473,12 @@ public class CardInfo {
|
|||
public String getAdventureSpellName() {
|
||||
return adventureSpellName;
|
||||
}
|
||||
|
||||
public boolean isModalDoubleFacesCard() {
|
||||
return modalDoubleFacesCard;
|
||||
}
|
||||
|
||||
public String getModalDoubleFacesSecondSideName() {
|
||||
return modalDoubleFacesSecondSideName;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ public enum CardRepository {
|
|||
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE";
|
||||
private static final String VERSION_ENTITY_NAME = "card";
|
||||
// raise this if db structure was changed
|
||||
private static final long CARD_DB_VERSION = 52;
|
||||
private static final long CARD_DB_VERSION = 53;
|
||||
// raise this if new cards were added to the server
|
||||
private static final long CARD_CONTENT_VERSION = 232;
|
||||
private static final long CARD_CONTENT_VERSION = 233;
|
||||
private Dao<CardInfo, Object> cardDao;
|
||||
private Set<String> classNames;
|
||||
private final RepositoryEventSource eventSource = new RepositoryEventSource();
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public final class CardScanner {
|
|||
new CardSetInfo(setInfo.getName(), set.getCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()),
|
||||
errorsList);
|
||||
if (card != null) {
|
||||
cardsToAdd.add(new CardInfo(card));
|
||||
cardsToAdd.add(new CardInfo(card)); // normal, transformed, adventure, modal double faces -- all must have single face in db
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
cardsToAdd.add(new CardInfo(splitCard.getLeftHalfCard()));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public enum SpellAbilityType {
|
||||
|
|
@ -13,7 +12,9 @@ public enum SpellAbilityType {
|
|||
SPLIT_FUSED("Split SpellAbility"),
|
||||
SPLIT_LEFT("LeftSplit SpellAbility"),
|
||||
SPLIT_RIGHT("RightSplit SpellAbility"),
|
||||
MODE("Mode SpellAbility"),
|
||||
MODAL("Modal SpellAbility"), // used for modal double faces cards
|
||||
MODAL_LEFT("LeftModal SpellAbility"),
|
||||
MODAL_RIGHT("RightModal SpellAbility"),
|
||||
SPLICE("Spliced SpellAbility"),
|
||||
ADVENTURE_SPELL("Adventure SpellAbility");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.cards.ModalDoubleFacesCardHalf;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -18,8 +18,13 @@ public enum MulticoloredPredicate implements Predicate<MageObject> {
|
|||
// 708.3. Each split card that consists of two halves with different colored mana symbols in their mana costs
|
||||
// is a multicolored card while it's not a spell on the stack. While it's a spell on the stack, it's only the
|
||||
// color or colors of the half or halves being cast. #
|
||||
if (input instanceof SplitCardHalf && game.getState().getZone(input.getId()) != Zone.STACK) {
|
||||
if (input instanceof SplitCardHalf
|
||||
&& game.getState().getZone(input.getId()) != Zone.STACK) {
|
||||
return 1 < ((SplitCardHalf) input).getMainCard().getColor(game).getColorCount();
|
||||
} else if (input instanceof ModalDoubleFacesCardHalf
|
||||
&& (game.getState().getZone(input.getId()) != Zone.STACK && game.getState().getZone(input.getId()) != Zone.BATTLEFIELD)) {
|
||||
// While a double-faced card isn’t on the stack or battlefield, consider only the characteristics of its front face.
|
||||
return 1 < ((ModalDoubleFacesCardHalf) input).getMainCard().getColor(game).getColorCount();
|
||||
} else {
|
||||
return 1 < input.getColor(game).getColorCount();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -32,10 +33,15 @@ public class NamePredicate implements Predicate<MageObject> {
|
|||
}
|
||||
// If a player names a card, the player may name either half of a split card, but not both.
|
||||
// A split card has the chosen name if one of its two names matches the chosen name.
|
||||
// Same for modal double faces cards
|
||||
if (input instanceof SplitCard) {
|
||||
return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames);
|
||||
} else if (input instanceof ModalDoubleFacesCard) {
|
||||
return CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames);
|
||||
} else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
|
||||
SplitCard card = (SplitCard) ((Spell) input).getCard();
|
||||
return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.filter.predicate.other;
|
|||
|
||||
import mage.cards.AdventureCard;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.mock.MockCard;
|
||||
import mage.constants.SubType;
|
||||
|
|
@ -52,6 +53,8 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
String fullName = input.getName();
|
||||
if (input instanceof MockCard) {
|
||||
fullName = ((MockCard) input).getFullName(true);
|
||||
} else if (input instanceof ModalDoubleFacesCard) {
|
||||
fullName = input.getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + ((ModalDoubleFacesCard) input).getRightHalfCard().getName();
|
||||
} else if (input instanceof AdventureCard) {
|
||||
fullName = input.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + ((AdventureCard) input).getSpellCard().getName();
|
||||
}
|
||||
|
|
@ -67,14 +70,14 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
}
|
||||
}
|
||||
|
||||
//separate by spaces
|
||||
// separate by spaces
|
||||
String[] tokens = text.toLowerCase(Locale.ENGLISH).split(" ");
|
||||
for (String token : tokens) {
|
||||
boolean found = false;
|
||||
if (!token.isEmpty()) {
|
||||
// then try to find in rules
|
||||
if (inRules) {
|
||||
if (input.isSplitCard()) {
|
||||
if (input instanceof SplitCard) {
|
||||
for (String rule : ((SplitCard) input).getLeftHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
|
|
@ -88,6 +91,22 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input instanceof ModalDoubleFacesCard) {
|
||||
for (String rule : ((ModalDoubleFacesCard) input).getLeftHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (String rule : ((ModalDoubleFacesCard) input).getRightHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input instanceof AdventureCard) {
|
||||
for (String rule : ((AdventureCard) input).getSpellCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
|
|
@ -96,6 +115,7 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String rule : input.getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
|
|
|
|||
|
|
@ -244,17 +244,29 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
card.setOwnerId(ownerId);
|
||||
gameCards.put(card.getId(), card);
|
||||
state.addCard(card);
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
// left
|
||||
Card leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
leftCard.setOwnerId(ownerId);
|
||||
gameCards.put(leftCard.getId(), leftCard);
|
||||
state.addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
rightCard.setOwnerId(ownerId);
|
||||
gameCards.put(rightCard.getId(), rightCard);
|
||||
state.addCard(rightCard);
|
||||
}
|
||||
if (card instanceof AdventureCard) {
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
// left
|
||||
Card leftCard = ((ModalDoubleFacesCard) card).getLeftHalfCard();
|
||||
leftCard.setOwnerId(ownerId);
|
||||
gameCards.put(leftCard.getId(), leftCard);
|
||||
state.addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((ModalDoubleFacesCard) card).getRightHalfCard();
|
||||
rightCard.setOwnerId(ownerId);
|
||||
gameCards.put(rightCard.getId(), rightCard);
|
||||
state.addCard(rightCard);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
Card spellCard = ((AdventureCard) card).getSpellCard();
|
||||
spellCard.setOwnerId(ownerId);
|
||||
gameCards.put(spellCard.getId(), spellCard);
|
||||
|
|
@ -1911,7 +1923,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
Iterator<Card> copiedCards = this.getState().getCopiedCards().iterator();
|
||||
while (copiedCards.hasNext()) {
|
||||
Card card = copiedCards.next();
|
||||
if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell) {
|
||||
if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell || card instanceof ModalDoubleFacesCardHalf) {
|
||||
continue; // only the main card is moves, not the halves (cause halfes is not copied - it uses original card -- TODO: need to fix (bugs with same card copy)?
|
||||
}
|
||||
Zone zone = state.getZone(card.getId());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
package mage.game;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import static java.util.Collections.emptyList;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.*;
|
||||
|
|
@ -12,6 +8,7 @@ import mage.abilities.effects.ContinuousEffects;
|
|||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.AdventureCard;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.Zone;
|
||||
import mage.designations.Designation;
|
||||
|
|
@ -40,6 +37,12 @@ import mage.util.ThreadLocalStringBuilder;
|
|||
import mage.watchers.Watcher;
|
||||
import mage.watchers.Watchers;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* <p>
|
||||
|
|
@ -626,6 +629,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
// public void addMessage(String message) {
|
||||
// this.messages.add(message);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a list of all players of the game ignoring range or if a player
|
||||
* has lost or left the game.
|
||||
|
|
@ -799,7 +803,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
|
||||
Set<Card> movedCards = new LinkedHashSet<>();
|
||||
Set<PermanentToken> movedTokens = new LinkedHashSet<>();
|
||||
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
|
||||
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext(); ) {
|
||||
GameEvent event = it.next();
|
||||
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
|
||||
UUID targetId = castEvent.getTargetId();
|
||||
|
|
@ -835,10 +839,14 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
// TODO Watchers?
|
||||
// TODO Abilities?
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
removeCopiedCard(((SplitCard) card).getLeftHalfCard());
|
||||
removeCopiedCard(((SplitCard) card).getRightHalfCard());
|
||||
}
|
||||
if (card instanceof ModalDoubleFacesCard) {
|
||||
removeCopiedCard(((ModalDoubleFacesCard) card).getLeftHalfCard());
|
||||
removeCopiedCard(((ModalDoubleFacesCard) card).getRightHalfCard());
|
||||
}
|
||||
if (card instanceof AdventureCard) {
|
||||
removeCopiedCard(((AdventureCard) card).getSpellCard());
|
||||
}
|
||||
|
|
@ -1194,21 +1202,34 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
|
||||
public Card copyCard(Card cardToCopy, Ability source, Game game) {
|
||||
// main card
|
||||
Card copiedCard = cardToCopy.copy();
|
||||
copiedCard.assignNewId();
|
||||
copiedCard.setOwnerId(source.getControllerId());
|
||||
copiedCard.setCopy(true, cardToCopy);
|
||||
copiedCards.put(copiedCard.getId(), copiedCard);
|
||||
addCard(copiedCard);
|
||||
if (copiedCard.isSplitCard()) {
|
||||
|
||||
// other faces
|
||||
if (copiedCard instanceof SplitCard) {
|
||||
// left
|
||||
Card leftCard = ((SplitCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)?
|
||||
copiedCards.put(leftCard.getId(), leftCard);
|
||||
addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((SplitCard) copiedCard).getRightHalfCard();
|
||||
copiedCards.put(rightCard.getId(), rightCard);
|
||||
addCard(rightCard);
|
||||
}
|
||||
if (copiedCard instanceof AdventureCard) {
|
||||
} else if (copiedCard instanceof ModalDoubleFacesCard) {
|
||||
// left
|
||||
Card leftCard = ((ModalDoubleFacesCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)?
|
||||
copiedCards.put(leftCard.getId(), leftCard);
|
||||
addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((ModalDoubleFacesCard) copiedCard).getRightHalfCard();
|
||||
copiedCards.put(rightCard.getId(), rightCard);
|
||||
addCard(rightCard);
|
||||
} else if (copiedCard instanceof AdventureCard) {
|
||||
Card spellCard = ((AdventureCard) copiedCard).getSpellCard();
|
||||
copiedCards.put(spellCard.getId(), spellCard);
|
||||
addCard(spellCard);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package mage.game;
|
||||
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.MeldCard;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.*;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
|
|
@ -19,7 +17,6 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.*;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
|
||||
/**
|
||||
* Created by samuelsandeen on 9/6/16.
|
||||
|
|
@ -54,7 +51,7 @@ public final class ZonesHandler {
|
|||
|
||||
public static List<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> zoneChangeInfos, Game game) {
|
||||
// Handle Unmelded Meld Cards
|
||||
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext();) {
|
||||
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) {
|
||||
ZoneChangeInfo info = itr.next();
|
||||
MeldCard card = game.getMeldCard(info.event.getTargetId());
|
||||
// Copies should be handled as normal cards.
|
||||
|
|
@ -106,6 +103,10 @@ public final class ZonesHandler {
|
|||
if (!(targetCard instanceof Permanent) && targetCard != null) {
|
||||
if (targetCard instanceof MeldCard) {
|
||||
cards = ((MeldCard) targetCard).getHalves();
|
||||
} else if (targetCard instanceof ModalDoubleFacesCard) {
|
||||
cards = new CardsImpl(targetCard);
|
||||
cards.add(((ModalDoubleFacesCard) targetCard).getLeftHalfCard());
|
||||
cards.add(((ModalDoubleFacesCard) targetCard).getRightHalfCard());
|
||||
} else {
|
||||
cards = new CardsImpl(targetCard);
|
||||
}
|
||||
|
|
@ -174,14 +175,19 @@ public final class ZonesHandler {
|
|||
throw new UnsupportedOperationException("to Zone " + toZone.toString() + " not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
game.setZone(event.getTargetId(), event.getToZone());
|
||||
if (targetCard instanceof MeldCard && cards != null) {
|
||||
if (event.getToZone() != Zone.BATTLEFIELD) {
|
||||
((MeldCard) targetCard).setMelded(false, game);
|
||||
}
|
||||
if (cards != null && (targetCard instanceof MeldCard || targetCard instanceof ModalDoubleFacesCard)) {
|
||||
// update other parts too
|
||||
for (Card card : cards.getCards(game)) {
|
||||
game.setZone(card.getId(), event.getToZone());
|
||||
}
|
||||
// reset meld status
|
||||
if (targetCard instanceof MeldCard) {
|
||||
if (event.getToZone() != Zone.BATTLEFIELD) {
|
||||
((MeldCard) targetCard).setMelded(false, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +209,7 @@ public final class ZonesHandler {
|
|||
if (info instanceof ZoneChangeInfo.Unmelded) {
|
||||
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
|
||||
MeldCard meld = game.getMeldCard(info.event.getTargetId());
|
||||
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext();) {
|
||||
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
|
||||
ZoneChangeInfo subInfo = itr.next();
|
||||
if (!maybeRemoveFromSourceZone(subInfo, game)) {
|
||||
itr.remove();
|
||||
|
|
@ -232,10 +238,10 @@ public final class ZonesHandler {
|
|||
if (info.faceDown) {
|
||||
card.setFaceDown(true, game);
|
||||
} else if (info.event.getToZone().equals(Zone.BATTLEFIELD)) {
|
||||
if (!card.isPermanent()
|
||||
if (!card.isPermanent()
|
||||
&& (!card.isTransformable() || Boolean.FALSE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())))) {
|
||||
// Non permanents (Instants, Sorceries, ... stay in the zone they are if an abilty/effect tries to move it to the battlefield
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!game.replaceEvent(event)) {
|
||||
|
|
@ -248,9 +254,10 @@ public final class ZonesHandler {
|
|||
Permanent permanent;
|
||||
if (card instanceof MeldCard) {
|
||||
permanent = new PermanentMeld(card, event.getPlayerId(), game);
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
throw new IllegalStateException("Try to move mdf card to battlefield instead half");
|
||||
} else if (card instanceof Permanent) {
|
||||
// This should never happen.
|
||||
permanent = (Permanent) card;
|
||||
throw new IllegalStateException("Try to move permanent card to battlefield");
|
||||
} else {
|
||||
permanent = new PermanentCard(card, event.getPlayerId(), game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -712,11 +712,6 @@ public class Spell extends StackObjImpl implements Card {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSplitCard() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransformable() {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1602,6 +1602,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
needId1 = object.getId();
|
||||
needId2 = ((SplitCard) object).getLeftHalfCard().getId();
|
||||
needId3 = ((SplitCard) object).getRightHalfCard().getId();
|
||||
} else if (object instanceof ModalDoubleFacesCard) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((ModalDoubleFacesCard) object).getLeftHalfCard().getId();
|
||||
needId3 = ((ModalDoubleFacesCard) object).getRightHalfCard().getId();
|
||||
} else if (object instanceof AdventureCard) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((AdventureCard) object).getMainCard().getId();
|
||||
|
|
@ -3402,10 +3406,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
// BASIC abilities
|
||||
if (object instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) object;
|
||||
getPlayableFromObjectSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
|
||||
SplitCard mainCard = (SplitCard) object;
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output);
|
||||
}
|
||||
if (object instanceof ModalDoubleFacesCard) {
|
||||
ModalDoubleFacesCard mainCard = (ModalDoubleFacesCard) object;
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output);
|
||||
} else if (object instanceof AdventureCard) {
|
||||
// adventure must use different card characteristics for different spells (main or adventure)
|
||||
AdventureCard adventureCard = (AdventureCard) object;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
package mage.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Abilities;
|
||||
|
|
@ -17,6 +11,8 @@ import mage.abilities.costs.mana.*;
|
|||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.MeldCard;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.*;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
|
|
@ -31,6 +27,13 @@ import mage.players.Player;
|
|||
import mage.target.Target;
|
||||
import mage.util.functions.CopyTokenFunction;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
|
|
@ -39,10 +42,10 @@ public final class CardUtil {
|
|||
private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
|
||||
|
||||
static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
|
||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
|
||||
|
||||
static final String[] ordinalStrings = {"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eightth", "ninth",
|
||||
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"};
|
||||
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"};
|
||||
|
||||
public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
|
||||
|
|
@ -877,4 +880,21 @@ public final class CardUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return card name for same name searching
|
||||
*
|
||||
* @param card
|
||||
* @return
|
||||
*/
|
||||
public static String getCardNameForSameNameSearch(Card card) {
|
||||
// it's ok to return one name only cause NamePredicate can find same card by first name
|
||||
if (card instanceof SplitCard) {
|
||||
return ((SplitCard) card).getLeftHalfCard().getName();
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
return ((ModalDoubleFacesCard) card).getLeftHalfCard().getName();
|
||||
} else {
|
||||
return card.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue