TokenScript: make CardTextChange working again

SpellAbility: changeTextIntrinsic to sub abilities
This commit is contained in:
Hanmac
2018-09-29 11:22:17 +02:00
parent 5371cbe9c8
commit a298dcf3bc
11 changed files with 308 additions and 20 deletions

View File

@@ -188,6 +188,21 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
return supertypes.remove(st);
}
public boolean remove(final String str) {
boolean changed = false;
if (CardType.isASupertype(str) && supertypes.remove(stringToSupertype.get(str))) {
changed = true;
} else if (CardType.isACardType(str) && coreTypes.remove(stringToCoreType.get(str))) {
changed = true;
} else if (subtypes.remove(str)) {
changed = true;
}
if (changed) {
calculatedType = null;
}
return changed;
}
public boolean setCreatureTypes(Collection<String> ctypes) {
// if it isn't a creature then this has no effect
if (!isCreature() && !isTribal()) {

View File

@@ -46,6 +46,11 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
protected Map<String, String> sVars = Maps.newHashMap();
protected Map<String, String> intrinsicChangedTextColors = Maps.newHashMap();
protected Map<String, String> intrinsicChangedTextTypes = Maps.newHashMap();
protected Map<String, String> changedTextColors = Maps.newHashMap();
protected Map<String, String> changedTextTypes = Maps.newHashMap();
/** Keys of descriptive (text) parameters. */
private static final ImmutableList<String> descriptiveKeys = ImmutableList.<String>builder()
.add("Description", "SpellDescription", "StackDescription", "TriggerDescription").build();
@@ -53,6 +58,11 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
private static final ImmutableList<String> mutableKeys = ImmutableList.<String>builder()
.add("AddAbility").build();
/**
* Keys that should not changed
*/
private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder()
.add("TokenScript", "LegacyImage", "TokenImage").build();
/**
* Sets the temporary.
*
@@ -449,9 +459,15 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
}
public void changeText() {
// copy changed text words into card trait there
this.changedTextColors = getHostCard().getChangedTextColorWords();
this.changedTextTypes = getHostCard().getChangedTextTypeWords();
for (final String key : this.mapParams.keySet()) {
final String value = this.originalMapParams.get(key), newValue;
if (descriptiveKeys.contains(key)) {
if (noChangeKeys.contains(key)) {
continue;
} else if (descriptiveKeys.contains(key)) {
// change descriptions differently
newValue = AbilityUtils.applyDescriptionTextChangeEffects(value, this);
} else if (mutableKeys.contains(key)) {
@@ -515,4 +531,53 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
public Set<String> getSVars() {
return sVars.keySet();
}
public Map<String, String> getChangedTextColors() {
return _combineChangedMap(intrinsicChangedTextColors, changedTextColors);
}
public Map<String, String> getChangedTextTypes() {
return _combineChangedMap(intrinsicChangedTextTypes, changedTextTypes);
}
private Map<String, String> _combineChangedMap(Map<String, String> input, Map<String, String> output) {
// no need to do something, just return hash
if (input.isEmpty()) {
return output;
}
if (output.isEmpty()) {
return input;
}
// magic combine them
Map<String, String> result = Maps.newHashMap(output);
for (Map.Entry<String, String> e : input.entrySet()) {
String value = e.getValue();
result.put(e.getKey(), output.containsKey(value) ? output.get(value) : value);
}
return result;
}
public void changeTextIntrinsic(Map<String,String> colorMap, Map<String,String> typeMap) {
intrinsicChangedTextColors = colorMap;
intrinsicChangedTextTypes = typeMap;
for (final String key : this.mapParams.keySet()) {
final String value = this.originalMapParams.get(key), newValue;
if (noChangeKeys.contains(key)) {
continue;
} else if (descriptiveKeys.contains(key)) {
// change descriptions differently
newValue = AbilityUtils.applyTextChangeEffects(value, true, colorMap, typeMap);
}else if (this.getHostCard().hasSVar(value)) {
// don't change literal SVar names!
continue;
} else {
newValue = AbilityUtils.applyTextChangeEffects(value, false, colorMap, typeMap);
}
if (newValue != null) {
this.mapParams.put(key, newValue);
}
}
// this does overwrite the original MapParams
this.originalMapParams = Maps.newHashMap(this.mapParams);
}
}

View File

@@ -1724,12 +1724,18 @@ public class AbilityUtils {
}
private static final String applyTextChangeEffects(final String def, final Card card, final boolean isDescriptive) {
return applyTextChangeEffects(def, isDescriptive,
card.getChangedTextColorWords(), card.getChangedTextTypeWords());
}
public static final String applyTextChangeEffects(final String def, final boolean isDescriptive,
Map<String,String> colorMap, Map<String,String> typeMap) {
if (StringUtils.isEmpty(def)) {
return def;
}
String replaced = def;
for (final Entry<String, String> e : card.getChangedTextColorWords().entrySet()) {
for (final Entry<String, String> e : colorMap.entrySet()) {
final String key = e.getKey();
String value;
if (key.equals("Any")) {
@@ -1750,7 +1756,7 @@ public class AbilityUtils {
replaced = replaced.replaceAll("(?<!>)" + key, value);
}
}
for (final Entry<String, String> e : card.getChangedTextTypeWords().entrySet()) {
for (final Entry<String, String> e : typeMap.entrySet()) {
final String key = e.getKey();
final String pkey = CardType.getPluralType(key);
final String pvalue = getReplacedText(pkey, CardType.getPluralType(e.getValue()), isDescriptive);

View File

@@ -3640,6 +3640,12 @@ public class Card extends GameEntity implements Comparable<Card> {
}
}
public final void removeIntrinsicKeyword(final KeywordInterface s) {
if (currentState.removeIntrinsicKeyword(s)) {
currentState.getView().updateKeywords(this, currentState);
}
}
public Collection<KeywordInterface> getExtrinsicKeyword() {
return extrinsicKeyword.getValues();
}

View File

@@ -7,8 +7,6 @@ import java.util.SortedMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import forge.card.CardType;
public final class CardChangedWords {
private final SortedMap<Long, CardChangedWord> map = Maps.newTreeMap();
@@ -68,14 +66,9 @@ public final class CardChangedWords {
// the actual change (b->c)
resultCache.put(ccw.getOriginalWord(), ccw.getNewWord());
// possible plural form
final String singular = CardType.getPluralType(ccw.getOriginalWord());
if (!singular.equals(ccw.getOriginalWord())) {
resultCache.put(singular, ccw.getNewWord());
}
}
// TODO should that be removed?
for (final String key : ImmutableList.copyOf(resultCache.keySet())) {
if (!key.equals("Any")) {
resultCache.put(key.toLowerCase(), resultCache.get(key).toLowerCase());

View File

@@ -231,6 +231,9 @@ public class CardState extends GameObject {
public final boolean removeIntrinsicKeyword(final String s) {
return intrinsicKeywords.remove(s);
}
public final boolean removeIntrinsicKeyword(final KeywordInterface s) {
return intrinsicKeywords.remove(s);
}
public final FCollectionView<SpellAbility> getSpellAbilities() {
FCollection<SpellAbility> newCol = new FCollection<SpellAbility>(manaAbilities);
@@ -539,7 +542,7 @@ public class CardState extends GameObject {
intrinsicKeywords.insert(inst);
}
}
public void updateChangedText() {
final List<CardTraitBase> allAbs = ImmutableList.<CardTraitBase>builder()
.addAll(manaAbilities)
@@ -554,4 +557,19 @@ public class CardState extends GameObject {
}
}
}
public void changeTextIntrinsic(Map<String,String> colorMap, Map<String,String> typeMap) {
final List<CardTraitBase> allAbs = ImmutableList.<CardTraitBase>builder()
.addAll(manaAbilities)
.addAll(nonManaAbilities)
.addAll(triggers)
.addAll(replacementEffects)
.addAll(staticAbilities)
.build();
for (final CardTraitBase ctb : allAbs) {
if (ctb.isIntrinsic()) {
ctb.changeTextIntrinsic(colorMap, typeMap);
}
}
}
}

View File

@@ -281,6 +281,8 @@ public final class CardUtil {
newCopy.setChangedCardKeywords(in.getChangedCardKeywords());
newCopy.setChangedCardTypes(in.getChangedCardTypesMap());
newCopy.copyChangedTextFrom(in);
newCopy.setMeldedWith(in.getMeldedWith());
newCopy.setTimestamp(in.getTimestamp());

View File

@@ -12,6 +12,7 @@ import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardFactory;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardUtil;
import forge.game.keyword.KeywordInterface;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -20,6 +21,8 @@ import forge.item.PaperToken;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
public class TokenInfo {
final String name;
final String imageName;
@@ -229,10 +232,121 @@ public class TokenInfo {
String edition = host.getSetCode();
PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition);
// TODO add Card Text Change from SpellAbility
if (token != null) {
return Card.fromPaperCard(token, null, game);
final Card result = Card.fromPaperCard(token, null, game);
// update Token with CardTextChanges
Map<String, String> colorMap = sa.getChangedTextColors();
Map<String, String> typeMap = sa.getChangedTextTypes();
if (!colorMap.isEmpty()) {
if (!result.isColorless()) {
// change Token Colors
byte color = CardUtil.getColors(result).getColor();
for (final Map.Entry<String, String> e : colorMap.entrySet()) {
byte v = MagicColor.fromName(e.getValue());
// Any used by Swirl the Mists
if ("Any".equals(e.getKey())) {
for (final byte c : MagicColor.WUBRG) {
// try to replace color flips
if ((color & c) != 0) {
color &= ~c;
color |= v;
}
}
} else {
byte c = MagicColor.fromName(e.getKey());
// try to replace color flips
if ((color & c) != 0) {
color &= ~c;
color |= v;
}
}
}
result.setColor(color);
}
}
if (!typeMap.isEmpty()) {
String oldName = result.getName();
CardType type = new CardType(result.getType());
String joinedName = StringUtils.join(type.getSubtypes(), " ");
final boolean nameGenerated = oldName.equals(joinedName);
boolean typeChanged = false;
if (!Iterables.isEmpty(type.getSubtypes())) {
for (final Map.Entry<String, String> e : typeMap.entrySet()) {
if (type.hasSubtype(e.getKey())) {
type.remove(e.getKey());
type.add(e.getValue());
typeChanged = true;
}
}
}
if (typeChanged) {
result.setType(type);
// update generated Name
if (nameGenerated) {
result.setName(StringUtils.join(type.getSubtypes(), " "));
}
}
}
// replace Intrinsic Keyword
List<KeywordInterface> toRemove = Lists.newArrayList();
List<String> toAdd = Lists.newArrayList();
for (final KeywordInterface k : result.getCurrentState().getIntrinsicKeywords()) {
final String o = k.getOriginal();
// only Modifiable should go there
if (!CardUtil.isKeywordModifiable(o)) {
continue;
}
String r = new String(o);
// replace types
for (final Map.Entry<String, String> e : typeMap.entrySet()) {
final String key = e.getKey();
final String pkey = CardType.getPluralType(key);
final String value = e.getValue();
final String pvalue = CardType.getPluralType(e.getValue());
r = r.replaceAll(pkey, pvalue);
r = r.replaceAll(key, value);
}
// replace color words
for (final Map.Entry<String, String> e : colorMap.entrySet()) {
final String vName = e.getValue();
final String vCaps = StringUtils.capitalize(vName);
final String vLow = vName.toLowerCase();
if ("Any".equals(e.getKey())) {
for (final byte c : MagicColor.WUBRG) {
final String cName = MagicColor.toLongString(c);
final String cNameCaps = StringUtils.capitalize(cName);
final String cNameLow = cName.toLowerCase();
r = r.replaceAll(cNameCaps, vCaps);
r = r.replaceAll(cNameLow, vLow);
}
} else {
final String cName = e.getKey();
final String cNameCaps = StringUtils.capitalize(cName);
final String cNameLow = cName.toLowerCase();
r = r.replaceAll(cNameCaps, vCaps);
r = r.replaceAll(cNameLow, vLow);
}
}
if (!r.equals(o)) {
toRemove.add(k);
toAdd.add(r);
}
}
for (final KeywordInterface k : toRemove) {
result.getCurrentState().removeIntrinsicKeyword(k);
}
result.addIntrinsicKeywords(toAdd);
result.getCurrentState().changeTextIntrinsic(colorMap, typeMap);
return result;
}
return null;

View File

@@ -96,6 +96,10 @@ public class KeywordCollection implements Iterable<String>, Serializable {
return result;
}
public boolean remove(KeywordInterface keyword) {
return map.remove(keyword.getKeyword(), keyword);
}
public boolean removeAll(Iterable<String> keywords) {
boolean result = false;
for (String k : keywords) {

View File

@@ -1709,6 +1709,31 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
}
/* (non-Javadoc)
* @see forge.game.CardTraitBase#changeTextIntrinsic(java.util.Map, java.util.Map)
*/
@Override
public void changeTextIntrinsic(Map<String, String> colorMap, Map<String, String> typeMap) {
super.changeTextIntrinsic(colorMap, typeMap);
if (subAbility != null) {
// if the parent of the subability is not this,
// then there might be a loop
if (subAbility.getParent() == this) {
subAbility.changeTextIntrinsic(colorMap, typeMap);
}
}
for (AbilitySub sa : additionalAbilities.values()) {
sa.changeTextIntrinsic(colorMap, typeMap);
}
for (List<AbilitySub> list : additionalAbilityLists.values()) {
for (AbilitySub sa : list) {
sa.changeTextIntrinsic(colorMap, typeMap);
}
}
}
@Override
public void setIntrinsic(boolean i) {
super.setIntrinsic(i);

View File

@@ -169,11 +169,7 @@ public abstract class Trigger extends TriggerReplacementBase {
if (!desc.contains("ABILITY")) {
return desc;
}
SpellAbility sa = getOverridingAbility();
if (sa == null && this.mapParams.containsKey("Execute")) {
sa = AbilityFactory.getAbility(state, this.mapParams.get("Execute"));
setOverridingAbility(sa);
}
SpellAbility sa = ensureAbility();
return replaceAbilityText(desc, sa);
@@ -583,4 +579,48 @@ public abstract class Trigger extends TriggerReplacementBase {
throw new RuntimeException("Trigger : clone() error, " + ex);
}
}
/* (non-Javadoc)
* @see forge.game.CardTraitBase#changeText()
*/
@Override
public void changeText() {
if (!isIntrinsic()) {
return;
}
super.changeText();
ensureAbility();
if (getOverridingAbility() != null) {
getOverridingAbility().changeText();
}
}
/* (non-Javadoc)
* @see forge.game.CardTraitBase#changeTextIntrinsic(java.util.Map, java.util.Map)
*/
@Override
public void changeTextIntrinsic(Map<String, String> colorMap, Map<String, String> typeMap) {
if (!isIntrinsic()) {
return;
}
super.changeTextIntrinsic(colorMap, typeMap);
ensureAbility();
if (getOverridingAbility() != null) {
getOverridingAbility().changeTextIntrinsic(colorMap, typeMap);
}
}
private SpellAbility ensureAbility() {
SpellAbility sa = getOverridingAbility();
if (sa == null && hasParam("Execute")) {
sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute"));
setOverridingAbility(sa);
}
return sa;
}
}