Keyword: link StaticAbility into it (#6079)

* Keyword: link StaticAbility into it

* fix getKeywordMagnitude to check for StaticAbility
This commit is contained in:
Hans Mackowiak
2024-09-07 16:19:57 +02:00
committed by GitHub
parent e7b05b2c9b
commit 4862071db7
19 changed files with 66 additions and 50 deletions

View File

@@ -1759,7 +1759,7 @@ public class ComputerUtilCard {
pumped.addPTBoost(power + berserkPower, toughness, timestamp, 0);
if (!kws.isEmpty()) {
pumped.addChangedCardKeywords(kws, null, false, timestamp, 0, false);
pumped.addChangedCardKeywords(kws, null, false, timestamp, null, false);
}
if (!hiddenKws.isEmpty()) {
pumped.addHiddenExtrinsicKeywords(timestamp, 0, hiddenKws);
@@ -1780,7 +1780,7 @@ public class ComputerUtilCard {
}
}
final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used?
pumped.addChangedCardKeywordsInternal(toCopy, null, false, timestamp2, 0, false);
pumped.addChangedCardKeywordsInternal(toCopy, null, false, timestamp2, null, false);
pumped.updateKeywordsCache(pumped.getCurrentState());
applyStaticContPT(ai.getGame(), pumped, new CardCollection(c));
return pumped;

View File

@@ -567,6 +567,9 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
}
protected IHasSVars getSVarFallback() {
if (this.getKeyword() != null && this.getKeyword().getStatic() != null) {
return this.getKeyword().getStatic();
}
if (getCardState() != null)
return getCardState();
return getHostCard();

View File

@@ -485,17 +485,17 @@ public class GameAction {
if (c.getCastSA() != null && !c.getCastSA().isIntrinsic() && c.getCastSA().getKeyword() != null) {
KeywordInterface ki = c.getCastSA().getKeyword();
ki.setHostCard(copied);
copied.addChangedCardKeywordsInternal(ImmutableList.of(ki), null, false, copied.getGameTimestamp(), 0, true);
copied.addChangedCardKeywordsInternal(ImmutableList.of(ki), null, false, copied.getGameTimestamp(), null, true);
}
// TODO hot fix for non-intrinsic offspring
Multimap<Long, KeywordInterface> addKw = MultimapBuilder.hashKeys().arrayListValues().build();
Multimap<StaticAbility, KeywordInterface> addKw = MultimapBuilder.hashKeys().arrayListValues().build();
for (KeywordInterface kw : c.getKeywords(Keyword.OFFSPRING)) {
if (!kw.isIntrinsic()) {
addKw.put(kw.getStaticId(), kw);
addKw.put(kw.getStatic(), kw);
}
}
if (!addKw.isEmpty()) {
for (Map.Entry<Long, Collection<KeywordInterface>> e : addKw.asMap().entrySet()) {
for (Map.Entry<StaticAbility, Collection<KeywordInterface>> e : addKw.asMap().entrySet()) {
copied.addChangedCardKeywordsInternal(e.getValue(), null, false, copied.getGameTimestamp(), e.getKey(), true);
}
}
@@ -601,7 +601,7 @@ public class GameAction {
// 400.7g try adding keyword back into card if it doesn't already have it
if (zoneTo.is(ZoneType.Stack) && cause != null && cause.isSpell() && !cause.isIntrinsic() && c.equals(cause.getHostCard())) {
if (cause.getKeyword() != null && !copied.getKeywords().contains(cause.getKeyword())) {
copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getNextTimestamp(), 0, true);
copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getNextTimestamp(), null, true);
}
}

View File

@@ -126,7 +126,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
params.put("Category", "Keywords");
c.addPerpetual(params);
}
c.addChangedCardKeywords(keywords, removeKeywords, removeAll, timestamp, 0);
c.addChangedCardKeywords(keywords, removeKeywords, removeAll, timestamp, null);
}
// do this after changing types in case it wasn't a creature before

View File

@@ -672,7 +672,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("Unearth") && movedCard.isInPlay()) {
movedCard.setUnearthed(true);
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false,
game.getNextTimestamp(), 0, true);
game.getNextTimestamp(), null, true);
registerDelayedTrigger(sa, "Exile", Lists.newArrayList(movedCard));
addLeaveBattlefieldReplacement(movedCard, sa, "Exile");
}

