add new TDFC class which allows for gradual transition to new refactor

This commit is contained in:
theelk801 2025-07-29 16:21:33 -04:00
parent 6b2317839b
commit acdc774e40
10 changed files with 222 additions and 51 deletions

View file

@ -1,34 +1,43 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardSetInfo;
import mage.cards.TransformingDoubleFacedCard;
import mage.constants.CardType;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author Loki
*/
public final class CloisteredYouth extends CardImpl {
public final class CloisteredYouth extends TransformingDoubleFacedCard {
public CloisteredYouth(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.HUMAN);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
super(
ownerId, setInfo,
new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN}, "{1}{W}",
"Unholy Fiend",
new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HORROR}, "B"
);
this.getLeftHalfCard().setPT(1, 1);
this.getRightHalfCard().setPT(3, 3);
this.secondSideCardClazz = mage.cards.u.UnholyFiend.class;
// At the beginning of your upkeep, you may transform Cloistered Youth.
this.addAbility(new TransformAbility());
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), true));
this.getLeftHalfCard().addAbility(new TransformAbility());
this.getLeftHalfCard().addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), true));
// Unholy Fiend
// At the beginning of your end step, you lose 1 life.
this.getRightHalfCard().addAbility(new BeginningOfEndStepTriggeredAbility(new LoseLifeSourceControllerEffect(1)));
this.finalizeDFC();
}
private CloisteredYouth(final CloisteredYouth card) {

View file

@ -1,33 +1,22 @@
package mage.cards.u;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.TransformingDoubleFacedCard;
import mage.cards.c.CloisteredYouth;
import mage.constants.CardType;
import mage.constants.SubType;
import java.util.UUID;
/**
*
* @author Loki
*/
public final class UnholyFiend extends CardImpl {
public UnholyFiend(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"");
this.subtype.add(SubType.HORROR);
this.color.setBlack(true);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
super(ownerId, setInfo, new CardType[]{}, "");
this.nightCard = true;
this.addAbility(new BeginningOfEndStepTriggeredAbility(new LoseLifeSourceControllerEffect(1)));
TransformingDoubleFacedCard.copyToBackFace(new CloisteredYouth(ownerId, setInfo), this);
}
private UnholyFiend(final UnholyFiend card) {

View file

@ -1,8 +1,9 @@
package org.mage.test.cards.abilities.keywords;
import mage.cards.s.SpringOfEternalPeace;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
@ -521,4 +522,52 @@ public class TransformTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Huntmaster of the Fells", 1);
assertPermanentCount(playerA, 6 + 1 + 1);
}
/**
* The following tests exist to make sure the TDFC refactor workaround functions correctly.
* They should eventually not be necessary
*/
private static final String youth = "Cloistered Youth";
@Test
public void testWorkaroundCloisteredYouth() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.HAND, playerA, youth);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, youth);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertPowerToughness(playerA, youth, 1, 1);
assertColor(playerA, youth, ObjectColor.WHITE, true);
assertColor(playerA, youth, ObjectColor.BLACK, false);
assertSubtype(youth, SubType.HUMAN);
assertNotSubtype(youth, SubType.HORROR);
}
private static final String fiend = "Unholy Fiend";
@Test
public void testWorkaroundUnholyFiend() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.HAND, playerA, youth);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, youth);
setChoice(playerA, true);
setStopAt(3, PhaseStep.END_TURN);
setStrictChooseMode(true);
execute();
assertPowerToughness(playerA, fiend, 3, 3);
assertColor(playerA, fiend, ObjectColor.BLACK, true);
assertColor(playerA, fiend, ObjectColor.WHITE, false);
assertSubtype(fiend, SubType.HORROR);
assertNotSubtype(fiend, SubType.HUMAN);
assertLife(playerA, 20 - 1);
}
}

View file

@ -1,5 +1,6 @@
package mage.cards;
import mage.MageInt;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Abilities;
@ -250,6 +251,10 @@ public interface Card extends MageObject, Ownerable {
List<UUID> getAttachments();
void setPT(int power, int toughness);
void setPT(MageInt power, MageInt toughness);
/**
* @param attachment can be any object: card, permanent, token
* @param source can be null for default checks like state base

View file

@ -1,5 +1,6 @@
package mage.cards;
import mage.MageInt;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
@ -393,6 +394,17 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
this.abilities.setControllerId(ownerId);
}
@Override
public void setPT(int power, int toughness) {
this.setPT(new MageInt(power), new MageInt(toughness));
}
@Override
public void setPT(MageInt power, MageInt toughness) {
this.power = power;
this.toughness = toughness;
}
@Override
public UUID getControllerOrOwnerId() {
return getOwnerId();

View file

@ -1,7 +1,5 @@
package mage.cards;
import mage.MageInt;
/**
* @author JayDi85
*/
@ -9,8 +7,4 @@ public interface ModalDoubleFacedCardHalf extends SubCard<ModalDoubleFacedCard>
@Override
ModalDoubleFacedCardHalf copy();
void setPT(int power, int toughness);
void setPT(MageInt power, MageInt toughness);
}

