mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Fix ChangeText not being fully applied for trigger effects + traits gained by static (#4619)
* Fix ChangeText not being applied for trigger effects * Clean up * Better approach * Better approach 2 * Fix traits gained by static abilities missing original host text changes * Refresh cache if text of static host gets changed * Clean up --------- Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60> Co-authored-by: TRT <>
This commit is contained in:
@@ -531,31 +531,6 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
||||
return true;
|
||||
}
|
||||
|
||||
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 (noChangeKeys.contains(key)) {
|
||||
continue;
|
||||
} else if (descriptiveKeys.contains(key)) {
|
||||
// change descriptions differently
|
||||
newValue = AbilityUtils.applyDescriptionTextChangeEffects(value, this);
|
||||
} else if (this.getHostCard().hasSVar(value)) {
|
||||
// don't change literal SVar names!
|
||||
newValue = null;
|
||||
} else {
|
||||
newValue = AbilityUtils.applyAbilityTextChangeEffects(value, this);
|
||||
}
|
||||
|
||||
if (newValue != null) {
|
||||
this.mapParams.put(key, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardView getCardView() {
|
||||
return CardView.get(hostCard);
|
||||
@@ -694,9 +669,37 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
||||
this.originalMapParams = Maps.newHashMap(this.mapParams);
|
||||
}
|
||||
|
||||
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 (noChangeKeys.contains(key)) {
|
||||
continue;
|
||||
} else if (descriptiveKeys.contains(key)) {
|
||||
// change descriptions differently
|
||||
newValue = AbilityUtils.applyDescriptionTextChangeEffects(value, this);
|
||||
} else if (this.getHostCard().hasSVar(value)) {
|
||||
// don't change literal SVar names!
|
||||
newValue = null;
|
||||
} else {
|
||||
newValue = AbilityUtils.applyAbilityTextChangeEffects(value, this);
|
||||
}
|
||||
|
||||
if (newValue != null) {
|
||||
this.mapParams.put(key, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void copyHelper(CardTraitBase copy, Card host) {
|
||||
copyHelper(copy, host, false);
|
||||
}
|
||||
protected void copyHelper(CardTraitBase copy, Card host, boolean keepTextChanges) {
|
||||
copy.originalMapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.mapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.mapParams = Maps.newHashMap(keepTextChanges ? mapParams : originalMapParams);
|
||||
copy.setSVars(sVars);
|
||||
copy.setCardState(cardState);
|
||||
// dont use setHostCard to not trigger the not copied parts yet
|
||||
|
||||
@@ -25,7 +25,6 @@ import forge.game.card.token.TokenInfo;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.event.GameEventTokenCreated;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
@@ -52,13 +51,10 @@ public class AmassEffect extends TokenEffectBase {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card card = sa.getHostCard();
|
||||
final Game game = card.getGame();
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final PlayerController pc = activator.getController();
|
||||
|
||||
final int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("Num", "1"), sa);
|
||||
final boolean remember = sa.hasParam("RememberAmass");
|
||||
final int amount = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("Num", "1"), sa);
|
||||
final String type = sa.getParam("Type");
|
||||
|
||||
// create army token if needed
|
||||
@@ -84,29 +80,25 @@ public class AmassEffect extends TokenEffectBase {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", CounterType.get(CounterEnumType.P1P1));
|
||||
params.put("Amount", 1);
|
||||
|
||||
CardCollectionView tgtCards = CardLists.getType(activator.getCardsIn(ZoneType.Battlefield), "Army");
|
||||
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), 1, 1, false, params);
|
||||
|
||||
if (tgtCards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
for (final Card tgtCard : tgtCards) {
|
||||
tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, table);
|
||||
if (remember) {
|
||||
card.addRemembered(tgtCard);
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", CounterType.get(CounterEnumType.P1P1));
|
||||
params.put("Amount", 1);
|
||||
Card tgt = activator.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), false, params);
|
||||
|
||||
if (sa.hasParam("RememberAmass")) {
|
||||
source.addRemembered(tgt);
|
||||
}
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
tgt.addCounter(CounterEnumType.P1P1, amount, activator, table);
|
||||
table.replaceCounterEffect(game, sa, true);
|
||||
// change type after counters
|
||||
long ts = game.getNextTimestamp();
|
||||
for (final Card tgtCard : tgtCards) {
|
||||
tgtCard.addChangedCardTypes(CardType.parse(type, true), null, false, EnumSet.noneOf(RemoveType.class), ts, 0, true, false);
|
||||
}
|
||||
tgt.addChangedCardTypes(CardType.parse(type, true), null, false, EnumSet.noneOf(RemoveType.class), game.getNextTimestamp(), 0, true, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4658,8 +4658,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
public final SpellAbility getSpellAbilityForStaticAbility(final String str, final StaticAbility stAb) {
|
||||
SpellAbility result = storedSpellAbility.get(stAb, str);
|
||||
if (result == null) {
|
||||
if (!canUseCachedTrait(result, stAb)) {
|
||||
result = AbilityFactory.getAbility(str, this, stAb);
|
||||
// apply text changes from the statics host
|
||||
result.changeTextIntrinsic(stAb.getChangedTextColors(), stAb.getChangedTextTypes());
|
||||
result.setIntrinsic(false);
|
||||
result.setGrantorStatic(stAb);
|
||||
storedSpellAbility.put(stAb, str, result);
|
||||
@@ -4669,8 +4671,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
public final Trigger getTriggerForStaticAbility(final String str, final StaticAbility stAb) {
|
||||
Trigger result = storedTrigger.get(stAb, str);
|
||||
if (result == null) {
|
||||
if (!canUseCachedTrait(result, stAb)) {
|
||||
result = TriggerHandler.parseTrigger(str, this, false, stAb);
|
||||
// apply text changes from the statics host
|
||||
result.changeTextIntrinsic(stAb.getChangedTextColors(), stAb.getChangedTextTypes());
|
||||
storedTrigger.put(stAb, str, result);
|
||||
}
|
||||
return result;
|
||||
@@ -4699,8 +4703,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
public final ReplacementEffect getReplacementEffectForStaticAbility(final String str, final StaticAbility stAb) {
|
||||
ReplacementEffect result = storedReplacementEffect.get(stAb, str);
|
||||
if (result == null) {
|
||||
if (!canUseCachedTrait(result, stAb)) {
|
||||
result = ReplacementHandler.parseReplacement(str, this, false, stAb);
|
||||
// apply text changes from the statics host
|
||||
result.changeTextIntrinsic(stAb.getChangedTextColors(), stAb.getChangedTextTypes());
|
||||
storedReplacementEffect.put(stAb, str, result);
|
||||
}
|
||||
return result;
|
||||
@@ -4708,8 +4714,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
public final StaticAbility getStaticAbilityForStaticAbility(final String str, final StaticAbility stAb) {
|
||||
StaticAbility result = storedStaticAbility.get(stAb, str);
|
||||
if (result == null) {
|
||||
if (!canUseCachedTrait(result, stAb)) {
|
||||
result = StaticAbility.create(str, this, stAb.getCardState(), false);
|
||||
// apply text changes from the statics host
|
||||
result.changeTextIntrinsic(stAb.getChangedTextColors(), stAb.getChangedTextTypes());
|
||||
storedStaticAbility.put(stAb, str, result);
|
||||
}
|
||||
return result;
|
||||
@@ -4781,6 +4789,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean canUseCachedTrait(CardTraitBase cached, CardTraitBase stAb) {
|
||||
if (cached == null) {
|
||||
return false;
|
||||
}
|
||||
return cached.getChangedTextColors().equals(stAb.getChangedTextColors()) && cached.getChangedTextTypes().equals(stAb.getChangedTextTypes());
|
||||
}
|
||||
|
||||
public final void addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
|
||||
Collection<Trigger> trigger, Collection<ReplacementEffect> replacements, Collection<StaticAbility> statics,
|
||||
boolean removeAll, boolean removeNonMana, long timestamp, long staticId) {
|
||||
|
||||
@@ -574,6 +574,9 @@ public class CardFactory {
|
||||
}
|
||||
|
||||
public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki) {
|
||||
copySpellAbility(from, to, host, p, lki, false);
|
||||
}
|
||||
public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki, final boolean keepTextChanges) {
|
||||
if (from.usesTargeting()) {
|
||||
to.setTargetRestrictions(from.getTargetRestrictions());
|
||||
}
|
||||
@@ -581,16 +584,16 @@ public class CardFactory {
|
||||
to.setStackDescription(from.getOriginalStackDescription());
|
||||
|
||||
if (from.getSubAbility() != null) {
|
||||
to.setSubAbility((AbilitySub) from.getSubAbility().copy(host, p, lki));
|
||||
to.setSubAbility((AbilitySub) from.getSubAbility().copy(host, p, lki, keepTextChanges));
|
||||
}
|
||||
for (Map.Entry<String, SpellAbility> e : from.getAdditionalAbilities().entrySet()) {
|
||||
to.setAdditionalAbility(e.getKey(), e.getValue().copy(host, p, lki));
|
||||
to.setAdditionalAbility(e.getKey(), e.getValue().copy(host, p, lki, keepTextChanges));
|
||||
}
|
||||
for (Map.Entry<String, List<AbilitySub>> e : from.getAdditionalAbilityLists().entrySet()) {
|
||||
to.setAdditionalAbilityList(e.getKey(), Lists.transform(e.getValue(), new Function<AbilitySub, AbilitySub>() {
|
||||
@Override
|
||||
public AbilitySub apply(AbilitySub input) {
|
||||
return (AbilitySub) input.copy(host, p, lki);
|
||||
return (AbilitySub) input.copy(host, p, lki, keepTextChanges);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1473,7 +1473,7 @@ public class CardFactoryUtil {
|
||||
sbTrig.append("Living Weapon (").append(inst.getReminderText()).append(")");
|
||||
|
||||
final StringBuilder sbGerm = new StringBuilder();
|
||||
sbGerm.append("DB$ Token | TokenAmount$ 1 | TokenScript$ b_0_0_phyrexian_germ |TokenOwner$ You | RememberTokens$ True");
|
||||
sbGerm.append("DB$ Token | TokenAmount$ 1 | TokenScript$ b_0_0_phyrexian_germ | TokenOwner$ You | RememberTokens$ True");
|
||||
|
||||
final SpellAbility saGerm = AbilityFactory.getAbility(sbGerm.toString(), card);
|
||||
|
||||
|
||||
@@ -1158,6 +1158,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return copy(host, this.getActivatingPlayer(), lki);
|
||||
}
|
||||
public SpellAbility copy(Card host, Player activ, final boolean lki) {
|
||||
return copy(host, activ, lki, false);
|
||||
}
|
||||
public SpellAbility copy(Card host, Player activ, final boolean lki, final boolean keepTextChanges) {
|
||||
SpellAbility clone = null;
|
||||
try {
|
||||
clone = (SpellAbility) clone();
|
||||
@@ -1166,7 +1169,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
|
||||
// don't use setHostCard to not trigger the not copied parts yet
|
||||
|
||||
copyHelper(clone, host);
|
||||
copyHelper(clone, host, lki || keepTextChanges);
|
||||
|
||||
// always set this to false, it is only set in CopyEffect
|
||||
clone.mayChooseNewTargets = false;
|
||||
@@ -1205,7 +1208,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
clone.additionalAbilities = Maps.newHashMap();
|
||||
clone.additionalAbilityLists = Maps.newHashMap();
|
||||
// run special copy Ability to make a deep copy
|
||||
CardFactory.copySpellAbility(this, clone, host, activ, lki);
|
||||
CardFactory.copySpellAbility(this, clone, host, activ, lki, keepTextChanges);
|
||||
} catch (final CloneNotSupportedException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
@@ -830,7 +830,7 @@ public final class StaticAbilityContinuous {
|
||||
if (!stAb.matchesValidParam("GainsValidAbilities", sa)) {
|
||||
continue;
|
||||
}
|
||||
SpellAbility newSA = sa.copy(affectedCard, false);
|
||||
SpellAbility newSA = sa.copy(affectedCard, sa.getActivatingPlayer(), false, true);
|
||||
if (params.containsKey("GainsAbilitiesLimitPerTurn")) {
|
||||
newSA.setRestrictions(sa.getRestrictions());
|
||||
newSA.getRestrictions().setLimitToCheck(params.get("GainsAbilitiesLimitPerTurn"));
|
||||
|
||||
@@ -583,7 +583,6 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.CardTraitBase#changeText()
|
||||
*/
|
||||
@@ -606,9 +605,6 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
*/
|
||||
@Override
|
||||
public void changeTextIntrinsic(Map<String, String> colorMap, Map<String, String> typeMap) {
|
||||
if (!isIntrinsic()) {
|
||||
return;
|
||||
}
|
||||
super.changeTextIntrinsic(colorMap, typeMap);
|
||||
|
||||
SpellAbility sa = ensureAbility();
|
||||
|
||||
@@ -521,7 +521,7 @@ public class TriggerHandler {
|
||||
controller = regtrig.getSpawningAbility().getActivatingPlayer();
|
||||
}
|
||||
// need to copy the SA because of TriggeringObjects
|
||||
sa = sa.copy(host, controller, false);
|
||||
sa = sa.copy(host, controller, false, true);
|
||||
}
|
||||
|
||||
sa.setTrigger(regtrig);
|
||||
|
||||
@@ -302,9 +302,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
if (si == null && sp.isActivatedAbility() && !sp.isCopied()) {
|
||||
// if not already copied use a fresh instance
|
||||
SpellAbility original = sp;
|
||||
sp = sp.copy();
|
||||
// need to reapply text changes
|
||||
sp.changeText();
|
||||
sp = sp.copy(sp.getHostCard(), activator, false, true);
|
||||
sp.setOriginalAbility(original);
|
||||
original.clearTargets();
|
||||
original.setXManaCostPaid(null);
|
||||
|
||||
Reference in New Issue
Block a user