diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index e18000c94d5..c3eded97553 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2927,7 +2927,7 @@ public String getAbilityText(final CardState state) { if (isCloaked()) { sb.append("Cloaked\r\n"); } - String keywordText = keywordsToText(getUnhiddenKeywords(state)); + String keywordText = keywordsToText(getUnhiddenKeywords(state).getValues()); sb.append(keywordText).append(keywordText.length() > 0 ? linebreak : ""); // Process replacement effects first so that "enters the battlefield tapped" @@ -3497,9 +3497,7 @@ public void updateSpellAbilities(List list, CardState state) { } // keywords should already been cleanup by layers - for (KeywordInterface kw : getUnhiddenKeywords(state)) { - kw.applySpellAbility(list); - } + getUnhiddenKeywords(state).applySpellAbility(list); } public final FCollectionView getAllSpellAbilities() { @@ -5230,10 +5228,10 @@ public boolean clearStaticChangedCardKeywords(final boolean updateView) { } // Hidden keywords will be left out - public final Collection getUnhiddenKeywords() { + public final KeywordCollection getUnhiddenKeywords() { return getUnhiddenKeywords(currentState); } - public final Collection getUnhiddenKeywords(CardState state) { + public final KeywordCollection getUnhiddenKeywords(CardState state) { return state.getCachedKeywords(); } @@ -7116,9 +7114,7 @@ public void updateStaticAbilities(List list, CardState state) { } // keywords are already sorted by Layer - for (KeywordInterface kw : getUnhiddenKeywords(state)) { - kw.applyStaticAbility(list); - } + getUnhiddenKeywords(state).applyStaticAbility(list); } public final FCollectionView getHiddenStaticAbilities() { @@ -7155,9 +7151,7 @@ public void updateTriggers(List list, CardState state) { } // Keywords are already sorted by Layer - for (KeywordInterface kw : getUnhiddenKeywords(state)) { - kw.applyTrigger(list); - } + getUnhiddenKeywords(state).applyTrigger(list); } public FCollectionView getReplacementEffects() { @@ -7175,9 +7169,7 @@ public void updateReplacementEffects(List list, CardState sta } // Keywords are already sorted by Layer - for (KeywordInterface kw : getUnhiddenKeywords(state)) { - kw.applyReplacementEffect(list); - } + getUnhiddenKeywords(state).applyReplacementEffect(list); if (!rulesHost) { return; diff --git a/forge-game/src/main/java/forge/game/card/CardState.java b/forge-game/src/main/java/forge/game/card/CardState.java index 3f618c301df..94872cb5ab8 100644 --- a/forge-game/src/main/java/forge/game/card/CardState.java +++ b/forge-game/src/main/java/forge/game/card/CardState.java @@ -374,8 +374,8 @@ public final void setAttractionLights(Set attractionLights) { view.updateAttractionLights(this); } - public final Collection getCachedKeywords() { - return cachedKeywords.getValues(); + public final KeywordCollection getCachedKeywords() { + return cachedKeywords; } public final Collection getCachedKeyword(final Keyword keyword) { diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java index 135af59d327..89b47d78d23 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java @@ -9,10 +9,13 @@ import com.google.common.collect.MultimapBuilder; import forge.game.card.Card; +import forge.game.card.ICardTraitChanges; +import forge.game.replacement.ReplacementEffect; +import forge.game.spellability.SpellAbility; +import forge.game.staticability.StaticAbility; +import forge.game.trigger.Trigger; -public class KeywordCollection implements Iterable { - - private transient KeywordCollectionView view; +public class KeywordCollection implements ICardTraitChanges, Iterable { // don't use enumKeys it causes a slow down private final Multimap map = MultimapBuilder.hashKeys() .linkedHashSetValues().build(); @@ -173,50 +176,46 @@ public String toString() { return sb.toString(); } - public KeywordCollectionView getView() { - if (view == null) { - view = new KeywordCollectionView(); + @Override + public List applySpellAbility(List list) { + for (KeywordInterface k : getValues()) { + k.applySpellAbility(list); } - return view; + return list; } - - public void applyChanges(Iterable changes) { - for (final IKeywordsChange ck : changes) { - ck.applyKeywords(this); + @Override + public List applyTrigger(List list) { + for (KeywordInterface k : getValues()) { + k.applyTrigger(list); } + return list; } - - public class KeywordCollectionView implements Iterable { - - protected KeywordCollectionView() { - } - - public boolean isEmpty() { - return KeywordCollection.this.isEmpty(); - } - - public int size() { - return KeywordCollection.this.size(); - } - - public int getAmount(String keyword) { - return KeywordCollection.this.getAmount(keyword); - } - - public boolean contains(Keyword keyword) { - return KeywordCollection.this.contains(keyword); + @Override + public List applyReplacementEffect(List list) { + for (KeywordInterface k : getValues()) { + k.applyReplacementEffect(list); } - public boolean contains(String keyword) { - return KeywordCollection.this.contains(keyword); + return list; + } + @Override + public List applyStaticAbility(List list) { + for (KeywordInterface k : getValues()) { + k.applyStaticAbility(list); } - - public List asStringList() { - return KeywordCollection.this.asStringList(); + return list; + } + @Override + public KeywordCollection copy(Card host, boolean lki) { + KeywordCollection result = new KeywordCollection(); + for (KeywordInterface ki : getValues()) { + result.insert(ki.copy(host, lki)); } + return result; + } - @Override - public Iterator iterator() { - return KeywordCollection.this.iterator(); + public void applyChanges(Iterable changes) { + for (final IKeywordsChange ck : changes) { + ck.applyKeywords(this); } } diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordsChange.java b/forge-game/src/main/java/forge/game/keyword/KeywordsChange.java index 1a95d2dd5fc..bd966b6dc41 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordsChange.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordsChange.java @@ -38,7 +38,7 @@ */ public class KeywordsChange implements ICardTraitChanges, IKeywordsChange, Cloneable { private KeywordCollection keywords = new KeywordCollection(); - private List removeKeywordInterfaces = Lists.newArrayList(); + private KeywordCollection removeKeywordInterfaces = new KeywordCollection(); private List removeKeywords = Lists.newArrayList(); private boolean removeAllKeywords; @@ -73,7 +73,7 @@ public KeywordsChange( } if (removeKeywordInterfaces != null) { - this.removeKeywordInterfaces.addAll(removeKeywordInterfaces); + this.removeKeywordInterfaces.insertAll(removeKeywordInterfaces); } this.removeAllKeywords = removeAll; @@ -90,7 +90,7 @@ public final Collection getKeywords() { } public final Collection getRemovedKeywordInstances() { - return this.removeKeywordInterfaces; + return this.removeKeywordInterfaces.getValues(); } /** * @@ -132,17 +132,9 @@ public KeywordsChange copy(final Card host, final boolean lki) { try { KeywordsChange result = (KeywordsChange)super.clone(); - result.keywords = new KeywordCollection(); - for (KeywordInterface ki : this.keywords.getValues()) { - result.keywords.insert(ki.copy(host, lki)); - } - + result.keywords = this.keywords.copy(host, lki); result.removeKeywords = Lists.newArrayList(removeKeywords); - - result.removeKeywordInterfaces = Lists.newArrayList(); - for (KeywordInterface ki : this.removeKeywordInterfaces) { - result.removeKeywordInterfaces.add(ki.copy(host, lki)); - } + result.removeKeywordInterfaces = this.removeKeywordInterfaces.copy(host, lki); return result; } catch (final Exception ex) { @@ -151,28 +143,16 @@ public KeywordsChange copy(final Card host, final boolean lki) { } public List applySpellAbility(List list) { - for (KeywordInterface k : this.keywords.getValues()) { - k.applySpellAbility(list); - } - return list; + return keywords.applySpellAbility(list); } public List applyTrigger(List list) { - for (KeywordInterface k : this.keywords.getValues()) { - k.applyTrigger(list); - } - return list; + return keywords.applyTrigger(list); } public List applyReplacementEffect(List list) { - for (KeywordInterface k : this.keywords.getValues()) { - k.applyReplacementEffect(list); - } - return list; + return keywords.applyReplacementEffect(list); } public List applyStaticAbility(List list) { - for (KeywordInterface k : this.keywords.getValues()) { - k.applyStaticAbility(list); - } - return list; + return keywords.applyStaticAbility(list); } public void applyKeywords(KeywordCollection list) { diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 4a903fe2d21..efc0c9da670 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -34,7 +34,6 @@ import forge.game.card.*; import forge.game.event.*; import forge.game.keyword.*; -import forge.game.keyword.KeywordCollection.KeywordCollectionView; import forge.game.mana.ManaPool; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; @@ -1039,8 +1038,8 @@ private void updateKeywords() { updateKeywordCardAbilityText(); } - public final KeywordCollectionView getKeywords() { - return keywords.getView(); + public final KeywordCollection getKeywords() { + return keywords; } @Override diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index c135ca514b7..daddba99a62 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -1,6 +1,5 @@ package forge.game.player; -import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -332,22 +331,14 @@ public void updateAdditionalVillainousChoices(Player p) { set(TrackableProperty.AdditionalVillainousChoices, p.getAdditionalVotesAmount()); } - public ImmutableMultiset getKeywords() { + public List getKeywords() { return get(TrackableProperty.Keywords); } - public List getDisplayableKeywords() { - final List allKws; - final ImmutableMultiset kws = getKeywords(); - synchronized (kws) { - allKws = Lists.newArrayList(kws.elementSet()); - } - return allKws; - } public boolean hasKeyword(String keyword) { return getKeywords().contains(keyword); } void updateKeywords(Player p) { - set(TrackableProperty.Keywords, ImmutableMultiset.copyOf(p.getKeywords().asStringList())); + set(TrackableProperty.Keywords, p.getKeywords().asStringList()); } public List getCommanders() { @@ -567,7 +558,7 @@ private List getDetailsList() { } details.add(Localizer.getInstance().getMessage("lblExtraTurnCountHas", getExtraTurnCount())); - final String keywords = Lang.joinHomogenous(getDisplayableKeywords()); + final String keywords = Lang.joinHomogenous(getKeywords()); if (!keywords.isEmpty()) { details.add(keywords); } diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 765c844d007..b8c244139e1 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -206,7 +206,7 @@ public enum TrackableProperty { OptionalAdditionalVote(TrackableTypes.IntegerType), ControlVotes(TrackableTypes.BooleanType), AdditionalVillainousChoices(TrackableTypes.IntegerType), - Keywords(TrackableTypes.KeywordCollectionViewType, FreezeMode.IgnoresFreeze), + Keywords(TrackableTypes.StringListType, FreezeMode.IgnoresFreeze), Commander(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), CommanderCast(TrackableTypes.IntegerMapType), CommanderDamage(TrackableTypes.IntegerMapType), diff --git a/forge-game/src/main/java/forge/trackable/TrackableTypes.java b/forge-game/src/main/java/forge/trackable/TrackableTypes.java index e4a70096dc7..220f94d78ca 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableTypes.java +++ b/forge-game/src/main/java/forge/trackable/TrackableTypes.java @@ -15,7 +15,6 @@ import forge.game.card.CardView.CardStateView; import forge.game.card.CounterType; import forge.game.combat.CombatView; -import forge.game.keyword.KeywordCollection.KeywordCollectionView; import forge.game.player.PlayerView; import forge.game.spellability.StackItemView; import forge.item.IPaperCard; @@ -296,12 +295,6 @@ public Map getDefaultValue() { return null; } }; - public static final TrackableType KeywordCollectionViewType = new TrackableType() { - @Override - protected KeywordCollectionView getDefaultValue() { - return null; - } - }; public static final TrackableType> GenericMapType = new TrackableType>() { @Override public Map getDefaultValue() { diff --git a/forge-gui/src/main/java/forge/gamemodes/net/NetworkChecksumUtil.java b/forge-gui/src/main/java/forge/gamemodes/net/NetworkChecksumUtil.java index 0488deded4a..d9bc33995bc 100644 --- a/forge-gui/src/main/java/forge/gamemodes/net/NetworkChecksumUtil.java +++ b/forge-gui/src/main/java/forge/gamemodes/net/NetworkChecksumUtil.java @@ -15,7 +15,6 @@ import forge.trackable.TrackableTypes.TrackableType; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.List; @@ -495,12 +494,6 @@ static int hashPropertyValue(Object value) { if (value instanceof Enum e) { return e.ordinal(); } - // KeywordCollectionView has no hashCode or content-based toString — hash by keyword strings - if (value instanceof forge.game.keyword.KeywordCollection.KeywordCollectionView kcv) { - List kws = kcv.asStringList(); - Collections.sort(kws); - return kws.hashCode(); - } // Fallback: use toString() for content-based hashing. Covers types like CardType // and ManaCost that have content-based toString() but no hashCode() override. return value.toString().hashCode();