View file

@ -114,17 +114,6 @@ public class ModalDoubleFacedCardHalfImpl extends CardImpl implements ModalDoubl
return this.parentCard;
}
@Override
public void setPT(int power, int toughness) {
this.setPT(new MageInt(power), new MageInt(toughness));
}
@Override
public void setPT(MageInt power, MageInt toughness) {
this.power = power;
this.toughness = toughness;
}
@Override
public String getIdName() {
// id must send to main card (popup card hint in game logs)

View file

@ -0,0 +1,90 @@
package mage.cards;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import java.util.UUID;
/**
* @author TheElk801
*/
public abstract class TransformingDoubleFacedCard extends CardImpl {
protected TransformingDoubleFacedCardHalfImpl leftHalfCard; // main card in all zone
protected TransformingDoubleFacedCardHalfImpl rightHalfCard; // second side card, can be only in stack and battlefield zones
public TransformingDoubleFacedCard(
UUID ownerId, CardSetInfo setInfo,
CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft,
String secondSideName,
CardType[] typesRight, SubType[] subTypesRight, String colorRight
) {
this(
ownerId, setInfo,
new SuperType[]{}, typesLeft, subTypesLeft, costsLeft,
secondSideName,
new SuperType[]{}, typesRight, subTypesRight, colorRight
);
}
public TransformingDoubleFacedCard(
UUID ownerId, CardSetInfo setInfo,
SuperType[] superTypesLeft, CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft,
String secondSideName,
SuperType[] superTypesRight, CardType[] typesRight, SubType[] subTypesRight, String colorRight
) {
super(ownerId, setInfo, typesLeft, costsLeft);
this.leftHalfCard = new TransformingDoubleFacedCardHalfImpl(ownerId, setInfo, costsLeft);
this.rightHalfCard = new TransformingDoubleFacedCardHalfImpl(ownerId, setInfo, "");
for (SuperType superType : superTypesLeft) {
this.getLeftHalfCard().getSuperType().add(superType);
}
this.getLeftHalfCard().getSubtype().add(subTypesLeft);
for (SuperType superType : superTypesRight) {
this.getRightHalfCard().getSuperType().add(superType);
}
this.getRightHalfCard().addCardType(typesRight);
this.getRightHalfCard().setName(secondSideName);
this.getRightHalfCard().addSubType(subTypesRight);
this.getRightHalfCard().getColor().addColor(new ObjectColor(colorRight));
}
protected TransformingDoubleFacedCard(final TransformingDoubleFacedCard card) {
super(card);
this.leftHalfCard = card.leftHalfCard.copy();
this.rightHalfCard = card.rightHalfCard.copy();
}
public Card getLeftHalfCard() {
return leftHalfCard;
}
public Card getRightHalfCard() {
return rightHalfCard;
}
protected void finalizeDFC() {
this.getSuperType().addAll(this.getLeftHalfCard().getSuperType());
this.getCardType().addAll(this.getLeftHalfCard().getCardType());
this.getSubtype().addAll(this.getLeftHalfCard().getSubtype());
for (Ability ability : this.getLeftHalfCard().getAbilities()) {
this.addAbility(ability);
}
this.power = this.getLeftHalfCard().getPower().copy();
this.toughness = this.getLeftHalfCard().getToughness().copy();
}
public static void copyToBackFace(TransformingDoubleFacedCard tdfc, Card card) {
card.getColor().setColor(tdfc.getRightHalfCard().getColor());
card.getSuperType().addAll(tdfc.getRightHalfCard().getSuperType());
card.getCardType().addAll(tdfc.getRightHalfCard().getCardType());
card.getSubtype().addAll(tdfc.getRightHalfCard().getSubtype());
for (Ability ability : tdfc.getRightHalfCard().getAbilities()) {
card.addAbility(ability);
}
card.setPT(tdfc.getRightHalfCard().getPower().copy(), tdfc.getRightHalfCard().getToughness().copy());
}
}

View file

@ -0,0 +1,24 @@
package mage.cards;
import mage.constants.CardType;
import java.util.UUID;
/**
* @author TheElk801
*/
public class TransformingDoubleFacedCardHalfImpl extends CardImpl {
TransformingDoubleFacedCardHalfImpl(UUID ownerId, CardSetInfo setInfo, String costs) {
super(ownerId, setInfo, new CardType[]{}, costs);
}
private TransformingDoubleFacedCardHalfImpl(final TransformingDoubleFacedCardHalfImpl card) {
super(card);
}
@Override
public TransformingDoubleFacedCardHalfImpl copy() {
return new TransformingDoubleFacedCardHalfImpl(this);
}
}

View file

@ -777,6 +777,16 @@ public class Spell extends StackObjectImpl implements Card {
public void setOwnerId(UUID controllerId) {
}
@Override
public void setPT(int power, int toughness) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public void setPT(MageInt power, MageInt toughness) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public List<String> getRules() {
return card.getRules();