View File

@@ -151,7 +151,7 @@ public class CloneEffect extends SpellAbilityEffect {
}
if (!pumpKeywords.isEmpty()) {
tgtCard.addChangedCardKeywords(pumpKeywords, Lists.newArrayList(), false, ts, 0);
tgtCard.addChangedCardKeywords(pumpKeywords, Lists.newArrayList(), false, ts, null);
TokenEffectBase.addPumpUntil(sa, tgtCard, ts);
}

View File

@@ -174,7 +174,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
}
if (keywords != null) {
tgtC.addChangedCardKeywords(keywords, Lists.newArrayList(), false, tStamp, 0);
tgtC.addChangedCardKeywords(keywords, Lists.newArrayList(), false, tStamp, null);
game.fireEvent(new GameEventCardStatsChanged(tgtC));
}

View File

@@ -150,7 +150,7 @@ public class DebuffEffect extends SpellAbilityEffect {
}
removedKW.addAll(kws);
tgtC.addChangedCardKeywords(addedKW, removedKW, false, timestamp, 0);
tgtC.addChangedCardKeywords(addedKW, removedKW, false, timestamp, null);
if (!"Permanent".equals(sa.getParam("Duration"))) {
final GameCommand until = new GameCommand() {

View File

@@ -86,7 +86,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
CardCollectionView list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), valid, sa.getActivatingPlayer(), host, sa);
for (final Card tgtC : list) {
tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, 0, true);
tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, null, true);
if (!"Permanent".equals(sa.getParam("Duration"))) {
// If not Permanent, remove protection at EOT

View File

@@ -153,7 +153,7 @@ public class ProtectEffect extends SpellAbilityEffect {
continue;
}
tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, 0, true);
tgtC.addChangedCardKeywords(gainsKWList, null, false, timestamp, null, true);
if (!"Permanent".equals(sa.getParam("Duration"))) {
// If not Permanent, remove protection at EOT

View File

@@ -68,7 +68,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
params.put("Category", "Keywords");
tgtC.addPerpetual(params);
}
tgtC.addChangedCardKeywords(kws, null, false, timestamp, 0);
tgtC.addChangedCardKeywords(kws, null, false, timestamp, null);
}
if (redrawPT) {
tgtC.updatePowerToughnessForView();

View File

@@ -81,7 +81,7 @@ public class PumpEffect extends SpellAbilityEffect {
params.put("Category", "Keywords");
gameCard.addPerpetual(params);
}
gameCard.addChangedCardKeywords(kws, Lists.newArrayList(), false, timestamp, 0);
gameCard.addChangedCardKeywords(kws, Lists.newArrayList(), false, timestamp, null);
}
if (!hiddenKws.isEmpty()) {

View File

@@ -177,7 +177,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
}
if (!pumpKeywords.isEmpty()) {
moved.addChangedCardKeywords(pumpKeywords, Lists.newArrayList(), false, timestamp, 0);
moved.addChangedCardKeywords(pumpKeywords, Lists.newArrayList(), false, timestamp, null);
addPumpUntil(sa, moved, timestamp);
}

View File

