Perpetual: use Record for different types (#8337)

* Perpetual: use Record for different types
This commit is contained in:
Hans Mackowiak
2025-08-09 13:04:14 +02:00
committed by GitHub
parent c18995915a
commit f0ebac019d
13 changed files with 187 additions and 143 deletions

View File

@@ -18,9 +18,7 @@
package forge.game.ability.effects;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Lists;
@@ -31,12 +29,13 @@ import forge.card.ColorSet;
import forge.card.RemoveType;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.cost.Cost;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardTraitChanges;
import forge.game.card.perpetual.*;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.keyword.Keyword;
import forge.game.replacement.ReplacementEffect;
@@ -85,49 +84,23 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
final boolean wasCreature = c.isCreature();
// Alchemy "incorporate" cost
ColorSet incColors = null;
if (sa.hasParam("Incorporate")) {
final String incorporate = sa.getParam("Incorporate");
Map <String, Object> params = new HashMap<>();
params.put("Incorporate", incorporate);
params.put("Timestamp", timestamp);
params.put("Category", "Incorporate");
c.addPerpetual(params);
final ManaCost incMCost = new ManaCost(new ManaCostParser(incorporate));
incColors = ColorSet.fromMask(incMCost.getColorProfile());
final ManaCost newCost = ManaCost.combine(c.getManaCost(), incMCost);
c.addChangedManaCost(newCost, timestamp, 0);
c.updateManaCostForView();
if (c.getFirstSpellAbility() != null) {
c.getFirstSpellAbility().getPayCosts().add(new Cost(incorporate, false));
}
final ManaCost incMCost = new ManaCost(new ManaCostParser(sa.getParam("Incorporate")));
PerpetualIncorporate p = new PerpetualIncorporate(timestamp, incMCost);
c.addPerpetual(p);
p.applyEffect(c);
}
if (!addType.isEmpty() || !removeType.isEmpty() || addAllCreatureTypes || !remove.isEmpty()) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("AddTypes", addType);
params.put("RemoveTypes", removeType);
params.put("RemoveXTypes", remove);
params.put("Timestamp", timestamp);
params.put("Category", "Types");
c.addPerpetual(params);
c.addPerpetual(new PerpetualTypes(timestamp, addType, removeType, remove));
}
c.addChangedCardTypes(addType, removeType, addAllCreatureTypes, remove, timestamp, 0, true, false);
}
if (!keywords.isEmpty() || !removeKeywords.isEmpty() || removeAll) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("AddKeywords", keywords);
params.put("RemoveKeywords", removeKeywords);
params.put("RemoveAll", removeAll);
params.put("Timestamp", timestamp);
params.put("Category", "Keywords");
c.addPerpetual(params);
c.addPerpetual(new PerpetualKeywords(timestamp, keywords, removeKeywords, removeAll));
}
c.addChangedCardKeywords(keywords, removeKeywords, removeAll, timestamp, null);
}
@@ -135,12 +108,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
// do this after changing types in case it wasn't a creature before
if (power != null || toughness != null) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("Power", power);
params.put("Toughness", toughness);
params.put("Timestamp", timestamp);
params.put("Category", "NewPT");
c.addPerpetual(params);
c.addPerpetual(new PerpetualNewPT(timestamp, power, toughness));
}
c.addNewPT(power, toughness, timestamp, 0);
} else if (!wasCreature && c.isCreature()) {
@@ -157,10 +125,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
if (colors != null) {
final boolean overwrite = sa.hasParam("OverwriteColors");
handleColors(c, colors, timestamp, overwrite, perpetual);
}
if (incColors != null) {
handleColors(c, incColors, timestamp, false, perpetual);
if (perpetual) {
c.addPerpetual(new PerpetualColors(timestamp, colors, overwrite));
}
c.addColor(colors, !overwrite, timestamp, 0, false);
}
if (sa.hasParam("LeaveBattlefield")) {
@@ -240,13 +208,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
if (removeAll || removeNonManaAbilities
|| !addedAbilities.isEmpty() || !removedAbilities.isEmpty() || !addedTriggers.isEmpty()
|| !addedReplacements.isEmpty() || !addedStaticAbilities.isEmpty()) {
c.addChangedCardTraits(addedAbilities, removedAbilities, addedTriggers, addedReplacements,
CardTraitChanges changes = c.addChangedCardTraits(addedAbilities, removedAbilities, addedTriggers, addedReplacements,
addedStaticAbilities, removeAll, removeNonManaAbilities, timestamp, 0);
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("Timestamp", timestamp);
params.put("Category", "Abilities");
c.addPerpetual(params);
c.addPerpetual(new PerpetualAbilities(timestamp, changes));
}
}
@@ -290,18 +255,4 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
c.removeHiddenExtrinsicKeywords(timestamp, 0);
}
static void handleColors(final Card c, final ColorSet colors, final long timestamp, final boolean overwrite,
final boolean perpetual) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("Colors", colors);
params.put("Overwrite", overwrite);
params.put("Timestamp", timestamp);
params.put("Category", "Colors");
c.addPerpetual(params);
}
c.addColor(colors, !overwrite, timestamp, 0, false);
}
}

