Merge pull request #3723 from tool4ever/universes

CardDb: support rebranded versions
This commit is contained in:
Paul Hammerton
2023-09-02 13:20:26 +01:00
committed by GitHub
9 changed files with 69 additions and 39 deletions

View File

@@ -293,6 +293,9 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
CardEdition upcomingSet = null; CardEdition upcomingSet = null;
Date today = new Date(); Date today = new Date();
// do this first so they're not considered missing
buildRenamedCards();
for (CardEdition e : editions.getOrderedEditions()) { for (CardEdition e : editions.getOrderedEditions()) {
boolean coreOrExpSet = e.getType() == CardEdition.Type.CORE || e.getType() == CardEdition.Type.EXPANSION; boolean coreOrExpSet = e.getType() == CardEdition.Type.CORE || e.getType() == CardEdition.Type.EXPANSION;
boolean isCoreExpSet = coreOrExpSet || e.getType() == CardEdition.Type.REPRINT; boolean isCoreExpSet = coreOrExpSet || e.getType() == CardEdition.Type.REPRINT;
@@ -353,6 +356,38 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
reIndex(); reIndex();
} }
private void buildRenamedCards() {
// for now just check Universes Within
for (CardInSet cis : editions.get("SLX").getCards()) {
String orgName = alternateName.get(cis.name);
if (orgName != null) {
// found original (beyond) print
CardRules org = getRules(orgName);
CardFace renamedMain = (CardFace) ((CardFace) org.getMainPart()).clone();
renamedMain.setName(renamedMain.getAltName());
renamedMain.setAltName(null);
// TODO this could mess up some "named ..." cardname literals but there's no printing like that currently
renamedMain.setOracleText(renamedMain.getOracleText().replace(orgName, renamedMain.getName()));
facesByName.put(renamedMain.getName(), renamedMain);
CardFace renamedOther = null;
if (org.getOtherPart() != null) {
renamedOther = (CardFace) ((CardFace) org.getOtherPart()).clone();
orgName = renamedOther.getName();
renamedOther.setName(renamedOther.getAltName());
renamedOther.setAltName(null);
renamedOther.setOracleText(renamedOther.getOracleText().replace(orgName, renamedOther.getName()));
facesByName.put(renamedOther.getName(), renamedOther);
}
CardRules within = new CardRules(new ICardFace[] { renamedMain, renamedOther, null, null, null, null, null }, org.getSplitType(), org.getAiHints());
// so workshop can edit same script
within.setNormalizedName(org.getNormalizedName());
rulesByName.put(cis.name, within);
}
}
}
public void addCard(PaperCard paperCard) { public void addCard(PaperCard paperCard) {
if (excludeCard(paperCard.getName(), paperCard.getEdition())) if (excludeCard(paperCard.getName(), paperCard.getEdition()))
return; return;
@@ -930,7 +965,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
public String getName(final String cardName) { public String getName(final String cardName) {
if (alternateName.containsKey(cardName)) { return getName(cardName, false);
}
public String getName(final String cardName, boolean engine) {
if (alternateName.containsKey(cardName) && engine) {
// TODO might want to implement GUI option so it always fetches the Within version
return alternateName.get(cardName); return alternateName.get(cardName);
} }
return cardName; return cardName;

View File

@@ -20,7 +20,7 @@ import forge.card.mana.ManaCost;
* <br><br> * <br><br>
* <i>Do not use reference to class except for card parsing.<br>Always use reference to interface type outside of package.</i> * <i>Do not use reference to class except for card parsing.<br>Always use reference to interface type outside of package.</i>
*/ */
final class CardFace implements ICardFace { final class CardFace implements ICardFace, Cloneable {
public enum FaceSelectionMethod { // public enum FaceSelectionMethod { //
USE_ACTIVE_FACE, USE_ACTIVE_FACE,
@@ -32,7 +32,7 @@ final class CardFace implements ICardFace {
private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<>()); private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<>());
private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<>()); private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<>());
private final String name; private String name;
private String altName = null; private String altName = null;
private CardType type = null; private CardType type = null;
private ManaCost manaCost = ManaCost.NO_COST; private ManaCost manaCost = ManaCost.NO_COST;
@@ -86,6 +86,7 @@ final class CardFace implements ICardFace {
throw new RuntimeException("Card name is empty"); throw new RuntimeException("Card name is empty");
} }
// Here come setters to allow parser supply values // Here come setters to allow parser supply values
void setName(String name) { this.name = name; }
void setAltName(String name) { this.altName = name; } void setAltName(String name) { this.altName = name; }
void setType(CardType type0) { this.type = type0; } void setType(CardType type0) { this.type = type0; }
void setManaCost(ManaCost manaCost0) { this.manaCost = manaCost0; } void setManaCost(ManaCost manaCost0) { this.manaCost = manaCost0; }
@@ -153,4 +154,14 @@ final class CardFace implements ICardFace {
public int compareTo(ICardFace o) { public int compareTo(ICardFace o) {
return getName().compareTo(o.getName()); return getName().compareTo(o.getName());
} }
/** {@inheritDoc} */
@Override
public final Object clone() {
try {
return super.clone();
} catch (final Exception ex) {
throw new RuntimeException("CardFace : clone() error, " + ex);
}
}
} }

View File

@@ -55,7 +55,7 @@ public final class CardRules implements ICardCharacteristics {
private String partnerWith; private String partnerWith;
private boolean custom; private boolean custom;
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) { public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
splitType = altMode; splitType = altMode;
mainPart = faces[0]; mainPart = faces[0];
otherPart = faces[1]; otherPart = faces[1];

View File

@@ -130,6 +130,7 @@ public abstract class DeckGeneratorBase {
int res = 0; int res = 0;
while (res < cnt) { while (res < cnt) {
PaperCard cp = source.get(MyRandom.getRandom().nextInt(srcLen)); PaperCard cp = source.get(MyRandom.getRandom().nextInt(srcLen));
// TODO AltName conversion needed?
int newCount = cardCounts.get(cp.getName()) + 1; int newCount = cardCounts.get(cp.getName()) + 1;
//add card to deck if not already maxed out on card //add card to deck if not already maxed out on card

View File

@@ -859,21 +859,23 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
@Override @Override
public final String getName() { public final String getName() {
return getName(currentState); return getName(currentState, false);
} }
public final String getName(boolean alt) {
return getName(currentState, alt);
}
public final String getName(CardStateName stateName) { public final String getName(CardStateName stateName) {
return getName(getState(stateName)); return getName(getState(stateName), false);
} }
public final String getName(CardState state, boolean alt) {
public final String getName(CardState state) {
String name = state.getName(); String name = state.getName();
for (CardChangedName change : this.changedCardNames.values()) { for (CardChangedName change : this.changedCardNames.values()) {
if (change.isOverwrite()) { if (change.isOverwrite()) {
name = change.getNewName(); name = change.getNewName();
} }
} }
return name; return alt ? StaticData.instance().getCommonCards().getName(name, true) : name;
} }
public final boolean hasNonLegendaryCreatureNames() { public final boolean hasNonLegendaryCreatureNames() {
@@ -5526,7 +5528,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return true; return true;
} }
} }
return sharesNameWith(c1.getName()); return sharesNameWith(c1.getName(true));
} }
public final boolean sharesNameWith(final String name) { public final boolean sharesNameWith(final String name) {
@@ -5535,7 +5537,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return false; return false;
} }
boolean shares = getName().equals(name); boolean shares = getName(true).equals(name);
// Split cards has extra logic to check if it does share a name with // Split cards has extra logic to check if it does share a name with
if (isSplitCard()) { if (isSplitCard()) {

View File

@@ -1184,7 +1184,7 @@ public class CardView extends GameEntityView {
} }
void updateName(CardState c) { void updateName(CardState c) {
Card card = c.getCard(); Card card = c.getCard();
setName(card.getName(c)); setName(card.getName(c, false));
if (CardView.this.getCurrentState() == this) { if (CardView.this.getCurrentState() == this) {
if (card != null) { if (card != null) {

View File

@@ -215,7 +215,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
Iterable<Entry<String,Integer>> cardsByName = null; Iterable<Entry<String,Integer>> cardsByName = null;
if (deck != null) { if (deck != null) {
final CardPool allCards = deck.getAllCardsInASinglePool(deck.has(DeckSection.Commander)); final CardPool allCards = deck.getAllCardsInASinglePool(deck.has(DeckSection.Commander));
cardsByName = Aggregates.groupSumBy(allCards, PaperCard.FN_GET_NAME); cardsByName = Aggregates.groupSumBy(allCards, pc -> pc.getRules().getNormalizedName());
} }
for (final Entry<TItem, Integer> itemEntry : itemsToAdd) { for (final Entry<TItem, Integer> itemEntry : itemsToAdd) {
@@ -237,7 +237,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
Entry<String, Integer> cardAmountInfo = Iterables.find(cardsByName, new Predicate<Entry<String, Integer>>() { Entry<String, Integer> cardAmountInfo = Iterables.find(cardsByName, new Predicate<Entry<String, Integer>>() {
@Override @Override
public boolean apply(Entry<String, Integer> t) { public boolean apply(Entry<String, Integer> t) {
return t.getKey().equals(card.getName()); return t.getKey().equals(card.getRules().getNormalizedName());
} }
}, null); }, null);
if (cardAmountInfo != null) { if (cardAmountInfo != null) {

View File

@@ -1,25 +0,0 @@
Name:Havengul Laboratory
ManaCost:no cost
Types:Legendary Land
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ Investigate | Cost$ 4 T | SpellDescription$ Investigate. (Create a Clue token. It's an artifact with "{2}, Sacrifice this artifact: Draw a card.")
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ ClueResearch | SVarCompare$ GE3 | Execute$ TrigTransform | TriggerDescription$ At the beginning of your end step, if you sacrificed three or more clues this turn, transform CARDNAME.
SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform
SVar:ClueResearch:PlayerCountPropertyYou$SacrificedThisTurn Clue
DeckHas:Ability$Investigate|Token|Sacrifice|Graveyard
DeckHints:Ability$Investigate
AlternateMode:DoubleFaced
Oracle:{T}: Add {C}.\n{4}, {T}: Investigate. (Create a Clue token. It's an artifact with "{2}, Sacrifice this artifact: Draw a card.")\nAt the beginning of your end step, if you sacrificed three or more clues this turn, transform Havengul Laboratory.
ALTERNATE
Name:Havengul Mystery
ManaCost:no cost
Types:Legendary Land
T:Mode$ Transformed | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When this land transforms into CARDNAME, return target creature card from your graveyard to the battlefield.
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select target creature card from your graveyard | Origin$ Graveyard | Destination$ Battlefield | RememberTargets$ True
T:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Battlefield | Destination$ Any | Execute$ TrigTransform2 | TriggerZones$ Battlefield | TriggerDescription$ When the creature card put onto the battlefield with CARDNAME leaves the battlefield, transform CARDNAME.
SVar:TrigTransform2:DB$ SetState | Defined$ Self | Mode$ Transform | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ Mana | Cost$ T PayLife<1> | Produced$ B | SpellDescription$ Add {B}.
Oracle:When this land transforms into Havengul Mystery, return target creature card from your graveyard to the battlefield.\nWhen the creature card put onto the battlefield with Havengul Mystery leaves the battlefield, transform Havengul Mystery.\n{T}, Pay 1 life: Add {B}.

View File

@@ -1,4 +1,5 @@
Name:Hawkins National Laboratory Name:Hawkins National Laboratory
AltName:Havengul Laboratory
ManaCost:no cost ManaCost:no cost
Types:Legendary Land Types:Legendary Land
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
@@ -14,6 +15,7 @@ Oracle:{T}: Add {C}.\n{4}, {T}: Investigate.\nAt the beginning of your end step,
ALTERNATE ALTERNATE
Name:The Upside Down Name:The Upside Down
AltName:Havengul Mystery
ManaCost:no cost ManaCost:no cost
Types:Legendary Land Types:Legendary Land
T:Mode$ Transformed | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When this land transforms into CARDNAME, return target creature card from your graveyard to the battlefield. T:Mode$ Transformed | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When this land transforms into CARDNAME, return target creature card from your graveyard to the battlefield.