@@ -1696,7 +1696,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
if (!Keyword.smartValueOf(counterType.toString().split(":")[0]).isMultipleRedundant()) {
num = getCounters(counterType);
}
addChangedCardKeywords(Collections.nCopies(num, counterType.toString()), null, false, timestamp, 0, updateView);
addChangedCardKeywords(Collections.nCopies(num, counterType.toString()), null, false, timestamp, null, updateView);
return true;
}
@@ -4657,7 +4657,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
} else if (category.equals("Keywords")) {
boolean removeAll = p.containsKey("RemoveAll") && (boolean) p.get("RemoveAll") == true;
addChangedCardKeywords((List<String>) p.get("AddKeywords"), Lists.newArrayList(), removeAll,
(long) p.get("Timestamp"), (long) 0);
(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"),
@@ -4963,7 +4963,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
KeywordInterface result = storedKeywordByText.get(triple);
if (result == null) {
result = ki.copy(this, false);
result.setStaticId(stAb.getId());
result.setStatic(stAb);
result.setIdx(idx);
result.setIntrinsic(true);
storedKeywordByText.put(triple, result);
@@ -5086,11 +5086,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
}
public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords,
final boolean removeAllKeywords, final long timestamp, final long staticId) {
addChangedCardKeywords(keywords, removeKeywords, removeAllKeywords, timestamp, staticId, true);
final boolean removeAllKeywords, final long timestamp, final StaticAbility st) {
addChangedCardKeywords(keywords, removeKeywords, removeAllKeywords, timestamp, st, true);
}
public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords,
final boolean removeAllKeywords, final long timestamp, final long staticId, final boolean updateView) {
final boolean removeAllKeywords, final long timestamp, final StaticAbility st, final boolean updateView) {
List<KeywordInterface> kws = Lists.newArrayList();
if (keywords != null) {
long idx = 1;
@@ -5104,14 +5104,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
}
}
if (canHave) {
kws.add(getKeywordForStaticAbility(kw, staticId, idx));
kws.add(getKeywordForStaticAbility(kw, st, idx));
}
idx++;
}
}
final KeywordsChange newCks = new KeywordsChange(kws, removeKeywords, removeAllKeywords);
changedCardKeywords.put(timestamp, staticId, newCks);
changedCardKeywords.put(timestamp, st == null ? 0l : st.getId(), newCks);
if (updateView) {
updateKeywords();
@@ -5120,12 +5120,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
}
}
public final KeywordInterface getKeywordForStaticAbility(String kw, final long staticId, final long idx) {
public final KeywordInterface getKeywordForStaticAbility(String kw, final StaticAbility st, final long idx) {
KeywordInterface result;
long staticId = st == null ? 0 : st.getId();
Triple<String, Long, Long> triple = Triple.of(kw, staticId, idx);
if (staticId < 1 || !storedKeywords.containsKey(triple)) {
result = Keyword.getInstance(kw);
result.setStaticId(staticId);
result.setStatic(st);
result.setIdx(idx);
result.createTraits(this, false);
if (staticId > 0) {
@@ -5138,8 +5139,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
}
public final void addKeywordForStaticAbility(KeywordInterface kw) {
if (kw.getStaticId() > 0) {
storedKeywords.put(Triple.of(kw.getOriginal(), kw.getStaticId(), kw.getIdx()), kw);
if (kw.getStatic() != null) {
storedKeywords.put(Triple.of(kw.getOriginal(), (long)kw.getStatic().getId(), kw.getIdx()), kw);
}
}
@@ -5184,8 +5185,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
public final void addChangedCardKeywordsInternal(
final Collection<KeywordInterface> keywords, final Collection<KeywordInterface> removeKeywords,
final boolean removeAllKeywords,
final long timestamp, final long staticId, final boolean updateView) {
final long timestamp, final StaticAbility st, final boolean updateView) {
final KeywordsChange newCks = new KeywordsChange(keywords, removeKeywords, removeAllKeywords);
long staticId = st == null ? 0 : st.getId();
changedCardKeywords.put(timestamp, staticId, newCks);
if (updateView) {
@@ -5821,9 +5823,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
if (StringUtils.isNumeric(s)) {
count += Integer.parseInt(s);
} else {
String svar = StringUtils.join(parse);
if (state.hasSVar(svar)) {
count += AbilityUtils.calculateAmount(this, state.getSVar(svar), null);
StaticAbility st = inst.getStatic();
// TODO make keywordinterface inherit from CardTrait somehow, or invent new interface
if (st != null && st.hasSVar(s)) {
count += AbilityUtils.calculateAmount(this, st.getSVar(s), null);
} else {
String svar = StringUtils.join(parse);
if (state.hasSVar(svar)) {
count += AbilityUtils.calculateAmount(this, state.getSVar(svar), null);
}
}
}
}
@@ -6568,7 +6576,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
suspectedTimestamp = getGame().getNextTimestamp();
// use this for CantHaveKeyword
addChangedCardKeywords(ImmutableList.of("Menace"), ImmutableList.<String>of(), false, suspectedTimestamp, 0, true);
addChangedCardKeywords(ImmutableList.of("Menace"), ImmutableList.<String>of(), false, suspectedTimestamp, null, true);
if (suspectedStatic == null) {
String effect = "Mode$ CantBlockBy | ValidBlocker$ Creature.Self | Description$ CARDNAME can't block.";
@@ -6719,7 +6727,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
new CardType(Collections.singletonList("Creature"), true),
false, EnumSet.of(RemoveType.EnchantmentTypes), bestowTimestamp, 0, updateView, false);
addChangedCardKeywords(Collections.singletonList("Enchant creature"), Lists.newArrayList(),
false, bestowTimestamp, 0, updateView);
false, bestowTimestamp, null, updateView);
}
public final void unanimateBestow() {

View File

@@ -25,7 +25,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
private Keyword keyword;
private String original;
private long staticId = 0;
private StaticAbility st = null;
private long idx = -1;
private List<Trigger> triggers = Lists.newArrayList();
@@ -367,11 +367,11 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
}
}
public long getStaticId() {
return this.staticId;
public StaticAbility getStatic() {
return this.st;
}
public void setStaticId(long v) {
this.staticId = v;
public void setStatic(StaticAbility st) {
this.st = st;
}
public long getIdx() {

View File

@@ -23,8 +23,10 @@ public interface KeywordInterface extends Cloneable {
String getReminderText();
int getAmount();
long getStaticId();
void setStaticId(long v);
StaticAbility getStatic();
void setStatic(StaticAbility st);
long getIdx();
void setIdx(long i);

View File

@@ -705,7 +705,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
mana.getSourceCard().getController(), mana.getSourceCard(), null)) {
final long timestamp = host.getGame().getNextTimestamp();
final List<String> kws = Arrays.asList(mana.getAddedKeywords().split(" & "));
host.addChangedCardKeywords(kws, null, false, timestamp, 0);
host.addChangedCardKeywords(kws, null, false, timestamp, null);
if (mana.addsKeywordsUntil()) {
final GameCommand untilEOT = new GameCommand() {
private static final long serialVersionUID = -8285169579025607693L;
@@ -2590,7 +2590,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
public boolean hasOptionalKeywordAmount(KeywordInterface kw) {
return this.optionalKeywordAmount.contains(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()));
long staticId = kw.getStatic() == null ? 0 : kw.getStatic().getId();
return this.optionalKeywordAmount.contains(kw.getKeyword(), Pair.of(kw.getIdx(), staticId));
}
public boolean hasOptionalKeywordAmount(Keyword kw) {
return this.optionalKeywordAmount.containsRow(kw);
@@ -2600,13 +2601,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
public int getOptionalKeywordAmount(KeywordInterface kw) {
return ObjectUtils.firstNonNull(this.optionalKeywordAmount.get(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId())), 0);
long staticId = kw.getStatic() == null ? 0 : kw.getStatic().getId();
return ObjectUtils.firstNonNull(this.optionalKeywordAmount.get(kw.getKeyword(), Pair.of(kw.getIdx(), staticId)), 0);
}
public int getOptionalKeywordAmount(Keyword kw) {
return this.optionalKeywordAmount.row(kw).values().stream().mapToInt(i->i).sum();
}
public void setOptionalKeywordAmount(KeywordInterface kw, int amount) {
this.optionalKeywordAmount.put(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()), amount);
long staticId = kw.getStatic() == null ? 0 : kw.getStatic().getId();
this.optionalKeywordAmount.put(kw.getKeyword(), Pair.of(kw.getIdx(), staticId), amount);
}
public void clearOptionalKeywordAmount() {
optionalKeywordAmount.clear();

View File

@@ -770,7 +770,7 @@ public final class StaticAbilityContinuous {
}
affectedCard.addChangedCardKeywords(newKeywords, removeKeywords,
removeAllAbilities, se.getTimestamp(), stAb.getId(), true);
removeAllAbilities, se.getTimestamp(), stAb, true);
}
// add HIDDEN keywords

View File

@@ -2,7 +2,7 @@ Name:Fumiko the Lowblood
ManaCost:2 R R
Types:Legendary Creature Human Samurai
PT:3/2
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Bushido:N | CalcKeywordN$ N | Description$ CARDNAME has bushido X, where X is the number of attacking creatures.
SVar:N:Count$Valid Creature.attacking
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Bushido:X | Description$ CARDNAME has bushido X, where X is the number of attacking creatures.
SVar:X:Count$Valid Creature.attacking
S:Mode$ MustAttack | ValidCreature$ Creature.OppCtrl | Description$ Creatures your opponents control attack each combat if able.
Oracle:Fumiko the Lowblood has bushido X, where X is the number of attacking creatures. (Whenever this creature blocks or becomes blocked, it gets +X/+X until end of turn.)\nCreatures your opponents control attack each combat if able.