mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
- 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:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,7 +268,13 @@ public enum CounterType {
|
|||||||
|
|
||||||
WIND(),
|
WIND(),
|
||||||
|
|
||||||
WISH();
|
WISH(),
|
||||||
|
|
||||||
|
// Player Counters
|
||||||
|
|
||||||
|
EXPERIENCE(),
|
||||||
|
|
||||||
|
POISON();
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
12
forge-gui/res/cardsfolder/d/daxos_the_returned.txt
Normal file
12
forge-gui/res/cardsfolder/d/daxos_the_returned.txt
Normal 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."
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user