Merge pull request #5991 from Jetz72/fixes20240825

Translation support for functional variants
This commit is contained in:
kevlahnota
2024-08-27 12:43:35 +08:00
committed by GitHub
22 changed files with 282 additions and 99 deletions

View File

@@ -156,6 +156,9 @@ final class CardFace implements ICardFace, Cloneable {
return null; return null;
return this.functionalVariants.get(variant); return this.functionalVariants.get(variant);
} }
@Override public Map<String, ? extends ICardFace> getFunctionalVariants() {
return this.functionalVariants;
}
CardFace getOrCreateFunctionalVariant(String variant) { CardFace getOrCreateFunctionalVariant(String variant) {
if (this.functionalVariants == null) { if (this.functionalVariants == null) {
this.functionalVariants = new HashMap<>(); this.functionalVariants = new HashMap<>();

View File

@@ -334,6 +334,17 @@ public final class CardRulesPredicates {
if (face == null) { if (face == null) {
return false; return false;
} }
if (face.hasFunctionalVariants()) {
for (Map.Entry<String, ? extends ICardFace> 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)) { if (op(face.getOracleText(), operand) || op(CardTranslation.getTranslatedOracle(face.getName()), operand)) {
return true; return true;
} }
@@ -343,6 +354,16 @@ public final class CardRulesPredicates {
if (face == null) { if (face == null) {
return false; return false;
} }
if (face.hasFunctionalVariants()) {
for (Map.Entry<String, ? extends ICardFace> 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)); return (op(CardTranslation.getTranslatedType(face.getName(), face.getType().toString()), operand) || op(face.getType().toString(), operand));
} }

View File

@@ -1,5 +1,7 @@
package forge.card; package forge.card;
import java.util.Map;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
* *
@@ -9,4 +11,5 @@ public interface ICardFace extends ICardCharacteristics, ICardRawAbilites, Compa
boolean hasFunctionalVariants(); boolean hasFunctionalVariants();
ICardFace getFunctionalVariant(String variant); ICardFace getFunctionalVariant(String variant);
Map<String, ? extends ICardFace> getFunctionalVariants();
} }

View File

@@ -253,5 +253,22 @@ public interface IPaperCard extends InventoryItem, Serializable {
String getCardRSpecImageKey(); String getCardRSpecImageKey();
String getCardGSpecImageKey(); 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();
}
} }

View File

@@ -17,13 +17,18 @@
*/ */
package forge.item; package forge.item;
import forge.util.IHasName; import forge.util.ITranslatable;
/** /**
* Interface to define a player's inventory may hold. Should include * Interface to define a player's inventory may hold. Should include
* CardPrinted, Booster, Pets, Plants... etc * CardPrinted, Booster, Pets, Plants... etc
*/ */
public interface InventoryItem extends IHasName { public interface InventoryItem extends ITranslatable {
String getItemType(); String getItemType();
String getImageKey(boolean altState); String getImageKey(boolean altState);
@Override
default String getUntranslatedType() {
return getItemType();
}
} }

View File

