mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Merge remote-tracking branch 'Core/master' into coremaster
This commit is contained in:
@@ -566,7 +566,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int thisRemove = Math.min(prefCard.getCounters(cType), stillToRemove);
|
int thisRemove = Math.min(prefCard.getCounters(cType), stillToRemove);
|
||||||
if (thisRemove > 0) {
|
if (thisRemove > 0) {
|
||||||
removed += thisRemove;
|
removed += thisRemove;
|
||||||
table.put(prefCard, CounterType.get(cType), thisRemove);
|
table.put(null, prefCard, CounterType.get(cType), thisRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -632,7 +632,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int thisRemove = Math.min(card.getCounters(ctype), c - toRemove);
|
int thisRemove = Math.min(card.getCounters(ctype), c - toRemove);
|
||||||
if (thisRemove > 0) {
|
if (thisRemove > 0) {
|
||||||
toRemove += thisRemove;
|
toRemove += thisRemove;
|
||||||
table.put(card, ctype, thisRemove);
|
table.put(null, card, ctype, thisRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -660,7 +660,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int over = Math.min(e.getValue(), c - toRemove);
|
int over = Math.min(e.getValue(), c - toRemove);
|
||||||
if (over > 0) {
|
if (over > 0) {
|
||||||
toRemove += over;
|
toRemove += over;
|
||||||
table.put(crd, e.getKey(), over);
|
table.put(null, crd, e.getKey(), over);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -690,7 +690,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int over = Math.min(e.getValue(), c - toRemove);
|
int over = Math.min(e.getValue(), c - toRemove);
|
||||||
if (over > 0) {
|
if (over > 0) {
|
||||||
toRemove += over;
|
toRemove += over;
|
||||||
table.put(crd, e.getKey(), over);
|
table.put(null, crd, e.getKey(), over);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -730,7 +730,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int over = Math.min(crd.getCounters(CounterEnumType.QUEST) - e, c - toRemove);
|
int over = Math.min(crd.getCounters(CounterEnumType.QUEST) - e, c - toRemove);
|
||||||
if (over > 0) {
|
if (over > 0) {
|
||||||
toRemove += over;
|
toRemove += over;
|
||||||
table.put(crd, CounterType.get(CounterEnumType.QUEST), over);
|
table.put(null, crd, CounterType.get(CounterEnumType.QUEST), over);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,7 +754,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int thisRemove = Math.min(card.getCounters(cost.counter), c - toRemove);
|
int thisRemove = Math.min(card.getCounters(cost.counter), c - toRemove);
|
||||||
if (thisRemove > 0) {
|
if (thisRemove > 0) {
|
||||||
toRemove += thisRemove;
|
toRemove += thisRemove;
|
||||||
table.put(card, cost.counter, thisRemove);
|
table.put(null, card, cost.counter, thisRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -768,7 +768,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
int thisRemove = Math.min(e.getValue(), c - toRemove);
|
int thisRemove = Math.min(e.getValue(), c - toRemove);
|
||||||
if (thisRemove > 0) {
|
if (thisRemove > 0) {
|
||||||
toRemove += thisRemove;
|
toRemove += thisRemove;
|
||||||
table.put(card, e.getKey(), thisRemove);
|
table.put(null, card, e.getKey(), thisRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,23 +3,12 @@ package forge.ai;
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
import com.google.common.collect.*;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
|
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
@@ -769,23 +758,10 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap));
|
game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap));
|
||||||
|
|
||||||
if (!combat.getAttackers().isEmpty()) {
|
|
||||||
List<GameEntity> attackedTarget = Lists.newArrayList();
|
|
||||||
for (final Card c : combat.getAttackers()) {
|
|
||||||
attackedTarget.add(combat.getDefenderByAttacker(c));
|
|
||||||
}
|
|
||||||
final Map<AbilityKey, Object> runParams = Maps.newEnumMap(AbilityKey.class);
|
|
||||||
runParams.put(AbilityKey.Attackers, combat.getAttackers());
|
|
||||||
runParams.put(AbilityKey.AttackingPlayer, combat.getAttackingPlayer());
|
|
||||||
runParams.put(AbilityKey.AttackedTarget, attackedTarget);
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclared, runParams, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Card c : combat.getAttackers()) {
|
for (final Card c : combat.getAttackers()) {
|
||||||
CombatUtil.checkDeclaredAttacker(game, c, combat);
|
CombatUtil.checkDeclaredAttacker(game, c, combat, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getTriggerHandler().resetActiveTriggers();
|
|
||||||
game.updateCombatForView();
|
game.updateCombatForView();
|
||||||
game.fireEvent(new GameEventCombatChanged());
|
game.fireEvent(new GameEventCombatChanged());
|
||||||
|
|
||||||
|
|||||||
@@ -163,12 +163,16 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public boolean matchesValid(final Object o, final String[] valids, final Card srcCard) {
|
public boolean matchesValid(final Object o, final String[] valids, final Card srcCard) {
|
||||||
|
return matchesValid(o, valids, srcCard, srcCard.getController());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesValid(final Object o, final String[] valids, final Card srcCard, final Player srcPlayer) {
|
||||||
if (o instanceof GameObject) {
|
if (o instanceof GameObject) {
|
||||||
final GameObject c = (GameObject) o;
|
final GameObject c = (GameObject) o;
|
||||||
return c.isValid(valids, srcCard.getController(), srcCard, this);
|
return c.isValid(valids, srcPlayer, srcCard, this);
|
||||||
} else if (o instanceof Iterable<?>) {
|
} else if (o instanceof Iterable<?>) {
|
||||||
for (Object o2 : (Iterable<?>)o) {
|
for (Object o2 : (Iterable<?>)o) {
|
||||||
if (matchesValid(o2, valids, srcCard)) {
|
if (matchesValid(o2, valids, srcCard, srcPlayer)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,14 +41,12 @@ import forge.game.spellability.TargetRestrictions;
|
|||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.collect.FCollection;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||||
protected final int id;
|
protected final int id;
|
||||||
private String name = "";
|
private String name = "";
|
||||||
private int preventNextDamage = 0;
|
private int preventNextDamage = 0;
|
||||||
protected CardCollection attachedCards;
|
protected CardCollection attachedCards = new CardCollection();
|
||||||
private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
|
private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
|
||||||
protected Map<CounterType, Integer> counters = Maps.newHashMap();
|
protected Map<CounterType, Integer> counters = Maps.newHashMap();
|
||||||
|
|
||||||
@@ -285,43 +283,36 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
public abstract boolean hasKeyword(final Keyword keyword);
|
public abstract boolean hasKeyword(final Keyword keyword);
|
||||||
|
|
||||||
public final CardCollectionView getEnchantedBy() {
|
public final CardCollectionView getEnchantedBy() {
|
||||||
if (attachedCards == null) {
|
|
||||||
return CardCollection.EMPTY;
|
|
||||||
}
|
|
||||||
// enchanted means attached by Aura
|
// enchanted means attached by Aura
|
||||||
return CardLists.filter(attachedCards, CardPredicates.Presets.AURA);
|
return CardLists.filter(getAttachedCards(), CardPredicates.Presets.AURA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// doesn't include phased out cards
|
||||||
public final CardCollectionView getAttachedCards() {
|
public final CardCollectionView getAttachedCards() {
|
||||||
return CardCollection.getView(attachedCards);
|
return CardLists.filter(attachedCards, CardPredicates.phasedIn());
|
||||||
|
}
|
||||||
|
|
||||||
|
// for view does include phased out cards
|
||||||
|
public final CardCollectionView getAllAttachedCards() {
|
||||||
|
return attachedCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setAttachedCards(final Iterable<Card> cards) {
|
public final void setAttachedCards(final Iterable<Card> cards) {
|
||||||
if (cards == null) {
|
attachedCards = new CardCollection(cards);
|
||||||
attachedCards = null;
|
updateAttachedCards();
|
||||||
}
|
|
||||||
else {
|
|
||||||
attachedCards = new CardCollection(cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
getView().updateAttachedCards(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasCardAttachments() {
|
public final boolean hasCardAttachments() {
|
||||||
return FCollection.hasElements(attachedCards);
|
return !getAttachedCards().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isEnchanted() {
|
public final boolean isEnchanted() {
|
||||||
if (attachedCards == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// enchanted means attached by Aura
|
// enchanted means attached by Aura
|
||||||
return Iterables.any(attachedCards, CardPredicates.Presets.AURA);
|
return Iterables.any(getAttachedCards(), CardPredicates.Presets.AURA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasCardAttachment(Card c) {
|
public final boolean hasCardAttachment(Card c) {
|
||||||
return FCollection.hasElement(attachedCards, c);
|
return getAttachedCards().contains(c);
|
||||||
}
|
}
|
||||||
public final boolean isEnchantedBy(Card c) {
|
public final boolean isEnchantedBy(Card c) {
|
||||||
// Rule 303.4k Even if c is no Aura it still counts
|
// Rule 303.4k Even if c is no Aura it still counts
|
||||||
@@ -329,9 +320,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasCardAttachment(final String cardName) {
|
public final boolean hasCardAttachment(final String cardName) {
|
||||||
if (attachedCards == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return CardLists.count(getAttachedCards(), CardPredicates.nameEquals(cardName)) > 0;
|
return CardLists.count(getAttachedCards(), CardPredicates.nameEquals(cardName)) > 0;
|
||||||
}
|
}
|
||||||
public final boolean isEnchantedBy(final String cardName) {
|
public final boolean isEnchantedBy(final String cardName) {
|
||||||
@@ -344,12 +332,8 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
* @param Card c
|
* @param Card c
|
||||||
*/
|
*/
|
||||||
public final void addAttachedCard(final Card c) {
|
public final void addAttachedCard(final Card c) {
|
||||||
if (attachedCards == null) {
|
|
||||||
attachedCards = new CardCollection();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attachedCards.add(c)) {
|
if (attachedCards.add(c)) {
|
||||||
getView().updateAttachedCards(this);
|
updateAttachedCards();
|
||||||
getGame().fireEvent(new GameEventCardAttachment(c, null, this));
|
getGame().fireEvent(new GameEventCardAttachment(c, null, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,17 +343,16 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
* @param Card c
|
* @param Card c
|
||||||
*/
|
*/
|
||||||
public final void removeAttachedCard(final Card c) {
|
public final void removeAttachedCard(final Card c) {
|
||||||
if (attachedCards == null) { return; }
|
|
||||||
|
|
||||||
if (attachedCards.remove(c)) {
|
if (attachedCards.remove(c)) {
|
||||||
if (attachedCards.isEmpty()) {
|
updateAttachedCards();
|
||||||
attachedCards = null;
|
|
||||||
}
|
|
||||||
getView().updateAttachedCards(this);
|
|
||||||
getGame().fireEvent(new GameEventCardAttachment(c, this, null));
|
getGame().fireEvent(new GameEventCardAttachment(c, this, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void updateAttachedCards() {
|
||||||
|
getView().updateAttachedCards(this);
|
||||||
|
}
|
||||||
|
|
||||||
public final void unAttachAllCards() {
|
public final void unAttachAllCards() {
|
||||||
for (Card c : Lists.newArrayList(getAttachedCards())) {
|
for (Card c : Lists.newArrayList(getAttachedCards())) {
|
||||||
c.unattachFromEntity(this);
|
c.unattachFromEntity(this);
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package forge.game;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.collect.ForwardingTable;
|
import com.google.common.collect.ForwardingTable;
|
||||||
import com.google.common.collect.HashBasedTable;
|
import com.google.common.collect.HashBasedTable;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
@@ -10,38 +13,46 @@ import com.google.common.collect.Table;
|
|||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
|
||||||
public class GameEntityCounterTable extends ForwardingTable<GameEntity, CounterType, Integer> {
|
public class GameEntityCounterTable extends ForwardingTable<Optional<Player>, GameEntity, Map<CounterType, Integer>> {
|
||||||
|
|
||||||
private Table<GameEntity, CounterType, Integer> dataMap = HashBasedTable.create();
|
private Table<Optional<Player>, GameEntity, Map<CounterType, Integer>> dataMap = HashBasedTable.create();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see com.google.common.collect.ForwardingTable#delegate()
|
* @see com.google.common.collect.ForwardingTable#delegate()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Table<GameEntity, CounterType, Integer> delegate() {
|
protected Table<Optional<Player>, GameEntity, Map<CounterType, Integer>> delegate() {
|
||||||
return dataMap;
|
return dataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
public Integer put(Player putter, GameEntity object, CounterType type, Integer value) {
|
||||||
* @see com.google.common.collect.ForwardingTable#put(java.lang.Object, java.lang.Object, java.lang.Object)
|
Optional<Player> o = Optional.fromNullable(putter);
|
||||||
*/
|
Map<CounterType, Integer> map = get(o, object);
|
||||||
@Override
|
if (map == null) {
|
||||||
public Integer put(GameEntity rowKey, CounterType columnKey, Integer value) {
|
map = Maps.newHashMap();
|
||||||
return super.put(rowKey, columnKey, get(rowKey, columnKey) + value);
|
put(o, object, map);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer get(Object rowKey, Object columnKey) {
|
|
||||||
if (!contains(rowKey, columnKey)) {
|
|
||||||
return 0; // helper to not return null value
|
|
||||||
}
|
}
|
||||||
return super.get(rowKey, columnKey);
|
return map.put(type, ObjectUtils.firstNonNull(map.get(type), 0) + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int get(Player putter, GameEntity object, CounterType type) {
|
||||||
|
Optional<Player> o = Optional.fromNullable(putter);
|
||||||
|
return ObjectUtils.firstNonNull(get(o, object).get(type), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int totalValues() {
|
||||||
|
int result = 0;
|
||||||
|
for (Map<CounterType, Integer> m : values()) {
|
||||||
|
for (Integer i : m.values()) {
|
||||||
|
result += i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* returns the counters that can still be removed from game entity
|
* returns the counters that can still be removed from game entity
|
||||||
@@ -52,7 +63,7 @@ public class GameEntityCounterTable extends ForwardingTable<GameEntity, CounterT
|
|||||||
result.putAll(ge.getCounters());
|
result.putAll(ge.getCounters());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
Map<CounterType, Integer> alreadyRemoved = row(ge);
|
Map<CounterType, Integer> alreadyRemoved = column(ge).get(Optional.absent());
|
||||||
for (Map.Entry<CounterType, Integer> e : ge.getCounters().entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : ge.getCounters().entrySet()) {
|
||||||
Integer rest = e.getValue() - (alreadyRemoved.containsKey(e.getKey()) ? alreadyRemoved.get(e.getKey()) : 0);
|
Integer rest = e.getValue() - (alreadyRemoved.containsKey(e.getKey()) ? alreadyRemoved.get(e.getKey()) : 0);
|
||||||
if (rest > 0) {
|
if (rest > 0) {
|
||||||
@@ -65,19 +76,34 @@ public class GameEntityCounterTable extends ForwardingTable<GameEntity, CounterT
|
|||||||
public Map<GameEntity, Integer> filterTable(CounterType type, String valid, Card host, CardTraitBase sa) {
|
public Map<GameEntity, Integer> filterTable(CounterType type, String valid, Card host, CardTraitBase sa) {
|
||||||
Map<GameEntity, Integer> result = Maps.newHashMap();
|
Map<GameEntity, Integer> result = Maps.newHashMap();
|
||||||
|
|
||||||
for (Map.Entry<GameEntity, Integer> e : column(type).entrySet()) {
|
for (Map.Entry<GameEntity, Map<Optional<Player>, Map<CounterType, Integer>>> gm : columnMap().entrySet()) {
|
||||||
if (e.getValue() > 0 && e.getKey().isValid(valid, host.getController(), host, sa)) {
|
if (gm.getKey().isValid(valid, host.getController(), host, sa)) {
|
||||||
result.put(e.getKey(), e.getValue());
|
for (Map<CounterType, Integer> cm : gm.getValue().values()) {
|
||||||
|
Integer old = ObjectUtils.firstNonNull(result.get(gm.getKey()), 0);
|
||||||
|
Integer v = ObjectUtils.firstNonNull(cm.get(type), 0);
|
||||||
|
result.put(gm.getKey(), old + v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void triggerCountersPutAll(final Game game) {
|
public void triggerCountersPutAll(final Game game) {
|
||||||
if (!isEmpty()) {
|
if (isEmpty()) {
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
return;
|
||||||
runParams.put(AbilityKey.Objects, this);
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false);
|
|
||||||
}
|
}
|
||||||
|
for (Cell<Optional<Player>, GameEntity, Map<CounterType, Integer>> c : cellSet()) {
|
||||||
|
if (c.getValue().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Source, c.getRowKey().get());
|
||||||
|
runParams.put(AbilityKey.Object, c.getColumnKey());
|
||||||
|
runParams.put(AbilityKey.CounterMap, c.getValue());
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.CounterPlayerAddedAll, runParams, false);
|
||||||
|
}
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Objects, this);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.game;
|
package forge.game;
|
||||||
|
|
||||||
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardView;
|
import forge.game.card.CardView;
|
||||||
import forge.trackable.TrackableCollection;
|
import forge.trackable.TrackableCollection;
|
||||||
import forge.trackable.TrackableObject;
|
import forge.trackable.TrackableObject;
|
||||||
@@ -54,6 +55,12 @@ public abstract class GameEntityView extends TrackableObject {
|
|||||||
public boolean hasCardAttachments() {
|
public boolean hasCardAttachments() {
|
||||||
return getAttachedCards() != null;
|
return getAttachedCards() != null;
|
||||||
}
|
}
|
||||||
|
public Iterable<CardView> getAllAttachedCards() {
|
||||||
|
return get(TrackableProperty.AllAttachedCards);
|
||||||
|
}
|
||||||
|
public boolean hasAnyCardAttachments() {
|
||||||
|
return getAllAttachedCards() != null;
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateAttachedCards(GameEntity e) {
|
protected void updateAttachedCards(GameEntity e) {
|
||||||
if (e.hasCardAttachments()) {
|
if (e.hasCardAttachments()) {
|
||||||
@@ -62,5 +69,11 @@ public abstract class GameEntityView extends TrackableObject {
|
|||||||
else {
|
else {
|
||||||
set(TrackableProperty.AttachedCards, null);
|
set(TrackableProperty.AttachedCards, null);
|
||||||
}
|
}
|
||||||
|
CardCollectionView all = e.getAllAttachedCards();
|
||||||
|
if (all.isEmpty()) {
|
||||||
|
set(TrackableProperty.AllAttachedCards, null);
|
||||||
|
} else {
|
||||||
|
set(TrackableProperty.AllAttachedCards, CardView.getCollection(all));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public enum AbilityKey {
|
|||||||
CounterAmount("CounterAmount"),
|
CounterAmount("CounterAmount"),
|
||||||
CounteredSA("CounteredSA"),
|
CounteredSA("CounteredSA"),
|
||||||
CounterNum("CounterNum"),
|
CounterNum("CounterNum"),
|
||||||
|
CounterMap("CounterMap"),
|
||||||
CounterTable("CounterTable"),
|
CounterTable("CounterTable"),
|
||||||
CounterType("CounterType"),
|
CounterType("CounterType"),
|
||||||
Crew("Crew"),
|
Crew("Crew"),
|
||||||
|
|||||||
@@ -399,19 +399,9 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
final Game game = card.getGame();
|
final Game game = card.getGame();
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
|
|
||||||
CounterType counterType = null;
|
|
||||||
String amount = sa.getParamOrDefault("CounterNum", "1");
|
String amount = sa.getParamOrDefault("CounterNum", "1");
|
||||||
boolean rememberAmount = sa.hasParam("RememberAmount");
|
boolean rememberAmount = sa.hasParam("RememberAmount");
|
||||||
|
|
||||||
if (!sa.hasParam("EachExistingCounter") && !sa.hasParam("EachFromSource") && !sa.hasParam("SharedKeywords")) {
|
|
||||||
try {
|
|
||||||
counterType = AbilityUtils.getCounterType(sa.getParam("CounterType"), sa);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Player placer = activator;
|
Player placer = activator;
|
||||||
if (sa.hasParam("Placer")) {
|
if (sa.hasParam("Placer")) {
|
||||||
final String pstr = sa.getParam("Placer");
|
final String pstr = sa.getParam("Placer");
|
||||||
@@ -422,7 +412,13 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
|
|
||||||
if (sa.hasParam("SharedKeywords")) {
|
if (sa.hasParam("TriggeredCounterMap")) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<CounterType, Integer> counterMap = (Map<CounterType, Integer>) sa.getTriggeringObject(AbilityKey.CounterMap);
|
||||||
|
for (Map.Entry<CounterType, Integer> e : counterMap.entrySet()) {
|
||||||
|
resolvePerType(sa, placer, e.getKey(), e.getValue(), table);
|
||||||
|
}
|
||||||
|
} else if (sa.hasParam("SharedKeywords")) {
|
||||||
List<String> keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & "));
|
List<String> keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & "));
|
||||||
List<ZoneType> zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone"));
|
List<ZoneType> zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone"));
|
||||||
String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[]{"Card"};
|
String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[]{"Card"};
|
||||||
@@ -431,13 +427,19 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
resolvePerType(sa, placer, CounterType.getType(k), counterAmount, table);
|
resolvePerType(sa, placer, CounterType.getType(k), counterAmount, table);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
CounterType counterType = null;
|
||||||
|
if (!sa.hasParam("EachExistingCounter") && !sa.hasParam("EachFromSource")) {
|
||||||
|
try {
|
||||||
|
counterType = AbilityUtils.getCounterType(sa.getParam("CounterType"), sa);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
resolvePerType(sa, placer, counterType, counterAmount, table);
|
resolvePerType(sa, placer, counterType, counterAmount, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalAdded = 0;
|
int totalAdded = table.totalValues();
|
||||||
for (Integer i : table.values()) {
|
|
||||||
totalAdded += i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalAdded > 0 && rememberAmount) {
|
if (totalAdded > 0 && rememberAmount) {
|
||||||
// TODO use SpellAbility Remember later
|
// TODO use SpellAbility Remember later
|
||||||
|
|||||||
@@ -1482,7 +1482,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
addCounterTimestamp(counterType);
|
addCounterTimestamp(counterType);
|
||||||
}
|
}
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
table.put(this, counterType, addAmount);
|
table.put(source, this, counterType, addAmount);
|
||||||
}
|
}
|
||||||
return addAmount;
|
return addAmount;
|
||||||
}
|
}
|
||||||
@@ -3226,19 +3226,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final CardCollectionView getEquippedBy() {
|
public final CardCollectionView getEquippedBy() {
|
||||||
if (this.attachedCards == null) {
|
return CardLists.filter(getAttachedCards(), CardPredicates.Presets.EQUIPMENT);
|
||||||
return CardCollection.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CardLists.filter(attachedCards, CardPredicates.Presets.EQUIPMENT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isEquipped() {
|
public final boolean isEquipped() {
|
||||||
if (this.attachedCards == null) {
|
return Iterables.any(getAttachedCards(), CardPredicates.Presets.EQUIPMENT);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Iterables.any(attachedCards, CardPredicates.Presets.EQUIPMENT);
|
|
||||||
}
|
}
|
||||||
public final boolean isEquippedBy(Card c) {
|
public final boolean isEquippedBy(Card c) {
|
||||||
return this.hasCardAttachment(c);
|
return this.hasCardAttachment(c);
|
||||||
@@ -3248,19 +3240,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final CardCollectionView getFortifiedBy() {
|
public final CardCollectionView getFortifiedBy() {
|
||||||
if (this.attachedCards == null) {
|
return CardLists.filter(getAttachedCards(), CardPredicates.Presets.FORTIFICATION);
|
||||||
return CardCollection.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CardLists.filter(attachedCards, CardPredicates.Presets.FORTIFICATION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isFortified() {
|
public final boolean isFortified() {
|
||||||
if (this.attachedCards == null) {
|
return Iterables.any(getAttachedCards(), CardPredicates.Presets.FORTIFICATION);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Iterables.any(attachedCards, CardPredicates.Presets.FORTIFICATION);
|
|
||||||
}
|
}
|
||||||
public final boolean isFortifiedBy(Card c) {
|
public final boolean isFortifiedBy(Card c) {
|
||||||
// 301.5e + 301.6
|
// 301.5e + 301.6
|
||||||
@@ -4624,6 +4608,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the game entity it was attached to
|
||||||
|
GameEntity ge = this.getEntityAttachedTo();
|
||||||
|
if (ge != null) {
|
||||||
|
ge.updateAttachedCards();
|
||||||
|
}
|
||||||
|
|
||||||
getGame().fireEvent(new GameEventCardPhased(this, isPhasedOut()));
|
getGame().fireEvent(new GameEventCardPhased(this, isPhasedOut()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +37,7 @@ import forge.util.collect.FCollectionView;
|
|||||||
* <p>
|
* <p>
|
||||||
* Predicate<Card> interface.
|
* Predicate<Card> interface.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
@@ -288,7 +288,7 @@ public final class CardPredicates {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Predicate<Card> greaterCMC(final int cmc) {
|
public static final Predicate<Card> greaterCMC(final int cmc) {
|
||||||
return new Predicate<Card>() {
|
return new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -354,7 +354,7 @@ public final class CardPredicates {
|
|||||||
public static final Predicate<Card> hasCounter(final CounterEnumType type, final int n) {
|
public static final Predicate<Card> hasCounter(final CounterEnumType type, final int n) {
|
||||||
return hasCounter(CounterType.get(type), n);
|
return hasCounter(CounterType.get(type), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Predicate<Card> hasLessCounter(final CounterType type, final int n) {
|
public static final Predicate<Card> hasLessCounter(final CounterType type, final int n) {
|
||||||
return new Predicate<Card>() {
|
return new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -479,6 +479,16 @@ public final class CardPredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final Predicate<Card> phasedIn() {
|
||||||
|
return new Predicate<Card>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final Card c)
|
||||||
|
{
|
||||||
|
return !c.isPhasedOut();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static class Presets {
|
public static class Presets {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -497,7 +507,7 @@ public final class CardPredicates {
|
|||||||
return c.isFaceDown();
|
return c.isFaceDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a Predicate<Card> to get all cards that are untapped.
|
* a Predicate<Card> to get all cards that are untapped.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -352,17 +352,19 @@ public class CombatUtil {
|
|||||||
* @param c
|
* @param c
|
||||||
* a {@link forge.game.card.Card} object.
|
* a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
public static void checkDeclaredAttacker(final Game game, final Card c, final Combat combat) {
|
public static void checkDeclaredAttacker(final Game game, final Card c, final Combat combat, boolean triggers) {
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
if (triggers) {
|
||||||
runParams.put(AbilityKey.Attacker, c);
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
final List<Card> otherAttackers = combat.getAttackers();
|
runParams.put(AbilityKey.Attacker, c);
|
||||||
otherAttackers.remove(c);
|
final List<Card> otherAttackers = combat.getAttackers();
|
||||||
runParams.put(AbilityKey.OtherAttackers, otherAttackers);
|
otherAttackers.remove(c);
|
||||||
runParams.put(AbilityKey.Attacked, combat.getDefenderByAttacker(c));
|
runParams.put(AbilityKey.OtherAttackers, otherAttackers);
|
||||||
runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(c));
|
runParams.put(AbilityKey.Attacked, combat.getDefenderByAttacker(c));
|
||||||
runParams.put(AbilityKey.Defenders, combat.getDefenders());
|
runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(c));
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false);
|
runParams.put(AbilityKey.Defenders, combat.getDefenders());
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.Attacks, runParams, false);
|
||||||
|
}
|
||||||
|
|
||||||
c.getDamageHistory().setCreatureAttackedThisCombat(true);
|
c.getDamageHistory().setCreatureAttackedThisCombat(true);
|
||||||
c.getDamageHistory().clearNotAttackedSinceLastUpkeepOf();
|
c.getDamageHistory().clearNotAttackedSinceLastUpkeepOf();
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.cost;
|
package forge.game.cost;
|
||||||
|
|
||||||
import com.google.common.collect.Table;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -115,11 +118,13 @@ public class CostRemoveAnyCounter extends CostPart {
|
|||||||
final Card source = ability.getHostCard();
|
final Card source = ability.getHostCard();
|
||||||
|
|
||||||
int removed = 0;
|
int removed = 0;
|
||||||
for (Table.Cell<GameEntity, CounterType, Integer> cell : decision.counterTable.cellSet()) {
|
for (Entry<GameEntity, Map<CounterType, Integer>> e : decision.counterTable.row(Optional.absent()).entrySet()) {
|
||||||
removed += cell.getValue();
|
for (Entry<CounterType, Integer> v : e.getValue().entrySet()) {
|
||||||
cell.getRowKey().subtractCounter(cell.getColumnKey(), cell.getValue());
|
removed += v.getValue();
|
||||||
if (cell.getRowKey() instanceof Card) {
|
e.getKey().subtractCounter(v.getKey(), v.getValue());
|
||||||
cell.getRowKey().getGame().updateLastStateForCard((Card) cell.getRowKey());
|
}
|
||||||
|
if (e.getKey() instanceof Card) {
|
||||||
|
e.getKey().getGame().updateLastStateForCard((Card) e.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,19 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.phase;
|
package forge.game.phase;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
|
import com.google.common.collect.*;
|
||||||
import org.apache.commons.lang3.time.StopWatch;
|
import org.apache.commons.lang3.time.StopWatch;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
|
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
@@ -621,9 +613,16 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
|
|
||||||
// fire AttackersDeclared trigger
|
// fire AttackersDeclared trigger
|
||||||
if (!combat.getAttackers().isEmpty()) {
|
if (!combat.getAttackers().isEmpty()) {
|
||||||
List<GameEntity> attackedTarget = Lists.newArrayList();
|
List<GameEntity> attackedTarget = new ArrayList<>();
|
||||||
for (final Card c : combat.getAttackers()) {
|
for (GameEntity ge : combat.getDefenders()) {
|
||||||
attackedTarget.add(combat.getDefenderByAttacker(c));
|
if (!combat.getAttackersOf(ge).isEmpty()) {
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Attackers, combat.getAttackersOf(ge));
|
||||||
|
runParams.put(AbilityKey.AttackingPlayer, combat.getAttackingPlayer());
|
||||||
|
runParams.put(AbilityKey.AttackedTarget, ge);
|
||||||
|
attackedTarget.add(ge);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclaredOneTarget, runParams, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
runParams.put(AbilityKey.Attackers, combat.getAttackers());
|
runParams.put(AbilityKey.Attackers, combat.getAttackers());
|
||||||
@@ -633,7 +632,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Card c : combat.getAttackers()) {
|
for (final Card c : combat.getAttackers()) {
|
||||||
CombatUtil.checkDeclaredAttacker(game, c, combat);
|
CombatUtil.checkDeclaredAttacker(game, c, combat, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getTriggerHandler().resetActiveTriggers();
|
game.getTriggerHandler().resetActiveTriggers();
|
||||||
|
|||||||
@@ -1030,7 +1030,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
|
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
|
||||||
}
|
}
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
table.put(this, counterType, addAmount);
|
table.put(source, this, counterType, addAmount);
|
||||||
}
|
}
|
||||||
return addAmount;
|
return addAmount;
|
||||||
}
|
}
|
||||||
@@ -3426,11 +3426,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isCursed() {
|
public final boolean isCursed() {
|
||||||
if (this.attachedCards == null) {
|
return CardLists.count(getAttachedCards(), CardPredicates.Presets.CURSE) > 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CardLists.count(attachedCards, CardPredicates.Presets.CURSE) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canDiscardBy(SpellAbility sa) {
|
public boolean canDiscardBy(SpellAbility sa) {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package forge.game.trigger;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.Localizer;
|
||||||
|
|
||||||
|
public class TriggerCounterPlayerAddedAll extends Trigger {
|
||||||
|
|
||||||
|
public TriggerCounterPlayerAddedAll(Map<String, String> params, Card host, boolean intrinsic) {
|
||||||
|
super(params, host, intrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||||
|
if (!matchesValidParam("ValidSource", runParams.get(AbilityKey.Source))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!matchesValidParam("ValidObject", runParams.get(AbilityKey.Object))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (hasParam("ValidObjectToSource")) {
|
||||||
|
if (!matchesValid(runParams.get(AbilityKey.Object), getParam("ValidObjectToSource").split(","), getHostCard(),
|
||||||
|
(Player)runParams.get(AbilityKey.Source))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||||
|
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Source, AbilityKey.Player, AbilityKey.Object, AbilityKey.CounterMap);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImportantStackObjects(SpellAbility sa) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(Localizer.getInstance().getMessage("lblAddedOnce")).append(": ");
|
||||||
|
sb.append(sa.getTriggeringObject(AbilityKey.Player)).append(": ");
|
||||||
|
sb.append(sa.getTriggeringObject(AbilityKey.Object));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ public enum TriggerType {
|
|||||||
AttackerBlockedOnce(TriggerAttackerBlockedOnce.class),
|
AttackerBlockedOnce(TriggerAttackerBlockedOnce.class),
|
||||||
AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class),
|
AttackerBlockedByCreature(TriggerAttackerBlockedByCreature.class),
|
||||||
AttackersDeclared(TriggerAttackersDeclared.class),
|
AttackersDeclared(TriggerAttackersDeclared.class),
|
||||||
|
AttackersDeclaredOneTarget(TriggerAttackersDeclared.class),
|
||||||
AttackerUnblocked(TriggerAttackerUnblocked.class),
|
AttackerUnblocked(TriggerAttackerUnblocked.class),
|
||||||
AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class),
|
AttackerUnblockedOnce(TriggerAttackerUnblockedOnce.class),
|
||||||
Attacks(TriggerAttacks.class),
|
Attacks(TriggerAttacks.class),
|
||||||
@@ -39,6 +40,7 @@ public enum TriggerType {
|
|||||||
Clashed(TriggerClashed.class),
|
Clashed(TriggerClashed.class),
|
||||||
CounterAdded(TriggerCounterAdded.class),
|
CounterAdded(TriggerCounterAdded.class),
|
||||||
CounterAddedOnce(TriggerCounterAddedOnce.class),
|
CounterAddedOnce(TriggerCounterAddedOnce.class),
|
||||||
|
CounterPlayerAddedAll(TriggerCounterPlayerAddedAll.class),
|
||||||
CounterAddedAll(TriggerCounterAddedAll.class),
|
CounterAddedAll(TriggerCounterAddedAll.class),
|
||||||
Countered(TriggerCountered.class),
|
Countered(TriggerCountered.class),
|
||||||
CounterRemoved(TriggerCounterRemoved.class),
|
CounterRemoved(TriggerCounterRemoved.class),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public enum TrackableProperty {
|
|||||||
Text(TrackableTypes.StringType),
|
Text(TrackableTypes.StringType),
|
||||||
PreventNextDamage(TrackableTypes.IntegerType),
|
PreventNextDamage(TrackableTypes.IntegerType),
|
||||||
AttachedCards(TrackableTypes.CardViewCollectionType),
|
AttachedCards(TrackableTypes.CardViewCollectionType),
|
||||||
|
AllAttachedCards(TrackableTypes.CardViewCollectionType),
|
||||||
Counters(TrackableTypes.CounterMapType),
|
Counters(TrackableTypes.CounterMapType),
|
||||||
CurrentPlane(TrackableTypes.StringType),
|
CurrentPlane(TrackableTypes.StringType),
|
||||||
PlanarPlayer(TrackableTypes.PlayerViewType),
|
PlanarPlayer(TrackableTypes.PlayerViewType),
|
||||||
|
|||||||
@@ -688,8 +688,8 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
|
|||||||
}
|
}
|
||||||
toPanel.getAttachedPanels().clear();
|
toPanel.getAttachedPanels().clear();
|
||||||
|
|
||||||
if (card.hasCardAttachments()) {
|
if (card.hasAnyCardAttachments()) {
|
||||||
final Iterable<CardView> enchants = card.getAttachedCards();
|
final Iterable<CardView> enchants = card.getAllAttachedCards();
|
||||||
for (final CardView e : enchants) {
|
for (final CardView e : enchants) {
|
||||||
final CardPanel cardE = getCardPanel(e.getId());
|
final CardPanel cardE = getCardPanel(e.getId());
|
||||||
if (cardE != null) {
|
if (cardE != null) {
|
||||||
|
|||||||
@@ -286,8 +286,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
|
|||||||
|
|
||||||
attachedPanels.clear();
|
attachedPanels.clear();
|
||||||
|
|
||||||
if (card.hasCardAttachments()) {
|
if (card.hasAnyCardAttachments()) {
|
||||||
final Iterable<CardView> enchants = card.getAttachedCards();
|
final Iterable<CardView> enchants = card.getAllAttachedCards();
|
||||||
for (final CardView e : enchants) {
|
for (final CardView e : enchants) {
|
||||||
final CardAreaPanel cardE = CardAreaPanel.get(e);
|
final CardAreaPanel cardE = CardAreaPanel.get(e);
|
||||||
if (cardE != null) {
|
if (cardE != null) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:2 U
|
|||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
K:Enchant creature
|
K:Enchant creature
|
||||||
A:SP$ Attach | Cost$ 2 U | ValidTgts$ Creature | AILogic$ Pump
|
A:SP$ Attach | Cost$ 2 U | ValidTgts$ Creature | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Enchanted creature gets +2/+2 and is goaded. (It attacks each combat if able and attacks a player other than you if able.)
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | Goad$ True | Description$ Enchanted creature gets +2/+2 and is goaded. (It attacks each combat if able and attacks a player other than you if able.)
|
||||||
T:Mode$ Attacks | ValidCard$ Card.AttachedBy | Execute$ DBScry | TriggerZones$ Battlefield | TriggerDescription$ Whenever enchanted creature attacks, you scry 2.
|
T:Mode$ Attacks | ValidCard$ Card.AttachedBy | Execute$ DBScry | TriggerZones$ Battlefield | TriggerDescription$ Whenever enchanted creature attacks, you scry 2.
|
||||||
SVar:DBScry:DB$ Scry | ScryNum$ 2
|
SVar:DBScry:DB$ Scry | ScryNum$ 2
|
||||||
Oracle:Enchant creature\nEnchanted creature gets +2/+2 and is goaded. (It attacks each combat if able and attacks a player other than you if able.)\nWhenever enchanted creature attacks, you scry 2.
|
Oracle:Enchant creature\nEnchanted creature gets +2/+2 and is goaded. (It attacks each combat if able and attacks a player other than you if able.)\nWhenever enchanted creature attacks, you scry 2.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:1 W
|
|||||||
Types:Artifact Equipment
|
Types:Artifact Equipment
|
||||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Equipped creature gets +2/+2.
|
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Equipped creature gets +2/+2.
|
||||||
T:Mode$ Attacks | ValidCard$ Card.AttachedBy | Attacked$ Player.withMostLife | Execute$ TrigToken | TriggerDescription$ Whenever equipped creature attacks the player with the most life or tied for most life, create a 4/4 white Angel creature token with flying that's tapped and attacking that player.
|
T:Mode$ Attacks | ValidCard$ Card.AttachedBy | Attacked$ Player.withMostLife | Execute$ TrigToken | TriggerDescription$ Whenever equipped creature attacks the player with the most life or tied for most life, create a 4/4 white Angel creature token with flying that's tapped and attacking that player.
|
||||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_4_4_angel_flying | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ True
|
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_4_4_angel_flying | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ TriggeredDefender
|
||||||
SVar:HasAttackEffect:TRUE
|
SVar:HasAttackEffect:TRUE
|
||||||
K:Equip:4
|
K:Equip:4
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
|
|||||||
8
forge-gui/res/cardsfolder/upcoming/bold_plagiarist.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/bold_plagiarist.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Bold Plagiarist
|
||||||
|
ManaCost:3 B
|
||||||
|
Types:Creature Vampire Rogue
|
||||||
|
PT:2/2
|
||||||
|
K:Flash
|
||||||
|
T:Mode$ CounterPlayerAddedAll | ValidSource$ Opponent | ValidObjectToSource$ Creature.YouCtrl | Execute$ TrigCounter | TriggerDescription$ Whenever an opponent puts one or more counters on a creature they control, they put the same number and kind of counters on CARDNAME.
|
||||||
|
SVar:TrigCounter:DB$ PutCounter | Defined$ Self | Placer$ TriggeredSource | TriggeredCounterMap$ True
|
||||||
|
Oracle:Flash\nWhenever an opponent puts one or more counters on a creature they control, they put the same number and kind of counters on Bold Plagiarist.
|
||||||
10
forge-gui/res/cardsfolder/upcoming/combat_calligrapher.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/combat_calligrapher.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Combat Calligrapher
|
||||||
|
ManaCost:3 W
|
||||||
|
Types:Creature Bird Cleric
|
||||||
|
PT:3/3
|
||||||
|
K:Flying
|
||||||
|
S:Mode$ CantAttack | ValidCard$ Creature.Inkling | Target$ You,Planeswalker.YouCtrl | Description$ Inklings can't attack you or planeswalkers you control.
|
||||||
|
T:Mode$ AttackersDeclaredOneTarget | AttackedTarget$ Opponent | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player attacks one of your opponents, that attacking player creates a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent.
|
||||||
|
SVar:TrigToken:DB$ Token | TokenScript$ wb_2_1_inkling_flying | TokenOwner$ TriggeredAttackingPlayer | TokenTapped$ True | TokenAttacking$ TriggeredAttackedTarget
|
||||||
|
DeckHas:Ability$Token
|
||||||
|
Oracle:Flying\nInklings can't attack you or planeswalkers you control.\nWhenever a player attacks one of your opponents, that attacking player creates a tapped 2/1 white and black Inkling creature token with flying that's attacking that opponent.
|
||||||
10
forge-gui/res/cardsfolder/upcoming/marshland_bloodcaster.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/marshland_bloodcaster.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Marshland Bloodcaster
|
||||||
|
ManaCost:4 B
|
||||||
|
Types:Creature Vampire Warlock
|
||||||
|
PT:3/5
|
||||||
|
K:Flying
|
||||||
|
A:AB$ Effect | Cost$ 1 B T | StaticAbilities$ ReduceCost | Triggers$ TrigCastSpell
|
||||||
|
SVar:ReduceCost:Mode$ Continuous | Affected$ Card.nonLand+YouCtrl | AddKeyword$ Alternative Cost:PayLife<ConvertedManaCost> | AffectedZone$ Hand,Graveyard,Exile,Library,Command | EffectZone$ Command | Description$ Rather than pay the mana cost of the next spell you cast this turn, you may pay life equal to that spell’s mana value.
|
||||||
|
SVar:TrigCastSpell:Mode$ SpellCast | ValidActivatingPlayer$ You | TriggerZones$ Command | Execute$ RemoveEffect | Static$ True
|
||||||
|
SVar:RemoveEffect:DB$ ChangeZone | Origin$ Command | Destination$ Exile
|
||||||
|
Oracle:Flying\n{1}{B}, {T}: Rather than pay the mana cost of the next spell you cast this turn, you may pay life equal to that spell’s mana value.
|
||||||
12
forge-gui/res/cardsfolder/upcoming/sproutback_trudge.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/sproutback_trudge.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Sproutback Trudge
|
||||||
|
ManaCost:7 G G
|
||||||
|
Types:Creature Fungus Beast
|
||||||
|
PT:9/7
|
||||||
|
K:Trample
|
||||||
|
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {X} less to cast, where X is the amount of life you gained this turn.
|
||||||
|
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Graveyard | CheckSVar$ X | SVarCompare$ GE1 | Execute$ TrigPlay | TriggerDescription$ At the beginning of your end step, if you gained life this turn, you may cast CARDNAME from your graveyard.
|
||||||
|
SVar:TrigPlay:DB$ Play | Defined$ Self | Optional$ True
|
||||||
|
SVar:X:Count$LifeYouGainedThisTurn
|
||||||
|
DeckHints:Ability$LifeGain
|
||||||
|
DeckHas:Ability$Graveyard
|
||||||
|
Oracle:This spell costs {X} less to cast, where X is the amount of life you gained this turn.\nTrample\nAt the beginning of your end step, if you gained life this turn, you may cast Sproutback Trudge from your graveyard.
|
||||||
@@ -2,7 +2,7 @@ Name:Zaffai, Thunder Conductor
|
|||||||
ManaCost:2 U R
|
ManaCost:2 U R
|
||||||
Types:Legendary Creature Human Shaman
|
Types:Legendary Creature Human Shaman
|
||||||
PT:1/4
|
PT:1/4
|
||||||
T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ DBScry | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, scry 1. If that spell's mana value is 5 or greater, create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 ore greater, CARDNAME deals 10 damage to an opponent chosen at random.
|
T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ DBScry | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, scry 1. If that spell's mana value is 5 or greater, create a 4/4 blue and red Elemental creature token. If that spell's mana value is 10 ore greater, CARDNAME deals 10 damage to an opponent chosen at random.
|
||||||
SVar:DBScry:DB$ Scry | ScryNum$ 1 | SubAbility$ DBToken
|
SVar:DBScry:DB$ Scry | ScryNum$ 1 | SubAbility$ DBToken
|
||||||
SVar:DBToken:DB$ Token | TokenScript$ ur_4_4_elemental | TokenOwner$ You | ConditionPresent$ Card.cmcGE5 | ConditionDefined$ TriggeredCard | ConditionCompare$ GE1 | SubAbility$ DBChoose
|
SVar:DBToken:DB$ Token | TokenScript$ ur_4_4_elemental | TokenOwner$ You | ConditionPresent$ Card.cmcGE5 | ConditionDefined$ TriggeredCard | ConditionCompare$ GE1 | SubAbility$ DBChoose
|
||||||
SVar:DBChoose:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | Random$ True | SubAbility$ DBDamage
|
SVar:DBChoose:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | Random$ True | SubAbility$ DBDamage
|
||||||
|
|||||||
@@ -861,11 +861,11 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.getCounters(cType) <= counterTable.get(c, cType)) {
|
if (c.getCounters(cType) <= counterTable.get(null, c, cType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
counterTable.put(c, cType, 1);
|
counterTable.put(null, c, cType, 1);
|
||||||
|
|
||||||
onSelectStateChanged(c, true);
|
onSelectStateChanged(c, true);
|
||||||
refresh();
|
refresh();
|
||||||
@@ -878,13 +878,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (counterType != null) {
|
if (counterType != null) {
|
||||||
if (c.getCounters(counterType) <= counterTable.get(c, counterType)) {
|
if (c.getCounters(counterType) <= counterTable.get(null, c, counterType)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) {
|
||||||
if (e.getValue() > counterTable.get(c, e.getKey())) {
|
if (e.getValue() > counterTable.get(null, c, e.getKey())) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -915,13 +915,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getDistibutedCounters() {
|
private int getDistibutedCounters() {
|
||||||
int sum = 0;
|
return counterTable.totalValues();
|
||||||
|
|
||||||
for (Integer v : this.counterTable.values()) {
|
|
||||||
sum += v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean isValidChoice(final GameEntity choice) {
|
protected final boolean isValidChoice(final GameEntity choice) {
|
||||||
@@ -934,7 +928,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<GameEntity> getSelected() {
|
public Collection<GameEntity> getSelected() {
|
||||||
return counterTable.rowKeySet();
|
return counterTable.columnKeySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user