View File

@@ -1,13 +1,12 @@
package forge.game.ability.effects;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import forge.GameCommand;
import forge.game.Game;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.perpetual.PerpetualNewPT;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.spellability.SpellAbility;
@@ -61,13 +60,8 @@ public class PowerExchangeEffect extends SpellAbilityEffect {
final long timestamp = game.getNextTimestamp();
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("Power", power2);
params.put("Timestamp", timestamp);
params.put("Category", "NewPT");
c1.addPerpetual(params);
params.put("Power", power1);
c2.addPerpetual(params);
c1.addPerpetual(new PerpetualNewPT(timestamp, power2, null));
c2.addPerpetual(new PerpetualNewPT(timestamp, power1, null));
}
c1.addNewPT(power2, null, timestamp, 0);
c2.addNewPT(power1, null, timestamp, 0);

View File

@@ -1,9 +1,7 @@
package forge.game.ability.effects;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -15,6 +13,8 @@ import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.perpetual.PerpetualKeywords;
import forge.game.card.perpetual.PerpetualPTBoost;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.player.PlayerCollection;
import forge.game.spellability.SpellAbility;
@@ -49,12 +49,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
if (a != 0 || d != 0) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("Power", a);
params.put("Toughness", d);
params.put("Timestamp", timestamp);
params.put("Category", "PTBoost");
tgtC.addPerpetual(params);
tgtC.addPerpetual(new PerpetualPTBoost(timestamp, a, d));
}
tgtC.addPTBoost(a, d, timestamp, 0);
redrawPT = true;
@@ -62,11 +57,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
if (!kws.isEmpty()) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("AddKeywords", kws);
params.put("Timestamp", timestamp);
params.put("Category", "Keywords");
tgtC.addPerpetual(params);
tgtC.addPerpetual(new PerpetualKeywords(timestamp, kws, null, false));
}
tgtC.addChangedCardKeywords(kws, null, false, timestamp, null);
}

View File

