From 92b80c69ff73725b71cf528e21b5489da719a6f4 Mon Sep 17 00:00:00 2001 From: elcnesh Date: Mon, 26 Jan 2015 10:41:06 +0000 Subject: [PATCH] Refactoring changes and other updates: - Completely refactor continuous static effects. Layers are now implemented properly in the sense that every effect applies the relevant parts in the relevant layer, while locking in the set of cards to which it applies. - Refactor the (un)freezing of Trackable objects to a per-game Tracker object rather than static methods, to allow multiple games hosted on the same machine. - Refactor the changing of card colours to match other parts of the code (like type and P/T changing) more closely. Also get rid of some static parameters in that code. - Some changes in how split cards on the stack are handled to make them more robust. - Some more minor changes/cleanup. --- .gitattributes | 2 + .../main/java/forge/ai/ComputerUtilMana.java | 14 +- .../main/java/forge/ai/ability/AnimateAi.java | 10 +- .../main/java/forge/game/CardTraitBase.java | 4 + forge-game/src/main/java/forge/game/Game.java | 6 + .../src/main/java/forge/game/GameAction.java | 74 ++-- .../main/java/forge/game/GameEntityView.java | 5 +- .../src/main/java/forge/game/GameView.java | 6 +- .../main/java/forge/game/StaticEffect.java | 320 +++++++++++++----- .../main/java/forge/game/StaticEffects.java | 226 +------------ .../ability/effects/AnimateAllEffect.java | 9 +- .../game/ability/effects/AnimateEffect.java | 45 +-- .../ability/effects/AnimateEffectBase.java | 19 +- .../ability/effects/ChangeZoneEffect.java | 5 +- .../game/ability/effects/CloneEffect.java | 2 +- .../game/ability/effects/EffectEffect.java | 2 +- .../src/main/java/forge/game/card/Card.java | 80 ++--- .../main/java/forge/game/card/CardColor.java | 77 +---- .../java/forge/game/card/CardFactory.java | 28 +- .../main/java/forge/game/card/CardState.java | 71 ++-- .../main/java/forge/game/card/CardUtil.java | 5 +- .../main/java/forge/game/card/CardView.java | 23 +- .../java/forge/game/combat/CombatView.java | 5 +- .../main/java/forge/game/player/Player.java | 2 +- .../java/forge/game/player/PlayerView.java | 5 +- .../forge/game/spellability/SpellAbility.java | 18 + .../game/spellability/SpellAbilityView.java | 4 +- .../game/spellability/StackItemView.java | 2 +- .../game/staticability/StaticAbility.java | 148 ++++---- .../StaticAbilityContinuous.java | 319 +++++++++-------- .../staticability/StaticAbilityLayer.java | 39 +++ .../java/forge/trackable/TrackableObject.java | 55 +-- .../main/java/forge/trackable/Tracker.java | 47 +++ .../src/main/java/forge/gui/GuiChoose.java | 14 +- .../forge/screens/match/VAssignDamage.java | 2 +- forge-gui-mobile/src/forge/card/CardZoom.java | 4 +- .../control/FControlGameEventHandler.java | 2 +- .../forge/player/PlayerControllerHuman.java | 4 +- 38 files changed, 876 insertions(+), 827 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java create mode 100644 forge-game/src/main/java/forge/trackable/Tracker.java 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));