From 5527f6fc6dcf456e47ddab62243ef55a004c388c Mon Sep 17 00:00:00 2001 From: Sol Date: Wed, 18 Nov 2015 20:19:32 +0000 Subject: [PATCH] - 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 --- .gitattributes | 2 + .../src/main/java/forge/ai/AiController.java | 11 +- .../src/main/java/forge/game/GameEntity.java | 29 ++++ .../effects/CountersProliferateEffect.java | 2 +- .../ability/effects/CountersPutEffect.java | 101 ++++++------ .../src/main/java/forge/game/card/Card.java | 17 +- .../java/forge/game/card/CardFactoryUtil.java | 6 + .../java/forge/game/card/CounterType.java | 8 +- .../game/event/GameEventPlayerCounters.java | 23 +++ .../forge/game/event/IGameEventVisitor.java | 2 + .../main/java/forge/game/player/Player.java | 147 ++++++++++++++++-- .../java/forge/game/player/PlayerView.java | 20 ++- .../forge/trackable/TrackableProperty.java | 2 +- .../res/cardsfolder/d/daxos_the_returned.txt | 12 ++ .../control/FControlGameEventHandler.java | 38 ++--- .../forge/match/input/InputProliferate.java | 26 +++- 16 files changed, 323 insertions(+), 123 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/event/GameEventPlayerCounters.java create mode 100644 forge-gui/res/cardsfolder/d/daxos_the_returned.txt diff --git a/.gitattributes b/.gitattributes index 0fbaea1ae61..348b47e9d70 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/GameEventMulligan.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/GameEventPlayerLivesChanged.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/dawntreader_elk.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_judgment.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/day_of_the_dragons.txt svneol=native#text/plain diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 32007742f50..a8f9d07c68e 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1485,8 +1485,15 @@ public class AiController { } for (Player e : enemies) { - if (e.getPoisonCounters() > 0) { - result.put(e, null); // poison counter type is hardcoded at data consumer's side (this works while players may have no other counters) + // TODO In the future check of enemies can get poison counters and give them some other bad counter type + 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); } } diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index d1d8054c0ff..9073a850317 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -20,6 +20,7 @@ package forge.game; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; +import forge.game.card.CounterType; import forge.game.event.GameEventCardAttachment; import forge.game.event.GameEventCardAttachment.AttachMethod; import forge.util.collect.FCollection; @@ -34,6 +35,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { private int preventNextDamage = 0; private CardCollection enchantedBy; private Map> preventionShieldsWithEffects = new TreeMap>(); + protected Map counters = new TreeMap<>(); protected GameEntity(int id0) { id = id0; @@ -198,6 +200,33 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { public abstract boolean hasProtectionFrom(final Card source); + // Counters! + public boolean hasCounters() { + return !counters.isEmpty(); + } + + // get all counters from a card + public final Map 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 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 public final boolean equals(Object o) { if (o == null) { return false; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java index 2dd07da7463..fb65b6fa915 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java @@ -29,7 +29,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect { return; for(Entry ge: proliferateChoice.entrySet()) { 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) ((Card) ge.getKey()).addCounter(ge.getValue(), 1, true); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index e69d9fcaeaf..270be889d4d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -1,5 +1,7 @@ package forge.game.ability.effects; +import com.google.common.collect.Lists; +import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -95,63 +97,72 @@ public class CountersPutEffect extends SpellAbilityEffect { } CardCollection tgtCards = new CardCollection(); + List tgtObjects = Lists.newArrayList(); if (sa.hasParam("Bolster")) { CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES); 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)); + tgtObjects.addAll(tgtCards); } else { - tgtCards.addAll(getDefinedCardsOrTargeted(sa)); + tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined")); } - for (final Card tgtCard : tgtCards) { - counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount; - if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) { - if (max != -1) { - counterAmount = max - tgtCard.getCounters(counterType); - } - if (sa.hasParam("Tribute")) { - String message = "Do you want to put " + tgtCard.getKeywordMagnitude("Tribute") + " +1/+1 counters on " + tgtCard + " ?"; - Player chooser = activator.getController().chooseSingleEntityForEffect(activator.getOpponents(), sa, "Choose an opponent"); - if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) { - tgtCard.setTributed(true); - } else { - continue; + for (final GameObject obj : tgtObjects) { + if (obj instanceof Card) { + Card tgtCard = (Card) obj; + counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount; + if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) { + if (max != -1) { + counterAmount = max - tgtCard.getCounters(counterType); } - } - if (rememberCards) { - card.addRemembered(tgtCard); - } - final Zone zone = tgtCard.getGame().getZoneOf(tgtCard); - if (zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack)) { - tgtCard.addCounter(counterType, counterAmount, true); - if (remember) { - final int value = tgtCard.getTotalCountersToAdd(); - tgtCard.addCountersAddedBy(card, counterType, value); + if (sa.hasParam("Tribute")) { + String message = "Do you want to put " + tgtCard.getKeywordMagnitude("Tribute") + " +1/+1 counters on " + tgtCard + " ?"; + Player chooser = activator.getController().chooseSingleEntityForEffect(activator.getOpponents(), sa, "Choose an opponent"); + if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) { + tgtCard.setTributed(true); + } else { + continue; + } } + if (rememberCards) { + card.addRemembered(tgtCard); + } + final Zone zone = tgtCard.getGame().getZoneOf(tgtCard); + if (zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack)) { + tgtCard.addCounter(counterType, counterAmount, true); + if (remember) { + final int value = tgtCard.getTotalCountersToAdd(); + tgtCard.addCountersAddedBy(card, counterType, value); + } - if (sa.hasParam("Evolve")) { - final HashMap runParams = new HashMap(); - runParams.put("Card", tgtCard); - tgtCard.getController().getGame().getTriggerHandler().runTrigger(TriggerType.Evolved, runParams, false); + if (sa.hasParam("Evolve")) { + final HashMap runParams = new HashMap(); + runParams.put("Card", tgtCard); + tgtCard.getController().getGame().getTriggerHandler().runTrigger(TriggerType.Evolved, runParams, false); + } + if (sa.hasParam("Monstrosity")) { + tgtCard.setMonstrous(true); + tgtCard.setMonstrosityNum(counterAmount); + final HashMap runParams = new HashMap(); + runParams.put("Card", tgtCard); + tgtCard.getController().getGame().getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false); + } + if (sa.hasParam("Renown")) { + tgtCard.setRenowned(true); + final HashMap runParams = new HashMap(); + runParams.put("Card", tgtCard); + tgtCard.getController().getGame().getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, runParams, false); + } + } else { + // adding counters to something like re-suspend cards + // etbcounter should apply multiplier + tgtCard.addCounter(counterType, counterAmount, etbcounter); } - if (sa.hasParam("Monstrosity")) { - tgtCard.setMonstrous(true); - tgtCard.setMonstrosityNum(counterAmount); - final HashMap runParams = new HashMap(); - runParams.put("Card", tgtCard); - tgtCard.getController().getGame().getTriggerHandler().runTrigger(TriggerType.BecomeMonstrous, runParams, false); - } - if (sa.hasParam("Renown")) { - tgtCard.setRenowned(true); - final HashMap runParams = new HashMap(); - runParams.put("Card", tgtCard); - tgtCard.getController().getGame().getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, runParams, false); - } - } else { - // adding counters to something like re-suspend cards - // etbcounter should apply multiplier - tgtCard.addCounter(counterType, counterAmount, etbcounter); } + } else if (obj instanceof Player) { + // Add Counters to players! + Player pl = (Player) obj; + pl.addCounter(counterType, counterAmount, true); } } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 30c71645983..62b84cb3904 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -92,7 +92,6 @@ public class Card extends GameEntity implements Comparable { private ZoneType castFrom = null; private final CardDamageHistory damageHistory = new CardDamageHistory(); - private Map counters = new TreeMap<>(); private Map> countersAddedBy = new TreeMap<>(); private List extrinsicKeyword = new ArrayList<>(); // Hidden keywords won't be displayed on the card @@ -911,7 +910,7 @@ public class Card extends GameEntity implements Comparable { 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; if(addAmount < 0) { addAmount = 0; // As per rule 107.1b @@ -1043,20 +1042,6 @@ public class Card extends GameEntity implements Comparable { } } - 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 getCounters() { - return counters; - } - - public final boolean hasCounters() { - return !counters.isEmpty(); - } - public final void setCounters(final Map allCounters) { counters = allCounters; view.updateCounters(this); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 814958f8813..44288c52368 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1158,6 +1158,12 @@ public class CardFactoryUtil { 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")) { return doXMath(cc.getPoisonCounters(), m, c); } diff --git a/forge-game/src/main/java/forge/game/card/CounterType.java b/forge-game/src/main/java/forge/game/card/CounterType.java index 423a29e7be1..7f7d0370086 100644 --- a/forge-game/src/main/java/forge/game/card/CounterType.java +++ b/forge-game/src/main/java/forge/game/card/CounterType.java @@ -268,7 +268,13 @@ public enum CounterType { WIND(), - WISH(); + WISH(), + + // Player Counters + + EXPERIENCE(), + + POISON(); private String name; diff --git a/forge-game/src/main/java/forge/game/event/GameEventPlayerCounters.java b/forge-game/src/main/java/forge/game/event/GameEventPlayerCounters.java new file mode 100644 index 00000000000..374f732fe09 --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventPlayerCounters.java @@ -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 visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java index b60c79d5c56..599d6d8527a 100644 --- a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java +++ b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java @@ -33,6 +33,7 @@ public interface IGameEventVisitor { T visit(GameEventMulligan event); T visit(GameEventPlayerControl event); T visit(GameEventPlayerDamaged gameEventPlayerDamaged); + T visit(GameEventPlayerCounters event); T visit(GameEventPlayerPoisoned event); T visit(GameEventPlayerPriority event); T visit(GameEventPlayerStatsChanged event); @@ -77,6 +78,7 @@ public interface IGameEventVisitor { public T visit(GameEventManaBurn event) { return null; } public T visit(GameEventMulligan 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(GameEventPlayerPriority event) { return null; } public T visit(GameEventPlayerStatsChanged event) { return null; } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 7f5bee52099..08041e11e26 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -32,12 +32,7 @@ import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.ability.effects.DetachedCardEffect; -import forge.game.card.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.*; import forge.game.card.CardPredicates.Presets; import forge.game.event.*; import forge.game.keyword.KeywordCollection; @@ -88,7 +83,6 @@ public class Player extends GameEntity implements Comparable { private final Map commanderDamage = new HashMap(); - private int poisonCounters = 0; private int life = 20; private int startingLife = 20; private final Map assignedDamage = new HashMap(); @@ -853,25 +847,144 @@ public class Player extends GameEntity implements Comparable { 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 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 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 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 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() { - return poisonCounters; + return getCounters(CounterType.POISON); } public final void setPoisonCounters(final int num, Card source) { - if (poisonCounters == num) { return; } - - int oldPoison = poisonCounters; - poisonCounters = num; - view.updatePoisonCounters(this); + int oldPoison = getCounters(CounterType.POISON); + setCounters(CounterType.POISON, num); game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); } public final void addPoisonCounters(final int num, final Card source) { - if (!hasKeyword("You can't get poison counters")) { - setPoisonCounters(poisonCounters + num, source); + int oldPoison = getCounters(CounterType.POISON); + 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) { - 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) { addChangedKeywords(ImmutableList.copyOf(addKeywords), ImmutableList.copyOf(removeKeywords), timestamp); @@ -1681,7 +1794,7 @@ public class Player extends GameEntity implements Comparable { } // 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); } diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index 1bc11c5e2f5..edce4b36283 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import forge.game.card.CounterType; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Objects; @@ -159,6 +160,13 @@ public class PlayerView extends GameEntityView { set(TrackableProperty.PoisonCounters, p.getPoisonCounters()); } + public Map getCounters() { + return get(TrackableProperty.Counters); + } + void updateCounters(Player p) { + set(TrackableProperty.Counters, p.getCounters()); + } + public int getMaxHandSize() { return get(TrackableProperty.MaxHandSize); } @@ -359,10 +367,16 @@ public class PlayerView extends GameEntityView { private List getDetailsList() { final List details = Lists.newArrayListWithCapacity(8); details.add(String.format("Life: %d", getLife())); - final int poison = getPoisonCounters(); - if (poison > 0) { - details.add(String.format("Poison counters: %d", poison)); + + Map counters = getCounters(); + if (counters != null) { + for (Entry 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 drawn this turn: %d", getNumDrawnThisTurn())); details.add(String.format("Damage prevention: %d", getPreventNextDamage())); diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 6e9b8a56e0f..35a9904db0c 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -12,6 +12,7 @@ public enum TrackableProperty { Text(TrackableTypes.StringType), PreventNextDamage(TrackableTypes.IntegerType), EnchantedBy(TrackableTypes.CardViewCollectionType), + Counters(TrackableTypes.CounterMapType), //Card Owner(TrackableTypes.PlayerViewType), @@ -27,7 +28,6 @@ public enum TrackableProperty { Tapped(TrackableTypes.BooleanType), Token(TrackableTypes.BooleanType), IsCommander(TrackableTypes.BooleanType), - Counters(TrackableTypes.CounterMapType), Damage(TrackableTypes.IntegerType), AssignedDamage(TrackableTypes.IntegerType), ShieldCount(TrackableTypes.IntegerType), diff --git a/forge-gui/res/cardsfolder/d/daxos_the_returned.txt b/forge-gui/res/cardsfolder/d/daxos_the_returned.txt new file mode 100644 index 00000000000..b65eefe7fda --- /dev/null +++ b/forge-gui/res/cardsfolder/d/daxos_the_returned.txt @@ -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." \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java index 54917bfaad8..a16a9b585b5 100644 --- a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java +++ b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java @@ -14,35 +14,7 @@ import forge.game.Game; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardView; -import forge.game.event.GameEvent; -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.event.*; import forge.game.player.Player; import forge.game.player.PlayerView; import forge.game.zone.PlayerZone; @@ -210,6 +182,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(final GameEventTurnBegan event) { turnUpdate = event.turnOwner.getView(); + processPlayer(event.turnOwner, livesUpdate); if (FModel.getPreferences().getPrefBoolean(FPref.UI_STACK_CREATURES) && event.turnOwner != null) { // anything except stack will get here updateZone(event.turnOwner, ZoneType.Battlefield); @@ -374,7 +347,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { final CardCollection cards = new CardCollection(); for (final Player p : event.players) { cards.addAll(p.getAllCards()); + processPlayer(p, livesUpdate); } + return processCards(cards, cardsRefreshDetails); } @@ -397,4 +372,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { public Void visit(final GameEventPlayerPoisoned event) { return processPlayer(event.receiver, livesUpdate); } + + @Override + public Void visit(final GameEventPlayerCounters event) { + return processPlayer(event.receiver, livesUpdate); + } } \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/match/input/InputProliferate.java b/forge-gui/src/main/java/forge/match/input/InputProliferate.java index d6033287838..0bb984db97f 100644 --- a/forge-gui/src/main/java/forge/match/input/InputProliferate.java +++ b/forge-gui/src/main/java/forge/match/input/InputProliferate.java @@ -31,12 +31,7 @@ public final class InputProliferate extends InputSelectManyBase { } else { for (final Entry 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"); } } @@ -79,7 +74,8 @@ public final class InputProliferate extends InputSelectManyBase { @Override 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; } @@ -87,7 +83,21 @@ public final class InputProliferate extends InputSelectManyBase { if (entityWasSelected) { this.chosenCounters.remove(player); } else { - this.chosenCounters.put(player, null /* POISON counter is meant */); + final List choices = new ArrayList(); + + 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();