diff --git a/.gitattributes b/.gitattributes index 10da3c5bdf7..bd3e1396dad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -631,6 +631,7 @@ forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java s forge-game/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java -text forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java svneol=native#text/plain forge-game/src/main/java/forge/game/staticability/StaticAbilityETBTapped.java -text +forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java -text forge-game/src/main/java/forge/game/staticability/StaticAbilityPreventDamage.java svneol=native#text/plain forge-game/src/main/java/forge/game/staticability/package-info.java svneol=native#text/plain forge-game/src/main/java/forge/game/trigger/Trigger.java svneol=native#text/plain @@ -706,6 +707,7 @@ forge-game/src/main/java/forge/trackable/TrackableObject.java -text forge-game/src/main/java/forge/trackable/TrackableProperty.java -text forge-game/src/main/java/forge/trackable/TrackableSerializer.java -text forge-game/src/main/java/forge/trackable/TrackableTypes.java -text +forge-game/src/main/java/forge/trackable/Tracker.java -text forge-game/src/main/java/forge/util/Expressions.java -text forge-game/src/main/java/forge/util/MessageUtil.java -text forge-game/src/test/java/forge/game/mana/ManaCostBeingPaidTest.java -text diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 9be230185da..29cd709a537 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -14,7 +14,6 @@ import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; -import forge.game.card.CardColor; import forge.game.card.CardLists; import forge.game.card.CardUtil; import forge.game.combat.CombatUtil; @@ -41,7 +40,6 @@ import org.apache.commons.lang3.tuple.Pair; import java.util.*; - public class ComputerUtilMana { private final static boolean DEBUG_MANA_PAYMENT = false; @@ -1207,13 +1205,11 @@ public class ComputerUtilMana { Card convoked = null; for (ManaCostShard toPay : cost) { for (Card c : list) { - for (CardColor col : c.getColor()) { - final int mask = col.getColorMask() & toPay.getColorMask(); - if (mask != 0) { - convoked = c; - convoke.put(c, toPay); - break; - } + final int mask = c.determineColor().getColor() & toPay.getColorMask(); + if (mask != 0) { + convoked = c; + convoke.put(c, toPay); + break; } if (convoked != null){ break; diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index a331ff3393c..c31d16ab15f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -1,7 +1,6 @@ package forge.ai.ability; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCost; @@ -27,6 +26,7 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbilityContinuous; +import forge.game.staticability.StaticAbilityLayer; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; @@ -396,7 +396,7 @@ public class AnimateAi extends SpellAbilityAi { card.addHiddenExtrinsicKeyword(k); } - card.addColor(finalDesc, !sa.hasParam("OverwriteColors"), true); + card.addColor(finalDesc, !sa.hasParam("OverwriteColors"), timestamp); //back to duplicating AnimateEffect.resolve //TODO will all these abilities/triggers/replacements/etc. lead to memory leaks or unintended effects? @@ -462,9 +462,11 @@ public class AnimateAi extends SpellAbilityAi { if (stAbs.size() > 0) { for (final String s : stAbs) { final String actualAbility = source.getSVar(s); - StaticAbility stAb = card.addStaticAbility(actualAbility); + final StaticAbility stAb = card.addStaticAbility(actualAbility); if ("Continuous".equals(stAb.getMapParams().get("Mode"))) { - StaticAbilityContinuous.applyContinuousAbility(stAb, Lists.newArrayList(card)); + for (final StaticAbilityLayer layer : stAb.getLayers()) { + StaticAbilityContinuous.applyContinuousAbility(stAb, new CardCollection(card), layer); + } } } } diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index d339bbb1096..70f04034b92 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -165,6 +165,10 @@ public abstract class CardTraitBase extends GameObject { return (this.suppressed || this.temporarilySuppressed); } + protected final boolean isNonTempSuppressed() { + return this.suppressed; + } + protected boolean meetsCommonRequirements(Map params) { final Player hostController = this.getHostCard().getController(); final Game game = hostController.getGame(); diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 4b5e10d4954..8e681b49cf7 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -63,6 +63,7 @@ import forge.game.trigger.TriggerType; import forge.game.zone.MagicStack; import forge.game.zone.Zone; import forge.game.zone.ZoneType; +import forge.trackable.Tracker; import forge.util.Aggregates; import forge.util.FCollection; import forge.util.FCollectionView; @@ -103,6 +104,7 @@ public class Game { private boolean disableAutoYields; private final GameView view; + private final Tracker tracker = new Tracker(); private GameEntityCache playerCache = new GameEntityCache<>(); public Player getPlayer(PlayerView playerView) { @@ -178,6 +180,10 @@ public class Game { return view; } + public Tracker getTracker() { + return tracker; + } + /** * Gets the players who are still fighting to win. */ diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index f9c4d0bef9f..d2c3534c763 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -20,6 +20,7 @@ package forge.game; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import forge.GameCommand; @@ -39,6 +40,7 @@ import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.game.staticability.StaticAbility; +import forge.game.staticability.StaticAbilityLayer; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; import forge.game.zone.PlayerZone; @@ -46,10 +48,10 @@ import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.item.PaperCard; -import forge.trackable.TrackableObject; import forge.util.Aggregates; import forge.util.CollectionSuppliers; import forge.util.Expressions; +import forge.util.FCollection; import forge.util.FCollectionView; import forge.util.ThreadUtil; import forge.util.Visitor; @@ -534,9 +536,9 @@ public class GameAction { } public final void checkStaticAbilities() { - checkStaticAbilities(new HashSet()); + checkStaticAbilities(true, new CardCollection()); } - public final void checkStaticAbilities(final Set affectedCards) { + public final void checkStaticAbilities(final boolean runEvents, final Set affectedCards) { if (game.isGameOver()) { return; } @@ -546,18 +548,19 @@ public class GameAction { game.getTriggerHandler().cleanUpTemporaryTriggers(); game.getReplacementHandler().cleanUpTemporaryReplacements(); - for (Player p : game.getPlayers()) { + for (final Player p : game.getPlayers()) { p.getManaPool().restoreColorReplacements(); } // search for cards with static abilities - final ArrayList staticAbilities = new ArrayList(); - final List staticList = new ArrayList(); + final FCollection staticAbilities = new FCollection(); + final CardCollection staticList = new CardCollection(); + game.forEachCardInGame(new Visitor() { @Override - public void visit(Card c) { + public void visit(final Card c) { for (int i = 0; i < c.getStaticAbilities().size(); i++) { - StaticAbility stAb = c.getStaticAbilities().get(i); + final StaticAbility stAb = c.getStaticAbilities().get(i); if (stAb.getMapParams().get("Mode").equals("Continuous")) { staticAbilities.add(stAb); } @@ -575,23 +578,36 @@ public class GameAction { final Comparator comp = new Comparator() { @Override public int compare(final StaticAbility a, final StaticAbility b) { - int layerDelta = a.getLayer() - b.getLayer(); - if (layerDelta != 0) return layerDelta; - - long tsDelta = a.getHostCard().getTimestamp() - b.getHostCard().getTimestamp(); - return tsDelta == 0 ? 0 : tsDelta > 0 ? 1 : -1; + return Long.compare(a.getHostCard().getTimestamp(), b.getHostCard().getTimestamp()); } }; Collections.sort(staticAbilities, comp); - for (final StaticAbility stAb : staticAbilities) { - List affectedHere = stAb.applyAbility("Continuous"); - if (null != affectedHere) { - affectedCards.addAll(affectedHere); + + final Map affectedPerAbility = Maps.newHashMap(); + for (final StaticAbilityLayer layer : StaticAbilityLayer.CONTINUOUS_LAYERS) { + for (final StaticAbility stAb : staticAbilities) { + final CardCollectionView previouslyAffected = affectedPerAbility.get(stAb); + final CardCollectionView affectedHere; + if (previouslyAffected == null) { + affectedHere = stAb.applyContinuousAbility(layer); + if (affectedHere != null) { + affectedPerAbility.put(stAb, affectedHere); + } + } else { + affectedHere = previouslyAffected; + stAb.applyContinuousAbility(layer, previouslyAffected); + } } } - CardCollectionView lands = game.getCardsIn(ZoneType.Battlefield); - GameActionUtil.grantBasicLandsManaAbilities(CardLists.filter(lands, CardPredicates.Presets.LANDS)); + for (final CardCollectionView affected : affectedPerAbility.values()) { + if (affected != null) { + Iterables.addAll(affectedCards, affected); + } + } + + 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++) { @@ -640,6 +656,10 @@ public class GameAction { final HashMap runParams = new HashMap(); game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false); + + if (runEvents && !affectedCards.isEmpty()) { + game.fireEvent(new GameEventCardStatsChanged(affectedCards)); + } } public final void checkStateEffects(final boolean runEvents) { @@ -652,11 +672,6 @@ public class GameAction { return; } - // final JFrame frame = Singletons.getView().getFrame(); - // if (!frame.isDisplayable()) { - // return; - // } - if (game.isGameOver()) { return; } @@ -669,11 +684,11 @@ public class GameAction { final boolean refreeze = game.getStack().isFrozen(); game.getStack().setFrozen(true); - TrackableObject.freeze(); //prevent views flickering during while updating for state-based effects + game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects // do this multiple times, sometimes creatures/permanents will survive when they shouldn't for (int q = 0; q < 9; q++) { - checkStaticAbilities(affectedCards); + checkStaticAbilities(false, affectedCards); boolean checkAgain = false; for (Player p : game.getPlayers()) { @@ -690,7 +705,7 @@ public class GameAction { } List noRegCreats = null; List desCreats = null; - for (Card c : game.getCardsIn(ZoneType.Battlefield)) { + for (final Card c : game.getCardsIn(ZoneType.Battlefield)) { if (c.isCreature()) { // Rule 704.5f - Put into grave (no regeneration) for toughness <= 0 if (c.getNetToughness() <= 0) { @@ -699,8 +714,7 @@ public class GameAction { } noRegCreats.add(c); checkAgain = true; - } - else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) { + } else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) { for (final Integer dmg : c.getReceivedDamageFromThisTurn().values()) { if (c.getNetToughness() <= dmg.intValue()) { if (desCreats == null) { @@ -775,7 +789,7 @@ public class GameAction { } } // for q=0;q<9 - TrackableObject.unfreeze(); + game.getTracker().unfreeze(); if (runEvents && !affectedCards.isEmpty()) { game.fireEvent(new GameEventCardStatsChanged(affectedCards)); diff --git a/forge-game/src/main/java/forge/game/GameEntityView.java b/forge-game/src/main/java/forge/game/GameEntityView.java index 85a7c928053..25a16dc5e7e 100644 --- a/forge-game/src/main/java/forge/game/GameEntityView.java +++ b/forge-game/src/main/java/forge/game/GameEntityView.java @@ -4,6 +4,7 @@ import forge.game.card.CardView; import forge.trackable.TrackableCollection; import forge.trackable.TrackableObject; import forge.trackable.TrackableProperty; +import forge.trackable.Tracker; public abstract class GameEntityView extends TrackableObject { public static GameEntityView get(GameEntity e) { @@ -21,8 +22,8 @@ public abstract class GameEntityView extends TrackableObject { return collection; } - protected GameEntityView(int id0) { - super(id0); + protected GameEntityView(final int id0, final Tracker tracker) { + super(id0, tracker); } public String getName() { diff --git a/forge-game/src/main/java/forge/game/GameView.java b/forge-game/src/main/java/forge/game/GameView.java index 16244ded50f..7a26b9242f7 100644 --- a/forge-game/src/main/java/forge/game/GameView.java +++ b/forge-game/src/main/java/forge/game/GameView.java @@ -36,8 +36,8 @@ public class GameView extends TrackableObject { private CombatView combatView; private final Game game; //TODO: Remove this when possible before network support added - public GameView(Game game0) { - super(-1); //ID not needed + public GameView(final Game game0) { + super(-1, game0.getTracker()); //ID not needed currentGame = this; game = game0; set(TrackableProperty.WinningTeam, -1); @@ -138,7 +138,7 @@ public class GameView extends TrackableObject { return; } - combatView = new CombatView(); + combatView = new CombatView(combat.getAttackingPlayer().getGame().getTracker()); for (final AttackingBand b : combat.getAttackingBands()) { if (b == null) continue; final GameEntity defender = combat.getDefenderByAttacker(b); diff --git a/forge-game/src/main/java/forge/game/StaticEffect.java b/forge-game/src/main/java/forge/game/StaticEffect.java index e15a9cbbc1a..69b896c90d1 100644 --- a/forge-game/src/main/java/forge/game/StaticEffect.java +++ b/forge-game/src/main/java/forge/game/StaticEffect.java @@ -18,14 +18,23 @@ package forge.game; import forge.game.card.Card; +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; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; import java.util.Map; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + /** *