@@ -28,6 +28,15 @@ public class CardTranslation {
for (String line : translationFile.readLines()) { for (String line : translationFile.readLines()) {
String[] matches = line.split("\\|"); String[] matches = line.split("\\|");
if (matches.length >= 2) { 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]); translatednames.put(matches[0], matches[1]);
} }
if (matches.length >= 3) { if (matches.length >= 3) {
@@ -53,7 +62,7 @@ public class CardTranslation {
if (name.contains(" // ")) { if (name.contains(" // ")) {
int splitIndex = name.indexOf(" // "); int splitIndex = name.indexOf(" // ");
String leftname = name.substring(0, splitIndex); 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); return translatednames.getOrDefault(leftname, leftname) + " // " + translatednames.getOrDefault(rightname, rightname);
} }
try { try {
@@ -74,6 +83,10 @@ public class CardTranslation {
return name; return name;
} }
public static String getTranslatedName(ITranslatable card) {
return getTranslatedName(card.getUntranslatedName());
}
private static String translateTokenName(String name) { private static String translateTokenName(String name) {
if (translatedTokenNames == null) if (translatedTokenNames == null)
translatedTokenNames = new HashMap<>(); translatedTokenNames = new HashMap<>();
@@ -203,6 +216,12 @@ public class CardTranslation {
return originaltype; 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) { public static String getTranslatedOracle(String name) {
if (needsTranslation()) { if (needsTranslation()) {
String toracle = translatedoracles.get(name); String toracle = translatedoracles.get(name);
@@ -212,13 +231,30 @@ public class CardTranslation {
return ""; return "";
} }
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) { public static String getTranslatedOracle(ITranslatable card) {
if (!needsTranslation()) return null; if(!needsTranslation())
return ""; //card.getUntranslatedOracle();
//Fallbacks and english versions of oracle texts are handled elsewhere.
return translatedoracles.getOrDefault(card.getTranslationKey(), "");
}
public static HashMap<String, String> getTranslationTexts(ITranslatable card) {
return getTranslationTexts(card, null);
}
public static HashMap<String, String> getTranslationTexts(ITranslatable cardMain, ITranslatable cardOther) {
if(!needsTranslation()) return null;
HashMap<String, String> translations = new HashMap<>(); HashMap<String, String> translations = new HashMap<>();
translations.put("name", getTranslatedName(cardname)); translations.put("name", getTranslatedName(cardMain));
translations.put("oracle", getTranslatedOracle(cardname)); translations.put("oracle", getTranslatedOracle(cardMain));
translations.put("altname", getTranslatedName(altcardname)); if(cardOther == null) {
translations.put("altoracle", getTranslatedOracle(altcardname)); translations.put("altname", "");
translations.put("altoracle", "");
}
else {
translations.put("altname", getTranslatedName(cardOther));
translations.put("altoracle", getTranslatedOracle(cardOther));
}
return translations; return translations;
} }
@@ -248,14 +284,17 @@ public class CardTranslation {
return result; return result;
} }
public static void buildOracleMapping(String faceName, String oracleText) { public static void buildOracleMapping(String faceName, String oracleText, String variantName) {
if (!needsTranslation() || oracleMappings.containsKey(faceName)) return; String translationKey = faceName;
String translatedText = getTranslatedOracle(faceName); if(variantName != null)
translationKey = faceName + " $" + variantName;
if (!needsTranslation() || oracleMappings.containsKey(translationKey)) return;
String translatedText = getTranslatedOracle(translationKey);
if (translatedText.isEmpty()) { if (translatedText.isEmpty()) {
// english card only, fall back // english card only, fall back
return; return;
} }
String translatedName = getTranslatedName(faceName); String translatedName = getTranslatedName(translationKey);
List <Pair <String, String> > mapping = new ArrayList<>(); List <Pair <String, String> > mapping = new ArrayList<>();
String [] splitOracleText = oracleText.split("\\\\n"); String [] splitOracleText = oracleText.split("\\\\n");
String [] splitTranslatedText = translatedText.split("\r\n\r\n"); String [] splitTranslatedText = translatedText.split("\r\n\r\n");
@@ -269,17 +308,17 @@ public class CardTranslation {
} }
mapping.add(Pair.of(toracle, ttranslated)); 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; if (!needsTranslation()) return descText;
String [] splitDescText = descText.split("\n"); String [] splitDescText = descText.split("\n");
String result = descText; String result = descText;
for (String text : splitDescText) { for (String text : splitDescText) {
text = text.trim(); text = text.trim();
if (text.isEmpty()) continue; if (text.isEmpty()) continue;
String translated = translateSingleDescriptionText(text, cardName); String translated = translateSingleDescriptionText(text, card);
if (!text.equals(translated)) { if (!text.equals(translated)) {
result = TextUtil.fastReplace(result, text, translated); result = TextUtil.fastReplace(result, text, translated);
} else { } else {
@@ -288,7 +327,7 @@ public class CardTranslation {
if (splitKeywords.length <= 1) continue; if (splitKeywords.length <= 1) continue;
for (String keyword : splitKeywords) { for (String keyword : splitKeywords) {
if (keyword.contains(" ")) continue; if (keyword.contains(" ")) continue;
translated = translateSingleDescriptionText(keyword, cardName); translated = translateSingleDescriptionText(keyword, card);
if (!keyword.equals(translated)) { if (!keyword.equals(translated)) {
result = TextUtil.fastReplace(result, keyword, translated); result = TextUtil.fastReplace(result, keyword, translated);
} }
@@ -298,13 +337,13 @@ public class CardTranslation {
return result; return result;
} }
public static String translateSingleDescriptionText(String descText, String cardName) { public static String translateSingleDescriptionText(String descText, ITranslatable card) {
if (descText == null) if (descText == null)
return ""; return "";
if (!needsTranslation()) return descText; if (!needsTranslation()) return descText;
if (translatedCaches.containsKey(descText)) return translatedCaches.get(descText); if (translatedCaches.containsKey(descText)) return translatedCaches.get(descText);
List <Pair <String, String> > mapping = oracleMappings.get(cardName); List <Pair <String, String> > mapping = oracleMappings.get(card.getTranslationKey());
if (mapping == null) return descText; if (mapping == null) return descText;
String result = descText; String result = descText;
if (!mapping.isEmpty()) { if (!mapping.isEmpty()) {

View File

@@ -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 "";
}
}

View File

@@ -82,8 +82,7 @@ public abstract class SpellAbilityEffect {
if (params.containsKey("SpellDescription")) { if (params.containsKey("SpellDescription")) {
if (rawSDesc.contains(",,,,,,")) rawSDesc = rawSDesc.replaceAll(",,,,,,", " "); if (rawSDesc.contains(",,,,,,")) rawSDesc = rawSDesc.replaceAll(",,,,,,", " ");
if (rawSDesc.contains(",,,")) rawSDesc = rawSDesc.replaceAll(",,,", " "); if (rawSDesc.contains(",,,")) rawSDesc = rawSDesc.replaceAll(",,,", " ");
String spellDesc = CardTranslation.translateSingleDescriptionText(rawSDesc, String spellDesc = CardTranslation.translateSingleDescriptionText(rawSDesc, sa.getHostCard());
sa.getHostCard().getName());
//trim reminder text from StackDesc //trim reminder text from StackDesc
int idxL = spellDesc.indexOf(" ("); int idxL = spellDesc.indexOf(" (");
@@ -113,7 +112,7 @@ public abstract class SpellAbilityEffect {
} else { } else {
final String condDesc = sa.getParam("ConditionDescription"); final String condDesc = sa.getParam("ConditionDescription");
final String afterDesc = sa.getParam("AfterDescription"); 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) { if (condDesc != null) {
sb.append(condDesc).append(" "); sb.append(condDesc).append(" ");
} }

View File

@@ -76,7 +76,7 @@ import java.util.Map.Entry;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class Card extends GameEntity implements Comparable<Card>, IHasSVars { public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITranslatable {
private Game game; private Game game;
private final IPaperCard paperCard; private final IPaperCard paperCard;
@@ -2227,7 +2227,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
sbLong.append("\r\n"); sbLong.append("\r\n");
} }
sb.append(sbLong); 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 // convert a keyword list to the String that should be displayed in game
@@ -2632,7 +2632,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
sbLong.append("\r\n"); sbLong.append("\r\n");
} }
sb.append(sbLong); sb.append(sbLong);
return CardTranslation.translateMultipleDescriptionText(sb.toString(), getName()); return CardTranslation.translateMultipleDescriptionText(sb.toString(), this);
} }
private String kickerDesc(String keyword, String remText) { private String kickerDesc(String keyword, String remText) {
@@ -3194,7 +3194,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
} }
sb.append(CardTranslation.translateMultipleDescriptionText(sbBefore.toString(), state.getName())); sb.append(CardTranslation.translateMultipleDescriptionText(sbBefore.toString(), state));
// add Spells there to main StringBuilder // add Spells there to main StringBuilder
sb.append(strSpell); sb.append(strSpell);
@@ -3223,7 +3223,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
} }
sb.append(CardTranslation.translateMultipleDescriptionText(sbAfter.toString(), state.getName())); sb.append(CardTranslation.translateMultipleDescriptionText(sbAfter.toString(), state));
return sb; return sb;
} }
@@ -3553,8 +3553,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public final void setCopiedPermanent(final Card c) { public final void setCopiedPermanent(final Card c) {
if (copiedPermanent == c) { return; } if (copiedPermanent == c) { return; }
copiedPermanent = c; copiedPermanent = c;
if(c != null) if(c != null) {
currentState.setOracleText(c.getOracleText()); currentState.setOracleText(c.getOracleText());
currentState.setFunctionalVariantName(c.getCurrentState().getFunctionalVariantName());
}
//Could fetch the card rules oracle text in an "else" clause here, //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 //but CardRules isn't aware of the card's state. May be better to
//just stash the original oracle text if this comes up. //just stash the original oracle text if this comes up.
@@ -7560,6 +7562,23 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
currentState.setOracleText(oracleText); 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 @Override
public CardView getView() { public CardView getView() {
return view; return view;

View File

@@ -355,13 +355,16 @@ public class CardFactory {
} }
private static void readCardFace(Card c, ICardFace face) { private static void readCardFace(Card c, ICardFace face) {
String variantName = null;
//If it's a functional variant card, switch to that first. //If it's a functional variant card, switch to that first.
if(face.hasFunctionalVariants()) { if(face.hasFunctionalVariants()) {
String variantName = c.getPaperCard().getFunctionalVariant(); variantName = c.getPaperCard().getFunctionalVariant();
if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(variantName)) { if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(variantName)) {
ICardFace variant = face.getFunctionalVariant(variantName); ICardFace variant = face.getFunctionalVariant(variantName);
if (variant != null) if (variant != null) {
face = variant; face = variant;
c.getCurrentState().setFunctionalVariantName(variantName);
}
else else
System.err.printf("Tried to apply unknown or unsupported variant - Card: \"%s\"; Variant: %s\n", face.getName(), variantName); 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 // Build English oracle and translated oracle mapping
if (c.getId() >= 0) { 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 // Name first so Senty has the Card name

View File

@@ -47,17 +47,20 @@ import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityPredicates; import forge.game.spellability.SpellAbilityPredicates;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.util.ITranslatable;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import io.sentry.Breadcrumb; import io.sentry.Breadcrumb;
import io.sentry.Sentry; 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 String name = "";
private CardType type = new CardType(false); private CardType type = new CardType(false);
private ManaCost manaCost = ManaCost.NO_COST; private ManaCost manaCost = ManaCost.NO_COST;
private byte color = MagicColor.COLORLESS; private byte color = MagicColor.COLORLESS;
private String oracleText = ""; private String oracleText = "";
private String functionalVariantName = null;
private int basePower = 0; private int basePower = 0;
private int baseToughness = 0; private int baseToughness = 0;
private String basePowerString = null; private String basePowerString = null;
@@ -202,6 +205,16 @@ public class CardState extends GameObject implements IHasSVars {
view.setOracleText(oracleText); 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() { public final int getBasePower() {
return basePower; return basePower;
@@ -605,6 +618,7 @@ public class CardState extends GameObject implements IHasSVars {
setManaCost(source.getManaCost()); setManaCost(source.getManaCost());
setColor(source.getColor()); setColor(source.getColor());
setOracleText(source.getOracleText()); setOracleText(source.getOracleText());
setFunctionalVariantName(source.getFunctionalVariantName());
setBasePower(source.getBasePower()); setBasePower(source.getBasePower());
setBaseToughness(source.getBaseToughness()); setBaseToughness(source.getBaseToughness());
setBaseLoyalty(source.getBaseLoyalty()); setBaseLoyalty(source.getBaseLoyalty());
@@ -804,4 +818,21 @@ public class CardState extends GameObject implements IHasSVars {
} }
return cloakUp; 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();
}
} }

View File

@@ -22,10 +22,7 @@ import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject; import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty; import forge.trackable.TrackableProperty;
import forge.trackable.Tracker; import forge.trackable.Tracker;
import forge.util.CardTranslation; import forge.util.*;
import forge.util.Lang;
import forge.util.Localizer;
import forge.util.TextUtil;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -1165,7 +1162,7 @@ public class CardView extends GameEntityView {
return (zone + ' ' + CardTranslation.getTranslatedName(name) + " (" + getId() + ")").trim(); 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 static final long serialVersionUID = 6673944200513430607L;
private final CardStateName state; private final CardStateName state;
@@ -1318,6 +1315,13 @@ public class CardView extends GameEntityView {
set(TrackableProperty.OracleText, oracleText.replace("\\n", "\r\n\r\n").trim()); 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() { public String getRulesText() {
return get(TrackableProperty.RulesText); return get(TrackableProperty.RulesText);
} }
@@ -1741,6 +1745,25 @@ public class CardView extends GameEntityView {
public boolean isAttraction() { public boolean isAttraction() {
return getType().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 //special methods for updating card and player properties as needed and returning the new collection

View File

@@ -23,6 +23,7 @@ import java.util.Objects;
import com.google.common.collect.*; import com.google.common.collect.*;
import forge.util.ITranslatable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import forge.game.Game; import forge.game.Game;
@@ -221,15 +222,16 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
public String getDescription() { public String getDescription() {
if (hasParam("Description") && !this.isSuppressed()) { if (hasParam("Description") && !this.isSuppressed()) {
String desc = AbilityUtils.applyDescriptionTextChangeEffects(getParam("Description"), this); String desc = AbilityUtils.applyDescriptionTextChangeEffects(getParam("Description"), this);
String currentName; ITranslatable nameSource;
if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) { if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) {
currentName = cardState.getName(); nameSource = cardState;
} else { } else {
currentName = getHostCard().getName(); nameSource = getHostCard();
} }
desc = CardTranslation.translateSingleDescriptionText(desc, currentName); desc = CardTranslation.translateMultipleDescriptionText(desc, nameSource);
desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); String translatedName = CardTranslation.getTranslatedName(nameSource);
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); desc = TextUtil.fastReplace(desc, "CARDNAME", translatedName);
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(translatedName));
if (desc.contains("EFFECTSOURCE")) { if (desc.contains("EFFECTSOURCE")) {
desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", getHostCard().getEffectSource().toString()); desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", getHostCard().getEffectSource().toString());
} }

View File

@@ -20,6 +20,7 @@ package forge.game.spellability;
import java.util.*; import java.util.*;
import forge.game.cost.CostSacrifice; import forge.game.cost.CostSacrifice;
import forge.util.*;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; 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.Trigger;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType; 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 //only SpellAbility can go on the stack
//override any methods as needed //override any methods as needed
@@ -988,16 +984,17 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
String desc = node.getDescription(); String desc = node.getDescription();
if (node.getHostCard() != null) { if (node.getHostCard() != null) {
String currentName; ITranslatable nameSource;
// if alternate state is viewed while card uses original // if alternate state is viewed while card uses original
if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) { if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) {
currentName = node.cardState.getName(); nameSource = node.cardState;
} else { } else {
currentName = node.getHostCard().getName(); nameSource = node.getHostCard();
} }
desc = CardTranslation.translateMultipleDescriptionText(desc, currentName); desc = CardTranslation.translateMultipleDescriptionText(desc, nameSource);
desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); String translatedName = CardTranslation.getTranslatedName(nameSource);
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); desc = TextUtil.fastReplace(desc, "CARDNAME", translatedName);
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(translatedName));
if (node.getOriginalHost() != null) { if (node.getOriginalHost() != null) {
desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getOriginalHost().getName()); desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getOriginalHost().getName());
} }

View File

@@ -45,11 +45,7 @@ import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.CardTranslation; import forge.util.*;
import forge.util.Expressions;
import forge.util.FileSection;
import forge.util.Lang;
import forge.util.TextUtil;
/** /**
* The Class StaticAbility. * The Class StaticAbility.
@@ -187,15 +183,16 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
@Override @Override
public final String toString() { public final String toString() {
if (hasParam("Description") && !this.isSuppressed()) { if (hasParam("Description") && !this.isSuppressed()) {
String currentName; ITranslatable nameSource;
if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) { if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) {
currentName = cardState.getName(); nameSource = cardState;
} else { } else {
currentName = getHostCard().getName(); nameSource = getHostCard();
} }
String desc = CardTranslation.translateSingleDescriptionText(getParam("Description"), currentName); String desc = CardTranslation.translateSingleDescriptionText(getParam("Description"), nameSource);
desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); String translatedName = CardTranslation.getTranslatedName(nameSource);
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); desc = TextUtil.fastReplace(desc, "CARDNAME", translatedName);
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(translatedName));
return desc; return desc;
} else { } else {

View File

@@ -36,6 +36,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.CostPaymentStack; import forge.game.zone.CostPaymentStack;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.CardTranslation; import forge.util.CardTranslation;
import forge.util.ITranslatable;
import forge.util.Lang; import forge.util.Lang;
import forge.util.TextUtil; import forge.util.TextUtil;
@@ -119,17 +120,18 @@ public abstract class Trigger extends TriggerReplacementBase {
public String toString(boolean active) { public String toString(boolean active) {
if (hasParam("TriggerDescription") && !this.isSuppressed()) { if (hasParam("TriggerDescription") && !this.isSuppressed()) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String currentName; ITranslatable nameSource;
if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) { if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) {
currentName = cardState.getName(); nameSource = cardState;
} else { } else {
currentName = getHostCard().getName(); nameSource = getHostCard();
} }
String desc = getParam("TriggerDescription"); String desc = getParam("TriggerDescription");
if (!desc.contains("ABILITY")) { if (!desc.contains("ABILITY")) {
desc = CardTranslation.translateSingleDescriptionText(getParam("TriggerDescription"), currentName); desc = CardTranslation.translateSingleDescriptionText(getParam("TriggerDescription"), nameSource);
desc = TextUtil.fastReplace(desc,"CARDNAME", CardTranslation.getTranslatedName(currentName)); String translatedName = CardTranslation.getTranslatedName(nameSource);
desc = TextUtil.fastReplace(desc,"NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); desc = TextUtil.fastReplace(desc,"CARDNAME", translatedName);
desc = TextUtil.fastReplace(desc,"NICKNAME", Lang.getInstance().getNickName(translatedName));
if (desc.contains("ORIGINALHOST") && this.getOriginalHost() != null) { if (desc.contains("ORIGINALHOST") && this.getOriginalHost() != null) {
desc = TextUtil.fastReplace(desc, "ORIGINALHOST", this.getOriginalHost().getName()); desc = TextUtil.fastReplace(desc, "ORIGINALHOST", this.getOriginalHost().getName());
} }
@@ -220,10 +222,10 @@ public abstract class Trigger extends TriggerReplacementBase {
} }
result = TextUtil.fastReplace(result, "ABILITY", saDesc); result = TextUtil.fastReplace(result, "ABILITY", saDesc);
String currentName = sa.getHostCard().getName(); result = CardTranslation.translateMultipleDescriptionText(result, sa.getHostCard());
result = CardTranslation.translateMultipleDescriptionText(result, currentName); String translatedName = CardTranslation.getTranslatedName(sa.getHostCard());
result = TextUtil.fastReplace(result,"CARDNAME", CardTranslation.getTranslatedName(currentName)); result = TextUtil.fastReplace(result,"CARDNAME", translatedName);
result = TextUtil.fastReplace(result,"NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); result = TextUtil.fastReplace(result,"NICKNAME", Lang.getInstance().getNickName(translatedName));
} }
return result; return result;

View File

@@ -123,6 +123,7 @@ public enum TrackableProperty {
ManaCost(TrackableTypes.ManaCostType), ManaCost(TrackableTypes.ManaCostType),
SetCode(TrackableTypes.StringType), SetCode(TrackableTypes.StringType),
Rarity(TrackableTypes.EnumType(CardRarity.class)), Rarity(TrackableTypes.EnumType(CardRarity.class)),
FunctionalVariant(TrackableTypes.StringType),
OracleText(TrackableTypes.StringType), OracleText(TrackableTypes.StringType),
RulesText(TrackableTypes.StringType), RulesText(TrackableTypes.StringType),
Power(TrackableTypes.IntegerType), Power(TrackableTypes.IntegerType),

View File

@@ -203,9 +203,9 @@ public class FCardImageRenderer {
if (card.isSplitCard()) { if (card.isSplitCard()) {
boolean needTranslation = !"en-US".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE)); boolean needTranslation = !"en-US".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE));
final CardStateView leftState = card.getLeftSplitState(); 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(); 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")); boolean isAftermath = (rightState.getKeywordKey().contains("Aftermath"));
BufferedImage leftArt = null; BufferedImage leftArt = null;
BufferedImage rightArt = null; BufferedImage rightArt = null;
@@ -247,9 +247,9 @@ public class FCardImageRenderer {
} else if (card.isFlipCard()) { } else if (card.isFlipCard()) {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null); boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(false); 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 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; CARD_ART_RATIO = 1.728f;
updateAreaSizes(ratio, ratio); updateAreaSizes(ratio, ratio);
int heightAdjust = OUTER_BORDER_THICKNESS + PT_SIZE / 2; int heightAdjust = OUTER_BORDER_THICKNESS + PT_SIZE / 2;
@@ -261,16 +261,16 @@ public class FCardImageRenderer {
} else if (card.isAdventureCard()) { } else if (card.isAdventureCard()) {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null); boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(false); 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 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; CARD_ART_RATIO = 1.37f;
updateAreaSizes(ratio, ratio); updateAreaSizes(ratio, ratio);
drawAdvCardImage(g, state, text, advState, advText, width, height, art); drawAdvCardImage(g, state, text, advState, advText, width, height, art);
} else { } else {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null); boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(altState); 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; CARD_ART_RATIO = 1.37f;
if (art != null && Math.abs((float)art.getWidth() / (float)art.getHeight() - CARD_ART_RATIO) > 0.1f) { 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(); CARD_ART_RATIO = (float)art.getWidth() / (float)art.getHeight();

View File

@@ -670,12 +670,13 @@ public class CardImageRenderer {
CardView cv = card.getBackup(); CardView cv = card.getBackup();
if (cv == null || isFaceDown) if (cv == null || isFaceDown)
cv = card; 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 { } else {
text = !card.isSplitCard() ? text = !card.isSplitCard() ?
card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state.getName(), "") : null) : card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state) : null) :
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null); card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState(), card.getRightSplitState()) : null);
} }
} else { } else {
if (noText) if (noText)
@@ -684,12 +685,13 @@ public class CardImageRenderer {
CardView cv = card.getBackup(); CardView cv = card.getBackup();
if (cv == null || isFaceDown) if (cv == null || isFaceDown)
cv = card; 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 { } else {
text = !card.isSplitCard() ? text = !card.isSplitCard() ?
card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state.getName(), "") : null) : card.getText(state, needTranslation ? state == null ? null : CardTranslation.getTranslationTexts(state) : null) :
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null); card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState(), card.getRightSplitState()) : null);
} }
} }
if (StringUtils.isEmpty(text)) { if (StringUtils.isEmpty(text)) {

View File

@@ -178,7 +178,7 @@ public class CardDetailUtil {
public static String formatCardType(final CardStateView card, final boolean canShow) { public static String formatCardType(final CardStateView card, final boolean canShow) {
boolean isInPlay = card.getCard() != null && ZoneType.Battlefield.equals(card.getCard().getZone()); 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" : ""); return canShow ? translatedtype : (card.getState() == CardStateName.FaceDown && isInPlay ? "Creature" : "");
} }
@@ -319,8 +319,8 @@ public class CardDetailUtil {
needTranslation = false; needTranslation = false;
} }
String text = !card.isSplitCard() ? String text = !card.isSplitCard() ?
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) : card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state) : null) :
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null ); card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState(), card.getRightSplitState()) : null );
// Bracket P/T for Level up // Bracket P/T for Level up
if (text.contains("LEVEL")) { if (text.contains("LEVEL")) {

View File

@@ -82,11 +82,12 @@ public class AdvancedSearch {
protected Set<String> getItemValues(PaperCard input) { protected Set<String> getItemValues(PaperCard input) {
Set<String> names = new HashSet<>(); Set<String> names = new HashSet<>();
names.add(input.getRules().getOracleText()); names.add(input.getRules().getOracleText());
names.add(CardTranslation.getTranslatedOracle(input.getName())); names.add(CardTranslation.getTranslatedOracle(input));
CardSplitType cardSplitType = input.getRules().getSplitType(); CardSplitType cardSplitType = input.getRules().getSplitType();
if (cardSplitType != CardSplitType.None && cardSplitType != CardSplitType.Split) { if (cardSplitType != CardSplitType.None && cardSplitType != CardSplitType.Split) {
if (input.getRules().getOtherPart() != null) { if (input.getRules().getOtherPart() != null) {
names.add(input.getRules().getOtherPart().getOracleText()); 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())); names.add(CardTranslation.getTranslatedOracle(input.getRules().getOtherPart().getName()));
} }
} }

View File

@@ -81,8 +81,8 @@ public enum ColumnDef {
* The type column. * The type column.
*/ */
TYPE("lblType", "ttType", 100, false, SortState.ASC, TYPE("lblType", "ttType", 100, false, SortState.ASC,
from -> CardTranslation.getTranslatedType(from.getKey().getName(), toType(from.getKey())), from -> CardTranslation.getTranslatedType(from.getKey()),
from -> CardTranslation.getTranslatedType(from.getKey().getName(), toType(from.getKey()))), from -> CardTranslation.getTranslatedType(from.getKey())),
/** /**
* The mana cost column. * The mana cost column.
*/ */
@@ -357,10 +357,6 @@ public enum ColumnDef {
return this.longName; 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) { private static IPaperCard toCard(final InventoryItem i) {
return i instanceof IPaperCard ? ((IPaperCard) i) : null; return i instanceof IPaperCard ? ((IPaperCard) i) : null;
} }