@@ -1,9 +1,7 @@
package forge.game.ability.effects;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import forge.util.*;
@@ -22,6 +20,8 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardUtil;
import forge.game.card.perpetual.PerpetualKeywords;
import forge.game.card.perpetual.PerpetualPTBoost;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.player.Player;
import forge.game.player.PlayerCollection;
@@ -59,12 +59,7 @@ public class PumpEffect extends SpellAbilityEffect {
if (a != 0 || d != 0) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("Power", a);
params.put("Toughness", d);
params.put("Timestamp", timestamp);
params.put("Category", "PTBoost");
gameCard.addPerpetual(params);
gameCard.addPerpetual(new PerpetualPTBoost(timestamp, a, d));
}
gameCard.addPTBoost(a, d, timestamp, 0);
redrawPT = true;
@@ -72,11 +67,7 @@ public class PumpEffect extends SpellAbilityEffect {
if (!kws.isEmpty()) {
if (perpetual) {
Map <String, Object> params = new HashMap<>();
params.put("AddKeywords", kws);
params.put("Timestamp", timestamp);
params.put("Category", "Keywords");
gameCard.addPerpetual(params);
gameCard.addPerpetual(new PerpetualKeywords(timestamp, kws, Lists.newArrayList(), false));
}
gameCard.addChangedCardKeywords(kws, Lists.newArrayList(), false, timestamp, null);
}

View File

@@ -33,6 +33,7 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.perpetual.PerpetualInterface;
import forge.game.combat.Combat;
import forge.game.combat.CombatLki;
import forge.game.cost.Cost;
@@ -2078,6 +2079,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
return result;
}
public ManaCost getChangedManaCost(long timestamp, long staticId) {
return changedCardManaCost.get(timestamp, staticId);
}
public void addChangedManaCost(ManaCost cost, long timestamp, long staticId) {
changedCardManaCost.put(timestamp, staticId, cost);
}
@@ -4812,22 +4816,22 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
return intensity > 0;
}
private List<Map<String, Object>> perpetual = new ArrayList<>();
private List<PerpetualInterface> perpetual = new ArrayList<>();
public final boolean hasPerpetual() {
return !perpetual.isEmpty();
}
public final List<Map<String, Object>> getPerpetual() {
public final List<PerpetualInterface> getPerpetual() {
return perpetual;
}
public final void addPerpetual(Map<String, Object> p) {
public final void addPerpetual(PerpetualInterface p) {
perpetual.add(p);
}
public final void removePerpetual(final long timestamp) {
Map<String, Object> toRemove = Maps.newHashMap();
for (Map<String, Object> p : perpetual) {
if (p.get("Timestamp").equals(timestamp)) {
PerpetualInterface toRemove = null;
for (PerpetualInterface p : perpetual) {
if (p.getTimestamp() == (timestamp)) {
toRemove = p;
break;
}
@@ -4837,39 +4841,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
public final void setPerpetual(final Card oldCard) {
perpetual = oldCard.getPerpetual();
for (Map<String, Object> p : perpetual) {
final String category = (String) p.get("Category");
if (category.equals("Abilities")) {
long timestamp = (long) p.get("Timestamp");
CardTraitChanges ctc = oldCard.getChangedCardTraits().get(timestamp, (long) 0).copy(this, false);
addChangedCardTraits(ctc, timestamp, (long) 0);
} else if (category.equals("Incorporate")) {
long ts = (long) p.get("Timestamp");
final ManaCost cCMC = oldCard.changedCardManaCost.get(ts, (long) 0);
addChangedManaCost(cCMC, ts, (long) 0);
updateManaCostForView();
if (getFirstSpellAbility() != null) {
getFirstSpellAbility().getPayCosts().add(new Cost((String) p.get("Incorporate"), false));
}
} else if (category.equals("NewPT")) {
addNewPT((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long)
p.get("Timestamp"), (long) 0);
} else if (category.equals("PTBoost")) {
addPTBoost((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long)
p.get("Timestamp"), (long) 0);
} else if (category.equals("Keywords")) {
boolean removeAll = p.containsKey("RemoveAll") && (boolean) p.get("RemoveAll") == true;
addChangedCardKeywords((List<String>) p.get("AddKeywords"), (List<String>) p.get("RemoveKeywords"),
removeAll, (long) p.get("Timestamp"), null);
} else if (category.equals("Types")) {
addChangedCardTypes((CardType) p.get("AddTypes"), (CardType) p.get("RemoveTypes"),
false, (Set<RemoveType>) p.get("RemoveXTypes"),
(long) p.get("Timestamp"), (long) 0, true, false);
} else if (category.equals("Colors")) {
addColor((ColorSet) p.get("Colors"), !(boolean) p.get("Overwrite"), (long) p.get("Timestamp"),
(long) 0, false);
}
for (PerpetualInterface p : perpetual) {
p.applyEffect(this);
}
}
@@ -5122,20 +5095,22 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
updateAbilityTextForView();
}
public final void addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
public final CardTraitChanges addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
Collection<Trigger> trigger, Collection<ReplacementEffect> replacements, Collection<StaticAbility> statics,
boolean removeAll, boolean removeNonMana, long timestamp, long staticId) {
addChangedCardTraits(spells, removedAbilities, trigger, replacements, statics, removeAll, removeNonMana, timestamp, staticId, true);
return addChangedCardTraits(spells, removedAbilities, trigger, replacements, statics, removeAll, removeNonMana, timestamp, staticId, true);
}
public final void addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
public final CardTraitChanges addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
Collection<Trigger> trigger, Collection<ReplacementEffect> replacements, Collection<StaticAbility> statics,
boolean removeAll, boolean removeNonMana, long timestamp, long staticId, boolean updateView) {
changedCardTraits.put(timestamp, staticId, new CardTraitChanges(
CardTraitChanges result = new CardTraitChanges(
spells, removedAbilities, trigger, replacements, statics, removeAll, removeNonMana
));
);
changedCardTraits.put(timestamp, staticId, result);
if (updateView) {
updateAbilityTextForView();
}
return result;
}
public final void addChangedCardTraits(CardTraitChanges ctc, long timestamp, long staticId) {

View File

@@ -0,0 +1,18 @@
package forge.game.card.perpetual;
import forge.game.card.Card;
import forge.game.card.CardTraitChanges;
public record PerpetualAbilities(long timestamp, CardTraitChanges changes) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addChangedCardTraits(changes.copy(c, false), timestamp, (long) 0);
}
}

View File

@@ -0,0 +1,18 @@
package forge.game.card.perpetual;
import forge.card.ColorSet;
import forge.game.card.Card;
public record PerpetualColors(long timestamp, ColorSet colors, boolean overwrite) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addColor(colors, !overwrite, timestamp, (long) 0, false);
}
}

View File

@@ -0,0 +1,28 @@
package forge.game.card.perpetual;
import forge.card.ColorSet;
import forge.card.mana.ManaCost;
import forge.game.card.Card;
import forge.game.cost.Cost;
public record PerpetualIncorporate(long timestamp, ManaCost incorporate) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
ColorSet colors = ColorSet.fromMask(incorporate.getColorProfile());
final ManaCost newCost = ManaCost.combine(c.getManaCost(), incorporate);
c.addChangedManaCost(newCost, timestamp, (long) 0);
c.addColor(colors, true, timestamp, (long) 0, false);
c.updateManaCostForView();
if (c.getFirstSpellAbility() != null) {
c.getFirstSpellAbility().getPayCosts().add(new Cost(incorporate, false));
}
}
}

