[M3C] Implement Ulalek, Fused Atrocity and new colorless hybrid mana (#12273)

* improved by (#12415)
* Added symbol downloads for new colorless hybrid
---------

Co-authored-by: Alexander Novotny <alexander_novo@mail.tmcc.edu>
Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
Evan Kranzler 2024-06-06 15:43:45 -04:00 committed by GitHub
parent 1b2de47e78
commit 27bea843a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 306 additions and 28 deletions

View file

@ -1,15 +1,16 @@
package mage;
/**
* Enum representing the mana symbols.
* <p>
* 107.4. The mana symbols are {W}, {U}, {B}, {R}, {G}, and {X}; the numerals
* {0}, {1}, {2}, {3}, {4}, and so on; the hybrid symbols {W/U}, {W/B}, {U/B},
* {U/R}, {B/R}, {B/G}, {R/G}, {R/W}, {G/W}, and {G/U}; the monocolored hybrid
* symbols {2/W}, {2/U}, {2/B}, {2/R}, and {2/G}; the Phyrexian mana symbols
* {W/P}, {U/P}, {B/P}, {R/P}, and {G/P}; and the snow symbol {S}.
* 107.4. The mana symbols are {W}, {U}, {B}, {R}, {G}, and {C}; the numerical symbols
* {0}, {1}, {2}, {3}, {4}, and so on; the variable symbol {X}; the hybrid symbols
* {W/U}, {W/B}, {U/B}, {U/R}, {B/R}, {B/G}, {R/G}, {R/W}, {G/W}, and {G/U};
* the monocolored hybrid symbols {2/W}, {2/U}, {2/B}, {2/R}, and {2/G};
* the Phyrexian mana symbols {W/P}, {U/P}, {B/P}, {R/P}, and {G/P};
* the hybrid Phyrexian symbols {W/U/P}, {W/B/P}, {U/B/P}, {U/R/P},
* {B/R/P}, {B/G/P}, {R/G/P}, {R/W/P}, {G/W/P}, and {G/U/P}; and the snow mana symbol {S}.
* <p>
* 107.4a. There are five primary colored mana symbols: {W} is white, {U} blue,
* {B} black, {R} red, and {G} green. These symbols are used to represent colored
@ -61,6 +62,7 @@ public enum ManaSymbol {
B("{B}", Type.PRIMARY, Type.COLORED, Type.MONOCOLORED),
R("{R}", Type.PRIMARY, Type.COLORED, Type.MONOCOLORED),
G("{G}", Type.PRIMARY, Type.COLORED, Type.MONOCOLORED),
C("{C}", Type.COLORLESS),
X("{X}", Type.GENERIC, Type.COLORLESS),
NUMERIC("{N/A}", Type.GENERIC, Type.COLORLESS),
HYBRID_WU("{W/U}", W, U, Type.HYBRID, Type.COLORED),
@ -73,11 +75,21 @@ public enum ManaSymbol {
HYBRID_RW("{R/W}", R, W, Type.HYBRID, Type.COLORED),
HYBRID_GW("{G/W}", G, W, Type.HYBRID, Type.COLORED),
HYBRID_GU("{G/U}", G, U, Type.HYBRID, Type.COLORED),
HYBRID_CW("{W/U}", W, U, Type.HYBRID, Type.COLORED),
HYBRID_CU("{W/U}", W, U, Type.HYBRID, Type.COLORED),
HYBRID_CB("{W/B}", W, B, Type.HYBRID, Type.COLORED),
HYBRID_CR("{U/R}", U, R, Type.HYBRID, Type.COLORED),
HYBRID_CG("{B/G}", B, G, Type.HYBRID, Type.COLORED),
MONOCOLORED_HYBRID_W("{2/W}", W, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
MONOCOLORED_HYBRID_U("{2/U}", U, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
MONOCOLORED_HYBRID_B("{2/B}", B, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
MONOCOLORED_HYBRID_R("{2/R}", R, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
MONOCOLORED_HYBRID_G("{2/G}", G, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
COLORLESS_HYBRID_W("{C/W}", C, W, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
COLORLESS_HYBRID_U("{C/U}", C, U, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
COLORLESS_HYBRID_B("{C/B}", C, B, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
COLORLESS_HYBRID_R("{C/R}", C, R, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
COLORLESS_HYBRID_G("{C/G}", C, G, Type.HYBRID, Type.MONOCOLORED, Type.COLORED),
PHYREXIAN_W("{W/P}", W, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED),
PHYREXIAN_G("{G/P}", G, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED),
PHYREXIAN_R("{R/P}", R, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED),

View file

@ -0,0 +1,106 @@
package mage.abilities.costs.mana;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.constants.ColoredManaSymbol;
import mage.game.Game;
import mage.players.ManaPool;
import java.util.ArrayList;
import java.util.List;
public class ColorlessHybridManaCost extends ManaCostImpl {
private final ColoredManaSymbol manaColor;
public ColorlessHybridManaCost(ColoredManaSymbol manaColor) {
this.manaColor = manaColor;
this.cost = new Mana(manaColor);
this.cost.add(Mana.ColorlessMana(1));
addColoredOption(manaColor);
options.add(Mana.ColorlessMana(1));
}
public ColorlessHybridManaCost(ColorlessHybridManaCost manaCost) {
super(manaCost);
this.manaColor = manaCost.manaColor;
}
@Override
public int manaValue() {
return 1;
}
@Override
public boolean isPaid() {
return paid || isColoredPaid(this.manaColor) || isColorlessPaid(1);
}
@Override
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
// Assign colorless first in an attempt to avoid things like pain lands and talismans causing damage
if (!assignColorless(ability, game, pool, 1, costToPay)) {
assignColored(ability, game, pool, manaColor, costToPay);
}
}
@Override
public String getText() {
return "{C/" + manaColor.toString() + '}';
}
@Override
public ColorlessHybridManaCost getUnpaid() {
return this;
}
@Override
public boolean testPay(Mana testMana) {
switch (manaColor) {
case B:
if (testMana.getBlack() > 0) {
return true;
}
case U:
if (testMana.getBlue() > 0) {
return true;
}
case R:
if (testMana.getRed() > 0) {
return true;
}
case W:
if (testMana.getWhite() > 0) {
return true;
}
case G:
if (testMana.getGreen() > 0) {
return true;
}
}
return testMana.getColorless() > 0;
}
@Override
public ColorlessHybridManaCost copy() {
return new ColorlessHybridManaCost(this);
}
@Override
public boolean containsColor(ColoredManaSymbol coloredManaSymbol) {
return manaColor == coloredManaSymbol;
}
public ColoredManaSymbol getManaColor() {
return manaColor;
}
@Override
public List<Mana> getManaOptions() {
List<Mana> manaList = new ArrayList<>();
manaList.add(new Mana(manaColor));
manaList.add(Mana.ColorlessMana(1));
return manaList;
}
}

View file

@ -18,7 +18,7 @@ public class HybridManaCost extends ManaCostImpl {
public HybridManaCost(ColoredManaSymbol mana1, ColoredManaSymbol mana2) {
this.mana1 = mana1;
this.mana2 = mana2;
this.cost = new Mana(mana1);
this.cost = mana1 == null ? Mana.ColorlessMana(1) : new Mana(mana1);
this.cost.add(new Mana(mana2));
addColoredOption(mana1);
addColoredOption(mana2);
@ -42,7 +42,7 @@ public class HybridManaCost extends ManaCostImpl {
@Override
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
if (assignColored(ability, game, pool, this.mana1, costToPay)) {
if (this.mana1 != null && assignColored(ability, game, pool, this.mana1, costToPay)) {
return;
}
assignColored(ability, game, pool, this.mana2, costToPay);
@ -81,6 +81,10 @@ public class HybridManaCost extends ManaCostImpl {
if (testMana.getGreen() > 0 || testMana.getAny() > 0) {
return true;
}
default:
if (testMana.getColorless() > 0) {
return true;
}
}
switch (mana2) {
case B:

View file

@ -151,14 +151,15 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
return false;
}
protected void assignColorless(Ability ability, Game game, ManaPool pool, int mana, Cost costToPay) {
protected boolean assignColorless(Ability ability, Game game, ManaPool pool, int mana, Cost costToPay) {
int conditionalCount = pool.getConditionalCount(ability, game, null, costToPay);
while (mana > payment.count() && (pool.count() > 0 || conditionalCount > 0)) {
if (pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseColorless();
}
break;
if (mana > payment.count() && (pool.count() > 0 || conditionalCount > 0)
&& pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseColorless();
return true;
}
return false;
}
protected boolean assignGeneric(Ability ability, Game game, ManaPool pool, int mana, FilterMana filterMana, Cost costToPay) {
@ -284,19 +285,21 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
switch (symbol) {
case B:
this.options.add(Mana.BlackMana(1));
break;
return;
case U:
this.options.add(Mana.BlueMana(1));
break;
return;
case W:
this.options.add(Mana.WhiteMana(1));
break;
return;
case R:
this.options.add(Mana.RedMana(1));
break;
return;
case G:
this.options.add(Mana.GreenMana(1));
break;
return;
default:
this.options.add(Mana.ColorlessMana(1));
}
}

View file

@ -348,6 +348,16 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
// Colorless hybrid
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof ColorlessHybridManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
if (pool.isEmpty()) {
return;
}
}
}
// monohybrid
// try to pay colored part
for (ManaCost cost : this) {
@ -492,6 +502,8 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
ManaCost cost;
if (without.length() == 1) {
cost = new ColoredManaCost(ColoredManaSymbol.lookup(without.charAt(0)));
} else if (without.charAt(0) == 'C') {
cost = new ColorlessHybridManaCost(ColoredManaSymbol.lookup(without.charAt(2)));
} else {
cost = new HybridManaCost(ColoredManaSymbol.lookup(without.charAt(0)), ColoredManaSymbol.lookup(without.charAt(2)));
}

View file

@ -261,6 +261,9 @@ public final class ManaUtil {
} else if (symbols.contains(ManaSymbol.HYBRID_WB)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_WB);
} else if (symbols.contains(ManaSymbol.HYBRID_CB)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_CB);
}
}
@ -282,6 +285,9 @@ public final class ManaUtil {
} else if (symbols.contains(ManaSymbol.HYBRID_UR)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_UR);
} else if (symbols.contains(ManaSymbol.HYBRID_CR)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_CR);
}
}
return chosenManaAbilityForHybrid;
@ -302,6 +308,9 @@ public final class ManaUtil {
} else if (symbols.contains(ManaSymbol.HYBRID_GU)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_GU);
} else if (symbols.contains(ManaSymbol.HYBRID_CU)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_CU);
}
}
return chosenManaAbilityForHybrid;
@ -322,6 +331,9 @@ public final class ManaUtil {
} else if (symbols.contains(ManaSymbol.HYBRID_RW)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_RW);
} else if (symbols.contains(ManaSymbol.HYBRID_CW)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_CW);
}
}
return chosenManaAbilityForHybrid;
@ -342,6 +354,9 @@ public final class ManaUtil {
} else if (symbols.contains(ManaSymbol.HYBRID_RG)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_RG);
} else if (symbols.contains(ManaSymbol.HYBRID_CG)) {
chosenManaAbilityForHybrid = ability;
countColored.add(ManaSymbol.HYBRID_CG);
}
}
return chosenManaAbilityForHybrid;