Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions forge-game/src/main/java/forge/game/card/Card.java
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -3497,9 +3497,7 @@ public void updateSpellAbilities(List<SpellAbility> 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<SpellAbility> getAllSpellAbilities() {
Expand Down Expand Up @@ -5230,10 +5228,10 @@ public boolean clearStaticChangedCardKeywords(final boolean updateView) {
}

// Hidden keywords will be left out
public final Collection<KeywordInterface> getUnhiddenKeywords() {
public final KeywordCollection getUnhiddenKeywords() {
return getUnhiddenKeywords(currentState);
}
public final Collection<KeywordInterface> getUnhiddenKeywords(CardState state) {
public final KeywordCollection getUnhiddenKeywords(CardState state) {
return state.getCachedKeywords();
}

Expand Down Expand Up @@ -7116,9 +7114,7 @@ public void updateStaticAbilities(List<StaticAbility> list, CardState state) {
}

// keywords are already sorted by Layer
for (KeywordInterface kw : getUnhiddenKeywords(state)) {
kw.applyStaticAbility(list);
}
getUnhiddenKeywords(state).applyStaticAbility(list);
}

public final FCollectionView<StaticAbility> getHiddenStaticAbilities() {
Expand Down Expand Up @@ -7155,9 +7151,7 @@ public void updateTriggers(List<Trigger> list, CardState state) {
}

// Keywords are already sorted by Layer
for (KeywordInterface kw : getUnhiddenKeywords(state)) {
kw.applyTrigger(list);
}
getUnhiddenKeywords(state).applyTrigger(list);
}

public FCollectionView<ReplacementEffect> getReplacementEffects() {
Expand All @@ -7175,9 +7169,7 @@ public void updateReplacementEffects(List<ReplacementEffect> list, CardState sta
}

// Keywords are already sorted by Layer
for (KeywordInterface kw : getUnhiddenKeywords(state)) {
kw.applyReplacementEffect(list);
}
getUnhiddenKeywords(state).applyReplacementEffect(list);

if (!rulesHost) {
return;
Expand Down
4 changes: 2 additions & 2 deletions forge-game/src/main/java/forge/game/card/CardState.java
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,8 @@ public final void setAttractionLights(Set<Integer> attractionLights) {
view.updateAttractionLights(this);
}

public final Collection<KeywordInterface> getCachedKeywords() {
return cachedKeywords.getValues();
public final KeywordCollection getCachedKeywords() {
return cachedKeywords;
}

public final Collection<KeywordInterface> getCachedKeyword(final Keyword keyword) {
Expand Down
77 changes: 38 additions & 39 deletions forge-game/src/main/java/forge/game/keyword/KeywordCollection.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<KeywordInterface> {

private transient KeywordCollectionView view;
public class KeywordCollection implements ICardTraitChanges, Iterable<KeywordInterface> {
// don't use enumKeys it causes a slow down
private final Multimap<Keyword, KeywordInterface> map = MultimapBuilder.hashKeys()
.linkedHashSetValues().build();
Expand Down Expand Up @@ -173,50 +176,46 @@ public String toString() {
return sb.toString();
}

public KeywordCollectionView getView() {
if (view == null) {
view = new KeywordCollectionView();
@Override
public List<SpellAbility> applySpellAbility(List<SpellAbility> list) {
for (KeywordInterface k : getValues()) {
k.applySpellAbility(list);
}
return view;
return list;
}

public void applyChanges(Iterable<IKeywordsChange> changes) {
for (final IKeywordsChange ck : changes) {
ck.applyKeywords(this);
@Override
public List<Trigger> applyTrigger(List<Trigger> list) {
for (KeywordInterface k : getValues()) {
k.applyTrigger(list);
}
return list;
}

public class KeywordCollectionView implements Iterable<KeywordInterface> {

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<ReplacementEffect> applyReplacementEffect(List<ReplacementEffect> list) {
for (KeywordInterface k : getValues()) {
k.applyReplacementEffect(list);
}
public boolean contains(String keyword) {
return KeywordCollection.this.contains(keyword);
return list;
}
@Override
public List<StaticAbility> applyStaticAbility(List<StaticAbility> list) {
for (KeywordInterface k : getValues()) {
k.applyStaticAbility(list);
}

public List<String> 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<KeywordInterface> iterator() {
return KeywordCollection.this.iterator();
public void applyChanges(Iterable<IKeywordsChange> changes) {
for (final IKeywordsChange ck : changes) {
ck.applyKeywords(this);
}
}

Expand Down
38 changes: 9 additions & 29 deletions forge-game/src/main/java/forge/game/keyword/KeywordsChange.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
*/
public class KeywordsChange implements ICardTraitChanges, IKeywordsChange, Cloneable {
private KeywordCollection keywords = new KeywordCollection();
private List<KeywordInterface> removeKeywordInterfaces = Lists.newArrayList();
private KeywordCollection removeKeywordInterfaces = new KeywordCollection();
private List<String> removeKeywords = Lists.newArrayList();
private boolean removeAllKeywords;

Expand Down Expand Up @@ -73,7 +73,7 @@ public KeywordsChange(
}

if (removeKeywordInterfaces != null) {
this.removeKeywordInterfaces.addAll(removeKeywordInterfaces);
this.removeKeywordInterfaces.insertAll(removeKeywordInterfaces);
}

this.removeAllKeywords = removeAll;
Expand All @@ -90,7 +90,7 @@ public final Collection<KeywordInterface> getKeywords() {
}

public final Collection<KeywordInterface> getRemovedKeywordInstances() {
return this.removeKeywordInterfaces;
return this.removeKeywordInterfaces.getValues();
}
/**
*
Expand Down Expand Up @@ -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) {
Expand All @@ -151,28 +143,16 @@ public KeywordsChange copy(final Card host, final boolean lki) {
}

public List<SpellAbility> applySpellAbility(List<SpellAbility> list) {
for (KeywordInterface k : this.keywords.getValues()) {
k.applySpellAbility(list);
}
return list;
return keywords.applySpellAbility(list);
}
public List<Trigger> applyTrigger(List<Trigger> list) {
for (KeywordInterface k : this.keywords.getValues()) {
k.applyTrigger(list);
}
return list;
return keywords.applyTrigger(list);
}
public List<ReplacementEffect> applyReplacementEffect(List<ReplacementEffect> list) {
for (KeywordInterface k : this.keywords.getValues()) {
k.applyReplacementEffect(list);
}
return list;
return keywords.applyReplacementEffect(list);
}
public List<StaticAbility> applyStaticAbility(List<StaticAbility> list) {
for (KeywordInterface k : this.keywords.getValues()) {
k.applyStaticAbility(list);
}
return list;
return keywords.applyStaticAbility(list);
}

public void applyKeywords(KeywordCollection list) {
Expand Down
5 changes: 2 additions & 3 deletions forge-game/src/main/java/forge/game/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1039,8 +1038,8 @@ private void updateKeywords() {
updateKeywordCardAbilityText();
}

public final KeywordCollectionView getKeywords() {
return keywords.getView();
public final KeywordCollection getKeywords() {
return keywords;
}

@Override
Expand Down
15 changes: 3 additions & 12 deletions forge-game/src/main/java/forge/game/player/PlayerView.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -332,22 +331,14 @@ public void updateAdditionalVillainousChoices(Player p) {
set(TrackableProperty.AdditionalVillainousChoices, p.getAdditionalVotesAmount());
}

public ImmutableMultiset<String> getKeywords() {
public List<String> getKeywords() {
return get(TrackableProperty.Keywords);
}
public List<String> getDisplayableKeywords() {
final List<String> allKws;
final ImmutableMultiset<String> 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<CardView> getCommanders() {
Expand Down Expand Up @@ -567,7 +558,7 @@ private List<String> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
7 changes: 0 additions & 7 deletions forge-game/src/main/java/forge/trackable/TrackableTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -296,12 +295,6 @@ public Map<CounterType, Integer> getDefaultValue() {
return null;
}
};
public static final TrackableType<KeywordCollectionView> KeywordCollectionViewType = new TrackableType<KeywordCollectionView>() {
@Override
protected KeywordCollectionView getDefaultValue() {
return null;
}
};
public static final TrackableType<Map<Object, Object>> GenericMapType = new TrackableType<Map<Object, Object>>() {
@Override
public Map<Object, Object> getDefaultValue() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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();
Expand Down
Loading