* StaticEffect class. @@ -35,45 +44,45 @@ import java.util.Map; * @version $Id$ */ public class StaticEffect { + private final Card source; private int keywordNumber = 0; - private List affectedCards = new ArrayList(); - private ArrayList affectedPlayers = new ArrayList(); + private CardCollectionView affectedCards = new CardCollection(); + private List affectedPlayers = Lists.newArrayList(); private int xValue = 0; private int yValue = 0; private long timestamp = -1; - private HashMap xValueMap = new HashMap(); + private Map xValueMap = Maps.newTreeMap(); private String chosenType; - private Map mapParams = new HashMap(); + private Map mapParams = Maps.newTreeMap(); // for P/T - private final HashMap originalPT = new HashMap(); + private final Map originalPT = Maps.newTreeMap(); // for types private boolean overwriteTypes = false; private boolean keepSupertype = false; private boolean removeSubTypes = false; - private final HashMap> types = new HashMap>(); - private final HashMap> originalTypes = new HashMap>(); + private final Map> types = Maps.newTreeMap(); + private final Map> originalTypes = Maps.newTreeMap(); // keywords private boolean overwriteKeywords = false; - private final HashMap> originalKeywords = new HashMap>(); + private final Map> originalKeywords = Maps.newTreeMap(); // for abilities private boolean overwriteAbilities = false; - private final HashMap> originalAbilities = new HashMap>(); + private final Map> originalAbilities = Maps.newTreeMap(); // for colors private String colorDesc = ""; private boolean overwriteColors = false; - private final HashMap timestamps = new HashMap(); - public StaticEffect(Card source) { + StaticEffect(final Card source) { this.source = source; } - + /** * setTimestamp TODO Write javadoc for this method. * @@ -130,7 +139,7 @@ public class StaticEffect { */ public final void addOriginalAbilities(final Card c, final SpellAbility sa) { if (!this.originalAbilities.containsKey(c)) { - final ArrayList list = new ArrayList(); + final List list = new ArrayList(); list.add(sa); this.originalAbilities.put(c, list); } else { @@ -148,8 +157,8 @@ public class StaticEffect { * @param s * a {@link java.util.ArrayList} object. */ - public final void addOriginalAbilities(final Card c, final ArrayList s) { - final ArrayList list = new ArrayList(s); + public final void addOriginalAbilities(final Card c, final List s) { + final List list = new ArrayList(s); if (!this.originalAbilities.containsKey(c)) { this.originalAbilities.put(c, list); } else { @@ -235,7 +244,7 @@ public class StaticEffect { */ public final void addOriginalKeyword(final Card c, final String s) { if (!this.originalKeywords.containsKey(c)) { - final ArrayList list = new ArrayList(); + final List list = new ArrayList(); list.add(s); this.originalKeywords.put(c, list); } else { @@ -251,10 +260,10 @@ public class StaticEffect { * @param c * a {@link forge.game.card.Card} object. * @param s - * a {@link java.util.ArrayList} object. + * a {@link List} object. */ - public final void addOriginalKeywords(final Card c, final ArrayList s) { - final ArrayList list = new ArrayList(s); + public final void addOriginalKeywords(final Card c, final List s) { + final List list = new ArrayList(s); if (!this.originalKeywords.containsKey(c)) { this.originalKeywords.put(c, list); } else { @@ -270,10 +279,10 @@ public class StaticEffect { * * @param c * a {@link forge.game.card.Card} object. - * @return a {@link java.util.ArrayList} object. + * @return a {@link List} object. */ - public final ArrayList getOriginalKeywords(final Card c) { - final ArrayList returnList = new ArrayList(); + public final List getOriginalKeywords(final Card c) { + final List returnList = new ArrayList(); if (this.originalKeywords.containsKey(c)) { returnList.addAll(this.originalKeywords.get(c)); } @@ -450,7 +459,7 @@ public class StaticEffect { */ public final void addOriginalType(final Card c, final String s) { if (!this.originalTypes.containsKey(c)) { - final ArrayList list = new ArrayList(); + final List list = new ArrayList(); list.add(s); this.originalTypes.put(c, list); } else { @@ -468,8 +477,8 @@ public class StaticEffect { * @param s * a {@link java.util.ArrayList} object. */ - public final void addOriginalTypes(final Card c, final ArrayList s) { - final ArrayList list = new ArrayList(s); + public final void addOriginalTypes(final Card c, final List s) { + final List list = new ArrayList(s); if (!this.originalTypes.containsKey(c)) { this.originalTypes.put(c, list); } else { @@ -487,8 +496,8 @@ public class StaticEffect { * a {@link forge.game.card.Card} object. * @return a {@link java.util.ArrayList} object. */ - public final ArrayList getOriginalTypes(final Card c) { - final ArrayList returnList = new ArrayList(); + public final List getOriginalTypes(final Card c) { + final List returnList = new ArrayList(); if (this.originalTypes.containsKey(c)) { returnList.addAll(this.originalTypes.get(c)); } @@ -642,59 +651,6 @@ public class StaticEffect { this.overwriteColors = overwriteColors; } - /** - *

- * Getter for the field timestamps. - *

- * - * @return a {@link java.util.HashMap} object. - */ - public final HashMap getTimestamps() { - return this.timestamps; - } - - /** - *

- * getTimestamp. - *

- * - * @param c - * a {@link forge.game.card.Card} object. - * @return a long. - */ - public final long getTimestamp(final Card c) { - long stamp = -1; - final Long l = this.timestamps.get(c); - if (null != l) { - stamp = l.longValue(); - } - return stamp; - } - - /** - *

- * addTimestamp. - *

- * - * @param c - * a {@link forge.game.card.Card} object. - * @param timestamp - * a long. - */ - public final void addTimestamp(final Card c, final long timestamp) { - this.timestamps.put(c, Long.valueOf(timestamp)); - } - - /** - *

- * clearTimestamps. - *

- */ - public final void clearTimestamps() { - this.timestamps.clear(); - } - - /** *

* Getter for the field source. @@ -736,8 +692,8 @@ public class StaticEffect { * * @return a {@link forge.CardList} object. */ - public final List getAffectedCards() { - return this.affectedCards; + public final CardCollectionView getAffectedCards() { + return affectedCards; } /** @@ -748,8 +704,8 @@ public class StaticEffect { * @param list * a {@link forge.CardList} object. */ - public final void setAffectedCards(final List list) { - this.affectedCards = list; + public final void setAffectedCards(final CardCollectionView list) { + affectedCards = list; } /** @@ -757,7 +713,7 @@ public class StaticEffect { * * @return the affected players */ - public final ArrayList getAffectedPlayers() { + public final List getAffectedPlayers() { return this.affectedPlayers; } @@ -878,4 +834,196 @@ public class StaticEffect { return this.chosenType; } + /** + * Undo everything that was changed by this effect. + * + * @return a {@link CardCollectionView} of all affected cards. + */ + final CardCollectionView remove() { + final CardCollectionView affectedCards = getAffectedCards(); + final List affectedPlayers = getAffectedPlayers(); + final Map params = getParams(); + final Player controller = getSource().getController(); + + String changeColorWordsTo = null; + + int powerBonus = 0; + String addP = ""; + int toughnessBonus = 0; + String addT = ""; + int keywordMultiplier = 1; + boolean setPT = false; + String[] addHiddenKeywords = null; + String addColors = null; + boolean removeMayLookAt = false, removeMayPlay = false; + + if (params.containsKey("ChangeColorWordsTo")) { + changeColorWordsTo = params.get("ChangeColorWordsTo"); + } + + if (params.containsKey("SetPower") || params.containsKey("SetToughness")) { + setPT = true; + } + + if (params.containsKey("AddPower")) { + addP = params.get("AddPower"); + if (addP.matches("[0-9][0-9]?")) { + powerBonus = Integer.valueOf(addP); + } else if (addP.equals("AffectedX")) { + // gets calculated at runtime + } else { + powerBonus = getXValue(); + } + } + + if (params.containsKey("AddToughness")) { + addT = params.get("AddToughness"); + if (addT.matches("[0-9][0-9]?")) { + toughnessBonus = Integer.valueOf(addT); + } else if (addT.equals("AffectedX")) { + // gets calculated at runtime + } else { + toughnessBonus = getYValue(); + } + } + + if (params.containsKey("KeywordMultiplier")) { + String multiplier = params.get("KeywordMultiplier"); + if (multiplier.equals("X")) { + keywordMultiplier = getXValue(); + } else { + keywordMultiplier = Integer.valueOf(multiplier); + } + } + + if (params.containsKey("AddHiddenKeyword")) { + addHiddenKeywords = params.get("AddHiddenKeyword").split(" & "); + } + + if (params.containsKey("AddColor")) { + final String colors = params.get("AddColor"); + if (colors.equals("ChosenColor")) { + addColors = CardUtil.getShortColorsString(getSource().getChosenColors()); + } else { + addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split(" & ")))); + } + } + + if (params.containsKey("SetColor")) { + final String colors = params.get("SetColor"); + if (colors.equals("ChosenColor")) { + addColors = CardUtil.getShortColorsString(getSource().getChosenColors()); + } else { + addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split(" & ")))); + } + } + + if (params.containsKey("MayLookAt")) { + removeMayLookAt = true; + } + if (params.containsKey("MayPlay")) { + removeMayPlay = true; + } + + if (params.containsKey("IgnoreEffectCost")) { + for (final SpellAbility s : getSource().getSpellAbilities()) { + if (s instanceof AbilityStatic && s.isTemporary()) { + getSource().removeSpellAbility(s); + } + } + } + + // modify players + for (final Player p : affectedPlayers) { + p.setUnlimitedHandSize(false); + p.setMaxHandSize(p.getStartingHandSize()); + p.removeChangedKeywords(getTimestamp()); + } + + // modify the affected card + for (final Card affectedCard : affectedCards) { + // Gain control + if (params.containsKey("GainControl")) { + affectedCard.removeTempController(getTimestamp()); + } + + // Revert changed color words + if (changeColorWordsTo != null) { + affectedCard.removeChangedTextColorWord(getTimestamp()); + } + + // remove set P/T + if (!params.containsKey("CharacteristicDefining") && setPT) { + affectedCard.removeNewPT(getTimestamp()); + } + + // remove P/T bonus + if (addP.startsWith("AffectedX")) { + powerBonus = getXMapValue(affectedCard); + } + if (addT.startsWith("AffectedX")) { + toughnessBonus = getXMapValue(affectedCard); + } + affectedCard.addSemiPermanentPowerBoost(powerBonus * -1); + affectedCard.addSemiPermanentToughnessBoost(toughnessBonus * -1); + + // remove keywords + // TODO regular keywords currently don't try to use keyword multiplier + // (Although nothing uses it at this time) + if (params.containsKey("AddKeyword") || params.containsKey("RemoveKeyword") + || params.containsKey("RemoveAllAbilities")) { + affectedCard.removeChangedCardKeywords(getTimestamp()); + } + + // remove abilities + if (params.containsKey("AddAbility") || params.containsKey("GainsAbilitiesOf")) { + for (final SpellAbility s : affectedCard.getSpellAbilities().threadSafeIterator()) { + if (s.isTemporary()) { + affectedCard.removeSpellAbility(s); + } + } + } + + if (addHiddenKeywords != null) { + for (final String k : addHiddenKeywords) { + for (int j = 0; j < keywordMultiplier; j++) { + affectedCard.removeHiddenExtrinsicKeyword(k); + } + } + } + + // 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); + } + } + + // remove Types + if (params.containsKey("AddType") || params.containsKey("RemoveType")) { + affectedCard.removeChangedCardTypes(getTimestamp()); + } + + // remove colors + if (addColors != null) { + affectedCard.removeColor(getTimestamp()); + } + + // remove may look at + if (removeMayLookAt) { + affectedCard.setMayLookAt(controller, false); + } + if (removeMayPlay) { + affectedCard.removeMayPlay(controller); + } + } + return affectedCards; + } + } // end class StaticEffect diff --git a/forge-game/src/main/java/forge/game/StaticEffects.java b/forge-game/src/main/java/forge/game/StaticEffects.java index fb685efebf0..2e942760ae4 100644 --- a/forge-game/src/main/java/forge/game/StaticEffects.java +++ b/forge-game/src/main/java/forge/game/StaticEffects.java @@ -17,19 +17,14 @@ */ package forge.game; -import java.util.ArrayList; -import java.util.Arrays; import java.util.EnumSet; -import java.util.List; import java.util.Map; import java.util.Set; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; + import forge.game.card.Card; -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; /** @@ -43,7 +38,7 @@ import forge.game.staticability.StaticAbility; public class StaticEffects { // **************** StaticAbility system ************************** - private final List staticEffects = new ArrayList(); + private final Map staticEffects = Maps.newHashMap(); //Global rule changes private final Set ruleChanges = EnumSet.noneOf(GlobalRuleChange.class); @@ -51,17 +46,17 @@ public class StaticEffects { ruleChanges.clear(); // remove all static effects - for (final StaticEffect se : staticEffects) { - affectedCards.addAll(removeStaticEffect(se)); + for (final StaticEffect se : staticEffects.values()) { + Iterables.addAll(affectedCards, se.remove()); } this.staticEffects.clear(); } - public void setGlobalRuleChange(GlobalRuleChange change) { + public void setGlobalRuleChange(final GlobalRuleChange change) { this.ruleChanges.add(change); } - public boolean getGlobalRuleChange(GlobalRuleChange change) { + public boolean getGlobalRuleChange(final GlobalRuleChange change) { return this.ruleChanges.contains(change); } @@ -71,202 +66,15 @@ public class StaticEffects { * @param staticEffect * a {@link StaticEffect}. */ - public final void addStaticEffect(final StaticEffect staticEffect) { - this.staticEffects.add(staticEffect); + public final StaticEffect getStaticEffect(final StaticAbility staticAbility) { + final StaticEffect currentEffect = staticEffects.get(staticAbility); + if (currentEffect != null) { + return currentEffect; + } + + final StaticEffect newEffect = new StaticEffect(staticAbility.getHostCard()); + this.staticEffects.put(staticAbility, newEffect); + return newEffect; } - /** - * Remove a static effect from the list of static effects and undo everything that was changed by the effect. - * - * @param se - * a {@link StaticEffect}. - */ - private static final List removeStaticEffect(final StaticEffect se) { - final List affectedCards = se.getAffectedCards(); - final ArrayList affectedPlayers = se.getAffectedPlayers(); - final Map params = se.getParams(); - final Player controller = se.getSource().getController(); - - String changeColorWordsTo = null; - - int powerBonus = 0; - String addP = ""; - int toughnessBonus = 0; - String addT = ""; - int keywordMultiplier = 1; - boolean setPT = false; - String[] addHiddenKeywords = null; - String addColors = null; - boolean removeMayLookAt = false, removeMayPlay = false; - - if (params.containsKey("ChangeColorWordsTo")) { - changeColorWordsTo = params.get("ChangeColorWordsTo"); - } - - if (params.containsKey("SetPower") || params.containsKey("SetToughness")) { - setPT = true; - } - - if (params.containsKey("AddPower")) { - addP = params.get("AddPower"); - if (addP.matches("[0-9][0-9]?")) { - powerBonus = Integer.valueOf(addP); - } else if (addP.equals("AffectedX")) { - // gets calculated at runtime - } else { - powerBonus = se.getXValue(); - } - } - - if (params.containsKey("AddToughness")) { - addT = params.get("AddToughness"); - if (addT.matches("[0-9][0-9]?")) { - toughnessBonus = Integer.valueOf(addT); - } else if (addT.equals("AffectedX")) { - // gets calculated at runtime - } else { - toughnessBonus = se.getYValue(); - } - } - - if (params.containsKey("KeywordMultiplier")) { - String multiplier = params.get("KeywordMultiplier"); - if (multiplier.equals("X")) { - keywordMultiplier = se.getXValue(); - } else { - keywordMultiplier = Integer.valueOf(multiplier); - } - } - - if (params.containsKey("AddHiddenKeyword")) { - addHiddenKeywords = params.get("AddHiddenKeyword").split(" & "); - } - - if (params.containsKey("AddColor")) { - final String colors = params.get("AddColor"); - if (colors.equals("ChosenColor")) { - addColors = CardUtil.getShortColorsString(se.getSource().getChosenColors()); - } else { - addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split(" & ")))); - } - } - - if (params.containsKey("SetColor")) { - final String colors = params.get("SetColor"); - if (colors.equals("ChosenColor")) { - addColors = CardUtil.getShortColorsString(se.getSource().getChosenColors()); - } else { - addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split(" & ")))); - } - } - - if (params.containsKey("MayLookAt")) { - removeMayLookAt = true; - } - if (params.containsKey("MayPlay")) { - removeMayPlay = true; - } - - if (params.containsKey("IgnoreEffectCost")) { - for (final SpellAbility s : se.getSource().getSpellAbilities()) { - if (s instanceof AbilityStatic && s.isTemporary()) { - se.getSource().removeSpellAbility(s); - } - } - } - - // modify players - for (final Player p : affectedPlayers) { - p.setUnlimitedHandSize(false); - p.setMaxHandSize(p.getStartingHandSize()); - p.removeChangedKeywords(se.getTimestamp()); - } - - // modify the affected card - for (final Card affectedCard : affectedCards) { - // Gain control - if (params.containsKey("GainControl")) { - affectedCard.removeTempController(se.getTimestamp()); - } - - // Revert changed color words - if (changeColorWordsTo != null) { - affectedCard.removeChangedTextColorWord(se.getTimestamp()); - } - - // remove set P/T - if (!params.containsKey("CharacteristicDefining") && setPT) { - affectedCard.removeNewPT(se.getTimestamp()); - } - - // remove P/T bonus - if (addP.startsWith("AffectedX")) { - powerBonus = se.getXMapValue(affectedCard); - } - if (addT.startsWith("AffectedX")) { - toughnessBonus = se.getXMapValue(affectedCard); - } - affectedCard.addSemiPermanentPowerBoost(powerBonus * -1); - affectedCard.addSemiPermanentToughnessBoost(toughnessBonus * -1); - - // remove keywords - // TODO regular keywords currently don't try to use keyword multiplier - // (Although nothing uses it at this time) - if (params.containsKey("AddKeyword") || params.containsKey("RemoveKeyword") - || params.containsKey("RemoveAllAbilities")) { - affectedCard.removeChangedCardKeywords(se.getTimestamp()); - } - - // remove abilities - if (params.containsKey("AddAbility") || params.containsKey("GainsAbilitiesOf")) { - for (final SpellAbility s : affectedCard.getSpellAbilities().threadSafeIterator()) { - if (s.isTemporary()) { - affectedCard.removeSpellAbility(s); - } - } - } - - if (addHiddenKeywords != null) { - for (final String k : addHiddenKeywords) { - for (int j = 0; j < keywordMultiplier; j++) { - affectedCard.removeHiddenExtrinsicKeyword(k); - } - } - } - - // 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); - } - } - - // remove Types - if (params.containsKey("AddType") || params.containsKey("RemoveType")) { - affectedCard.removeChangedCardTypes(se.getTimestamp()); - } - - // remove colors - if (addColors != null) { - affectedCard.removeColor(addColors, affectedCard, !se.isOverwriteColors(), - se.getTimestamp(affectedCard)); - } - - // remove may look at - if (removeMayLookAt) { - affectedCard.setMayLookAt(controller, false); - } - if (removeMayPlay) { - affectedCard.removeMayPlay(controller); - } - } - se.clearTimestamps(); - return affectedCards; - } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java index 90ae3fc7fa9..c45e2d86926 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java @@ -145,8 +145,8 @@ public class AnimateAllEffect extends AnimateEffectBase { list = CardLists.getValidCards(list, valid.split(","), host.getController(), host); for (final Card c : list) { - final long colorTimestamp = doAnimate(c, sa, power, toughness, types, removeTypes, - finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp); + doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc, + keywords, removeKeywords, hiddenKeywords, timestamp); // give abilities final ArrayList addedAbilities = new ArrayList(); @@ -232,8 +232,9 @@ public class AnimateAllEffect extends AnimateEffectBase { @Override public void run() { - doUnanimate(c, sa, finalDesc, hiddenKeywords, addedAbilities, addedTriggers, addedReplacements, - colorTimestamp, false, removedAbilities, timestamp); + doUnanimate(c, sa, finalDesc, hiddenKeywords, + addedAbilities, addedTriggers, addedReplacements, + false, removedAbilities, timestamp); // give back suppressed triggers for (final Trigger t : removedTriggers) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java index 6b3169e5c31..9ef87d293c8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java @@ -80,17 +80,17 @@ public class AnimateEffect extends AnimateEffectBase { types.add(source.getChosenType()); } - final ArrayList keywords = new ArrayList(); + final List keywords = new ArrayList(); if (sa.hasParam("Keywords")) { keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); } - final ArrayList removeKeywords = new ArrayList(); + final List removeKeywords = new ArrayList(); if (sa.hasParam("RemoveKeywords")) { removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & "))); } - final ArrayList hiddenKeywords = new ArrayList(); + final List hiddenKeywords = new ArrayList(); if (sa.hasParam("HiddenKeywords")) { hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & "))); } @@ -117,31 +117,31 @@ public class AnimateEffect extends AnimateEffectBase { final String finalDesc = tmpDesc; // abilities to add to the animated being - final ArrayList abilities = new ArrayList(); + final List abilities = new ArrayList(); if (sa.hasParam("Abilities")) { abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(","))); } // replacement effects to add to the animated being - final ArrayList replacements = new ArrayList(); + final List replacements = new ArrayList(); if (sa.hasParam("Replacements")) { replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(","))); } // triggers to add to the animated being - final ArrayList triggers = new ArrayList(); + final List triggers = new ArrayList(); if (sa.hasParam("Triggers")) { triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(","))); } // static abilities to add to the animated being - final ArrayList stAbs = new ArrayList(); + final List stAbs = new ArrayList(); if (sa.hasParam("staticAbilities")) { stAbs.addAll(Arrays.asList(sa.getParam("staticAbilities").split(","))); } // sVars to add to the animated being - final ArrayList sVars = new ArrayList(); + final List sVars = new ArrayList(); if (sa.hasParam("sVars")) { sVars.addAll(Arrays.asList(sa.getParam("sVars").split(","))); } @@ -149,11 +149,11 @@ public class AnimateEffect extends AnimateEffectBase { List tgts = getTargetCards(sa); for (final Card c : tgts) { - final long colorTimestamp = doAnimate(c, sa, power, toughness, types, removeTypes, - finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp); + doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc, + keywords, removeKeywords, hiddenKeywords, timestamp); // remove abilities - final ArrayList removedAbilities = new ArrayList(); + final List removedAbilities = new ArrayList(); boolean clearAbilities = sa.hasParam("OverwriteAbilities"); boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean removeAll = sa.hasParam("RemoveAllAbilities"); @@ -174,7 +174,7 @@ public class AnimateEffect extends AnimateEffectBase { } // give abilities - final ArrayList addedAbilities = new ArrayList(); + final List addedAbilities = new ArrayList(); if (abilities.size() > 0) { for (final String s : abilities) { final String actualAbility = source.getSVar(s); @@ -185,7 +185,7 @@ public class AnimateEffect extends AnimateEffectBase { } // Grant triggers - final ArrayList addedTriggers = new ArrayList(); + final List addedTriggers = new ArrayList(); if (triggers.size() > 0) { for (final String s : triggers) { final String actualTrigger = source.getSVar(s); @@ -195,7 +195,7 @@ public class AnimateEffect extends AnimateEffectBase { } // give replacement effects - final ArrayList addedReplacements = new ArrayList(); + final List addedReplacements = new ArrayList(); if (replacements.size() > 0) { for (final String s : replacements) { final String actualReplacement = source.getSVar(s); @@ -205,7 +205,7 @@ public class AnimateEffect extends AnimateEffectBase { } // suppress triggers from the animated card - final ArrayList removedTriggers = new ArrayList(); + final List removedTriggers = new ArrayList(); if (sa.hasParam("OverwriteTriggers") || removeAll) { final FCollectionView triggersToRemove = c.getTriggers(); for (final Trigger trigger : triggersToRemove) { @@ -238,7 +238,7 @@ public class AnimateEffect extends AnimateEffectBase { } // suppress static abilities from the animated card - final ArrayList removedStatics = new ArrayList(); + final List removedStatics = new ArrayList(); if (sa.hasParam("OverwriteStatics") || removeAll) { final FCollectionView staticsToRemove = c.getStaticAbilities(); for (final StaticAbility stAb : staticsToRemove) { @@ -248,7 +248,7 @@ public class AnimateEffect extends AnimateEffectBase { } // suppress static abilities from the animated card - final ArrayList removedReplacements = new ArrayList(); + final List removedReplacements = new ArrayList(); if (sa.hasParam("OverwriteReplacements") || removeAll) { for (final ReplacementEffect re : c.getReplacementEffects()) { re.setTemporarilySuppressed(true); @@ -270,8 +270,9 @@ public class AnimateEffect extends AnimateEffectBase { @Override public void run() { - doUnanimate(c, sa, finalDesc, hiddenKeywords, addedAbilities, addedTriggers, addedReplacements, - colorTimestamp, givesStAbs, removedAbilities, timestamp); + doUnanimate(c, sa, finalDesc, hiddenKeywords, + addedAbilities, addedTriggers, addedReplacements, + givesStAbs, removedAbilities, timestamp); game.fireEvent(new GameEventCardStatsChanged(c)); // give back suppressed triggers @@ -354,11 +355,11 @@ public class AnimateEffect extends AnimateEffectBase { } final boolean permanent = sa.hasParam("Permanent"); - final ArrayList types = new ArrayList(); + final List types = new ArrayList(); if (sa.hasParam("Types")) { types.addAll(Arrays.asList(sa.getParam("Types").split(","))); } - final ArrayList keywords = new ArrayList(); + final List keywords = new ArrayList(); if (sa.hasParam("Keywords")) { keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); } @@ -370,7 +371,7 @@ public class AnimateEffect extends AnimateEffectBase { keywords.remove(k); } } - final ArrayList colors = new ArrayList(); + final List colors = new ArrayList(); if (sa.hasParam("Colors")) { colors.addAll(Arrays.asList(sa.getParam("Colors").split(","))); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java index 1a969ce817d..b262b266189 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java @@ -26,12 +26,13 @@ import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; import java.util.ArrayList; +import java.util.List; public abstract class AnimateEffectBase extends SpellAbilityEffect { - long doAnimate(final Card c, final SpellAbility sa, final int power, final int toughness, + void doAnimate(final Card c, final SpellAbility sa, final int power, final int toughness, final CardType addType, final CardType removeType, final String colors, - final ArrayList keywords, final ArrayList removeKeywords, - final ArrayList hiddenKeywords, final long timestamp) { + final List keywords, final List removeKeywords, + final List hiddenKeywords, final long timestamp) { boolean removeSuperTypes = false; boolean removeCardTypes = false; @@ -84,8 +85,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { c.addHiddenExtrinsicKeyword(k); } - final long colorTimestamp = c.addColor(colors, !sa.hasParam("OverwriteColors"), true); - return colorTimestamp; + c.addColor(colors, !sa.hasParam("OverwriteColors"), timestamp); } /** @@ -113,10 +113,9 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { * a long. */ void doUnanimate(final Card c, SpellAbility sa, final String colorDesc, - final ArrayList hiddenKeywords, final ArrayList addedAbilities, - final ArrayList addedTriggers, final ArrayList addedReplacements, - final long colorTimestamp, final boolean givesStAbs, - final ArrayList removedAbilities, final long timestamp) { + final List hiddenKeywords, final List addedAbilities, + final List addedTriggers, final List addedReplacements, + final boolean givesStAbs, final List removedAbilities, final long timestamp) { c.removeNewPT(timestamp); @@ -132,7 +131,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { c.removeChangedCardTypes(timestamp); } - c.removeColor(colorDesc, c, !sa.hasParam("OverwriteColors"), colorTimestamp); + c.removeColor(timestamp); for (final String k : hiddenKeywords) { c.removeHiddenExtrinsicKeyword(k); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index a38dec8ffaa..0a2c25b7eea 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -402,9 +402,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect { hostCard.clearRemembered(); } - boolean optional = sa.hasParam("Optional"); - - long ts = game.getNextTimestamp(); + final boolean optional = sa.hasParam("Optional"); + final long ts = game.getNextTimestamp(); for (final Card tgtC : tgtCards) { if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java index 4e36fe688d5..515987fd7ba 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java @@ -290,7 +290,7 @@ public class CloneEffect extends SpellAbilityEffect { shortColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(","))); } } - tgtCard.addColor(shortColors, !sa.hasParam("OverwriteColors"), true); + tgtCard.addColor(shortColors, !sa.hasParam("OverwriteColors"), tgtCard.getTimestamp()); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index 62cf0c75773..e6c248b3ea9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -112,7 +112,7 @@ public class EffectEffect extends SpellAbilityEffect { eff.setToken(true); // Set token to true, so when leaving play it gets nuked eff.setOwner(controller); eff.setImageKey(sa.hasParam("Image") ? ImageKeys.getTokenKey(sa.getParam("Image")) : hostCard.getImageKey()); - eff.setColor(hostCard.getColor()); + eff.setColor(hostCard.determineColor().getColor()); eff.setImmutable(true); eff.setEffectSource(hostCard); 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 6c024b63aae..92e9966a510 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -271,7 +271,7 @@ public class Card extends GameEntity implements Comparable { game.addCard(id0, this); } paperCard = paperCard0; - view = new CardView(id0); + view = new CardView(id0, game == null ? null : game.getTracker()); currentState = new CardState(view.getCurrentState(), this); states.put(CardStateName.Original, currentState); states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this)); @@ -1080,44 +1080,19 @@ public class Card extends GameEntity implements Comparable { return currentState.getManaCost(); } - public final void addColor(String s) { - if (s.equals("")) { - s = "0"; - } - ManaCost mc = new ManaCost(new ManaCostParser(s)); - currentState.getCardColor().add(new CardColor(mc.getColorProfile())); + public final void addColor(final String s, final boolean addToColors, final long timestamp) { + currentState.addColor(s, addToColors, timestamp); } - public final long addColor(final String s, final boolean addToColors, final boolean bIncrease) { - if (bIncrease) { - CardColor.increaseTimestamp(); - } - currentState.getCardColor().add(new CardColor(s, addToColors)); - currentState.getView().updateColors(this); - return CardColor.getTimestamp(); + public final void removeColor(final long timestamp) { + currentState.removeColor(timestamp); } - public final void removeColor(final String s, final Card c, final boolean addTo, final long timestampIn) { - CardColor removeCol = null; - for (final CardColor cc : currentState.getCardColor()) { - if (cc.equals(s, c, addTo, timestampIn)) { - removeCol = cc; - } - } - - if (removeCol != null) { - currentState.getCardColor().remove(removeCol); - currentState.getView().updateColors(this); - } + public final void setColor(final byte color) { + currentState.setColor(new CardColor(color)); } - - public final void setColor(final Iterable colors) { - currentState.setCardColor(colors); - currentState.getView().updateColors(this); - } - - public final Iterable getColor() { - return currentState.getCardColor(); + public final void setColor(final String color) { + currentState.setColor(new CardColor(color)); } public final ColorSet determineColor() { @@ -2441,7 +2416,7 @@ public class Card extends GameEntity implements Comparable { } public Map getChangedCardTypes() { - return changedCardTypes; + return Collections.unmodifiableMap(changedCardTypes); } public final void addChangedCardTypes(final CardType addType, final CardType removeType, @@ -3212,14 +3187,10 @@ public class Card extends GameEntity implements Comparable { /** {@inheritDoc} */ @Override public final String toString() { - String name = "Morph"; - if (!isFaceDown()) { - name = getName(); - if (StringUtils.isEmpty(name) && paperCard != null) { - name = paperCard.getName(); //make it possible to see likely card name before it's set - } + if (getView() == null) { + return getPaperCard().getName(); } - return name + " (" + id + ")"; + return getView().toString(); } public final boolean isUnearthed() { @@ -6273,25 +6244,16 @@ public class Card extends GameEntity implements Comparable { view.updateCommander(this); } - public void setSplitStateToPlayAbility(SpellAbility sa) { - if (!isSplitCard()) { return; } // just in case + public void setSplitStateToPlayAbility(final SpellAbility sa) { + if (!isSplitCard()) { + return; // just in case + } // Split card support - for (SpellAbility a : getState(CardStateName.LeftSplit).getNonManaAbilities()) { - if (sa == a || sa.getDescription().equals(String.format("%s (without paying its mana cost)", a.getDescription()))) { - setState(CardStateName.LeftSplit, true); - return; - } + if (sa.isLeftSplit()) { + setState(CardStateName.LeftSplit, true); + } else if (sa.isRightSplit()) { + setState(CardStateName.RightSplit, true); } - for (SpellAbility a : getState(CardStateName.RightSplit).getNonManaAbilities()) { - if (sa == a || sa.getDescription().equals(String.format("%s (without paying its mana cost)", a.getDescription()))) { - setState(CardStateName.RightSplit, true); - return; - } - } - if (sa.getHostCard().hasKeyword("Fuse")) { // it's ok that such card won't change its side - return; - } - throw new RuntimeException("Not found which part to choose for ability " + sa + " from card " + this); } // Optional costs paid diff --git a/forge-game/src/main/java/forge/game/card/CardColor.java b/forge-game/src/main/java/forge/game/card/CardColor.java index 0aeeb190b29..2a06918865e 100644 --- a/forge-game/src/main/java/forge/game/card/CardColor.java +++ b/forge-game/src/main/java/forge/game/card/CardColor.java @@ -30,82 +30,35 @@ import forge.card.mana.ManaCostParser; * @version $Id$ */ public class CardColor { - private static long timeStamp = 0; - public static long getTimestamp() { return CardColor.timeStamp; } - static void increaseTimestamp() { CardColor.timeStamp++; } - - // takes care of individual card color, for global color change effects use - // AllZone.getGameInfo().getColorChanges() private final byte colorMask; - public final byte getColorMask() { return colorMask; } + public final byte getColorMask() { + return colorMask; + } private final boolean additional; public final boolean isAdditional() { return this.additional; } - private long stamp = 0; - - /** - *

- * Getter for the field stamp. - *

- * - * @return a long. - */ - public final long getStamp() { - return this.stamp; + private final long timestamp; + public final long getTimestamp() { + return this.timestamp; } - /** - *

- * Constructor for Card_Color. - *

- * - * @param mc - * a {@link forge.game.mana.ManaCostBeingPaid} object. - * @param c - * a {@link forge.game.card.Card} object. - * @param addToColors - * a boolean. - * @param baseColor - * a boolean. - */ - CardColor(final String colors, final boolean addToColors) { - this.additional = addToColors; - ManaCost mc = new ManaCost(new ManaCostParser(colors)); + CardColor(final String colors) { + this(colors, false, 0L); + } + CardColor(final String colors, final boolean addToColors, final long timestamp) { + final ManaCost mc = new ManaCost(new ManaCostParser(colors)); this.colorMask = mc.getColorProfile(); - this.stamp = CardColor.timeStamp; + this.additional = addToColors; + this.timestamp = timestamp; } - public CardColor(byte mask) { + CardColor(final byte mask) { this.colorMask = mask; this.additional = false; - this.stamp = 0; - } - - - public CardColor() { - this((byte)0); - } - - /** - *

- * equals. - *

- * - * @param cost - * a {@link java.lang.String} object. - * @param c - * a {@link forge.game.card.Card} object. - * @param addToColors - * a boolean. - * @param time - * a long. - * @return a boolean. - */ - public final boolean equals(final String cost, final Card c, final boolean addToColors, final long time) { - return (addToColors == this.additional) && (this.stamp == time); + this.timestamp = 0; } public final ColorSet toColorSet() { diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 6d6ae8f0d74..7675cbbff71 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -163,7 +163,7 @@ public class CardFactory { } final String finalColors = tmp; - c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), true); + c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), c.getTimestamp()); } c.clearControllers(); @@ -288,7 +288,14 @@ public class CardFactory { } if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) { - CardState original = card.getState(CardStateName.Original); + for (final SpellAbility sa : card.getSpellAbilities()) { + if (state == CardStateName.LeftSplit) { + sa.setLeftSplit(); + } else { + sa.setRightSplit(); + } + } + final CardState original = card.getState(CardStateName.Original); original.addNonManaAbilities(card.getCurrentState().getNonManaAbilities()); original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywords()); // Copy 'Fuse' to original side original.getSVars().putAll(card.getCurrentState().getSVars()); // Unfortunately need to copy these to (Effect looks for sVars on execute) @@ -306,7 +313,7 @@ public class CardFactory { else if (card.isPlane()) { buildPlaneAbilities(card); } - CardFactoryUtil.setupKeywordedAbilities(card); + CardFactoryUtil.setupKeywordedAbilities(card); // Should happen AFTER setting left/right split abilities to set Fuse ability to both sides card.getView().updateState(card); } @@ -379,11 +386,8 @@ public class CardFactory { card.setManaCost(combinedManaCost); // Combined card color - int combinedColor = rules.getMainPart().getColor().getColor() | rules.getOtherPart().getColor().getColor(); - CardColor combinedCardColor = new CardColor((byte)combinedColor); - ArrayList combinedCardColorArr = new ArrayList(); - combinedCardColorArr.add(combinedCardColor); - card.setColor(combinedCardColorArr); + final byte combinedColor = (byte) (rules.getMainPart().getColor().getColor() | rules.getOtherPart().getColor().getColor()); + card.setColor(combinedColor); card.setType(new CardType(rules.getType())); // Combined text based on Oracle text - might not be necessary, temporarily disabled. @@ -411,11 +415,7 @@ public class CardFactory { // Super and 'middle' types should use enums. c.setType(new CardType(face.getType())); - // What a perverted color code we have! - CardColor col1 = new CardColor(face.getColor().getColor()); - ArrayList ccc = new ArrayList(); - ccc.add(col1); - c.setColor(ccc); + c.setColor(face.getColor().getColor()); if (face.getIntPower() >= 0) { c.setBasePower(face.getIntPower()); @@ -657,7 +657,7 @@ public class CardFactory { // TODO - most tokens mana cost is 0, this needs to be fixed // c.setManaCost(manaCost); - c.addColor(manaCost); + c.setColor(manaCost); c.setToken(true); for (final String t : types) { 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 15b4a5ac3fa..52bf88dc358 100644 --- a/forge-game/src/main/java/forge/game/card/CardState.java +++ b/forge-game/src/main/java/forge/game/card/CardState.java @@ -17,8 +17,17 @@ */ package forge.game.card; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import forge.card.CardEdition; import forge.card.CardRarity; @@ -34,19 +43,11 @@ import forge.game.trigger.Trigger; import forge.util.FCollection; import forge.util.FCollectionView; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.commons.lang3.StringUtils; - - public class CardState { private String name = ""; private CardType type = new CardType(); private ManaCost manaCost = ManaCost.NO_COST; - private List cardColor = new ArrayList(); + private final SortedMap cardColor = Maps.newTreeMap(); private int basePower = 0; private int baseToughness = 0; private List intrinsicKeywords = new ArrayList(); @@ -117,27 +118,49 @@ public class CardState { view.updateManaCost(this); } - public final List getCardColor() { - return cardColor; + public final void addColor(final CardColor color) { + cardColor.put(color.getTimestamp(), color); + getView().updateColors(this); } - public final void setCardColor(final Iterable cardColor0) { - cardColor = Lists.newArrayList(cardColor0); + + public final void addColor(final String s, final boolean addToColors, final long timestamp) { + addColor(new CardColor(s, addToColors, timestamp)); + } + + public final void removeColor(final long timestampIn) { + final CardColor removeCol = cardColor.remove(timestampIn); + + if (removeCol != null) { + getView().updateColors(this); + } + } + + public final void setColor(final CardColor color) { + setColor(ImmutableMap.of(color.getTimestamp(), color)); + } + private final void setColor(final Map cardColor0) { + cardColor.clear(); + cardColor.putAll(cardColor0); view.updateColors(this); } public final void resetCardColor() { - if (cardColor.isEmpty()) { return; } - - cardColor = Lists.newArrayList(cardColor.subList(0, 1)); + if (cardColor.isEmpty()) { + return; + } + final Long firstKey = cardColor.firstKey(); + final CardColor first = cardColor.get(firstKey); + cardColor.clear(); + cardColor.put(firstKey, first); view.updateColors(this); } public final ColorSet determineColor() { - final List colorList = getCardColor(); + final Iterable colorList = cardColor.values(); byte colors = 0; - for (int i = colorList.size() - 1;i >= 0;i--) { - final CardColor cc = colorList.get(i); - colors |= cc.getColorMask(); - if (!cc.isAdditional()) { - return ColorSet.fromMask(colors); + for (final CardColor cc : colorList) { + if (cc.isAdditional()) { + colors |= cc.getColorMask(); + } else { + colors = cc.getColorMask(); } } return ColorSet.fromMask(colors); @@ -366,7 +389,7 @@ public class CardState { setName(source.getName()); setType(source.type); setManaCost(source.getManaCost()); - setCardColor(source.getCardColor()); + setColor(source.cardColor); setBasePower(source.getBasePower()); setBaseToughness(source.getBaseToughness()); intrinsicKeywords = new ArrayList(source.intrinsicKeywords); diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index eeb1dcdaa2a..a62b99115ff 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -240,10 +240,7 @@ public final class CardUtil { newCopy.setCounters(in.getCounters()); newCopy.setExtrinsicKeyword(in.getExtrinsicKeyword()); - // Determine the color for LKI copy, not just getColor - ArrayList currentColor = new ArrayList(); - currentColor.add(new CardColor(in.determineColor().getColor())); - newCopy.setColor(currentColor); + newCopy.setColor(in.determineColor().getColor()); newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn()); newCopy.setEnchanting(in.getEnchanting()); diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 664e033a500..ddc24b074ac 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -28,6 +28,7 @@ import forge.item.IPaperCard; import forge.trackable.TrackableCollection; import forge.trackable.TrackableObject; import forge.trackable.TrackableProperty; +import forge.trackable.Tracker; import forge.util.FCollectionView; public class CardView extends GameEntityView { @@ -68,20 +69,20 @@ public class CardView extends GameEntityView { return false; } - public CardView(int id0) { - super(id0); - set(TrackableProperty.CurrentState, new CardStateView(id0, CardStateName.Original)); + public CardView(final int id0, final Tracker tracker) { + super(id0, tracker); + set(TrackableProperty.CurrentState, new CardStateView(id0, CardStateName.Original, tracker)); } - public CardView(int id0, String name0) { - this(id0); + public CardView(final int id0, final Tracker tracker, final String name0) { + this(id0, tracker); getCurrentState().setName(name0); set(TrackableProperty.Name, name0); set(TrackableProperty.ChangedColorWords, new HashMap()); set(TrackableProperty.ChangedTypes, new HashMap()); set(TrackableProperty.Sickness, true); } - public CardView(int id0, String name0, PlayerView ownerAndController, String imageKey) { - this(id0, name0); + public CardView(final int id0, final Tracker tracker, final String name0, final PlayerView ownerAndController, final String imageKey) { + this(id0, tracker, name0); set(TrackableProperty.Owner, ownerAndController); set(TrackableProperty.Controller, ownerAndController); set(TrackableProperty.ImageKey, imageKey); @@ -632,8 +633,8 @@ public class CardView extends GameEntityView { public CardStateView getAlternateState() { return get(TrackableProperty.AlternateState); } - CardStateView createAlternateState(CardStateName state0) { - return new CardStateView(getId(), state0); + CardStateView createAlternateState(final CardStateName state0) { + return new CardStateView(getId(), state0, tracker); } public CardStateView getState(final boolean alternate0) { @@ -716,8 +717,8 @@ public class CardView extends GameEntityView { public class CardStateView extends TrackableObject { private final CardStateName state; - public CardStateView(int id0, CardStateName state0) { - super(id0); + public CardStateView(final int id0, final CardStateName state0, final Tracker tracker) { + super(id0, tracker); state = state0; } diff --git a/forge-game/src/main/java/forge/game/combat/CombatView.java b/forge-game/src/main/java/forge/game/combat/CombatView.java index 45290ce4aaf..1a7707dc246 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatView.java +++ b/forge-game/src/main/java/forge/game/combat/CombatView.java @@ -12,12 +12,13 @@ import forge.game.GameEntityView; import forge.game.card.CardView; import forge.trackable.TrackableObject; import forge.trackable.TrackableProperty; +import forge.trackable.Tracker; import forge.util.FCollection; public class CombatView extends TrackableObject { - public CombatView() { - super(-1); //ID not needed + public CombatView(final Tracker tracker) { + super(-1, tracker); //ID not needed set(TrackableProperty.AttackersWithDefenders, new HashMap()); set(TrackableProperty.AttackersWithBlockers, new HashMap>()); set(TrackableProperty.BandsWithDefenders, new HashMap, GameEntityView>()); 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 d1782bda0dd..d4c59b4159a 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -150,7 +150,7 @@ public class Player extends GameEntity implements Comparable { zones.put(z, toPut); } - view = new PlayerView(id0); + view = new PlayerView(id0, game.getTracker()); view.updateMaxHandSize(this); view.updateKeywords(this); setName(chooseName(name0)); 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 0521011ff06..c62c292b67d 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -16,6 +16,7 @@ import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.trackable.TrackableCollection; import forge.trackable.TrackableProperty; +import forge.trackable.Tracker; import forge.util.FCollectionView; @@ -35,8 +36,8 @@ public class PlayerView extends GameEntityView { return collection; } - public PlayerView(int id0) { - super(id0); + public PlayerView(final int id0, final Tracker tracker) { + super(id0, tracker); set(TrackableProperty.Mana, Maps.newHashMapWithExpectedSize(MagicColor.NUMBER_OR_COLORS + 1)); } 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 ea639f9cc59..8ddfb44f3d2 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -96,6 +96,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean morphup = false; private boolean cumulativeupkeep = false; private boolean outlast = false; + private SplitSide splitSide = null; + enum SplitSide { LEFT, RIGHT }; private int totalManaSpent = 0; /** The pay costs. */ @@ -545,6 +547,22 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit outlast = outlast0; } + public boolean isLeftSplit() { + return splitSide == SplitSide.LEFT; + } + public boolean isRightSplit() { + return splitSide == SplitSide.RIGHT; + } + public void setNoSplit() { + splitSide = null; + } + public void setLeftSplit() { + splitSide = SplitSide.LEFT; + } + public void setRightSplit() { + splitSide = SplitSide.RIGHT; + } + public SpellAbility copy() { SpellAbility clone = null; try { diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityView.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityView.java index addd7d989f1..0629875154a 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityView.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityView.java @@ -22,8 +22,8 @@ public class SpellAbilityView extends TrackableObject { return collection; } - SpellAbilityView(SpellAbility sa) { - super(sa.getId()); + SpellAbilityView(final SpellAbility sa) { + super(sa.getId(), sa.getHostCard() == null || sa.getHostCard().getGame() == null ? null : sa.getHostCard().getGame().getTracker()); updateHostCard(sa); updateDescription(sa); updatePromptIfOnlyPossibleAbility(sa); diff --git a/forge-game/src/main/java/forge/game/spellability/StackItemView.java b/forge-game/src/main/java/forge/game/spellability/StackItemView.java index b37b2cb4fd9..e8661960eea 100644 --- a/forge-game/src/main/java/forge/game/spellability/StackItemView.java +++ b/forge-game/src/main/java/forge/game/spellability/StackItemView.java @@ -25,7 +25,7 @@ public class StackItemView extends TrackableObject { } public StackItemView(SpellAbilityStackInstance si) { - super(si.getId()); + super(si.getId(), si.getSourceCard().getGame().getTracker()); updateKey(si); updateSourceTrigger(si); updateText(si); diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index 7f058943cd6..5550a2feae0 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -22,6 +22,8 @@ import forge.game.CardTraitBase; import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.card.Card; +import forge.game.card.CardCollection; +import forge.game.card.CardCollectionView; import forge.game.cost.Cost; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -30,20 +32,22 @@ import forge.game.zone.ZoneType; import forge.util.Expressions; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Maps; /** * The Class StaticAbility. */ public class StaticAbility extends CardTraitBase { - private int layer = 0; - private List ignoreEffectCards = new ArrayList(); - private List ignoreEffectPlayers = new ArrayList(); - - // ******************************************************* + private final Set layers; + private CardCollectionView ignoreEffectCards = new CardCollection(); + private final List ignoreEffectPlayers = new ArrayList(); /** *

@@ -56,8 +60,8 @@ public class StaticAbility extends CardTraitBase { * a {@link forge.game.card.Card} object. * @return a {@link java.util.HashMap} object. */ - private static HashMap parseParams(final String abString, final Card hostCard) { - final HashMap mapParameters = new HashMap(); + private static Map parseParams(final String abString, final Card hostCard) { + final Map mapParameters = Maps.newHashMap(); if (!(abString.length() > 0)) { throw new RuntimeException("StaticEffectFactory : getAbility -- abString too short in " @@ -94,58 +98,63 @@ public class StaticAbility extends CardTraitBase { return mapParameters; } - // In which layer should the ability be applied (for continuous effects - // only) /** - * Gets the layer. + * Gets the {@link Set} of {@link StaticAbilityLayer}s in which this + * {@link StaticAbility} is to be applied. * - * @return the layer + * @return the applicable layers. */ - public final int generateLayer() { - + private final Set generateLayer() { if (!this.mapParams.get("Mode").equals("Continuous")) { - return 0; + return EnumSet.noneOf(StaticAbilityLayer.class); } + final Set layers = EnumSet.noneOf(StaticAbilityLayer.class); if (this.mapParams.containsKey("GainControl")) { - return 2; + layers.add(StaticAbilityLayer.CONTROL); } if (this.mapParams.containsKey("ChangeColorWordsTo")) { - return 3; + layers.add(StaticAbilityLayer.TEXT); } if (this.mapParams.containsKey("AddType") || this.mapParams.containsKey("RemoveType") || this.mapParams.containsKey("RemoveCardTypes") || this.mapParams.containsKey("RemoveSubTypes") || this.mapParams.containsKey("RemoveSuperTypes") || this.mapParams.containsKey("RemoveCreatureTypes")) { - return 4; + layers.add(StaticAbilityLayer.TYPE); } if (this.mapParams.containsKey("AddColor") || this.mapParams.containsKey("RemoveColor") || this.mapParams.containsKey("SetColor")) { - return 5; + layers.add(StaticAbilityLayer.COLOR); } if (this.mapParams.containsKey("RemoveAllAbilities") || this.mapParams.containsKey("GainsAbilitiesOf")) { - return 6; // Layer 6 + layers.add(StaticAbilityLayer.ABILITIES1); } if (this.mapParams.containsKey("AddKeyword") || this.mapParams.containsKey("AddAbility") || this.mapParams.containsKey("AddTrigger") || this.mapParams.containsKey("RemoveTriggers") || this.mapParams.containsKey("RemoveKeyword") || this.mapParams.containsKey("AddReplacementEffects")) { - return 7; // Layer 6 (dependent) + layers.add(StaticAbilityLayer.ABILITIES2); } if (this.mapParams.containsKey("CharacteristicDefining")) { - return 8; // Layer 7a + layers.add(StaticAbilityLayer.CHARACTERISTIC); } - if (this.mapParams.containsKey("AddPower") || this.mapParams.containsKey("AddToughness") - || this.mapParams.containsKey("SetPower") || this.mapParams.containsKey("SetToughness")) { - return 9; // This is the collection of 7b and 7c + if (this.mapParams.containsKey("SetPower") || this.mapParams.containsKey("SetToughness")) { + layers.add(StaticAbilityLayer.SETPT); + } + if (this.mapParams.containsKey("AddPower") || this.mapParams.containsKey("AddToughness")) { + layers.add(StaticAbilityLayer.MODIFYPT); } - return 10; // rules change + if (layers.isEmpty()) { + return EnumSet.of(StaticAbilityLayer.RULES); + } + + return layers; } /** @@ -174,11 +183,7 @@ public class StaticAbility extends CardTraitBase { * the host */ public StaticAbility(final String params, final Card host) { - final Map parsedParams = parseParams(params, host); - this.originalMapParams.putAll(parsedParams); - this.mapParams.putAll(parsedParams); - this.hostCard = host; - this.layer = this.generateLayer(); + this(parseParams(params, host), host); } /** @@ -189,37 +194,49 @@ public class StaticAbility extends CardTraitBase { * @param host * the host */ - public StaticAbility(final Map params, final Card host) { + private StaticAbility(final Map params, final Card host) { this.originalMapParams.putAll(params); this.mapParams.putAll(params); - this.layer = this.generateLayer(); + this.layers = this.generateLayer(); this.hostCard = host; } - // apply the ability if it has the right mode + public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer) { + if (!shouldApplyContinuousAbility(layer, false)) { + return null; + } + return StaticAbilityContinuous.applyContinuousAbility(this, layer); + } + + public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer, final CardCollectionView affected) { + if (!shouldApplyContinuousAbility(layer, true)) { + return null; + } + return StaticAbilityContinuous.applyContinuousAbility(this, affected, layer); + } + /** - * Apply ability. + * Check whether a continuous ability should be applied. * - * @param mode - * the mode - * @return + * @param layer + * the {@link StaticAbilityLayer} under investigation. + * @param ignoreTempSuppression + * whether to ignore temporary suppression of this ability, to be + * used when this ability has already begun applying in another + * layer and has since been removed from its host card by another + * effect (see rule 613.5). + * @return {@code true} if and only if this is a continuous ability that + * affects the specified layer, it's not suppressed, and its + * conditions are fulfilled. */ - public final List applyAbility(final String mode) { - - // don't apply the ability if it hasn't got the right mode - if (!this.mapParams.get("Mode").equals(mode)) { - return null; + private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer, final boolean ignoreTempSuppression) { + final boolean isSuppressed; + if (ignoreTempSuppression) { + isSuppressed = this.isNonTempSuppressed(); + } else { + isSuppressed = this.isSuppressed(); } - - if (this.isSuppressed() || !this.checkConditions()) { - return null; - } - - if (mode.equals("Continuous")) { - return StaticAbilityContinuous.applyContinuousAbility(this); - } - - return null; + return mapParams.get("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed && this.checkConditions(); } // apply the ability if it has the right mode @@ -557,15 +574,15 @@ public class StaticAbility extends CardTraitBase { /** * @return the ignoreEffectCards */ - public List getIgnoreEffectCards() { + public CardCollectionView getIgnoreEffectCards() { return ignoreEffectCards; } /** * @param c the ignoreEffectCards to set */ - public void setIgnoreEffectCards(List cards) { - this.ignoreEffectCards = cards; + public void setIgnoreEffectCards(final CardCollectionView cards) { + ignoreEffectCards = cards; } /** @@ -578,27 +595,20 @@ public class StaticAbility extends CardTraitBase { /** * @param p the ignoreEffectPlayers to add */ - public void addIgnoreEffectPlayers(Player p) { - this.ignoreEffectPlayers.add(p); + public void addIgnoreEffectPlayers(final Player p) { + ignoreEffectPlayers.add(p); } public void clearIgnoreEffects() { - this.ignoreEffectPlayers.clear(); - this.ignoreEffectCards.clear(); + ignoreEffectPlayers.clear(); + ignoreEffectCards = new CardCollection(); } /** * @return the layer */ - public int getLayer() { - return layer; + public Set getLayers() { + return layers; } - /** - * @param layer the layer to set - */ - public void setLayer(int layer) { - this.layer = layer; - } - -} // end class StaticEffectFactory +} // end class StaticAbility 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 aa572a01e73..bc449e369c3 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -17,14 +17,24 @@ */ package forge.game.staticability; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import forge.GameCommand; import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.ManaCostShard; -import forge.game.*; +import forge.game.Game; +import forge.game.GlobalRuleChange; +import forge.game.StaticEffect; +import forge.game.StaticEffects; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; @@ -45,43 +55,59 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; -import org.apache.commons.lang3.StringUtils; - -import java.util.*; - /** * The Class StaticAbility_Continuous. */ -public class StaticAbilityContinuous { - - public static List applyContinuousAbility(final StaticAbility stAb) { - final List affectedCards = StaticAbilityContinuous.getAffectedCards(stAb); - return applyContinuousAbility(stAb, affectedCards); - } +public final class StaticAbilityContinuous { + + // Private constructor to prevent instantiation + private StaticAbilityContinuous() { + } /** - * - * TODO Write javadoc for this method. + * Apply the effects of a static ability that apply in a particular layer. + * The cards to which the effects are applied are dynamically determined. * * @param stAb - * a StaticAbility - * @return - * + * a {@link StaticAbility}. + * @param layer + * the {@link StaticAbilityLayer} of effects to apply. + * @return a {@link CardCollectionView} of cards that have been affected. + * @see #getAffectedCards(StaticAbility) + * @see #applyContinuousAbility(StaticAbility, CardCollectionView, + * StaticAbilityLayer) */ - public static List applyContinuousAbility(final StaticAbility stAb, List affectedCards) { + public static CardCollectionView applyContinuousAbility(final StaticAbility stAb, final StaticAbilityLayer layer) { + final CardCollectionView affectedCards = getAffectedCards(stAb); + return applyContinuousAbility(stAb, affectedCards, layer); + } + + /** + * Apply the effects of a static ability that apply in a particular layer to + * a predefined set of cards. + * + * @param stAb + * a {@link StaticAbility}. + * @param affectedCards + * a {@link CardCollectionView} of cards that are to be affected. + * @param layer + * the {@link StaticAbilityLayer} of effects to apply. + * @return a {@link CardCollectionView} of cards that have been affected, + * identical to {@code affectedCards}. + */ + public static CardCollectionView applyContinuousAbility(final StaticAbility stAb, final CardCollectionView affectedCards, final StaticAbilityLayer layer) { final Map params = stAb.getMapParams(); final Card hostCard = stAb.getHostCard(); final Player controller = hostCard.getController(); - final StaticEffect se = new StaticEffect(hostCard); final ArrayList affectedPlayers = StaticAbilityContinuous.getAffectedPlayers(stAb); final Game game = hostCard.getGame(); + final StaticEffect se = game.getStaticEffects().getStaticEffect(stAb); se.setAffectedCards(affectedCards); se.setAffectedPlayers(affectedPlayers); se.setParams(params); se.setTimestamp(hostCard.getTimestamp()); - game.getStaticEffects().addStaticEffect(se); String changeColorWordsTo = null; @@ -116,26 +142,26 @@ public class StaticAbilityContinuous { boolean controllerMayPlay = false, mayPlayWithoutManaCost = false, mayPlayIgnoreColor = false; //Global rules changes - if (params.containsKey("GlobalRule")) { + if (layer == StaticAbilityLayer.RULES && params.containsKey("GlobalRule")) { final StaticEffects effects = game.getStaticEffects(); effects.setGlobalRuleChange(GlobalRuleChange.fromString(params.get("GlobalRule"))); } - if (params.containsKey("ChangeColorWordsTo")) { + if (layer == StaticAbilityLayer.TEXT && params.containsKey("ChangeColorWordsTo")) { changeColorWordsTo = params.get("ChangeColorWordsTo"); } - if (params.containsKey("SetPower")) { + if (layer == StaticAbilityLayer.SETPT &¶ms.containsKey("SetPower")) { setP = params.get("SetPower"); setPower = AbilityUtils.calculateAmount(hostCard, setP, stAb); } - if (params.containsKey("SetToughness")) { + if (layer == StaticAbilityLayer.SETPT && params.containsKey("SetToughness")) { setT = params.get("SetToughness"); setToughness = AbilityUtils.calculateAmount(hostCard, setT, stAb); } - if (params.containsKey("AddPower")) { + if (layer == StaticAbilityLayer.MODIFYPT && params.containsKey("AddPower")) { addP = params.get("AddPower"); powerBonus = AbilityUtils.calculateAmount(hostCard, addP, stAb); if (!StringUtils.isNumeric(addP) && !addP.equals("AffectedX")) { @@ -143,7 +169,7 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("AddToughness")) { + if (layer == StaticAbilityLayer.MODIFYPT && params.containsKey("AddToughness")) { addT = params.get("AddToughness"); toughnessBonus = AbilityUtils.calculateAmount(hostCard, addT, stAb); if (!StringUtils.isNumeric(addT) && !addT.equals("AffectedX")) { @@ -151,8 +177,8 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("KeywordMultiplier")) { - String multiplier = params.get("KeywordMultiplier"); + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("KeywordMultiplier")) { + final String multiplier = params.get("KeywordMultiplier"); if (multiplier.equals("X")) { se.setXValue(AbilityUtils.calculateAmount(hostCard, "X", stAb)); } else { @@ -160,7 +186,7 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("AddKeyword")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddKeyword")) { addKeywords = params.get("AddKeyword").split(" & "); final Iterable chosencolors = hostCard.getChosenColors(); for (final String color : chosencolors) { @@ -187,19 +213,19 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("AddHiddenKeyword")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddHiddenKeyword")) { addHiddenKeywords = params.get("AddHiddenKeyword").split(" & "); } - if (params.containsKey("RemoveKeyword")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("RemoveKeyword")) { removeKeywords = params.get("RemoveKeyword").split(" & "); } - if (params.containsKey("RemoveAllAbilities")) { + if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("RemoveAllAbilities")) { removeAllAbilities = true; } - if (params.containsKey("AddAbility")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddAbility")) { final String[] sVars = params.get("AddAbility").split(" & "); for (int i = 0; i < sVars.length; i++) { sVars[i] = hostCard.getSVar(sVars[i]); @@ -207,7 +233,7 @@ public class StaticAbilityContinuous { addAbilities = sVars; } - if (params.containsKey("AddReplacementEffects")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddReplacementEffects")) { final String[] sVars = params.get("AddReplacementEffects").split(" & "); for (int i = 0; i < sVars.length; i++) { sVars[i] = hostCard.getSVar(sVars[i]); @@ -215,11 +241,11 @@ public class StaticAbilityContinuous { addReplacements = sVars; } - if (params.containsKey("AddSVar")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddSVar")) { addSVars = params.get("AddSVar").split(" & "); } - if (params.containsKey("AddType")) { + if (layer == StaticAbilityLayer.TYPE && params.containsKey("AddType")) { addTypes = params.get("AddType").split(" & "); if (addTypes[0].equals("ChosenType")) { final String chosenType = hostCard.getChosenType(); @@ -233,7 +259,7 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("RemoveType")) { + if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("RemoveType")) { removeTypes = params.get("RemoveType").split(" & "); if (removeTypes[0].equals("ChosenType")) { final String chosenType = hostCard.getChosenType(); @@ -242,60 +268,66 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("RemoveSuperTypes")) { - removeSuperTypes = true; - } + if (layer == StaticAbilityLayer.TYPE) { + if (params.containsKey("RemoveSuperTypes")) { + removeSuperTypes = true; + } - if (params.containsKey("RemoveCardTypes")) { - removeCardTypes = true; - } + if (params.containsKey("RemoveCardTypes")) { + removeCardTypes = true; + } - if (params.containsKey("RemoveSubTypes")) { - removeSubTypes = true; - } + if (params.containsKey("RemoveSubTypes")) { + removeSubTypes = true; + } - if (params.containsKey("RemoveCreatureTypes")) { - removeCreatureTypes = true; - } - - if (params.containsKey("AddColor")) { - final String colors = params.get("AddColor"); - if (colors.equals("ChosenColor")) { - addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); - } else { - addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split( - " & ")))); + if (params.containsKey("RemoveCreatureTypes")) { + removeCreatureTypes = true; } } - if (params.containsKey("SetColor")) { - final String colors = params.get("SetColor"); - if (colors.equals("ChosenColor")) { - addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); - } else { - addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList( - colors.split(" & ")))); + if (layer == StaticAbilityLayer.COLOR) { + if (params.containsKey("AddColor")) { + final String colors = params.get("AddColor"); + if (colors.equals("ChosenColor")) { + addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); + } else { + addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split( + " & ")))); + } + } + + if (params.containsKey("SetColor")) { + final String colors = params.get("SetColor"); + if (colors.equals("ChosenColor")) { + addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); + } else { + addColors = CardUtil.getShortColorsString(new ArrayList(Arrays.asList( + colors.split(" & ")))); + } + se.setOverwriteColors(true); } - se.setOverwriteColors(true); } - if (params.containsKey("AddTrigger")) { - final String[] sVars = params.get("AddTrigger").split(" & "); - for (int i = 0; i < sVars.length; i++) { - sVars[i] = hostCard.getSVar(sVars[i]); + if (layer == StaticAbilityLayer.ABILITIES2) { + if (params.containsKey("AddTrigger")) { + final String[] sVars = params.get("AddTrigger").split(" & "); + for (int i = 0; i < sVars.length; i++) { + sVars[i] = hostCard.getSVar(sVars[i]); + } + addTriggers = sVars; + } + + if (params.containsKey("AddStaticAbility")) { + final String[] sVars = params.get("AddStaticAbility").split(" & "); + for (int i = 0; i < sVars.length; i++) { + sVars[i] = hostCard.getSVar(sVars[i]); + } + addStatics = sVars; } - addTriggers = sVars; } - if (params.containsKey("AddStaticAbility")) { - final String[] sVars = params.get("AddStaticAbility").split(" & "); - for (int i = 0; i < sVars.length; i++) { - sVars[i] = hostCard.getSVar(sVars[i]); - } - addStatics = sVars; - } - - if (params.containsKey("GainsAbilitiesOf")) { + if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("GainsAbilitiesOf")) { final String[] valids = params.get("GainsAbilitiesOf").split(","); ArrayList validZones = new ArrayList(); validZones.add(ZoneType.Battlefield); @@ -326,21 +358,24 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("MayLookAt")) { - controllerMayLookAt = true; - } - if (params.containsKey("MayPlay")) { - controllerMayPlay = true; - if (params.containsKey("MayPlayWithoutManaCost")) { - mayPlayWithoutManaCost = true; - } else if (params.containsKey("MayPlayIgnoreColor")) { - mayPlayIgnoreColor = true; + if (layer == StaticAbilityLayer.RULES) { + // These fall under Rule changes, as they don't fit any other category + if (params.containsKey("MayLookAt")) { + controllerMayLookAt = true; + } + if (params.containsKey("MayPlay")) { + controllerMayPlay = true; + if (params.containsKey("MayPlayWithoutManaCost")) { + mayPlayWithoutManaCost = true; + } else if (params.containsKey("MayPlayIgnoreColor")) { + mayPlayIgnoreColor = true; + } } - } - if (params.containsKey("IgnoreEffectCost")) { - String cost = params.get("IgnoreEffectCost"); - buildIgnorEffectAbility(stAb, cost, affectedPlayers, affectedCards); + if (params.containsKey("IgnoreEffectCost")) { + String cost = params.get("IgnoreEffectCost"); + buildIgnorEffectAbility(stAb, cost, affectedPlayers, affectedCards); + } } // modify players @@ -353,24 +388,26 @@ public class StaticAbilityContinuous { } } - if (params.containsKey("SetMaxHandSize")) { - String mhs = params.get("SetMaxHandSize"); - if (mhs.equals("Unlimited")) { - p.setUnlimitedHandSize(true); - } else { - int max = AbilityUtils.calculateAmount(hostCard, mhs, stAb); - p.setMaxHandSize(max); + if (layer == StaticAbilityLayer.RULES) { + if (params.containsKey("SetMaxHandSize")) { + String mhs = params.get("SetMaxHandSize"); + if (mhs.equals("Unlimited")) { + p.setUnlimitedHandSize(true); + } else { + int max = AbilityUtils.calculateAmount(hostCard, mhs, stAb); + p.setMaxHandSize(max); + } } - } - if (params.containsKey("RaiseMaxHandSize")) { - String rmhs = params.get("RaiseMaxHandSize"); - int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb); - p.setMaxHandSize(p.getMaxHandSize() + rmax); - } + if (params.containsKey("RaiseMaxHandSize")) { + String rmhs = params.get("RaiseMaxHandSize"); + int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb); + p.setMaxHandSize(p.getMaxHandSize() + rmax); + } - if (params.containsKey("ManaColorConversion")) { - AbilityUtils.applyManaColorConversion(p, params); + if (params.containsKey("ManaColorConversion")) { + AbilityUtils.applyManaColorConversion(p, params); + } } } @@ -379,7 +416,7 @@ public class StaticAbilityContinuous { final Card affectedCard = affectedCards.get(i); // Gain control - if (params.containsKey("GainControl")) { + if (layer == StaticAbilityLayer.CONTROL && params.containsKey("GainControl")) { affectedCard.addTempController(hostCard.getController(), hostCard.getTimestamp()); } @@ -403,35 +440,39 @@ public class StaticAbilityContinuous { } // set P/T - if (params.containsKey("CharacteristicDefining")) { - if (setPower != -1) { - affectedCard.setBasePower(setPower); + if (layer == StaticAbilityLayer.SETPT) { + if (params.containsKey("CharacteristicDefining")) { + if (setPower != -1) { + affectedCard.setBasePower(setPower); + } + if (setToughness != -1) { + affectedCard.setBaseToughness(setToughness); + } + } else if ((setPower != -1) || (setToughness != -1)) { + // non CharacteristicDefining + if (setP.startsWith("AffectedX")) { + setPower = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, setP)); + } + if (setT.startsWith("AffectedX")) { + setToughness = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, setT)); + } + affectedCard.addNewPT(setPower, setToughness, hostCard.getTimestamp()); } - if (setToughness != -1) { - affectedCard.setBaseToughness(setToughness); - } - } else // non CharacteristicDefining - if ((setPower != -1) || (setToughness != -1)) { - if (setP.startsWith("AffectedX")) { - setPower = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, setP)); - } - if (setT.startsWith("AffectedX")) { - setToughness = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, setT)); - } - affectedCard.addNewPT(setPower, setToughness, hostCard.getTimestamp()); } // add P/T bonus - if (addP.startsWith("AffectedX")) { - powerBonus = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, addP)); - se.addXMapValue(affectedCard, powerBonus); + if (layer == StaticAbilityLayer.MODIFYPT) { + if (addP.startsWith("AffectedX")) { + powerBonus = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, addP)); + se.addXMapValue(affectedCard, powerBonus); + } + if (addT.startsWith("AffectedX")) { + toughnessBonus = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, addT)); + se.addXMapValue(affectedCard, toughnessBonus); + } + affectedCard.addSemiPermanentPowerBoost(powerBonus); + affectedCard.addSemiPermanentToughnessBoost(toughnessBonus); } - if (addT.startsWith("AffectedX")) { - toughnessBonus = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, addT)); - se.addXMapValue(affectedCard, toughnessBonus); - } - affectedCard.addSemiPermanentPowerBoost(powerBonus); - affectedCard.addSemiPermanentToughnessBoost(toughnessBonus); // add keywords // TODO regular keywords currently don't try to use keyword multiplier @@ -517,8 +558,7 @@ public class StaticAbilityContinuous { // add colors if (addColors != null) { - final long t = affectedCard.addColor(addColors, !se.isOverwriteColors(), true); - se.addTimestamp(affectedCard, t); + affectedCard.addColor(addColors, !se.isOverwriteColors(), hostCard.getTimestamp()); } // add triggers @@ -545,7 +585,7 @@ public class StaticAbilityContinuous { } // remove triggers - if (params.containsKey("RemoveTriggers") || removeAllAbilities) { + if (layer == StaticAbilityLayer.ABILITIES2 && (params.containsKey("RemoveTriggers") || removeAllAbilities)) { for (final Trigger trigger : affectedCard.getTriggers()) { trigger.setTemporarilySuppressed(true); } @@ -575,7 +615,7 @@ public class StaticAbilityContinuous { return affectedCards; } - private static void buildIgnorEffectAbility(final StaticAbility stAb, final String costString, final List players, final List cards) { + private static void buildIgnorEffectAbility(final StaticAbility stAb, final String costString, final List players, final CardCollectionView cards) { final List validActivator = new ArrayList(players); for (final Card c : cards) { validActivator.add(c.getController()); @@ -637,19 +677,18 @@ public class StaticAbilityContinuous { return players; } - private static List getAffectedCards(final StaticAbility stAb) { + private static CardCollectionView getAffectedCards(final StaticAbility stAb) { final Map params = stAb.getMapParams(); final Card hostCard = stAb.getHostCard(); final Game game = hostCard.getGame(); final Player controller = hostCard.getController(); if (params.containsKey("CharacteristicDefining")) { - return Lists.newArrayList(hostCard); // will always be the card itself + return new CardCollection(hostCard); // will always be the card itself } // non - CharacteristicDefining CardCollection affectedCards; - if (params.containsKey("AffectedZone")) { affectedCards = new CardCollection(game.getCardsIn(ZoneType.listValueOf(params.get("AffectedZone")))); } else { @@ -672,7 +711,7 @@ public class StaticAbilityContinuous { if (params.containsKey("Affected")) { affectedCards = CardLists.getValidCards(affectedCards, params.get("Affected").split(","), controller, hostCard); } - affectedCards.removeAll((List)stAb.getIgnoreEffectCards()); + affectedCards.removeAll((List) stAb.getIgnoreEffectCards()); return affectedCards; } } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java new file mode 100644 index 00000000000..606ca597fbe --- /dev/null +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java @@ -0,0 +1,39 @@ +package forge.game.staticability; + +import com.google.common.collect.ImmutableList; + +public enum StaticAbilityLayer { + + /** Layer 2 for control-changing effects. */ + CONTROL, + + /** Layer 3 for text-changing effects. */ + TEXT, + + /** Layer 4 for type-changing effects. */ + TYPE, + + /** Layer 5 for color-changing effects. */ + COLOR, + + /** Layer 6 for ability-removing and -copying effects. */ + ABILITIES1, + + /** Layer 6 for ability-granting effects. */ + ABILITIES2, + + /** Layer 7a for characteristic-defining power/toughness effects. */ + CHARACTERISTIC, + + /** Layer 7b for power- and/or toughness-setting effects. */ + SETPT, + + /** Layer 7c for power- and/or toughness-modifying effects. */ + MODIFYPT, + + /** Layer for game rule-changing effects. */ + RULES; + + public final static ImmutableList CONTINUOUS_LAYERS = + ImmutableList.of(CONTROL, TEXT, TYPE, COLOR, ABILITIES1, ABILITIES2, CHARACTERISTIC, SETPT, MODIFYPT, RULES); +} diff --git a/forge-game/src/main/java/forge/trackable/TrackableObject.java b/forge-game/src/main/java/forge/trackable/TrackableObject.java index 8ef8fb5acb4..48c6468ba71 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableObject.java +++ b/forge-game/src/main/java/forge/trackable/TrackableObject.java @@ -1,50 +1,27 @@ package forge.trackable; -import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; +import java.util.Map; +import java.util.Set; import forge.game.IIdentifiable; //base class for objects that can be tracked and synced between game server and GUI public abstract class TrackableObject implements IIdentifiable { - private static int freezeCounter = 0; - public static void freeze() { - freezeCounter++; - } - public static void unfreeze() { - if (freezeCounter == 0 || --freezeCounter > 0 || delayedPropChanges.isEmpty()) { - return; - } - //after being unfrozen, ensure all changes delayed during freeze are now applied - for (DelayedPropChange change : delayedPropChanges) { - change.object.set(change.prop, change.value); - } - delayedPropChanges.clear(); - } - private static class DelayedPropChange { - private final TrackableObject object; - private final TrackableProperty prop; - private final Object value; - private DelayedPropChange(TrackableObject object0, TrackableProperty prop0, Object value0) { - object = object0; - prop = prop0; - value = value0; - } - } - private static final ArrayList delayedPropChanges = new ArrayList(); - private final int id; - private final EnumMap props; - private final EnumSet changedProps; + protected final transient Tracker tracker; + private final Map props; + private final Set changedProps; - protected TrackableObject(int id0) { + protected TrackableObject(final int id0, final Tracker tracker) { id = id0; + this.tracker = tracker; props = new EnumMap(TrackableProperty.class); changedProps = EnumSet.noneOf(TrackableProperty.class); } - public int getId() { + public final int getId() { return id; } @@ -54,13 +31,13 @@ public abstract class TrackableObject implements IIdentifiable { } @Override - public final boolean equals(Object o) { + public final boolean equals(final Object o) { if (o == null) { return false; } return o.hashCode() == id && o.getClass().equals(getClass()); } @SuppressWarnings("unchecked") - protected T get(TrackableProperty key) { + protected final T get(final TrackableProperty key) { T value = (T)props.get(key); if (value == null) { value = key.getDefaultValue(); @@ -68,9 +45,9 @@ public abstract class TrackableObject implements IIdentifiable { return value; } - protected void set(TrackableProperty key, T value) { - if (freezeCounter > 0 && key.respectFreeze()) { //if trackable objects currently frozen, queue up delayed prop change - delayedPropChanges.add(new DelayedPropChange(this, key, value)); + protected final void set(final TrackableProperty key, final T value) { + if (tracker != null && tracker.isFrozen() && key.respectFreeze()) { //if trackable objects currently frozen, queue up delayed prop change + tracker.addDelayedPropChange(this, key, value); return; } if (value == null || value.equals(key.getDefaultValue())) { @@ -84,11 +61,11 @@ public abstract class TrackableObject implements IIdentifiable { } //use when updating collection type properties with using set - protected void flagAsChanged(TrackableProperty key) { + protected final void flagAsChanged(final TrackableProperty key) { changedProps.add(key); } - public void serialize(TrackableSerializer ts) { + public final void serialize(final TrackableSerializer ts) { ts.write(changedProps.size()); for (TrackableProperty key : changedProps) { ts.write(TrackableProperty.serialize(key)); @@ -97,7 +74,7 @@ public abstract class TrackableObject implements IIdentifiable { changedProps.clear(); } - public void deserialize(TrackableDeserializer td) { + public final void deserialize(final TrackableDeserializer td) { int count = td.readInt(); for (int i = 0; i < count; i++) { TrackableProperty key = TrackableProperty.deserialize(td.readInt()); diff --git a/forge-game/src/main/java/forge/trackable/Tracker.java b/forge-game/src/main/java/forge/trackable/Tracker.java new file mode 100644 index 00000000000..3e7750ba623 --- /dev/null +++ b/forge-game/src/main/java/forge/trackable/Tracker.java @@ -0,0 +1,47 @@ +package forge.trackable; + +import java.util.List; + +import com.google.common.collect.Lists; + +public class Tracker { + private int freezeCounter = 0; + private final List delayedPropChanges = Lists.newArrayList(); + + public final boolean isFrozen() { + return freezeCounter > 0; + } + + public void freeze() { + freezeCounter++; + } + + public void unfreeze() { + if (!isFrozen() || --freezeCounter > 0 || delayedPropChanges.isEmpty()) { + return; + } + //after being unfrozen, ensure all changes delayed during freeze are now applied + for (final DelayedPropChange change : delayedPropChanges) { + change.object.set(change.prop, change.value); + } + delayedPropChanges.clear(); + } + + public void addDelayedPropChange(final TrackableObject object, final TrackableProperty prop, final Object value) { + delayedPropChanges.add(new DelayedPropChange(object, prop, value)); + } + + private class DelayedPropChange { + private final TrackableObject object; + private final TrackableProperty prop; + private final Object value; + private DelayedPropChange(final TrackableObject object0, final TrackableProperty prop0, final Object value0) { + object = object0; + prop = prop0; + value = value0; + } + @Override public String toString() { + return "Set " + prop + " of " + object + " to " + value; + } + } +} diff --git a/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java b/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java index cd73e27cb62..da3b101d5f9 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java +++ b/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java @@ -204,14 +204,17 @@ public class GuiChoose { @Override public void valueChanged(final ListSelectionEvent ev) { final T sel = list.getSelectedValue(); + if (sel instanceof InventoryItem) { + CMatchUI.SINGLETON_INSTANCE.setCard((InventoryItem) list.getSelectedValue()); + return; + } + final CardView card; if (sel instanceof CardStateView) { card = ((CardStateView) sel).getCard(); - } - else if (sel instanceof CardView) { + } else if (sel instanceof CardView) { card = (CardView) sel; - } - else { + } else { card = null; } if (card != null) { @@ -220,9 +223,6 @@ public class GuiChoose { GuiUtils.clearPanelSelections(); GuiUtils.setPanelSelection(card); } - if (list.getSelectedValue() instanceof InventoryItem) { - CMatchUI.SINGLETON_INSTANCE.setCard((InventoryItem) list.getSelectedValue()); - } } }); diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/VAssignDamage.java b/forge-gui-desktop/src/main/java/forge/screens/match/VAssignDamage.java index 99b66e4af27..d88a6ec1a18 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/VAssignDamage.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/VAssignDamage.java @@ -199,7 +199,7 @@ public class VAssignDamage { } else if (defender instanceof PlayerView) { final PlayerView p = (PlayerView)defender; - fakeCard = new CardView(-1, defender.toString(), p, CMatchUI.SINGLETON_INSTANCE.avatarImages.get(p.getLobbyPlayerName())); + fakeCard = new CardView(-1, null, defender.toString(), p, CMatchUI.SINGLETON_INSTANCE.avatarImages.get(p.getLobbyPlayerName())); } addPanelForDefender(pnlDefenders, fakeCard); } diff --git a/forge-gui-mobile/src/forge/card/CardZoom.java b/forge-gui-mobile/src/forge/card/CardZoom.java index 36d019e39bd..519c9b6f13c 100644 --- a/forge-gui-mobile/src/forge/card/CardZoom.java +++ b/forge-gui-mobile/src/forge/card/CardZoom.java @@ -117,9 +117,9 @@ public class CardZoom extends FOverlay { } if (item instanceof InventoryItem) { InventoryItem ii = (InventoryItem)item; - return new CardView(-1, ii.getName(), null, ImageKeys.getImageKey(ii, false)); + return new CardView(-1, null, ii.getName(), null, ImageKeys.getImageKey(ii, false)); } - return new CardView(-1, item.toString()); + return new CardView(-1, null, item.toString()); } @Override diff --git a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java index 79e86b55724..99f514a62a0 100644 --- a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java +++ b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java @@ -229,7 +229,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { final List options = Lists.newArrayList(); for (final Entry kv : ev.cards.entries()) { //use fake card so real cards appear with proper formatting - final CardView fakeCard = new CardView(-1, " -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --"); + final CardView fakeCard = new CardView(-1, null, " -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --"); options.add(fakeCard); options.add(kv.getValue().getView()); } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 4791e3a1c6e..f40f037be39 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1205,11 +1205,11 @@ public class PlayerControllerHuman extends PlayerController { tempShowCards(pile2); final List cards = Lists.newArrayListWithCapacity(pile1.size() + pile2.size() + 2); - final CardView pileView1 = new CardView(Integer.MIN_VALUE, "--- Pile 1 ---"); + final CardView pileView1 = new CardView(Integer.MIN_VALUE, null, "--- Pile 1 ---"); cards.add(pileView1); cards.addAll(CardView.getCollection(pile1)); - final CardView pileView2 = new CardView(Integer.MIN_VALUE + 1, "--- Pile 2 ---"); + final CardView pileView2 = new CardView(Integer.MIN_VALUE + 1, null, "--- Pile 2 ---"); cards.add(pileView2); cards.addAll(CardView.getCollection(pile2));