Merge branch '1580-znr-iridescent-hornbeetle-wants-to-count-counters-added-to-creatures' into 'master'

Resolve "ZNR: Iridescent Hornbeetle wants to count counters added to CREATURES"

Closes #1580

See merge request core-developers/forge!3196
This commit is contained in:
Michael Kamensky
2020-09-29 05:10:35 +00:00
8 changed files with 75 additions and 32 deletions

View File

@@ -20,9 +20,12 @@ package forge.game;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import forge.card.CardRarity; import forge.card.CardRarity;
import forge.card.CardStateName; import forge.card.CardStateName;
@@ -53,6 +56,8 @@ import forge.util.Visitor;
import java.util.*; import java.util.*;
import org.apache.commons.lang3.tuple.Pair;
/** /**
* Represents the state of a <i>single game</i>, a new instance is created for each game. * Represents the state of a <i>single game</i>, a new instance is created for each game.
*/ */
@@ -86,6 +91,8 @@ public class Game {
private Map<Player, PlayerCollection> attackedThisTurn = Maps.newHashMap(); private Map<Player, PlayerCollection> attackedThisTurn = Maps.newHashMap();
private Map<Player, PlayerCollection> attackedLastTurn = Maps.newHashMap(); private Map<Player, PlayerCollection> attackedLastTurn = Maps.newHashMap();
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
private Player monarch = null; private Player monarch = null;
private Player monarchBeginTurn = null; private Player monarchBeginTurn = null;
@@ -939,4 +946,46 @@ public class Game {
} }
return result; return result;
} }
public void onCleanupPhase() {
clearCounterAddedThisTurn();
for (Player player : getPlayers()) {
player.onCleanupPhase();
}
}
public void addCounterAddedThisTurn(Player putter, CounterType cType, Card card, Integer value) {
if (putter == null || card == null || value <= 0) {
return;
}
List<Pair<Card, Integer>> result = countersAddedThisTurn.get(cType, putter);
if (result == null) {
result = Lists.newArrayList();
}
result.add(Pair.of(CardUtil.getLKICopy(card), value));
if (!countersAddedThisTurn.contains(cType, putter)) {
countersAddedThisTurn.put(cType, putter, result);
}
}
public int getCounterAddedThisTurn(CounterType cType, String validPlayer, String validCard, Card source, Player sourceController, SpellAbility spellAbility) {
int result = 0;
if (!countersAddedThisTurn.containsRow(cType)) {
return result;
}
for (Map.Entry<Player, List<Pair<Card, Integer>>> e : countersAddedThisTurn.row(cType).entrySet()) {
if (e.getKey().isValid(validPlayer.split(","), sourceController, source, spellAbility)) {
for (Pair<Card, Integer> p : e.getValue()) {
if (p.getKey().isValid(validCard.split(","), sourceController, source, spellAbility)) {
result += p.getValue();
}
}
}
}
return result;
}
public void clearCounterAddedThisTurn() {
countersAddedThisTurn.clear();
}
} }

View File

@@ -1593,6 +1593,8 @@ public class AbilityUtils {
final String[] sq; final String[] sq;
sq = l[0].split("\\."); sq = l[0].split("\\.");
final Game game = c.getGame();
if (ctb != null) { if (ctb != null) {
// Count$Compare <int comparator value>.<True>.<False> // Count$Compare <int comparator value>.<True>.<False>
if (sq[0].startsWith("Compare")) { if (sq[0].startsWith("Compare")) {
@@ -1776,6 +1778,13 @@ public class AbilityUtils {
} }
} }
} }
if (l[0].startsWith("CountersAddedThisTurn")) {
final String[] parts = l[0].split(" ");
CounterType cType = CounterType.getType(parts[1]);
return CardFactoryUtil.doXMath(game.getCounterAddedThisTurn(cType, parts[2], parts[3], c, sa.getActivatingPlayer(), sa), expr, c);
}
} }
} }
return CardFactoryUtil.xCount(c, s2); return CardFactoryUtil.xCount(c, s2);

View File

