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.
This commit is contained in:
elcnesh
2015-01-26 10:41:06 +00:00
parent 90f88cce9d
commit 92b80c69ff
38 changed files with 876 additions and 827 deletions

2
.gitattributes vendored
View File

@@ -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/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/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/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/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/staticability/package-info.java svneol=native#text/plain
forge-game/src/main/java/forge/game/trigger/Trigger.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/TrackableProperty.java -text
forge-game/src/main/java/forge/trackable/TrackableSerializer.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/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/Expressions.java -text
forge-game/src/main/java/forge/util/MessageUtil.java -text forge-game/src/main/java/forge/util/MessageUtil.java -text
forge-game/src/test/java/forge/game/mana/ManaCostBeingPaidTest.java -text forge-game/src/test/java/forge/game/mana/ManaCostBeingPaidTest.java -text

View File

@@ -14,7 +14,6 @@ import forge.game.ability.ApiType;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CardColor;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardUtil; import forge.game.card.CardUtil;
import forge.game.combat.CombatUtil; import forge.game.combat.CombatUtil;
@@ -41,7 +40,6 @@ import org.apache.commons.lang3.tuple.Pair;
import java.util.*; import java.util.*;
public class ComputerUtilMana { public class ComputerUtilMana {
private final static boolean DEBUG_MANA_PAYMENT = false; private final static boolean DEBUG_MANA_PAYMENT = false;
@@ -1207,13 +1205,11 @@ public class ComputerUtilMana {
Card convoked = null; Card convoked = null;
for (ManaCostShard toPay : cost) { for (ManaCostShard toPay : cost) {
for (Card c : list) { for (Card c : list) {
for (CardColor col : c.getColor()) { final int mask = c.determineColor().getColor() & toPay.getColorMask();
final int mask = col.getColorMask() & toPay.getColorMask(); if (mask != 0) {
if (mask != 0) { convoked = c;
convoked = c; convoke.put(c, toPay);
convoke.put(c, toPay); break;
break;
}
} }
if (convoked != null){ if (convoked != null){
break; break;

View File

@@ -1,7 +1,6 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
@@ -27,6 +26,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions; import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityContinuous; import forge.game.staticability.StaticAbilityContinuous;
import forge.game.staticability.StaticAbilityLayer;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerHandler;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -396,7 +396,7 @@ public class AnimateAi extends SpellAbilityAi {
card.addHiddenExtrinsicKeyword(k); card.addHiddenExtrinsicKeyword(k);
} }
card.addColor(finalDesc, !sa.hasParam("OverwriteColors"), true); card.addColor(finalDesc, !sa.hasParam("OverwriteColors"), timestamp);
//back to duplicating AnimateEffect.resolve //back to duplicating AnimateEffect.resolve
//TODO will all these abilities/triggers/replacements/etc. lead to memory leaks or unintended effects? //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) { if (stAbs.size() > 0) {
for (final String s : stAbs) { for (final String s : stAbs) {
final String actualAbility = source.getSVar(s); final String actualAbility = source.getSVar(s);
StaticAbility stAb = card.addStaticAbility(actualAbility); final StaticAbility stAb = card.addStaticAbility(actualAbility);
if ("Continuous".equals(stAb.getMapParams().get("Mode"))) { 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);
}
} }
} }
} }

View File

@@ -165,6 +165,10 @@ public abstract class CardTraitBase extends GameObject {
return (this.suppressed || this.temporarilySuppressed); return (this.suppressed || this.temporarilySuppressed);
} }
protected final boolean isNonTempSuppressed() {
return this.suppressed;
}
protected boolean meetsCommonRequirements(Map<String, String> params) { protected boolean meetsCommonRequirements(Map<String, String> params) {
final Player hostController = this.getHostCard().getController(); final Player hostController = this.getHostCard().getController();
final Game game = hostController.getGame(); final Game game = hostController.getGame();

View File

@@ -63,6 +63,7 @@ import forge.game.trigger.TriggerType;
import forge.game.zone.MagicStack; import forge.game.zone.MagicStack;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.trackable.Tracker;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.FCollection; import forge.util.FCollection;
import forge.util.FCollectionView; import forge.util.FCollectionView;
@@ -103,6 +104,7 @@ public class Game {
private boolean disableAutoYields; private boolean disableAutoYields;
private final GameView view; private final GameView view;
private final Tracker tracker = new Tracker();
private GameEntityCache<Player, PlayerView> playerCache = new GameEntityCache<>(); private GameEntityCache<Player, PlayerView> playerCache = new GameEntityCache<>();
public Player getPlayer(PlayerView playerView) { public Player getPlayer(PlayerView playerView) {
@@ -178,6 +180,10 @@ public class Game {
return view; return view;
} }
public Tracker getTracker() {
return tracker;
}
/** /**
* Gets the players who are still fighting to win. * Gets the players who are still fighting to win.
*/ */

View File

@@ -20,6 +20,7 @@ package forge.game;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import forge.GameCommand; import forge.GameCommand;
@@ -39,6 +40,7 @@ import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions; import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityLayer;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZone;
@@ -46,10 +48,10 @@ import forge.game.zone.PlayerZoneBattlefield;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.trackable.TrackableObject;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.CollectionSuppliers; import forge.util.CollectionSuppliers;
import forge.util.Expressions; import forge.util.Expressions;
import forge.util.FCollection;
import forge.util.FCollectionView; import forge.util.FCollectionView;
import forge.util.ThreadUtil; import forge.util.ThreadUtil;
import forge.util.Visitor; import forge.util.Visitor;
@@ -534,9 +536,9 @@ public class GameAction {
} }
public final void checkStaticAbilities() { public final void checkStaticAbilities() {
checkStaticAbilities(new HashSet<Card>()); checkStaticAbilities(true, new CardCollection());
} }
public final void checkStaticAbilities(final Set<Card> affectedCards) { public final void checkStaticAbilities(final boolean runEvents, final Set<Card> affectedCards) {
if (game.isGameOver()) { if (game.isGameOver()) {
return; return;
} }
@@ -546,18 +548,19 @@ public class GameAction {
game.getTriggerHandler().cleanUpTemporaryTriggers(); game.getTriggerHandler().cleanUpTemporaryTriggers();
game.getReplacementHandler().cleanUpTemporaryReplacements(); game.getReplacementHandler().cleanUpTemporaryReplacements();
for (Player p : game.getPlayers()) { for (final Player p : game.getPlayers()) {
p.getManaPool().restoreColorReplacements(); p.getManaPool().restoreColorReplacements();
} }
// search for cards with static abilities // search for cards with static abilities
final ArrayList<StaticAbility> staticAbilities = new ArrayList<StaticAbility>(); final FCollection<StaticAbility> staticAbilities = new FCollection<StaticAbility>();
final List<Card> staticList = new ArrayList<Card>(); final CardCollection staticList = new CardCollection();
game.forEachCardInGame(new Visitor<Card>() { game.forEachCardInGame(new Visitor<Card>() {
@Override @Override
public void visit(Card c) { public void visit(final Card c) {
for (int i = 0; i < c.getStaticAbilities().size(); i++) { 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")) { if (stAb.getMapParams().get("Mode").equals("Continuous")) {
staticAbilities.add(stAb); staticAbilities.add(stAb);
} }
@@ -575,23 +578,36 @@ public class GameAction {
final Comparator<StaticAbility> comp = new Comparator<StaticAbility>() { final Comparator<StaticAbility> comp = new Comparator<StaticAbility>() {
@Override @Override
public int compare(final StaticAbility a, final StaticAbility b) { public int compare(final StaticAbility a, final StaticAbility b) {
int layerDelta = a.getLayer() - b.getLayer(); return Long.compare(a.getHostCard().getTimestamp(), b.getHostCard().getTimestamp());
if (layerDelta != 0) return layerDelta;
long tsDelta = a.getHostCard().getTimestamp() - b.getHostCard().getTimestamp();
return tsDelta == 0 ? 0 : tsDelta > 0 ? 1 : -1;
} }
}; };
Collections.sort(staticAbilities, comp); Collections.sort(staticAbilities, comp);
for (final StaticAbility stAb : staticAbilities) {
List<Card> affectedHere = stAb.applyAbility("Continuous"); final Map<StaticAbility, CardCollectionView> affectedPerAbility = Maps.newHashMap();
if (null != affectedHere) { for (final StaticAbilityLayer layer : StaticAbilityLayer.CONTINUOUS_LAYERS) {
affectedCards.addAll(affectedHere); 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); for (final CardCollectionView affected : affectedPerAbility.values()) {
GameActionUtil.grantBasicLandsManaAbilities(CardLists.filter(lands, CardPredicates.Presets.LANDS)); 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 (final Card c : staticList) {
for (int i = 0; i < c.getStaticCommandList().size(); i++) { for (int i = 0; i < c.getStaticCommandList().size(); i++) {
@@ -640,6 +656,10 @@ public class GameAction {
final HashMap<String, Object> runParams = new HashMap<String, Object>(); final HashMap<String, Object> runParams = new HashMap<String, Object>();
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
if (runEvents && !affectedCards.isEmpty()) {
game.fireEvent(new GameEventCardStatsChanged(affectedCards));
}
} }
public final void checkStateEffects(final boolean runEvents) { public final void checkStateEffects(final boolean runEvents) {
@@ -652,11 +672,6 @@ public class GameAction {
return; return;
} }
// final JFrame frame = Singletons.getView().getFrame();
// if (!frame.isDisplayable()) {
// return;
// }
if (game.isGameOver()) { if (game.isGameOver()) {
return; return;
} }
@@ -669,11 +684,11 @@ public class GameAction {
final boolean refreeze = game.getStack().isFrozen(); final boolean refreeze = game.getStack().isFrozen();
game.getStack().setFrozen(true); 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 // do this multiple times, sometimes creatures/permanents will survive when they shouldn't
for (int q = 0; q < 9; q++) { for (int q = 0; q < 9; q++) {
checkStaticAbilities(affectedCards); checkStaticAbilities(false, affectedCards);
boolean checkAgain = false; boolean checkAgain = false;
for (Player p : game.getPlayers()) { for (Player p : game.getPlayers()) {
@@ -690,7 +705,7 @@ public class GameAction {
} }
List<Card> noRegCreats = null; List<Card> noRegCreats = null;
List<Card> desCreats = null; List<Card> desCreats = null;
for (Card c : game.getCardsIn(ZoneType.Battlefield)) { for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
if (c.isCreature()) { if (c.isCreature()) {
// Rule 704.5f - Put into grave (no regeneration) for toughness <= 0 // Rule 704.5f - Put into grave (no regeneration) for toughness <= 0
if (c.getNetToughness() <= 0) { if (c.getNetToughness() <= 0) {
@@ -699,8 +714,7 @@ public class GameAction {
} }
noRegCreats.add(c); noRegCreats.add(c);
checkAgain = true; 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()) { for (final Integer dmg : c.getReceivedDamageFromThisTurn().values()) {
if (c.getNetToughness() <= dmg.intValue()) { if (c.getNetToughness() <= dmg.intValue()) {
if (desCreats == null) { if (desCreats == null) {
@@ -775,7 +789,7 @@ public class GameAction {
} }
} // for q=0;q<9 } // for q=0;q<9
TrackableObject.unfreeze(); game.getTracker().unfreeze();
if (runEvents && !affectedCards.isEmpty()) { if (runEvents && !affectedCards.isEmpty()) {
game.fireEvent(new GameEventCardStatsChanged(affectedCards)); game.fireEvent(new GameEventCardStatsChanged(affectedCards));

View File

@@ -4,6 +4,7 @@ import forge.game.card.CardView;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject; import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty; import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
public abstract class GameEntityView extends TrackableObject { public abstract class GameEntityView extends TrackableObject {
public static GameEntityView get(GameEntity e) { public static GameEntityView get(GameEntity e) {
@@ -21,8 +22,8 @@ public abstract class GameEntityView extends TrackableObject {
return collection; return collection;
} }
protected GameEntityView(int id0) { protected GameEntityView(final int id0, final Tracker tracker) {
super(id0); super(id0, tracker);
} }
public String getName() { public String getName() {

View File

@@ -36,8 +36,8 @@ public class GameView extends TrackableObject {
private CombatView combatView; private CombatView combatView;
private final Game game; //TODO: Remove this when possible before network support added private final Game game; //TODO: Remove this when possible before network support added
public GameView(Game game0) { public GameView(final Game game0) {
super(-1); //ID not needed super(-1, game0.getTracker()); //ID not needed
currentGame = this; currentGame = this;
game = game0; game = game0;
set(TrackableProperty.WinningTeam, -1); set(TrackableProperty.WinningTeam, -1);
@@ -138,7 +138,7 @@ public class GameView extends TrackableObject {
return; return;
} }
combatView = new CombatView(); combatView = new CombatView(combat.getAttackingPlayer().getGame().getTracker());
for (final AttackingBand b : combat.getAttackingBands()) { for (final AttackingBand b : combat.getAttackingBands()) {
if (b == null) continue; if (b == null) continue;
final GameEntity defender = combat.getDefenderByAttacker(b); final GameEntity defender = combat.getDefenderByAttacker(b);

View File

@@ -18,14 +18,23 @@
package forge.game; package forge.game;
import forge.game.card.Card; 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.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/** /**
* <p> * <p>
* StaticEffect class. * StaticEffect class.
@@ -35,45 +44,45 @@ import java.util.Map;
* @version $Id$ * @version $Id$
*/ */
public class StaticEffect { public class StaticEffect {
private final Card source; private final Card source;
private int keywordNumber = 0; private int keywordNumber = 0;
private List<Card> affectedCards = new ArrayList<Card>(); private CardCollectionView affectedCards = new CardCollection();
private ArrayList<Player> affectedPlayers = new ArrayList<Player>(); private List<Player> affectedPlayers = Lists.newArrayList();
private int xValue = 0; private int xValue = 0;
private int yValue = 0; private int yValue = 0;
private long timestamp = -1; private long timestamp = -1;
private HashMap<Card, Integer> xValueMap = new HashMap<Card, Integer>(); private Map<Card, Integer> xValueMap = Maps.newTreeMap();
private String chosenType; private String chosenType;
private Map<String, String> mapParams = new HashMap<String, String>(); private Map<String, String> mapParams = Maps.newTreeMap();
// for P/T // for P/T
private final HashMap<Card, String> originalPT = new HashMap<Card, String>(); private final Map<Card, String> originalPT = Maps.newTreeMap();
// for types // for types
private boolean overwriteTypes = false; private boolean overwriteTypes = false;
private boolean keepSupertype = false; private boolean keepSupertype = false;
private boolean removeSubTypes = false; private boolean removeSubTypes = false;
private final HashMap<Card, ArrayList<String>> types = new HashMap<Card, ArrayList<String>>(); private final Map<Card, List<String>> types = Maps.newTreeMap();
private final HashMap<Card, ArrayList<String>> originalTypes = new HashMap<Card, ArrayList<String>>(); private final Map<Card, List<String>> originalTypes = Maps.newTreeMap();
// keywords // keywords
private boolean overwriteKeywords = false; private boolean overwriteKeywords = false;
private final HashMap<Card, ArrayList<String>> originalKeywords = new HashMap<Card, ArrayList<String>>(); private final Map<Card, List<String>> originalKeywords = Maps.newTreeMap();
// for abilities // for abilities
private boolean overwriteAbilities = false; private boolean overwriteAbilities = false;
private final HashMap<Card, ArrayList<SpellAbility>> originalAbilities = new HashMap<Card, ArrayList<SpellAbility>>(); private final Map<Card, List<SpellAbility>> originalAbilities = Maps.newTreeMap();
// for colors // for colors
private String colorDesc = ""; private String colorDesc = "";
private boolean overwriteColors = false; private boolean overwriteColors = false;
private final HashMap<Card, Long> timestamps = new HashMap<Card, Long>();
public StaticEffect(Card source) { StaticEffect(final Card source) {
this.source = source; this.source = source;
} }
/** /**
* setTimestamp TODO Write javadoc for this method. * setTimestamp TODO Write javadoc for this method.
* *
@@ -130,7 +139,7 @@ public class StaticEffect {
*/ */
public final void addOriginalAbilities(final Card c, final SpellAbility sa) { public final void addOriginalAbilities(final Card c, final SpellAbility sa) {
if (!this.originalAbilities.containsKey(c)) { if (!this.originalAbilities.containsKey(c)) {
final ArrayList<SpellAbility> list = new ArrayList<SpellAbility>(); final List<SpellAbility> list = new ArrayList<SpellAbility>();
list.add(sa); list.add(sa);
this.originalAbilities.put(c, list); this.originalAbilities.put(c, list);
} else { } else {
@@ -148,8 +157,8 @@ public class StaticEffect {
* @param s * @param s
* a {@link java.util.ArrayList} object. * a {@link java.util.ArrayList} object.
*/ */
public final void addOriginalAbilities(final Card c, final ArrayList<SpellAbility> s) { public final void addOriginalAbilities(final Card c, final List<SpellAbility> s) {
final ArrayList<SpellAbility> list = new ArrayList<SpellAbility>(s); final List<SpellAbility> list = new ArrayList<SpellAbility>(s);
if (!this.originalAbilities.containsKey(c)) { if (!this.originalAbilities.containsKey(c)) {
this.originalAbilities.put(c, list); this.originalAbilities.put(c, list);
} else { } else {
@@ -235,7 +244,7 @@ public class StaticEffect {
*/ */
public final void addOriginalKeyword(final Card c, final String s) { public final void addOriginalKeyword(final Card c, final String s) {
if (!this.originalKeywords.containsKey(c)) { if (!this.originalKeywords.containsKey(c)) {
final ArrayList<String> list = new ArrayList<String>(); final List<String> list = new ArrayList<String>();
list.add(s); list.add(s);
this.originalKeywords.put(c, list); this.originalKeywords.put(c, list);
} else { } else {
@@ -251,10 +260,10 @@ public class StaticEffect {
* @param c * @param c
* a {@link forge.game.card.Card} object. * a {@link forge.game.card.Card} object.
* @param s * @param s
* a {@link java.util.ArrayList} object. * a {@link List} object.
*/ */
public final void addOriginalKeywords(final Card c, final ArrayList<String> s) { public final void addOriginalKeywords(final Card c, final List<String> s) {
final ArrayList<String> list = new ArrayList<String>(s); final List<String> list = new ArrayList<String>(s);
if (!this.originalKeywords.containsKey(c)) { if (!this.originalKeywords.containsKey(c)) {
this.originalKeywords.put(c, list); this.originalKeywords.put(c, list);
} else { } else {
@@ -270,10 +279,10 @@ public class StaticEffect {
* *
* @param c * @param c
* a {@link forge.game.card.Card} object. * a {@link forge.game.card.Card} object.
* @return a {@link java.util.ArrayList} object. * @return a {@link List} object.
*/ */
public final ArrayList<String> getOriginalKeywords(final Card c) { public final List<String> getOriginalKeywords(final Card c) {
final ArrayList<String> returnList = new ArrayList<String>(); final List<String> returnList = new ArrayList<String>();
if (this.originalKeywords.containsKey(c)) { if (this.originalKeywords.containsKey(c)) {
returnList.addAll(this.originalKeywords.get(c)); returnList.addAll(this.originalKeywords.get(c));
} }
@@ -450,7 +459,7 @@ public class StaticEffect {
*/ */
public final void addOriginalType(final Card c, final String s) { public final void addOriginalType(final Card c, final String s) {
if (!this.originalTypes.containsKey(c)) { if (!this.originalTypes.containsKey(c)) {
final ArrayList<String> list = new ArrayList<String>(); final List<String> list = new ArrayList<String>();
list.add(s); list.add(s);
this.originalTypes.put(c, list); this.originalTypes.put(c, list);
} else { } else {
@@ -468,8 +477,8 @@ public class StaticEffect {
* @param s * @param s
* a {@link java.util.ArrayList} object. * a {@link java.util.ArrayList} object.
*/ */
public final void addOriginalTypes(final Card c, final ArrayList<String> s) { public final void addOriginalTypes(final Card c, final List<String> s) {
final ArrayList<String> list = new ArrayList<String>(s); final List<String> list = new ArrayList<String>(s);
if (!this.originalTypes.containsKey(c)) { if (!this.originalTypes.containsKey(c)) {
this.originalTypes.put(c, list); this.originalTypes.put(c, list);
} else { } else {
@@ -487,8 +496,8 @@ public class StaticEffect {
* a {@link forge.game.card.Card} object. * a {@link forge.game.card.Card} object.
* @return a {@link java.util.ArrayList} object. * @return a {@link java.util.ArrayList} object.
*/ */
public final ArrayList<String> getOriginalTypes(final Card c) { public final List<String> getOriginalTypes(final Card c) {
final ArrayList<String> returnList = new ArrayList<String>(); final List<String> returnList = new ArrayList<String>();
if (this.originalTypes.containsKey(c)) { if (this.originalTypes.containsKey(c)) {
returnList.addAll(this.originalTypes.get(c)); returnList.addAll(this.originalTypes.get(c));
} }
@@ -642,59 +651,6 @@ public class StaticEffect {
this.overwriteColors = overwriteColors; this.overwriteColors = overwriteColors;
} }
/**
* <p>
* Getter for the field <code>timestamps</code>.
* </p>
*
* @return a {@link java.util.HashMap} object.
*/
public final HashMap<Card, Long> getTimestamps() {
return this.timestamps;
}
/**
* <p>
* getTimestamp.
* </p>
*
* @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;
}
/**
* <p>
* addTimestamp.
* </p>
*
* @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));
}
/**
* <p>
* clearTimestamps.
* </p>
*/
public final void clearTimestamps() {
this.timestamps.clear();
}
/** /**
* <p> * <p>
* Getter for the field <code>source</code>. * Getter for the field <code>source</code>.
@@ -736,8 +692,8 @@ public class StaticEffect {
* *
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
public final List<Card> getAffectedCards() { public final CardCollectionView getAffectedCards() {
return this.affectedCards; return affectedCards;
} }
/** /**
@@ -748,8 +704,8 @@ public class StaticEffect {
* @param list * @param list
* a {@link forge.CardList} object. * a {@link forge.CardList} object.
*/ */
public final void setAffectedCards(final List<Card> list) { public final void setAffectedCards(final CardCollectionView list) {
this.affectedCards = list; affectedCards = list;
} }
/** /**
@@ -757,7 +713,7 @@ public class StaticEffect {
* *
* @return the affected players * @return the affected players
*/ */
public final ArrayList<Player> getAffectedPlayers() { public final List<Player> getAffectedPlayers() {
return this.affectedPlayers; return this.affectedPlayers;
} }
@@ -878,4 +834,196 @@ public class StaticEffect {
return this.chosenType; 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<Player> affectedPlayers = getAffectedPlayers();
final Map<String, String> 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<String>(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<String>(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 } // end class StaticEffect

View File

@@ -17,19 +17,14 @@
*/ */
package forge.game; package forge.game;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; 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.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; import forge.game.staticability.StaticAbility;
/** /**
@@ -43,7 +38,7 @@ import forge.game.staticability.StaticAbility;
public class StaticEffects { public class StaticEffects {
// **************** StaticAbility system ************************** // **************** StaticAbility system **************************
private final List<StaticEffect> staticEffects = new ArrayList<StaticEffect>(); private final Map<StaticAbility, StaticEffect> staticEffects = Maps.newHashMap();
//Global rule changes //Global rule changes
private final Set<GlobalRuleChange> ruleChanges = EnumSet.noneOf(GlobalRuleChange.class); private final Set<GlobalRuleChange> ruleChanges = EnumSet.noneOf(GlobalRuleChange.class);
@@ -51,17 +46,17 @@ public class StaticEffects {
ruleChanges.clear(); ruleChanges.clear();
// remove all static effects // remove all static effects
for (final StaticEffect se : staticEffects) { for (final StaticEffect se : staticEffects.values()) {
affectedCards.addAll(removeStaticEffect(se)); Iterables.addAll(affectedCards, se.remove());
} }
this.staticEffects.clear(); this.staticEffects.clear();
} }
public void setGlobalRuleChange(GlobalRuleChange change) { public void setGlobalRuleChange(final GlobalRuleChange change) {
this.ruleChanges.add(change); this.ruleChanges.add(change);
} }
public boolean getGlobalRuleChange(GlobalRuleChange change) { public boolean getGlobalRuleChange(final GlobalRuleChange change) {
return this.ruleChanges.contains(change); return this.ruleChanges.contains(change);
} }
@@ -71,202 +66,15 @@ public class StaticEffects {
* @param staticEffect * @param staticEffect
* a {@link StaticEffect}. * a {@link StaticEffect}.
*/ */
public final void addStaticEffect(final StaticEffect staticEffect) { public final StaticEffect getStaticEffect(final StaticAbility staticAbility) {
this.staticEffects.add(staticEffect); 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<Card> removeStaticEffect(final StaticEffect se) {
final List<Card> affectedCards = se.getAffectedCards();
final ArrayList<Player> affectedPlayers = se.getAffectedPlayers();
final Map<String, String> 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<String>(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<String>(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;
}
} }

View File

@@ -145,8 +145,8 @@ public class AnimateAllEffect extends AnimateEffectBase {
list = CardLists.getValidCards(list, valid.split(","), host.getController(), host); list = CardLists.getValidCards(list, valid.split(","), host.getController(), host);
for (final Card c : list) { for (final Card c : list) {
final long colorTimestamp = doAnimate(c, sa, power, toughness, types, removeTypes, doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc,
finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp); keywords, removeKeywords, hiddenKeywords, timestamp);
// give abilities // give abilities
final ArrayList<SpellAbility> addedAbilities = new ArrayList<SpellAbility>(); final ArrayList<SpellAbility> addedAbilities = new ArrayList<SpellAbility>();
@@ -232,8 +232,9 @@ public class AnimateAllEffect extends AnimateEffectBase {
@Override @Override
public void run() { public void run() {
doUnanimate(c, sa, finalDesc, hiddenKeywords, addedAbilities, addedTriggers, addedReplacements, doUnanimate(c, sa, finalDesc, hiddenKeywords,
colorTimestamp, false, removedAbilities, timestamp); addedAbilities, addedTriggers, addedReplacements,
false, removedAbilities, timestamp);
// give back suppressed triggers // give back suppressed triggers
for (final Trigger t : removedTriggers) { for (final Trigger t : removedTriggers) {

View File

@@ -80,17 +80,17 @@ public class AnimateEffect extends AnimateEffectBase {
types.add(source.getChosenType()); types.add(source.getChosenType());
} }
final ArrayList<String> keywords = new ArrayList<String>(); final List<String> keywords = new ArrayList<String>();
if (sa.hasParam("Keywords")) { if (sa.hasParam("Keywords")) {
keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
} }
final ArrayList<String> removeKeywords = new ArrayList<String>(); final List<String> removeKeywords = new ArrayList<String>();
if (sa.hasParam("RemoveKeywords")) { if (sa.hasParam("RemoveKeywords")) {
removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & "))); removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & ")));
} }
final ArrayList<String> hiddenKeywords = new ArrayList<String>(); final List<String> hiddenKeywords = new ArrayList<String>();
if (sa.hasParam("HiddenKeywords")) { if (sa.hasParam("HiddenKeywords")) {
hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & "))); hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & ")));
} }
@@ -117,31 +117,31 @@ public class AnimateEffect extends AnimateEffectBase {
final String finalDesc = tmpDesc; final String finalDesc = tmpDesc;
// abilities to add to the animated being // abilities to add to the animated being
final ArrayList<String> abilities = new ArrayList<String>(); final List<String> abilities = new ArrayList<String>();
if (sa.hasParam("Abilities")) { if (sa.hasParam("Abilities")) {
abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(","))); abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(",")));
} }
// replacement effects to add to the animated being // replacement effects to add to the animated being
final ArrayList<String> replacements = new ArrayList<String>(); final List<String> replacements = new ArrayList<String>();
if (sa.hasParam("Replacements")) { if (sa.hasParam("Replacements")) {
replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(","))); replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(",")));
} }
// triggers to add to the animated being // triggers to add to the animated being
final ArrayList<String> triggers = new ArrayList<String>(); final List<String> triggers = new ArrayList<String>();
if (sa.hasParam("Triggers")) { if (sa.hasParam("Triggers")) {
triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(","))); triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(",")));
} }
// static abilities to add to the animated being // static abilities to add to the animated being
final ArrayList<String> stAbs = new ArrayList<String>(); final List<String> stAbs = new ArrayList<String>();
if (sa.hasParam("staticAbilities")) { if (sa.hasParam("staticAbilities")) {
stAbs.addAll(Arrays.asList(sa.getParam("staticAbilities").split(","))); stAbs.addAll(Arrays.asList(sa.getParam("staticAbilities").split(",")));
} }
// sVars to add to the animated being // sVars to add to the animated being
final ArrayList<String> sVars = new ArrayList<String>(); final List<String> sVars = new ArrayList<String>();
if (sa.hasParam("sVars")) { if (sa.hasParam("sVars")) {
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(","))); sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
} }
@@ -149,11 +149,11 @@ public class AnimateEffect extends AnimateEffectBase {
List<Card> tgts = getTargetCards(sa); List<Card> tgts = getTargetCards(sa);
for (final Card c : tgts) { for (final Card c : tgts) {
final long colorTimestamp = doAnimate(c, sa, power, toughness, types, removeTypes, doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc,
finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp); keywords, removeKeywords, hiddenKeywords, timestamp);
// remove abilities // remove abilities
final ArrayList<SpellAbility> removedAbilities = new ArrayList<SpellAbility>(); final List<SpellAbility> removedAbilities = new ArrayList<SpellAbility>();
boolean clearAbilities = sa.hasParam("OverwriteAbilities"); boolean clearAbilities = sa.hasParam("OverwriteAbilities");
boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean clearSpells = sa.hasParam("OverwriteSpells");
boolean removeAll = sa.hasParam("RemoveAllAbilities"); boolean removeAll = sa.hasParam("RemoveAllAbilities");
@@ -174,7 +174,7 @@ public class AnimateEffect extends AnimateEffectBase {
} }
// give abilities // give abilities
final ArrayList<SpellAbility> addedAbilities = new ArrayList<SpellAbility>(); final List<SpellAbility> addedAbilities = new ArrayList<SpellAbility>();
if (abilities.size() > 0) { if (abilities.size() > 0) {
for (final String s : abilities) { for (final String s : abilities) {
final String actualAbility = source.getSVar(s); final String actualAbility = source.getSVar(s);
@@ -185,7 +185,7 @@ public class AnimateEffect extends AnimateEffectBase {
} }
// Grant triggers // Grant triggers
final ArrayList<Trigger> addedTriggers = new ArrayList<Trigger>(); final List<Trigger> addedTriggers = new ArrayList<Trigger>();
if (triggers.size() > 0) { if (triggers.size() > 0) {
for (final String s : triggers) { for (final String s : triggers) {
final String actualTrigger = source.getSVar(s); final String actualTrigger = source.getSVar(s);
@@ -195,7 +195,7 @@ public class AnimateEffect extends AnimateEffectBase {
} }
// give replacement effects // give replacement effects
final ArrayList<ReplacementEffect> addedReplacements = new ArrayList<ReplacementEffect>(); final List<ReplacementEffect> addedReplacements = new ArrayList<ReplacementEffect>();
if (replacements.size() > 0) { if (replacements.size() > 0) {
for (final String s : replacements) { for (final String s : replacements) {
final String actualReplacement = source.getSVar(s); final String actualReplacement = source.getSVar(s);
@@ -205,7 +205,7 @@ public class AnimateEffect extends AnimateEffectBase {
} }
// suppress triggers from the animated card // suppress triggers from the animated card
final ArrayList<Trigger> removedTriggers = new ArrayList<Trigger>(); final List<Trigger> removedTriggers = new ArrayList<Trigger>();
if (sa.hasParam("OverwriteTriggers") || removeAll) { if (sa.hasParam("OverwriteTriggers") || removeAll) {
final FCollectionView<Trigger> triggersToRemove = c.getTriggers(); final FCollectionView<Trigger> triggersToRemove = c.getTriggers();
for (final Trigger trigger : triggersToRemove) { for (final Trigger trigger : triggersToRemove) {
@@ -238,7 +238,7 @@ public class AnimateEffect extends AnimateEffectBase {
} }
// suppress static abilities from the animated card // suppress static abilities from the animated card
final ArrayList<StaticAbility> removedStatics = new ArrayList<StaticAbility>(); final List<StaticAbility> removedStatics = new ArrayList<StaticAbility>();
if (sa.hasParam("OverwriteStatics") || removeAll) { if (sa.hasParam("OverwriteStatics") || removeAll) {
final FCollectionView<StaticAbility> staticsToRemove = c.getStaticAbilities(); final FCollectionView<StaticAbility> staticsToRemove = c.getStaticAbilities();
for (final StaticAbility stAb : staticsToRemove) { for (final StaticAbility stAb : staticsToRemove) {
@@ -248,7 +248,7 @@ public class AnimateEffect extends AnimateEffectBase {
} }
// suppress static abilities from the animated card // suppress static abilities from the animated card
final ArrayList<ReplacementEffect> removedReplacements = new ArrayList<ReplacementEffect>(); final List<ReplacementEffect> removedReplacements = new ArrayList<ReplacementEffect>();
if (sa.hasParam("OverwriteReplacements") || removeAll) { if (sa.hasParam("OverwriteReplacements") || removeAll) {
for (final ReplacementEffect re : c.getReplacementEffects()) { for (final ReplacementEffect re : c.getReplacementEffects()) {
re.setTemporarilySuppressed(true); re.setTemporarilySuppressed(true);
@@ -270,8 +270,9 @@ public class AnimateEffect extends AnimateEffectBase {
@Override @Override
public void run() { public void run() {
doUnanimate(c, sa, finalDesc, hiddenKeywords, addedAbilities, addedTriggers, addedReplacements, doUnanimate(c, sa, finalDesc, hiddenKeywords,
colorTimestamp, givesStAbs, removedAbilities, timestamp); addedAbilities, addedTriggers, addedReplacements,
givesStAbs, removedAbilities, timestamp);
game.fireEvent(new GameEventCardStatsChanged(c)); game.fireEvent(new GameEventCardStatsChanged(c));
// give back suppressed triggers // give back suppressed triggers
@@ -354,11 +355,11 @@ public class AnimateEffect extends AnimateEffectBase {
} }
final boolean permanent = sa.hasParam("Permanent"); final boolean permanent = sa.hasParam("Permanent");
final ArrayList<String> types = new ArrayList<String>(); final List<String> types = new ArrayList<String>();
if (sa.hasParam("Types")) { if (sa.hasParam("Types")) {
types.addAll(Arrays.asList(sa.getParam("Types").split(","))); types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
} }
final ArrayList<String> keywords = new ArrayList<String>(); final List<String> keywords = new ArrayList<String>();
if (sa.hasParam("Keywords")) { if (sa.hasParam("Keywords")) {
keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
} }
@@ -370,7 +371,7 @@ public class AnimateEffect extends AnimateEffectBase {
keywords.remove(k); keywords.remove(k);
} }
} }
final ArrayList<String> colors = new ArrayList<String>(); final List<String> colors = new ArrayList<String>();
if (sa.hasParam("Colors")) { if (sa.hasParam("Colors")) {
colors.addAll(Arrays.asList(sa.getParam("Colors").split(","))); colors.addAll(Arrays.asList(sa.getParam("Colors").split(",")));
} }

View File

@@ -26,12 +26,13 @@ import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public abstract class AnimateEffectBase extends SpellAbilityEffect { 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 CardType addType, final CardType removeType, final String colors,
final ArrayList<String> keywords, final ArrayList<String> removeKeywords, final List<String> keywords, final List<String> removeKeywords,
final ArrayList<String> hiddenKeywords, final long timestamp) { final List<String> hiddenKeywords, final long timestamp) {
boolean removeSuperTypes = false; boolean removeSuperTypes = false;
boolean removeCardTypes = false; boolean removeCardTypes = false;
@@ -84,8 +85,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
c.addHiddenExtrinsicKeyword(k); c.addHiddenExtrinsicKeyword(k);
} }
final long colorTimestamp = c.addColor(colors, !sa.hasParam("OverwriteColors"), true); c.addColor(colors, !sa.hasParam("OverwriteColors"), timestamp);
return colorTimestamp;
} }
/** /**
@@ -113,10 +113,9 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
* a long. * a long.
*/ */
void doUnanimate(final Card c, SpellAbility sa, final String colorDesc, void doUnanimate(final Card c, SpellAbility sa, final String colorDesc,
final ArrayList<String> hiddenKeywords, final ArrayList<SpellAbility> addedAbilities, final List<String> hiddenKeywords, final List<SpellAbility> addedAbilities,
final ArrayList<Trigger> addedTriggers, final ArrayList<ReplacementEffect> addedReplacements, final List<Trigger> addedTriggers, final List<ReplacementEffect> addedReplacements,
final long colorTimestamp, final boolean givesStAbs, final boolean givesStAbs, final List<SpellAbility> removedAbilities, final long timestamp) {
final ArrayList<SpellAbility> removedAbilities, final long timestamp) {
c.removeNewPT(timestamp); c.removeNewPT(timestamp);
@@ -132,7 +131,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
c.removeChangedCardTypes(timestamp); c.removeChangedCardTypes(timestamp);
} }
c.removeColor(colorDesc, c, !sa.hasParam("OverwriteColors"), colorTimestamp); c.removeColor(timestamp);
for (final String k : hiddenKeywords) { for (final String k : hiddenKeywords) {
c.removeHiddenExtrinsicKeyword(k); c.removeHiddenExtrinsicKeyword(k);

View File

@@ -402,9 +402,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
hostCard.clearRemembered(); hostCard.clearRemembered();
} }
boolean optional = sa.hasParam("Optional"); final boolean optional = sa.hasParam("Optional");
final long ts = game.getNextTimestamp();
long ts = game.getNextTimestamp();
for (final Card tgtC : tgtCards) { for (final Card tgtC : tgtCards) {
if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) { if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) {

View File

@@ -290,7 +290,7 @@ public class CloneEffect extends SpellAbilityEffect {
shortColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(","))); shortColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(",")));
} }
} }
tgtCard.addColor(shortColors, !sa.hasParam("OverwriteColors"), true); tgtCard.addColor(shortColors, !sa.hasParam("OverwriteColors"), tgtCard.getTimestamp());
} }
} }

View File

@@ -112,7 +112,7 @@ public class EffectEffect extends SpellAbilityEffect {
eff.setToken(true); // Set token to true, so when leaving play it gets nuked eff.setToken(true); // Set token to true, so when leaving play it gets nuked
eff.setOwner(controller); eff.setOwner(controller);
eff.setImageKey(sa.hasParam("Image") ? ImageKeys.getTokenKey(sa.getParam("Image")) : hostCard.getImageKey()); 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.setImmutable(true);
eff.setEffectSource(hostCard); eff.setEffectSource(hostCard);

View File

@@ -271,7 +271,7 @@ public class Card extends GameEntity implements Comparable<Card> {
game.addCard(id0, this); game.addCard(id0, this);
} }
paperCard = paperCard0; paperCard = paperCard0;
view = new CardView(id0); view = new CardView(id0, game == null ? null : game.getTracker());
currentState = new CardState(view.getCurrentState(), this); currentState = new CardState(view.getCurrentState(), this);
states.put(CardStateName.Original, currentState); states.put(CardStateName.Original, currentState);
states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this)); states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this));
@@ -1080,44 +1080,19 @@ public class Card extends GameEntity implements Comparable<Card> {
return currentState.getManaCost(); return currentState.getManaCost();
} }
public final void addColor(String s) { public final void addColor(final String s, final boolean addToColors, final long timestamp) {
if (s.equals("")) { currentState.addColor(s, addToColors, timestamp);
s = "0";
}
ManaCost mc = new ManaCost(new ManaCostParser(s));
currentState.getCardColor().add(new CardColor(mc.getColorProfile()));
} }
public final long addColor(final String s, final boolean addToColors, final boolean bIncrease) { public final void removeColor(final long timestamp) {
if (bIncrease) { currentState.removeColor(timestamp);
CardColor.increaseTimestamp();
}
currentState.getCardColor().add(new CardColor(s, addToColors));
currentState.getView().updateColors(this);
return CardColor.getTimestamp();
} }
public final void removeColor(final String s, final Card c, final boolean addTo, final long timestampIn) { public final void setColor(final byte color) {
CardColor removeCol = null; currentState.setColor(new CardColor(color));
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 String color) {
public final void setColor(final Iterable<CardColor> colors) { currentState.setColor(new CardColor(color));
currentState.setCardColor(colors);
currentState.getView().updateColors(this);
}
public final Iterable<CardColor> getColor() {
return currentState.getCardColor();
} }
public final ColorSet determineColor() { public final ColorSet determineColor() {
@@ -2441,7 +2416,7 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public Map<Long, CardChangedType> getChangedCardTypes() { public Map<Long, CardChangedType> getChangedCardTypes() {
return changedCardTypes; return Collections.unmodifiableMap(changedCardTypes);
} }
public final void addChangedCardTypes(final CardType addType, final CardType removeType, public final void addChangedCardTypes(final CardType addType, final CardType removeType,
@@ -3212,14 +3187,10 @@ public class Card extends GameEntity implements Comparable<Card> {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final String toString() { public final String toString() {
String name = "Morph"; if (getView() == null) {
if (!isFaceDown()) { return getPaperCard().getName();
name = getName();
if (StringUtils.isEmpty(name) && paperCard != null) {
name = paperCard.getName(); //make it possible to see likely card name before it's set
}
} }
return name + " (" + id + ")"; return getView().toString();
} }
public final boolean isUnearthed() { public final boolean isUnearthed() {
@@ -6273,25 +6244,16 @@ public class Card extends GameEntity implements Comparable<Card> {
view.updateCommander(this); view.updateCommander(this);
} }
public void setSplitStateToPlayAbility(SpellAbility sa) { public void setSplitStateToPlayAbility(final SpellAbility sa) {
if (!isSplitCard()) { return; } // just in case if (!isSplitCard()) {
return; // just in case
}
// Split card support // Split card support
for (SpellAbility a : getState(CardStateName.LeftSplit).getNonManaAbilities()) { if (sa.isLeftSplit()) {
if (sa == a || sa.getDescription().equals(String.format("%s (without paying its mana cost)", a.getDescription()))) { setState(CardStateName.LeftSplit, true);
setState(CardStateName.LeftSplit, true); } else if (sa.isRightSplit()) {
return; 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 // Optional costs paid

View File

@@ -30,82 +30,35 @@ import forge.card.mana.ManaCostParser;
* @version $Id$ * @version $Id$
*/ */
public class CardColor { 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; private final byte colorMask;
public final byte getColorMask() { return colorMask; } public final byte getColorMask() {
return colorMask;
}
private final boolean additional; private final boolean additional;
public final boolean isAdditional() { public final boolean isAdditional() {
return this.additional; return this.additional;
} }
private long stamp = 0; private final long timestamp;
public final long getTimestamp() {
/** return this.timestamp;
* <p>
* Getter for the field <code>stamp</code>.
* </p>
*
* @return a long.
*/
public final long getStamp() {
return this.stamp;
} }
/** CardColor(final String colors) {
* <p> this(colors, false, 0L);
* Constructor for Card_Color. }
* </p> CardColor(final String colors, final boolean addToColors, final long timestamp) {
* final ManaCost mc = new ManaCost(new ManaCostParser(colors));
* @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));
this.colorMask = mc.getColorProfile(); 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.colorMask = mask;
this.additional = false; this.additional = false;
this.stamp = 0; this.timestamp = 0;
}
public CardColor() {
this((byte)0);
}
/**
* <p>
* equals.
* </p>
*
* @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);
} }
public final ColorSet toColorSet() { public final ColorSet toColorSet() {

View File

@@ -163,7 +163,7 @@ public class CardFactory {
} }
final String finalColors = tmp; final String finalColors = tmp;
c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), true); c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), c.getTimestamp());
} }
c.clearControllers(); c.clearControllers();
@@ -288,7 +288,14 @@ public class CardFactory {
} }
if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) { 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.addNonManaAbilities(card.getCurrentState().getNonManaAbilities());
original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywords()); // Copy 'Fuse' to original side 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) 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()) { else if (card.isPlane()) {
buildPlaneAbilities(card); 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); card.getView().updateState(card);
} }
@@ -379,11 +386,8 @@ public class CardFactory {
card.setManaCost(combinedManaCost); card.setManaCost(combinedManaCost);
// Combined card color // Combined card color
int combinedColor = rules.getMainPart().getColor().getColor() | rules.getOtherPart().getColor().getColor(); final byte combinedColor = (byte) (rules.getMainPart().getColor().getColor() | rules.getOtherPart().getColor().getColor());
CardColor combinedCardColor = new CardColor((byte)combinedColor); card.setColor(combinedColor);
ArrayList<CardColor> combinedCardColorArr = new ArrayList<CardColor>();
combinedCardColorArr.add(combinedCardColor);
card.setColor(combinedCardColorArr);
card.setType(new CardType(rules.getType())); card.setType(new CardType(rules.getType()));
// Combined text based on Oracle text - might not be necessary, temporarily disabled. // 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. // Super and 'middle' types should use enums.
c.setType(new CardType(face.getType())); c.setType(new CardType(face.getType()));
// What a perverted color code we have! c.setColor(face.getColor().getColor());
CardColor col1 = new CardColor(face.getColor().getColor());
ArrayList<CardColor> ccc = new ArrayList<CardColor>();
ccc.add(col1);
c.setColor(ccc);
if (face.getIntPower() >= 0) { if (face.getIntPower() >= 0) {
c.setBasePower(face.getIntPower()); c.setBasePower(face.getIntPower());
@@ -657,7 +657,7 @@ public class CardFactory {
// TODO - most tokens mana cost is 0, this needs to be fixed // TODO - most tokens mana cost is 0, this needs to be fixed
// c.setManaCost(manaCost); // c.setManaCost(manaCost);
c.addColor(manaCost); c.setColor(manaCost);
c.setToken(true); c.setToken(true);
for (final String t : types) { for (final String t : types) {

View File

@@ -17,8 +17,17 @@
*/ */
package forge.game.card; 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.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Maps;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.card.CardRarity; import forge.card.CardRarity;
@@ -34,19 +43,11 @@ import forge.game.trigger.Trigger;
import forge.util.FCollection; import forge.util.FCollection;
import forge.util.FCollectionView; 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 { public class CardState {
private String name = ""; private String name = "";
private CardType type = new CardType(); private CardType type = new CardType();
private ManaCost manaCost = ManaCost.NO_COST; private ManaCost manaCost = ManaCost.NO_COST;
private List<CardColor> cardColor = new ArrayList<CardColor>(); private final SortedMap<Long, CardColor> cardColor = Maps.newTreeMap();
private int basePower = 0; private int basePower = 0;
private int baseToughness = 0; private int baseToughness = 0;
private List<String> intrinsicKeywords = new ArrayList<String>(); private List<String> intrinsicKeywords = new ArrayList<String>();
@@ -117,27 +118,49 @@ public class CardState {
view.updateManaCost(this); view.updateManaCost(this);
} }
public final List<CardColor> getCardColor() { public final void addColor(final CardColor color) {
return cardColor; cardColor.put(color.getTimestamp(), color);
getView().updateColors(this);
} }
public final void setCardColor(final Iterable<CardColor> 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<Long, CardColor> cardColor0) {
cardColor.clear();
cardColor.putAll(cardColor0);
view.updateColors(this); view.updateColors(this);
} }
public final void resetCardColor() { public final void resetCardColor() {
if (cardColor.isEmpty()) { return; } if (cardColor.isEmpty()) {
return;
cardColor = Lists.newArrayList(cardColor.subList(0, 1)); }
final Long firstKey = cardColor.firstKey();
final CardColor first = cardColor.get(firstKey);
cardColor.clear();
cardColor.put(firstKey, first);
view.updateColors(this); view.updateColors(this);
} }
public final ColorSet determineColor() { public final ColorSet determineColor() {
final List<CardColor> colorList = getCardColor(); final Iterable<CardColor> colorList = cardColor.values();
byte colors = 0; byte colors = 0;
for (int i = colorList.size() - 1;i >= 0;i--) { for (final CardColor cc : colorList) {
final CardColor cc = colorList.get(i); if (cc.isAdditional()) {
colors |= cc.getColorMask(); colors |= cc.getColorMask();
if (!cc.isAdditional()) { } else {
return ColorSet.fromMask(colors); colors = cc.getColorMask();
} }
} }
return ColorSet.fromMask(colors); return ColorSet.fromMask(colors);
@@ -366,7 +389,7 @@ public class CardState {
setName(source.getName()); setName(source.getName());
setType(source.type); setType(source.type);
setManaCost(source.getManaCost()); setManaCost(source.getManaCost());
setCardColor(source.getCardColor()); setColor(source.cardColor);
setBasePower(source.getBasePower()); setBasePower(source.getBasePower());
setBaseToughness(source.getBaseToughness()); setBaseToughness(source.getBaseToughness());
intrinsicKeywords = new ArrayList<String>(source.intrinsicKeywords); intrinsicKeywords = new ArrayList<String>(source.intrinsicKeywords);

View File

@@ -240,10 +240,7 @@ public final class CardUtil {
newCopy.setCounters(in.getCounters()); newCopy.setCounters(in.getCounters());
newCopy.setExtrinsicKeyword(in.getExtrinsicKeyword()); newCopy.setExtrinsicKeyword(in.getExtrinsicKeyword());
// Determine the color for LKI copy, not just getColor newCopy.setColor(in.determineColor().getColor());
ArrayList<CardColor> currentColor = new ArrayList<CardColor>();
currentColor.add(new CardColor(in.determineColor().getColor()));
newCopy.setColor(currentColor);
newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn());
newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn()); newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn());
newCopy.setEnchanting(in.getEnchanting()); newCopy.setEnchanting(in.getEnchanting());

View File

@@ -28,6 +28,7 @@ import forge.item.IPaperCard;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject; import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty; import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
import forge.util.FCollectionView; import forge.util.FCollectionView;
public class CardView extends GameEntityView { public class CardView extends GameEntityView {
@@ -68,20 +69,20 @@ public class CardView extends GameEntityView {
return false; return false;
} }
public CardView(int id0) { public CardView(final int id0, final Tracker tracker) {
super(id0); super(id0, tracker);
set(TrackableProperty.CurrentState, new CardStateView(id0, CardStateName.Original)); set(TrackableProperty.CurrentState, new CardStateView(id0, CardStateName.Original, tracker));
} }
public CardView(int id0, String name0) { public CardView(final int id0, final Tracker tracker, final String name0) {
this(id0); this(id0, tracker);
getCurrentState().setName(name0); getCurrentState().setName(name0);
set(TrackableProperty.Name, name0); set(TrackableProperty.Name, name0);
set(TrackableProperty.ChangedColorWords, new HashMap<String, String>()); set(TrackableProperty.ChangedColorWords, new HashMap<String, String>());
set(TrackableProperty.ChangedTypes, new HashMap<String, String>()); set(TrackableProperty.ChangedTypes, new HashMap<String, String>());
set(TrackableProperty.Sickness, true); set(TrackableProperty.Sickness, true);
} }
public CardView(int id0, String name0, PlayerView ownerAndController, String imageKey) { public CardView(final int id0, final Tracker tracker, final String name0, final PlayerView ownerAndController, final String imageKey) {
this(id0, name0); this(id0, tracker, name0);
set(TrackableProperty.Owner, ownerAndController); set(TrackableProperty.Owner, ownerAndController);
set(TrackableProperty.Controller, ownerAndController); set(TrackableProperty.Controller, ownerAndController);
set(TrackableProperty.ImageKey, imageKey); set(TrackableProperty.ImageKey, imageKey);
@@ -632,8 +633,8 @@ public class CardView extends GameEntityView {
public CardStateView getAlternateState() { public CardStateView getAlternateState() {
return get(TrackableProperty.AlternateState); return get(TrackableProperty.AlternateState);
} }
CardStateView createAlternateState(CardStateName state0) { CardStateView createAlternateState(final CardStateName state0) {
return new CardStateView(getId(), state0); return new CardStateView(getId(), state0, tracker);
} }
public CardStateView getState(final boolean alternate0) { public CardStateView getState(final boolean alternate0) {
@@ -716,8 +717,8 @@ public class CardView extends GameEntityView {
public class CardStateView extends TrackableObject { public class CardStateView extends TrackableObject {
private final CardStateName state; private final CardStateName state;
public CardStateView(int id0, CardStateName state0) { public CardStateView(final int id0, final CardStateName state0, final Tracker tracker) {
super(id0); super(id0, tracker);
state = state0; state = state0;
} }

View File

@@ -12,12 +12,13 @@ import forge.game.GameEntityView;
import forge.game.card.CardView; import forge.game.card.CardView;
import forge.trackable.TrackableObject; import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty; import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
import forge.util.FCollection; import forge.util.FCollection;
public class CombatView extends TrackableObject { public class CombatView extends TrackableObject {
public CombatView() { public CombatView(final Tracker tracker) {
super(-1); //ID not needed super(-1, tracker); //ID not needed
set(TrackableProperty.AttackersWithDefenders, new HashMap<CardView, GameEntityView>()); set(TrackableProperty.AttackersWithDefenders, new HashMap<CardView, GameEntityView>());
set(TrackableProperty.AttackersWithBlockers, new HashMap<CardView, FCollection<CardView>>()); set(TrackableProperty.AttackersWithBlockers, new HashMap<CardView, FCollection<CardView>>());
set(TrackableProperty.BandsWithDefenders, new HashMap<FCollection<CardView>, GameEntityView>()); set(TrackableProperty.BandsWithDefenders, new HashMap<FCollection<CardView>, GameEntityView>());

View File

@@ -150,7 +150,7 @@ public class Player extends GameEntity implements Comparable<Player> {
zones.put(z, toPut); zones.put(z, toPut);
} }
view = new PlayerView(id0); view = new PlayerView(id0, game.getTracker());
view.updateMaxHandSize(this); view.updateMaxHandSize(this);
view.updateKeywords(this); view.updateKeywords(this);
setName(chooseName(name0)); setName(chooseName(name0));

View File

@@ -16,6 +16,7 @@ import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.trackable.TrackableProperty; import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
import forge.util.FCollectionView; import forge.util.FCollectionView;
@@ -35,8 +36,8 @@ public class PlayerView extends GameEntityView {
return collection; return collection;
} }
public PlayerView(int id0) { public PlayerView(final int id0, final Tracker tracker) {
super(id0); super(id0, tracker);
set(TrackableProperty.Mana, Maps.newHashMapWithExpectedSize(MagicColor.NUMBER_OR_COLORS + 1)); set(TrackableProperty.Mana, Maps.newHashMapWithExpectedSize(MagicColor.NUMBER_OR_COLORS + 1));
} }

View File

@@ -96,6 +96,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private boolean morphup = false; private boolean morphup = false;
private boolean cumulativeupkeep = false; private boolean cumulativeupkeep = false;
private boolean outlast = false; private boolean outlast = false;
private SplitSide splitSide = null;
enum SplitSide { LEFT, RIGHT };
private int totalManaSpent = 0; private int totalManaSpent = 0;
/** The pay costs. */ /** The pay costs. */
@@ -545,6 +547,22 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
outlast = outlast0; 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() { public SpellAbility copy() {
SpellAbility clone = null; SpellAbility clone = null;
try { try {

View File

@@ -22,8 +22,8 @@ public class SpellAbilityView extends TrackableObject {
return collection; return collection;
} }
SpellAbilityView(SpellAbility sa) { SpellAbilityView(final SpellAbility sa) {
super(sa.getId()); super(sa.getId(), sa.getHostCard() == null || sa.getHostCard().getGame() == null ? null : sa.getHostCard().getGame().getTracker());
updateHostCard(sa); updateHostCard(sa);
updateDescription(sa); updateDescription(sa);
updatePromptIfOnlyPossibleAbility(sa); updatePromptIfOnlyPossibleAbility(sa);

View File

@@ -25,7 +25,7 @@ public class StackItemView extends TrackableObject {
} }
public StackItemView(SpellAbilityStackInstance si) { public StackItemView(SpellAbilityStackInstance si) {
super(si.getId()); super(si.getId(), si.getSourceCard().getGame().getTracker());
updateKey(si); updateKey(si);
updateSourceTrigger(si); updateSourceTrigger(si);
updateText(si); updateText(si);

View File

@@ -22,6 +22,8 @@ import forge.game.CardTraitBase;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
@@ -30,20 +32,22 @@ import forge.game.zone.ZoneType;
import forge.util.Expressions; import forge.util.Expressions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import com.google.common.collect.Maps;
/** /**
* The Class StaticAbility. * The Class StaticAbility.
*/ */
public class StaticAbility extends CardTraitBase { public class StaticAbility extends CardTraitBase {
private int layer = 0; private final Set<StaticAbilityLayer> layers;
private List<Card> ignoreEffectCards = new ArrayList<Card>(); private CardCollectionView ignoreEffectCards = new CardCollection();
private List<Player> ignoreEffectPlayers = new ArrayList<Player>(); private final List<Player> ignoreEffectPlayers = new ArrayList<Player>();
// *******************************************************
/** /**
* <p> * <p>
@@ -56,8 +60,8 @@ public class StaticAbility extends CardTraitBase {
* a {@link forge.game.card.Card} object. * a {@link forge.game.card.Card} object.
* @return a {@link java.util.HashMap} object. * @return a {@link java.util.HashMap} object.
*/ */
private static HashMap<String, String> parseParams(final String abString, final Card hostCard) { private static Map<String, String> parseParams(final String abString, final Card hostCard) {
final HashMap<String, String> mapParameters = new HashMap<String, String>(); final Map<String, String> mapParameters = Maps.newHashMap();
if (!(abString.length() > 0)) { if (!(abString.length() > 0)) {
throw new RuntimeException("StaticEffectFactory : getAbility -- abString too short in " throw new RuntimeException("StaticEffectFactory : getAbility -- abString too short in "
@@ -94,58 +98,63 @@ public class StaticAbility extends CardTraitBase {
return mapParameters; 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<StaticAbilityLayer> generateLayer() {
if (!this.mapParams.get("Mode").equals("Continuous")) { if (!this.mapParams.get("Mode").equals("Continuous")) {
return 0; return EnumSet.noneOf(StaticAbilityLayer.class);
} }
final Set<StaticAbilityLayer> layers = EnumSet.noneOf(StaticAbilityLayer.class);
if (this.mapParams.containsKey("GainControl")) { if (this.mapParams.containsKey("GainControl")) {
return 2; layers.add(StaticAbilityLayer.CONTROL);
} }
if (this.mapParams.containsKey("ChangeColorWordsTo")) { if (this.mapParams.containsKey("ChangeColorWordsTo")) {
return 3; layers.add(StaticAbilityLayer.TEXT);
} }
if (this.mapParams.containsKey("AddType") || this.mapParams.containsKey("RemoveType") if (this.mapParams.containsKey("AddType") || this.mapParams.containsKey("RemoveType")
|| this.mapParams.containsKey("RemoveCardTypes") || this.mapParams.containsKey("RemoveSubTypes") || this.mapParams.containsKey("RemoveCardTypes") || this.mapParams.containsKey("RemoveSubTypes")
|| this.mapParams.containsKey("RemoveSuperTypes") || this.mapParams.containsKey("RemoveCreatureTypes")) { || this.mapParams.containsKey("RemoveSuperTypes") || this.mapParams.containsKey("RemoveCreatureTypes")) {
return 4; layers.add(StaticAbilityLayer.TYPE);
} }
if (this.mapParams.containsKey("AddColor") || this.mapParams.containsKey("RemoveColor") if (this.mapParams.containsKey("AddColor") || this.mapParams.containsKey("RemoveColor")
|| this.mapParams.containsKey("SetColor")) { || this.mapParams.containsKey("SetColor")) {
return 5; layers.add(StaticAbilityLayer.COLOR);
} }
if (this.mapParams.containsKey("RemoveAllAbilities") || this.mapParams.containsKey("GainsAbilitiesOf")) { 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") if (this.mapParams.containsKey("AddKeyword") || this.mapParams.containsKey("AddAbility")
|| this.mapParams.containsKey("AddTrigger") || this.mapParams.containsKey("RemoveTriggers") || this.mapParams.containsKey("AddTrigger") || this.mapParams.containsKey("RemoveTriggers")
|| this.mapParams.containsKey("RemoveKeyword") || this.mapParams.containsKey("AddReplacementEffects")) { || this.mapParams.containsKey("RemoveKeyword") || this.mapParams.containsKey("AddReplacementEffects")) {
return 7; // Layer 6 (dependent) layers.add(StaticAbilityLayer.ABILITIES2);
} }
if (this.mapParams.containsKey("CharacteristicDefining")) { if (this.mapParams.containsKey("CharacteristicDefining")) {
return 8; // Layer 7a layers.add(StaticAbilityLayer.CHARACTERISTIC);
} }
if (this.mapParams.containsKey("AddPower") || this.mapParams.containsKey("AddToughness") if (this.mapParams.containsKey("SetPower") || this.mapParams.containsKey("SetToughness")) {
|| this.mapParams.containsKey("SetPower") || this.mapParams.containsKey("SetToughness")) { layers.add(StaticAbilityLayer.SETPT);
return 9; // This is the collection of 7b and 7c }
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 * the host
*/ */
public StaticAbility(final String params, final Card host) { public StaticAbility(final String params, final Card host) {
final Map<String, String> parsedParams = parseParams(params, host); this(parseParams(params, host), host);
this.originalMapParams.putAll(parsedParams);
this.mapParams.putAll(parsedParams);
this.hostCard = host;
this.layer = this.generateLayer();
} }
/** /**
@@ -189,37 +194,49 @@ public class StaticAbility extends CardTraitBase {
* @param host * @param host
* the host * the host
*/ */
public StaticAbility(final Map<String, String> params, final Card host) { private StaticAbility(final Map<String, String> params, final Card host) {
this.originalMapParams.putAll(params); this.originalMapParams.putAll(params);
this.mapParams.putAll(params); this.mapParams.putAll(params);
this.layer = this.generateLayer(); this.layers = this.generateLayer();
this.hostCard = host; 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 * @param layer
* the mode * the {@link StaticAbilityLayer} under investigation.
* @return * @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<Card> applyAbility(final String mode) { private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer, final boolean ignoreTempSuppression) {
final boolean isSuppressed;
// don't apply the ability if it hasn't got the right mode if (ignoreTempSuppression) {
if (!this.mapParams.get("Mode").equals(mode)) { isSuppressed = this.isNonTempSuppressed();
return null; } else {
isSuppressed = this.isSuppressed();
} }
return mapParams.get("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed && this.checkConditions();
if (this.isSuppressed() || !this.checkConditions()) {
return null;
}
if (mode.equals("Continuous")) {
return StaticAbilityContinuous.applyContinuousAbility(this);
}
return null;
} }
// apply the ability if it has the right mode // apply the ability if it has the right mode
@@ -557,15 +574,15 @@ public class StaticAbility extends CardTraitBase {
/** /**
* @return the ignoreEffectCards * @return the ignoreEffectCards
*/ */
public List<Card> getIgnoreEffectCards() { public CardCollectionView getIgnoreEffectCards() {
return ignoreEffectCards; return ignoreEffectCards;
} }
/** /**
* @param c the ignoreEffectCards to set * @param c the ignoreEffectCards to set
*/ */
public void setIgnoreEffectCards(List<Card> cards) { public void setIgnoreEffectCards(final CardCollectionView cards) {
this.ignoreEffectCards = cards; ignoreEffectCards = cards;
} }
/** /**
@@ -578,27 +595,20 @@ public class StaticAbility extends CardTraitBase {
/** /**
* @param p the ignoreEffectPlayers to add * @param p the ignoreEffectPlayers to add
*/ */
public void addIgnoreEffectPlayers(Player p) { public void addIgnoreEffectPlayers(final Player p) {
this.ignoreEffectPlayers.add(p); ignoreEffectPlayers.add(p);
} }
public void clearIgnoreEffects() { public void clearIgnoreEffects() {
this.ignoreEffectPlayers.clear(); ignoreEffectPlayers.clear();
this.ignoreEffectCards.clear(); ignoreEffectCards = new CardCollection();
} }
/** /**
* @return the layer * @return the layer
*/ */
public int getLayer() { public Set<StaticAbilityLayer> getLayers() {
return layer; return layers;
} }
/** } // end class StaticAbility
* @param layer the layer to set
*/
public void setLayer(int layer) {
this.layer = layer;
}
} // end class StaticEffectFactory

View File

@@ -17,14 +17,24 @@
*/ */
package forge.game.staticability; 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.Iterables;
import com.google.common.collect.Lists;
import forge.GameCommand; import forge.GameCommand;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaCostShard; 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.AbilityFactory;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
@@ -45,43 +55,59 @@ import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerHandler;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/** /**
* The Class StaticAbility_Continuous. * The Class StaticAbility_Continuous.
*/ */
public class StaticAbilityContinuous { public final class StaticAbilityContinuous {
public static List<Card> applyContinuousAbility(final StaticAbility stAb) { // Private constructor to prevent instantiation
final List<Card> affectedCards = StaticAbilityContinuous.getAffectedCards(stAb); private StaticAbilityContinuous() {
return applyContinuousAbility(stAb, affectedCards); }
}
/** /**
* * Apply the effects of a static ability that apply in a particular layer.
* TODO Write javadoc for this method. * The cards to which the effects are applied are dynamically determined.
* *
* @param stAb * @param stAb
* a StaticAbility * a {@link StaticAbility}.
* @return * @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<Card> applyContinuousAbility(final StaticAbility stAb, List<Card> 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<String, String> params = stAb.getMapParams(); final Map<String, String> params = stAb.getMapParams();
final Card hostCard = stAb.getHostCard(); final Card hostCard = stAb.getHostCard();
final Player controller = hostCard.getController(); final Player controller = hostCard.getController();
final StaticEffect se = new StaticEffect(hostCard);
final ArrayList<Player> affectedPlayers = StaticAbilityContinuous.getAffectedPlayers(stAb); final ArrayList<Player> affectedPlayers = StaticAbilityContinuous.getAffectedPlayers(stAb);
final Game game = hostCard.getGame(); final Game game = hostCard.getGame();
final StaticEffect se = game.getStaticEffects().getStaticEffect(stAb);
se.setAffectedCards(affectedCards); se.setAffectedCards(affectedCards);
se.setAffectedPlayers(affectedPlayers); se.setAffectedPlayers(affectedPlayers);
se.setParams(params); se.setParams(params);
se.setTimestamp(hostCard.getTimestamp()); se.setTimestamp(hostCard.getTimestamp());
game.getStaticEffects().addStaticEffect(se);
String changeColorWordsTo = null; String changeColorWordsTo = null;
@@ -116,26 +142,26 @@ public class StaticAbilityContinuous {
boolean controllerMayPlay = false, mayPlayWithoutManaCost = false, mayPlayIgnoreColor = false; boolean controllerMayPlay = false, mayPlayWithoutManaCost = false, mayPlayIgnoreColor = false;
//Global rules changes //Global rules changes
if (params.containsKey("GlobalRule")) { if (layer == StaticAbilityLayer.RULES && params.containsKey("GlobalRule")) {
final StaticEffects effects = game.getStaticEffects(); final StaticEffects effects = game.getStaticEffects();
effects.setGlobalRuleChange(GlobalRuleChange.fromString(params.get("GlobalRule"))); effects.setGlobalRuleChange(GlobalRuleChange.fromString(params.get("GlobalRule")));
} }
if (params.containsKey("ChangeColorWordsTo")) { if (layer == StaticAbilityLayer.TEXT && params.containsKey("ChangeColorWordsTo")) {
changeColorWordsTo = params.get("ChangeColorWordsTo"); changeColorWordsTo = params.get("ChangeColorWordsTo");
} }
if (params.containsKey("SetPower")) { if (layer == StaticAbilityLayer.SETPT &&params.containsKey("SetPower")) {
setP = params.get("SetPower"); setP = params.get("SetPower");
setPower = AbilityUtils.calculateAmount(hostCard, setP, stAb); setPower = AbilityUtils.calculateAmount(hostCard, setP, stAb);
} }
if (params.containsKey("SetToughness")) { if (layer == StaticAbilityLayer.SETPT && params.containsKey("SetToughness")) {
setT = params.get("SetToughness"); setT = params.get("SetToughness");
setToughness = AbilityUtils.calculateAmount(hostCard, setT, stAb); setToughness = AbilityUtils.calculateAmount(hostCard, setT, stAb);
} }
if (params.containsKey("AddPower")) { if (layer == StaticAbilityLayer.MODIFYPT && params.containsKey("AddPower")) {
addP = params.get("AddPower"); addP = params.get("AddPower");
powerBonus = AbilityUtils.calculateAmount(hostCard, addP, stAb); powerBonus = AbilityUtils.calculateAmount(hostCard, addP, stAb);
if (!StringUtils.isNumeric(addP) && !addP.equals("AffectedX")) { 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"); addT = params.get("AddToughness");
toughnessBonus = AbilityUtils.calculateAmount(hostCard, addT, stAb); toughnessBonus = AbilityUtils.calculateAmount(hostCard, addT, stAb);
if (!StringUtils.isNumeric(addT) && !addT.equals("AffectedX")) { if (!StringUtils.isNumeric(addT) && !addT.equals("AffectedX")) {
@@ -151,8 +177,8 @@ public class StaticAbilityContinuous {
} }
} }
if (params.containsKey("KeywordMultiplier")) { if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("KeywordMultiplier")) {
String multiplier = params.get("KeywordMultiplier"); final String multiplier = params.get("KeywordMultiplier");
if (multiplier.equals("X")) { if (multiplier.equals("X")) {
se.setXValue(AbilityUtils.calculateAmount(hostCard, "X", stAb)); se.setXValue(AbilityUtils.calculateAmount(hostCard, "X", stAb));
} else { } else {
@@ -160,7 +186,7 @@ public class StaticAbilityContinuous {
} }
} }
if (params.containsKey("AddKeyword")) { if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddKeyword")) {
addKeywords = params.get("AddKeyword").split(" & "); addKeywords = params.get("AddKeyword").split(" & ");
final Iterable<String> chosencolors = hostCard.getChosenColors(); final Iterable<String> chosencolors = hostCard.getChosenColors();
for (final String color : chosencolors) { 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(" & "); addHiddenKeywords = params.get("AddHiddenKeyword").split(" & ");
} }
if (params.containsKey("RemoveKeyword")) { if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("RemoveKeyword")) {
removeKeywords = params.get("RemoveKeyword").split(" & "); removeKeywords = params.get("RemoveKeyword").split(" & ");
} }
if (params.containsKey("RemoveAllAbilities")) { if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("RemoveAllAbilities")) {
removeAllAbilities = true; removeAllAbilities = true;
} }
if (params.containsKey("AddAbility")) { if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddAbility")) {
final String[] sVars = params.get("AddAbility").split(" & "); final String[] sVars = params.get("AddAbility").split(" & ");
for (int i = 0; i < sVars.length; i++) { for (int i = 0; i < sVars.length; i++) {
sVars[i] = hostCard.getSVar(sVars[i]); sVars[i] = hostCard.getSVar(sVars[i]);
@@ -207,7 +233,7 @@ public class StaticAbilityContinuous {
addAbilities = sVars; addAbilities = sVars;
} }
if (params.containsKey("AddReplacementEffects")) { if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddReplacementEffects")) {
final String[] sVars = params.get("AddReplacementEffects").split(" & "); final String[] sVars = params.get("AddReplacementEffects").split(" & ");
for (int i = 0; i < sVars.length; i++) { for (int i = 0; i < sVars.length; i++) {
sVars[i] = hostCard.getSVar(sVars[i]); sVars[i] = hostCard.getSVar(sVars[i]);
@@ -215,11 +241,11 @@ public class StaticAbilityContinuous {
addReplacements = sVars; addReplacements = sVars;
} }
if (params.containsKey("AddSVar")) { if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddSVar")) {
addSVars = params.get("AddSVar").split(" & "); addSVars = params.get("AddSVar").split(" & ");
} }
if (params.containsKey("AddType")) { if (layer == StaticAbilityLayer.TYPE && params.containsKey("AddType")) {
addTypes = params.get("AddType").split(" & "); addTypes = params.get("AddType").split(" & ");
if (addTypes[0].equals("ChosenType")) { if (addTypes[0].equals("ChosenType")) {
final String chosenType = hostCard.getChosenType(); 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(" & "); removeTypes = params.get("RemoveType").split(" & ");
if (removeTypes[0].equals("ChosenType")) { if (removeTypes[0].equals("ChosenType")) {
final String chosenType = hostCard.getChosenType(); final String chosenType = hostCard.getChosenType();
@@ -242,60 +268,66 @@ public class StaticAbilityContinuous {
} }
} }
if (params.containsKey("RemoveSuperTypes")) { if (layer == StaticAbilityLayer.TYPE) {
removeSuperTypes = true; if (params.containsKey("RemoveSuperTypes")) {
} removeSuperTypes = true;
}
if (params.containsKey("RemoveCardTypes")) { if (params.containsKey("RemoveCardTypes")) {
removeCardTypes = true; removeCardTypes = true;
} }
if (params.containsKey("RemoveSubTypes")) { if (params.containsKey("RemoveSubTypes")) {
removeSubTypes = true; removeSubTypes = true;
} }
if (params.containsKey("RemoveCreatureTypes")) { if (params.containsKey("RemoveCreatureTypes")) {
removeCreatureTypes = true; 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<String>(Arrays.asList(colors.split(
" & "))));
} }
} }
if (params.containsKey("SetColor")) { if (layer == StaticAbilityLayer.COLOR) {
final String colors = params.get("SetColor"); if (params.containsKey("AddColor")) {
if (colors.equals("ChosenColor")) { final String colors = params.get("AddColor");
addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); if (colors.equals("ChosenColor")) {
} else { addColors = CardUtil.getShortColorsString(hostCard.getChosenColors());
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList( } else {
colors.split(" & ")))); addColors = CardUtil.getShortColorsString(new ArrayList<String>(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<String>(Arrays.asList(
colors.split(" & "))));
}
se.setOverwriteColors(true);
} }
se.setOverwriteColors(true);
} }
if (params.containsKey("AddTrigger")) { if (layer == StaticAbilityLayer.ABILITIES2) {
final String[] sVars = params.get("AddTrigger").split(" & "); if (params.containsKey("AddTrigger")) {
for (int i = 0; i < sVars.length; i++) { final String[] sVars = params.get("AddTrigger").split(" & ");
sVars[i] = hostCard.getSVar(sVars[i]); 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")) { if (layer == StaticAbilityLayer.ABILITIES1 && params.containsKey("GainsAbilitiesOf")) {
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")) {
final String[] valids = params.get("GainsAbilitiesOf").split(","); final String[] valids = params.get("GainsAbilitiesOf").split(",");
ArrayList<ZoneType> validZones = new ArrayList<ZoneType>(); ArrayList<ZoneType> validZones = new ArrayList<ZoneType>();
validZones.add(ZoneType.Battlefield); validZones.add(ZoneType.Battlefield);
@@ -326,21 +358,24 @@ public class StaticAbilityContinuous {
} }
} }
if (params.containsKey("MayLookAt")) { if (layer == StaticAbilityLayer.RULES) {
controllerMayLookAt = true; // These fall under Rule changes, as they don't fit any other category
} if (params.containsKey("MayLookAt")) {
if (params.containsKey("MayPlay")) { controllerMayLookAt = true;
controllerMayPlay = true; }
if (params.containsKey("MayPlayWithoutManaCost")) { if (params.containsKey("MayPlay")) {
mayPlayWithoutManaCost = true; controllerMayPlay = true;
} else if (params.containsKey("MayPlayIgnoreColor")) { if (params.containsKey("MayPlayWithoutManaCost")) {
mayPlayIgnoreColor = true; mayPlayWithoutManaCost = true;
} else if (params.containsKey("MayPlayIgnoreColor")) {
mayPlayIgnoreColor = true;
}
} }
}
if (params.containsKey("IgnoreEffectCost")) { if (params.containsKey("IgnoreEffectCost")) {
String cost = params.get("IgnoreEffectCost"); String cost = params.get("IgnoreEffectCost");
buildIgnorEffectAbility(stAb, cost, affectedPlayers, affectedCards); buildIgnorEffectAbility(stAb, cost, affectedPlayers, affectedCards);
}
} }
// modify players // modify players
@@ -353,24 +388,26 @@ public class StaticAbilityContinuous {
} }
} }
if (params.containsKey("SetMaxHandSize")) { if (layer == StaticAbilityLayer.RULES) {
String mhs = params.get("SetMaxHandSize"); if (params.containsKey("SetMaxHandSize")) {
if (mhs.equals("Unlimited")) { String mhs = params.get("SetMaxHandSize");
p.setUnlimitedHandSize(true); if (mhs.equals("Unlimited")) {
} else { p.setUnlimitedHandSize(true);
int max = AbilityUtils.calculateAmount(hostCard, mhs, stAb); } else {
p.setMaxHandSize(max); int max = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
p.setMaxHandSize(max);
}
} }
}
if (params.containsKey("RaiseMaxHandSize")) { if (params.containsKey("RaiseMaxHandSize")) {
String rmhs = params.get("RaiseMaxHandSize"); String rmhs = params.get("RaiseMaxHandSize");
int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb); int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb);
p.setMaxHandSize(p.getMaxHandSize() + rmax); p.setMaxHandSize(p.getMaxHandSize() + rmax);
} }
if (params.containsKey("ManaColorConversion")) { if (params.containsKey("ManaColorConversion")) {
AbilityUtils.applyManaColorConversion(p, params); AbilityUtils.applyManaColorConversion(p, params);
}
} }
} }
@@ -379,7 +416,7 @@ public class StaticAbilityContinuous {
final Card affectedCard = affectedCards.get(i); final Card affectedCard = affectedCards.get(i);
// Gain control // Gain control
if (params.containsKey("GainControl")) { if (layer == StaticAbilityLayer.CONTROL && params.containsKey("GainControl")) {
affectedCard.addTempController(hostCard.getController(), hostCard.getTimestamp()); affectedCard.addTempController(hostCard.getController(), hostCard.getTimestamp());
} }
@@ -403,35 +440,39 @@ public class StaticAbilityContinuous {
} }
// set P/T // set P/T
if (params.containsKey("CharacteristicDefining")) { if (layer == StaticAbilityLayer.SETPT) {
if (setPower != -1) { if (params.containsKey("CharacteristicDefining")) {
affectedCard.setBasePower(setPower); 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 // add P/T bonus
if (addP.startsWith("AffectedX")) { if (layer == StaticAbilityLayer.MODIFYPT) {
powerBonus = CardFactoryUtil.xCount(affectedCard, AbilityUtils.getSVar(stAb, addP)); if (addP.startsWith("AffectedX")) {
se.addXMapValue(affectedCard, powerBonus); 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 // add keywords
// TODO regular keywords currently don't try to use keyword multiplier // TODO regular keywords currently don't try to use keyword multiplier
@@ -517,8 +558,7 @@ public class StaticAbilityContinuous {
// add colors // add colors
if (addColors != null) { if (addColors != null) {
final long t = affectedCard.addColor(addColors, !se.isOverwriteColors(), true); affectedCard.addColor(addColors, !se.isOverwriteColors(), hostCard.getTimestamp());
se.addTimestamp(affectedCard, t);
} }
// add triggers // add triggers
@@ -545,7 +585,7 @@ public class StaticAbilityContinuous {
} }
// remove triggers // remove triggers
if (params.containsKey("RemoveTriggers") || removeAllAbilities) { if (layer == StaticAbilityLayer.ABILITIES2 && (params.containsKey("RemoveTriggers") || removeAllAbilities)) {
for (final Trigger trigger : affectedCard.getTriggers()) { for (final Trigger trigger : affectedCard.getTriggers()) {
trigger.setTemporarilySuppressed(true); trigger.setTemporarilySuppressed(true);
} }
@@ -575,7 +615,7 @@ public class StaticAbilityContinuous {
return affectedCards; return affectedCards;
} }
private static void buildIgnorEffectAbility(final StaticAbility stAb, final String costString, final List<Player> players, final List<Card> cards) { private static void buildIgnorEffectAbility(final StaticAbility stAb, final String costString, final List<Player> players, final CardCollectionView cards) {
final List<Player> validActivator = new ArrayList<Player>(players); final List<Player> validActivator = new ArrayList<Player>(players);
for (final Card c : cards) { for (final Card c : cards) {
validActivator.add(c.getController()); validActivator.add(c.getController());
@@ -637,19 +677,18 @@ public class StaticAbilityContinuous {
return players; return players;
} }
private static List<Card> getAffectedCards(final StaticAbility stAb) { private static CardCollectionView getAffectedCards(final StaticAbility stAb) {
final Map<String, String> params = stAb.getMapParams(); final Map<String, String> params = stAb.getMapParams();
final Card hostCard = stAb.getHostCard(); final Card hostCard = stAb.getHostCard();
final Game game = hostCard.getGame(); final Game game = hostCard.getGame();
final Player controller = hostCard.getController(); final Player controller = hostCard.getController();
if (params.containsKey("CharacteristicDefining")) { 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 // non - CharacteristicDefining
CardCollection affectedCards; CardCollection affectedCards;
if (params.containsKey("AffectedZone")) { if (params.containsKey("AffectedZone")) {
affectedCards = new CardCollection(game.getCardsIn(ZoneType.listValueOf(params.get("AffectedZone")))); affectedCards = new CardCollection(game.getCardsIn(ZoneType.listValueOf(params.get("AffectedZone"))));
} else { } else {
@@ -672,7 +711,7 @@ public class StaticAbilityContinuous {
if (params.containsKey("Affected")) { if (params.containsKey("Affected")) {
affectedCards = CardLists.getValidCards(affectedCards, params.get("Affected").split(","), controller, hostCard); affectedCards = CardLists.getValidCards(affectedCards, params.get("Affected").split(","), controller, hostCard);
} }
affectedCards.removeAll((List<?>)stAb.getIgnoreEffectCards()); affectedCards.removeAll((List<?>) stAb.getIgnoreEffectCards());
return affectedCards; return affectedCards;
} }
} }

View File

@@ -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<StaticAbilityLayer> CONTINUOUS_LAYERS =
ImmutableList.of(CONTROL, TEXT, TYPE, COLOR, ABILITIES1, ABILITIES2, CHARACTERISTIC, SETPT, MODIFYPT, RULES);
}

View File

@@ -1,50 +1,27 @@
package forge.trackable; package forge.trackable;
import java.util.ArrayList;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import forge.game.IIdentifiable; import forge.game.IIdentifiable;
//base class for objects that can be tracked and synced between game server and GUI //base class for objects that can be tracked and synced between game server and GUI
public abstract class TrackableObject implements IIdentifiable { 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<DelayedPropChange> delayedPropChanges = new ArrayList<DelayedPropChange>();
private final int id; private final int id;
private final EnumMap<TrackableProperty, Object> props; protected final transient Tracker tracker;
private final EnumSet<TrackableProperty> changedProps; private final Map<TrackableProperty, Object> props;
private final Set<TrackableProperty> changedProps;
protected TrackableObject(int id0) { protected TrackableObject(final int id0, final Tracker tracker) {
id = id0; id = id0;
this.tracker = tracker;
props = new EnumMap<TrackableProperty, Object>(TrackableProperty.class); props = new EnumMap<TrackableProperty, Object>(TrackableProperty.class);
changedProps = EnumSet.noneOf(TrackableProperty.class); changedProps = EnumSet.noneOf(TrackableProperty.class);
} }
public int getId() { public final int getId() {
return id; return id;
} }
@@ -54,13 +31,13 @@ public abstract class TrackableObject implements IIdentifiable {
} }
@Override @Override
public final boolean equals(Object o) { public final boolean equals(final Object o) {
if (o == null) { return false; } if (o == null) { return false; }
return o.hashCode() == id && o.getClass().equals(getClass()); return o.hashCode() == id && o.getClass().equals(getClass());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T> T get(TrackableProperty key) { protected final <T> T get(final TrackableProperty key) {
T value = (T)props.get(key); T value = (T)props.get(key);
if (value == null) { if (value == null) {
value = key.getDefaultValue(); value = key.getDefaultValue();
@@ -68,9 +45,9 @@ public abstract class TrackableObject implements IIdentifiable {
return value; return value;
} }
protected <T> void set(TrackableProperty key, T value) { protected final <T> void set(final TrackableProperty key, final T value) {
if (freezeCounter > 0 && key.respectFreeze()) { //if trackable objects currently frozen, queue up delayed prop change if (tracker != null && tracker.isFrozen() && key.respectFreeze()) { //if trackable objects currently frozen, queue up delayed prop change
delayedPropChanges.add(new DelayedPropChange(this, key, value)); tracker.addDelayedPropChange(this, key, value);
return; return;
} }
if (value == null || value.equals(key.getDefaultValue())) { 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 //use when updating collection type properties with using set
protected void flagAsChanged(TrackableProperty key) { protected final void flagAsChanged(final TrackableProperty key) {
changedProps.add(key); changedProps.add(key);
} }
public void serialize(TrackableSerializer ts) { public final void serialize(final TrackableSerializer ts) {
ts.write(changedProps.size()); ts.write(changedProps.size());
for (TrackableProperty key : changedProps) { for (TrackableProperty key : changedProps) {
ts.write(TrackableProperty.serialize(key)); ts.write(TrackableProperty.serialize(key));
@@ -97,7 +74,7 @@ public abstract class TrackableObject implements IIdentifiable {
changedProps.clear(); changedProps.clear();
} }
public void deserialize(TrackableDeserializer td) { public final void deserialize(final TrackableDeserializer td) {
int count = td.readInt(); int count = td.readInt();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
TrackableProperty key = TrackableProperty.deserialize(td.readInt()); TrackableProperty key = TrackableProperty.deserialize(td.readInt());

View File

@@ -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<DelayedPropChange> 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;
}
}
}

View File

@@ -204,14 +204,17 @@ public class GuiChoose {
@Override @Override
public void valueChanged(final ListSelectionEvent ev) { public void valueChanged(final ListSelectionEvent ev) {
final T sel = list.getSelectedValue(); final T sel = list.getSelectedValue();
if (sel instanceof InventoryItem) {
CMatchUI.SINGLETON_INSTANCE.setCard((InventoryItem) list.getSelectedValue());
return;
}
final CardView card; final CardView card;
if (sel instanceof CardStateView) { if (sel instanceof CardStateView) {
card = ((CardStateView) sel).getCard(); card = ((CardStateView) sel).getCard();
} } else if (sel instanceof CardView) {
else if (sel instanceof CardView) {
card = (CardView) sel; card = (CardView) sel;
} } else {
else {
card = null; card = null;
} }
if (card != null) { if (card != null) {
@@ -220,9 +223,6 @@ public class GuiChoose {
GuiUtils.clearPanelSelections(); GuiUtils.clearPanelSelections();
GuiUtils.setPanelSelection(card); GuiUtils.setPanelSelection(card);
} }
if (list.getSelectedValue() instanceof InventoryItem) {
CMatchUI.SINGLETON_INSTANCE.setCard((InventoryItem) list.getSelectedValue());
}
} }
}); });

View File

@@ -199,7 +199,7 @@ public class VAssignDamage {
} }
else if (defender instanceof PlayerView) { else if (defender instanceof PlayerView) {
final PlayerView p = (PlayerView)defender; 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); addPanelForDefender(pnlDefenders, fakeCard);
} }

View File

@@ -117,9 +117,9 @@ public class CardZoom extends FOverlay {
} }
if (item instanceof InventoryItem) { if (item instanceof InventoryItem) {
InventoryItem ii = (InventoryItem)item; 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 @Override

View File

@@ -229,7 +229,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
final List<CardView> options = Lists.newArrayList(); final List<CardView> options = Lists.newArrayList();
for (final Entry<Player, Card> kv : ev.cards.entries()) { for (final Entry<Player, Card> kv : ev.cards.entries()) {
//use fake card so real cards appear with proper formatting //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(fakeCard);
options.add(kv.getValue().getView()); options.add(kv.getValue().getView());
} }

View File

@@ -1205,11 +1205,11 @@ public class PlayerControllerHuman extends PlayerController {
tempShowCards(pile2); tempShowCards(pile2);
final List<CardView> cards = Lists.newArrayListWithCapacity(pile1.size() + pile2.size() + 2); final List<CardView> 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.add(pileView1);
cards.addAll(CardView.getCollection(pile1)); 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.add(pileView2);
cards.addAll(CardView.getCollection(pile2)); cards.addAll(CardView.getCollection(pile2));