- Counters moved from being on Card to on GameEntity allowing Poison + Experience Counters to work the same as other counter types

- Improved some PlayerView updates which weren't re-writing player detail hoverbox often enough
This commit is contained in:
Sol
2015-11-18 20:19:32 +00:00
parent 83cd79e705
commit 5527f6fc6d
16 changed files with 323 additions and 123 deletions

2
.gitattributes vendored
View File

@@ -548,6 +548,7 @@ forge-game/src/main/java/forge/game/event/GameEventManaBurn.java -text
forge-game/src/main/java/forge/game/event/GameEventManaPool.java -text forge-game/src/main/java/forge/game/event/GameEventManaPool.java -text
forge-game/src/main/java/forge/game/event/GameEventMulligan.java -text forge-game/src/main/java/forge/game/event/GameEventMulligan.java -text
forge-game/src/main/java/forge/game/event/GameEventPlayerControl.java -text forge-game/src/main/java/forge/game/event/GameEventPlayerControl.java -text
forge-game/src/main/java/forge/game/event/GameEventPlayerCounters.java -text
forge-game/src/main/java/forge/game/event/GameEventPlayerDamaged.java -text forge-game/src/main/java/forge/game/event/GameEventPlayerDamaged.java -text
forge-game/src/main/java/forge/game/event/GameEventPlayerLivesChanged.java -text forge-game/src/main/java/forge/game/event/GameEventPlayerLivesChanged.java -text
forge-game/src/main/java/forge/game/event/GameEventPlayerPoisoned.java -text forge-game/src/main/java/forge/game/event/GameEventPlayerPoisoned.java -text
@@ -4300,6 +4301,7 @@ forge-gui/res/cardsfolder/d/dawnstrider.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/dawnstrike_paladin.txt -text forge-gui/res/cardsfolder/d/dawnstrike_paladin.txt -text
forge-gui/res/cardsfolder/d/dawntreader_elk.txt -text forge-gui/res/cardsfolder/d/dawntreader_elk.txt -text
forge-gui/res/cardsfolder/d/daxos_of_meletis.txt -text forge-gui/res/cardsfolder/d/daxos_of_meletis.txt -text
forge-gui/res/cardsfolder/d/daxos_the_returned.txt -text
forge-gui/res/cardsfolder/d/day_of_destiny.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/day_of_destiny.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/day_of_judgment.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/day_of_judgment.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/day_of_the_dragons.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/day_of_the_dragons.txt svneol=native#text/plain

View File

@@ -1485,8 +1485,15 @@ public class AiController {
} }
for (Player e : enemies) { for (Player e : enemies) {
if (e.getPoisonCounters() > 0) { // TODO In the future check of enemies can get poison counters and give them some other bad counter type
result.put(e, null); // poison counter type is hardcoded at data consumer's side (this works while players may have no other counters) if (e.getCounters(CounterType.POISON) > 0) {
result.put(e, CounterType.POISON);
}
}
for (Player pl : allies) {
if (pl.getCounters(CounterType.EXPERIENCE) > 0) {
result.put(pl, CounterType.EXPERIENCE);
} }
} }

View File