@@ -1239,7 +1239,7 @@ public class Card extends GameEntity implements Comparable<Card> {
final int loyaltyBefore = getCurrentLoyalty(); final int loyaltyBefore = getCurrentLoyalty();
setCounters(counterType, newValue); setCounters(counterType, newValue);
getController().addCounterToPermThisTurn(counterType, addAmount); getGame().addCounterAddedThisTurn(source, counterType, this, addAmount);
view.updateCounters(this); view.updateCounters(this);
//fire card stats changed event if p/t bonuses or loyalty changed from added counters //fire card stats changed event if p/t bonuses or loyalty changed from added counters
@@ -1267,7 +1267,8 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} else { } else {
setCounters(counterType, newValue); setCounters(counterType, newValue);
getController().addCounterToPermThisTurn(counterType, addAmount);
getGame().addCounterAddedThisTurn(source, counterType, this, addAmount);
view.updateCounters(this); view.updateCounters(this);
} }
if (newValue <= 0) { if (newValue <= 0) {

View File

@@ -841,14 +841,6 @@ public class CardFactoryUtil {
return doXMath(maxNum, m, c); return doXMath(maxNum, m, c);
} }
// Count$CountersAddedToPermYouCtrl <CounterType>
if (l[0].startsWith("CountersAddedToPermYouCtrl")) {
final String[] components = l[0].split(" ", 2);
final CounterType counterType = CounterType.getType(components[1]);
int n = cc.getCounterToPermThisTurn(counterType);
return doXMath(n, m, c);
}
if (l[0].startsWith("CommanderCastFromCommandZone")) { if (l[0].startsWith("CommanderCastFromCommandZone")) {
// only used by Opal Palace, and it does add the trigger to the card // only used by Opal Palace, and it does add the trigger to the card
return doXMath(cc.getCommanderCast(c), m, c); return doXMath(cc.getCommanderCast(c), m, c);

View File

@@ -501,9 +501,7 @@ public class PhaseHandler implements java.io.Serializable {
bPreventCombatDamageThisTurn = false; bPreventCombatDamageThisTurn = false;
if (!bRepeatCleanup) { if (!bRepeatCleanup) {
// only call onCleanupPhase when Cleanup is not repeated // only call onCleanupPhase when Cleanup is not repeated
for (Player player : game.getPlayers()) { game.onCleanupPhase();
player.onCleanupPhase();
}
setPlayerTurn(handleNextTurn()); setPlayerTurn(handleNextTurn());
// "Trigger" for begin turn to get around a phase skipping // "Trigger" for begin turn to get around a phase skipping
final Map<AbilityKey, Object> runParams = AbilityKey.newMap(); final Map<AbilityKey, Object> runParams = AbilityKey.newMap();

View File

@@ -114,8 +114,6 @@ public class Player extends GameEntity implements Comparable<Player> {
private CardCollection sacrificedThisTurn = new CardCollection(); private CardCollection sacrificedThisTurn = new CardCollection();
private Map<CounterType, Integer> countersAddedtoPermThisTurn = Maps.newHashMap();
/** A list of tokens not in play, but on their way. /** A list of tokens not in play, but on their way.
* This list is kept in order to not break ETB-replacement * This list is kept in order to not break ETB-replacement
* on tokens. */ * on tokens. */
@@ -2289,20 +2287,6 @@ public class Player extends GameEntity implements Comparable<Player> {
sacrificedThisTurn.clear(); sacrificedThisTurn.clear();
} }
public final void addCounterToPermThisTurn(final CounterType type, final int x) {
countersAddedtoPermThisTurn.put(type, getCounterToPermThisTurn(type) + x);
}
public final Integer getCounterToPermThisTurn(final CounterType type) {
if (countersAddedtoPermThisTurn.containsKey(type))
return countersAddedtoPermThisTurn.get(type);
return 0;
}
public final void resetCounterToPermThisTurn() {
countersAddedtoPermThisTurn.clear();
}
public final int getSpellsCastThisTurn() { public final int getSpellsCastThisTurn() {
return spellsCastThisTurn; return spellsCastThisTurn;
} }
@@ -2521,7 +2505,6 @@ public class Player extends GameEntity implements Comparable<Player> {
resetSurveilThisTurn(); resetSurveilThisTurn();
resetCycledThisTurn(); resetCycledThisTurn();
resetSacrificedThisTurn(); resetSacrificedThisTurn();
resetCounterToPermThisTurn();
clearAssignedDamage(); clearAssignedDamage();
resetAttackersDeclaredThisTurn(); resetAttackersDeclaredThisTurn();
resetAttackedOpponentsThisTurn(); resetAttackedOpponentsThisTurn();

View File

@@ -5,6 +5,6 @@ PT:2/2
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | CheckSVar$ X | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of each end step, if a +1/+1 counter was put on a permanent under your control this turn, put a +1/+1 counter on CARDNAME. T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | CheckSVar$ X | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of each end step, if a +1/+1 counter was put on a permanent under your control this turn, put a +1/+1 counter on CARDNAME.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
DeckHints:Ability$Counters DeckHints:Ability$Counters
SVar:X:Count$CountersAddedToPermYouCtrl P1P1 DeckHas:Ability$Counters
SVar:Picture:http://www.wizards.com/global/images/magic/general/fairgrounds_trumpeter.jpg SVar:X:Count$CountersAddedThisTurn P1P1 Player Permanent.YouCtrl
Oracle:At the beginning of each end step, if a +1/+1 counter was put on a permanent under your control this turn, put a +1/+1 counter on Fairgrounds Trumpeter. Oracle:At the beginning of each end step, if a +1/+1 counter was put on a permanent under your control this turn, put a +1/+1 counter on Fairgrounds Trumpeter.

View File

@@ -0,0 +1,11 @@
Name:Iridescent Hornbeetle
ManaCost:4 G
Types:Creature Insect
PT:3/4
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, create a 1/1 green Insect creature token for each +1/+1 counter you've put on creatures under your control this turn.
SVar:TrigToken:DB$ Token | TokenAmount$ X | References$ X | TokenOwner$ You | TokenScript$ g_1_1_insect
SVar:X:Count$CountersAddedThisTurn P1P1 You Creature.YouCtrl
DeckNeeds:Ability$Counters
DeckHas:Ability$Counters
Oracle:At the beginning of your end step, create a 1/1 green Insect creature token for each +1/+1 counter you've put on creatures under your control this turn.