View File

@@ -0,0 +1,8 @@
package forge.game.card.perpetual;
import forge.game.card.Card;
public interface PerpetualInterface {
long getTimestamp();
void applyEffect(Card c);
}

View File

@@ -0,0 +1,17 @@
package forge.game.card.perpetual;
import java.util.List;
import forge.game.card.Card;
public record PerpetualKeywords(long timestamp, List<String> addKeywords, List<String> removeKeywords, boolean removeAll) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addChangedCardKeywords(addKeywords, removeKeywords, removeAll, timestamp, null);
}
}

View File

@@ -0,0 +1,16 @@
package forge.game.card.perpetual;
import forge.game.card.Card;
public record PerpetualNewPT(long timestamp, Integer power, Integer toughness) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addNewPT(power, toughness, timestamp, (long) 0);
}
}

View File

@@ -0,0 +1,16 @@
package forge.game.card.perpetual;
import forge.game.card.Card;
public record PerpetualPTBoost(long timestamp, Integer power, Integer toughness) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addPTBoost(power, toughness, timestamp, (long) 0);
}
}

View File

@@ -0,0 +1,21 @@
package forge.game.card.perpetual;
import java.util.Set;
import forge.card.CardType;
import forge.card.RemoveType;
import forge.game.card.Card;
public record PerpetualTypes(long timestamp, CardType addTypes, CardType removeTypes, Set<RemoveType> removeXTypes) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addChangedCardTypes(addTypes, removeTypes, false, removeXTypes, timestamp, (long) 0, true, false);
}
}