diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 104e4843ff0..77ff6849087 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -608,7 +608,6 @@ public class ComputerUtilCost { Set colorsAvailable = Sets.newHashSet(); if (additionalLands != null) { - GameActionUtil.grantBasicLandsManaAbilities(additionalLands); cardsToConsider.addAll(additionalLands); } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 58d99a2658a..70645eaf92a 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -848,9 +848,6 @@ public class GameAction { } } - final CardCollection lands = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS); - GameActionUtil.grantBasicLandsManaAbilities(lands); - for (final Card c : staticList) { for (int i = 0; i < c.getStaticCommandList().size(); i++) { final Object[] staticCheck = c.getStaticCommandList().get(i); @@ -896,13 +893,16 @@ public class GameAction { } } - final Map runParams = Maps.newHashMap(); - game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false); + if (runEvents) { + final Map runParams = Maps.newHashMap(); + game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false); + } // Update P/T and type in the view only once after all the cards have been processed, to avoid flickering for (Card c : affectedCards) { c.updatePowerToughnessForView(); c.updateTypesForView(); + c.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering } if (runEvents && !affectedCards.isEmpty()) { diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 5eea36a7da2..ee4f4fd7f79 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -22,10 +22,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import forge.card.MagicColor; import forge.card.mana.ManaCostParser; -import forge.game.ability.AbilityFactory; -import forge.game.ability.AbilityFactory.AbilityRecordType; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.card.*; @@ -40,7 +37,6 @@ import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; import java.util.List; -import java.util.Map; /** @@ -52,59 +48,11 @@ import java.util.Map; * @version $Id$ */ public final class GameActionUtil { - // Cache these instead of generating them on the fly, to avoid excessive allocations every time - // static abilities are checked. - @SuppressWarnings("unchecked") - private static final Map[] BASIC_LAND_ABILITIES_PARAMS = new Map[MagicColor.WUBRG.length]; - private static final AbilityRecordType[] BASIC_LAND_ABILITIES_TYPES = new AbilityRecordType[MagicColor.WUBRG.length]; - static { - for (int i = 0; i < MagicColor.WUBRG.length; i++ ) { - String color = MagicColor.toShortString(MagicColor.WUBRG[i]); - String abString = "AB$ Mana | Cost$ T | Produced$ " + color + - " | SpellDescription$ Add {" + color + "}."; - Map mapParams = AbilityFactory.getMapParams(abString); - BASIC_LAND_ABILITIES_PARAMS[i] = mapParams; - BASIC_LAND_ABILITIES_TYPES[i] = AbilityRecordType.getRecordType(mapParams); - } - } private GameActionUtil() { throw new AssertionError(); } - /** - * Gets the st land mana abilities. - * @param game - * - * @return the stLandManaAbilities - */ - public static void grantBasicLandsManaAbilities(List lands) { - // remove all abilities granted by this Command - for (final Card land : lands) { - List origManaAbs = Lists.newArrayList(land.getManaAbilities()); - // will get comodification exception without a different list - for (final SpellAbility sa : origManaAbs) { - if (sa.isBasicLandAbility()) { - land.getCurrentState().removeManaAbility(sa); - } - } - } - - // add all appropriate mana abilities based on current types - for (int i = 0; i < MagicColor.WUBRG.length; i++ ) { - String landType = MagicColor.Constant.BASIC_LANDS.get(i); - Map mapParams = BASIC_LAND_ABILITIES_PARAMS[i]; - AbilityRecordType type = BASIC_LAND_ABILITIES_TYPES[i]; - for (final Card land : lands) { - if (land.getType().hasSubtype(landType)) { - final SpellAbility sa = AbilityFactory.getAbility(mapParams, type, land, null); - sa.setBasicLandAbility(true); - land.getCurrentState().addManaAbility(sa); - } - } - } - } // stLandManaAbilities - /** *