@@ -20,6 +20,7 @@ package forge.game;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CounterType;
import forge.game.event.GameEventCardAttachment; import forge.game.event.GameEventCardAttachment;
import forge.game.event.GameEventCardAttachment.AttachMethod; import forge.game.event.GameEventCardAttachment.AttachMethod;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
@@ -34,6 +35,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
private int preventNextDamage = 0; private int preventNextDamage = 0;
private CardCollection enchantedBy; private CardCollection enchantedBy;
private Map<Card, Map<String, String>> preventionShieldsWithEffects = new TreeMap<Card, Map<String, String>>(); private Map<Card, Map<String, String>> preventionShieldsWithEffects = new TreeMap<Card, Map<String, String>>();
protected Map<CounterType, Integer> counters = new TreeMap<>();
protected GameEntity(int id0) { protected GameEntity(int id0) {
id = id0; id = id0;
@@ -198,6 +200,33 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
public abstract boolean hasProtectionFrom(final Card source); public abstract boolean hasProtectionFrom(final Card source);
// Counters!
public boolean hasCounters() {
return !counters.isEmpty();
}
// get all counters from a card
public final Map<CounterType, Integer> getCounters() {
return counters;
}
public final int getCounters(final CounterType counterName) {
Integer value = counters.get(counterName);
return value == null ? 0 : value;
}
public void setCounters(final CounterType counterType, final Integer num) {
counters.put(counterType, num);
}
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
abstract public boolean canReceiveCounters(final CounterType type);
abstract protected void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents);
abstract public void subtractCounter(final CounterType counterName, final int n);
abstract public void clearCounters();
@Override @Override
public final boolean equals(Object o) { public final boolean equals(Object o) {
if (o == null) { return false; } if (o == null) { return false; }

View File

@@ -29,7 +29,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
return; return;
for(Entry<GameEntity, CounterType> ge: proliferateChoice.entrySet()) { for(Entry<GameEntity, CounterType> ge: proliferateChoice.entrySet()) {
if( ge.getKey() instanceof Player ) if( ge.getKey() instanceof Player )
((Player) ge.getKey()).addPoisonCounters(1, sa.getHostCard()); ((Player) ge.getKey()).addCounter(ge.getValue(), 1, true);
else if( ge.getKey() instanceof Card) else if( ge.getKey() instanceof Card)
((Card) ge.getKey()).addCounter(ge.getValue(), 1, true); ((Card) ge.getKey()).addCounter(ge.getValue(), 1, true);
} }

View File

@@ -1,5 +1,7 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import com.google.common.collect.Lists;
import forge.game.GameObject;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
@@ -95,15 +97,19 @@ public class CountersPutEffect extends SpellAbilityEffect {
} }
CardCollection tgtCards = new CardCollection(); CardCollection tgtCards = new CardCollection();
List<GameObject> tgtObjects = Lists.newArrayList();
if (sa.hasParam("Bolster")) { if (sa.hasParam("Bolster")) {
CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES); CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense)); CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense));
tgtCards.addAll(activator.getController().chooseCardsForEffect(leastToughness, sa, "Choose a creature with the least toughness", 1, 1, false)); tgtCards.addAll(activator.getController().chooseCardsForEffect(leastToughness, sa, "Choose a creature with the least toughness", 1, 1, false));
tgtObjects.addAll(tgtCards);
} else { } else {
tgtCards.addAll(getDefinedCardsOrTargeted(sa)); tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined"));
} }
for (final Card tgtCard : tgtCards) { for (final GameObject obj : tgtObjects) {
if (obj instanceof Card) {
Card tgtCard = (Card) obj;
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount; counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount;
if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) { if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) {
if (max != -1) { if (max != -1) {
@@ -153,6 +159,11 @@ public class CountersPutEffect extends SpellAbilityEffect {
tgtCard.addCounter(counterType, counterAmount, etbcounter); tgtCard.addCounter(counterType, counterAmount, etbcounter);
} }
} }
} else if (obj instanceof Player) {
// Add Counters to players!
Player pl = (Player) obj;
pl.addCounter(counterType, counterAmount, true);
}
} }
} }

View File

