Reworking card types in preparation for implementing Grist, the Hunger Tide (#7899)

Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
Evan Kranzler 2021-07-08 23:28:43 -04:00 committed by GitHub
parent 07e1dff10c
commit 572104b8fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1159 changed files with 2704 additions and 2203 deletions

View file

@ -33,7 +33,17 @@ public interface MageObject extends MageItem, Serializable {
void setName(String name);
ArrayList<CardType> getCardType();
default List<CardType> getCardType() {
return getCardType(null);
}
/**
* Return dynamic card types (game isn't null) or static card types (game is null)
*
* @param game can be null
* @return
*/
List<CardType> getCardType(Game game);
/**
* Return original object's subtypes
@ -141,52 +151,92 @@ public interface MageObject extends MageItem, Serializable {
void setZoneChangeCounter(int value, Game game);
default boolean isHistoric() {
return getCardType().contains(CardType.ARTIFACT)
default boolean isHistoric(Game game) {
return getCardType(game).contains(CardType.ARTIFACT)
|| getSuperType().contains(SuperType.LEGENDARY)
|| getSubtype().contains(SubType.SAGA);
|| hasSubtype(SubType.SAGA, game);
}
default boolean isCreature() {
return getCardType().contains(CardType.CREATURE);
return isCreature(null);
}
default boolean isCreature(Game game) {
return getCardType(game).contains(CardType.CREATURE);
}
default boolean isArtifact() {
return getCardType().contains(CardType.ARTIFACT);
return isArtifact(null);
}
default boolean isArtifact(Game game) {
return getCardType(game).contains(CardType.ARTIFACT);
}
default boolean isLand() {
return getCardType().contains(CardType.LAND);
return isLand(null);
}
default boolean isLand(Game game) {
return getCardType(game).contains(CardType.LAND);
}
default boolean isEnchantment() {
return getCardType().contains(CardType.ENCHANTMENT);
return isEnchantment(null);
}
default boolean isEnchantment(Game game) {
return getCardType(game).contains(CardType.ENCHANTMENT);
}
default boolean isInstant() {
return getCardType().contains(CardType.INSTANT);
return isInstant(null);
}
default boolean isInstant(Game game) {
return getCardType(game).contains(CardType.INSTANT);
}
default boolean isSorcery() {
return getCardType().contains(CardType.SORCERY);
return isSorcery(null);
}
default boolean isSorcery(Game game) {
return getCardType(game).contains(CardType.SORCERY);
}
default boolean isInstantOrSorcery() {
return this.isInstant() || this.isSorcery();
}
default boolean isInstantOrSorcery(Game game) {
return this.isInstant(game) || this.isSorcery(game);
}
default boolean isPlaneswalker() {
return getCardType().contains(CardType.PLANESWALKER);
return isPlaneswalker(null);
}
default boolean isPlaneswalker(Game game) {
return getCardType(game).contains(CardType.PLANESWALKER);
}
default boolean isTribal() {
return getCardType().contains(CardType.TRIBAL);
return isTribal(null);
}
default boolean isTribal(Game game) {
return getCardType(game).contains(CardType.TRIBAL);
}
default boolean isPermanent() {
return isCreature() || isArtifact() || isPlaneswalker() || isEnchantment() || isLand();
}
default boolean isPermanent(Game game) {
return isCreature(game) || isArtifact(game) || isPlaneswalker(game) || isEnchantment(game) || isLand(game);
}
default boolean isLegendary() {
return getSuperType().contains(SuperType.LEGENDARY);
}
@ -210,11 +260,69 @@ public interface MageObject extends MageItem, Serializable {
return getSuperType().contains(SuperType.WORLD);
}
default void addCardType(CardType cardType) {
if (getCardType().contains(cardType)) {
return;
/**
* Add card type from static effects (permanently)
*
* @param cardTypes
*/
default void addCardType(CardType... cardTypes) {
addCardType(null, cardTypes);
}
/**
* Add card type from dynamic effects (game isn't null) and from static effects (game is null)
*
* @param game
* @param cardTypes
*/
default void addCardType(Game game, CardType... cardTypes) {
List<CardType> currentCardTypes;
if (game != null) {
// dynamic
currentCardTypes = game.getState().getCreateMageObjectAttribute(this, game).getCardType();
} else {
// static
currentCardTypes = getCardType();
}
getCardType().add(cardType);
for (CardType cardType : cardTypes) {
if (!currentCardTypes.contains(cardType)) {
currentCardTypes.add(cardType);
}
}
}
default void removeCardType(CardType... cardTypes) {
removeCardType(null, cardTypes);
}
default void removeCardType(Game game, CardType... cardTypes) {
List<CardType> currentCardTypes;
if (game != null) {
// dynamic
currentCardTypes = game.getState().getCreateMageObjectAttribute(this, game).getCardType();
} else {
// static
currentCardTypes = getCardType();
}
for (CardType cardType : cardTypes) {
currentCardTypes.remove(cardType);
}
}
default void removeAllCardTypes() {
removeAllCardTypes(null);
}
default void removeAllCardTypes(Game game) {
List<CardType> currentCardTypes;
if (game != null) {
// dynamic
currentCardTypes = game.getState().getCreateMageObjectAttribute(this, game).getCardType();
} else {
// static
currentCardTypes = getCardType();
}
currentCardTypes.clear();
}
/**
@ -251,7 +359,7 @@ public interface MageObject extends MageItem, Serializable {
*/
default void addSubType(Game game, SubType... subTypes) {
for (SubType subType : subTypes) {
if (subType.canGain(this)
if (subType.canGain(game, this)
&& !hasSubtype(subType, game)) {
game.getState().getCreateMageObjectAttribute(this, game).getSubtype().add(subType);
}
@ -326,20 +434,21 @@ public interface MageObject extends MageItem, Serializable {
* Checks whether two cards share card types.
*
* @param otherCard
* @param game
* @return
*/
default boolean shareTypes(Card otherCard) {
return this.shareTypes(otherCard, false);
default boolean shareTypes(Card otherCard, Game game) {
return this.shareTypes(otherCard, game, false);
}
default boolean shareTypes(Card otherCard, boolean permanentOnly) {
default boolean shareTypes(Card otherCard, Game game, boolean permanentOnly) {
if (otherCard == null) {
throw new IllegalArgumentException("Params can't be null");
}
for (CardType type : getCardType()) {
if (otherCard.getCardType().contains(type)
for (CardType type : getCardType(game)) {
if (otherCard.getCardType(game).contains(type)
&& (!permanentOnly || type.isPermanentType())) {
return true;
}
@ -349,10 +458,10 @@ public interface MageObject extends MageItem, Serializable {
}
default boolean shareCreatureTypes(Game game, MageObject otherCard) {
if (!isCreature() && !isTribal()) {
if (!isCreature(game) && !isTribal(game)) {
return false;
}
if (!otherCard.isCreature() && !otherCard.isTribal()) {
if (!otherCard.isCreature(game) && !otherCard.isTribal(game)) {
return false;
}
boolean isAllA = this.isAllCreatureTypes(game);
@ -392,10 +501,6 @@ public interface MageObject extends MageItem, Serializable {
*/
void setIsAllCreatureTypes(Game game, boolean value);
default void addCardTypes(ArrayList<CardType> cardType) {
getCardType().addAll(cardType);
}
List<TextPart> getTextParts();
TextPart addTextPart(TextPart textPart);

View file

@ -36,7 +36,7 @@ public abstract class MageObjectImpl implements MageObject {
protected ObjectColor color;
protected ObjectColor frameColor;
protected FrameStyle frameStyle;
protected ArrayList<CardType> cardType = new ArrayList<>();
protected List<CardType> cardType = new ArrayList<>();
protected SubTypes subtype = new SubTypes();
protected Set<SuperType> supertype = EnumSet.noneOf(SuperType.class);
protected Abilities<Ability> abilities;
@ -114,7 +114,16 @@ public abstract class MageObjectImpl implements MageObject {
}
@Override
public ArrayList<CardType> getCardType() {
public List<CardType> getCardType(Game game) {
if (game != null) {
// dynamic
MageObjectAttribute mageObjectAttribute = game.getState().getMageObjectAttribute(getId());
if (mageObjectAttribute != null) {
return mageObjectAttribute.getCardType();
}
}
// static
return cardType;
}
@ -202,7 +211,7 @@ public abstract class MageObjectImpl implements MageObject {
public ObjectColor getFrameColor(Game game) {
// For lands, add any colors of mana the land can produce to
// its frame colors while game is active to represent ability changes during the game.
if (this.isLand() && !(this instanceof MockCard)) {
if (this.isLand(game) && !(this instanceof MockCard)) {
ObjectColor cl = frameColor.copy();
Set<ManaType> manaTypes = EnumSet.noneOf(ManaType.class);
for (Ability ab : getAbilities()) {
@ -294,9 +303,6 @@ public abstract class MageObjectImpl implements MageObject {
@Override
public boolean isAllCreatureTypes(Game game) {
if (game == null) {
return this.getAbilities().containsClass(ChangelingAbility.class);
}
return this.getSubtype(game).isAllCreatureTypes();
}
@ -307,7 +313,7 @@ public abstract class MageObjectImpl implements MageObject {
@Override
public void setIsAllCreatureTypes(Game game, boolean value) {
this.getSubtype(game).setIsAllCreatureTypes(value && (this.isTribal() || this.isCreature()));
this.getSubtype(game).setIsAllCreatureTypes(value && (this.isTribal(game) || this.isCreature(game)));
}
@Override

View file

@ -281,7 +281,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
}
if (sourceObject == null) { // source is no permanent
sourceObject = game.getObject(source.getSourceId());
if (sourceObject == null || sourceObject.isPermanent()) {
if (sourceObject == null || sourceObject.isPermanent(game)) {
return false; // No source object found => ability is not valid
}
}

View file

@ -53,7 +53,7 @@ public class ConstellationAbility extends TriggeredAbilityImpl {
return false;
}
Permanent permanent = game.getPermanent(event.getTargetId());
return permanent != null && ((thisOr && permanent.getId().equals(getSourceId())) || permanent.isEnchantment());
return permanent != null && ((thisOr && permanent.getId().equals(getSourceId())) || permanent.isEnchantment(game));
}
@Override

View file

@ -44,7 +44,7 @@ public class ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility extends Triggere
return false;
}
Permanent permanent = stackAbility.getSourcePermanentOrLKI(game);
if (permanent == null || !permanent.isPlaneswalker()
if (permanent == null || !permanent.isPlaneswalker(game)
|| !permanent.hasSubtype(planeswalkerSubType, game)) {
return false;
}

View file

@ -30,7 +30,7 @@ public class AttachedToCreatureSourceTriggeredAbility extends TriggeredAbilityIm
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent attachedPermanent = game.getPermanent(event.getTargetId());
return attachedPermanent != null && attachedPermanent.isCreature();
return attachedPermanent != null && attachedPermanent.isCreature(game);
}
@Override

View file

@ -66,7 +66,7 @@ public class AttacksAllTriggeredAbility extends TriggeredAbilityImpl {
check = true;
} else {
Permanent planeswalker = game.getPermanent(event.getTargetId());
if (planeswalker != null && planeswalker.isPlaneswalker() && planeswalker.isControlledBy(getControllerId())) {
if (planeswalker != null && planeswalker.isPlaneswalker(game) && planeswalker.isControlledBy(getControllerId())) {
check = true;
}
}

View file

@ -35,7 +35,7 @@ public class BecomesMonstrousTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && permanent.isCreature()
if (permanent != null && permanent.isCreature(game)
&& (permanent.isControlledBy(getControllerId()))) {
this.getEffects().setTargetPointer(new FixedTarget(permanent, game));
return true;

View file

@ -46,7 +46,7 @@ public class DealsCombatDamageToACreatureTriggeredAbility extends TriggeredAbili
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null
|| !permanent.isCreature()
|| !permanent.isCreature(game)
|| !event.getSourceId().equals(this.sourceId)
|| !((DamagedEvent) event).isCombatDamage()) {
return false;

View file

@ -78,7 +78,7 @@ public class DealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbility
case DAMAGED_PERMANENT:
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null
|| !permanent.isPlaneswalker()
|| !permanent.isPlaneswalker(game)
|| !orPlaneswalker) {
return false;
}

View file

@ -62,7 +62,7 @@ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityI
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null || !permanent.isCreature()) {
if (permanent == null || !permanent.isCreature(game)) {
return false;
}
if (combatDamageOnly && !((DamagedEvent) event).isCombatDamage()) {

View file

@ -56,7 +56,7 @@ public class DealsDamageToAPlayerTriggeredAbility extends TriggeredAbilityImpl {
if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null
|| !permanent.isPlaneswalker()
|| !permanent.isPlaneswalker(game)
|| !orPlaneswalker) {
return false;
}

View file

@ -45,7 +45,7 @@ public class DestroyPlaneswalkerWhenDamagedTriggeredAbility extends TriggeredAbi
return false;
}
boolean applies = filter != null ?
permanent.isPlaneswalker() && filter.match(permanent, game) : event.getSourceId().equals(getSourceId());
permanent.isPlaneswalker(game) && filter.match(permanent, game) : event.getSourceId().equals(getSourceId());
if (applies) {
Effect effect = new DestroyTargetEffect();
effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));

View file

@ -29,7 +29,7 @@ public class ExertCreatureControllerTriggeredAbility extends TriggeredAbilityImp
public boolean checkTrigger(GameEvent event, Game game) {
boolean weAreExerting = isControlledBy(event.getPlayerId());
Permanent exerted = game.getPermanent(event.getTargetId());
boolean exertedIsCreature = (exerted != null) && exerted.isCreature();
boolean exertedIsCreature = (exerted != null) && exerted.isCreature(game);
return weAreExerting && exertedIsCreature;
}

View file

@ -49,7 +49,7 @@ public class LandfallAbility extends TriggeredAbilityImpl {
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null
&& permanent.isLand()
&& permanent.isLand(game)
&& permanent.isControlledBy(this.controllerId)) {
triggeringLand = permanent;
if (setTargetPointer == SetTargetPointer.PERMANENT) {

View file

@ -107,8 +107,8 @@ class LicidContinuousEffect extends ContinuousEffectImpl {
if (licid != null) {
switch (layer) {
case TypeChangingEffects_4:
licid.getCardType().clear();
licid.addCardType(CardType.ENCHANTMENT);
licid.removeAllCardTypes(game);
licid.addCardType(game, CardType.ENCHANTMENT);
licid.removeAllSubTypes(game);
licid.addSubType(game, SubType.AURA);
break;

View file

@ -37,7 +37,7 @@ public class MagecraftAbility extends TriggeredAbilityImpl {
Spell spell = game.getSpell(event.getTargetId());
if (spell == null
|| !spell.isControlledBy(getControllerId())
|| !spell.isInstantOrSorcery()) {
|| !spell.isInstantOrSorcery(game)) {
return false;
}
getEffects().setValue(SPELL_KEY, spell);

View file

@ -48,7 +48,7 @@ public class TapLandForManaAllTriggeredAbility extends TriggeredAbilityImpl {
permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
}
if (permanent != null && permanent.isLand()) {
if (permanent != null && permanent.isLand(game)) {
if (setTargetPointer) {
getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game));
}

View file

@ -36,7 +36,7 @@ public class TapLandForManaAllTriggeredManaAbility extends TriggeredManaAbility
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (permanent != null && permanent.isLand()) {
if (permanent != null && permanent.isLand(game)) {
if (setTargetPointer) {
getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game));
}

View file

@ -30,7 +30,7 @@ public class EnchantedSourceCondition implements Condition {
if (permanent != null) {
for (UUID uuid : permanent.getAttachments()) {
Permanent attached = game.getBattlefield().getPermanent(uuid);
if (attached != null && attached.isEnchantment()) {
if (attached != null && attached.isEnchantment(game)) {
if (++numberOfFoundEnchantments >= numberOfEnchantments) {
return true;
}

View file

@ -23,7 +23,7 @@ public enum EnchantedTargetCondition implements Condition {
if (targetPermanent != null) {
for (UUID uuid : targetPermanent.getAttachments()) {
Permanent attached = game.getBattlefield().getPermanent(uuid);
if (attached != null && attached.isEnchantment()) {
if (attached != null && attached.isEnchantment(game)) {
return true;
}
}

View file

@ -18,6 +18,6 @@ instance;
@Override
public boolean apply(Game game, Ability source) {
MageObject object = game.getObject(source.getSourceId());
return object != null && !object.isLand();
return object != null && !object.isLand(game);
}
}

View file

@ -23,7 +23,7 @@ public class TargetHasCardTypeCondition implements Condition {
if (!source.getTargets().isEmpty()) {
MageObject mageObject = game.getObject(source.getFirstTarget());
if (mageObject != null) {
return mageObject.getCardType().contains(cardType);
return mageObject.getCardType(game).contains(cardType);
}
}
return false;

View file

@ -27,13 +27,13 @@ public class TopLibraryCardTypeCondition implements Condition {
if (card != null) {
switch (this.type) {
case CREATURE:
return card.isCreature();
return card.isCreature(game);
case LAND:
return card.isLand();
return card.isLand(game);
case SORCERY:
return card.isSorcery();
return card.isSorcery(game);
case INSTANT:
return card.isInstant();
return card.isInstant(game);
}
}
}

View file

@ -42,7 +42,7 @@ public class ExileTopCreatureCardOfGraveyardCost extends CostImpl {
if(controller != null) {
Card topCard = null;
for (Card card :controller.getGraveyard().getCards(game)) {
if (card.isCreature()) {
if (card.isCreature(game)) {
topCard = card;
}
}

View file

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

View file

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

View file

@ -18,7 +18,7 @@ public class CardTypeAssignment extends RoleAssignment<CardType> {
protected Set<CardType> makeSet(Card card, Game game) {
return attributes
.stream()
.filter(subType -> card.getCardType().contains(subType))
.filter(subType -> card.getCardType(game).contains(subType))
.collect(Collectors.toSet());
}
}

View file

@ -1,6 +1,5 @@
package mage.abilities.dynamicvalue.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
@ -31,7 +30,7 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return getStream(game, sourceAbility)
.filter(card -> !card.isCopy() && !(card instanceof PermanentToken))
.map(MageObject::getCardType)
.map(card -> card.getCardType(game))
.flatMap(Collection::stream)
.distinct()
.mapToInt(x -> 1)

View file

@ -20,7 +20,7 @@ public enum InstantSorceryExileGraveyardCount implements DynamicValue {
if (player != null) {
int exileCount = 0;
for (Card exiledCard : game.getExile().getAllCards(game)) {
if (exiledCard.getOwnerId().equals(player.getId()) && exiledCard.isInstantOrSorcery()) {
if (exiledCard.getOwnerId().equals(player.getId()) && exiledCard.isInstantOrSorcery(game)) {
exileCount++;
}
}

View file

@ -41,7 +41,7 @@ public class ParleyCount implements DynamicValue, MageSingleton {
if (player != null) {
Card card = player.getLibrary().getFromTop(game);
if (card != null) {
if (!card.isLand()) {
if (!card.isLand(game)) {
parleyValue++;
}
player.revealCards(sourceObject.getIdName() + " (" + player.getName() + ')', new CardsImpl(card), game);

View file

@ -35,7 +35,7 @@ public class ApplyCountersEffect extends ContinuousEffectImpl {
}
}
if (layer == Layer.PTChangingEffects_7 && sublayer == SubLayer.Counters_7d) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE, game)) {
for (BoostCounter counter : permanent.getCounters(game).getBoostCounters()) {
permanent.addPower(counter.getPower() * counter.getCount());
permanent.addToughness(counter.getToughness() * counter.getCount());

View file

@ -85,7 +85,7 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
if (card == null || player == null) {
return false;
}
if (!card.isLand()) {
if (!card.isLand(game)) {
if (card instanceof SplitCard) {
Card leftCard = ((SplitCard) card).getLeftHalfCard();
player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());

View file

@ -67,7 +67,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null) {
firstCardFace = card;
card = card.getSecondCardFace();
if (!card.isEnchantment() || !card.hasSubtype(SubType.AURA, game)) {
if (!card.isEnchantment(game) || !card.hasSubtype(SubType.AURA, game)) {
return false;
}
}
@ -205,11 +205,11 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
if (((ZoneChangeEvent) event).getToZone() == Zone.BATTLEFIELD
&& (((ZoneChangeEvent) event).getFromZone() != Zone.STACK)) {
Card card = game.getCard(event.getTargetId());
return card != null && (card.isEnchantment() && card.hasSubtype(SubType.AURA, game)
return card != null && (card.isEnchantment(game) && card.hasSubtype(SubType.AURA, game)
|| // in case of transformable enchantments
(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null
&& card.getSecondCardFace() != null
&& card.getSecondCardFace().isEnchantment()
&& card.getSecondCardFace().isEnchantment(game)
&& card.getSecondCardFace().hasSubtype(SubType.AURA, game)));
}
return false;

View file

@ -26,7 +26,7 @@ public class EquipEffect extends AttachEffect {
// state-based action. See rule 704.) An Equipment cant equip more than one creature. If a spell or ability
// would cause an Equipment to equip more than one creature, the Equipments controller chooses which creature
// it equips.
if (sourcePermanent != null && sourcePermanent.hasSubtype(SubType.EQUIPMENT, game) && !sourcePermanent.isCreature()) {
if (sourcePermanent != null && sourcePermanent.hasSubtype(SubType.EQUIPMENT, game) && !sourcePermanent.isCreature(game)) {
return super.apply(game, source);
}
return false;

View file

@ -99,9 +99,9 @@ public class CopyEffect extends ContinuousEffectImpl {
permanent.getColor(game).setColor(copyFromObject.getColor(game));
permanent.getManaCost().clear();
permanent.getManaCost().add(copyFromObject.getManaCost());
permanent.getCardType().clear();
for (CardType type : copyFromObject.getCardType()) {
permanent.addCardType(type);
permanent.removeAllCardTypes(game);
for (CardType type : copyFromObject.getCardType(game)) {
permanent.addCardType(game, type);
}
permanent.removeAllSubTypes(game);

View file

@ -27,9 +27,9 @@ public class CopyTokenEffect extends ContinuousEffectImpl {
Permanent permanent = game.getPermanent(source.getSourceId());
permanent.setName(token.getName());
permanent.getColor(game).setColor(token.getColor(game));
permanent.getCardType().clear();
for (CardType type : token.getCardType()) {
permanent.addCardType(type);
permanent.removeAllCardTypes(game);
for (CardType type : token.getCardType(game)) {
permanent.addCardType(game, type);
}
permanent.removeAllSubTypes(game);
permanent.copySubTypesFrom(game, token);

View file

@ -39,8 +39,8 @@ public class DamageEachOtherEffect extends OneShotEffect {
}
if (sourceCreature != null && targetCreature != null
&& sourceCreature.isCreature()
&& targetCreature.isCreature()) {
&& sourceCreature.isCreature(game)
&& targetCreature.isCreature(game)) {
targetCreature.damage(sourceCreature.getPower().getValue(), sourceCreature.getId(), source, game, false, true);
if (sourceOnBattlefield) {
sourceCreature.damage(targetCreature.getPower().getValue(), targetCreature.getId(), source, game, false, true);

View file

@ -31,7 +31,7 @@ public class FightTargetSourceEffect extends OneShotEffect {
Permanent creature1 = game.getPermanent(getTargetPointer().getFirst(game, source));
// 20110930 - 701.10
if (creature1 != null && sourcePermanent != null) {
if (creature1.isCreature() && sourcePermanent.isCreature()) {
if (creature1.isCreature(game) && sourcePermanent.isCreature(game)) {
return sourcePermanent.fight(creature1, source, game);
}
}

View file

@ -55,7 +55,7 @@ public class FightTargetsEffect extends OneShotEffect {
Permanent creature2 = game.getPermanent(target2Id);
// 20110930 - 701.10
if (creature1 != null && creature2 != null) {
if (creature1.isCreature() && creature2.isCreature()) {
if (creature1.isCreature(game) && creature2.isCreature(game)) {
return creature1.fight(creature2, source, game);
}
}

View file

@ -24,8 +24,8 @@ public class FortifyEffect extends AttachEffect{
// fortify keyword ability. Rules 301.5ae apply to Fortifications in relation to lands just as they apply to
// Equipment in relation to creatures, with one clarification relating to rule 301.5c: a Fortification thats
// also a creature (not a land) cant fortify a land. (See rule 702.66, Fortify.)
if (sourcePermanent != null && sourcePermanent.hasSubtype(SubType.FORTIFICATION, game) && !sourcePermanent.isCreature()
&& !sourcePermanent.isLand()) {
if (sourcePermanent != null && sourcePermanent.hasSubtype(SubType.FORTIFICATION, game) && !sourcePermanent.isCreature(game)
&& !sourcePermanent.isLand(game)) {
return super.apply(game, source);
}
return false;

View file

@ -60,7 +60,7 @@ public class HideawayPlayEffect extends OneShotEffect {
* If the removed card is a land, you may play it as a result of the last ability only if it's your turn
* and you haven't already played a land that turn. This counts as your land play for the turn.
*/
if (card.isLand()) {
if (card.isLand(game)) {
UUID playerId = controller.getId();
if (!game.isActivePlayer(playerId) || !game.getPlayer(playerId).canPlayLand()) {
return false;

View file

@ -46,7 +46,7 @@ public class PreventDamageByTargetEffect extends PreventionEffectImpl {
if (!this.used && super.applies(event, source, game)) {
MageObject mageObject = game.getObject(event.getSourceId());
if (mageObject != null
&& mageObject.isInstantOrSorcery()) {
&& mageObject.isInstantOrSorcery(game)) {
for (Target target : source.getTargets()) {
if (target instanceof TargetSpell) {
if (((TargetSpell) target).getSourceIds().contains(event.getSourceId())) {

View file

@ -41,7 +41,7 @@ public class RevealTopLandToBattlefieldElseHandEffect extends OneShotEffect {
}
cards.add(card);
controller.revealCards(sourceObject.getName(), cards, game);
if (card.isLand()) {
if (card.isLand(game)) {
return controller.moveCards(card, Zone.BATTLEFIELD, source, game);
} else {
controller.moveCards(card, Zone.HAND, source, game);

View file

@ -40,7 +40,7 @@ public class UntapAllThatAttackedEffect extends OneShotEffect {
Set<MageObjectReference> attackedThisTurn = watcher.getAttackedThisTurnCreatures();
for (MageObjectReference mor : attackedThisTurn) {
Permanent permanent = mor.getPermanent(game);
if (permanent != null && permanent.isCreature()) {
if (permanent != null && permanent.isCreature(game)) {
permanent.untap(game);
}
}

View file

@ -62,7 +62,7 @@ public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEff
if (payAlsoForAttackingPlaneswalker) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null
&& permanent.isPlaneswalker()
&& permanent.isPlaneswalker(game)
&& permanent.isControlledBy(source.getControllerId())) {
return true;
}

View file

@ -34,7 +34,7 @@ public class AddCardTypeAttachedEffect extends ContinuousEffectImpl {
if (equipment != null && equipment.getAttachedTo() != null) {
Permanent target = game.getPermanent(equipment.getAttachedTo());
if (target != null) {
target.addCardType(addedCardType);
target.addCardType(game, addedCardType);
}
}
return true;

View file

@ -9,6 +9,7 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
@ -16,7 +17,7 @@ import java.util.Locale;
*/
public class AddCardTypeSourceEffect extends ContinuousEffectImpl {
private final ArrayList<CardType> addedCardTypes = new ArrayList<>();
private final List<CardType> addedCardTypes = new ArrayList<>();
public AddCardTypeSourceEffect(Duration duration, CardType... addedCardType) {
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
@ -46,7 +47,7 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && affectedObjectList.contains(new MageObjectReference(permanent, game))) {
for (CardType cardType : addedCardTypes) {
permanent.addCardType(cardType);
permanent.addCardType(game, cardType);
}
return true;
} else if (this.getDuration() == Duration.Custom) {

View file

@ -8,6 +8,7 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
@ -16,7 +17,7 @@ import java.util.UUID;
*/
public class AddCardTypeTargetEffect extends ContinuousEffectImpl {
private final ArrayList<CardType> addedCardTypes = new ArrayList<>();
private final List<CardType> addedCardTypes = new ArrayList<>();
public AddCardTypeTargetEffect(Duration duration, CardType... addedCardType) {
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
@ -43,7 +44,7 @@ public class AddCardTypeTargetEffect extends ContinuousEffectImpl {
Permanent target = game.getPermanent(targetId);
if (target != null) {
for (CardType cardType : addedCardTypes) {
target.addCardType(cardType);
target.addCardType(game, cardType);
}
result = true;
}

View file

@ -102,8 +102,8 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
if (land == null) {
continue;
}
if (!land.isLand()) {
land.addCardType(CardType.LAND);
if (!land.isLand(game)) {
land.addCardType(game, CardType.LAND);
}
if (loseOther) {
// 305.7 Note that this doesn't remove any abilities

View file

@ -98,8 +98,8 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
break;
case TypeChangingEffects_4:
for (CardType t : token.getCardType()) {
permanent.addCardType(t);
for (CardType t : token.getCardType(game)) {
permanent.addCardType(game, t);
}
if (theyAreStillType != null || loseTypes) {
permanent.removeAllCreatureTypes(game);

View file

@ -70,11 +70,11 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
switch (loseType) {
case ALL:
case ALL_BUT_COLOR:
permanent.getCardType().clear();
permanent.removeAllCardTypes(game);
break;
}
for (CardType t : token.getCardType()) {
permanent.addCardType(t);
for (CardType t : token.getCardType(game)) {
permanent.addCardType(game, t);
}
// sub type

View file

@ -28,7 +28,7 @@ public class BecomesCreatureIfVehicleEffect extends ContinuousEffectImpl {
if (aura != null && aura.getAttachedTo() != null) {
Permanent enchanted = game.getPermanent(aura.getAttachedTo());
if (enchanted != null && enchanted.hasSubtype(SubType.VEHICLE, game)) {
enchanted.addCardType(addedType);
enchanted.addCardType(game, addedType);
}
}

View file

@ -91,14 +91,14 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
switch (layer) {
case TypeChangingEffects_4:
if (losePreviousTypes) {
permanent.getCardType().clear();
permanent.removeAllCardTypes(game);
}
for (CardType cardType : token.getCardType()) {
permanent.addCardType(cardType);
for (CardType cardType : token.getCardType(game)) {
permanent.addCardType(game, cardType);
}
if (theyAreStillType != null && theyAreStillType.isEmpty()
|| theyAreStillType == null && permanent.isLand()) {
|| theyAreStillType == null && permanent.isLand(game)) {
permanent.removeAllCreatureTypes(game);
}
permanent.copySubTypesFrom(game, token);

View file

@ -81,8 +81,8 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl {
break;
case TypeChangingEffects_4:
for (CardType t : token.getCardType()) {
permanent.addCardType(t);
for (CardType t : token.getCardType(game)) {
permanent.addCardType(game, t);
}
if (loseAllAbilities || removeSubtypes) {
permanent.removeAllCreatureTypes(game);

View file

@ -40,8 +40,8 @@ public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl impleme
this.discard();
return false;
}
permanent.getCardType().clear();
permanent.getCardType().add(CardType.ENCHANTMENT);
permanent.removeAllCardTypes(game);
permanent.addCardType(game, CardType.ENCHANTMENT);
permanent.retainAllEnchantmentSubTypes(game);
permanent.setIsAllCreatureTypes(game, false);
return true;

View file

@ -73,8 +73,8 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
case TypeChangingEffects_4:
permanent.setName("");
permanent.getSuperType().clear();
permanent.getCardType().clear();
permanent.addCardType(CardType.CREATURE);
permanent.removeAllCardTypes(game);
permanent.addCardType(game, CardType.CREATURE);
permanent.removeAllSubTypes(game);
permanent.getManaCost().clear();
break;

View file

@ -134,8 +134,8 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen
case TypeChangingEffects_4:
permanent.setName("");
permanent.getSuperType().clear();
permanent.getCardType().clear();
permanent.addCardType(CardType.CREATURE);
permanent.removeAllCardTypes(game);
permanent.addCardType(game, CardType.CREATURE);
permanent.removeAllSubTypes(game);
break;
case ColorChangingEffects_5:

View file

@ -78,7 +78,7 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
int affectedTargets = 0;
for (UUID permanentId : targetPointer.getTargets(game, source)) {
Permanent target = game.getPermanent(permanentId);
if (target != null && target.isCreature()) {
if (target != null && target.isCreature(game)) {
target.addPower(power.calculate(game, source, this));
target.addToughness(toughness.calculate(game, source, this));
affectedTargets++;

View file

@ -55,12 +55,12 @@ public class CastAsThoughItHadFlashAllEffect extends AsThoughEffectImpl {
Card card = game.getCard(affectedSpellId);
if (card != null) {
//Allow lands with morph to be played at instant speed
if (card.isLand()) {
if (card.isLand(game)) {
boolean morphAbility = card.getAbilities().stream().anyMatch(ability -> ability instanceof MorphAbility);
if (morphAbility) {
Card cardCopy = card.copy();
cardCopy.getCardType().clear();
cardCopy.addCardType(CardType.CREATURE);
cardCopy.removeAllCardTypes(game);
cardCopy.addCardType(game, CardType.CREATURE);
return filter.match(cardCopy, source.getSourceId(), affectedControllerId, game);
}
}

View file

@ -2,25 +2,29 @@ package mage.abilities.effects.common.continuous;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.*;
import mage.game.Game;
import mage.util.CardUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Warning, do not copy it - hasSubTypeForDeckbuilding uses it to find additional subtypes in cards
*
* @author TheElk801
*/
public class HasSubtypesSourceEffect extends ContinuousEffectImpl {
public final class HasSubtypesSourceEffect extends ContinuousEffectImpl {
private final List<SubType> subtypes = new ArrayList<>();
public HasSubtypesSourceEffect(SubType... subTypes) {
super(Duration.EndOfGame, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
subtypes.addAll(Arrays.asList(subTypes));
this.staticText = setText();
}
public HasSubtypesSourceEffect(final HasSubtypesSourceEffect effect) {
@ -45,30 +49,16 @@ public class HasSubtypesSourceEffect extends ContinuousEffectImpl {
return true;
}
private String setText() {
String s = "{this} is also ";
switch (subtypes.size()) {
case 0:
throw new UnsupportedOperationException("Can't have zero subtypes");
case 1:
s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0);
break;
case 2:
s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0);
s += " and ";
s += subtypes.get(1).getIndefiniteArticle() + " " + subtypes.get(1);
break;
default:
for (int i = 0; i < subtypes.size(); i++) {
if (i == 0) {
s += subtypes.get(i).getIndefiniteArticle() + " " + subtypes.get(i) + ", ";
} else if (i == subtypes.size() - 1) {
s += "and " + subtypes.get(i);
} else {
s += subtypes.get(i) + ", ";
}
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
return s;
return "{this} is also " + subtypes.get(0).getIndefiniteArticle() + ' '
+ CardUtil.concatWithAnd(subtypes.stream().map(SubType::getDescription).collect(Collectors.toList()));
}
public boolean hasSubtype(SubType subType) {
return subtypes.contains(subType);
}
}

View file

@ -49,7 +49,7 @@ public class LoseArtifactTypeTargetEffect extends ContinuousEffectImpl {
if (permanent == null) {
continue;
}
permanent.getCardType().remove(CardType.ARTIFACT);
permanent.removeCardType(game, CardType.ARTIFACT);
permanent.removeAllSubTypes(game, SubTypeSet.ArtifactType);
return true;
}

View file

@ -61,8 +61,8 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement
if (permanent == null) {
return false;
}
permanent.getCardType().remove(CardType.CREATURE);
if (!permanent.isTribal()) {
permanent.removeCardType(game, CardType.CREATURE);
if (!permanent.isTribal(game)) {
permanent.removeAllCreatureTypes(game);
}
if (permanent.isAttacking() || permanent.getBlocking() > 0) {

View file

@ -93,7 +93,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
}
// can't cast without mana cost
if (!cardToCheck.isLand() && cardToCheck.getManaCost().isEmpty()) {
if (!cardToCheck.isLand(game) && cardToCheck.getManaCost().isEmpty()) {
return false;
}

View file

@ -32,7 +32,7 @@ public class SpellsCostReductionAllOfChosenCardTypeEffect extends SpellsCostRedu
protected boolean selectedByRuntimeData(Card card, Ability source, Game game) {
Object savedType = game.getState().getValue(source.getSourceId() + "_type");
if (savedType instanceof String) {
return card.getCardType().contains(CardType.fromString((String) savedType));
return card.getCardType(game).contains(CardType.fromString((String) savedType));
}
return false;
}

View file

@ -9,10 +9,7 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.common.DamagedByWatcher;
/**

View file

@ -72,7 +72,7 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
}
// can't cast without mana cost
if (!cardToCheck.isLand() && cardToCheck.getManaCost().isEmpty()) {
if (!cardToCheck.isLand(game) && cardToCheck.getManaCost().isEmpty()) {
return false;
}

View file

@ -106,7 +106,7 @@ public class ExploreSourceEffect extends OneShotEffect {
permanentController.revealCards("Explored card", cards, game);
cardWasRevealed = true;
if (card != null) {
if (card.isLand()) {
if (card.isLand(game)) {
permanentController.moveCards(card, Zone.HAND, source, game);
} else {
if (game.getState().getZone(permanentId) == Zone.BATTLEFIELD) { // needed in case LKI object is used

View file

@ -51,7 +51,7 @@ public class ManifestEffect extends OneShotEffect {
Set<Card> cards = controller.getLibrary().getTopCards(game, amount);
for (Card card : cards) {
ManaCosts manaCosts = null;
if (card.isCreature()) {
if (card.isCreature(game)) {
manaCosts = card.getSpellAbility() != null ? card.getSpellAbility().getManaCosts() : null;
if (manaCosts == null) {
manaCosts = new ManaCostsImpl("{0}");

View file

@ -54,7 +54,7 @@ public class ManifestTargetPlayerEffect extends OneShotEffect {
Set<Card> cards = targetPlayer.getLibrary().getTopCards(game, amount);
for (Card card : cards) {
ManaCosts manaCosts = null;
if (card.isCreature()) {
if (card.isCreature(game)) {
manaCosts = card.getSpellAbility().getManaCosts();
if (manaCosts == null) {
manaCosts = new ManaCostsImpl("{0}");

View file

@ -1,6 +1,5 @@
package mage.abilities.hint.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.hint.Hint;
import mage.cards.Card;
@ -35,7 +34,7 @@ public enum CardTypesInGraveyardHint implements Hint {
return null;
}
List<String> types = stream
.map(MageObject::getCardType)
.map(card -> card.getCardType(game))
.flatMap(Collection::stream)
.distinct()
.map(CardType::toString)

View file

@ -8,14 +8,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.AttachEffect;
import mage.cards.Card;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SpellAbilityCastMode;
import mage.constants.SpellAbilityType;
import mage.constants.SubType;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
@ -24,9 +17,8 @@ import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* 702.102. Bestow
*
* <p>
* 702.102a Bestow represents two static abilities, one that functions while the
* card with bestow is on the stack and another that functions both while it's
* on stack and while it's on the battlefield. "Bestow [cost]" means "You may
@ -36,61 +28,60 @@ import mage.target.common.TargetCreaturePermanent;
* spell has an illegal target as it resolves and or the permanent this spell
* becomes, becomes unattached." Paying a card's bestow cost follows the rules
* for paying alternative costs in rules 601.2b and 601.2e-g.
*
* <p>
* 702.102b If a spell's controller chooses to pay its bestow cost, that player
* chooses a legal target for that Aura spell as defined by its enchant creature
* ability and rule 601.2c. See also rule 303.4.
*
* <p>
* 702.102c A spell's controller can't choose to pay its bestow cost unless that
* player can choose a legal target for that spell after it becomes an Aura
* spell.
*
* <p>
* 702.102d As an Aura spell with bestow begins resolving, if its target is
* illegal, the effect making it an Aura spell ends. It continues resolving as a
* creature spell and will be put onto the battlefield under the control of the
* spell's controller. This is an exception to rule 608.3a.
*
* <p>
* 702.102e If an Aura with bestow is attached to an illegal object or player,
* it becomes unattached. This is an exception to rule 704.5n.
*
* <p>
* You don't choose whether the spell is going to be an Aura spell or not until
* the spell is already on the stack. Abilities that affect when you can cast a
* spell, such as flash, will apply to the creature card in whatever zone you're
* casting it from. For example, an effect that said you can cast creature
* spells as though they have flash will allow you to cast a creature card with
* bestow as an Aura spell anytime you could cast an instant.
*
* <p>
* On the stack, a spell with bestow is either a creature spell or an Aura
* spell. It's never both.
*
* <p>
* Unlike other Aura spells, an Aura spell with bestow isn't countered if its
* target is illegal as it begins to resolve. Rather, the effect making it an
* Aura spell ends, it loses enchant creature, it returns to being an
* enchantment creature spell, and it resolves and enters the battlefield as an
* enchantment creature.
*
* <p>
* Unlike other Auras, an Aura with bestow isn't put into its owner's graveyard
* if it becomes unattached. Rather, the effect making it an Aura ends, it loses
* enchant creature, and it remains on the battlefield as an enchantment
* creature. It can attack (and its {T} abilities can be activated, if it has
* any) on the turn it becomes unattached if it's been under your control
* continuously, even as an Aura, since your most recent turn began.
*
* <p>
* If a permanent with bestow enters the battlefield by any method other than
* being cast, it will be an enchantment creature. You can't choose to pay the
* bestow cost and have it become an Aura.
*
* <p>
* Auras attached to a creature don't become tapped when the creature becomes
* tapped. Except in some rare cases, an Aura with bestow remains untapped when
* it becomes unattached and becomes a creature.
*
*
* @author LevelX2
*/
public class BestowAbility extends SpellAbility {
public BestowAbility(Card card, String manaString) {
super(new ManaCostsImpl(manaString), card.getName() + " using bestow");
super(new ManaCostsImpl<>(manaString), card.getName() + " using bestow");
this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
this.spellAbilityCastMode = SpellAbilityCastMode.BESTOW;
this.timing = TimingRule.SORCERY;
@ -127,15 +118,10 @@ public class BestowAbility extends SpellAbility {
MageObject basicObject = permanent.getBasicMageObject(game);
if (basicObject != null) {
basicObject.getSubtype().remove(SubType.AURA);
if (!basicObject.isCreature()) {
basicObject.addCardType(CardType.CREATURE);
}
basicObject.addCardType(CardType.CREATURE);
}
permanent.getSubtype().remove(SubType.AURA);
if (!permanent.isCreature()) {
permanent.addCardType(CardType.CREATURE);
}
permanent.addCardType(CardType.CREATURE);
}
}
@ -143,8 +129,8 @@ public class BestowAbility extends SpellAbility {
// permanently changes to the object
if (card != null) {
card.addSubType(SubType.AURA);
card.getCardType().remove(CardType.CREATURE);
card.getCardType().add(CardType.ENCHANTMENT);
card.removeCardType(CardType.CREATURE);
card.addCardType(CardType.ENCHANTMENT);
}
}
}
@ -172,14 +158,15 @@ class BestowEntersBattlefieldEffect extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent bestowPermanent = game.getPermanentEntering(source.getSourceId());
if (bestowPermanent != null) {
if (bestowPermanent.hasSubtype(SubType.AURA, game)) {
MageObject basicObject = bestowPermanent.getBasicMageObject(game);
if (basicObject != null && !basicObject.getSubtype().contains(SubType.AURA)) {
basicObject.getSubtype(null).add(SubType.AURA);
basicObject.getCardType().remove(CardType.CREATURE);
}
}
if (bestowPermanent == null || !bestowPermanent.hasSubtype(SubType.AURA, game)) {
return false;
}
// change types permanently
MageObject basicObject = bestowPermanent.getBasicMageObject(game);
if (basicObject != null && !basicObject.getSubtype().contains(SubType.AURA)) {
basicObject.addSubType(SubType.AURA);
basicObject.removeCardType(CardType.CREATURE);
}
return false;
}

View file

@ -54,7 +54,7 @@ public class BoastAbility extends ActivatedAbilityImpl {
return 1;
}
Permanent permanent = game.getPermanent(getSourceId());
if (permanent != null && !permanent.isCreature()) {
if (permanent != null && !permanent.isCreature(game)) {
return 1;
}
return game.getBattlefield()

View file

@ -124,7 +124,7 @@ class CascadeEffect extends OneShotEffect {
Card cardToCast = null;
for (Card card : controller.getLibrary().getCards(game)) {
cardsToExile.add(card);
if (!card.isLand() && card.getManaValue() < sourceCost) {
if (!card.isLand(game) && card.getManaValue() < sourceCost) {
cardToCast = card;
break;
}

View file

@ -9,7 +9,9 @@ import mage.constants.Zone;
import java.util.Set;
/*
/**
* Allows card to be companion
*
* @author emerald000
*/
public class CompanionAbility extends SpecialAction {

View file

@ -5,7 +5,9 @@ import mage.cards.Card;
import java.io.Serializable;
import java.util.Set;
/*
/**
* Checking deck for companion legality
*
* @author emerald000
*/
public interface CompanionCondition extends Serializable {
@ -16,7 +18,7 @@ public interface CompanionCondition extends Serializable {
String getRule();
/**
* @param deck The set of cards to check.
* @param deck The set of cards to check.
* @param startingSize
* @return Whether the companion is valid for that deck.
*/

View file

@ -8,26 +8,27 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
* FAQ 2013/01/11
*
* <p>
* 702.98. Evolve
*
* <p>
* 702.98a Evolve is a triggered ability. "Evolve" means "Whenever a creature
* enters the battlefield under your control, if that creature's power is
* greater than this creature's power and/or that creature's toughness is
* greater than this creature's toughness, put a +1/+1 counter on this
* creature."
*
* <p>
* 702.98b If a creature has multiple instances of evolve, each triggers
* separately
*
* <p>
* Rulings
*
* <p>
* When comparing the stats of the two creatures, you always compare power to
* power and toughness to toughness. Whenever a creature enters the battlefield
* under your control, check its power and toughness against the power and
@ -81,9 +82,9 @@ public class EvolveAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!event.getTargetId().equals(this.getSourceId())) {
Permanent triggeringCreature = game.getPermanent(event.getTargetId());
Permanent triggeringCreature = ((EntersTheBattlefieldEvent) event).getTarget();
if (triggeringCreature != null
&& triggeringCreature.isCreature()
&& triggeringCreature.isCreature(game)
&& triggeringCreature.isControlledBy(this.controllerId)) {
Permanent sourceCreature = game.getPermanent(sourceId);
if (sourceCreature != null && isPowerOrThoughnessGreater(sourceCreature, triggeringCreature)) {

View file

@ -58,7 +58,7 @@ class FearEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) {
return blocker.isArtifact() || blocker.getColor(game).isBlack();
return blocker.isArtifact(game) || blocker.getColor(game).isBlack();
}
@Override

View file

@ -32,7 +32,7 @@ public class HexproofFromArtifactsCreaturesAndEnchantments extends HexproofBaseA
@Override
public boolean checkObject(MageObject source, Game game) {
return source.isArtifact() || source.isCreature() || source.isEnchantment();
return source.isArtifact(game) || source.isCreature(game) || source.isEnchantment(game);
}
@Override

View file

@ -32,7 +32,7 @@ public class HexproofFromPlaneswalkersAbility extends HexproofBaseAbility {
@Override
public boolean checkObject(MageObject source, Game game) {
return source.isPlaneswalker();
return source.isPlaneswalker(game);
}
@Override

View file

@ -57,7 +57,7 @@ class IntimidateEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) {
boolean result = false;
if (blocker.isArtifact() && (blocker.isCreature())) {
if (blocker.isArtifact(game) && (blocker.isCreature(game))) {
result = true;
}
if (attacker.getColor(game).shares(blocker.getColor(game))) {

View file

@ -281,15 +281,15 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
return alternateCosts;
}
public static void setPermanentToFaceDownCreature(MageObject mageObject) {
public static void setPermanentToFaceDownCreature(MageObject mageObject, Game game) {
mageObject.getPower().modifyBaseValue(2);
mageObject.getToughness().modifyBaseValue(2);
mageObject.getAbilities().clear();
mageObject.getColor().setColor(new ObjectColor());
mageObject.getColor(game).setColor(new ObjectColor());
mageObject.setName("");
mageObject.getCardType().clear();
mageObject.addCardType(CardType.CREATURE);
mageObject.getSubtype().clear();
mageObject.removeAllCardTypes(game);
mageObject.addCardType(game, CardType.CREATURE);
mageObject.removeAllSubTypes(game);
mageObject.getSuperType().clear();
mageObject.getManaCost().clear();
if (mageObject instanceof Permanent) {

View file

@ -34,7 +34,7 @@ public class OverloadAbility extends SpellAbility {
super(costs, card.getName() + " with overload");
this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
this.addEffect(effect);
this.timing = (card.isSorcery() ? TimingRule.SORCERY : TimingRule.INSTANT);
this.timing = (card.isSorcery(null) ? TimingRule.SORCERY : TimingRule.INSTANT);
}
public OverloadAbility(final OverloadAbility ability) {

View file

@ -100,7 +100,7 @@ public class ProtectionAbility extends StaticAbility {
// object is still a card and not a spell yet. So return only if the source object can't be a spell
// otherwise the following FilterObject check will be applied
if (source instanceof StackObject
|| !source.isInstantOrSorcery()) {
|| !source.isInstantOrSorcery(game)) {
return true;
}
}
@ -108,7 +108,7 @@ public class ProtectionAbility extends StaticAbility {
// Emrakul, the Aeons Torn
if (filter instanceof FilterStackObject) {
if (filter.match(source, game)) {
return !source.isInstantOrSorcery();
return !source.isInstantOrSorcery(game);
}
}

View file

@ -50,7 +50,7 @@ public class RecoverAbility extends TriggeredAbilityImpl {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent()) {
if (zEvent.getTarget().isOwnedBy(getControllerId())
&& zEvent.getTarget().isCreature()
&& zEvent.getTarget().isCreature(game)
&& !zEvent.getTarget().getId().equals(getSourceId())) {
return true;
}

View file

@ -75,7 +75,7 @@ public class SoulbondAbility extends EntersBattlefieldTriggeredAbility {
boolean self = false;
boolean other = false;
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(getControllerId())) {
if (permanent.isCreature()) {
if (permanent.isCreature(game)) {
if (permanent.getId().equals(getSourceId())) {
if (permanent.isControlledBy(getControllerId())) {
self = true;
@ -131,7 +131,7 @@ class SoulboundEntersSelfEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && permanent.isCreature()) {
if (permanent != null && permanent.isCreature(game)) {
Player controller = game.getPlayer(permanent.getControllerId());
if (controller != null) {
TargetControlledPermanent target = new TargetControlledPermanent(filter);
@ -234,11 +234,11 @@ class SoulboundEntersOtherEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && permanent.getPairedCard() == null
&& permanent.isCreature()) {
&& permanent.isCreature(game)) {
Player controller = game.getPlayer(permanent.getControllerId());
if (controller != null) {
Permanent enteringPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (enteringPermanent != null && enteringPermanent.isCreature() && enteringPermanent.getPairedCard() == null) {
if (enteringPermanent != null && enteringPermanent.isCreature(game) && enteringPermanent.getPairedCard() == null) {
enteringPermanent.setPairedCard(new MageObjectReference(permanent, game));
permanent.setPairedCard(new MageObjectReference(enteringPermanent, game));
if (!game.isSimulation()) {

View file

@ -161,7 +161,7 @@ class SpliceOntoInstantOrSorceryEffect extends SpliceCardEffectImpl {
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
MageObject object = game.getObject(abilityToModify.getSourceId());
if (object != null && object.isInstantOrSorcery()) {
if (object != null && object.isInstantOrSorcery(game)) {
return spliceSpellCanBeActivated(source, game);
}
return false;

View file

@ -24,7 +24,7 @@ public class SunburstAbility extends EntersBattlefieldAbility {
private static final String ruleCreature = "Sunburst <i>(This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.)</i>";
private static final String ruleNonCreature = "Sunburst <i>(This enters the battlefield with a charge counter on it for each color of mana spent to cast it.)</i>";
private boolean isCreature;
private final boolean isCreature;
public SunburstAbility(Card card) {
super(new SunburstEffect(), "");
@ -68,7 +68,7 @@ class SunburstEffect extends OneShotEffect {
int countersAmount = amount.calculate(game, source, this);
if (countersAmount > 0) {
Counter counter;
if (permanent.isCreature()) {
if (permanent.isCreature(game)) {
counter = CounterType.P1P1.createInstance(countersAmount);
} else {
counter = CounterType.CHARGE.createInstance(countersAmount);

View file

@ -209,7 +209,7 @@ public class SuspendAbility extends SpecialAction {
return ActivationStatus.getFalse();
}
MageObject object = game.getObject(sourceId);
return new ActivationStatus(object.isInstant()
return new ActivationStatus(object.isInstant(game)
|| object.hasAbility(FlashAbility.getInstance(), game)
|| null != game.getContinuousEffects().asThough(sourceId,
AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
@ -370,7 +370,7 @@ class SuspendPlayCardEffect extends OneShotEffect {
game, true, new ApprovingObject(source, game));
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
if (cardWasCast) {
if (card.isCreature()) {
if (card.isCreature(game)) {
ContinuousEffect effect = new GainHasteEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1));
game.addEffect(effect, source);

View file

@ -45,9 +45,9 @@ public class TransformAbility extends SimpleStaticAbility {
permanent.getColor(game).setColor(sourceCard.getColor(game));
permanent.getManaCost().clear();
permanent.getManaCost().add(sourceCard.getManaCost());
permanent.getCardType().clear();
for (CardType type : sourceCard.getCardType()) {
permanent.addCardType(type);
permanent.removeAllCardTypes(game);
for (CardType type : sourceCard.getCardType(game)) {
permanent.addCardType(game, type);
}
permanent.removeAllSubTypes(game);
permanent.copySubTypesFrom(game, sourceCard);

View file

@ -44,7 +44,7 @@ class InstantOrSorceryCastManaCondition extends ManaCondition implements Conditi
public boolean apply(Game game, Ability source) {
if (source instanceof SpellAbility) {
MageObject object = game.getObject(source.getSourceId());
return object != null && object.isInstantOrSorcery();
return object != null && object.isInstantOrSorcery(game);
}
return false;
}

View file

@ -19,7 +19,7 @@ public class ArtifactCastManaCondition extends ManaCondition implements Conditio
public boolean apply(Game game, Ability source) {
if (source instanceof SpellAbility) {
MageObject object = game.getObject(source.getSourceId());
if (object != null && object.isArtifact()) {
if (object != null && object.isArtifact(game)) {
return true;
}
}

View file

@ -18,7 +18,7 @@ public class CreatureCastManaCondition extends ManaCondition implements Conditio
public boolean apply(Game game, Ability source) {
if (source instanceof SpellAbility) {
MageObject object = game.getObject(source.getSourceId());
if (object != null && object.isCreature()) {
if (object != null && object.isCreature(game)) {
return true;
}
}

View file

@ -19,7 +19,7 @@ public class PlaneswalkerCastManaCondition extends ManaCondition implements Cond
public boolean apply(Game game, Ability source) {
if (source instanceof SpellAbility) {
MageObject object = game.getObject(source.getSourceId());
if (object != null && object.isPlaneswalker()) {
if (object != null && object.isPlaneswalker(game)) {
return true;
}
}

View file

@ -5,7 +5,9 @@ import mage.Mana;
import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.Counters;
@ -197,4 +199,16 @@ public interface Card extends MageObject {
}
return true;
}
List<CardType> getCardTypeForDeckbuilding();
boolean hasCardTypeForDeckbuilding(CardType cardType);
/**
* Checking subtype in cards for deck and companion validation
*
* @param subType
* @return
*/
boolean hasSubTypeForDeckbuilding(SubType subType);
}

View file

@ -3,8 +3,10 @@ package mage.cards;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
import mage.ObjectColor;
import mage.abilities.*;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect;
import mage.abilities.keyword.ChangelingAbility;
import mage.abilities.keyword.FlashbackAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.repository.PluginClassloaderRegistery;
@ -269,7 +271,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
&& mainCardState != null
&& !mainCardState.hasLostAllAbilities()
&& mainCardState.getAbilities().containsClass(FlashbackAbility.class)) {
FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant() ? TimingRule.INSTANT : TimingRule.SORCERY);
FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant(game) ? TimingRule.INSTANT : TimingRule.SORCERY);
flash.setSourceId(this.getId());
flash.setControllerId(this.getOwnerId());
flash.setSpellAbilityType(this.getSpellAbility().getSpellAbilityType());
@ -830,4 +832,38 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
}
return false;
}
@Override
public List<CardType> getCardTypeForDeckbuilding() {
return getCardType();
}
@Override
public boolean hasCardTypeForDeckbuilding(CardType cardType) {
return getCardTypeForDeckbuilding().contains(cardType);
}
@Override
public boolean hasSubTypeForDeckbuilding(SubType subType) {
// own subtype
if (this.hasSubtype(subType, null)) {
return true;
}
// gained subtypes from source ability
if (this.getAbilities()
.stream()
.filter(SimpleStaticAbility.class::isInstance)
.map(Ability::getAllEffects)
.flatMap(Collection::stream)
.filter(HasSubtypesSourceEffect.class::isInstance)
.map(HasSubtypesSourceEffect.class::cast)
.anyMatch(effect -> effect.hasSubtype(subType))) {
return true;
}
// changeling (any subtype)
return subType.getSubTypeSet() == SubTypeSet.CreatureType
&& this.getAbilities().containsClass(ChangelingAbility.class);
}
}

View file

@ -1,5 +1,6 @@
package mage.cards;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.keyword.PartnerWithAbility;
@ -313,7 +314,7 @@ public abstract class ExpansionSet implements Serializable {
return booster.stream().anyMatch(card -> card.isLegendary() && card.isCreature());
}
if (needsPlaneswalker) {
return booster.stream().filter(card -> card.isPlaneswalker()).count() == 1;
return booster.stream().filter(MageObject::isPlaneswalker).count() == 1;
}
// TODO: add partner check

View file

@ -15,7 +15,6 @@ import mage.game.events.ZoneChangeEvent;
import mage.util.CardUtil;
import mage.util.SubTypes;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@ -166,10 +165,10 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
}
@Override
public ArrayList<CardType> getCardType() {
public List<CardType> getCardType(Game game) {
// CardImpl's constructor can call some code on init, so you must check left/right before
// it's a bad workaround
return leftHalfCard != null ? leftHalfCard.getCardType() : cardType;
return leftHalfCard != null ? leftHalfCard.getCardType(game) : cardType;
}
@Override

View file

@ -316,8 +316,8 @@ public class CardInfo {
return res;
}
public final ArrayList<CardType> getTypes() {
ArrayList<CardType> list = new ArrayList<>();
public final List<CardType> getTypes() {
List<CardType> list = new ArrayList<>();
for (String type : this.types.split(SEPARATOR)) {
try {
list.add(CardType.valueOf(type));
@ -327,7 +327,7 @@ public class CardInfo {
return list;
}
public final void setTypes(ArrayList<CardType> types) {
public final void setTypes(List<CardType> types) {
StringBuilder sb = new StringBuilder();
for (CardType item : types) {
sb.append(item.name()).append(SEPARATOR);

View file

@ -6,6 +6,7 @@ import mage.game.Game;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author North
@ -70,7 +71,7 @@ public enum CardType {
* @return
*/
public static CardType[] mergeTypes(CardType[] a, CardType[] b) {
ArrayList<CardType> cardTypes = new ArrayList<>();
List<CardType> cardTypes = new ArrayList<>();
cardTypes.addAll(Arrays.asList(a));
cardTypes.addAll(Arrays.asList(b));
return cardTypes.toArray(new CardType[0]);
@ -90,7 +91,7 @@ public enum CardType {
@Override
public boolean apply(MageObject input, Game game) {
return input.getCardType().contains(cardType);
return input.getCardType(game).contains(cardType);
}
@Override

Some files were not shown because too many files have changed in this diff Show more