From 4afc86b4b4fc9fab1233ea8b6f2d32dbfca88ded Mon Sep 17 00:00:00 2001 From: Jetz Date: Mon, 26 Aug 2024 00:25:43 -0400 Subject: [PATCH] Support functional variants in translations --- .../src/main/java/forge/card/CardFace.java | 3 + .../java/forge/card/CardRulesPredicates.java | 21 ++++++ .../src/main/java/forge/card/ICardFace.java | 3 + .../src/main/java/forge/item/IPaperCard.java | 19 ++++- .../main/java/forge/item/InventoryItem.java | 9 ++- .../main/java/forge/util/CardTranslation.java | 73 ++++++++++++++----- .../main/java/forge/util/ITranslatable.java | 22 ++++++ .../game/ability/SpellAbilityEffect.java | 5 +- .../src/main/java/forge/game/card/Card.java | 31 ++++++-- .../java/forge/game/card/CardFactory.java | 9 ++- .../main/java/forge/game/card/CardState.java | 33 ++++++++- .../main/java/forge/game/card/CardView.java | 33 +++++++-- .../game/replacement/ReplacementEffect.java | 14 ++-- .../forge/game/spellability/SpellAbility.java | 19 ++--- .../game/staticability/StaticAbility.java | 19 ++--- .../main/java/forge/game/trigger/Trigger.java | 22 +++--- .../forge/trackable/TrackableProperty.java | 1 + .../toolbox/imaging/FCardImageRenderer.java | 14 ++-- .../src/forge/card/CardImageRenderer.java | 14 ++-- .../java/forge/gui/card/CardDetailUtil.java | 6 +- .../forge/itemmanager/AdvancedSearch.java | 3 +- .../java/forge/itemmanager/ColumnDef.java | 8 +- 22 files changed, 282 insertions(+), 99 deletions(-) create mode 100644 forge-core/src/main/java/forge/util/ITranslatable.java diff --git a/forge-core/src/main/java/forge/card/CardFace.java b/forge-core/src/main/java/forge/card/CardFace.java index 41e487afdff..2fbf01e2664 100644 --- a/forge-core/src/main/java/forge/card/CardFace.java +++ b/forge-core/src/main/java/forge/card/CardFace.java @@ -156,6 +156,9 @@ final class CardFace implements ICardFace, Cloneable { return null; return this.functionalVariants.get(variant); } + @Override public Map getFunctionalVariants() { + return this.functionalVariants; + } CardFace getOrCreateFunctionalVariant(String variant) { if (this.functionalVariants == null) { this.functionalVariants = new HashMap<>(); diff --git a/forge-core/src/main/java/forge/card/CardRulesPredicates.java b/forge-core/src/main/java/forge/card/CardRulesPredicates.java index af5d1297cc5..f6d358cd9cc 100644 --- a/forge-core/src/main/java/forge/card/CardRulesPredicates.java +++ b/forge-core/src/main/java/forge/card/CardRulesPredicates.java @@ -334,6 +334,17 @@ public final class CardRulesPredicates { if (face == null) { return false; } + if (face.hasFunctionalVariants()) { + for (Map.Entry v : face.getFunctionalVariants().entrySet()) { + //Not a very pretty implementation, but an ICardFace doesn't have a specific variant, so they all need to be checked. + String origOracle = v.getValue().getOracleText(); + if(op(origOracle, operand)) + return true; + String name = v.getValue().getName() + " $" + v.getKey(); + if(op(CardTranslation.getTranslatedOracle(name), operand)) + return true; + } + } if (op(face.getOracleText(), operand) || op(CardTranslation.getTranslatedOracle(face.getName()), operand)) { return true; } @@ -343,6 +354,16 @@ public final class CardRulesPredicates { if (face == null) { return false; } + if (face.hasFunctionalVariants()) { + for (Map.Entry v : face.getFunctionalVariants().entrySet()) { + String origType = v.getValue().getType().toString(); + if(op(origType, operand)) + return true; + String name = v.getValue().getName() + " $" + v.getKey(); + if(op(CardTranslation.getTranslatedType(name, origType), operand)) + return true; + } + } return (op(CardTranslation.getTranslatedType(face.getName(), face.getType().toString()), operand) || op(face.getType().toString(), operand)); } diff --git a/forge-core/src/main/java/forge/card/ICardFace.java b/forge-core/src/main/java/forge/card/ICardFace.java index 24fb2c86611..494e234c6df 100644 --- a/forge-core/src/main/java/forge/card/ICardFace.java +++ b/forge-core/src/main/java/forge/card/ICardFace.java @@ -1,5 +1,7 @@ package forge.card; +import java.util.Map; + /** * TODO: Write javadoc for this type. * @@ -9,4 +11,5 @@ public interface ICardFace extends ICardCharacteristics, ICardRawAbilites, Compa boolean hasFunctionalVariants(); ICardFace getFunctionalVariant(String variant); + Map getFunctionalVariants(); } diff --git a/forge-core/src/main/java/forge/item/IPaperCard.java b/forge-core/src/main/java/forge/item/IPaperCard.java index 5ed0d681219..69a387729bf 100644 --- a/forge-core/src/main/java/forge/item/IPaperCard.java +++ b/forge-core/src/main/java/forge/item/IPaperCard.java @@ -253,5 +253,22 @@ public interface IPaperCard extends InventoryItem, Serializable { String getCardRSpecImageKey(); String getCardGSpecImageKey(); - public boolean isRebalanced(); + boolean isRebalanced(); + + @Override + default String getTranslationKey() { + if(!NO_FUNCTIONAL_VARIANT.equals(getFunctionalVariant())) + return getName() + " $" + getFunctionalVariant(); + return getName(); + } + + @Override + default String getUntranslatedType() { + return getRules().getType().toString(); + } + + @Override + default String getUntranslatedOracle() { + return getRules().getOracleText(); + } } \ No newline at end of file diff --git a/forge-core/src/main/java/forge/item/InventoryItem.java b/forge-core/src/main/java/forge/item/InventoryItem.java index df8e20a1818..4de644d0489 100644 --- a/forge-core/src/main/java/forge/item/InventoryItem.java +++ b/forge-core/src/main/java/forge/item/InventoryItem.java @@ -17,13 +17,18 @@ */ package forge.item; -import forge.util.IHasName; +import forge.util.ITranslatable; /** * Interface to define a player's inventory may hold. Should include * CardPrinted, Booster, Pets, Plants... etc */ -public interface InventoryItem extends IHasName { +public interface InventoryItem extends ITranslatable { String getItemType(); String getImageKey(boolean altState); + + @Override + default String getUntranslatedType() { + return getItemType(); + } } diff --git a/forge-core/src/main/java/forge/util/CardTranslation.java b/forge-core/src/main/java/forge/util/CardTranslation.java index ed6d976115e..3912e2daf83 100644 --- a/forge-core/src/main/java/forge/util/CardTranslation.java +++ b/forge-core/src/main/java/forge/util/CardTranslation.java @@ -28,6 +28,15 @@ public class CardTranslation { for (String line : translationFile.readLines()) { String[] matches = line.split("\\|"); if (matches.length >= 2) { + if (matches[0].indexOf('$') > 0) { + //Functional variant, e.g. "Garbage Elemental $C" + String[] variantSplit = matches[0].split("\\s*\\$", 2); + if(variantSplit.length > 1) { + //Add the base name to the translated names. + translatednames.put(variantSplit[0], matches[1]); + matches[0] = variantSplit[0] + " $" + variantSplit[1]; //Standardize storage. + } + } translatednames.put(matches[0], matches[1]); } if (matches.length >= 3) { @@ -53,7 +62,7 @@ public class CardTranslation { if (name.contains(" // ")) { int splitIndex = name.indexOf(" // "); String leftname = name.substring(0, splitIndex); - String rightname = name.substring(splitIndex + 4, name.length()); + String rightname = name.substring(splitIndex + 4); return translatednames.getOrDefault(leftname, leftname) + " // " + translatednames.getOrDefault(rightname, rightname); } try { @@ -74,6 +83,10 @@ public class CardTranslation { return name; } + public static String getTranslatedName(ITranslatable card) { + return getTranslatedName(card.getUntranslatedName()); + } + private static String translateTokenName(String name) { if (translatedTokenNames == null) translatedTokenNames = new HashMap<>(); @@ -203,6 +216,12 @@ public class CardTranslation { return originaltype; } + public static String getTranslatedType(ITranslatable item) { + if (!needsTranslation()) + return item.getUntranslatedType(); + return translatedtypes.getOrDefault(item.getTranslationKey(), item.getUntranslatedType()); + } + public static String getTranslatedOracle(String name) { if (needsTranslation()) { String toracle = translatedoracles.get(name); @@ -212,13 +231,30 @@ public class CardTranslation { return ""; } - public static HashMap getTranslationTexts(String cardname, String altcardname) { - if (!needsTranslation()) return null; + public static String getTranslatedOracle(ITranslatable card) { + if(!needsTranslation()) + return ""; //card.getUntranslatedOracle(); + //Fallbacks and english versions of oracle texts are handled elsewhere. + return translatedoracles.getOrDefault(card.getTranslationKey(), ""); + } + + public static HashMap getTranslationTexts(ITranslatable card) { + return getTranslationTexts(card, null); + } + + public static HashMap getTranslationTexts(ITranslatable cardMain, ITranslatable cardOther) { + if(!needsTranslation()) return null; HashMap translations = new HashMap<>(); - translations.put("name", getTranslatedName(cardname)); - translations.put("oracle", getTranslatedOracle(cardname)); - translations.put("altname", getTranslatedName(altcardname)); - translations.put("altoracle", getTranslatedOracle(altcardname)); + translations.put("name", getTranslatedName(cardMain)); + translations.put("oracle", getTranslatedOracle(cardMain)); + if(cardOther == null) { + translations.put("altname", ""); + translations.put("altoracle", ""); + } + else { + translations.put("altname", getTranslatedName(cardOther)); + translations.put("altoracle", getTranslatedOracle(cardOther)); + } return translations; } @@ -248,14 +284,17 @@ public class CardTranslation { return result; } - public static void buildOracleMapping(String faceName, String oracleText) { - if (!needsTranslation() || oracleMappings.containsKey(faceName)) return; - String translatedText = getTranslatedOracle(faceName); + public static void buildOracleMapping(String faceName, String oracleText, String variantName) { + String translationKey = faceName; + if(variantName != null) + translationKey = faceName + " $" + variantName; + if (!needsTranslation() || oracleMappings.containsKey(translationKey)) return; + String translatedText = getTranslatedOracle(translationKey); if (translatedText.isEmpty()) { // english card only, fall back return; } - String translatedName = getTranslatedName(faceName); + String translatedName = getTranslatedName(translationKey); List > mapping = new ArrayList<>(); String [] splitOracleText = oracleText.split("\\\\n"); String [] splitTranslatedText = translatedText.split("\r\n\r\n"); @@ -269,17 +308,17 @@ public class CardTranslation { } mapping.add(Pair.of(toracle, ttranslated)); } - oracleMappings.put(faceName, mapping); + oracleMappings.put(translationKey, mapping); } - public static String translateMultipleDescriptionText(String descText, String cardName) { + public static String translateMultipleDescriptionText(String descText, ITranslatable card) { if (!needsTranslation()) return descText; String [] splitDescText = descText.split("\n"); String result = descText; for (String text : splitDescText) { text = text.trim(); if (text.isEmpty()) continue; - String translated = translateSingleDescriptionText(text, cardName); + String translated = translateSingleDescriptionText(text, card); if (!text.equals(translated)) { result = TextUtil.fastReplace(result, text, translated); } else { @@ -288,7 +327,7 @@ public class CardTranslation { if (splitKeywords.length <= 1) continue; for (String keyword : splitKeywords) { if (keyword.contains(" ")) continue; - translated = translateSingleDescriptionText(keyword, cardName); + translated = translateSingleDescriptionText(keyword, card); if (!keyword.equals(translated)) { result = TextUtil.fastReplace(result, keyword, translated); } @@ -298,13 +337,13 @@ public class CardTranslation { return result; } - public static String translateSingleDescriptionText(String descText, String cardName) { + public static String translateSingleDescriptionText(String descText, ITranslatable card) { if (descText == null) return ""; if (!needsTranslation()) return descText; if (translatedCaches.containsKey(descText)) return translatedCaches.get(descText); - List > mapping = oracleMappings.get(cardName); + List > mapping = oracleMappings.get(card.getTranslationKey()); if (mapping == null) return descText; String result = descText; if (!mapping.isEmpty()) { diff --git a/forge-core/src/main/java/forge/util/ITranslatable.java b/forge-core/src/main/java/forge/util/ITranslatable.java new file mode 100644 index 00000000000..6bbba27afb3 --- /dev/null +++ b/forge-core/src/main/java/forge/util/ITranslatable.java @@ -0,0 +1,22 @@ +package forge.util; + +public interface ITranslatable extends IHasName { + default String getTranslationKey() { + return getName(); + } + + //Fallback methods - used if no translation is found for the given key. + + default String getUntranslatedName() { + return getName(); + } + + default String getUntranslatedType() { + return ""; + } + + default String getUntranslatedOracle() { + return ""; + } + +} diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 463467db6f8..9dd437d9aa5 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -82,8 +82,7 @@ public abstract class SpellAbilityEffect { if (params.containsKey("SpellDescription")) { if (rawSDesc.contains(",,,,,,")) rawSDesc = rawSDesc.replaceAll(",,,,,,", " "); if (rawSDesc.contains(",,,")) rawSDesc = rawSDesc.replaceAll(",,,", " "); - String spellDesc = CardTranslation.translateSingleDescriptionText(rawSDesc, - sa.getHostCard().getName()); + String spellDesc = CardTranslation.translateSingleDescriptionText(rawSDesc, sa.getHostCard()); //trim reminder text from StackDesc int idxL = spellDesc.indexOf(" ("); @@ -113,7 +112,7 @@ public abstract class SpellAbilityEffect { } else { final String condDesc = sa.getParam("ConditionDescription"); final String afterDesc = sa.getParam("AfterDescription"); - final String baseDesc = CardTranslation.translateSingleDescriptionText(this.getStackDescription(sa), sa.getHostCard().getName()); + final String baseDesc = CardTranslation.translateSingleDescriptionText(this.getStackDescription(sa), sa.getHostCard()); if (condDesc != null) { sb.append(condDesc).append(" "); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f94ad7292d5..1e17474b3ce 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -76,7 +76,7 @@ import java.util.Map.Entry; * @author Forge * @version $Id$ */ -public class Card extends GameEntity implements Comparable, IHasSVars { +public class Card extends GameEntity implements Comparable, IHasSVars, ITranslatable { private Game game; private final IPaperCard paperCard; @@ -2227,7 +2227,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { sbLong.append("\r\n"); } sb.append(sbLong); - return CardTranslation.translateMultipleDescriptionText(sb.toString(), getName()); + return CardTranslation.translateMultipleDescriptionText(sb.toString(), this); } // convert a keyword list to the String that should be displayed in game @@ -2632,7 +2632,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { sbLong.append("\r\n"); } sb.append(sbLong); - return CardTranslation.translateMultipleDescriptionText(sb.toString(), getName()); + return CardTranslation.translateMultipleDescriptionText(sb.toString(), this); } private String kickerDesc(String keyword, String remText) { @@ -3194,7 +3194,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } - sb.append(CardTranslation.translateMultipleDescriptionText(sbBefore.toString(), state.getName())); + sb.append(CardTranslation.translateMultipleDescriptionText(sbBefore.toString(), state)); // add Spells there to main StringBuilder sb.append(strSpell); @@ -3223,7 +3223,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } - sb.append(CardTranslation.translateMultipleDescriptionText(sbAfter.toString(), state.getName())); + sb.append(CardTranslation.translateMultipleDescriptionText(sbAfter.toString(), state)); return sb; } @@ -3553,8 +3553,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final void setCopiedPermanent(final Card c) { if (copiedPermanent == c) { return; } copiedPermanent = c; - if(c != null) + if(c != null) { currentState.setOracleText(c.getOracleText()); + currentState.setFunctionalVariantName(c.getCurrentState().getFunctionalVariantName()); + } //Could fetch the card rules oracle text in an "else" clause here, //but CardRules isn't aware of the card's state. May be better to //just stash the original oracle text if this comes up. @@ -7558,6 +7560,23 @@ public class Card extends GameEntity implements Comparable, IHasSVars { currentState.setOracleText(oracleText); } + @Override + public String getTranslationKey() { + return currentState.getTranslationKey(); + } + @Override + public String getUntranslatedName() { + return this.getName(); + } + @Override + public String getUntranslatedType() { + return currentState.getUntranslatedType(); + } + @Override + public String getUntranslatedOracle() { + return currentState.getUntranslatedOracle(); + } + @Override public CardView getView() { return view; diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index dbf3ab681e5..45891ffedf3 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -355,13 +355,16 @@ public class CardFactory { } private static void readCardFace(Card c, ICardFace face) { + String variantName = null; //If it's a functional variant card, switch to that first. if(face.hasFunctionalVariants()) { - String variantName = c.getPaperCard().getFunctionalVariant(); + variantName = c.getPaperCard().getFunctionalVariant(); if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(variantName)) { ICardFace variant = face.getFunctionalVariant(variantName); - if (variant != null) + if (variant != null) { face = variant; + c.getCurrentState().setFunctionalVariantName(variantName); + } else System.err.printf("Tried to apply unknown or unsupported variant - Card: \"%s\"; Variant: %s\n", face.getName(), variantName); } @@ -369,7 +372,7 @@ public class CardFactory { // Build English oracle and translated oracle mapping if (c.getId() >= 0) { - CardTranslation.buildOracleMapping(face.getName(), face.getOracleText()); + CardTranslation.buildOracleMapping(face.getName(), face.getOracleText(), variantName); } // Name first so Senty has the Card name diff --git a/forge-game/src/main/java/forge/game/card/CardState.java b/forge-game/src/main/java/forge/game/card/CardState.java index 2e5e206b514..0cfb70e1a66 100644 --- a/forge-game/src/main/java/forge/game/card/CardState.java +++ b/forge-game/src/main/java/forge/game/card/CardState.java @@ -47,17 +47,20 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityPredicates; import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; +import forge.util.ITranslatable; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; import io.sentry.Breadcrumb; import io.sentry.Sentry; +import org.apache.commons.lang3.StringUtils; -public class CardState extends GameObject implements IHasSVars { +public class CardState extends GameObject implements IHasSVars, ITranslatable { private String name = ""; private CardType type = new CardType(false); private ManaCost manaCost = ManaCost.NO_COST; private byte color = MagicColor.COLORLESS; private String oracleText = ""; + private String functionalVariantName = null; private int basePower = 0; private int baseToughness = 0; private String basePowerString = null; @@ -202,6 +205,16 @@ public class CardState extends GameObject implements IHasSVars { view.setOracleText(oracleText); } + public String getFunctionalVariantName() { + return functionalVariantName; + } + public void setFunctionalVariantName(String functionalVariantName) { + if(functionalVariantName != null && functionalVariantName.isEmpty()) + functionalVariantName = null; + this.functionalVariantName = functionalVariantName; + view.setFunctionalVariantName(functionalVariantName); + } + public final int getBasePower() { return basePower; @@ -605,6 +618,7 @@ public class CardState extends GameObject implements IHasSVars { setManaCost(source.getManaCost()); setColor(source.getColor()); setOracleText(source.getOracleText()); + setFunctionalVariantName(source.getFunctionalVariantName()); setBasePower(source.getBasePower()); setBaseToughness(source.getBaseToughness()); setBaseLoyalty(source.getBaseLoyalty()); @@ -804,4 +818,21 @@ public class CardState extends GameObject implements IHasSVars { } return cloakUp; } + + @Override + public String getTranslationKey() { + if(StringUtils.isNotEmpty(functionalVariantName)) + return name + " $" + functionalVariantName; + return name; + } + + @Override + public String getUntranslatedType() { + return getType().toString(); + } + + @Override + public String getUntranslatedOracle() { + return getOracleText(); + } } diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 336759b930a..79d180e7a3a 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -22,10 +22,7 @@ import forge.trackable.TrackableCollection; import forge.trackable.TrackableObject; import forge.trackable.TrackableProperty; import forge.trackable.Tracker; -import forge.util.CardTranslation; -import forge.util.Lang; -import forge.util.Localizer; -import forge.util.TextUtil; +import forge.util.*; import forge.util.collect.FCollectionView; import org.apache.commons.lang3.StringUtils; @@ -1165,7 +1162,7 @@ public class CardView extends GameEntityView { return (zone + ' ' + CardTranslation.getTranslatedName(name) + " (" + getId() + ")").trim(); } - public class CardStateView extends TrackableObject { + public class CardStateView extends TrackableObject implements ITranslatable { private static final long serialVersionUID = 6673944200513430607L; private final CardStateName state; @@ -1318,6 +1315,13 @@ public class CardView extends GameEntityView { set(TrackableProperty.OracleText, oracleText.replace("\\n", "\r\n\r\n").trim()); } + public String getFunctionalVariantName() { + return get(TrackableProperty.FunctionalVariant); + } + void setFunctionalVariantName(String functionalVariant) { + set(TrackableProperty.FunctionalVariant, functionalVariant); + } + public String getRulesText() { return get(TrackableProperty.RulesText); } @@ -1741,6 +1745,25 @@ public class CardView extends GameEntityView { public boolean isAttraction() { return getType().isAttraction(); } + + @Override + public String getTranslationKey() { + String key = getName(); + String variant = getFunctionalVariantName(); + if(StringUtils.isNotEmpty(variant)) + key = key + " $" + variant; + return key; + } + + @Override + public String getUntranslatedType() { + return getType().toString(); + } + + @Override + public String getUntranslatedOracle() { + return getOracleText(); + } } //special methods for updating card and player properties as needed and returning the new collection diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java index bb74c9c2871..70b1284bd4c 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java @@ -23,6 +23,7 @@ import java.util.Objects; import com.google.common.collect.*; +import forge.util.ITranslatable; import org.apache.commons.lang3.StringUtils; import forge.game.Game; @@ -221,15 +222,16 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { public String getDescription() { if (hasParam("Description") && !this.isSuppressed()) { String desc = AbilityUtils.applyDescriptionTextChangeEffects(getParam("Description"), this); - String currentName; + ITranslatable nameSource; if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) { - currentName = cardState.getName(); + nameSource = cardState; } else { - currentName = getHostCard().getName(); + nameSource = getHostCard(); } - desc = CardTranslation.translateSingleDescriptionText(desc, currentName); - desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); - desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); + desc = CardTranslation.translateMultipleDescriptionText(desc, nameSource); + String translatedName = CardTranslation.getTranslatedName(nameSource); + desc = TextUtil.fastReplace(desc, "CARDNAME", translatedName); + desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(translatedName)); if (desc.contains("EFFECTSOURCE")) { desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", getHostCard().getEffectSource().toString()); } diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index aa6bfafb962..6e43c207946 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -20,6 +20,7 @@ package forge.game.spellability; import java.util.*; import forge.game.cost.CostSacrifice; +import forge.util.*; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -68,11 +69,6 @@ import forge.game.staticability.StaticAbilityMustTarget; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; -import forge.util.Aggregates; -import forge.util.CardTranslation; -import forge.util.Lang; -import forge.util.Localizer; -import forge.util.TextUtil; //only SpellAbility can go on the stack //override any methods as needed @@ -988,16 +984,17 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } String desc = node.getDescription(); if (node.getHostCard() != null) { - String currentName; + ITranslatable nameSource; // if alternate state is viewed while card uses original if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) { - currentName = node.cardState.getName(); + nameSource = node.cardState; } else { - currentName = node.getHostCard().getName(); + nameSource = node.getHostCard(); } - desc = CardTranslation.translateMultipleDescriptionText(desc, currentName); - desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); - desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); + desc = CardTranslation.translateMultipleDescriptionText(desc, nameSource); + String translatedName = CardTranslation.getTranslatedName(nameSource); + desc = TextUtil.fastReplace(desc, "CARDNAME", translatedName); + desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(translatedName)); if (node.getOriginalHost() != null) { desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getOriginalHost().getName()); } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index 9474eae2bbc..7a4952bf594 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -45,11 +45,7 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.util.CardTranslation; -import forge.util.Expressions; -import forge.util.FileSection; -import forge.util.Lang; -import forge.util.TextUtil; +import forge.util.*; /** * The Class StaticAbility. @@ -187,15 +183,16 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone @Override public final String toString() { if (hasParam("Description") && !this.isSuppressed()) { - String currentName; + ITranslatable nameSource; if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) { - currentName = cardState.getName(); + nameSource = cardState; } else { - currentName = getHostCard().getName(); + nameSource = getHostCard(); } - String desc = CardTranslation.translateSingleDescriptionText(getParam("Description"), currentName); - desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); - desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); + String desc = CardTranslation.translateSingleDescriptionText(getParam("Description"), nameSource); + String translatedName = CardTranslation.getTranslatedName(nameSource); + desc = TextUtil.fastReplace(desc, "CARDNAME", translatedName); + desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(translatedName)); return desc; } else { diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java index 0e012e7d4bc..99152cfbd57 100644 --- a/forge-game/src/main/java/forge/game/trigger/Trigger.java +++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java @@ -36,6 +36,7 @@ import forge.game.spellability.SpellAbility; import forge.game.zone.CostPaymentStack; import forge.game.zone.ZoneType; import forge.util.CardTranslation; +import forge.util.ITranslatable; import forge.util.Lang; import forge.util.TextUtil; @@ -119,17 +120,18 @@ public abstract class Trigger extends TriggerReplacementBase { public String toString(boolean active) { if (hasParam("TriggerDescription") && !this.isSuppressed()) { StringBuilder sb = new StringBuilder(); - String currentName; + ITranslatable nameSource; if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) { - currentName = cardState.getName(); + nameSource = cardState; } else { - currentName = getHostCard().getName(); + nameSource = getHostCard(); } String desc = getParam("TriggerDescription"); if (!desc.contains("ABILITY")) { - desc = CardTranslation.translateSingleDescriptionText(getParam("TriggerDescription"), currentName); - desc = TextUtil.fastReplace(desc,"CARDNAME", CardTranslation.getTranslatedName(currentName)); - desc = TextUtil.fastReplace(desc,"NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); + desc = CardTranslation.translateSingleDescriptionText(getParam("TriggerDescription"), nameSource); + String translatedName = CardTranslation.getTranslatedName(nameSource); + desc = TextUtil.fastReplace(desc,"CARDNAME", translatedName); + desc = TextUtil.fastReplace(desc,"NICKNAME", Lang.getInstance().getNickName(translatedName)); if (desc.contains("ORIGINALHOST") && this.getOriginalHost() != null) { desc = TextUtil.fastReplace(desc, "ORIGINALHOST", this.getOriginalHost().getName()); } @@ -220,10 +222,10 @@ public abstract class Trigger extends TriggerReplacementBase { } result = TextUtil.fastReplace(result, "ABILITY", saDesc); - String currentName = sa.getHostCard().getName(); - result = CardTranslation.translateMultipleDescriptionText(result, currentName); - result = TextUtil.fastReplace(result,"CARDNAME", CardTranslation.getTranslatedName(currentName)); - result = TextUtil.fastReplace(result,"NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); + result = CardTranslation.translateMultipleDescriptionText(result, sa.getHostCard()); + String translatedName = CardTranslation.getTranslatedName(sa.getHostCard()); + result = TextUtil.fastReplace(result,"CARDNAME", translatedName); + result = TextUtil.fastReplace(result,"NICKNAME", Lang.getInstance().getNickName(translatedName)); } return result; diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 4ef568df142..88a10d07e93 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -123,6 +123,7 @@ public enum TrackableProperty { ManaCost(TrackableTypes.ManaCostType), SetCode(TrackableTypes.StringType), Rarity(TrackableTypes.EnumType(CardRarity.class)), + FunctionalVariant(TrackableTypes.StringType), OracleText(TrackableTypes.StringType), RulesText(TrackableTypes.StringType), Power(TrackableTypes.IntegerType), diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java b/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java index 32f9e7c2821..5186889f9dc 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java @@ -203,9 +203,9 @@ public class FCardImageRenderer { if (card.isSplitCard()) { boolean needTranslation = !"en-US".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE)); final CardStateView leftState = card.getLeftSplitState(); - final String leftText = needTranslation ? CardTranslation.getTranslatedOracle(leftState.getName()) : leftState.getOracleText(); + final String leftText = needTranslation ? CardTranslation.getTranslatedOracle(leftState) : leftState.getOracleText(); final CardStateView rightState = card.getRightSplitState(); - String rightText = needTranslation ? CardTranslation.getTranslatedOracle(rightState.getName()) : rightState.getOracleText(); + String rightText = needTranslation ? CardTranslation.getTranslatedOracle(rightState) : rightState.getOracleText(); boolean isAftermath = (rightState.getKeywordKey().contains("Aftermath")); BufferedImage leftArt = null; BufferedImage rightArt = null; @@ -247,9 +247,9 @@ public class FCardImageRenderer { } else if (card.isFlipCard()) { boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null); final CardStateView state = card.getState(false); - final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null); + final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state) : null); final CardStateView flipState = card.getState(true); - final String flipText = card.getText(flipState, needTranslation ? CardTranslation.getTranslationTexts(flipState.getName(), "") : null); + final String flipText = card.getText(flipState, needTranslation ? CardTranslation.getTranslationTexts(flipState) : null); CARD_ART_RATIO = 1.728f; updateAreaSizes(ratio, ratio); int heightAdjust = OUTER_BORDER_THICKNESS + PT_SIZE / 2; @@ -261,16 +261,16 @@ public class FCardImageRenderer { } else if (card.isAdventureCard()) { boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null); final CardStateView state = card.getState(false); - final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null); + final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state) : null); final CardStateView advState = card.getState(true); - final String advText = card.getText(advState, needTranslation ? CardTranslation.getTranslationTexts(advState.getName(), "") : null); + final String advText = card.getText(advState, needTranslation ? CardTranslation.getTranslationTexts(advState) : null); CARD_ART_RATIO = 1.37f; updateAreaSizes(ratio, ratio); drawAdvCardImage(g, state, text, advState, advText, width, height, art); } else { boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null); final CardStateView state = card.getState(altState); - final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null); + final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state) : null); CARD_ART_RATIO = 1.37f; if (art != null && Math.abs((float)art.getWidth() / (float)art.getHeight() - CARD_ART_RATIO) > 0.1f) { CARD_ART_RATIO = (float)art.getWidth() / (float)art.getHeight(); diff --git a/forge-gui-mobile/src/forge/card/CardImageRenderer.java b/forge-gui-mobile/src/forge/card/CardImageRenderer.java index 91a0d116e70..b76cefdc408 100644 --- a/forge-gui-mobile/src/forge/card/CardImageRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardImageRenderer.java @@ -670,12 +670,13 @@ public class CardImageRenderer { CardView cv = card.getBackup(); if (cv == null || isFaceDown) cv = card; - text = cv.getText(cv.getState(true), needTranslation ? CardTranslation.getTranslationTexts(cv.getName(), "") : null); + CardStateView csv = cv.getState(true); + text = cv.getText(csv, needTranslation ? CardTranslation.getTranslationTexts(csv) : null); } else { text = !card.isSplitCard() ? - card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state.getName(), "") : null) : - card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null); + card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state) : null) : + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState(), card.getRightSplitState()) : null); } } else { if (noText) @@ -684,12 +685,13 @@ public class CardImageRenderer { CardView cv = card.getBackup(); if (cv == null || isFaceDown) cv = card; - text = cv.getText(cv.getState(false), needTranslation ? CardTranslation.getTranslationTexts(cv.getName(), "") : null); + CardStateView csv = cv.getState(false); + text = cv.getText(csv, needTranslation ? CardTranslation.getTranslationTexts(csv) : null); } else { text = !card.isSplitCard() ? - card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state.getName(), "") : null) : - card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null); + card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state) : null) : + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState(), card.getRightSplitState()) : null); } } if (StringUtils.isEmpty(text)) { diff --git a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java index 68bb4f7a236..59ebe64a76c 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java @@ -178,7 +178,7 @@ public class CardDetailUtil { public static String formatCardType(final CardStateView card, final boolean canShow) { boolean isInPlay = card.getCard() != null && ZoneType.Battlefield.equals(card.getCard().getZone()); - String translatedtype = CardTranslation.getTranslatedType(card.getName(), card.getType().toString()); + String translatedtype = CardTranslation.getTranslatedType(card); return canShow ? translatedtype : (card.getState() == CardStateName.FaceDown && isInPlay ? "Creature" : ""); } @@ -319,8 +319,8 @@ public class CardDetailUtil { needTranslation = false; } String text = !card.isSplitCard() ? - card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) : - card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null ); + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state) : null) : + card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState(), card.getRightSplitState()) : null ); // Bracket P/T for Level up if (text.contains("LEVEL")) { diff --git a/forge-gui/src/main/java/forge/itemmanager/AdvancedSearch.java b/forge-gui/src/main/java/forge/itemmanager/AdvancedSearch.java index 1e3a5b5307c..8eb1e0c34c7 100644 --- a/forge-gui/src/main/java/forge/itemmanager/AdvancedSearch.java +++ b/forge-gui/src/main/java/forge/itemmanager/AdvancedSearch.java @@ -82,11 +82,12 @@ public class AdvancedSearch { protected Set getItemValues(PaperCard input) { Set names = new HashSet<>(); names.add(input.getRules().getOracleText()); - names.add(CardTranslation.getTranslatedOracle(input.getName())); + names.add(CardTranslation.getTranslatedOracle(input)); CardSplitType cardSplitType = input.getRules().getSplitType(); if (cardSplitType != CardSplitType.None && cardSplitType != CardSplitType.Split) { if (input.getRules().getOtherPart() != null) { names.add(input.getRules().getOtherPart().getOracleText()); + //Doesn't support a combination of functional variant + split card, but none of those exist yet. names.add(CardTranslation.getTranslatedOracle(input.getRules().getOtherPart().getName())); } } diff --git a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java index 68750c3dd6b..77e25d479b6 100644 --- a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java +++ b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java @@ -81,8 +81,8 @@ public enum ColumnDef { * The type column. */ TYPE("lblType", "ttType", 100, false, SortState.ASC, - from -> CardTranslation.getTranslatedType(from.getKey().getName(), toType(from.getKey())), - from -> CardTranslation.getTranslatedType(from.getKey().getName(), toType(from.getKey()))), + from -> CardTranslation.getTranslatedType(from.getKey()), + from -> CardTranslation.getTranslatedType(from.getKey())), /** * The mana cost column. */ @@ -357,10 +357,6 @@ public enum ColumnDef { return this.longName; } - private static String toType(final InventoryItem i) { - return i instanceof IPaperCard ? ((IPaperCard) i).getRules().getType().toString() : i.getItemType(); - } - private static IPaperCard toCard(final InventoryItem i) { return i instanceof IPaperCard ? ((IPaperCard) i) : null; }