@@ -92,7 +92,6 @@ public class Card extends GameEntity implements Comparable<Card> {
private ZoneType castFrom = null; private ZoneType castFrom = null;
private final CardDamageHistory damageHistory = new CardDamageHistory(); private final CardDamageHistory damageHistory = new CardDamageHistory();
private Map<CounterType, Integer> counters = new TreeMap<>();
private Map<Card, Map<CounterType, Integer>> countersAddedBy = new TreeMap<>(); private Map<Card, Map<CounterType, Integer>> countersAddedBy = new TreeMap<>();
private List<String> extrinsicKeyword = new ArrayList<>(); private List<String> extrinsicKeyword = new ArrayList<>();
// Hidden keywords won't be displayed on the card // Hidden keywords won't be displayed on the card
@@ -911,7 +910,7 @@ public class Card extends GameEntity implements Comparable<Card> {
addCounter(counterType, n, applyMultiplier, false); addCounter(counterType, n, applyMultiplier, false);
} }
private void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents) { protected void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents) {
int addAmount = n; int addAmount = n;
if(addAmount < 0) { if(addAmount < 0) {
addAmount = 0; // As per rule 107.1b addAmount = 0; // As per rule 107.1b
@@ -1043,20 +1042,6 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} }
public final int getCounters(final CounterType counterName) {
Integer value = counters.get(counterName);
return value == null ? 0 : value;
}
// get all counters from a card
public final Map<CounterType, Integer> getCounters() {
return counters;
}
public final boolean hasCounters() {
return !counters.isEmpty();
}
public final void setCounters(final Map<CounterType, Integer> allCounters) { public final void setCounters(final Map<CounterType, Integer> allCounters) {
counters = allCounters; counters = allCounters;
view.updateCounters(this); view.updateCounters(this);

View File

@@ -1158,6 +1158,12 @@ public class CardFactoryUtil {
return doXMath(c.getTotalDamageRecievedThisTurn(), m, c); return doXMath(c.getTotalDamageRecievedThisTurn(), m, c);
} }
if (sq[0].startsWith("YourCounters")) {
// "YourCountersExperience" or "YourCountersPoison"
String counterType = sq[0].substring(12);
return doXMath(cc.getCounters(CounterType.getType(counterType)), m, c);
}
if (sq[0].contains("YourPoisonCounters")) { if (sq[0].contains("YourPoisonCounters")) {
return doXMath(cc.getPoisonCounters(), m, c); return doXMath(cc.getPoisonCounters(), m, c);
} }

View File

@@ -268,7 +268,13 @@ public enum CounterType {
WIND(), WIND(),
WISH(); WISH(),
// Player Counters
EXPERIENCE(),
POISON();
private String name; private String name;

View File

@@ -0,0 +1,23 @@
package forge.game.event;
import forge.game.card.CounterType;
import forge.game.player.Player;
public class GameEventPlayerCounters extends GameEvent {
public final Player receiver;
public final CounterType type;
public final int oldValue;
public final int amount;
public GameEventPlayerCounters(Player recv, CounterType t, int old, int num) {
receiver = recv;
type = t;
oldValue = old;
amount = num;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
}

View File

@@ -33,6 +33,7 @@ public interface IGameEventVisitor<T> {
T visit(GameEventMulligan event); T visit(GameEventMulligan event);
T visit(GameEventPlayerControl event); T visit(GameEventPlayerControl event);
T visit(GameEventPlayerDamaged gameEventPlayerDamaged); T visit(GameEventPlayerDamaged gameEventPlayerDamaged);
T visit(GameEventPlayerCounters event);
T visit(GameEventPlayerPoisoned event); T visit(GameEventPlayerPoisoned event);
T visit(GameEventPlayerPriority event); T visit(GameEventPlayerPriority event);
T visit(GameEventPlayerStatsChanged event); T visit(GameEventPlayerStatsChanged event);
@@ -77,6 +78,7 @@ public interface IGameEventVisitor<T> {
public T visit(GameEventManaBurn event) { return null; } public T visit(GameEventManaBurn event) { return null; }
public T visit(GameEventMulligan event) { return null; } public T visit(GameEventMulligan event) { return null; }
public T visit(GameEventPlayerControl event) { return null; } public T visit(GameEventPlayerControl event) { return null; }
public T visit(GameEventPlayerCounters event) { return null; }
public T visit(GameEventPlayerPoisoned event) { return null; } public T visit(GameEventPlayerPoisoned event) { return null; }
public T visit(GameEventPlayerPriority event) { return null; } public T visit(GameEventPlayerPriority event) { return null; }
public T visit(GameEventPlayerStatsChanged event) { return null; } public T visit(GameEventPlayerStatsChanged event) { return null; }

View File

@@ -32,12 +32,7 @@ import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.ability.effects.DetachedCardEffect; import forge.game.ability.effects.DetachedCardEffect;
import forge.game.card.Card; import forge.game.card.*;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.event.*; import forge.game.event.*;
import forge.game.keyword.KeywordCollection; import forge.game.keyword.KeywordCollection;
@@ -88,7 +83,6 @@ public class Player extends GameEntity implements Comparable<Player> {
private final Map<Card, Integer> commanderDamage = new HashMap<Card, Integer>(); private final Map<Card, Integer> commanderDamage = new HashMap<Card, Integer>();
private int poisonCounters = 0;
private int life = 20; private int life = 20;
private int startingLife = 20; private int startingLife = 20;
private final Map<Card, Integer> assignedDamage = new HashMap<Card, Integer>(); private final Map<Card, Integer> assignedDamage = new HashMap<Card, Integer>();
@@ -853,25 +847,144 @@ public class Player extends GameEntity implements Comparable<Player> {
return false; return false;
} }
public final boolean canReceiveCounters(final CounterType type) {
if (hasKeyword("PLAYER can't have counters placed on him or her.")) {
return false;
}
if (type == CounterType.POISON) {
if (hasKeyword("You can't get poison counters")) {
return false;
}
}
return true;
}
public final void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier) {
addCounter(counterType, n, applyMultiplier, true);
}
@Override
protected void addCounter(CounterType counterType, int n, boolean applyMultiplier, boolean fireEvents) {
if (!canReceiveCounters(counterType)) {
return;
}
int addAmount = n;
if(addAmount <= 0) {
// Can't add negative or 0 counters, bail out now
return;
}
/* TODO Add Counter replacement if it ever effects Players
final HashMap<String, Object> repParams = new HashMap<>();
repParams.put("Event", "AddCounter");
repParams.put("Affected", this);
repParams.put("CounterType", counterType);
repParams.put("CounterNum", addAmount);
repParams.put("EffectOnly", applyMultiplier);
if (getGame().getReplacementHandler().run(repParams) != ReplacementResult.NotReplaced) {
return;
}
*/
final int oldValue = getCounters(counterType);
final int newValue = addAmount + oldValue;
counters.put(counterType, newValue);
view.updateCounters(this);
if (fireEvents) {
getGame().fireEvent(new GameEventPlayerCounters(this, counterType, oldValue, newValue));
}
/* TODO Run triggers when something cares
final Map<String, Object> runParams = new TreeMap<>();
runParams.put("Player", this);
runParams.put("CounterType", counterType);
for (int i = 0; i < addAmount; i++) {
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAdded, runParams, false);
}
if (addAmount > 0) {
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, runParams, false);
}
*/
}
@Override
public void subtractCounter(CounterType counterName, int num) {
int oldValue = getCounters(counterName);
int newValue = Math.max(oldValue - num, 0);
final int delta = oldValue - newValue;
if (delta == 0) { return; }
if (newValue > 0) {
counters.put(counterName, newValue);
}
else {
counters.remove(counterName);
}
view.updateCounters(this);
getGame().fireEvent(new GameEventPlayerCounters(this, counterName, oldValue, newValue));
/* TODO Run triggers when something cares
int curCounters = oldValue;
for (int i = 0; i < delta && curCounters != 0; i++) {
final Map<String, Object> runParams = new TreeMap<>();
runParams.put("Card", this);
runParams.put("CounterType", counterName);
runParams.put("NewCounterAmount", --curCounters);
getGame().getTriggerHandler().runTrigger(TriggerType.CounterRemoved, runParams, false);
}
*/
}
public final void clearCounters() {
if (counters.isEmpty()) { return; }
counters.clear();
view.updateCounters(this);
getGame().fireEvent(new GameEventPlayerCounters(this, null, 0, 0));
}
public void setCounters(final CounterType counterType, final Integer num) {
counters.put(counterType, num);
view.updateCounters(this);
getGame().fireEvent(new GameEventPlayerCounters(this, counterType, 0, 0));
}
@Override
public void setCounters(Map<CounterType, Integer> allCounters) {
counters = allCounters;
view.updateCounters(this);
getGame().fireEvent(new GameEventPlayerCounters(this, null, 0, 0));
}
// TODO Merge These calls into the primary counter calls
public final int getPoisonCounters() { public final int getPoisonCounters() {
return poisonCounters; return getCounters(CounterType.POISON);
} }
public final void setPoisonCounters(final int num, Card source) { public final void setPoisonCounters(final int num, Card source) {
if (poisonCounters == num) { return; } int oldPoison = getCounters(CounterType.POISON);
setCounters(CounterType.POISON, num);
int oldPoison = poisonCounters;
poisonCounters = num;
view.updatePoisonCounters(this);
game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num));
} }
public final void addPoisonCounters(final int num, final Card source) { public final void addPoisonCounters(final int num, final Card source) {
if (!hasKeyword("You can't get poison counters")) { int oldPoison = getCounters(CounterType.POISON);
setPoisonCounters(poisonCounters + num, source); addCounter(CounterType.POISON, num, false, true);
if (oldPoison != getCounters(CounterType.POISON)) {
game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num));
} }
} }
public final void removePoisonCounters(final int num, final Card source) { public final void removePoisonCounters(final int num, final Card source) {
setPoisonCounters(poisonCounters - num, source); int oldPoison = getCounters(CounterType.POISON);
subtractCounter(CounterType.POISON, num);
if (oldPoison != getCounters(CounterType.POISON)) {
game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num));
} }
}
// ================ POISON Merged =================================
public final void addChangedKeywords(final String[] addKeywords, final String[] removeKeywords, final Long timestamp) { public final void addChangedKeywords(final String[] addKeywords, final String[] removeKeywords, final Long timestamp) {
addChangedKeywords(ImmutableList.copyOf(addKeywords), ImmutableList.copyOf(removeKeywords), timestamp); addChangedKeywords(ImmutableList.copyOf(addKeywords), ImmutableList.copyOf(removeKeywords), timestamp);
@@ -1681,7 +1794,7 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
// Rule 704.5c - If a player has ten or more poison counters, he or she loses the game. // Rule 704.5c - If a player has ten or more poison counters, he or she loses the game.
if (poisonCounters >= 10) { if (getCounters(CounterType.POISON) >= 10) {
return loseConditionMet(GameLossReason.Poisoned, null); return loseConditionMet(GameLossReason.Poisoned, null);
} }

View File

@@ -6,6 +6,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import forge.game.card.CounterType;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Objects; import com.google.common.base.Objects;
@@ -159,6 +160,13 @@ public class PlayerView extends GameEntityView {
set(TrackableProperty.PoisonCounters, p.getPoisonCounters()); set(TrackableProperty.PoisonCounters, p.getPoisonCounters());
} }
public Map<CounterType, Integer> getCounters() {
return get(TrackableProperty.Counters);
}
void updateCounters(Player p) {
set(TrackableProperty.Counters, p.getCounters());
}
public int getMaxHandSize() { public int getMaxHandSize() {
return get(TrackableProperty.MaxHandSize); return get(TrackableProperty.MaxHandSize);
} }
@@ -359,10 +367,16 @@ public class PlayerView extends GameEntityView {
private List<String> getDetailsList() { private List<String> getDetailsList() {
final List<String> details = Lists.newArrayListWithCapacity(8); final List<String> details = Lists.newArrayListWithCapacity(8);
details.add(String.format("Life: %d", getLife())); details.add(String.format("Life: %d", getLife()));
final int poison = getPoisonCounters();
if (poison > 0) { Map<CounterType, Integer> counters = getCounters();
details.add(String.format("Poison counters: %d", poison)); if (counters != null) {
for (Entry<CounterType, Integer> p : counters.entrySet()) {
if (p.getValue() > 0) {
details.add(String.format("%s counters: %d", p.getKey().getName(), p.getValue()));
} }
}
}
details.add(String.format("Cards in hand: %d/%s", getHandSize(), getMaxHandString())); details.add(String.format("Cards in hand: %d/%s", getHandSize(), getMaxHandString()));
details.add(String.format("Cards drawn this turn: %d", getNumDrawnThisTurn())); details.add(String.format("Cards drawn this turn: %d", getNumDrawnThisTurn()));
details.add(String.format("Damage prevention: %d", getPreventNextDamage())); details.add(String.format("Damage prevention: %d", getPreventNextDamage()));

View File

@@ -12,6 +12,7 @@ public enum TrackableProperty {
Text(TrackableTypes.StringType), Text(TrackableTypes.StringType),
PreventNextDamage(TrackableTypes.IntegerType), PreventNextDamage(TrackableTypes.IntegerType),
EnchantedBy(TrackableTypes.CardViewCollectionType), EnchantedBy(TrackableTypes.CardViewCollectionType),
Counters(TrackableTypes.CounterMapType),
//Card //Card
Owner(TrackableTypes.PlayerViewType), Owner(TrackableTypes.PlayerViewType),
@@ -27,7 +28,6 @@ public enum TrackableProperty {
Tapped(TrackableTypes.BooleanType), Tapped(TrackableTypes.BooleanType),
Token(TrackableTypes.BooleanType), Token(TrackableTypes.BooleanType),
IsCommander(TrackableTypes.BooleanType), IsCommander(TrackableTypes.BooleanType),
Counters(TrackableTypes.CounterMapType),
Damage(TrackableTypes.IntegerType), Damage(TrackableTypes.IntegerType),
AssignedDamage(TrackableTypes.IntegerType), AssignedDamage(TrackableTypes.IntegerType),
ShieldCount(TrackableTypes.IntegerType), ShieldCount(TrackableTypes.IntegerType),

View File

@@ -0,0 +1,12 @@
Name:Daxos the Returned
ManaCost:1 W B
Types:Legendary Creature Zombie Solder
PT:2/2
T:Mode$ SpellCast | ValidCard$ Enchantment | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast an enchantment spell, you get an experience counter.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ You | CounterType$ Experience | CounterNum$ 1
A:AB$ Token | Cost$ 1 W B | TokenAmount$ 1 | TokenName$ Spirit | TokenTypes$ Creature,Spirit | TokenOwner$ You | TokenColors$ White,Black | TokenPower$ X | TokenToughness$ X | TokenImage$ W B N N Spirit | TokenStaticAbilities$ Static | TokenSVars$ X | References$ X | SpellDescription$ Put a white and black Spirit enchantment creature token onto the battlefield. It has "This creature's power and toughness are each equal to the number of experience counters you have."
SVar:Static:Mode$ Continuous | SetPower$ X | SetToughness$ X | CharacteristicDefining$ True | Description$ This creature's power and toughness are each equal to the number of experience counters you have.
SVar:X:Count$YourCountersExperience
SVar:BuffedBy:Enchantment
SVar:Picture:http://www.wizards.com/global/images/magic/general/daxos_the_returned.jpg
Oracle:Whenever you cast an enchantment spell, you get an experience counter.\n{1}{W}{B}: Put a white and black Spirit enchantment creature token onto the battlefield. It has "This creature's power and toughness are each equal to the number of experience counters you have."

View File

@@ -14,35 +14,7 @@ import forge.game.Game;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardView; import forge.game.card.CardView;
import forge.game.event.GameEvent; import forge.game.event.*;
import forge.game.event.GameEventAnteCardsSelected;
import forge.game.event.GameEventAttackersDeclared;
import forge.game.event.GameEventBlockersDeclared;
import forge.game.event.GameEventCardAttachment;
import forge.game.event.GameEventCardChangeZone;
import forge.game.event.GameEventCardCounters;
import forge.game.event.GameEventCardDamaged;
import forge.game.event.GameEventCardPhased;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.event.GameEventCardTapped;
import forge.game.event.GameEventCombatChanged;
import forge.game.event.GameEventCombatEnded;
import forge.game.event.GameEventGameFinished;
import forge.game.event.GameEventGameOutcome;
import forge.game.event.GameEventManaPool;
import forge.game.event.GameEventPlayerControl;
import forge.game.event.GameEventPlayerLivesChanged;
import forge.game.event.GameEventPlayerPoisoned;
import forge.game.event.GameEventPlayerPriority;
import forge.game.event.GameEventPlayerStatsChanged;
import forge.game.event.GameEventShuffle;
import forge.game.event.GameEventSpellAbilityCast;
import forge.game.event.GameEventSpellRemovedFromStack;
import forge.game.event.GameEventSpellResolved;
import forge.game.event.GameEventTurnBegan;
import forge.game.event.GameEventTurnPhase;
import forge.game.event.GameEventZone;
import forge.game.event.IGameEventVisitor;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZone;
@@ -210,6 +182,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
@Override @Override
public Void visit(final GameEventTurnBegan event) { public Void visit(final GameEventTurnBegan event) {
turnUpdate = event.turnOwner.getView(); turnUpdate = event.turnOwner.getView();
processPlayer(event.turnOwner, livesUpdate);
if (FModel.getPreferences().getPrefBoolean(FPref.UI_STACK_CREATURES) && event.turnOwner != null) { if (FModel.getPreferences().getPrefBoolean(FPref.UI_STACK_CREATURES) && event.turnOwner != null) {
// anything except stack will get here // anything except stack will get here
updateZone(event.turnOwner, ZoneType.Battlefield); updateZone(event.turnOwner, ZoneType.Battlefield);
@@ -374,7 +347,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
final CardCollection cards = new CardCollection(); final CardCollection cards = new CardCollection();
for (final Player p : event.players) { for (final Player p : event.players) {
cards.addAll(p.getAllCards()); cards.addAll(p.getAllCards());
processPlayer(p, livesUpdate);
} }
return processCards(cards, cardsRefreshDetails); return processCards(cards, cardsRefreshDetails);
} }
@@ -397,4 +372,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(final GameEventPlayerPoisoned event) { public Void visit(final GameEventPlayerPoisoned event) {
return processPlayer(event.receiver, livesUpdate); return processPlayer(event.receiver, livesUpdate);
} }
@Override
public Void visit(final GameEventPlayerCounters event) {
return processPlayer(event.receiver, livesUpdate);
}
} }

View File

@@ -31,14 +31,9 @@ public final class InputProliferate extends InputSelectManyBase<GameEntity> {
} }
else { else {
for (final Entry<GameEntity, CounterType> ge : chosenCounters.entrySet()) { for (final Entry<GameEntity, CounterType> ge : chosenCounters.entrySet()) {
if (ge.getKey() instanceof Player) {
sb.append("* A poison counter to player ").append(ge.getKey()).append("\n");
}
else {
sb.append("* ").append(ge.getKey()).append(" -> ").append(ge.getValue()).append("counter\n"); sb.append("* ").append(ge.getKey()).append(" -> ").append(ge.getValue()).append("counter\n");
} }
} }
}
return sb.toString(); return sb.toString();
} }
@@ -79,7 +74,8 @@ public final class InputProliferate extends InputSelectManyBase<GameEntity> {
@Override @Override
protected final void onPlayerSelected(final Player player, final ITriggerEvent triggerEvent) { protected final void onPlayerSelected(final Player player, final ITriggerEvent triggerEvent) {
if (player.getPoisonCounters() == 0 || player.hasKeyword("You can't get poison counters")) { if (!player.hasCounters()) {
// Can't select a player without counters
return; return;
} }
@@ -87,7 +83,21 @@ public final class InputProliferate extends InputSelectManyBase<GameEntity> {
if (entityWasSelected) { if (entityWasSelected) {
this.chosenCounters.remove(player); this.chosenCounters.remove(player);
} else { } else {
this.chosenCounters.put(player, null /* POISON counter is meant */); final List<CounterType> choices = new ArrayList<CounterType>();
for (final CounterType ct : player.getCounters().keySet()) {
if (player.getCounters(ct) > 0) {
choices.add(ct);
}
}
if (player.hasKeyword("You can't get poison counters")) {
choices.remove(CounterType.POISON);
}
final CounterType toAdd = choices.size() == 1 ? choices.get(0) : getController().getGui().one("Select counter type", choices);
chosenCounters.put(player, toAdd);
this.chosenCounters.put(player, CounterType.POISON);
} }
refresh(); refresh();