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/StaticAbilityContinuous.java svneol=native#text/plain
forge-game/src/main/java/forge/game/staticability/StaticAbilityETBTapped.java -text
forge-game/src/main/java/forge/game/staticability/StaticAbilityLayer.java -text
forge-game/src/main/java/forge/game/staticability/StaticAbilityPreventDamage.java svneol=native#text/plain
forge-game/src/main/java/forge/game/staticability/package-info.java svneol=native#text/plain
forge-game/src/main/java/forge/game/trigger/Trigger.java svneol=native#text/plain
@@ -706,6 +707,7 @@ forge-game/src/main/java/forge/trackable/TrackableObject.java -text
forge-game/src/main/java/forge/trackable/TrackableProperty.java -text
forge-game/src/main/java/forge/trackable/TrackableSerializer.java -text
forge-game/src/main/java/forge/trackable/TrackableTypes.java -text
forge-game/src/main/java/forge/trackable/Tracker.java -text
forge-game/src/main/java/forge/util/Expressions.java -text
forge-game/src/main/java/forge/util/MessageUtil.java -text
forge-game/src/test/java/forge/game/mana/ManaCostBeingPaidTest.java -text

View File

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

View File

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

View File

@@ -165,6 +165,10 @@ public abstract class CardTraitBase extends GameObject {
return (this.suppressed || this.temporarilySuppressed);
}
protected final boolean isNonTempSuppressed() {
return this.suppressed;
}
protected boolean meetsCommonRequirements(Map<String, String> params) {
final Player hostController = this.getHostCard().getController();
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.Zone;
import forge.game.zone.ZoneType;
import forge.trackable.Tracker;
import forge.util.Aggregates;
import forge.util.FCollection;
import forge.util.FCollectionView;
@@ -103,6 +104,7 @@ public class Game {
private boolean disableAutoYields;
private final GameView view;
private final Tracker tracker = new Tracker();
private GameEntityCache<Player, PlayerView> playerCache = new GameEntityCache<>();
public Player getPlayer(PlayerView playerView) {
@@ -178,6 +180,10 @@ public class Game {
return view;
}
public Tracker getTracker() {
return tracker;
}
/**
* Gets the players who are still fighting to win.
*/

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,19 +17,14 @@
*/
package forge.game;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
/**
@@ -43,7 +38,7 @@ import forge.game.staticability.StaticAbility;
public class StaticEffects {
// **************** StaticAbility system **************************
private final List<StaticEffect> staticEffects = new ArrayList<StaticEffect>();
private final Map<StaticAbility, StaticEffect> staticEffects = Maps.newHashMap();
//Global rule changes
private final Set<GlobalRuleChange> ruleChanges = EnumSet.noneOf(GlobalRuleChange.class);
@@ -51,17 +46,17 @@ public class StaticEffects {
ruleChanges.clear();
// remove all static effects
for (final StaticEffect se : staticEffects) {
affectedCards.addAll(removeStaticEffect(se));
for (final StaticEffect se : staticEffects.values()) {
Iterables.addAll(affectedCards, se.remove());
}
this.staticEffects.clear();
}
public void setGlobalRuleChange(GlobalRuleChange change) {
public void setGlobalRuleChange(final GlobalRuleChange change) {
this.ruleChanges.add(change);
}
public boolean getGlobalRuleChange(GlobalRuleChange change) {
public boolean getGlobalRuleChange(final GlobalRuleChange change) {
return this.ruleChanges.contains(change);
}
@@ -71,202 +66,15 @@ public class StaticEffects {
* @param staticEffect
* a {@link StaticEffect}.
*/
public final void addStaticEffect(final StaticEffect staticEffect) {
this.staticEffects.add(staticEffect);
public final StaticEffect getStaticEffect(final StaticAbility staticAbility) {
final StaticEffect currentEffect = staticEffects.get(staticAbility);
if (currentEffect != null) {
return currentEffect;
}
final StaticEffect newEffect = new StaticEffect(staticAbility.getHostCard());
this.staticEffects.put(staticAbility, newEffect);
return newEffect;
}
/**
* Remove a static effect from the list of static effects and undo everything that was changed by the effect.
*
* @param se
* a {@link StaticEffect}.
*/
private static final List<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);
for (final Card c : list) {
final long colorTimestamp = doAnimate(c, sa, power, toughness, types, removeTypes,
finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp);
doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc,
keywords, removeKeywords, hiddenKeywords, timestamp);
// give abilities
final ArrayList<SpellAbility> addedAbilities = new ArrayList<SpellAbility>();
@@ -232,8 +232,9 @@ public class AnimateAllEffect extends AnimateEffectBase {
@Override
public void run() {
doUnanimate(c, sa, finalDesc, hiddenKeywords, addedAbilities, addedTriggers, addedReplacements,
colorTimestamp, false, removedAbilities, timestamp);
doUnanimate(c, sa, finalDesc, hiddenKeywords,
addedAbilities, addedTriggers, addedReplacements,
false, removedAbilities, timestamp);
// give back suppressed triggers
for (final Trigger t : removedTriggers) {

View File

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

View File

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

View File

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

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.setOwner(controller);
eff.setImageKey(sa.hasParam("Image") ? ImageKeys.getTokenKey(sa.getParam("Image")) : hostCard.getImageKey());
eff.setColor(hostCard.getColor());
eff.setColor(hostCard.determineColor().getColor());
eff.setImmutable(true);
eff.setEffectSource(hostCard);

View File

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

View File

@@ -30,82 +30,35 @@ import forge.card.mana.ManaCostParser;
* @version $Id$
*/
public class CardColor {
private static long timeStamp = 0;
public static long getTimestamp() { return CardColor.timeStamp; }
static void increaseTimestamp() { CardColor.timeStamp++; }
// takes care of individual card color, for global color change effects use
// AllZone.getGameInfo().getColorChanges()
private final byte colorMask;
public final byte getColorMask() { return colorMask; }
public final byte getColorMask() {
return colorMask;
}
private final boolean additional;
public final boolean isAdditional() {
return this.additional;
}
private long stamp = 0;
/**
* <p>
* Getter for the field <code>stamp</code>.
* </p>
*
* @return a long.
*/
public final long getStamp() {
return this.stamp;
private final long timestamp;
public final long getTimestamp() {
return this.timestamp;
}
/**
* <p>
* Constructor for Card_Color.
* </p>
*
* @param mc
* a {@link forge.game.mana.ManaCostBeingPaid} object.
* @param c
* a {@link forge.game.card.Card} object.
* @param addToColors
* a boolean.
* @param baseColor
* a boolean.
*/
CardColor(final String colors, final boolean addToColors) {
this.additional = addToColors;
ManaCost mc = new ManaCost(new ManaCostParser(colors));
CardColor(final String colors) {
this(colors, false, 0L);
}
CardColor(final String colors, final boolean addToColors, final long timestamp) {
final ManaCost mc = new ManaCost(new ManaCostParser(colors));
this.colorMask = mc.getColorProfile();
this.stamp = CardColor.timeStamp;
this.additional = addToColors;
this.timestamp = timestamp;
}
public CardColor(byte mask) {
CardColor(final byte mask) {
this.colorMask = mask;
this.additional = false;
this.stamp = 0;
}
public CardColor() {
this((byte)0);
}
/**
* <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);
this.timestamp = 0;
}
public final ColorSet toColorSet() {

View File

@@ -163,7 +163,7 @@ public class CardFactory {
}
final String finalColors = tmp;
c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), true);
c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), c.getTimestamp());
}
c.clearControllers();
@@ -288,7 +288,14 @@ public class CardFactory {
}
if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) {
CardState original = card.getState(CardStateName.Original);
for (final SpellAbility sa : card.getSpellAbilities()) {
if (state == CardStateName.LeftSplit) {
sa.setLeftSplit();
} else {
sa.setRightSplit();
}
}
final CardState original = card.getState(CardStateName.Original);
original.addNonManaAbilities(card.getCurrentState().getNonManaAbilities());
original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywords()); // Copy 'Fuse' to original side
original.getSVars().putAll(card.getCurrentState().getSVars()); // Unfortunately need to copy these to (Effect looks for sVars on execute)
@@ -306,7 +313,7 @@ public class CardFactory {
else if (card.isPlane()) {
buildPlaneAbilities(card);
}
CardFactoryUtil.setupKeywordedAbilities(card);
CardFactoryUtil.setupKeywordedAbilities(card); // Should happen AFTER setting left/right split abilities to set Fuse ability to both sides
card.getView().updateState(card);
}
@@ -379,11 +386,8 @@ public class CardFactory {
card.setManaCost(combinedManaCost);
// Combined card color
int combinedColor = rules.getMainPart().getColor().getColor() | rules.getOtherPart().getColor().getColor();
CardColor combinedCardColor = new CardColor((byte)combinedColor);
ArrayList<CardColor> combinedCardColorArr = new ArrayList<CardColor>();
combinedCardColorArr.add(combinedCardColor);
card.setColor(combinedCardColorArr);
final byte combinedColor = (byte) (rules.getMainPart().getColor().getColor() | rules.getOtherPart().getColor().getColor());
card.setColor(combinedColor);
card.setType(new CardType(rules.getType()));
// Combined text based on Oracle text - might not be necessary, temporarily disabled.
@@ -411,11 +415,7 @@ public class CardFactory {
// Super and 'middle' types should use enums.
c.setType(new CardType(face.getType()));
// What a perverted color code we have!
CardColor col1 = new CardColor(face.getColor().getColor());
ArrayList<CardColor> ccc = new ArrayList<CardColor>();
ccc.add(col1);
c.setColor(ccc);
c.setColor(face.getColor().getColor());
if (face.getIntPower() >= 0) {
c.setBasePower(face.getIntPower());
@@ -657,7 +657,7 @@ public class CardFactory {
// TODO - most tokens mana cost is 0, this needs to be fixed
// c.setManaCost(manaCost);
c.addColor(manaCost);
c.setColor(manaCost);
c.setToken(true);
for (final String t : types) {

View File

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

View File

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

View File

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

View File

@@ -12,12 +12,13 @@ import forge.game.GameEntityView;
import forge.game.card.CardView;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
import forge.util.FCollection;
public class CombatView extends TrackableObject {
public CombatView() {
super(-1); //ID not needed
public CombatView(final Tracker tracker) {
super(-1, tracker); //ID not needed
set(TrackableProperty.AttackersWithDefenders, new HashMap<CardView, GameEntityView>());
set(TrackableProperty.AttackersWithBlockers, new HashMap<CardView, FCollection<CardView>>());
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);
}
view = new PlayerView(id0);
view = new PlayerView(id0, game.getTracker());
view.updateMaxHandSize(this);
view.updateKeywords(this);
setName(chooseName(name0));

View File

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

View File

@@ -96,6 +96,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private boolean morphup = false;
private boolean cumulativeupkeep = false;
private boolean outlast = false;
private SplitSide splitSide = null;
enum SplitSide { LEFT, RIGHT };
private int totalManaSpent = 0;
/** The pay costs. */
@@ -545,6 +547,22 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
outlast = outlast0;
}
public boolean isLeftSplit() {
return splitSide == SplitSide.LEFT;
}
public boolean isRightSplit() {
return splitSide == SplitSide.RIGHT;
}
public void setNoSplit() {
splitSide = null;
}
public void setLeftSplit() {
splitSide = SplitSide.LEFT;
}
public void setRightSplit() {
splitSide = SplitSide.RIGHT;
}
public SpellAbility copy() {
SpellAbility clone = null;
try {

View File

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

View File

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

View File

@@ -22,6 +22,8 @@ import forge.game.CardTraitBase;
import forge.game.GameEntity;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
@@ -30,20 +32,22 @@ import forge.game.zone.ZoneType;
import forge.util.Expressions;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Maps;
/**
* The Class StaticAbility.
*/
public class StaticAbility extends CardTraitBase {
private int layer = 0;
private List<Card> ignoreEffectCards = new ArrayList<Card>();
private List<Player> ignoreEffectPlayers = new ArrayList<Player>();
// *******************************************************
private final Set<StaticAbilityLayer> layers;
private CardCollectionView ignoreEffectCards = new CardCollection();
private final List<Player> ignoreEffectPlayers = new ArrayList<Player>();
/**
* <p>
@@ -56,8 +60,8 @@ public class StaticAbility extends CardTraitBase {
* a {@link forge.game.card.Card} object.
* @return a {@link java.util.HashMap} object.
*/
private static HashMap<String, String> parseParams(final String abString, final Card hostCard) {
final HashMap<String, String> mapParameters = new HashMap<String, String>();
private static Map<String, String> parseParams(final String abString, final Card hostCard) {
final Map<String, String> mapParameters = Maps.newHashMap();
if (!(abString.length() > 0)) {
throw new RuntimeException("StaticEffectFactory : getAbility -- abString too short in "
@@ -94,58 +98,63 @@ public class StaticAbility extends CardTraitBase {
return mapParameters;
}
// In which layer should the ability be applied (for continuous effects
// only)
/**
* Gets the layer.
* Gets the {@link Set} of {@link StaticAbilityLayer}s in which this
* {@link StaticAbility} is to be applied.
*
* @return the layer
* @return the applicable layers.
*/
public final int generateLayer() {
private final Set<StaticAbilityLayer> generateLayer() {
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")) {
return 2;
layers.add(StaticAbilityLayer.CONTROL);
}
if (this.mapParams.containsKey("ChangeColorWordsTo")) {
return 3;
layers.add(StaticAbilityLayer.TEXT);
}
if (this.mapParams.containsKey("AddType") || this.mapParams.containsKey("RemoveType")
|| this.mapParams.containsKey("RemoveCardTypes") || this.mapParams.containsKey("RemoveSubTypes")
|| this.mapParams.containsKey("RemoveSuperTypes") || this.mapParams.containsKey("RemoveCreatureTypes")) {
return 4;
layers.add(StaticAbilityLayer.TYPE);
}
if (this.mapParams.containsKey("AddColor") || this.mapParams.containsKey("RemoveColor")
|| this.mapParams.containsKey("SetColor")) {
return 5;
layers.add(StaticAbilityLayer.COLOR);
}
if (this.mapParams.containsKey("RemoveAllAbilities") || this.mapParams.containsKey("GainsAbilitiesOf")) {
return 6; // Layer 6
layers.add(StaticAbilityLayer.ABILITIES1);
}
if (this.mapParams.containsKey("AddKeyword") || this.mapParams.containsKey("AddAbility")
|| this.mapParams.containsKey("AddTrigger") || this.mapParams.containsKey("RemoveTriggers")
|| this.mapParams.containsKey("RemoveKeyword") || this.mapParams.containsKey("AddReplacementEffects")) {
return 7; // Layer 6 (dependent)
layers.add(StaticAbilityLayer.ABILITIES2);
}
if (this.mapParams.containsKey("CharacteristicDefining")) {
return 8; // Layer 7a
layers.add(StaticAbilityLayer.CHARACTERISTIC);
}
if (this.mapParams.containsKey("AddPower") || this.mapParams.containsKey("AddToughness")
|| this.mapParams.containsKey("SetPower") || this.mapParams.containsKey("SetToughness")) {
return 9; // This is the collection of 7b and 7c
if (this.mapParams.containsKey("SetPower") || this.mapParams.containsKey("SetToughness")) {
layers.add(StaticAbilityLayer.SETPT);
}
if (this.mapParams.containsKey("AddPower") || this.mapParams.containsKey("AddToughness")) {
layers.add(StaticAbilityLayer.MODIFYPT);
}
return 10; // rules change
if (layers.isEmpty()) {
return EnumSet.of(StaticAbilityLayer.RULES);
}
return layers;
}
/**
@@ -174,11 +183,7 @@ public class StaticAbility extends CardTraitBase {
* the host
*/
public StaticAbility(final String params, final Card host) {
final Map<String, String> parsedParams = parseParams(params, host);
this.originalMapParams.putAll(parsedParams);
this.mapParams.putAll(parsedParams);
this.hostCard = host;
this.layer = this.generateLayer();
this(parseParams(params, host), host);
}
/**
@@ -189,37 +194,49 @@ public class StaticAbility extends CardTraitBase {
* @param host
* the host
*/
public StaticAbility(final Map<String, String> params, final Card host) {
private StaticAbility(final Map<String, String> params, final Card host) {
this.originalMapParams.putAll(params);
this.mapParams.putAll(params);
this.layer = this.generateLayer();
this.layers = this.generateLayer();
this.hostCard = host;
}
// apply the ability if it has the right mode
public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer) {
if (!shouldApplyContinuousAbility(layer, false)) {
return null;
}
return StaticAbilityContinuous.applyContinuousAbility(this, layer);
}
public final CardCollectionView applyContinuousAbility(final StaticAbilityLayer layer, final CardCollectionView affected) {
if (!shouldApplyContinuousAbility(layer, true)) {
return null;
}
return StaticAbilityContinuous.applyContinuousAbility(this, affected, layer);
}
/**
* Apply ability.
* Check whether a continuous ability should be applied.
*
* @param mode
* the mode
* @return
* @param layer
* the {@link StaticAbilityLayer} under investigation.
* @param ignoreTempSuppression
* whether to ignore temporary suppression of this ability, to be
* used when this ability has already begun applying in another
* layer and has since been removed from its host card by another
* effect (see rule 613.5).
* @return {@code true} if and only if this is a continuous ability that
* affects the specified layer, it's not suppressed, and its
* conditions are fulfilled.
*/
public final List<Card> applyAbility(final String mode) {
// don't apply the ability if it hasn't got the right mode
if (!this.mapParams.get("Mode").equals(mode)) {
return null;
private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer, final boolean ignoreTempSuppression) {
final boolean isSuppressed;
if (ignoreTempSuppression) {
isSuppressed = this.isNonTempSuppressed();
} else {
isSuppressed = this.isSuppressed();
}
if (this.isSuppressed() || !this.checkConditions()) {
return null;
}
if (mode.equals("Continuous")) {
return StaticAbilityContinuous.applyContinuousAbility(this);
}
return null;
return mapParams.get("Mode").equals("Continuous") && layers.contains(layer) && !isSuppressed && this.checkConditions();
}
// apply the ability if it has the right mode
@@ -557,15 +574,15 @@ public class StaticAbility extends CardTraitBase {
/**
* @return the ignoreEffectCards
*/
public List<Card> getIgnoreEffectCards() {
public CardCollectionView getIgnoreEffectCards() {
return ignoreEffectCards;
}
/**
* @param c the ignoreEffectCards to set
*/
public void setIgnoreEffectCards(List<Card> cards) {
this.ignoreEffectCards = cards;
public void setIgnoreEffectCards(final CardCollectionView cards) {
ignoreEffectCards = cards;
}
/**
@@ -578,27 +595,20 @@ public class StaticAbility extends CardTraitBase {
/**
* @param p the ignoreEffectPlayers to add
*/
public void addIgnoreEffectPlayers(Player p) {
this.ignoreEffectPlayers.add(p);
public void addIgnoreEffectPlayers(final Player p) {
ignoreEffectPlayers.add(p);
}
public void clearIgnoreEffects() {
this.ignoreEffectPlayers.clear();
this.ignoreEffectCards.clear();
ignoreEffectPlayers.clear();
ignoreEffectCards = new CardCollection();
}
/**
* @return the layer
*/
public int getLayer() {
return layer;
public Set<StaticAbilityLayer> getLayers() {
return layers;
}
/**
* @param layer the layer to set
*/
public void setLayer(int layer) {
this.layer = layer;
}
} // end class StaticEffectFactory
} // end class StaticAbility

View File

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

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

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

View File

@@ -199,7 +199,7 @@ public class VAssignDamage {
}
else if (defender instanceof PlayerView) {
final PlayerView p = (PlayerView)defender;
fakeCard = new CardView(-1, defender.toString(), p, CMatchUI.SINGLETON_INSTANCE.avatarImages.get(p.getLobbyPlayerName()));
fakeCard = new CardView(-1, null, defender.toString(), p, CMatchUI.SINGLETON_INSTANCE.avatarImages.get(p.getLobbyPlayerName()));
}
addPanelForDefender(pnlDefenders, fakeCard);
}

View File

@@ -117,9 +117,9 @@ public class CardZoom extends FOverlay {
}
if (item instanceof InventoryItem) {
InventoryItem ii = (InventoryItem)item;
return new CardView(-1, ii.getName(), null, ImageKeys.getImageKey(ii, false));
return new CardView(-1, null, ii.getName(), null, ImageKeys.getImageKey(ii, false));
}
return new CardView(-1, item.toString());
return new CardView(-1, null, item.toString());
}
@Override

View File

@@ -229,7 +229,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
final List<CardView> options = Lists.newArrayList();
for (final Entry<Player, Card> kv : ev.cards.entries()) {
//use fake card so real cards appear with proper formatting
final CardView fakeCard = new CardView(-1, " -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --");
final CardView fakeCard = new CardView(-1, null, " -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --");
options.add(fakeCard);
options.add(kv.getValue().getView());
}

View File

@@ -1205,11 +1205,11 @@ public class PlayerControllerHuman extends PlayerController {
tempShowCards(pile2);
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.addAll(CardView.getCollection(pile1));
final CardView pileView2 = new CardView(Integer.MIN_VALUE + 1, "--- Pile 2 ---");
final CardView pileView2 = new CardView(Integer.MIN_VALUE + 1, null, "--- Pile 2 ---");
cards.add(pileView2);
cards.addAll(CardView.getCollection(pile2));