[NEO] Implementing Compleated mechanic and hybrid phyrexian mana (ready for review) (#8677)

* [NEO] Implemented Tamiyo, Compleated Sage

* replaced PhyrexianManaCost calls with ManaCostsImpl calls

* updated phyrexian mana implementation

* added phyrexian hybrid symbol support

* updated starting loyalty implementation for planeswalkers

* change compleated to singleton

* implemented Compleated ability

* added some missing loyalty setters

* changed when loyalty is added to a walker to fix bugs

* slight change to some tests to fix them from failing

* fixed token issue
This commit is contained in:
Evan Kranzler 2022-02-10 10:25:23 -05:00 committed by GitHub
parent 3709b5c098
commit 54203c16d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
293 changed files with 865 additions and 870 deletions

View file

@ -3,7 +3,6 @@ package mage;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
@ -40,6 +39,7 @@ public abstract class MageObjectImpl implements MageObject {
protected String text;
protected MageInt power;
protected MageInt toughness;
protected int startingLoyalty = -1; // -2 means X, -1 means none, 0 and up is normal
protected boolean copy;
protected MageObject copyFrom; // copied card INFO (used to call original adjusters)
@ -68,6 +68,7 @@ public abstract class MageObjectImpl implements MageObject {
frameStyle = object.frameStyle;
power = object.power.copy();
toughness = object.toughness.copy();
startingLoyalty = object.startingLoyalty;
abilities = object.abilities.copy();
this.cardType.addAll(object.cardType);
this.subtype.copyFrom(object.subtype);
@ -167,21 +168,12 @@ public abstract class MageObjectImpl implements MageObject {
@Override
public int getStartingLoyalty() {
for (Ability ab : getAbilities()) {
if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) {
return ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).getStartingLoyalty();
}
}
return 0;
return startingLoyalty;
}
@Override
public void setStartingLoyalty(int startingLoyalty) {
for (Ability ab : getAbilities()) {
if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) {
((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).setStartingLoyalty(startingLoyalty);
}
}
this.startingLoyalty = startingLoyalty;
}
@Override

View file

@ -83,6 +83,16 @@ public enum ManaSymbol {
PHYREXIAN_R("{R/P}", R, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED),
PHYREXIAN_B("{B/P}", B, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED),
PHYREXIAN_U("{U/P}", U, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED),
PHYREXIAN_HYBRID_WU("{W/U/P}", W, U, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_WB("{W/B/P}", W, B, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_UB("{U/B/P}", U, B, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_UR("{U/R/P}", U, R, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_BR("{B/R/P}", B, R, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_BG("{B/G/P}", B, G, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_RG("{R/G/P}", R, G, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_RW("{R/W/P}", R, W, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_GW("{G/W/P}", G, W, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
PHYREXIAN_HYBRID_GU("{G/U/P}", G, U, Type.PHYREXIAN, Type.HYBRID, Type.COLORED),
SNOW("{S}", Type.SNOW);
private enum Type {

View file

@ -4,7 +4,10 @@ import mage.MageIdentifier;
import mage.MageObject;
import mage.abilities.costs.*;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.mana.*;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
@ -536,14 +539,15 @@ public abstract class AbilityImpl implements Ability {
while (costIterator.hasNext()) {
ManaCost cost = costIterator.next();
if (cost instanceof PhyrexianManaCost) {
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) cost;
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(this, this, controller.getId(), game)
&& controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + '?', this, game)) {
costIterator.remove();
costs.add(payLifeCost);
}
if (!cost.isPhyrexian()) {
continue;
}
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(this, this, controller.getId(), game)
&& controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + cost.getText().replace("/P", "") + '?', this, game)) {
costIterator.remove();
costs.add(payLifeCost);
manaCostsToPay.incrPhyrexianPaid();
}
}
}

View file

@ -6,7 +6,6 @@ import mage.abilities.condition.Condition;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.PhyrexianManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.keyword.FlashAbility;
@ -61,19 +60,13 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
public ActivatedAbilityImpl(Zone zone, Effect effect) {
super(AbilityType.ACTIVATED, zone);
if (effect != null) {
this.addEffect(effect);
}
this.addEffect(effect);
}
public ActivatedAbilityImpl(Zone zone, Effect effect, ManaCosts cost) {
super(AbilityType.ACTIVATED, zone);
if (effect != null) {
this.addEffect(effect);
}
if (cost != null) {
this.addManaCost(cost);
}
this.addEffect(effect);
this.addManaCost(cost);
}
public ActivatedAbilityImpl(Zone zone, Effects effects, ManaCosts cost) {
@ -83,30 +76,18 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
this.addEffect(effect);
}
}
if (cost != null) {
this.addManaCost(cost);
}
this.addManaCost(cost);
}
public ActivatedAbilityImpl(Zone zone, Effect effect, Cost cost) {
super(AbilityType.ACTIVATED, zone);
if (effect != null) {
this.addEffect(effect);
}
if (cost != null) {
if (cost instanceof PhyrexianManaCost) {
this.addManaCost((PhyrexianManaCost) cost);
} else {
this.addCost(cost);
}
}
this.addEffect(effect);
this.addCost(cost);
}
public ActivatedAbilityImpl(Zone zone, Effect effect, Costs<Cost> costs) {
super(AbilityType.ACTIVATED, zone);
if (effect != null) {
this.addEffect(effect);
}
this.addEffect(effect);
if (costs != null) {
for (Cost cost : costs) {
this.addCost(cost);
@ -121,15 +102,13 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
this.addEffect(effect);
}
}
if (cost != null) {
this.addCost(cost);
}
this.addCost(cost);
}
public ActivatedAbilityImpl(Zone zone, Effects effects, Costs<Cost> costs) {
super(AbilityType.ACTIVATED, zone);
for (Effect effect : effects) {
if (effect != null) {
if (effects != null) {
for (Effect effect : effects) {
this.addEffect(effect);
}
}

View file

@ -1,39 +0,0 @@
package mage.abilities.common;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.counters.CounterType;
/**
* @author LevelX2
*/
public class PlaneswalkerEntersWithLoyaltyCountersAbility extends EntersBattlefieldAbility {
private int startingLoyalty;
public PlaneswalkerEntersWithLoyaltyCountersAbility(int loyalty) {
super(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(loyalty)));
startingLoyalty = loyalty;
setRuleVisible(false);
}
public PlaneswalkerEntersWithLoyaltyCountersAbility(final PlaneswalkerEntersWithLoyaltyCountersAbility ability) {
super(ability);
startingLoyalty = ability.startingLoyalty;
}
public int getStartingLoyalty() {
return startingLoyalty;
}
public void setStartingLoyalty(int startingLoyalty) {
this.startingLoyalty = startingLoyalty;
this.getEffects().clear();
this.addEffect(new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(startingLoyalty))));
}
@Override
public PlaneswalkerEntersWithLoyaltyCountersAbility copy() {
return new PlaneswalkerEntersWithLoyaltyCountersAbility(this);
}
}

View file

@ -43,7 +43,7 @@ public class ColoredManaCost extends ManaCostImpl {
@Override
public String getText() {
return '{' + mana.toString() + '}';
return '{' + mana.toString() + (this.phyrexian ? "/P" : "") + '}';
}
@Override

View file

@ -1,8 +1,5 @@
package mage.abilities.costs.mana;
import java.util.ArrayList;
import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -10,6 +7,9 @@ import mage.constants.ColoredManaSymbol;
import mage.game.Game;
import mage.players.ManaPool;
import java.util.ArrayList;
import java.util.List;
public class HybridManaCost extends ManaCostImpl {
private final ColoredManaSymbol mana1;
@ -50,7 +50,7 @@ public class HybridManaCost extends ManaCostImpl {
@Override
public String getText() {
return '{' + mana1.toString() + '/' + mana2.toString() + '}';
return '{' + mana1.toString() + '/' + mana2.toString() + (this.phyrexian ? "/P" : "") + '}';
}
@Override

View file

@ -1,7 +1,5 @@
package mage.abilities.costs.mana;
import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -11,6 +9,8 @@ import mage.filter.Filter;
import mage.game.Game;
import mage.players.ManaPool;
import java.util.List;
public interface ManaCost extends Cost {
int manaValue();
@ -45,4 +45,7 @@ public interface ManaCost extends Cost {
@Override
ManaCost copy();
boolean isPhyrexian();
void setPhyrexian(boolean phyrexian);
}

View file

@ -25,6 +25,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
protected Mana cost;
protected ManaOptions options;
protected Filter sourceFilter;
protected boolean phyrexian = false;
public ManaCostImpl() {
payment = new Mana();
@ -41,6 +42,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if (manaCost.sourceFilter != null) {
this.sourceFilter = manaCost.sourceFilter.copy();
}
this.phyrexian = manaCost.phyrexian;
}
@Override
@ -288,4 +290,17 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
public String toString() {
return getText();
}
@Override
public boolean isPhyrexian() {
return phyrexian;
}
@Override
public void setPhyrexian(boolean phyrexian) {
if (phyrexian) {
this.options.add(Mana.GenericMana(0));
}
this.phyrexian = phyrexian;
}
}

View file

@ -54,4 +54,8 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
.collect(Collectors.toCollection(ManaCostsImpl::new));
}
void incrPhyrexianPaid();
int getPhyrexianPaid();
}

View file

@ -28,6 +28,8 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
protected final UUID id;
protected String text = null;
protected boolean phyrexian = false;
private int phyrexianPaid = 0;
private static final Map<String, ManaCosts> costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls
@ -46,6 +48,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
for (T cost : costs) {
this.add(cost.copy());
}
this.phyrexian = costs.phyrexian;
}
@Override
@ -175,58 +178,53 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
while (manaCostIterator.hasNext()) {
ManaCost manaCost = manaCostIterator.next();
if (manaCost instanceof PhyrexianManaCost) {
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) manaCost;
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(abilityToPay, source, payingPlayer.getId(), game)
&& payingPlayer.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + '?', source, game)) {
manaCostIterator.remove();
tempCosts.add(payLifeCost);
}
if (!manaCost.isPhyrexian()) {
continue;
}
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(abilityToPay, source, payingPlayer.getId(), game)
&& payingPlayer.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + manaCost.getText().replace("/P", "") + '?', source, game)) {
manaCostIterator.remove();
tempCosts.add(payLifeCost);
this.incrPhyrexianPaid();
}
}
tempCosts.pay(source, game, source, payingPlayer.getId(), false, null);
}
private void handleLikePhyrexianManaCosts(Player player, Ability source, Game game) {
if (this.isEmpty()) {
return; // nothing to be done without any mana costs. prevents NRE from occurring here
}
FilterMana phyrexianColors = player.getPhyrexianColors();
if (player.getPhyrexianColors() != null) {
Costs<PayLifeCost> tempCosts = new CostsImpl<>();
Iterator<T> manaCostIterator = this.iterator();
while (manaCostIterator.hasNext()) {
ManaCost manaCost = manaCostIterator.next();
Mana mana = manaCost.getMana();
PhyrexianManaCost tempPhyrexianCost = null;
/* find which color mana is in the cost and set it in the temp Phyrexian cost */
if (phyrexianColors.isWhite() && mana.getWhite() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.W);
} else if (phyrexianColors.isBlue() && mana.getBlue() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.U);
} else if (phyrexianColors.isBlack() && mana.getBlack() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.B);
} else if (phyrexianColors.isRed() && mana.getRed() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.R);
} else if (phyrexianColors.isGreen() && mana.getGreen() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.G);
}
if (tempPhyrexianCost != null) {
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(source, source, player.getId(), game)
&& player.chooseUse(Outcome.LoseLife, "Pay 2 life (using an active ability) instead of " + tempPhyrexianCost.getBaseText() + '?', source, game)) {
manaCostIterator.remove();
tempCosts.add(payLifeCost);
}
}
}
tempCosts.pay(source, game, source, player.getId(), false, null);
if (player.getPhyrexianColors() == null) {
return;
}
Costs<PayLifeCost> tempCosts = new CostsImpl<>();
Iterator<T> manaCostIterator = this.iterator();
while (manaCostIterator.hasNext()) {
ManaCost manaCost = manaCostIterator.next();
Mana mana = manaCost.getMana();
/* find which color mana is in the cost and set it in the temp Phyrexian cost */
if ((!phyrexianColors.isWhite() || mana.getWhite() <= 0)
&& (!phyrexianColors.isBlue() || mana.getBlue() <= 0)
&& (!phyrexianColors.isBlack() || mana.getBlack() <= 0)
&& (!phyrexianColors.isRed() || mana.getRed() <= 0)
&& (!phyrexianColors.isGreen() || mana.getGreen() <= 0)) {
continue;
}
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(source, source, player.getId(), game)
&& player.chooseUse(Outcome.LoseLife, "Pay 2 life (using an active ability) instead of " + manaCost.getMana() + '?', source, game)) {
manaCostIterator.remove();
tempCosts.add(payLifeCost);
}
}
tempCosts.pay(source, game, source, player.getId(), false, null);
}
@Override
@ -447,49 +445,57 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
for (ManaCost cost : savedCosts) {
this.add(cost.copy());
}
} else {
String[] symbols = mana.split("^\\{|}\\{|}$");
int modifierForX = 0;
for (String symbol : symbols) {
if (!symbol.isEmpty()) {
if (symbol.length() == 1 || isNumeric(symbol)) {
if (Character.isDigit(symbol.charAt(0))) {
this.add(new GenericManaCost(Integer.valueOf(symbol)));
} else if (symbol.equals("S")) {
this.add(new SnowManaCost());
} else if (symbol.equals("C")) {
this.add(new ColorlessManaCost(1));
} else if (!symbol.equals("X")) {
this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0))));
} else // check X wasn't added before
if (modifierForX == 0) {
// count X occurence
for (String s : symbols) {
if (s.equals("X")) {
modifierForX++;
}
}
this.add(new VariableManaCost(VariableCostType.NORMAL, modifierForX));
} //TODO: handle multiple {X} and/or {Y} symbols
} else if (Character.isDigit(symbol.charAt(0))) {
MonoHybridManaCost cost;
if (extractMonoHybridGenericValue) {
// for tests only, no usage in real game
cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)), Integer.parseInt(symbol.substring(0, 1)));
} else {
cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)));
return;
}
String[] symbols = mana.split("^\\{|}\\{|}$");
int modifierForX = 0;
for (String symbol : symbols) {
if (symbol.isEmpty()) {
continue;
}
if (symbol.length() == 1 || isNumeric(symbol)) {
if (Character.isDigit(symbol.charAt(0))) {
this.add(new GenericManaCost(Integer.valueOf(symbol)));
} else if (symbol.equals("S")) {
this.add(new SnowManaCost());
} else if (symbol.equals("C")) {
this.add(new ColorlessManaCost(1));
} else if (!symbol.equals("X")) {
this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0))));
} else // check X wasn't added before
if (modifierForX == 0) {
// count X occurence
for (String s : symbols) {
if (s.equals("X")) {
modifierForX++;
}
}
this.add(cost);
} else if (symbol.contains("P")) {
this.add(new PhyrexianManaCost(ColoredManaSymbol.lookup(symbol.charAt(0))));
} else {
this.add(new HybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)), ColoredManaSymbol.lookup(symbol.charAt(2))));
}
this.add(new VariableManaCost(VariableCostType.NORMAL, modifierForX));
} //TODO: handle multiple {X} and/or {Y} symbols
} else if (Character.isDigit(symbol.charAt(0))) {
MonoHybridManaCost cost;
if (extractMonoHybridGenericValue) {
// for tests only, no usage in real game
cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)), Integer.parseInt(symbol.substring(0, 1)));
} else {
cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)));
}
this.add(cost);
} else {
boolean phyrexian = symbol.contains("/P");
String without = symbol.replace("/P", "");
ManaCost cost;
if (without.length() == 1) {
cost = new ColoredManaCost(ColoredManaSymbol.lookup(without.charAt(0)));
} else {
cost = new HybridManaCost(ColoredManaSymbol.lookup(without.charAt(0)), ColoredManaSymbol.lookup(without.charAt(2)));
}
cost.setPhyrexian(phyrexian);
this.add(cost);
}
if (!extractMonoHybridGenericValue) {
costsCache.put(mana, this.copy());
}
}
if (!extractMonoHybridGenericValue) {
costsCache.put(mana, this.copy());
}
}
@ -597,6 +603,29 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
return new ManaCostsImpl<>(this);
}
@Override
public boolean isPhyrexian() {
return phyrexian;
}
@Override
public void setPhyrexian(boolean phyrexian) {
this.phyrexian = phyrexian;
for (T cost : this) {
cost.setPhyrexian(phyrexian);
}
}
@Override
public void incrPhyrexianPaid() {
this.phyrexianPaid++;
}
@Override
public int getPhyrexianPaid() {
return phyrexianPaid;
}
@Override
public Filter getSourceFilter() {
for (T cost : this) {

View file

@ -1,40 +0,0 @@
package mage.abilities.costs.mana;
import mage.Mana;
import mage.constants.ColoredManaSymbol;
/**
*
* @author nantuko
*/
public class PhyrexianManaCost extends ColoredManaCost {
public PhyrexianManaCost(ColoredManaSymbol mana) {
super(mana);
options.add(Mana.GenericMana(0));
}
public PhyrexianManaCost(PhyrexianManaCost manaCost) {
super(manaCost);
}
@Override
public String getText() {
return '{' + mana.toString() + "/P}";
}
public String getBaseText() {
return super.getText();
}
@Override
public ColoredManaCost getUnpaid() {
return new ColoredManaCost(this);
}
@Override
public PhyrexianManaCost copy() {
return new PhyrexianManaCost(this);
}
}

View file

@ -132,6 +132,7 @@ public class CopyEffect extends ContinuousEffectImpl {
// and abilities that were chosen for this creature as it entered the battlefield. (2018-03-16)
permanent.getPower().setValue(copyFromObject.getPower().getBaseValueModified());
permanent.getToughness().setValue(copyFromObject.getToughness().getBaseValueModified());
permanent.setStartingLoyalty(copyFromObject.getStartingLoyalty());
if (copyFromObject instanceof Permanent) {
Permanent targetPermanent = (Permanent) copyFromObject;
permanent.setTransformed(targetPermanent.isTransformed());

View file

@ -0,0 +1,42 @@
package mage.abilities.keyword;
import mage.abilities.MageSingleton;
import mage.abilities.StaticAbility;
import mage.constants.Zone;
import java.io.ObjectStreamException;
/**
* @author TheElk801
*/
public class CompleatedAbility extends StaticAbility implements MageSingleton {
private static final CompleatedAbility instance;
static {
instance = new CompleatedAbility();
}
private Object readResolve() throws ObjectStreamException {
return instance;
}
public static CompleatedAbility getInstance() {
return instance;
}
private CompleatedAbility() {
super(Zone.ALL, null);
}
@Override
public String getRule() {
return "compleated";
}
@Override
public CompleatedAbility copy() {
return instance;
}
}

View file

@ -66,6 +66,7 @@ public class TransformAbility extends SimpleStaticAbility {
}
permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
permanent.setStartingLoyalty(sourceCard.getStartingLoyalty());
}
public static Card transformCardSpellStatic(Card mainSide, Card otherSide, Game game) {

View file

@ -4,9 +4,7 @@ import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.cards.*;
import mage.cards.mock.MockCard;
import mage.cards.mock.MockSplitCard;
@ -246,13 +244,15 @@ public class CardInfo {
// Starting loyalty
if (card.isPlaneswalker()) {
for (Ability ab : card.getAbilities()) {
if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) {
this.startingLoyalty = "" + ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).getStartingLoyalty();
}
}
if (this.startingLoyalty == null) {
this.startingLoyalty = "";
switch (card.getStartingLoyalty()) {
case -2:
this.startingLoyalty = "X";
break;
case -1:
this.startingLoyalty = "";
break;
default:
this.startingLoyalty = "" + card.getStartingLoyalty();
}
} else {
this.startingLoyalty = "";
@ -491,8 +491,8 @@ public class CardInfo {
if (o == null || !(o instanceof CardInfo)) return false;
CardInfo other = (CardInfo) o;
return (this.name.equals(other.name)
&& this.setCode.equals(other.setCode)
&& this.cardNumber.equals(other.cardNumber));
&& this.setCode.equals(other.setCode)
&& this.cardNumber.equals(other.cardNumber));
}
@Override

View file

@ -61,6 +61,7 @@ public class PermanentCard extends PermanentImpl {
private void init(Card card, Game game) {
power = card.getPower().copy();
toughness = card.getToughness().copy();
startingLoyalty = card.getStartingLoyalty();
copyFromCard(card, game);
// if temporary added abilities to the spell/card exist, you need to add it to the permanent derived from that card
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(card.getId());

View file

@ -1123,19 +1123,35 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
MorphAbility.setPermanentToFaceDownCreature(this, game);
}
EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone, EnterEventType.SELF);
if (game.replaceEvent(new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone, EnterEventType.SELF))) {
return false;
}
EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone);
if (game.replaceEvent(event)) {
return false;
}
event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone);
if (!game.replaceEvent(event)) {
if (fireEvent) {
game.addSimultaneousEvent(event);
return true;
if (this.isPlaneswalker(game)) {
int loyalty;
if (this.getStartingLoyalty() == -2) {
loyalty = source.getManaCostsToPay().getX();
} else {
loyalty = this.getStartingLoyalty();
}
int countersToAdd;
if (this.hasAbility(CompleatedAbility.getInstance(), game)) {
countersToAdd = loyalty - 2 * source.getManaCostsToPay().getPhyrexianPaid();
} else {
countersToAdd = loyalty;
}
if (countersToAdd > 0) {
this.addCounters(CounterType.LOYALTY.createInstance(countersToAdd), source, game);
}
}
return false;
if (!fireEvent) {
return false;
}
game.addSimultaneousEvent(event);
return true;
}
@Override

View file

@ -86,6 +86,7 @@ public class PermanentToken extends PermanentImpl {
this.supertype.addAll(token.getSuperType());
this.subtype.copyFrom(token.getSubtype(game));
this.tokenDescriptor = token.getTokenDescriptor();
this.startingLoyalty = token.getStartingLoyalty();
}
@Override

View file

@ -0,0 +1,34 @@
package mage.game.permanent.token;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
/**
* @author TheElk801
*/
public final class TamiyosNotebookToken extends TokenImpl {
private static final FilterCard filter = new FilterCard("spells");
public TamiyosNotebookToken() {
super("Tamiyo's Notebook", "Tamiyo's Notebook, a legendary colorless artifact token with \"Spells you cast cost {2} less to cast\" and \"{T}: Draw a card.\"");
addSuperType(SuperType.LEGENDARY);
this.cardType.add(CardType.ARTIFACT);
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2)));
this.addAbility(new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new TapSourceCost()));
}
public TamiyosNotebookToken(final TamiyosNotebookToken token) {
super(token);
}
public TamiyosNotebookToken copy() {
return new TamiyosNotebookToken(this);
}
}

View file

@ -92,6 +92,7 @@ public class CopyTokenFunction implements Function<Token, Card> {
target.getPower().modifyBaseValue(sourceObj.getPower().getBaseValueModified());
target.getToughness().modifyBaseValue(sourceObj.getToughness().getBaseValueModified());
target.setStartingLoyalty(sourceObj.getStartingLoyalty());
return target;
}