* Find the alternative costs to a {@link SpellAbility}. diff --git a/forge-game/src/main/java/forge/game/StaticEffect.java b/forge-game/src/main/java/forge/game/StaticEffect.java index 73d2b83f8c2..4462acce247 100644 --- a/forge-game/src/main/java/forge/game/StaticEffect.java +++ b/forge-game/src/main/java/forge/game/StaticEffect.java @@ -23,7 +23,6 @@ import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardUtil; import forge.game.player.Player; -import forge.game.replacement.ReplacementEffect; import forge.game.spellability.AbilityStatic; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbility; @@ -1023,7 +1022,7 @@ public class StaticEffect { if (params.containsKey("AddAbility") || params.containsKey("GainsAbilitiesOf")) { for (final SpellAbility s : affectedCard.getSpellAbilities().threadSafeIterable()) { if (s.isTemporary()) { - affectedCard.removeSpellAbility(s); + affectedCard.removeSpellAbility(s, false); } } } @@ -1038,15 +1037,7 @@ public class StaticEffect { // remove abilities if (params.containsKey("RemoveAllAbilities")) { - for (final SpellAbility ab : affectedCard.getSpellAbilities()) { - ab.setTemporarilySuppressed(false); - } - for (final StaticAbility stA : affectedCard.getStaticAbilities()) { - stA.setTemporarilySuppressed(false); - } - for (final ReplacementEffect rE : affectedCard.getReplacementEffects()) { - rE.setTemporarilySuppressed(false); - } + affectedCard.unSuppressCardTraits(); } // remove Types 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 f01e63d54f2..304a14b0a4b 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -254,6 +254,8 @@ public class Card extends GameEntity implements Comparable { private Table etbCounters = HashBasedTable.create(); + private SpellAbility[] basicLandAbilities = new SpellAbility[MagicColor.WUBRG.length]; + // Enumeration for CMC request types public enum SplitCMCMode { CurrentSideCMC, @@ -2137,14 +2139,21 @@ public class Card extends GameEntity implements Comparable { } public final void addSpellAbility(final SpellAbility a) { + addSpellAbility(a, true); + } + public final void addSpellAbility(final SpellAbility a, final boolean updateView) { a.setHostCard(this); - if (currentState.addSpellAbility(a)) { + if (currentState.addSpellAbility(a) && updateView) { currentState.getView().updateAbilityText(this, currentState); } } public final void removeSpellAbility(final SpellAbility a) { - if (currentState.removeSpellAbility(a)) { + removeSpellAbility(a, true); + } + + public final void removeSpellAbility(final SpellAbility a, final boolean updateView) { + if (currentState.removeSpellAbility(a) && updateView) { currentState.getView().updateAbilityText(this, currentState); } } @@ -2168,6 +2177,11 @@ public class Card extends GameEntity implements Comparable { } public void updateSpellAbilities(List list, CardState state, Boolean mana) { + // do Basic Land Abilities there + if (mana == null || mana == true) { + updateBasicLandAbilities(list, state); + } + for (KeywordInterface kw : getUnhiddenKeywords(state)) { for (SpellAbility sa : kw.getAbilities()) { if (mana == null || mana == sa.isManaAbility()) { @@ -2177,6 +2191,30 @@ public class Card extends GameEntity implements Comparable { } } + private void updateBasicLandAbilities(List list, CardState state) { + final CardTypeView type = state.getTypeWithChanges(); + + if (!type.isLand()) { + // no land, do nothing there + return; + } + + for (int i = 0; i < MagicColor.WUBRG.length; i++ ) { + byte c = MagicColor.WUBRG[i]; + if (type.hasSubtype(MagicColor.Constant.BASIC_LANDS.get(i))) { + SpellAbility sa = basicLandAbilities[i]; + + // no Ability for this type yet, make a new one + if (sa == null) { + sa = CardFactoryUtil.buildBasicLandAbility(state, c); + basicLandAbilities[i] = sa; + } + + list.add(sa); + } + } + } + public final FCollectionView getIntrinsicSpellAbilities() { return currentState.getIntrinsicSpellAbilities(); } @@ -2785,20 +2823,9 @@ public class Card extends GameEntity implements Comparable { } public final void removeChangedCardTypes(final long timestamp, final boolean updateView) { - CardChangedType changed = changedCardTypes.remove(timestamp); - if (updateView) { + if (changedCardTypes.remove(timestamp) != null && updateView) { currentState.getView().updateType(currentState); } - - // if it stops being a land, the abilities does need to be removed - if (changed != null && changed.getAddType() != null && changed.getAddType().isLand()) { - for (final String s : changed.getAddType().getSubtypes()) { - if(CardType.isABasicLandType(s)) { - GameActionUtil.grantBasicLandsManaAbilities(ImmutableList.of(this)); - break; - } - } - } } public final void addColor(final String s, final boolean addToColors, final long timestamp) { @@ -5721,4 +5748,25 @@ public class Card extends GameEntity implements Comparable { public void removeWithFlash(Long timestamp) { withFlash.removeAll(timestamp); } + + public void unSuppressCardTraits() { + // specially reset basic land abilities + for (final SpellAbility ab : basicLandAbilities) { + if (ab != null) { + ab.setTemporarilySuppressed(false); + } + } + for (final SpellAbility ab : getSpellAbilities()) { + ab.setTemporarilySuppressed(false); + } + for (final Trigger t : getTriggers()) { + t.setTemporarilySuppressed(false); + } + for (final StaticAbility stA : getStaticAbilities()) { + stA.setTemporarilySuppressed(false); + } + for (final ReplacementEffect rE : getReplacementEffects()) { + rE.setTemporarilySuppressed(false); + } + } } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 16b57a7b8d1..4f1c987634e 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -73,6 +73,15 @@ import java.util.Map.Entry; */ public class CardFactoryUtil { + public static SpellAbility buildBasicLandAbility(final CardState state, byte color) { + String strcolor = MagicColor.toShortString(color); + String abString = "AB$ Mana | Cost$ T | Produced$ " + strcolor + + " | SpellDescription$ Add {" + strcolor + "}."; + SpellAbility sa = AbilityFactory.getAbility(abString, state); + sa.setIntrinsic(true); // always intristic + return sa; + } + /** *

* abilityMorphDown. diff --git a/forge-game/src/main/java/forge/game/spellability/Spell.java b/forge-game/src/main/java/forge/game/spellability/Spell.java index bf901763044..4c62b6e2fd2 100644 --- a/forge-game/src/main/java/forge/game/spellability/Spell.java +++ b/forge-game/src/main/java/forge/game/spellability/Spell.java @@ -97,7 +97,8 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable boolean lkicheck = false; boolean flash = false; - if (activator != null && !card.getController().equals(activator)) { + // do performanceMode only for cases where the activator is different than controller + if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)) { // always make a lki copy in this case? card = CardUtil.getLKICopy(card); card.setController(activator, 0); @@ -122,7 +123,8 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable lkicheck = true; } - if (!Spell.performanceMode && lkicheck) { + + if (lkicheck) { game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects game.getAction().checkStaticAbilities(false, Sets.newHashSet(card), new CardCollection(card)); } @@ -130,7 +132,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable flash = card.withFlash(activator); // reset static abilities - if (!Spell.performanceMode && lkicheck) { + if (lkicheck) { game.getAction().checkStaticAbilities(false); game.getTracker().unfreeze(); } diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 8f9a469506d..0cf37051c0e 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -80,8 +80,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private Player deltrigActivatingPlayer = null; // used by delayed triggers to ensure the original activator can be restored private Player targetingPlayer = null; - private boolean basicLandAbility; // granted by basic land type - private Card grantorCard = null; // card which grants the ability (equipment or owner of static ability that gave this one) private SpellAbility mayPlayOriginal = null; @@ -1202,14 +1200,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return cm != null && cm.getAmountOfX() > 0; } - public boolean isBasicLandAbility() { - return basicLandAbility; - } - - public void setBasicLandAbility(boolean basicLandAbility0) { - basicLandAbility = basicLandAbility0; - } - @Override public boolean canBeTargetedBy(SpellAbility sa) { return sa.canTargetSpellAbility(this); diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index 90470138785..6549c9368a2 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -651,7 +651,7 @@ public final class StaticAbilityContinuous { if (addFullAbs != null) { for (final SpellAbility ab : addFullAbs) { - affectedCard.addSpellAbility(ab); + affectedCard.addSpellAbility(ab, false); } } @@ -669,7 +669,7 @@ public final class StaticAbilityContinuous { sa.setTemporary(true); sa.setIntrinsic(false); sa.setOriginalHost(hostCard); - affectedCard.addSpellAbility(sa); + affectedCard.addSpellAbility(sa, false); } } } @@ -769,8 +769,6 @@ public final class StaticAbilityContinuous { Player mayPlayController = params.containsKey("MayPlayCardOwner") ? affectedCard.getOwner() : controller; affectedCard.setMayPlay(mayPlayController, mayPlayWithoutManaCost, mayPlayAltManaCost, mayPlayWithFlash, mayPlayGrantZonePermissions, stAb); } - - affectedCard.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering } return affectedCards;