mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
TriggerCounterPlayerAddedAll: new Trigger that triggers when a player puts more counters on one object
This commit is contained in:
@@ -566,7 +566,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
int thisRemove = Math.min(prefCard.getCounters(cType), stillToRemove);
|
||||
if (thisRemove > 0) {
|
||||
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);
|
||||
if (thisRemove > 0) {
|
||||
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);
|
||||
if (over > 0) {
|
||||
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);
|
||||
if (over > 0) {
|
||||
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);
|
||||
if (over > 0) {
|
||||
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);
|
||||
if (thisRemove > 0) {
|
||||
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);
|
||||
if (thisRemove > 0) {
|
||||
toRemove += thisRemove;
|
||||
table.put(card, e.getKey(), thisRemove);
|
||||
table.put(null, card, e.getKey(), thisRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,12 +163,16 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
||||
* @return a boolean.
|
||||
*/
|
||||
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) {
|
||||
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<?>) {
|
||||
for (Object o2 : (Iterable<?>)o) {
|
||||
if (matchesValid(o2, valids, srcCard)) {
|
||||
if (matchesValid(o2, valids, srcCard, srcPlayer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package forge.game;
|
||||
|
||||
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.HashBasedTable;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -10,38 +13,46 @@ import com.google.common.collect.Table;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.player.Player;
|
||||
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)
|
||||
* @see com.google.common.collect.ForwardingTable#delegate()
|
||||
*/
|
||||
@Override
|
||||
protected Table<GameEntity, CounterType, Integer> delegate() {
|
||||
protected Table<Optional<Player>, GameEntity, Map<CounterType, Integer>> delegate() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.google.common.collect.ForwardingTable#put(java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Integer put(GameEntity rowKey, CounterType columnKey, Integer value) {
|
||||
return super.put(rowKey, columnKey, get(rowKey, columnKey) + value);
|
||||
public Integer put(Player putter, GameEntity object, CounterType type, Integer value) {
|
||||
Optional<Player> o = Optional.fromNullable(putter);
|
||||
Map<CounterType, Integer> map = get(o, object);
|
||||
if (map == null) {
|
||||
map = Maps.newHashMap();
|
||||
put(o, object, map);
|
||||
}
|
||||
return map.put(type, ObjectUtils.firstNonNull(map.get(type), 0) + value);
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
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
|
||||
@@ -52,7 +63,7 @@ public class GameEntityCounterTable extends ForwardingTable<GameEntity, CounterT
|
||||
result.putAll(ge.getCounters());
|
||||
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()) {
|
||||
Integer rest = e.getValue() - (alreadyRemoved.containsKey(e.getKey()) ? alreadyRemoved.get(e.getKey()) : 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) {
|
||||
Map<GameEntity, Integer> result = Maps.newHashMap();
|
||||
|
||||
for (Map.Entry<GameEntity, Integer> e : column(type).entrySet()) {
|
||||
if (e.getValue() > 0 && e.getKey().isValid(valid, host.getController(), host, sa)) {
|
||||
result.put(e.getKey(), e.getValue());
|
||||
for (Map.Entry<GameEntity, Map<Optional<Player>, Map<CounterType, Integer>>> gm : columnMap().entrySet()) {
|
||||
if (gm.getKey().isValid(valid, host.getController(), host, sa)) {
|
||||
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;
|
||||
}
|
||||
|
||||
public void triggerCountersPutAll(final Game game) {
|
||||
if (!isEmpty()) {
|
||||
if (isEmpty()) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public enum AbilityKey {
|
||||
CounterAmount("CounterAmount"),
|
||||
CounteredSA("CounteredSA"),
|
||||
CounterNum("CounterNum"),
|
||||
CounterMap("CounterMap"),
|
||||
CounterTable("CounterTable"),
|
||||
CounterType("CounterType"),
|
||||
Crew("Crew"),
|
||||
|
||||
@@ -399,19 +399,9 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
final Game game = card.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
|
||||
CounterType counterType = null;
|
||||
String amount = sa.getParamOrDefault("CounterNum", "1");
|
||||
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;
|
||||
if (sa.hasParam("Placer")) {
|
||||
final String pstr = sa.getParam("Placer");
|
||||
@@ -422,7 +412,13 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
|
||||
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<ZoneType> zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone"));
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
|
||||
int totalAdded = 0;
|
||||
for (Integer i : table.values()) {
|
||||
totalAdded += i;
|
||||
}
|
||||
int totalAdded = table.totalValues();
|
||||
|
||||
if (totalAdded > 0 && rememberAmount) {
|
||||
// TODO use SpellAbility Remember later
|
||||
|
||||
@@ -1482,7 +1482,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
addCounterTimestamp(counterType);
|
||||
}
|
||||
if (table != null) {
|
||||
table.put(this, counterType, addAmount);
|
||||
table.put(source, this, counterType, addAmount);
|
||||
}
|
||||
return addAmount;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
*/
|
||||
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.ability.AbilityUtils;
|
||||
@@ -115,11 +118,13 @@ public class CostRemoveAnyCounter extends CostPart {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
int removed = 0;
|
||||
for (Table.Cell<GameEntity, CounterType, Integer> cell : decision.counterTable.cellSet()) {
|
||||
removed += cell.getValue();
|
||||
cell.getRowKey().subtractCounter(cell.getColumnKey(), cell.getValue());
|
||||
if (cell.getRowKey() instanceof Card) {
|
||||
cell.getRowKey().getGame().updateLastStateForCard((Card) cell.getRowKey());
|
||||
for (Entry<GameEntity, Map<CounterType, Integer>> e : decision.counterTable.row(Optional.absent()).entrySet()) {
|
||||
for (Entry<CounterType, Integer> v : e.getValue().entrySet()) {
|
||||
removed += v.getValue();
|
||||
e.getKey().subtractCounter(v.getKey(), v.getValue());
|
||||
}
|
||||
if (e.getKey() instanceof Card) {
|
||||
e.getKey().getGame().updateLastStateForCard((Card) e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1030,7 +1030,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, AbilityKey.newMap(runParams), false);
|
||||
}
|
||||
if (table != null) {
|
||||
table.put(this, counterType, addAmount);
|
||||
table.put(source, this, counterType, addAmount);
|
||||
}
|
||||
return addAmount;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,7 @@ public enum TriggerType {
|
||||
Clashed(TriggerClashed.class),
|
||||
CounterAdded(TriggerCounterAdded.class),
|
||||
CounterAddedOnce(TriggerCounterAddedOnce.class),
|
||||
CounterPlayerAddedAll(TriggerCounterPlayerAddedAll.class),
|
||||
CounterAddedAll(TriggerCounterAddedAll.class),
|
||||
Countered(TriggerCountered.class),
|
||||
CounterRemoved(TriggerCounterRemoved.class),
|
||||
|
||||
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.
|
||||
@@ -861,11 +861,11 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c.getCounters(cType) <= counterTable.get(c, cType)) {
|
||||
if (c.getCounters(cType) <= counterTable.get(null, c, cType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
counterTable.put(c, cType, 1);
|
||||
counterTable.put(null, c, cType, 1);
|
||||
|
||||
onSelectStateChanged(c, true);
|
||||
refresh();
|
||||
@@ -878,13 +878,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return null;
|
||||
}
|
||||
if (counterType != null) {
|
||||
if (c.getCounters(counterType) <= counterTable.get(c, counterType)) {
|
||||
if (c.getCounters(counterType) <= counterTable.get(null, c, counterType)) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
boolean found = false;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@@ -915,13 +915,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
}
|
||||
|
||||
private int getDistibutedCounters() {
|
||||
int sum = 0;
|
||||
|
||||
for (Integer v : this.counterTable.values()) {
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
return counterTable.totalValues();
|
||||
}
|
||||
|
||||
protected final boolean isValidChoice(final GameEntity choice) {
|
||||
@@ -934,7 +928,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
@Override
|
||||
public Collection<GameEntity> getSelected() {
|
||||
return counterTable.rowKeySet();
|
||||
return counterTable.columnKeySet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user