Compare commits

...

2 Commits

Author SHA1 Message Date
Hans Mackowiak
fef579b9f2 Merge branch 'master' into linkedAbilityV2 2022-04-26 08:16:12 +02:00
Hans Mackowiak
eba393fc03 ~ first part for Linked Abilities 2022-04-18 15:34:07 +02:00
15 changed files with 216 additions and 126 deletions

View File

@@ -4,9 +4,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import forge.card.CardStateName; import forge.card.CardStateName;
@@ -35,6 +37,8 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
protected Card hostCard; protected Card hostCard;
protected CardState cardState = null; protected CardState cardState = null;
protected List<StaticLayerInterface> grantedByStatic = Lists.newArrayList();
/** The map params. */ /** The map params. */
protected Map<String, String> originalMapParams = Maps.newHashMap(), protected Map<String, String> originalMapParams = Maps.newHashMap(),
mapParams = Maps.newHashMap(); mapParams = Maps.newHashMap();
@@ -589,6 +593,31 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
return null; return null;
} }
public Card getOriginalOrHost() {
return ObjectUtils.defaultIfNull(getOriginalHost(), getHostCard());
}
public List<StaticLayerInterface> getGrantedByStatic() {
return grantedByStatic;
}
public void setGrantedByStatic(List<StaticLayerInterface> list) {
this.grantedByStatic = Lists.newArrayList(list);
}
public void addGrantedByStatic(final StaticLayerInterface stAb) {
grantedByStatic.add(stAb);
}
// TODO currently Clone effects doesn't set Grantor like giving Abilities does
// if it would, then this needs to be refactored anyway
public Card getFirstGrantor() {
if (grantedByStatic.isEmpty()) {
return null;
}
return grantedByStatic.get(0).getHostCard();
}
public boolean isCopiedTrait() { public boolean isCopiedTrait() {
if (this.getCardState() == null) { if (this.getCardState() == null) {
return false; return false;
@@ -650,7 +679,8 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
copy.mapParams = Maps.newHashMap(originalMapParams); copy.mapParams = Maps.newHashMap(originalMapParams);
copy.setSVars(sVars); copy.setSVars(sVars);
copy.setCardState(cardState); copy.setCardState(cardState);
// dont use setHostCard to not trigger the not copied parts yet copy.setGrantedByStatic(grantedByStatic);
// don't use setHostCard to not trigger the not copied parts yet
copy.hostCard = host; copy.hostCard = host;
} }

View File

@@ -0,0 +1,8 @@
package forge.game;
import forge.game.card.Card;
public interface StaticLayerInterface {
public Card getHostCard();
}

View File

@@ -105,9 +105,9 @@ public class AbilityUtils {
} }
else if (defined.equals("OriginalHost")) { else if (defined.equals("OriginalHost")) {
if (sa instanceof SpellAbility) { if (sa instanceof SpellAbility) {
c = ((SpellAbility)sa).getRootAbility().getOriginalHost(); c = ((SpellAbility)sa).getRootAbility().getFirstGrantor();
} else { } else {
c = sa.getOriginalHost(); c = sa.getFirstGrantor();
} }
} }
else if (defined.equals("EffectSource")) { else if (defined.equals("EffectSource")) {
@@ -539,7 +539,7 @@ public class AbilityUtils {
val = 0; val = 0;
} }
} else if (calcX[0].equals("OriginalHost")) { } else if (calcX[0].equals("OriginalHost")) {
val = xCount(ability.getOriginalHost(), calcX[1], ability); val = xCount(ability.getFirstGrantor(), calcX[1], ability);
} else if (calcX[0].startsWith("Remembered")) { } else if (calcX[0].startsWith("Remembered")) {
// Add whole Remembered list to handlePaid // Add whole Remembered list to handlePaid
final CardCollection list = new CardCollection(); final CardCollection list = new CardCollection();

View File

@@ -1,20 +1,21 @@
package forge.game.card; package forge.game.card;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingTable; import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import forge.game.StaticLayerInterface;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
public class ActivationTable extends ForwardingTable<SpellAbility, Optional<StaticAbility>, Integer> { public class ActivationTable extends ForwardingTable<SpellAbility, List<StaticLayerInterface>, Integer> {
Table<SpellAbility, Optional<StaticAbility>, Integer> dataTable = HashBasedTable.create(); Table<SpellAbility, List<StaticLayerInterface>, Integer> dataTable = HashBasedTable.create();
@Override @Override
protected Table<SpellAbility, Optional<StaticAbility>, Integer> delegate() { protected Table<SpellAbility, List<StaticLayerInterface>, Integer> delegate() {
return dataTable; return dataTable;
} }
@@ -36,7 +37,7 @@ public class ActivationTable extends ForwardingTable<SpellAbility, Optional<Stat
SpellAbility original = getOriginal(sa); SpellAbility original = getOriginal(sa);
if (original != null) { if (original != null) {
Optional<StaticAbility> st = Optional.fromNullable(root.getGrantorStatic()); List<StaticLayerInterface> st = root.getGrantedByStatic();
delegate().put(original, st, ObjectUtils.defaultIfNull(get(original, st), 0) + 1); delegate().put(original, st, ObjectUtils.defaultIfNull(get(original, st), 0) + 1);
} }
@@ -45,7 +46,7 @@ public class ActivationTable extends ForwardingTable<SpellAbility, Optional<Stat
public Integer get(SpellAbility sa) { public Integer get(SpellAbility sa) {
SpellAbility root = sa.getRootAbility(); SpellAbility root = sa.getRootAbility();
SpellAbility original = getOriginal(sa); SpellAbility original = getOriginal(sa);
Optional<StaticAbility> st = Optional.fromNullable(root.getGrantorStatic()); List<StaticLayerInterface> st = root.getGrantedByStatic();
if (contains(original, st)) { if (contains(original, st)) {
return get(original, st); return get(original, st);

View File

@@ -36,6 +36,7 @@ import forge.game.GameEntityCounterTable;
import forge.game.GameStage; import forge.game.GameStage;
import forge.game.GlobalRuleChange; import forge.game.GlobalRuleChange;
import forge.game.IHasSVars; import forge.game.IHasSVars;
import forge.game.StaticLayerInterface;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -74,6 +75,7 @@ import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import io.sentry.Breadcrumb; import io.sentry.Breadcrumb;
import io.sentry.Sentry; import io.sentry.Sentry;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@@ -149,11 +151,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private final Table<Long, Long, CardTraitChanges> changedCardTraits = TreeBasedTable.create(); // Layer 6 private final Table<Long, Long, CardTraitChanges> changedCardTraits = TreeBasedTable.create(); // Layer 6
// stores the card traits created by static abilities // stores the card traits created by static abilities
private final Table<StaticAbility, String, SpellAbility> storedSpellAbilility = TreeBasedTable.create(); private final Table<StaticAbility, String, SpellAbility> storedSpellAbility = TreeBasedTable.create();
private final Table<StaticAbility, String, Trigger> storedTrigger = TreeBasedTable.create(); private final Table<StaticAbility, String, Trigger> storedTrigger = TreeBasedTable.create();
private final Table<StaticAbility, String, ReplacementEffect> storedReplacementEffect = TreeBasedTable.create(); private final Table<StaticAbility, String, ReplacementEffect> storedReplacementEffect = TreeBasedTable.create();
private final Table<StaticAbility, String, StaticAbility> storedStaticAbility = TreeBasedTable.create(); private final Table<StaticAbility, String, StaticAbility> storedStaticAbility = TreeBasedTable.create();
private final Table<StaticLayerInterface, SpellAbility, SpellAbility> copiedSpellAbility = HashBasedTable.create();
// x=timestamp y=StaticAbility id // x=timestamp y=StaticAbility id
private final Table<Long, Long, CardColor> changedCardColorsByText = TreeBasedTable.create(); // Layer 3 by Text Change private final Table<Long, Long, CardColor> changedCardColorsByText = TreeBasedTable.create(); // Layer 3 by Text Change
private final Table<Long, Long, CardColor> changedCardColorsCharacterDefining = TreeBasedTable.create(); // Layer 5 CDA private final Table<Long, Long, CardColor> changedCardColorsCharacterDefining = TreeBasedTable.create(); // Layer 5 CDA
@@ -329,11 +333,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private final ActivationTable numberGameActivations = new ActivationTable(); private final ActivationTable numberGameActivations = new ActivationTable();
private final ActivationTable numberAbilityResolved = new ActivationTable(); private final ActivationTable numberAbilityResolved = new ActivationTable();
private final Map<SpellAbility, List<String>> chosenModesTurn = Maps.newHashMap(); private final LinkedAbilityTable<String> chosenModesTurn = new LinkedAbilityTable<String>();
private final Map<SpellAbility, List<String>> chosenModesGame = Maps.newHashMap(); private final LinkedAbilityTable<String> chosenModesGame = new LinkedAbilityTable<String>();
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesTurnStatic = HashBasedTable.create();
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesGameStatic = HashBasedTable.create();
private CombatLki combatLKI; private CombatLki combatLKI;
@@ -4254,12 +4255,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public final SpellAbility getSpellAbilityForStaticAbility(final String str, final StaticAbility stAb) { public final SpellAbility getSpellAbilityForStaticAbility(final String str, final StaticAbility stAb) {
SpellAbility result = storedSpellAbilility.get(stAb, str); SpellAbility result = storedSpellAbility.get(stAb, str);
if (result == null) { if (result == null) {
result = AbilityFactory.getAbility(str, this, stAb); result = AbilityFactory.getAbility(str, this, stAb);
result.setIntrinsic(false); result.setIntrinsic(false);
result.setGrantorStatic(stAb); result.addGrantedByStatic(stAb);
storedSpellAbilility.put(stAb, str, result); storedSpellAbility.put(stAb, str, result);
} }
return result; return result;
} }
@@ -4268,6 +4269,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
Trigger result = storedTrigger.get(stAb, str); Trigger result = storedTrigger.get(stAb, str);
if (result == null) { if (result == null) {
result = TriggerHandler.parseTrigger(str, this, false, stAb); result = TriggerHandler.parseTrigger(str, this, false, stAb);
result.addGrantedByStatic(stAb);
storedTrigger.put(stAb, str, result); storedTrigger.put(stAb, str, result);
} }
return result; return result;
@@ -4278,6 +4280,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
ReplacementEffect result = storedReplacementEffect.get(stAb, str); ReplacementEffect result = storedReplacementEffect.get(stAb, str);
if (result == null) { if (result == null) {
result = ReplacementHandler.parseReplacement(str, this, false, stAb); result = ReplacementHandler.parseReplacement(str, this, false, stAb);
result.addGrantedByStatic(stAb);
storedReplacementEffect.put(stAb, str, result); storedReplacementEffect.put(stAb, str, result);
} }
return result; return result;
@@ -4287,11 +4290,28 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
StaticAbility result = storedStaticAbility.get(stAb, str); StaticAbility result = storedStaticAbility.get(stAb, str);
if (result == null) { if (result == null) {
result = StaticAbility.create(str, this, stAb.getCardState(), false); result = StaticAbility.create(str, this, stAb.getCardState(), false);
result.addGrantedByStatic(stAb);
storedStaticAbility.put(stAb, str, result); storedStaticAbility.put(stAb, str, result);
} }
return result; return result;
} }
public final SpellAbility copySpellAbilityForStaticAbility(final SpellAbility sa, final StaticAbility stAb, final boolean intrinsic) {
SpellAbility result = copiedSpellAbility.get(stAb, sa);
if (result == null) {
result = sa.copy(this, false);
if (stAb.hasParam("GainsAbilitiesLimitPerTurn")) {
result.setRestrictions(sa.getRestrictions());
result.getRestrictions().setLimitToCheck(stAb.getParam("GainsAbilitiesLimitPerTurn"));
}
result.setOriginalAbility(sa.getOriginalAbility()); // need to be set to get the Once Per turn Clause correct
result.addGrantedByStatic(stAb);
result.setIntrinsic(intrinsic);
copiedSpellAbility.put(stAb, sa, result);
}
return result;
}
public final void addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities, public final void addChangedCardTraits(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
Collection<Trigger> trigger, Collection<ReplacementEffect> replacements, Collection<StaticAbility> statics, Collection<Trigger> trigger, Collection<ReplacementEffect> replacements, Collection<StaticAbility> statics,
boolean removeAll, boolean removeNonMana, long timestamp, long staticId) { boolean removeAll, boolean removeNonMana, long timestamp, long staticId) {
@@ -6863,91 +6883,19 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public List<String> getChosenModesTurn(SpellAbility ability) { public List<String> getChosenModesTurn(SpellAbility ability) {
SpellAbility original = null; return chosenModesTurn.get(ability);
SpellAbility root = ability.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
}
if (ability.getGrantorStatic() != null) {
return chosenModesTurnStatic.get(original, ability.getGrantorStatic());
}
return chosenModesTurn.get(original);
} }
public List<String> getChosenModesGame(SpellAbility ability) { public List<String> getChosenModesGame(SpellAbility ability) {
SpellAbility original = null; return chosenModesGame.get(ability);
SpellAbility root = ability.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
}
if (ability.getGrantorStatic() != null) {
return chosenModesGameStatic.get(original, ability.getGrantorStatic());
}
return chosenModesGame.get(original);
} }
public void addChosenModes(SpellAbility ability, String mode) { public void addChosenModes(SpellAbility ability, String mode) {
SpellAbility original = null; this.chosenModesTurn.put(mode, ability);
SpellAbility root = ability.getRootAbility(); this.chosenModesGame.put(mode, ability);
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
}
if (ability.getGrantorStatic() != null) {
List<String> result = chosenModesTurnStatic.get(original, ability.getGrantorStatic());
if (result == null) {
result = Lists.newArrayList();
chosenModesTurnStatic.put(original, ability.getGrantorStatic(), result);
}
result.add(mode);
result = chosenModesGameStatic.get(original, ability.getGrantorStatic());
if (result == null) {
result = Lists.newArrayList();
chosenModesGameStatic.put(original, ability.getGrantorStatic(), result);
}
result.add(mode);
} else {
List<String> result = chosenModesTurn.get(original);
if (result == null) {
result = Lists.newArrayList();
chosenModesTurn.put(original, result);
}
result.add(mode);
result = chosenModesGame.get(original);
if (result == null) {
result = Lists.newArrayList();
chosenModesGame.put(original, result);
}
result.add(mode);
}
} }
public void resetChosenModeTurn() { public void resetChosenModeTurn() {
chosenModesTurn.clear(); chosenModesTurn.clear();
chosenModesTurnStatic.clear();
} }
public int getPlaneswalkerAbilityActivated() { public int getPlaneswalkerAbilityActivated() {

View File

@@ -0,0 +1,111 @@
package forge.game.card;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import forge.game.CardTraitBase;
import forge.game.StaticLayerInterface;
import forge.util.collect.FCollection;
public class LinkedAbilityTable<T> extends ForwardingTable<Card, List<StaticLayerInterface>, FCollection<T>> {
private Table<Card, List<StaticLayerInterface>, FCollection<T>> dataTable = HashBasedTable.create();
@Override
protected Table<Card, List<StaticLayerInterface>, FCollection<T>> delegate() {
return dataTable;
}
protected FCollection<T> getSupplier() {
return new FCollection<T>();
}
protected FCollection<T> putInternal(T object, Card host, List<StaticLayerInterface> st) {
host = ObjectUtils.defaultIfNull(host.getEffectSource(), host);
FCollection<T> old;
if (contains(host, st)) {
old = get(host, st);
} else {
old = getSupplier();
delegate().put(host, st, old);
}
old.add(object);
return old;
}
/*
public FCollection<T> put(T object, Card host) {
return putInternal(object, host, null);
}
//*/
public FCollection<T> put(T object, CardTraitBase ctb) {
return putInternal(object, ctb.getOriginalOrHost(), ctb.getGrantedByStatic());
}
protected void setInternal(Iterable<T> list, Card host, List<StaticLayerInterface> st) {
host = ObjectUtils.defaultIfNull(host.getEffectSource(), host);
if (list == null || Iterables.isEmpty(list)) {
delegate().remove(host, st);
} else {
FCollection<T> old = getSupplier();
old.addAll(list);
delegate().put(host, st, old);
}
}
public void set(Iterable<T> list, CardTraitBase ctb) {
setInternal(list, ctb.getOriginalOrHost(), ctb.getGrantedByStatic());
}
public FCollection<T> get(CardTraitBase ctb) {
Card host = ctb.getOriginalOrHost();
host = ObjectUtils.defaultIfNull(host.getEffectSource(), host);
List<StaticLayerInterface> st = ctb.getGrantedByStatic();
if (contains(host, st)) {
return get(host, st);
} else {
return FCollection.<T>getEmpty();
}
}
public FCollection<T> get(Card host) {
if (contains(host, Lists.newArrayList())) {
return get(host, Lists.newArrayList());
} else {
return FCollection.<T>getEmpty();
}
}
public boolean contains(T object, CardTraitBase ctb) {
return get(ctb).contains(object);
}
public boolean remove(T value) {
boolean changed = false;
for (FCollection<T> col : delegate().values()) {
if (col.remove(value)) {
changed = true;
}
}
return changed;
}
public boolean hasOtherLinkedValues(Card host) {
for (List<StaticLayerInterface> o : this.columnKeySet()) {
if (!o.isEmpty()) {
return true;
}
}
for (Card ctbHost : this.rowKeySet()) {
if (!host.equals(ctbHost)) {
return true;
}
}
return false;
}
}

View File

@@ -3,7 +3,6 @@ package forge.game.card.token;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@@ -273,7 +272,7 @@ public class TokenInfo {
final Card host = sa.getHostCard(); final Card host = sa.getHostCard();
final Game game = host.getGame(); final Game game = host.getGame();
String edition = ObjectUtils.firstNonNull(sa.getOriginalHost(), host).getSetCode(); String edition = sa.getOriginalOrHost().getSetCode();
PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition); PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition);
if (token == null) { if (token == null) {

View File

@@ -81,7 +81,7 @@ public class CostRemoveCounter extends CostPart {
} else { } else {
List<Card> typeList; List<Card> typeList;
if (type.equals("OriginalHost")) { if (type.equals("OriginalHost")) {
typeList = Lists.newArrayList(ability.getOriginalHost()); typeList = Lists.newArrayList(ability.getFirstGrantor());
} else { } else {
typeList = CardLists.getValidCards(payer.getCardsIn(this.zone), type.split(";"), payer, source, ability); typeList = CardLists.getValidCards(payer.getCardsIn(this.zone), type.split(";"), payer, source, ability);
} }
@@ -155,7 +155,7 @@ public class CostRemoveCounter extends CostPart {
else { else {
List<Card> typeList; List<Card> typeList;
if (type.equals("OriginalHost")) { if (type.equals("OriginalHost")) {
typeList = Lists.newArrayList(ability.getOriginalHost()); typeList = Lists.newArrayList(ability.getFirstGrantor());
} else { } else {
typeList = CardLists.getValidCards(payer.getCardsIn(this.zone), type.split(";"), payer, source, ability); typeList = CardLists.getValidCards(payer.getCardsIn(this.zone), type.split(";"), payer, source, ability);
} }

View File

@@ -113,7 +113,10 @@ public class CostSacrifice extends CostPartWithList {
final Card source = ability.getHostCard(); final Card source = ability.getHostCard();
if (getType().equals("OriginalHost")) { if (getType().equals("OriginalHost")) {
Card originalEquipment = ability.getOriginalHost(); Card originalEquipment = ability.getFirstGrantor();
if (!originalEquipment.getController().equals(activator)) {
return false;
}
return originalEquipment.isEquipping() && originalEquipment.canBeSacrificedBy(ability, effect); return originalEquipment.isEquipping() && originalEquipment.canBeSacrificedBy(ability, effect);
} }
else if (!payCostFromSource()) { // You can always sac all else if (!payCostFromSource()) { // You can always sac all
@@ -131,7 +134,7 @@ public class CostSacrifice extends CostPartWithList {
// if X is defined, it needs to be calculated and checked, if X is // if X is defined, it needs to be calculated and checked, if X is
// choice, it can be Paid even if it's 0 // choice, it can be Paid even if it's 0
} }
else return source.canBeSacrificedBy(ability, effect); else return source.getController().equals(activator) && source.canBeSacrificedBy(ability, effect);
} }
@Override @Override

View File

@@ -76,7 +76,7 @@ public class CostUnattach extends CostPartWithList {
if (type.equals("CARDNAME")) { if (type.equals("CARDNAME")) {
return source.isEquipping(); return source.isEquipping();
} else if (type.equals("OriginalHost")) { } else if (type.equals("OriginalHost")) {
Card originalEquipment = ability.getOriginalHost(); Card originalEquipment = ability.getFirstGrantor();
return originalEquipment.isEquipping(); return originalEquipment.isEquipping();
} else { } else {
return CardLists.getValidCards(source.getEquippedBy(), type, payer, source, ability).size() > 0; return CardLists.getValidCards(source.getEquippedBy(), type, payer, source, ability).size() > 0;
@@ -89,7 +89,7 @@ public class CostUnattach extends CostPartWithList {
return source; return source;
} }
} else if (getType().equals("OriginalHost")) { } else if (getType().equals("OriginalHost")) {
Card originalEquipment = ability.getOriginalHost(); Card originalEquipment = ability.getFirstGrantor();
if (originalEquipment.isEquipping()) { if (originalEquipment.isEquipping()) {
return originalEquipment; return originalEquipment;
} }

View File

@@ -920,8 +920,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
desc = CardTranslation.translateMultipleDescriptionText(desc, currentName); desc = CardTranslation.translateMultipleDescriptionText(desc, currentName);
desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName)); desc = TextUtil.fastReplace(desc, "CARDNAME", CardTranslation.getTranslatedName(currentName));
desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName))); desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(CardTranslation.getTranslatedName(currentName)));
if (node.getOriginalHost() != null) { if (node.getFirstGrantor() != null) {
desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getOriginalHost().getName()); desc = TextUtil.fastReplace(desc, "ORIGINALHOST", node.getFirstGrantor().getName());
} }
sb.append(desc); sb.append(desc);
} }
@@ -2354,7 +2354,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
public SpellAbility getOriginalAbility() { public SpellAbility getOriginalAbility() {
return grantorOriginal; return ObjectUtils.defaultIfNull(grantorOriginal, this);
} }
public void setOriginalAbility(final SpellAbility sa) { public void setOriginalAbility(final SpellAbility sa) {
grantorOriginal = sa; grantorOriginal = sa;

View File

@@ -31,6 +31,7 @@ import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.GameStage; import forge.game.GameStage;
import forge.game.IIdentifiable; import forge.game.IIdentifiable;
import forge.game.StaticLayerInterface;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
@@ -51,7 +52,7 @@ import forge.util.TextUtil;
/** /**
* The Class StaticAbility. * The Class StaticAbility.
*/ */
public class StaticAbility extends CardTraitBase implements IIdentifiable, Cloneable, Comparable<StaticAbility> { public class StaticAbility extends CardTraitBase implements IIdentifiable, Cloneable, Comparable<StaticAbility>, StaticLayerInterface {
private static int maxId = 0; private static int maxId = 0;
private static int nextId() { return ++maxId; } private static int nextId() { return ++maxId; }

View File

@@ -615,9 +615,7 @@ public final class StaticAbilityContinuous {
List<KeywordInterface> keywords = Lists.newArrayList(); List<KeywordInterface> keywords = Lists.newArrayList();
for (SpellAbility sa : state.getSpellAbilities()) { for (SpellAbility sa : state.getSpellAbilities()) {
SpellAbility newSA = sa.copy(affectedCard, false); SpellAbility newSA = affectedCard.copySpellAbilityForStaticAbility(sa, stAb, true);
newSA.setOriginalAbility(sa); // need to be set to get the Once Per turn Clause correct
newSA.setGrantorStatic(stAb);
//newSA.setIntrinsic(false); needs to be changed by CardTextChanges //newSA.setIntrinsic(false); needs to be changed by CardTextChanges
spellAbilities.add(newSA); spellAbilities.add(newSA);
@@ -626,7 +624,6 @@ public final class StaticAbilityContinuous {
for (String ability : params.get("GainTextAbilities").split(" & ")) { for (String ability : params.get("GainTextAbilities").split(" & ")) {
final SpellAbility sa = AbilityFactory.getAbility(AbilityUtils.getSVar(stAb, ability), affectedCard, stAb); final SpellAbility sa = AbilityFactory.getAbility(AbilityUtils.getSVar(stAb, ability), affectedCard, stAb);
sa.setIntrinsic(true); // needs to be affected by Text sa.setIntrinsic(true); // needs to be affected by Text
sa.setGrantorStatic(stAb);
spellAbilities.add(sa); spellAbilities.add(sa);
} }
} }
@@ -835,15 +832,7 @@ public final class StaticAbilityContinuous {
if (loyaltyAB && !sa.isPwAbility()) { if (loyaltyAB && !sa.isPwAbility()) {
continue; continue;
} }
SpellAbility newSA = sa.copy(affectedCard, false); addedAbilities.add(affectedCard.copySpellAbilityForStaticAbility(sa, stAb, false));
if (params.containsKey("GainsAbilitiesLimitPerTurn")) {
newSA.setRestrictions(sa.getRestrictions());
newSA.getRestrictions().setLimitToCheck(params.get("GainsAbilitiesLimitPerTurn"));
}
newSA.setOriginalAbility(sa); // need to be set to get the Once Per turn Clause correct
newSA.setGrantorStatic(stAb);
newSA.setIntrinsic(false);
addedAbilities.add(newSA);
} }
} }
} }

View File

@@ -206,8 +206,8 @@ public abstract class Trigger extends TriggerReplacementBase {
if (!saDesc.startsWith(sa.getHostCard().getName())) { if (!saDesc.startsWith(sa.getHostCard().getName())) {
saDesc = saDesc.substring(0, 1).toLowerCase() + saDesc.substring(1); saDesc = saDesc.substring(0, 1).toLowerCase() + saDesc.substring(1);
} }
if (saDesc.contains("ORIGINALHOST") && sa.getOriginalHost() != null) { if (saDesc.contains("ORIGINALHOST") && sa.getFirstGrantor() != null) {
saDesc = TextUtil.fastReplace(saDesc, "ORIGINALHOST", sa.getOriginalHost().getName()); saDesc = TextUtil.fastReplace(saDesc, "ORIGINALHOST", sa.getFirstGrantor().getName());
} }
} else { } else {
saDesc = "<take no action>"; // printed in case nothing is chosen for the ability (e.g. Charm with Up to X) saDesc = "<take no action>"; // printed in case nothing is chosen for the ability (e.g. Charm with Up to X)

View File

@@ -943,7 +943,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
return PaymentDecision.card(source, cntRemoved >= 0 ? cntRemoved : maxCounters); return PaymentDecision.card(source, cntRemoved >= 0 ? cntRemoved : maxCounters);
} else if (type.equals("OriginalHost")) { } else if (type.equals("OriginalHost")) {
final int maxCounters = ability.getOriginalHost().getCounters(cost.counter); final int maxCounters = ability.getFirstGrantor().getCounters(cost.counter);
if (amount.equals("All")) { if (amount.equals("All")) {
cntRemoved = maxCounters; cntRemoved = maxCounters;
} }
@@ -951,7 +951,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
return null; return null;
} }
return PaymentDecision.card(ability.getOriginalHost(), cntRemoved >= 0 ? cntRemoved : maxCounters); return PaymentDecision.card(ability.getFirstGrantor(), cntRemoved >= 0 ? cntRemoved : maxCounters);
} }
CardCollectionView validCards = CardLists.getValidCards(player.getCardsIn(cost.zone), type.split(";"), player, source, ability); CardCollectionView validCards = CardLists.getValidCards(player.getCardsIn(cost.zone), type.split(";"), player, source, ability);
@@ -994,7 +994,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
} }
if (type.equals("OriginalHost")) { if (type.equals("OriginalHost")) {
Card host = ability.getOriginalHost(); Card host = ability.getFirstGrantor();
if (host.getController() == ability.getActivatingPlayer() && host.isInPlay()) { if (host.getController() == ability.getActivatingPlayer() && host.isInPlay()) {
return controller.confirmPayment(cost, Localizer.getInstance().getMessage("lblSacrificeCardConfirm", CardTranslation.getTranslatedName(host.getName())), ability) ? PaymentDecision.card(host) : null; return controller.confirmPayment(cost, Localizer.getInstance().getMessage("lblSacrificeCardConfirm", CardTranslation.getTranslatedName(host.getName())), ability) ? PaymentDecision.card(host) : null;
} }