mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Merge remote-tracking branch 'core-developers/master'
This commit is contained in:
@@ -75,6 +75,7 @@ public enum AiProps { /** */
|
|||||||
ALWAYS_COPY_SPELL_IF_CMC_DIFF ("2"), /** */
|
ALWAYS_COPY_SPELL_IF_CMC_DIFF ("2"), /** */
|
||||||
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS ("true"), /** */
|
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS ("true"), /** */
|
||||||
ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE ("false"), /** */
|
ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE ("false"), /** */
|
||||||
|
ACTIVELY_PROTECT_VS_CURSE_AURAS("false"), /** */
|
||||||
DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD ("2"), /** */
|
DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD ("2"), /** */
|
||||||
DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR ("true"), /** */
|
DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR ("true"), /** */
|
||||||
DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR ("5"), /** */
|
DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR ("5"), /** */
|
||||||
|
|||||||
@@ -1845,6 +1845,28 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Generic curse auras
|
||||||
|
else if ((threatApi == ApiType.Attach && (topStack.isCurse() || "Curse".equals(topStack.getParam("AILogic"))))) {
|
||||||
|
AiController aic = aiPlayer.isAI() ? ((PlayerControllerAi)aiPlayer.getController()).getAi() : null;
|
||||||
|
boolean enableCurseAuraRemoval = aic != null ? aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE) : false;
|
||||||
|
if (enableCurseAuraRemoval) {
|
||||||
|
for (final Object o : objects) {
|
||||||
|
if (o instanceof Card) {
|
||||||
|
final Card c = (Card) o;
|
||||||
|
// give Shroud to targeted creatures
|
||||||
|
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll && tgt == null) && !grantShroud) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (saviourApi == ApiType.Protection) {
|
||||||
|
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threatened.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Iterables.addAll(threatened, ComputerUtil.predictThreatenedObjects(aiPlayer, saviour, topStack.getSubAbility()));
|
Iterables.addAll(threatened, ComputerUtil.predictThreatenedObjects(aiPlayer, saviour, topStack.getSubAbility()));
|
||||||
return threatened;
|
return threatened;
|
||||||
|
|||||||
@@ -340,17 +340,19 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// select the worst of the best
|
// select the worst of the best
|
||||||
final Card worst = ComputerUtilCard.getWorstAI(maxList);
|
final Card worst = ComputerUtilCard.getWorstAI(maxList);
|
||||||
if (worst.isLand()) {
|
if (worst != null) {
|
||||||
// e.g. Clan Guildmage, make sure we're not using the same land we want to animate to activate the ability
|
if (worst.isLand()) {
|
||||||
this.holdAnimatedTillMain2(ai, worst);
|
// e.g. Clan Guildmage, make sure we're not using the same land we want to animate to activate the ability
|
||||||
if (!ComputerUtilMana.canPayManaCost(sa, ai, 0)) {
|
this.holdAnimatedTillMain2(ai, worst);
|
||||||
this.releaseHeldTillMain2(ai, worst);
|
if (!ComputerUtilMana.canPayManaCost(sa, ai, 0)) {
|
||||||
return false;
|
this.releaseHeldTillMain2(ai, worst);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.rememberAnimatedThisTurn(ai, worst);
|
||||||
|
sa.getTargets().add(worst);
|
||||||
}
|
}
|
||||||
this.rememberAnimatedThisTurn(ai, worst);
|
return true;
|
||||||
sa.getTargets().add(worst);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is reasonable for now. Kamahl, Fist of Krosa and a sorcery or
|
// This is reasonable for now. Kamahl, Fist of Krosa and a sorcery or
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
private boolean smallSetOverride = false;
|
private boolean smallSetOverride = false;
|
||||||
private String boosterMustContain = "";
|
private String boosterMustContain = "";
|
||||||
private String boosterReplaceSlotFromPrintSheet = "";
|
private String boosterReplaceSlotFromPrintSheet = "";
|
||||||
|
private String[] chaosDraftThemes = new String[0];
|
||||||
private boolean doublePickToStartRound = false;
|
private boolean doublePickToStartRound = false;
|
||||||
private final CardInSet[] cards;
|
private final CardInSet[] cards;
|
||||||
private final Map<String, Integer> tokenNormalized;
|
private final Map<String, Integer> tokenNormalized;
|
||||||
@@ -195,6 +196,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
public boolean getDoublePickToStartRound() { return doublePickToStartRound; }
|
public boolean getDoublePickToStartRound() { return doublePickToStartRound; }
|
||||||
public String getBoosterMustContain() { return boosterMustContain; }
|
public String getBoosterMustContain() { return boosterMustContain; }
|
||||||
public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; }
|
public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; }
|
||||||
|
public String[] getChaosDraftThemes() { return chaosDraftThemes; }
|
||||||
public CardInSet[] getCards() { return cards; }
|
public CardInSet[] getCards() { return cards; }
|
||||||
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
||||||
|
|
||||||
@@ -385,6 +387,9 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
|
|
||||||
res.boosterMustContain = section.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature
|
res.boosterMustContain = section.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature
|
||||||
res.boosterReplaceSlotFromPrintSheet = section.get("BoosterReplaceSlotFromPrintSheet", ""); // e.g. Zendikar Rising guaranteed double-faced card
|
res.boosterReplaceSlotFromPrintSheet = section.get("BoosterReplaceSlotFromPrintSheet", ""); // e.g. Zendikar Rising guaranteed double-faced card
|
||||||
|
|
||||||
|
res.chaosDraftThemes = section.get("ChaosDraftThemes", "").split(";"); // semicolon separated list of theme names
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,16 +139,22 @@ public final class MagicColor {
|
|||||||
public static final ImmutableList<String> SNOW_LANDS = ImmutableList.of("Snow-Covered Plains", "Snow-Covered Island", "Snow-Covered Swamp", "Snow-Covered Mountain", "Snow-Covered Forest");
|
public static final ImmutableList<String> SNOW_LANDS = ImmutableList.of("Snow-Covered Plains", "Snow-Covered Island", "Snow-Covered Swamp", "Snow-Covered Mountain", "Snow-Covered Forest");
|
||||||
public static final ImmutableMap<String, String> ANY_COLOR_CONVERSION = new ImmutableMap.Builder<String, String>()
|
public static final ImmutableMap<String, String> ANY_COLOR_CONVERSION = new ImmutableMap.Builder<String, String>()
|
||||||
.put("ManaColorConversion", "Additive")
|
.put("ManaColorConversion", "Additive")
|
||||||
.put("WhiteConversion", "All")
|
.put("WhiteConversion", "Color")
|
||||||
.put("BlueConversion", "All")
|
.put("BlueConversion", "Color")
|
||||||
.put("BlackConversion", "All")
|
.put("BlackConversion", "Color")
|
||||||
.put("RedConversion", "All")
|
.put("RedConversion", "Color")
|
||||||
.put("GreenConversion", "All")
|
.put("GreenConversion", "Color")
|
||||||
|
.put("ColorlessConversion", "Color")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static final ImmutableMap<String, String> ANY_TYPE_CONVERSION = new ImmutableMap.Builder<String, String>()
|
public static final ImmutableMap<String, String> ANY_TYPE_CONVERSION = new ImmutableMap.Builder<String, String>()
|
||||||
.putAll(ANY_COLOR_CONVERSION)
|
.put("ManaColorConversion", "Additive")
|
||||||
.put("ColorlessConversion", "All")
|
.put("WhiteConversion", "Type")
|
||||||
|
.put("BlueConversion", "Type")
|
||||||
|
.put("BlackConversion", "Type")
|
||||||
|
.put("RedConversion", "Type")
|
||||||
|
.put("GreenConversion", "Type")
|
||||||
|
.put("ColorlessConversion", "Type")
|
||||||
.build();
|
.build();
|
||||||
/**
|
/**
|
||||||
* Private constructor to prevent instantiation.
|
* Private constructor to prevent instantiation.
|
||||||
|
|||||||
@@ -3,23 +3,20 @@ package forge.item.generation;
|
|||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.item.BoosterPack;
|
import forge.item.BoosterPack;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
|
import forge.util.BagRandomizer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ChaosBoosterSupplier implements IUnOpenedProduct {
|
public class ChaosBoosterSupplier implements IUnOpenedProduct {
|
||||||
private List<CardEdition> sets;
|
private BagRandomizer<CardEdition> randomizer;
|
||||||
|
|
||||||
public ChaosBoosterSupplier(List<CardEdition> sets) {
|
public ChaosBoosterSupplier(Iterable<CardEdition> sets) throws IllegalArgumentException {
|
||||||
this.sets = sets;
|
randomizer = new BagRandomizer<>(sets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PaperCard> get() {
|
public List<PaperCard> get() {
|
||||||
if (sets.size() == 0) {
|
final CardEdition set = randomizer.getNextItem();
|
||||||
System.out.println("No chaos boosters left to supply.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final CardEdition set = sets.remove(0);
|
|
||||||
final BoosterPack pack = new BoosterPack(set.getCode(), set.getBoosterTemplate());
|
final BoosterPack pack = new BoosterPack(set.getCode(), set.getBoosterTemplate());
|
||||||
return pack.getCards();
|
return pack.getCards();
|
||||||
}
|
}
|
||||||
|
|||||||
76
forge-core/src/main/java/forge/util/BagRandomizer.java
Normal file
76
forge-core/src/main/java/forge/util/BagRandomizer.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package forge.util;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure that allows random draws from a set number of items,
|
||||||
|
* where all items are returned once before the first will be retrieved.
|
||||||
|
* The bag will be shuffled after each time all items have been returned.
|
||||||
|
* @param <T> an object
|
||||||
|
*/
|
||||||
|
public class BagRandomizer<T > implements Iterable<T>{
|
||||||
|
private static Random random = new SecureRandom();
|
||||||
|
|
||||||
|
private T[] bag;
|
||||||
|
private int currentPosition = 0;
|
||||||
|
|
||||||
|
public BagRandomizer(T[] items) throws IllegalArgumentException {
|
||||||
|
if (items.length == 0) {
|
||||||
|
throw new IllegalArgumentException("Must include at least one item!");
|
||||||
|
}
|
||||||
|
bag = items;
|
||||||
|
shuffleBag();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BagRandomizer(Iterable<T> items) throws IllegalArgumentException {
|
||||||
|
ArrayList<T> list = new ArrayList<>();
|
||||||
|
for (T item : items) {
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
if (list.size() == 0) {
|
||||||
|
throw new IllegalArgumentException("Must include at least one item!");
|
||||||
|
}
|
||||||
|
bag = (T[]) list.toArray();
|
||||||
|
shuffleBag();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getNextItem() {
|
||||||
|
// reset bag if last position is reached
|
||||||
|
if (currentPosition >= bag.length) {
|
||||||
|
shuffleBag();
|
||||||
|
currentPosition = 0;
|
||||||
|
}
|
||||||
|
return bag[currentPosition++];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shuffleBag() {
|
||||||
|
int n = bag.length;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int r = (int) (random.nextDouble() * (i + 1));
|
||||||
|
T swap = bag[r];
|
||||||
|
bag[r] = bag[i];
|
||||||
|
bag[i] = swap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return new BagRandomizerIterator<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BagRandomizerIterator<T> implements Iterator<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return bag.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
return (T) BagRandomizer.this.getNextItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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/>.
|
||||||
*/
|
*/
|
||||||
@@ -33,7 +33,7 @@ import java.util.Map.Entry;
|
|||||||
* <p>
|
* <p>
|
||||||
* GameInfo class.
|
* GameInfo class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id: GameOutcome.java 17608 2012-10-20 22:27:27Z Max mtg $
|
* @version $Id: GameOutcome.java 17608 2012-10-20 22:27:27Z Max mtg $
|
||||||
*/
|
*/
|
||||||
@@ -47,7 +47,7 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
|
|
||||||
public final List<PaperCard> lostCards;
|
public final List<PaperCard> lostCards;
|
||||||
public final List<PaperCard> wonCards;
|
public final List<PaperCard> wonCards;
|
||||||
|
|
||||||
private AnteResult(List<PaperCard> cards, boolean won) {
|
private AnteResult(List<PaperCard> cards, boolean won) {
|
||||||
// Need empty lists for other results for addition of change ownership cards
|
// Need empty lists for other results for addition of change ownership cards
|
||||||
if (won) {
|
if (won) {
|
||||||
@@ -67,15 +67,20 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
this.lostCards.addAll(cards);
|
this.lostCards.addAll(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AnteResult won(List<PaperCard> cards) { return new AnteResult(cards, true); }
|
public static AnteResult won(List<PaperCard> cards) {
|
||||||
public static AnteResult lost(List<PaperCard> cards) { return new AnteResult(cards, false); }
|
return new AnteResult(cards, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnteResult lost(List<PaperCard> cards) {
|
||||||
|
return new AnteResult(cards, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int lastTurnNumber = 0;
|
private int lastTurnNumber = 0;
|
||||||
private int lifeDelta = 0;
|
private int lifeDelta = 0;
|
||||||
private int winningTeam = -1;
|
private int winningTeam = -1;
|
||||||
|
|
||||||
private final HashMap<RegisteredPlayer, PlayerStatistics> playerRating = new HashMap<>();
|
private final HashMap<RegisteredPlayer, PlayerStatistics> playerRating = new HashMap<>();
|
||||||
private final HashMap<RegisteredPlayer, String> playerNames = new HashMap<>();
|
private final HashMap<RegisteredPlayer, String> playerNames = new HashMap<>();
|
||||||
|
|
||||||
public final Map<RegisteredPlayer, AnteResult> anteResult = new HashMap<>();
|
public final Map<RegisteredPlayer, AnteResult> anteResult = new HashMap<>();
|
||||||
@@ -83,21 +88,22 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
|
|
||||||
public GameOutcome(GameEndReason reason, final Iterable<Player> players) {
|
public GameOutcome(GameEndReason reason, final Iterable<Player> players) {
|
||||||
winCondition = reason;
|
winCondition = reason;
|
||||||
calculateLifeDelta(players);
|
|
||||||
|
|
||||||
int winnersHealth = 0;
|
|
||||||
int opponentsHealth = 0;
|
|
||||||
|
|
||||||
for (final Player p : players) {
|
for (final Player p : players) {
|
||||||
this.playerRating.put(p.getRegisteredPlayer(), p.getStats());
|
this.playerRating.put(p.getRegisteredPlayer(), p.getStats());
|
||||||
this.playerNames.put(p.getRegisteredPlayer(), p.getName());
|
this.playerNames.put(p.getRegisteredPlayer(), p.getName());
|
||||||
|
|
||||||
if (p.getOutcome().hasWon() && winCondition == GameEndReason.AllOpposingTeamsLost) {
|
if (winCondition == GameEndReason.AllOpposingTeamsLost && p.getOutcome().hasWon()) {
|
||||||
// Only mark the WinningTeam when "Team mode" is on.
|
// Only mark the WinningTeam when "Team mode" is on.
|
||||||
winningTeam = p.getTeam();
|
winningTeam = p.getTeam();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unable to calculate lifeDelta between a winning and losing player whe a draw is in place
|
||||||
|
if (winCondition == GameEndReason.Draw) return;
|
||||||
|
|
||||||
|
int winnersHealth = 0;
|
||||||
|
int opponentsHealth = 0;
|
||||||
for (final Player p : players) {
|
for (final Player p : players) {
|
||||||
if (p.getTeam() == winningTeam) {
|
if (p.getTeam() == winningTeam) {
|
||||||
winnersHealth += p.getLife();
|
winnersHealth += p.getLife();
|
||||||
@@ -106,22 +112,22 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateLifeDelta(players);
|
||||||
lifeDelta = Math.max(0, winnersHealth - opponentsHealth);
|
lifeDelta = Math.max(0, winnersHealth - opponentsHealth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateLifeDelta(Iterable<Player> players) {
|
private void calculateLifeDelta(Iterable<Player> players) {
|
||||||
int opponentsHealth = 0;
|
int opponentsHealth = 0;
|
||||||
int winnersHealth = 0;
|
int winnersHealth = 0;
|
||||||
|
|
||||||
for (Player p : players) {
|
for (Player p : players) {
|
||||||
if (p.getOutcome().hasWon()) {
|
if (p.getOutcome().hasWon()) {
|
||||||
winnersHealth += p.getLife();
|
winnersHealth += p.getLife();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
opponentsHealth += p.getLife();
|
opponentsHealth += p.getLife();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifeDelta = Math.max(0, winnersHealth - opponentsHealth);
|
lifeDelta = Math.max(0, winnersHealth - opponentsHealth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +156,7 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the winner.
|
* Gets the winner.
|
||||||
*
|
*
|
||||||
* @return the winner
|
* @return the winner
|
||||||
*/
|
*/
|
||||||
public LobbyPlayer getWinningLobbyPlayer() {
|
public LobbyPlayer getWinningLobbyPlayer() {
|
||||||
@@ -169,7 +175,7 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
* distinguish between human player names (a problem for hotseat games).
|
* distinguish between human player names (a problem for hotseat games).
|
||||||
*/
|
*/
|
||||||
public RegisteredPlayer getWinningPlayer() {
|
public RegisteredPlayer getWinningPlayer() {
|
||||||
for(Entry<RegisteredPlayer, PlayerStatistics> pair : playerRating.entrySet()) {
|
for (Entry<RegisteredPlayer, PlayerStatistics> pair : playerRating.entrySet()) {
|
||||||
if (pair.getValue().getOutcome().hasWon()) {
|
if (pair.getValue().getOutcome().hasWon()) {
|
||||||
return pair.getKey();
|
return pair.getKey();
|
||||||
}
|
}
|
||||||
@@ -196,7 +202,7 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the win spell effect.
|
* Gets the win spell effect.
|
||||||
*
|
*
|
||||||
* @return the win spell effect
|
* @return the win spell effect
|
||||||
*/
|
*/
|
||||||
public String getWinSpellEffect() {
|
public String getWinSpellEffect() {
|
||||||
@@ -227,7 +233,7 @@ public final class GameOutcome implements Iterable<Entry<RegisteredPlayer, Playe
|
|||||||
|
|
||||||
public List<String> getOutcomeStrings() {
|
public List<String> getOutcomeStrings() {
|
||||||
List<String> outcomes = Lists.newArrayList();
|
List<String> outcomes = Lists.newArrayList();
|
||||||
for(RegisteredPlayer player : playerNames.keySet()) {
|
for (RegisteredPlayer player : playerNames.keySet()) {
|
||||||
outcomes.add(getOutcomeString(player));
|
outcomes.add(getOutcomeString(player));
|
||||||
}
|
}
|
||||||
return outcomes;
|
return outcomes;
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ public class GameView extends TrackableObject {
|
|||||||
public boolean isMatchOver() {
|
public boolean isMatchOver() {
|
||||||
return get(TrackableProperty.MatchOver);
|
return get(TrackableProperty.MatchOver);
|
||||||
}
|
}
|
||||||
|
public boolean isMulligan() {
|
||||||
|
if (get(TrackableProperty.Mulligan) == null)
|
||||||
|
return false;
|
||||||
|
return get(TrackableProperty.Mulligan);
|
||||||
|
}
|
||||||
|
public void updateIsMulligan(boolean value) {
|
||||||
|
set(TrackableProperty.Mulligan, value);
|
||||||
|
}
|
||||||
public String getWinningPlayerName() {
|
public String getWinningPlayerName() {
|
||||||
return get(TrackableProperty.WinningPlayerName);
|
return get(TrackableProperty.WinningPlayerName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
else if (defined.equals("Enchanted")) {
|
else if (defined.equals("Enchanted")) {
|
||||||
c = hostCard.getEnchantingCard();
|
c = hostCard.getEnchantingCard();
|
||||||
if ((c == null) && (sa.getRootAbility() != null)
|
if ((c == null) && (sa != null) && (sa.getRootAbility() != null)
|
||||||
&& (sa.getRootAbility().getPaidList("Sacrificed") != null)
|
&& (sa.getRootAbility().getPaidList("Sacrificed") != null)
|
||||||
&& !sa.getRootAbility().getPaidList("Sacrificed").isEmpty()) {
|
&& !sa.getRootAbility().getPaidList("Sacrificed").isEmpty()) {
|
||||||
c = sa.getRootAbility().getPaidList("Sacrificed").get(0).getEnchantingCard();
|
c = sa.getRootAbility().getPaidList("Sacrificed").get(0).getEnchantingCard();
|
||||||
@@ -168,8 +168,8 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
if (crd instanceof Card) {
|
if (crd instanceof Card) {
|
||||||
c = game.getCardState((Card) crd);
|
c = game.getCardState((Card) crd);
|
||||||
} else if (crd instanceof List<?>) {
|
} else if (crd instanceof Iterable<?>) {
|
||||||
cards.addAll((CardCollection) crd);
|
cards.addAll(Iterables.filter((Iterable<?>) crd, Card.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (defined.equals("Remembered") || defined.equals("RememberedCard")) {
|
else if (defined.equals("Remembered") || defined.equals("RememberedCard")) {
|
||||||
@@ -657,9 +657,7 @@ public class AbilityUtils {
|
|||||||
if (calcX[0].startsWith("TriggeredPlayers")) {
|
if (calcX[0].startsWith("TriggeredPlayers")) {
|
||||||
key = "Triggered" + key.substring(16);
|
key = "Triggered" + key.substring(16);
|
||||||
}
|
}
|
||||||
final List<Player> players = new ArrayList<>();
|
return CardFactoryUtil.playerXCount(getDefinedPlayers(card, key, sa), calcX[1], card) * multiplier;
|
||||||
Iterables.addAll(players, getDefinedPlayers(card, key, sa));
|
|
||||||
return CardFactoryUtil.playerXCount(players, calcX[1], card) * multiplier;
|
|
||||||
}
|
}
|
||||||
if (calcX[0].startsWith("TriggeredPlayer") || calcX[0].startsWith("TriggeredTarget")) {
|
if (calcX[0].startsWith("TriggeredPlayer") || calcX[0].startsWith("TriggeredTarget")) {
|
||||||
final SpellAbility root = sa.getRootAbility();
|
final SpellAbility root = sa.getRootAbility();
|
||||||
@@ -1078,20 +1076,10 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
if (o != null) {
|
if (o != null) {
|
||||||
if (o instanceof Player) {
|
if (o instanceof Player) {
|
||||||
final Player p = (Player) o;
|
players.add((Player) o);
|
||||||
if (!players.contains(p)) {
|
|
||||||
players.add(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (o instanceof List) {
|
if (o instanceof Iterable) {
|
||||||
final List<?> pList = (List<?>)o;
|
players.addAll(Iterables.filter((Iterable<?>)o, Player.class));
|
||||||
if (!pList.isEmpty()) {
|
|
||||||
for (final Object p : pList) {
|
|
||||||
if (p instanceof Player && !players.contains(p)) {
|
|
||||||
players.add((Player) p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1802,9 +1790,12 @@ public class AbilityUtils {
|
|||||||
if (params.containsKey(key)) {
|
if (params.containsKey(key)) {
|
||||||
String convertTo = params.get(key);
|
String convertTo = params.get(key);
|
||||||
byte convertByte = 0;
|
byte convertByte = 0;
|
||||||
if ("All".equals(convertTo)) {
|
if ("Type".equals(convertTo)) {
|
||||||
// IMPORTANT! We need to use Mana Color here not Card Color.
|
// IMPORTANT! We need to use Mana Color here not Card Color.
|
||||||
convertByte = ManaAtom.ALL_MANA_TYPES;
|
convertByte = ManaAtom.ALL_MANA_TYPES;
|
||||||
|
} else if ("Color".equals(convertTo)) {
|
||||||
|
// IMPORTANT! We need to use Mana Color here not Card Color.
|
||||||
|
convertByte = ManaAtom.ALL_MANA_COLORS;
|
||||||
} else {
|
} else {
|
||||||
for (final String convertColor : convertTo.split(",")) {
|
for (final String convertColor : convertTo.split(",")) {
|
||||||
convertByte |= ManaAtom.fromName(convertColor);
|
convertByte |= ManaAtom.fromName(convertColor);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import forge.GameCommand;
|
|||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
|
import forge.game.GameLogEntryType;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -431,6 +432,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
final Player player = sa.getActivatingPlayer();
|
final Player player = sa.getActivatingPlayer();
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
final Game game = player.getGame();
|
final Game game = player.getGame();
|
||||||
|
final CardCollection commandCards = new CardCollection();
|
||||||
|
|
||||||
ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||||
final List<ZoneType> origin = Lists.newArrayList();
|
final List<ZoneType> origin = Lists.newArrayList();
|
||||||
@@ -664,6 +666,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
tgtC.setExiledWith(host);
|
tgtC.setExiledWith(host);
|
||||||
}
|
}
|
||||||
movedCard = game.getAction().moveTo(destination, tgtC, sa);
|
movedCard = game.getAction().moveTo(destination, tgtC, sa);
|
||||||
|
if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(movedCard.getName()).append(" has moved from Command Zone to ").append(player).append("'s hand.");
|
||||||
|
game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString());
|
||||||
|
commandCards.add(movedCard); //add to list to reveal the commandzone cards
|
||||||
|
}
|
||||||
// If a card is Exiled from the stack, remove its spells from the stack
|
// If a card is Exiled from the stack, remove its spells from the stack
|
||||||
if (sa.hasParam("Fizzle")) {
|
if (sa.hasParam("Fizzle")) {
|
||||||
if (tgtC.isInZone(ZoneType.Exile) || tgtC.isInZone(ZoneType.Hand)
|
if (tgtC.isInZone(ZoneType.Exile) || tgtC.isInZone(ZoneType.Hand)
|
||||||
@@ -709,6 +717,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reveal command cards that changes zone from command zone to player's hand
|
||||||
|
if (!commandCards.isEmpty()) {
|
||||||
|
game.getAction().reveal(commandCards, player, true, "Revealed cards in ");
|
||||||
|
}
|
||||||
|
|
||||||
triggerList.triggerChangesZoneAll(game);
|
triggerList.triggerChangesZoneAll(game);
|
||||||
|
|
||||||
// for things like Gaea's Blessing
|
// for things like Gaea's Blessing
|
||||||
|
|||||||
@@ -56,8 +56,9 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
|
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
|
||||||
final int num;
|
final int num;
|
||||||
// hotfix for Vindictive Lich when using getCardForUi
|
boolean additionalDesc = sa.hasParam("AdditionalDescription");
|
||||||
if (source.getController() == null && sa.getParamOrDefault("CharmNum", "1").contains("MaxUniqueOpponents")) {
|
// hotfix for complex cards when using getCardForUi
|
||||||
|
if (source.getController() == null && additionalDesc) {
|
||||||
// using getCardForUi game is not set, so can't guess max charm
|
// using getCardForUi game is not set, so can't guess max charm
|
||||||
num = Integer.MAX_VALUE;
|
num = Integer.MAX_VALUE;
|
||||||
} else {
|
} else {
|
||||||
@@ -73,8 +74,8 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
sb.append(sa.getCostDescription());
|
sb.append(sa.getCostDescription());
|
||||||
sb.append(oppChooses ? "An opponent chooses " : "Choose ");
|
sb.append(oppChooses ? "An opponent chooses " : "Choose ");
|
||||||
|
|
||||||
if (num == min) {
|
if (num == min || num == Integer.MAX_VALUE) {
|
||||||
sb.append(Lang.getNumeral(num));
|
sb.append(Lang.getNumeral(min));
|
||||||
} else if (min == 0) {
|
} else if (min == 0) {
|
||||||
sb.append("up to ").append(Lang.getNumeral(num));
|
sb.append("up to ").append(Lang.getNumeral(num));
|
||||||
} else {
|
} else {
|
||||||
@@ -97,7 +98,6 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
sb.append(". You may choose the same mode more than once.");
|
sb.append(". You may choose the same mode more than once.");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean additionalDesc = sa.hasParam("AdditionalDescription");
|
|
||||||
if (additionalDesc) {
|
if (additionalDesc) {
|
||||||
sb.append(" ").append(sa.getParam("AdditionalDescription").trim());
|
sb.append(" ").append(sa.getParam("AdditionalDescription").trim());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,6 +163,11 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
host.addRemembered(one);
|
host.addRemembered(one);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sa.hasParam("ImprintRevealed") && hasRevealed) {
|
||||||
|
for (final Card one : top) {
|
||||||
|
host.addImprintedCard(one);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (sa.hasParam("Choser")) {
|
if (sa.hasParam("Choser")) {
|
||||||
final FCollectionView<Player> choosers = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Choser"), sa);
|
final FCollectionView<Player> choosers = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Choser"), sa);
|
||||||
if (!choosers.isEmpty()) {
|
if (!choosers.isEmpty()) {
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ public class LifeExchangeEffect extends SpellAbilityEffect {
|
|||||||
final int life1 = p1.getLife();
|
final int life1 = p1.getLife();
|
||||||
final int life2 = p2.getLife();
|
final int life2 = p2.getLife();
|
||||||
|
|
||||||
|
if (sa.hasParam("RememberDifference")) {
|
||||||
|
final int diff = life1 - life2;
|
||||||
|
source.addRemembered(diff);
|
||||||
|
}
|
||||||
|
|
||||||
if ((life1 > life2) && p1.canLoseLife() && p2.canGainLife()) {
|
if ((life1 > life2) && p1.canLoseLife() && p2.canGainLife()) {
|
||||||
final int diff = life1 - life2;
|
final int diff = life1 - life2;
|
||||||
p1.loseLife(diff);
|
p1.loseLife(diff);
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public class SetStateEffect extends SpellAbilityEffect {
|
|||||||
final boolean manifestUp = sa.hasParam("ManifestUp");
|
final boolean manifestUp = sa.hasParam("ManifestUp");
|
||||||
final boolean hiddenAgenda = sa.hasParam("HiddenAgenda");
|
final boolean hiddenAgenda = sa.hasParam("HiddenAgenda");
|
||||||
final boolean optional = sa.hasParam("Optional");
|
final boolean optional = sa.hasParam("Optional");
|
||||||
|
final CardCollection transformedCards = new CardCollection();
|
||||||
|
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
|
|
||||||
@@ -130,8 +131,12 @@ public class SetStateEffect extends SpellAbilityEffect {
|
|||||||
if (remChanged) {
|
if (remChanged) {
|
||||||
host.addRemembered(tgt);
|
host.addRemembered(tgt);
|
||||||
}
|
}
|
||||||
|
transformedCards.add(tgt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table.triggerCountersPutAll(game);
|
table.triggerCountersPutAll(game);
|
||||||
|
if (!transformedCards.isEmpty()) {
|
||||||
|
game.getAction().reveal(transformedCards, p, true, "Transformed cards in ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1823,7 +1823,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|
||||||
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|
||||||
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|
||||||
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
|
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")
|
||||||
|
|| keyword.startsWith("Encore")) {
|
||||||
// keyword parsing takes care of adding a proper description
|
// keyword parsing takes care of adding a proper description
|
||||||
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
|
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
|
||||||
sbLong.append(getName()).append(" can't be blocked ");
|
sbLong.append(getName()).append(" can't be blocked ");
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
private Table<Card, GameEntity, Integer> dataMap = HashBasedTable.create();
|
private Table<Card, GameEntity, Integer> dataMap = HashBasedTable.create();
|
||||||
|
|
||||||
public CardDamageMap(Table<Card, GameEntity, Integer> damageMap) {
|
public CardDamageMap(Table<Card, GameEntity, Integer> damageMap) {
|
||||||
this.putAll(damageMap);
|
putAll(damageMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardDamageMap() {
|
public CardDamageMap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void triggerPreventDamage(boolean isCombat) {
|
public void triggerPreventDamage(boolean isCombat) {
|
||||||
for (Map.Entry<GameEntity, Map<Card, Integer>> e : this.columnMap().entrySet()) {
|
for (Map.Entry<GameEntity, Map<Card, Integer>> e : columnMap().entrySet()) {
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (final int i : e.getValue().values()) {
|
for (final int i : e.getValue().values()) {
|
||||||
sum += i;
|
sum += i;
|
||||||
@@ -51,7 +51,7 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
|
|
||||||
public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) {
|
public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) {
|
||||||
// Source -> Targets
|
// Source -> Targets
|
||||||
for (Map.Entry<Card, Map<GameEntity, Integer>> e : this.rowMap().entrySet()) {
|
for (Map.Entry<Card, Map<GameEntity, Integer>> e : rowMap().entrySet()) {
|
||||||
final Card sourceLKI = e.getKey();
|
final Card sourceLKI = e.getKey();
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (final Integer i : e.getValue().values()) {
|
for (final Integer i : e.getValue().values()) {
|
||||||
@@ -71,7 +71,7 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Targets -> Source
|
// Targets -> Source
|
||||||
for (Map.Entry<GameEntity, Map<Card, Integer>> e : this.columnMap().entrySet()) {
|
for (Map.Entry<GameEntity, Map<Card, Integer>> e : columnMap().entrySet()) {
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (final int i : e.getValue().values()) {
|
for (final int i : e.getValue().values()) {
|
||||||
sum += i;
|
sum += i;
|
||||||
@@ -106,9 +106,16 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
return dataMap;
|
return dataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int filteredAmount(String validSource, String validTarget, Card host, SpellAbility sa) {
|
public int totalAmount() {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
for (int i : values()) {
|
||||||
|
result += i;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardDamageMap filteredMap(String validSource, String validTarget, Card host, SpellAbility sa) {
|
||||||
|
CardDamageMap result = new CardDamageMap();
|
||||||
Set<Card> filteredSource = null;
|
Set<Card> filteredSource = null;
|
||||||
Set<GameEntity> filteredTarget = null;
|
Set<GameEntity> filteredTarget = null;
|
||||||
if (validSource != null) {
|
if (validSource != null) {
|
||||||
@@ -125,7 +132,8 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
if (filteredTarget != null && !filteredTarget.contains(c.getColumnKey())) {
|
if (filteredTarget != null && !filteredTarget.contains(c.getColumnKey())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
result += c.getValue();
|
|
||||||
|
result.put(c.getRowKey(), c.getColumnKey(), c.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -1156,6 +1156,9 @@ public class CardFactoryUtil {
|
|||||||
if (sq[0].contains("Landfall")) {
|
if (sq[0].contains("Landfall")) {
|
||||||
return doXMath(Integer.parseInt(sq[cc.hasLandfall() ? 1 : 2]), m, c);
|
return doXMath(Integer.parseInt(sq[cc.hasLandfall() ? 1 : 2]), m, c);
|
||||||
}
|
}
|
||||||
|
if (sq[0].contains("Monarch")) {
|
||||||
|
return doXMath(Integer.parseInt(sq[cc.equals(game.getMonarch()) ? 1 : 2]), m, c);
|
||||||
|
}
|
||||||
if (sq[0].contains("Blessing")) {
|
if (sq[0].contains("Blessing")) {
|
||||||
return doXMath(Integer.parseInt(sq[cc.hasBlessing() ? 1 : 2]), m, c);
|
return doXMath(Integer.parseInt(sq[cc.hasBlessing() ? 1 : 2]), m, c);
|
||||||
}
|
}
|
||||||
@@ -2906,7 +2909,7 @@ public class CardFactoryUtil {
|
|||||||
final String repeatStr = "DB$ RepeatEach | RepeatPlayers$ OpponentsOtherThanDefendingPlayer | ChangeZoneTable$ True";
|
final String repeatStr = "DB$ RepeatEach | RepeatPlayers$ OpponentsOtherThanDefendingPlayer | ChangeZoneTable$ True";
|
||||||
|
|
||||||
final String copyStr = "DB$ CopyPermanent | Defined$ Self | TokenTapped$ True | Optional$ True | TokenAttacking$ Remembered"
|
final String copyStr = "DB$ CopyPermanent | Defined$ Self | TokenTapped$ True | Optional$ True | TokenAttacking$ Remembered"
|
||||||
+ " | ChoosePlayerOrPlaneswalker$ True | ImprintTokens$ True";
|
+ " | ChoosePlayerOrPlaneswalker$ True | ImprintTokens$ True";
|
||||||
|
|
||||||
final String delTrigStr = "DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | RememberObjects$ Imprinted"
|
final String delTrigStr = "DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | RememberObjects$ Imprinted"
|
||||||
+ " | TriggerDescription$ Exile the tokens at end of combat.";
|
+ " | TriggerDescription$ Exile the tokens at end of combat.";
|
||||||
@@ -4344,6 +4347,52 @@ public class CardFactoryUtil {
|
|||||||
sa.setIntrinsic(intrinsic);
|
sa.setIntrinsic(intrinsic);
|
||||||
inst.addSpellAbility(sa);
|
inst.addSpellAbility(sa);
|
||||||
|
|
||||||
|
} else if (keyword.startsWith("Encore")) {
|
||||||
|
final String[] k = keyword.split(":");
|
||||||
|
final String manacost = k[1];
|
||||||
|
|
||||||
|
String effect = "AB$ RepeatEach | Cost$ " + manacost + " ExileFromGrave<1/CARDNAME> " +
|
||||||
|
"| ActivationZone$ Graveyard | RepeatPlayers$ Opponent" +
|
||||||
|
"| PrecostDesc$ Encore | CostDesc$ " + ManaCostParser.parse(manacost) +
|
||||||
|
"| SpellDescription$ (" + inst.getReminderText() + ")";
|
||||||
|
|
||||||
|
final String copyStr = "DB$ CopyPermanent | Defined$ Self | ImprintTokens$ True " +
|
||||||
|
"| AddKeywords$ Haste | RememberTokens$ True | TokenRemembered$ Player.IsRemembered";
|
||||||
|
|
||||||
|
final String pumpStr = "DB$ PumpAll | ValidCards$ Creature.IsRemembered " +
|
||||||
|
"| KW$ HIDDEN CARDNAME attacks specific player each combat if able:Remembered";
|
||||||
|
|
||||||
|
final String pumpcleanStr = "DB$ Cleanup | ForgetDefined$ RememberedCard";
|
||||||
|
|
||||||
|
final String delTrigStr = "DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | RememberObjects$ Imprinted " +
|
||||||
|
"| StackDescription$ None | TriggerDescription$ Sacrifice them at the beginning of the next end step.";
|
||||||
|
|
||||||
|
final String sacStr = "DB$ SacrificeAll | Defined$ DelayTriggerRemembered";
|
||||||
|
|
||||||
|
final String cleanupStr = "DB$ Cleanup | ClearImprinted$ True";
|
||||||
|
|
||||||
|
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
|
||||||
|
sa.setIntrinsic(intrinsic);
|
||||||
|
inst.addSpellAbility(sa);
|
||||||
|
|
||||||
|
AbilitySub copySA = (AbilitySub) AbilityFactory.getAbility(copyStr, card);
|
||||||
|
sa.setAdditionalAbility("RepeatSubAbility", copySA);
|
||||||
|
|
||||||
|
AbilitySub pumpSA = (AbilitySub) AbilityFactory.getAbility(pumpStr, card);
|
||||||
|
copySA.setSubAbility(pumpSA);
|
||||||
|
|
||||||
|
AbilitySub pumpcleanSA = (AbilitySub) AbilityFactory.getAbility(pumpcleanStr, card);
|
||||||
|
pumpSA.setSubAbility(pumpcleanSA);
|
||||||
|
|
||||||
|
AbilitySub delTrigSA = (AbilitySub) AbilityFactory.getAbility(delTrigStr, card);
|
||||||
|
sa.setSubAbility(delTrigSA);
|
||||||
|
|
||||||
|
AbilitySub sacSA = (AbilitySub) AbilityFactory.getAbility(sacStr, card);
|
||||||
|
delTrigSA.setAdditionalAbility("Execute", sacSA);
|
||||||
|
|
||||||
|
AbilitySub cleanupSA = (AbilitySub) AbilityFactory.getAbility(cleanupStr, card);
|
||||||
|
delTrigSA.setSubAbility(cleanupSA);
|
||||||
|
|
||||||
} else if (keyword.startsWith("Spectacle")) {
|
} else if (keyword.startsWith("Spectacle")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final Cost cost = new Cost(k[1], false);
|
final Cost cost = new Cost(k[1], false);
|
||||||
@@ -4404,6 +4453,8 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(),"has suspended", c.getName(), "with", String.valueOf(counters),"time counters on it.");
|
String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(),"has suspended", c.getName(), "with", String.valueOf(counters),"time counters on it.");
|
||||||
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
|
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
|
||||||
|
//reveal suspended card
|
||||||
|
game.getAction().reveal(new CardCollection(c), c.getOwner(), true, c.getName() + " is suspended with " + counters + " time counters in ");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final StringBuilder sbDesc = new StringBuilder();
|
final StringBuilder sbDesc = new StringBuilder();
|
||||||
@@ -4686,7 +4737,7 @@ public class CardFactoryUtil {
|
|||||||
altCostSA.setRestrictions(restriction);
|
altCostSA.setRestrictions(restriction);
|
||||||
|
|
||||||
String costDescription = TextUtil.fastReplace(params.get("Description"),"CARDNAME", card.getName());
|
String costDescription = TextUtil.fastReplace(params.get("Description"),"CARDNAME", card.getName());
|
||||||
if (costDescription.isEmpty()) {
|
if (costDescription == null || costDescription.isEmpty()) {
|
||||||
costDescription = TextUtil.concatWithSpace("You may", abCost.toStringAlt(), "rather than pay", TextUtil.addSuffix(card.getName(), "'s mana cost."));
|
costDescription = TextUtil.concatWithSpace("You may", abCost.toStringAlt(), "rather than pay", TextUtil.addSuffix(card.getName(), "'s mana cost."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4758,6 +4809,16 @@ public class CardFactoryUtil {
|
|||||||
String opName = Expressions.operatorName(part.substring(kwLength, kwLength + 2));
|
String opName = Expressions.operatorName(part.substring(kwLength, kwLength + 2));
|
||||||
String operand = part.substring(kwLength + 2);
|
String operand = part.substring(kwLength + 2);
|
||||||
postponedAdjectives.add(Pair.of(true, "power" + opName + operand));
|
postponedAdjectives.add(Pair.of(true, "power" + opName + operand));
|
||||||
|
} else if (part.startsWith("toughness")) {
|
||||||
|
int kwLength = 9;
|
||||||
|
String operand = part.substring(kwLength + 2);
|
||||||
|
String opName = "";
|
||||||
|
if (part.startsWith("toughnessGE")) {
|
||||||
|
opName = " or greater";
|
||||||
|
} else {
|
||||||
|
opName = "update CardFactoryUtil line 4773";
|
||||||
|
}
|
||||||
|
postponedAdjectives.add(Pair.of(true, "toughness " + operand + opName));
|
||||||
} else if (CardType.isACreatureType(part)) {
|
} else if (CardType.isACreatureType(part)) {
|
||||||
if (creatures != null && CardType.isACreatureType(creatures)) { // e.g. Kor Castigator
|
if (creatures != null && CardType.isACreatureType(creatures)) { // e.g. Kor Castigator
|
||||||
creatures = StringUtils.capitalize(Lang.getPlural(part)) + creatures;
|
creatures = StringUtils.capitalize(Lang.getPlural(part)) + creatures;
|
||||||
|
|||||||
@@ -253,6 +253,13 @@ public class CardView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
return counters.equals(otherCard.getCounters());
|
return counters.equals(otherCard.getCounters());
|
||||||
}
|
}
|
||||||
|
public boolean hasSamePT(CardView otherCard) {
|
||||||
|
if (getCurrentState().getPower() != otherCard.getCurrentState().getPower())
|
||||||
|
return false;
|
||||||
|
if (getCurrentState().getToughness() != otherCard.getCurrentState().getToughness())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
void updateCounters(Card c) {
|
void updateCounters(Card c) {
|
||||||
set(TrackableProperty.Counters, c.getCounters());
|
set(TrackableProperty.Counters, c.getCounters());
|
||||||
updateLethalDamage(c);
|
updateLethalDamage(c);
|
||||||
@@ -691,6 +698,20 @@ public class CardView extends GameEntityView {
|
|||||||
return get(TrackableProperty.AlternateState);
|
return get(TrackableProperty.AlternateState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasLeftSplitState() {
|
||||||
|
return getLeftSplitState() != null;
|
||||||
|
}
|
||||||
|
public CardStateView getLeftSplitState() {
|
||||||
|
return get(TrackableProperty.LeftSplitState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRightSplitState() {
|
||||||
|
return getRightSplitState() != null;
|
||||||
|
}
|
||||||
|
public CardStateView getRightSplitState() {
|
||||||
|
return get(TrackableProperty.RightSplitState);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasBackSide() {
|
public boolean hasBackSide() {
|
||||||
return get(TrackableProperty.HasBackSide);
|
return get(TrackableProperty.HasBackSide);
|
||||||
}
|
}
|
||||||
@@ -731,9 +752,8 @@ public class CardView extends GameEntityView {
|
|||||||
|
|
||||||
CardState currentState = c.getCurrentState();
|
CardState currentState = c.getCurrentState();
|
||||||
if (isSplitCard) {
|
if (isSplitCard) {
|
||||||
if (c.getCurrentStateName() != CardStateName.LeftSplit && c.getCurrentStateName() != CardStateName.RightSplit && c.getCurrentStateName() != CardStateName.FaceDown) {
|
set(TrackableProperty.LeftSplitState, c.getState(CardStateName.LeftSplit).getView());
|
||||||
currentState = c.getState(CardStateName.LeftSplit);
|
set(TrackableProperty.RightSplitState, c.getState(CardStateName.RightSplit).getView());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CardStateView currentStateView = currentState.getView();
|
CardStateView currentStateView = currentState.getView();
|
||||||
@@ -901,6 +921,12 @@ public class CardView extends GameEntityView {
|
|||||||
public ColorSet getOriginalColors() {
|
public ColorSet getOriginalColors() {
|
||||||
return get(TrackableProperty.OriginalColors);
|
return get(TrackableProperty.OriginalColors);
|
||||||
}
|
}
|
||||||
|
public ColorSet getLeftSplitColors() {
|
||||||
|
return get(TrackableProperty.LeftSplitColors);
|
||||||
|
}
|
||||||
|
public ColorSet getRightSplitColors() {
|
||||||
|
return get(TrackableProperty.RightSplitColors);
|
||||||
|
}
|
||||||
void updateColors(Card c) {
|
void updateColors(Card c) {
|
||||||
set(TrackableProperty.Colors, c.determineColor());
|
set(TrackableProperty.Colors, c.determineColor());
|
||||||
}
|
}
|
||||||
@@ -909,6 +935,10 @@ public class CardView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
void setOriginalColors(Card c) {
|
void setOriginalColors(Card c) {
|
||||||
set(TrackableProperty.OriginalColors, c.determineColor());
|
set(TrackableProperty.OriginalColors, c.determineColor());
|
||||||
|
if (c.isSplitCard()) {
|
||||||
|
set(TrackableProperty.LeftSplitColors, c.determineColor(c.getState(CardStateName.LeftSplit)));
|
||||||
|
set(TrackableProperty.RightSplitColors, c.determineColor(c.getState(CardStateName.RightSplit)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void updateHasChangeColors(boolean hasChangeColor) {
|
void updateHasChangeColors(boolean hasChangeColor) {
|
||||||
set(TrackableProperty.HasChangedColors, hasChangeColor);
|
set(TrackableProperty.HasChangedColors, hasChangeColor);
|
||||||
@@ -1082,6 +1112,7 @@ public class CardView extends GameEntityView {
|
|||||||
public String getProtectionKey() { return get(TrackableProperty.ProtectionKey); }
|
public String getProtectionKey() { return get(TrackableProperty.ProtectionKey); }
|
||||||
public String getHexproofKey() { return get(TrackableProperty.HexproofKey); }
|
public String getHexproofKey() { return get(TrackableProperty.HexproofKey); }
|
||||||
public boolean hasDeathtouch() { return get(TrackableProperty.HasDeathtouch); }
|
public boolean hasDeathtouch() { return get(TrackableProperty.HasDeathtouch); }
|
||||||
|
public boolean hasDevoid() { return get(TrackableProperty.HasDevoid); }
|
||||||
public boolean hasDefender() { return get(TrackableProperty.HasDefender); }
|
public boolean hasDefender() { return get(TrackableProperty.HasDefender); }
|
||||||
public boolean hasDoubleStrike() { return get(TrackableProperty.HasDoubleStrike); }
|
public boolean hasDoubleStrike() { return get(TrackableProperty.HasDoubleStrike); }
|
||||||
public boolean hasFirstStrike() { return get(TrackableProperty.HasFirstStrike); }
|
public boolean hasFirstStrike() { return get(TrackableProperty.HasFirstStrike); }
|
||||||
@@ -1118,6 +1149,7 @@ public class CardView extends GameEntityView {
|
|||||||
void updateKeywords(Card c, CardState state) {
|
void updateKeywords(Card c, CardState state) {
|
||||||
c.updateKeywordsCache(state);
|
c.updateKeywordsCache(state);
|
||||||
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
||||||
|
set(TrackableProperty.HasDevoid, c.hasKeyword(Keyword.DEVOID, state));
|
||||||
set(TrackableProperty.HasDefender, c.hasKeyword(Keyword.DEFENDER, state));
|
set(TrackableProperty.HasDefender, c.hasKeyword(Keyword.DEFENDER, state));
|
||||||
set(TrackableProperty.HasDoubleStrike, c.hasKeyword(Keyword.DOUBLE_STRIKE, state));
|
set(TrackableProperty.HasDoubleStrike, c.hasKeyword(Keyword.DOUBLE_STRIKE, state));
|
||||||
set(TrackableProperty.HasFirstStrike, c.hasKeyword(Keyword.FIRST_STRIKE, state));
|
set(TrackableProperty.HasFirstStrike, c.hasKeyword(Keyword.FIRST_STRIKE, state));
|
||||||
|
|||||||
@@ -337,15 +337,12 @@ public class CostAdjustment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int value = 0;
|
int value = Integer.parseInt(amount);
|
||||||
if (StringUtils.isNumeric(amount)) {
|
|
||||||
value = Integer.parseInt(amount);
|
if (staticAbility.hasParam("RaiseTo")) {
|
||||||
} else {
|
int cmc = manaCost.getConvertedManaCost();
|
||||||
if ("Min3".equals(amount)) {
|
if (cmc < value) {
|
||||||
int cmc = manaCost.getConvertedManaCost();
|
value = Integer.parseInt(amount) - cmc;
|
||||||
if (cmc < 3) {
|
|
||||||
value = 3 - cmc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public enum Keyword {
|
|||||||
EMBALM("Embalm", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Create a token that's a copy of this card, except it's white, it has no mana cost, and it's a Zombie in addition to its other types. Embalm only as a sorcery."),
|
EMBALM("Embalm", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Create a token that's a copy of this card, except it's white, it has no mana cost, and it's a Zombie in addition to its other types. Embalm only as a sorcery."),
|
||||||
EMERGE("Emerge", KeywordWithCost.class, false, "You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost."),
|
EMERGE("Emerge", KeywordWithCost.class, false, "You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost."),
|
||||||
ENCHANT("Enchant", KeywordWithType.class, false, "Target a %s as you cast this. This card enters the battlefield attached to that %s."),
|
ENCHANT("Enchant", KeywordWithType.class, false, "Target a %s as you cast this. This card enters the battlefield attached to that %s."),
|
||||||
|
ENCORE("Encore", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: For each opponent, create a token copy that attacks that opponent this turn if able. They gain haste. Sacrifice them at the beginning of the next end step. Activate only as a sorcery."),
|
||||||
ENTWINE("Entwine", KeywordWithCost.class, true, "You may choose all modes of this spell instead of just one. If you do, you pay an additional %s."),
|
ENTWINE("Entwine", KeywordWithCost.class, true, "You may choose all modes of this spell instead of just one. If you do, you pay an additional %s."),
|
||||||
EPIC("Epic", SimpleKeyword.class, true, "For the rest of the game, you can't cast spells. At the beginning of each of your upkeeps for the rest of the game, copy this spell except for its epic ability. If the spell has any targets, you may choose new targets for the copy."),
|
EPIC("Epic", SimpleKeyword.class, true, "For the rest of the game, you can't cast spells. At the beginning of each of your upkeeps for the rest of the game, copy this spell except for its epic ability. If the spell has any targets, you may choose new targets for the copy."),
|
||||||
EQUIP("Equip", Equip.class, false, "%s: Attach to target %s you control. Equip only as a sorcery."),
|
EQUIP("Equip", Equip.class, false, "%s: Attach to target %s you control. Equip only as a sorcery."),
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
if (infect) {
|
if (infect) {
|
||||||
addPoisonCounters(amount, source, counterTable);
|
addPoisonCounters(amount, source, counterTable);
|
||||||
}
|
}
|
||||||
else {
|
else if (!hasKeyword("Damage doesn't cause you to lose life.")) {
|
||||||
// Worship does not reduce the damage dealt but changes the effect
|
// Worship does not reduce the damage dealt but changes the effect
|
||||||
// of the damage
|
// of the damage
|
||||||
if (hasKeyword("DamageLifeThreshold:7") && life - 7 <= amount) {
|
if (hasKeyword("DamageLifeThreshold:7") && life - 7 <= amount) {
|
||||||
@@ -741,6 +741,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
restDamage = 0;
|
restDamage = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (c.getName().equals("Obosh, the Preypiercer")) {
|
||||||
|
if (c.getController().equals(source.getController()) && source.getCMC() % 2 != 0) {
|
||||||
|
restDamage *= 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -439,6 +439,12 @@ public class PlayerView extends GameEntityView {
|
|||||||
return types.size();
|
return types.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasDelirium() {
|
||||||
|
if (get(TrackableProperty.HasDelirium) == null)
|
||||||
|
return false;
|
||||||
|
return get(TrackableProperty.HasDelirium);
|
||||||
|
}
|
||||||
|
|
||||||
private static TrackableProperty getZoneProp(final ZoneType zone) {
|
private static TrackableProperty getZoneProp(final ZoneType zone) {
|
||||||
switch (zone) {
|
switch (zone) {
|
||||||
case Ante:
|
case Ante:
|
||||||
@@ -466,6 +472,10 @@ public class PlayerView extends GameEntityView {
|
|||||||
if (prop == null) { return; }
|
if (prop == null) { return; }
|
||||||
set(prop, CardView.getCollection(zone.getCards(false)));
|
set(prop, CardView.getCollection(zone.getCards(false)));
|
||||||
|
|
||||||
|
//update delirium
|
||||||
|
if (ZoneType.Graveyard == zone.getZoneType())
|
||||||
|
set(TrackableProperty.HasDelirium, getZoneTypes(TrackableProperty.Graveyard) >= 4);
|
||||||
|
|
||||||
//update flashback zone when graveyard, library, or exile zones updated
|
//update flashback zone when graveyard, library, or exile zones updated
|
||||||
switch (zone.getZoneType()) {
|
switch (zone.getZoneType()) {
|
||||||
case Command:
|
case Command:
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public class StackItemView extends TrackableObject implements IHasCardView {
|
|||||||
updateAbility(si);
|
updateAbility(si);
|
||||||
updateOptionalTrigger(si);
|
updateOptionalTrigger(si);
|
||||||
updateSubInstance(si);
|
updateSubInstance(si);
|
||||||
|
updateOptionalCost(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
@@ -47,6 +48,55 @@ public class StackItemView extends TrackableObject implements IHasCardView {
|
|||||||
set(TrackableProperty.Key, si.getSpellAbility(false).yieldKey());
|
set(TrackableProperty.Key, si.getSpellAbility(false).yieldKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOptionalCostString() {
|
||||||
|
return get(TrackableProperty.OptionalCosts);
|
||||||
|
}
|
||||||
|
void updateOptionalCost(SpellAbilityStackInstance si) {
|
||||||
|
String OptionalCostString = "";
|
||||||
|
boolean kicked = false;
|
||||||
|
boolean entwined = false;
|
||||||
|
boolean buyback = false;
|
||||||
|
boolean retraced = false;
|
||||||
|
boolean jumpstart = false;
|
||||||
|
boolean additional = false;
|
||||||
|
boolean alternate = false;
|
||||||
|
boolean generic = false;
|
||||||
|
|
||||||
|
for (OptionalCost cost : si.getSpellAbility(false).getOptionalCosts()) {
|
||||||
|
if (cost == OptionalCost.Kicker1 || cost == OptionalCost.Kicker2)
|
||||||
|
kicked = true;
|
||||||
|
if (cost == OptionalCost.Entwine)
|
||||||
|
entwined = true;
|
||||||
|
if (cost == OptionalCost.Buyback)
|
||||||
|
buyback = true;
|
||||||
|
if (cost == OptionalCost.Retrace)
|
||||||
|
retraced = true;
|
||||||
|
if (cost == OptionalCost.Jumpstart)
|
||||||
|
jumpstart = true;
|
||||||
|
if (cost == OptionalCost.Flash)
|
||||||
|
additional = true;
|
||||||
|
if (cost == OptionalCost.Generic)
|
||||||
|
generic = true;
|
||||||
|
if (cost == OptionalCost.AltCost)
|
||||||
|
alternate = true;
|
||||||
|
}
|
||||||
|
if (!alternate) {
|
||||||
|
if (kicked && !generic)
|
||||||
|
OptionalCostString += "Kicked";
|
||||||
|
if (entwined)
|
||||||
|
OptionalCostString += OptionalCostString.equals("") ? "Entwined" : ", Entwined";
|
||||||
|
if (buyback)
|
||||||
|
OptionalCostString += OptionalCostString.equals("") ? "Buyback" : ", Buyback";
|
||||||
|
if (retraced)
|
||||||
|
OptionalCostString += OptionalCostString.equals("") ? "Retraced" : ", Retraced";
|
||||||
|
if (jumpstart)
|
||||||
|
OptionalCostString += OptionalCostString.equals("") ? "Jumpstart" : ", Jumpstart";
|
||||||
|
if (additional || generic)
|
||||||
|
OptionalCostString += OptionalCostString.equals("") ? "Additional" : ", Additional";
|
||||||
|
}
|
||||||
|
set(TrackableProperty.OptionalCosts, OptionalCostString);
|
||||||
|
}
|
||||||
|
|
||||||
public int getSourceTrigger() {
|
public int getSourceTrigger() {
|
||||||
return get(TrackableProperty.SourceTrigger);
|
return get(TrackableProperty.SourceTrigger);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -631,6 +631,13 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasParam("UnlessDefinedPlayer")) {
|
||||||
|
List<Player> players = AbilityUtils.getDefinedPlayers(hostCard, getParam("UnlessDefinedPlayer"), null);
|
||||||
|
if (!players.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasParam("TopCardOfLibraryIs")) {
|
if (hasParam("TopCardOfLibraryIs")) {
|
||||||
if (controller.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (controller.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -29,24 +29,25 @@ public class TriggerDamageAll extends Trigger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
|
final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
|
||||||
return filterTable(table) > 0;
|
return !table.filteredMap(getParam("ValidSource"), getParam("ValidTarget"), getHostCard(), null).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||||
final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
|
CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
|
||||||
|
table = table.filteredMap(getParam("ValidSource"), getParam("ValidTarget"), getHostCard(), null);
|
||||||
|
|
||||||
sa.setTriggeringObject(AbilityKey.DamageAmount, filterTable(table));
|
sa.setTriggeringObject(AbilityKey.DamageAmount, table.totalAmount());
|
||||||
|
sa.setTriggeringObject(AbilityKey.Sources, table.rowKeySet());
|
||||||
|
sa.setTriggeringObject(AbilityKey.Targets, table.columnKeySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getImportantStackObjects(SpellAbility sa) {
|
public String getImportantStackObjects(SpellAbility sa) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(Localizer.getInstance().getMessage("lblDamageSource")).append(": ").append(sa.getTriggeringObject(AbilityKey.Sources)).append(", ");
|
||||||
|
sb.append(Localizer.getInstance().getMessage("lblDamaged")).append(": ").append(sa.getTriggeringObject(AbilityKey.Targets)).append(", ");
|
||||||
sb.append(Localizer.getInstance().getMessage("lblAmount")).append(": ").append(sa.getTriggeringObject(AbilityKey.DamageAmount));
|
sb.append(Localizer.getInstance().getMessage("lblAmount")).append(": ").append(sa.getTriggeringObject(AbilityKey.DamageAmount));
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int filterTable(CardDamageMap table) {
|
|
||||||
return table.filteredAmount(getParam("ValidSource"), getParam("ValidTarget"), getHostCard(), null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -573,6 +573,11 @@ public class TriggerHandler {
|
|||||||
host.addRemembered(sa.getActivatingPlayer());
|
host.addRemembered(sa.getActivatingPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (regtrig.hasParam("RememberTriggeringCard")) {
|
||||||
|
Card triggeredCard = ((Card) sa.getTriggeringObject(AbilityKey.Card));
|
||||||
|
host.addRemembered(triggeredCard);
|
||||||
|
}
|
||||||
|
|
||||||
sa.setStackDescription(sa.toString());
|
sa.setStackDescription(sa.toString());
|
||||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||||
// need to be set for demonic pact to look for chosen modes
|
// need to be set for demonic pact to look for chosen modes
|
||||||
|
|||||||
@@ -101,10 +101,10 @@ public class TriggerTapsForMana extends Trigger {
|
|||||||
if (!this.getHostCard().hasChosenColor() || !produced.contains(MagicColor.toShortString(this.getHostCard().getChosenColor()))) {
|
if (!this.getHostCard().hasChosenColor() || !produced.contains(MagicColor.toShortString(this.getHostCard().getChosenColor()))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!produced.contains(MagicColor.toShortString(this.getParam("Produced")))) {
|
}
|
||||||
|
if (!produced.contains(MagicColor.toShortString(this.getParam("Produced")))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ public enum TrackableProperty {
|
|||||||
PairedWith(TrackableTypes.CardViewType),
|
PairedWith(TrackableTypes.CardViewType),
|
||||||
CurrentState(TrackableTypes.CardStateViewType, FreezeMode.IgnoresFreeze),
|
CurrentState(TrackableTypes.CardStateViewType, FreezeMode.IgnoresFreeze),
|
||||||
AlternateState(TrackableTypes.CardStateViewType, FreezeMode.IgnoresFreeze),
|
AlternateState(TrackableTypes.CardStateViewType, FreezeMode.IgnoresFreeze),
|
||||||
|
LeftSplitState(TrackableTypes.CardStateViewType, FreezeMode.IgnoresFreeze),
|
||||||
|
RightSplitState(TrackableTypes.CardStateViewType, FreezeMode.IgnoresFreeze),
|
||||||
HiddenId(TrackableTypes.IntegerType),
|
HiddenId(TrackableTypes.IntegerType),
|
||||||
ExertedThisTurn(TrackableTypes.BooleanType),
|
ExertedThisTurn(TrackableTypes.BooleanType),
|
||||||
|
|
||||||
@@ -76,6 +78,8 @@ public enum TrackableProperty {
|
|||||||
Name(TrackableTypes.StringType),
|
Name(TrackableTypes.StringType),
|
||||||
Colors(TrackableTypes.ColorSetType),
|
Colors(TrackableTypes.ColorSetType),
|
||||||
OriginalColors(TrackableTypes.ColorSetType),
|
OriginalColors(TrackableTypes.ColorSetType),
|
||||||
|
LeftSplitColors(TrackableTypes.ColorSetType),
|
||||||
|
RightSplitColors(TrackableTypes.ColorSetType),
|
||||||
ImageKey(TrackableTypes.StringType),
|
ImageKey(TrackableTypes.StringType),
|
||||||
Type(TrackableTypes.CardTypeViewType),
|
Type(TrackableTypes.CardTypeViewType),
|
||||||
ManaCost(TrackableTypes.ManaCostType),
|
ManaCost(TrackableTypes.ManaCostType),
|
||||||
@@ -92,6 +96,7 @@ public enum TrackableProperty {
|
|||||||
|
|
||||||
KeywordKey(TrackableTypes.StringType),
|
KeywordKey(TrackableTypes.StringType),
|
||||||
HasDeathtouch(TrackableTypes.BooleanType),
|
HasDeathtouch(TrackableTypes.BooleanType),
|
||||||
|
HasDevoid(TrackableTypes.BooleanType),
|
||||||
HasDefender(TrackableTypes.BooleanType),
|
HasDefender(TrackableTypes.BooleanType),
|
||||||
HasDoubleStrike(TrackableTypes.BooleanType),
|
HasDoubleStrike(TrackableTypes.BooleanType),
|
||||||
HasFirstStrike(TrackableTypes.BooleanType),
|
HasFirstStrike(TrackableTypes.BooleanType),
|
||||||
@@ -160,6 +165,7 @@ public enum TrackableProperty {
|
|||||||
IsExtraTurn(TrackableTypes.BooleanType),
|
IsExtraTurn(TrackableTypes.BooleanType),
|
||||||
ExtraTurnCount(TrackableTypes.IntegerType),
|
ExtraTurnCount(TrackableTypes.IntegerType),
|
||||||
HasPriority(TrackableTypes.BooleanType),
|
HasPriority(TrackableTypes.BooleanType),
|
||||||
|
HasDelirium(TrackableTypes.BooleanType),
|
||||||
|
|
||||||
//SpellAbility
|
//SpellAbility
|
||||||
HostCard(TrackableTypes.CardViewType),
|
HostCard(TrackableTypes.CardViewType),
|
||||||
@@ -181,6 +187,7 @@ public enum TrackableProperty {
|
|||||||
SubInstance(TrackableTypes.StackItemViewType),
|
SubInstance(TrackableTypes.StackItemViewType),
|
||||||
Ability(TrackableTypes.BooleanType),
|
Ability(TrackableTypes.BooleanType),
|
||||||
OptionalTrigger(TrackableTypes.BooleanType),
|
OptionalTrigger(TrackableTypes.BooleanType),
|
||||||
|
OptionalCosts(TrackableTypes.StringType),
|
||||||
|
|
||||||
//Combat
|
//Combat
|
||||||
AttackersWithDefenders(TrackableTypes.GenericMapType, FreezeMode.IgnoresFreeze),
|
AttackersWithDefenders(TrackableTypes.GenericMapType, FreezeMode.IgnoresFreeze),
|
||||||
@@ -199,6 +206,7 @@ public enum TrackableProperty {
|
|||||||
WinningPlayerName(TrackableTypes.StringType),
|
WinningPlayerName(TrackableTypes.StringType),
|
||||||
WinningTeam(TrackableTypes.IntegerType),
|
WinningTeam(TrackableTypes.IntegerType),
|
||||||
MatchOver(TrackableTypes.BooleanType),
|
MatchOver(TrackableTypes.BooleanType),
|
||||||
|
Mulligan(TrackableTypes.BooleanType),
|
||||||
NumGamesInMatch(TrackableTypes.IntegerType),
|
NumGamesInMatch(TrackableTypes.IntegerType),
|
||||||
NumPlayedGamesInMatch(TrackableTypes.IntegerType),
|
NumPlayedGamesInMatch(TrackableTypes.IntegerType),
|
||||||
Stack(TrackableTypes.StackItemViewListType),
|
Stack(TrackableTypes.StackItemViewListType),
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx-backend-android</artifactId>
|
<artifactId>gdx-backend-android</artifactId>
|
||||||
<version>1.9.10</version>
|
<version>1.9.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ public class CardDetailPanel extends SkinnedPanel {
|
|||||||
} else {
|
} else {
|
||||||
final String manaCost;
|
final String manaCost;
|
||||||
if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack) { //only display current state's mana cost when on stack
|
if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack) { //only display current state's mana cost when on stack
|
||||||
manaCost = card.getCurrentState().getManaCost() + " // " + card.getAlternateState().getManaCost();
|
manaCost = card.getLeftSplitState().getManaCost() + " // " + card.getAlternateState().getManaCost();
|
||||||
} else {
|
} else {
|
||||||
manaCost = state.getManaCost().toString();
|
manaCost = state.getManaCost().toString();
|
||||||
}
|
}
|
||||||
@@ -256,7 +256,7 @@ public class CardDetailPanel extends SkinnedPanel {
|
|||||||
idLabel.setText(mayView ? CardDetailUtil.formatCardId(state) : "");
|
idLabel.setText(mayView ? CardDetailUtil.formatCardId(state) : "");
|
||||||
|
|
||||||
// fill the card text
|
// fill the card text
|
||||||
cdArea.setText(FSkin.encodeSymbols(CardDetailUtil.composeCardText(state, gameView, mayView), true));
|
cdArea.setText(FSkin.encodeSymbols(CardDetailUtil.composeCardText( card.isSplitCard() && !isInAltState ? card.getLeftSplitState() : state, gameView, mayView), true));
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import net.miginfocom.swing.MigLayout;
|
|||||||
import forge.assets.FSkinProp;
|
import forge.assets.FSkinProp;
|
||||||
import forge.game.GameView;
|
import forge.game.GameView;
|
||||||
import forge.gauntlet.GauntletWinLoseController;
|
import forge.gauntlet.GauntletWinLoseController;
|
||||||
|
import forge.util.Localizer;
|
||||||
import forge.toolbox.FLabel;
|
import forge.toolbox.FLabel;
|
||||||
import forge.toolbox.FSkin;
|
import forge.toolbox.FSkin;
|
||||||
import forge.toolbox.FSkin.SkinnedPanel;
|
import forge.toolbox.FSkin.SkinnedPanel;
|
||||||
@@ -50,7 +51,7 @@ public class GauntletWinLose extends ControlWinLose {
|
|||||||
controller = new GauntletWinLoseController(view0, game0) {
|
controller = new GauntletWinLoseController(view0, game0) {
|
||||||
@Override
|
@Override
|
||||||
protected void showOutcome(boolean isMatchOver, String message1, String message2, FSkinProp icon, List<String> lstEventNames, List<String> lstEventRecords, int len, int num) {
|
protected void showOutcome(boolean isMatchOver, String message1, String message2, FSkinProp icon, List<String> lstEventNames, List<String> lstEventRecords, int len, int num) {
|
||||||
final JLabel lblTitle = new FLabel.Builder().text("Gauntlet Progress")
|
final JLabel lblTitle = new FLabel.Builder().text(Localizer.getInstance().getMessage("lblGauntletProgress"))
|
||||||
.fontAlign(SwingConstants.CENTER).fontSize(18).build();
|
.fontAlign(SwingConstants.CENTER).fontSize(18).build();
|
||||||
|
|
||||||
final JPanel pnlResults = new JPanel();
|
final JPanel pnlResults = new JPanel();
|
||||||
|
|||||||
@@ -1,34 +1,33 @@
|
|||||||
package forge.view;
|
package forge.view;
|
||||||
|
|
||||||
|
import forge.LobbyPlayer;
|
||||||
|
import forge.deck.Deck;
|
||||||
|
import forge.deck.DeckGroup;
|
||||||
|
import forge.deck.io.DeckSerializer;
|
||||||
|
import forge.game.*;
|
||||||
|
import forge.game.player.RegisteredPlayer;
|
||||||
|
import forge.model.FModel;
|
||||||
|
import forge.player.GamePlayerUtil;
|
||||||
|
import forge.properties.ForgeConstants;
|
||||||
|
import forge.tournament.system.*;
|
||||||
|
import forge.util.Lang;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
import forge.util.WordUtil;
|
||||||
|
import forge.util.storage.IStorage;
|
||||||
|
import org.apache.commons.lang3.time.StopWatch;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import forge.LobbyPlayer;
|
|
||||||
import forge.deck.DeckGroup;
|
|
||||||
import forge.game.*;
|
|
||||||
import forge.properties.ForgeConstants;
|
|
||||||
import forge.tournament.system.*;
|
|
||||||
import forge.util.TextUtil;
|
|
||||||
import forge.util.WordUtil;
|
|
||||||
import forge.util.storage.IStorage;
|
|
||||||
import org.apache.commons.lang3.time.StopWatch;
|
|
||||||
|
|
||||||
import forge.deck.Deck;
|
|
||||||
import forge.deck.io.DeckSerializer;
|
|
||||||
import forge.game.player.RegisteredPlayer;
|
|
||||||
import forge.model.FModel;
|
|
||||||
import forge.player.GamePlayerUtil;
|
|
||||||
import forge.util.Lang;
|
|
||||||
|
|
||||||
public class SimulateMatch {
|
public class SimulateMatch {
|
||||||
public static void simulate(String[] args) {
|
public static void simulate(String[] args) {
|
||||||
FModel.initialize(null, null);
|
FModel.initialize(null, null);
|
||||||
|
|
||||||
System.out.println("Simulation mode");
|
System.out.println("Simulation mode");
|
||||||
if(args.length < 4) {
|
if (args.length < 4) {
|
||||||
argumentHelp();
|
argumentHelp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -49,11 +48,9 @@ public class SimulateMatch {
|
|||||||
|
|
||||||
options = new ArrayList<>();
|
options = new ArrayList<>();
|
||||||
params.put(a.substring(1), options);
|
params.put(a.substring(1), options);
|
||||||
}
|
} else if (options != null) {
|
||||||
else if (options != null) {
|
|
||||||
options.add(a);
|
options.add(a);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
System.err.println("Illegal parameter usage");
|
System.err.println("Illegal parameter usage");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -97,7 +94,7 @@ public class SimulateMatch {
|
|||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
if (params.containsKey("d")) {
|
if (params.containsKey("d")) {
|
||||||
for(String deck : params.get("d")) {
|
for (String deck : params.get("d")) {
|
||||||
Deck d = deckFromCommandLineParameter(deck, type);
|
Deck d = deckFromCommandLineParameter(deck, type);
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
System.out.println(TextUtil.concatNoSpace("Could not load deck - ", deck, ", match cannot start"));
|
System.out.println(TextUtil.concatNoSpace("Could not load deck - ", deck, ", match cannot start"));
|
||||||
@@ -130,7 +127,7 @@ public class SimulateMatch {
|
|||||||
|
|
||||||
if (matchSize != 0) {
|
if (matchSize != 0) {
|
||||||
int iGame = 0;
|
int iGame = 0;
|
||||||
while(!mc.isMatchOver()) {
|
while (!mc.isMatchOver()) {
|
||||||
// play games until the match ends
|
// play games until the match ends
|
||||||
simulateSingleMatch(mc, iGame, outputGamelog);
|
simulateSingleMatch(mc, iGame, outputGamelog);
|
||||||
iGame++;
|
iGame++;
|
||||||
@@ -159,38 +156,26 @@ public class SimulateMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void simulateSingleMatch(final Match mc, int iGame, boolean outputGamelog) {
|
public static void simulateSingleMatch(final Match mc, int iGame, boolean outputGamelog) {
|
||||||
final StopWatch sw = new StopWatch();
|
final StopWatch sw = new StopWatch();
|
||||||
sw.start();
|
sw.start();
|
||||||
|
|
||||||
final Game g1 = mc.createGame();
|
final Game g1 = mc.createGame();
|
||||||
// will run match in the same thread
|
// will run match in the same thread
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
try {
|
try {
|
||||||
TimeLimitedCodeBlock.runWithTimeout(new Runnable() {
|
TimeLimitedCodeBlock.runWithTimeout(() -> {
|
||||||
@Override
|
mc.startGame(g1);
|
||||||
public void run() {
|
sw.stop();
|
||||||
mc.startGame(g1);
|
|
||||||
sw.stop();
|
|
||||||
}
|
|
||||||
}, 120, TimeUnit.SECONDS);
|
}, 120, TimeUnit.SECONDS);
|
||||||
}
|
} catch (TimeoutException e) {
|
||||||
catch (TimeoutException e) {
|
|
||||||
System.out.println("Stopping slow match as draw");
|
System.out.println("Stopping slow match as draw");
|
||||||
g1.setGameOver(GameEndReason.Draw);
|
} catch (Exception | StackOverflowError e) {
|
||||||
sw.stop();
|
|
||||||
}catch (Exception e){
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
g1.setGameOver(GameEndReason.Draw);
|
} finally {
|
||||||
sw.stop();
|
|
||||||
}catch(StackOverflowError e){
|
|
||||||
g1.setGameOver(GameEndReason.Draw);
|
g1.setGameOver(GameEndReason.Draw);
|
||||||
sw.stop();
|
sw.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<GameLogEntry> log;
|
List<GameLogEntry> log;
|
||||||
if (outputGamelog) {
|
if (outputGamelog) {
|
||||||
log = g1.getGameLog().getLogEntries(null);
|
log = g1.getGameLog().getLogEntries(null);
|
||||||
@@ -198,15 +183,15 @@ public class SimulateMatch {
|
|||||||
log = g1.getGameLog().getLogEntries(GameLogEntryType.MATCH_RESULTS);
|
log = g1.getGameLog().getLogEntries(GameLogEntryType.MATCH_RESULTS);
|
||||||
}
|
}
|
||||||
Collections.reverse(log);
|
Collections.reverse(log);
|
||||||
for(GameLogEntry l : log) {
|
for (GameLogEntry l : log) {
|
||||||
System.out.println(l);
|
System.out.println(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If both players life totals to 0 in a single turn, the game should end in a draw
|
// If both players life totals to 0 in a single turn, the game should end in a draw
|
||||||
if(g1.getOutcome().isDraw()){
|
if (g1.getOutcome().isDraw()) {
|
||||||
System.out.println(String.format("Game %d ended in a Draw! Took %d ms.", 1+iGame, sw.getTime()));
|
System.out.printf("\nGame Result: Game %d ended in a Draw! Took %d ms.%n", 1 + iGame, sw.getTime());
|
||||||
} else {
|
} else {
|
||||||
System.out.println(String.format("\nGame %d ended in %d ms. %s has won!\n", 1+iGame, sw.getTime(), g1.getOutcome().getWinningLobbyPlayer().getName()));
|
System.out.printf("\nGame Result: Game %d ended in %d ms. %s has won!\n%n", 1 + iGame, sw.getTime(), g1.getOutcome().getWinningLobbyPlayer().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +204,7 @@ public class SimulateMatch {
|
|||||||
List<TournamentPlayer> players = new ArrayList<>();
|
List<TournamentPlayer> players = new ArrayList<>();
|
||||||
int numPlayers = 0;
|
int numPlayers = 0;
|
||||||
if (params.containsKey("d")) {
|
if (params.containsKey("d")) {
|
||||||
for(String deck : params.get("d")) {
|
for (String deck : params.get("d")) {
|
||||||
Deck d = deckFromCommandLineParameter(deck, rules.getGameType());
|
Deck d = deckFromCommandLineParameter(deck, rules.getGameType());
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
System.out.println(TextUtil.concatNoSpace("Could not load deck - ", deck, ", match cannot start"));
|
System.out.println(TextUtil.concatNoSpace("Could not load deck - ", deck, ", match cannot start"));
|
||||||
@@ -239,7 +224,7 @@ public class SimulateMatch {
|
|||||||
if (!folder.isDirectory()) {
|
if (!folder.isDirectory()) {
|
||||||
System.out.println("Directory not found - " + foldName);
|
System.out.println("Directory not found - " + foldName);
|
||||||
} else {
|
} else {
|
||||||
for(File deck : folder.listFiles(new FilenameFilter() {
|
for (File deck : folder.listFiles(new FilenameFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File dir, String name) {
|
public boolean accept(File dir, String name) {
|
||||||
return name.endsWith(".dck");
|
return name.endsWith(".dck");
|
||||||
@@ -281,16 +266,16 @@ public class SimulateMatch {
|
|||||||
System.out.println(TextUtil.concatNoSpace("Starting a ", tournament, " tournament with ",
|
System.out.println(TextUtil.concatNoSpace("Starting a ", tournament, " tournament with ",
|
||||||
String.valueOf(numPlayers), " players over ",
|
String.valueOf(numPlayers), " players over ",
|
||||||
String.valueOf(tourney.getTotalRounds()), " rounds"));
|
String.valueOf(tourney.getTotalRounds()), " rounds"));
|
||||||
while(!tourney.isTournamentOver()) {
|
while (!tourney.isTournamentOver()) {
|
||||||
if (tourney.getActiveRound() != curRound) {
|
if (tourney.getActiveRound() != curRound) {
|
||||||
if (curRound != 0) {
|
if (curRound != 0) {
|
||||||
System.out.println(TextUtil.concatNoSpace("End Round - ", String.valueOf(curRound)));
|
System.out.println(TextUtil.concatNoSpace("End Round - ", String.valueOf(curRound)));
|
||||||
}
|
}
|
||||||
curRound = tourney.getActiveRound();
|
curRound = tourney.getActiveRound();
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println(TextUtil.concatNoSpace("Round ", String.valueOf(curRound) ," Pairings:"));
|
System.out.println(TextUtil.concatNoSpace("Round ", String.valueOf(curRound), " Pairings:"));
|
||||||
|
|
||||||
for(TournamentPairing pairing : tourney.getActivePairings()) {
|
for (TournamentPairing pairing : tourney.getActivePairings()) {
|
||||||
System.out.println(pairing.outputHeader());
|
System.out.println(pairing.outputHeader());
|
||||||
}
|
}
|
||||||
System.out.println();
|
System.out.println();
|
||||||
@@ -311,10 +296,10 @@ public class SimulateMatch {
|
|||||||
int iGame = 0;
|
int iGame = 0;
|
||||||
while (!mc.isMatchOver()) {
|
while (!mc.isMatchOver()) {
|
||||||
// play games until the match ends
|
// play games until the match ends
|
||||||
try{
|
try {
|
||||||
simulateSingleMatch(mc, iGame, outputGamelog);
|
simulateSingleMatch(mc, iGame, outputGamelog);
|
||||||
iGame++;
|
iGame++;
|
||||||
} catch(Exception e) {
|
} catch (Exception e) {
|
||||||
exceptions++;
|
exceptions++;
|
||||||
System.out.println(e.toString());
|
System.out.println(e.toString());
|
||||||
if (exceptions > 5) {
|
if (exceptions > 5) {
|
||||||
@@ -349,10 +334,10 @@ public class SimulateMatch {
|
|||||||
|
|
||||||
private static Deck deckFromCommandLineParameter(String deckname, GameType type) {
|
private static Deck deckFromCommandLineParameter(String deckname, GameType type) {
|
||||||
int dotpos = deckname.lastIndexOf('.');
|
int dotpos = deckname.lastIndexOf('.');
|
||||||
if(dotpos > 0 && dotpos == deckname.length()-4) {
|
if (dotpos > 0 && dotpos == deckname.length() - 4) {
|
||||||
String baseDir = type.equals(GameType.Commander) ?
|
String baseDir = type.equals(GameType.Commander) ?
|
||||||
ForgeConstants.DECK_COMMANDER_DIR : ForgeConstants.DECK_CONSTRUCTED_DIR;
|
ForgeConstants.DECK_COMMANDER_DIR : ForgeConstants.DECK_CONSTRUCTED_DIR;
|
||||||
return DeckSerializer.fromFile(new File(baseDir+deckname));
|
return DeckSerializer.fromFile(new File(baseDir + deckname));
|
||||||
}
|
}
|
||||||
|
|
||||||
IStorage<Deck> deckStore = null;
|
IStorage<Deck> deckStore = null;
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
|
|||||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
|
PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
|
||||||
int ofs = pc != null && Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH) ? -12 : 12;
|
int ofs = pc != null && Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH) ? -12 : 12;
|
||||||
|
|
||||||
drawManaCost(g, card.getCurrentState().getManaCost(), ofs);
|
drawManaCost(g, card.getLeftSplitState().getManaCost(), ofs);
|
||||||
drawManaCost(g, card.getAlternateState().getManaCost(), -ofs);
|
drawManaCost(g, card.getAlternateState().getManaCost(), -ofs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx-backend-robovm</artifactId>
|
<artifactId>gdx-backend-robovm</artifactId>
|
||||||
<version>1.9.10</version>
|
<version>1.9.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -60,18 +60,18 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx-backend-lwjgl</artifactId>
|
<artifactId>gdx-backend-lwjgl</artifactId>
|
||||||
<version>1.9.10</version>
|
<version>1.9.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx-platform</artifactId>
|
<artifactId>gdx-platform</artifactId>
|
||||||
<version>1.2.0</version>
|
<version>1.9.11</version>
|
||||||
<classifier>natives-desktop</classifier>
|
<classifier>natives-desktop</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx-freetype-platform</artifactId>
|
<artifactId>gdx-freetype-platform</artifactId>
|
||||||
<version>1.9.10</version>
|
<version>1.9.11</version>
|
||||||
<classifier>natives-desktop</classifier>
|
<classifier>natives-desktop</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
|
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
|
||||||
|
|||||||
@@ -63,12 +63,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx</artifactId>
|
<artifactId>gdx</artifactId>
|
||||||
<version>1.9.10</version>
|
<version>1.9.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.badlogicgames.gdx</groupId>
|
<groupId>com.badlogicgames.gdx</groupId>
|
||||||
<artifactId>gdx-freetype</artifactId>
|
<artifactId>gdx-freetype</artifactId>
|
||||||
<version>1.9.10</version>
|
<version>1.9.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public class Forge implements ApplicationListener {
|
|||||||
public static float heigtModifier = 0.0f;
|
public static float heigtModifier = 0.0f;
|
||||||
private static boolean isloadingaMatch = false;
|
private static boolean isloadingaMatch = false;
|
||||||
public static boolean showFPS = false;
|
public static boolean showFPS = false;
|
||||||
|
public static boolean altPlayerLayout = false;
|
||||||
public static boolean enableUIMask = false;
|
public static boolean enableUIMask = false;
|
||||||
public static boolean enablePreloadExtendedArt = false;
|
public static boolean enablePreloadExtendedArt = false;
|
||||||
public static String locale = "en-US";
|
public static String locale = "en-US";
|
||||||
@@ -123,6 +124,7 @@ public class Forge implements ApplicationListener {
|
|||||||
|
|
||||||
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
|
||||||
showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS);
|
showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS);
|
||||||
|
altPlayerLayout = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERINFOLAYOUT);
|
||||||
enableUIMask = prefs.getPrefBoolean(FPref.UI_ENABLE_BORDER_MASKING);
|
enableUIMask = prefs.getPrefBoolean(FPref.UI_ENABLE_BORDER_MASKING);
|
||||||
enablePreloadExtendedArt = prefs.getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART);
|
enablePreloadExtendedArt = prefs.getPrefBoolean(FPref.UI_ENABLE_PRELOAD_EXTENDED_ART);
|
||||||
locale = prefs.getPref(FPref.UI_LANGUAGE);
|
locale = prefs.getPref(FPref.UI_LANGUAGE);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.GL20;
|
|||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||||
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||||
import com.badlogic.gdx.math.Matrix4;
|
import com.badlogic.gdx.math.Matrix4;
|
||||||
@@ -37,8 +38,75 @@ public class Graphics {
|
|||||||
private int failedClipCount;
|
private int failedClipCount;
|
||||||
private float alphaComposite = 1;
|
private float alphaComposite = 1;
|
||||||
private int transformCount = 0;
|
private int transformCount = 0;
|
||||||
|
private String sVertex = "uniform mat4 u_projTrans;\n" +
|
||||||
|
"\n" +
|
||||||
|
"attribute vec4 a_position;\n" +
|
||||||
|
"attribute vec2 a_texCoord0;\n" +
|
||||||
|
"attribute vec4 a_color;\n" +
|
||||||
|
"\n" +
|
||||||
|
"varying vec4 v_color;\n" +
|
||||||
|
"varying vec2 v_texCoord;\n" +
|
||||||
|
"\n" +
|
||||||
|
"uniform vec2 u_viewportInverse;\n" +
|
||||||
|
"\n" +
|
||||||
|
"void main() {\n" +
|
||||||
|
" gl_Position = u_projTrans * a_position;\n" +
|
||||||
|
" v_texCoord = a_texCoord0;\n" +
|
||||||
|
" v_color = a_color;\n" +
|
||||||
|
"}";
|
||||||
|
private String sFragment = "#ifdef GL_ES\n" +
|
||||||
|
"precision mediump float;\n" +
|
||||||
|
"precision mediump int;\n" +
|
||||||
|
"#endif\n" +
|
||||||
|
"\n" +
|
||||||
|
"uniform sampler2D u_texture;\n" +
|
||||||
|
"\n" +
|
||||||
|
"// The inverse of the viewport dimensions along X and Y\n" +
|
||||||
|
"uniform vec2 u_viewportInverse;\n" +
|
||||||
|
"\n" +
|
||||||
|
"// Color of the outline\n" +
|
||||||
|
"uniform vec3 u_color;\n" +
|
||||||
|
"\n" +
|
||||||
|
"// Thickness of the outline\n" +
|
||||||
|
"uniform float u_offset;\n" +
|
||||||
|
"\n" +
|
||||||
|
"// Step to check for neighbors\n" +
|
||||||
|
"uniform float u_step;\n" +
|
||||||
|
"\n" +
|
||||||
|
"varying vec4 v_color;\n" +
|
||||||
|
"varying vec2 v_texCoord;\n" +
|
||||||
|
"\n" +
|
||||||
|
"#define ALPHA_VALUE_BORDER 0.5\n" +
|
||||||
|
"\n" +
|
||||||
|
"void main() {\n" +
|
||||||
|
" vec2 T = v_texCoord.xy;\n" +
|
||||||
|
"\n" +
|
||||||
|
" float alpha = 0.0;\n" +
|
||||||
|
" bool allin = true;\n" +
|
||||||
|
" for( float ix = -u_offset; ix < u_offset; ix += u_step )\n" +
|
||||||
|
" {\n" +
|
||||||
|
" for( float iy = -u_offset; iy < u_offset; iy += u_step )\n" +
|
||||||
|
" {\n" +
|
||||||
|
" float newAlpha = texture2D(u_texture, T + vec2(ix, iy) * u_viewportInverse).a;\n" +
|
||||||
|
" allin = allin && newAlpha > ALPHA_VALUE_BORDER;\n" +
|
||||||
|
" if (newAlpha > ALPHA_VALUE_BORDER && newAlpha >= alpha)\n" +
|
||||||
|
" {\n" +
|
||||||
|
" alpha = newAlpha;\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" if (allin)\n" +
|
||||||
|
" {\n" +
|
||||||
|
" alpha = 0.0;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" gl_FragColor = vec4(u_color,alpha);\n" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
private final ShaderProgram shaderOutline = new ShaderProgram(sVertex, sFragment);
|
||||||
|
|
||||||
public Graphics() {
|
public Graphics() {
|
||||||
|
ShaderProgram.pedantic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void begin(float regionWidth0, float regionHeight0) {
|
public void begin(float regionWidth0, float regionHeight0) {
|
||||||
@@ -60,6 +128,7 @@ public class Graphics {
|
|||||||
public void dispose() {
|
public void dispose() {
|
||||||
batch.dispose();
|
batch.dispose();
|
||||||
shapeRenderer.dispose();
|
shapeRenderer.dispose();
|
||||||
|
shaderOutline.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpriteBatch getBatch() {
|
public SpriteBatch getBatch() {
|
||||||
@@ -568,7 +637,7 @@ public class Graphics {
|
|||||||
|
|
||||||
public void drawBorderImage(FImage image, Color borderColor, Color tintColor, float x, float y, float w, float h, boolean tint) {
|
public void drawBorderImage(FImage image, Color borderColor, Color tintColor, float x, float y, float w, float h, boolean tint) {
|
||||||
float oldalpha = alphaComposite;
|
float oldalpha = alphaComposite;
|
||||||
if(tint){
|
if(tint && !tintColor.equals(borderColor)){
|
||||||
drawRoundRect(2f, borderLining(borderColor.toString()), x, y, w, h, (h-w)/12);
|
drawRoundRect(2f, borderLining(borderColor.toString()), x, y, w, h, (h-w)/12);
|
||||||
fillRoundRect(tintColor, x, y, w, h, (h-w)/12);
|
fillRoundRect(tintColor, x, y, w, h, (h-w)/12);
|
||||||
} else {
|
} else {
|
||||||
@@ -604,6 +673,56 @@ public class Graphics {
|
|||||||
public void drawImage(TextureRegion image, float x, float y, float w, float h) {
|
public void drawImage(TextureRegion image, float x, float y, float w, float h) {
|
||||||
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||||
}
|
}
|
||||||
|
public void drawImage(TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) {
|
||||||
|
//1st image is the image on top of the shader, 2nd image is for the outline reference for the shader glow...
|
||||||
|
// if the 1st image don't have transparency in the middle (only on the sides, top and bottom, use the 1st image as outline reference...
|
||||||
|
if (!selected) {
|
||||||
|
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||||
|
} else {
|
||||||
|
batch.end();
|
||||||
|
shaderOutline.begin();
|
||||||
|
shaderOutline.setUniformf("u_viewportInverse", new Vector2(1f / w, 1f / h));
|
||||||
|
shaderOutline.setUniformf("u_offset", 3f);
|
||||||
|
shaderOutline.setUniformf("u_step", Math.min(1f, w / 70f));
|
||||||
|
shaderOutline.setUniformf("u_color", new Vector3(glowColor.r, glowColor.g, glowColor.b));
|
||||||
|
shaderOutline.end();
|
||||||
|
batch.setShader(shaderOutline);
|
||||||
|
batch.begin();
|
||||||
|
//glow
|
||||||
|
batch.draw(glowImageReference, adjustX(x), adjustY(y, h), w, h);
|
||||||
|
batch.end();
|
||||||
|
batch.setShader(null);
|
||||||
|
batch.begin();
|
||||||
|
//img
|
||||||
|
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void drawDeckBox(FImage cardArt, float scale, TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) {
|
||||||
|
float yBox = y-(h*0.25f);
|
||||||
|
if (!selected) {
|
||||||
|
cardArt.draw(this,x+((w-w*scale)/2), y+((h-h*scale)/3f), w*scale, h*scale/1.85f);
|
||||||
|
batch.draw(image, adjustX(x), adjustY(yBox, h), w, h);
|
||||||
|
} else {
|
||||||
|
batch.end();
|
||||||
|
shaderOutline.begin();
|
||||||
|
shaderOutline.setUniformf("u_viewportInverse", new Vector2(1f / w, 1f / h));
|
||||||
|
shaderOutline.setUniformf("u_offset", 3f);
|
||||||
|
shaderOutline.setUniformf("u_step", Math.min(1f, w / 70f));
|
||||||
|
shaderOutline.setUniformf("u_color", new Vector3(glowColor.r, glowColor.g, glowColor.b));
|
||||||
|
shaderOutline.end();
|
||||||
|
batch.setShader(shaderOutline);
|
||||||
|
batch.begin();
|
||||||
|
//glow
|
||||||
|
batch.draw(glowImageReference, adjustX(x), adjustY(yBox, h), w, h);
|
||||||
|
batch.end();
|
||||||
|
batch.setShader(null);
|
||||||
|
batch.begin();
|
||||||
|
//cardart
|
||||||
|
cardArt.draw(this,x+((w-w*scale)/2), y+((h-h*scale)/3f), w*scale, h*scale/1.85f);
|
||||||
|
//deckbox
|
||||||
|
batch.draw(image, adjustX(x), adjustY(yBox, h), w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void drawRepeatingImage(Texture image, float x, float y, float w, float h) {
|
public void drawRepeatingImage(Texture image, float x, float y, float w, float h) {
|
||||||
if (startClip(x, y, w, h)) { //only render if clip successful, otherwise it will escape bounds
|
if (startClip(x, y, w, h)) { //only render if clip successful, otherwise it will escape bounds
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData;
|
|||||||
import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph;
|
import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph;
|
||||||
import com.badlogic.gdx.graphics.g2d.PixmapPacker.Page;
|
import com.badlogic.gdx.graphics.g2d.PixmapPacker.Page;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file is 'borrowed' from gdx-tools in the libgdx source
|
* This file is 'borrowed' from gdx-tools in the libgdx source
|
||||||
@@ -201,7 +202,7 @@ public class BitmapFontWriter {
|
|||||||
: " alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0";
|
: " alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0";
|
||||||
|
|
||||||
// INFO LINE
|
// INFO LINE
|
||||||
buf.append(xmlOpen).append("info face=\"").append(info.face == null ? "" : info.face.replaceAll("\"", "'"))
|
buf.append(xmlOpen).append("info face=\"").append(info.face == null ? "" : TextUtil.fastReplace(info.face,"\"", "'"))
|
||||||
.append("\" size=").append(quote(info.size)).append(" bold=").append(quote(info.bold ? 1 : 0)).append(" italic=")
|
.append("\" size=").append(quote(info.size)).append(" bold=").append(quote(info.bold ? 1 : 0)).append(" italic=")
|
||||||
.append(quote(info.italic ? 1 : 0)).append(" charset=\"").append(info.charset == null ? "" : info.charset)
|
.append(quote(info.italic ? 1 : 0)).append(" charset=\"").append(info.charset == null ? "" : info.charset)
|
||||||
.append("\" unicode=").append(quote(info.unicode ? 1 : 0)).append(" stretchH=").append(quote(info.stretchH))
|
.append("\" unicode=").append(quote(info.unicode ? 1 : 0)).append(" stretchH=").append(quote(info.stretchH))
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ public class FSkin {
|
|||||||
private static final Map<FSkinProp, FSkinImage> images = new HashMap<>(512);
|
private static final Map<FSkinProp, FSkinImage> images = new HashMap<>(512);
|
||||||
private static final Map<Integer, TextureRegion> avatars = new HashMap<>(150);
|
private static final Map<Integer, TextureRegion> avatars = new HashMap<>(150);
|
||||||
private static final Map<Integer, TextureRegion> sleeves = new HashMap<>(64);
|
private static final Map<Integer, TextureRegion> sleeves = new HashMap<>(64);
|
||||||
private static final Map<Integer, TextureRegion> borders = new HashMap<>(2);
|
private static final Map<Integer, TextureRegion> borders = new HashMap<>();
|
||||||
|
private static final Map<Integer, TextureRegion> deckbox = new HashMap<>();
|
||||||
|
|
||||||
private static Array<String> allSkins;
|
private static Array<String> allSkins;
|
||||||
private static FileHandle preferredDir;
|
private static FileHandle preferredDir;
|
||||||
@@ -196,6 +197,7 @@ public class FSkin {
|
|||||||
final FileHandle f10 = getDefaultSkinFile(ForgeConstants.SPRITE_BORDER_FILE);
|
final FileHandle f10 = getDefaultSkinFile(ForgeConstants.SPRITE_BORDER_FILE);
|
||||||
final FileHandle f11 = getSkinFile(ForgeConstants.SPRITE_BUTTONS_FILE);
|
final FileHandle f11 = getSkinFile(ForgeConstants.SPRITE_BUTTONS_FILE);
|
||||||
final FileHandle f12 = getSkinFile(ForgeConstants.SPRITE_START_FILE);
|
final FileHandle f12 = getSkinFile(ForgeConstants.SPRITE_START_FILE);
|
||||||
|
final FileHandle f13 = getDefaultSkinFile(ForgeConstants.SPRITE_DECKBOX_FILE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
textures.put(f1.path(), new Texture(f1));
|
textures.put(f1.path(), new Texture(f1));
|
||||||
@@ -331,10 +333,20 @@ public class FSkin {
|
|||||||
FSkin.sleeves.put(scount++, new TextureRegion(txDefaultSleeves, i, j, 360, 500));
|
FSkin.sleeves.put(scount++, new TextureRegion(txDefaultSleeves, i, j, 360, 500));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//borders
|
||||||
Texture bordersBW = new Texture(f10);
|
Texture bordersBW = new Texture(f10);
|
||||||
FSkin.borders.put(0, new TextureRegion(bordersBW, 2, 2, 672, 936));
|
FSkin.borders.put(0, new TextureRegion(bordersBW, 2, 2, 672, 936));
|
||||||
FSkin.borders.put(1, new TextureRegion(bordersBW, 676, 2, 672, 936));
|
FSkin.borders.put(1, new TextureRegion(bordersBW, 676, 2, 672, 936));
|
||||||
|
//deckboxes
|
||||||
|
Texture deckboxes = new Texture(f13, textureFilter);
|
||||||
|
if (textureFilter)
|
||||||
|
deckboxes.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
|
||||||
|
//gold bg
|
||||||
|
FSkin.deckbox.put(0, new TextureRegion(deckboxes, 2, 2, 488, 680));
|
||||||
|
//deck box for card art
|
||||||
|
FSkin.deckbox.put(1, new TextureRegion(deckboxes, 492, 2, 488, 680));
|
||||||
|
//generic deck box
|
||||||
|
FSkin.deckbox.put(2, new TextureRegion(deckboxes, 982, 2, 488, 680));
|
||||||
|
|
||||||
preferredIcons.dispose();
|
preferredIcons.dispose();
|
||||||
pxDefaultAvatars.dispose();
|
pxDefaultAvatars.dispose();
|
||||||
@@ -430,5 +442,9 @@ public class FSkin {
|
|||||||
return borders;
|
return borders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, TextureRegion> getDeckbox() {
|
||||||
|
return deckbox;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isLoaded() { return loaded; }
|
public static boolean isLoaded() { return loaded; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,15 +288,18 @@ public class ImageCache {
|
|||||||
public static FImage getBorderImage(String textureString) {
|
public static FImage getBorderImage(String textureString) {
|
||||||
return getBorder(textureString);
|
return getBorder(textureString);
|
||||||
}
|
}
|
||||||
public static Color getTint(CardView c) {
|
public static Color getTint(CardView c, Texture t) {
|
||||||
if (c == null)
|
if (c == null)
|
||||||
return Color.CLEAR;
|
return borderColor(t);
|
||||||
if (c.isFaceDown())
|
if (c.isFaceDown())
|
||||||
return Color.CLEAR;
|
return Color.valueOf("#171717");
|
||||||
|
|
||||||
CardView.CardStateView state = c.getCurrentState();
|
CardView.CardStateView state = c.getCurrentState();
|
||||||
if (state.getColors().isColorless()) //Moonlace -> target spell or permanent becomes colorless.
|
if (state.getColors().isColorless()) { //Moonlace -> target spell or permanent becomes colorless.
|
||||||
|
if (state.hasDevoid()) //devoid is colorless at all zones so return its corresponding border color...
|
||||||
|
return borderColor(t);
|
||||||
return Color.valueOf("#A0A6A4");
|
return Color.valueOf("#A0A6A4");
|
||||||
|
}
|
||||||
else if (state.getColors().isMonoColor()) {
|
else if (state.getColors().isMonoColor()) {
|
||||||
if (state.getColors().hasBlack())
|
if (state.getColors().hasBlack())
|
||||||
return Color.valueOf("#48494a");
|
return Color.valueOf("#48494a");
|
||||||
@@ -311,6 +314,7 @@ public class ImageCache {
|
|||||||
}
|
}
|
||||||
else if (state.getColors().isMulticolor())
|
else if (state.getColors().isMulticolor())
|
||||||
return Color.valueOf("#F9E084");
|
return Color.valueOf("#F9E084");
|
||||||
return Color.CLEAR;
|
|
||||||
|
return borderColor(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,11 +190,17 @@ public class CardFaceSymbols {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void drawColorSet(Graphics g, ColorSet colorSet, float x, float y, final float imageSize) {
|
public static void drawColorSet(Graphics g, ColorSet colorSet, float x, float y, final float imageSize) {
|
||||||
|
drawColorSet(g, colorSet, x, y, imageSize, false);
|
||||||
|
}
|
||||||
|
public static void drawColorSet(Graphics g, ColorSet colorSet, float x, float y, final float imageSize, boolean vertical) {
|
||||||
final float dx = imageSize;
|
final float dx = imageSize;
|
||||||
|
|
||||||
for (final ManaCostShard s : colorSet.getOrderedShards()) {
|
for (final ManaCostShard s : colorSet.getOrderedShards()) {
|
||||||
drawSymbol(s.getImageKey(), g, x, y, imageSize, imageSize);
|
drawSymbol(s.getImageKey(), g, x, y, imageSize, imageSize);
|
||||||
x += dx;
|
if (!vertical)
|
||||||
|
x += dx;
|
||||||
|
else
|
||||||
|
y += dx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ public class CardImageRenderer {
|
|||||||
ManaCost mainManaCost = state.getManaCost();
|
ManaCost mainManaCost = state.getManaCost();
|
||||||
if (card.isSplitCard() && card.getAlternateState() != null) {
|
if (card.isSplitCard() && card.getAlternateState() != null) {
|
||||||
//handle rendering both parts of split card
|
//handle rendering both parts of split card
|
||||||
mainManaCost = state.getManaCost();
|
mainManaCost = card.getLeftSplitState().getManaCost();
|
||||||
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
||||||
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||||
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||||
@@ -459,7 +459,7 @@ public class CardImageRenderer {
|
|||||||
|
|
||||||
y += cardNameBoxHeight + innerBorderThickness;
|
y += cardNameBoxHeight + innerBorderThickness;
|
||||||
Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT);
|
Color[] textBoxColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.TEXT_BOX_TINT);
|
||||||
drawDetailsTextBox(g, state, gameView, canShow, textBoxColors, x, y, w, textBoxHeight);
|
drawDetailsTextBox(g, card.isSplitCard() && !altState ? card.getLeftSplitState() : state, gameView, canShow, textBoxColors, x, y, w, textBoxHeight);
|
||||||
|
|
||||||
y += textBoxHeight + innerBorderThickness;
|
y += textBoxHeight + innerBorderThickness;
|
||||||
Color[] ptColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.PT_BOX_TINT);
|
Color[] ptColors = FSkinColor.tintColors(Color.WHITE, colors, CardRenderer.PT_BOX_TINT);
|
||||||
@@ -506,7 +506,7 @@ public class CardImageRenderer {
|
|||||||
ManaCost mainManaCost = state.getManaCost();
|
ManaCost mainManaCost = state.getManaCost();
|
||||||
if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack) { //only display current state's mana cost when on stack
|
if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack) { //only display current state's mana cost when on stack
|
||||||
//handle rendering both parts of split card
|
//handle rendering both parts of split card
|
||||||
mainManaCost = state.getManaCost();
|
mainManaCost = card.getLeftSplitState().getManaCost();
|
||||||
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
||||||
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + HEADER_PADDING;
|
||||||
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||||
|
|||||||
@@ -370,6 +370,7 @@ public class CardRenderer {
|
|||||||
ManaCost mainManaCost = card.getCurrentState().getManaCost();
|
ManaCost mainManaCost = card.getCurrentState().getManaCost();
|
||||||
if (card.isSplitCard()) {
|
if (card.isSplitCard()) {
|
||||||
//handle rendering both parts of split card
|
//handle rendering both parts of split card
|
||||||
|
mainManaCost = card.getLeftSplitState().getManaCost();
|
||||||
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
ManaCost otherManaCost = card.getAlternateState().getManaCost();
|
||||||
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + MANA_COST_PADDING;
|
manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + MANA_COST_PADDING;
|
||||||
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth + MANA_COST_PADDING, y, MANA_SYMBOL_SIZE);
|
CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth + MANA_COST_PADDING, y, MANA_SYMBOL_SIZE);
|
||||||
@@ -522,7 +523,7 @@ public class CardRenderer {
|
|||||||
g.drawImage(image, x, y, w, h);
|
g.drawImage(image, x, y, w, h);
|
||||||
else {
|
else {
|
||||||
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||||
g.drawBorderImage(ImageCache.getBorderImage(image.toString(), canshow), ImageCache.borderColor(image), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
|
g.drawBorderImage(ImageCache.getBorderImage(image.toString(), canshow), ImageCache.borderColor(image), ImageCache.getTint(card, image), x, y, w, h, t); //tint check for changed colors
|
||||||
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -543,9 +544,9 @@ public class CardRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) {
|
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) {
|
||||||
drawCardWithOverlays(g, card, x, y, w, h, pos, false, false);
|
drawCardWithOverlays(g, card, x, y, w, h, pos, false, false, false);
|
||||||
}
|
}
|
||||||
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean stackview, boolean showAltState) {
|
public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean stackview, boolean showAltState, boolean isChoiceList) {
|
||||||
boolean canShow = MatchController.instance.mayView(card);
|
boolean canShow = MatchController.instance.mayView(card);
|
||||||
float oldAlpha = g.getfloatAlphaComposite();
|
float oldAlpha = g.getfloatAlphaComposite();
|
||||||
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
boolean unselectable = !MatchController.instance.isSelectable(card) && MatchController.instance.isSelecting();
|
||||||
@@ -560,7 +561,7 @@ public class CardRenderer {
|
|||||||
h -= 2 * padding;
|
h -= 2 * padding;
|
||||||
|
|
||||||
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
||||||
final CardStateView details = showAltState ? card.getAlternateState() : card.getCurrentState();
|
final CardStateView details = showAltState ? card.getAlternateState() : isChoiceList && card.isSplitCard() ? card.getLeftSplitState() : card.getCurrentState();
|
||||||
final boolean isFaceDown = card.isFaceDown();
|
final boolean isFaceDown = card.isFaceDown();
|
||||||
final DetailColors borderColor = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(details, canShow); // canShow doesn't work here for face down Morphs
|
final DetailColors borderColor = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(details, canShow); // canShow doesn't work here for face down Morphs
|
||||||
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
|
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
|
||||||
@@ -932,8 +933,8 @@ public class CardRenderer {
|
|||||||
dy *= -1; // flip card costs for Aftermath cards
|
dy *= -1; // flip card costs for Aftermath cards
|
||||||
}
|
}
|
||||||
|
|
||||||
drawManaCost(g, card.getAlternateState().getManaCost(), x - padding, y - dy, w + 2 * padding, h, manaSymbolSize);
|
drawManaCost(g, card.getRightSplitState().getManaCost(), x - padding, y - dy, w + 2 * padding, h, manaSymbolSize);
|
||||||
drawManaCost(g, card.getCurrentState().getManaCost(), x - padding, y + dy, w + 2 * padding, h, manaSymbolSize);
|
drawManaCost(g, card.getLeftSplitState().getManaCost(), x - padding, y + dy, w + 2 * padding, h, manaSymbolSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public class FDeckChooser extends FScreen {
|
|||||||
private Callback<Deck> callback;
|
private Callback<Deck> callback;
|
||||||
private NetDeckCategory netDeckCategory;
|
private NetDeckCategory netDeckCategory;
|
||||||
private boolean refreshingDeckType;
|
private boolean refreshingDeckType;
|
||||||
|
private boolean firstactivation = true;
|
||||||
|
|
||||||
private final DeckManager lstDecks;
|
private final DeckManager lstDecks;
|
||||||
private final FButton btnNewDeck = new FButton(Localizer.getInstance().getMessage("lblNewDeck"));
|
private final FButton btnNewDeck = new FButton(Localizer.getInstance().getMessage("lblNewDeck"));
|
||||||
@@ -226,6 +227,11 @@ public class FDeckChooser extends FScreen {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivate() {
|
public void onActivate() {
|
||||||
|
//somehow a loaded deck state from startup don't refresh accordingly for imageview so refresh it on first activation
|
||||||
|
if(firstactivation) {
|
||||||
|
needRefreshOnActivate = true;
|
||||||
|
firstactivation = false;
|
||||||
|
}
|
||||||
if (needRefreshOnActivate) {
|
if (needRefreshOnActivate) {
|
||||||
needRefreshOnActivate = false;
|
needRefreshOnActivate = false;
|
||||||
refreshDecksList(selectedDeckType, true, null);
|
refreshDecksList(selectedDeckType, true, null);
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
package forge.itemmanager.views;
|
package forge.itemmanager.views;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
import forge.Forge.KeyInputAdapter;
|
import forge.Forge.KeyInputAdapter;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.assets.FImage;
|
import forge.assets.FImage;
|
||||||
|
import forge.assets.FImageComplex;
|
||||||
|
import forge.assets.FSkin;
|
||||||
import forge.assets.FSkinColor;
|
import forge.assets.FSkinColor;
|
||||||
import forge.assets.FSkinImage;
|
import forge.assets.FSkinImage;
|
||||||
import forge.assets.FSkinColor.Colors;
|
import forge.assets.FSkinColor.Colors;
|
||||||
import forge.assets.FSkinFont;
|
import forge.assets.FSkinFont;
|
||||||
import forge.assets.ImageCache;
|
import forge.assets.ImageCache;
|
||||||
|
import forge.card.CardFaceSymbols;
|
||||||
import forge.card.CardRenderer;
|
import forge.card.CardRenderer;
|
||||||
import forge.card.CardRenderer.CardStackPosition;
|
import forge.card.CardRenderer.CardStackPosition;
|
||||||
import forge.card.CardZoom;
|
import forge.card.CardZoom;
|
||||||
|
import forge.card.ColorSet;
|
||||||
import forge.deck.ArchetypeDeckGenerator;
|
import forge.deck.ArchetypeDeckGenerator;
|
||||||
import forge.deck.CardThemedDeckGenerator;
|
import forge.deck.CardThemedDeckGenerator;
|
||||||
import forge.deck.CommanderDeckGenerator;
|
import forge.deck.CommanderDeckGenerator;
|
||||||
import forge.deck.DeckProxy;
|
import forge.deck.DeckProxy;
|
||||||
import forge.deck.FDeckViewer;
|
import forge.deck.FDeckViewer;
|
||||||
|
import forge.deck.io.DeckPreferences;
|
||||||
import forge.item.InventoryItem;
|
import forge.item.InventoryItem;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.itemmanager.ColumnDef;
|
import forge.itemmanager.ColumnDef;
|
||||||
@@ -27,6 +33,7 @@ import forge.itemmanager.ItemManagerConfig;
|
|||||||
import forge.itemmanager.ItemManagerModel;
|
import forge.itemmanager.ItemManagerModel;
|
||||||
import forge.itemmanager.SItemManagerUtil;
|
import forge.itemmanager.SItemManagerUtil;
|
||||||
import forge.itemmanager.filters.ItemFilter;
|
import forge.itemmanager.filters.ItemFilter;
|
||||||
|
import forge.planarconquest.ConquestCommander;
|
||||||
import forge.toolbox.FCardPanel;
|
import forge.toolbox.FCardPanel;
|
||||||
import forge.toolbox.FComboBox;
|
import forge.toolbox.FComboBox;
|
||||||
import forge.toolbox.FDisplayObject;
|
import forge.toolbox.FDisplayObject;
|
||||||
@@ -36,6 +43,7 @@ import forge.toolbox.FEvent.FEventHandler;
|
|||||||
import forge.toolbox.FLabel;
|
import forge.toolbox.FLabel;
|
||||||
import forge.toolbox.FScrollPane;
|
import forge.toolbox.FScrollPane;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
import forge.util.TextUtil;
|
||||||
import forge.util.Utils;
|
import forge.util.Utils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -61,7 +69,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
private static final float GROUP_HEADER_GLYPH_WIDTH = Utils.scale(6);
|
private static final float GROUP_HEADER_GLYPH_WIDTH = Utils.scale(6);
|
||||||
private static final float GROUP_HEADER_LINE_THICKNESS = Utils.scale(1);
|
private static final float GROUP_HEADER_LINE_THICKNESS = Utils.scale(1);
|
||||||
private static final float SEL_BORDER_SIZE = Utils.scale(1);
|
private static final float SEL_BORDER_SIZE = Utils.scale(1);
|
||||||
private static final int MIN_COLUMN_COUNT = 1;
|
private static final int MIN_COLUMN_COUNT = Forge.isLandscapeMode() ? 2 : 1;
|
||||||
private static final int MAX_COLUMN_COUNT = 10;
|
private static final int MAX_COLUMN_COUNT = 10;
|
||||||
|
|
||||||
private final List<Integer> selectedIndices = new ArrayList<>();
|
private final List<Integer> selectedIndices = new ArrayList<>();
|
||||||
@@ -550,7 +558,8 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
}
|
}
|
||||||
else if (count == 2) {
|
else if (count == 2) {
|
||||||
if (item != null && item.selected) {
|
if (item != null && item.selected) {
|
||||||
itemManager.activateSelectedItems();
|
if (!(item.getKey() instanceof DeckProxy))
|
||||||
|
itemManager.activateSelectedItems();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -854,6 +863,19 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tap(float x, float y, int count) {
|
public boolean tap(float x, float y, int count) {
|
||||||
|
ItemInfo item = getItemAtPoint(x + getLeft(), y + getTop());
|
||||||
|
if (item != null) {
|
||||||
|
if(item.getKey() instanceof DeckProxy) {
|
||||||
|
DeckProxy dp = (DeckProxy)item.getKey();
|
||||||
|
if (count >= 2 && !dp.isGeneratedDeck()) {
|
||||||
|
//double tap to add to favorites or remove....
|
||||||
|
if (DeckPreferences.getPrefs(dp).getStarCount() > 0)
|
||||||
|
DeckPreferences.getPrefs(dp).setStarCount(0);
|
||||||
|
else
|
||||||
|
DeckPreferences.getPrefs(dp).setStarCount(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (groupBy != null && !items.isEmpty() && y < GROUP_HEADER_HEIGHT) {
|
if (groupBy != null && !items.isEmpty() && y < GROUP_HEADER_HEIGHT) {
|
||||||
isCollapsed = !isCollapsed;
|
isCollapsed = !isCollapsed;
|
||||||
btnExpandCollapseAll.updateIsAllCollapsed();
|
btnExpandCollapseAll.updateIsAllCollapsed();
|
||||||
@@ -869,7 +891,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
ItemInfo item = getItemAtPoint(x + getLeft(), y + getTop());
|
ItemInfo item = getItemAtPoint(x + getLeft(), y + getTop());
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
if(item.getKey() instanceof CardThemedDeckGenerator || item.getKey() instanceof CommanderDeckGenerator
|
if(item.getKey() instanceof CardThemedDeckGenerator || item.getKey() instanceof CommanderDeckGenerator
|
||||||
|| item.getKey() instanceof ArchetypeDeckGenerator){
|
|| item.getKey() instanceof ArchetypeDeckGenerator || item.getKey() instanceof DeckProxy){
|
||||||
FDeckViewer.show(((DeckProxy)item.getKey()).getDeck());
|
FDeckViewer.show(((DeckProxy)item.getKey()).getDeck());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -922,6 +944,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
private int index;
|
private int index;
|
||||||
private CardStackPosition pos;
|
private CardStackPosition pos;
|
||||||
private boolean selected;
|
private boolean selected;
|
||||||
|
private final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE;
|
||||||
|
|
||||||
private ItemInfo(T item0, Group group0) {
|
private ItemInfo(T item0, Group group0) {
|
||||||
item = item0;
|
item = item0;
|
||||||
@@ -954,30 +977,100 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
final float y = getTop() - group.getTop() - getScrollValue();
|
final float y = getTop() - group.getTop() - getScrollValue();
|
||||||
final float w = getWidth();
|
final float w = getWidth();
|
||||||
final float h = getHeight();
|
final float h = getHeight();
|
||||||
|
Texture dpImg = null;
|
||||||
if (selected) { //if round border is enabled, the select highlight is also rounded..
|
boolean deckSelectMode = false;
|
||||||
if (Forge.enableUIMask) {
|
if (item instanceof DeckProxy) {
|
||||||
//fillroundrect has rough/aliased corner
|
dpImg = ImageCache.getImage(item);
|
||||||
g.fillRoundRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
deckSelectMode = true;
|
||||||
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE, (h - w) / 10);
|
}
|
||||||
//drawroundrect has GL_SMOOTH to `smoothen/faux` the aliased corner
|
if (selected) {
|
||||||
g.drawRoundRect(1f, Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
if (!deckSelectMode) {
|
||||||
w + 1.5f * SEL_BORDER_SIZE, h + 1.5f * SEL_BORDER_SIZE, (h - w) / 10);
|
//if round border is enabled, the select highlight is also rounded..
|
||||||
|
if (Forge.enableUIMask) {
|
||||||
|
//fillroundrect has rough/aliased corner
|
||||||
|
g.fillRoundRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE, w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE, (h - w) / 10);
|
||||||
|
//drawroundrect has GL_SMOOTH to `smoothen/faux` the aliased corner
|
||||||
|
g.drawRoundRect(1f, Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE, w + 1.5f * SEL_BORDER_SIZE, h + 1.5f * SEL_BORDER_SIZE, (h - w) / 10);
|
||||||
|
}
|
||||||
|
else //default rectangle highlight
|
||||||
|
g.fillRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE, w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
||||||
}
|
}
|
||||||
else //default rectangle highlight
|
|
||||||
g.fillRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE,
|
|
||||||
w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item instanceof PaperCard) {
|
if (item instanceof PaperCard) {
|
||||||
CardRenderer.drawCard(g, (PaperCard)item, x, y, w, h, pos);
|
CardRenderer.drawCard(g, (PaperCard) item, x, y, w, h, pos);
|
||||||
}
|
} else if (item instanceof ConquestCommander) {
|
||||||
else {
|
CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos);
|
||||||
|
} else if (deckSelectMode) {
|
||||||
|
DeckProxy dp = ((DeckProxy) item);
|
||||||
|
ColorSet deckColor = dp.getColor();
|
||||||
|
float scale = 0.75f;
|
||||||
|
|
||||||
|
if (dpImg != null) {//generated decks have missing info...
|
||||||
|
if (Forge.enableUIMask){
|
||||||
|
//commander bg
|
||||||
|
g.drawImage(FSkin.getDeckbox().get(0), FSkin.getDeckbox().get(0), x, y, w, h, Color.GREEN, selected);
|
||||||
|
TextureRegion tr = ImageCache.croppedBorderImage(dpImg);
|
||||||
|
g.drawImage(tr, x+(w-w*scale)/2, y+(h-h*scale)/1.5f, w*scale, h*scale);
|
||||||
|
} else {
|
||||||
|
if (selected)
|
||||||
|
g.fillRect(Color.GREEN, x - SEL_BORDER_SIZE, y - SEL_BORDER_SIZE, w + 2 * SEL_BORDER_SIZE, h + 2 * SEL_BORDER_SIZE);
|
||||||
|
g.drawImage(dpImg, x, y, w, h);
|
||||||
|
}
|
||||||
|
//fake labelname shadow
|
||||||
|
g.drawText(item.getName(), GROUP_HEADER_FONT, Color.BLACK, (x + PADDING)-1f, (y + PADDING*2)+1f, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, false);
|
||||||
|
//labelname
|
||||||
|
g.drawText(item.getName(), GROUP_HEADER_FONT, Color.WHITE, x + PADDING, y + PADDING*2, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, false);
|
||||||
|
} else {
|
||||||
|
if (!dp.isGeneratedDeck()){
|
||||||
|
FImageComplex cardArt = CardRenderer.getCardArt(dp.getHighestCMCCard().getImageKey(false), false, false, false);
|
||||||
|
//draw the deckbox
|
||||||
|
if (cardArt == null){
|
||||||
|
//draw generic box if null or still loading
|
||||||
|
g.drawImage(FSkin.getDeckbox().get(2), FSkin.getDeckbox().get(2), x, y-(h*0.25f), w, h, Color.GREEN, selected);
|
||||||
|
} else {
|
||||||
|
g.drawDeckBox(cardArt, scale, FSkin.getDeckbox().get(1), FSkin.getDeckbox().get(2), x, y, w, h, Color.GREEN, selected);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//generic box
|
||||||
|
g.drawImage(FSkin.getDeckbox().get(2), FSkin.getDeckbox().get(2), x, y-(h*0.25f), w, h, Color.GREEN, selected);
|
||||||
|
}
|
||||||
|
if (deckColor != null) {
|
||||||
|
//deck color identity
|
||||||
|
float symbolSize = IMAGE_SIZE;
|
||||||
|
if (Forge.isLandscapeMode()) {
|
||||||
|
if (columnCount == 4)
|
||||||
|
symbolSize = IMAGE_SIZE * 1.5f;
|
||||||
|
else if (columnCount == 3)
|
||||||
|
symbolSize = IMAGE_SIZE * 2f;
|
||||||
|
else if (columnCount == 2)
|
||||||
|
symbolSize = IMAGE_SIZE * 3f;
|
||||||
|
else if (columnCount == 1)
|
||||||
|
symbolSize = IMAGE_SIZE * 4f;
|
||||||
|
} else {
|
||||||
|
if (columnCount > 2)
|
||||||
|
symbolSize = IMAGE_SIZE * (0.5f);
|
||||||
|
}
|
||||||
|
//vertical mana icons
|
||||||
|
CardFaceSymbols.drawColorSet(g, deckColor, x +(w-symbolSize), y+(h/8), symbolSize, true);
|
||||||
|
if(!dp.isGeneratedDeck()) {
|
||||||
|
if (Forge.hdbuttons)
|
||||||
|
g.drawImage(DeckPreferences.getPrefs(dp).getStarCount() > 0 ? FSkinImage.HDSTAR_FILLED : FSkinImage.HDSTAR_OUTLINE, x, y, symbolSize, symbolSize);
|
||||||
|
else
|
||||||
|
g.drawImage(DeckPreferences.getPrefs(dp).getStarCount() > 0 ? FSkinImage.STAR_FILLED : FSkinImage.STAR_OUTLINE, x, y, symbolSize, symbolSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String deckname = TextUtil.fastReplace(item.getName(),"] #", "]\n#");
|
||||||
|
//deckname fakeshadow
|
||||||
|
g.drawText(deckname, GROUP_HEADER_FONT, Color.BLACK, (x + PADDING)-1f, (y + (h/10) + PADDING)+1f, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, true);
|
||||||
|
//deck name
|
||||||
|
g.drawText(deckname, GROUP_HEADER_FONT, Color.WHITE, x + PADDING, y + (h/10) + PADDING, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Texture img = ImageCache.getImage(item);
|
Texture img = ImageCache.getImage(item);
|
||||||
if (img != null) {
|
if (img != null) {
|
||||||
g.drawImage(img, x, y, w, h);
|
g.drawImage(img, x, y, w, h);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
g.fillRect(Color.BLACK, x, y, w, h);
|
g.fillRect(Color.BLACK, x, y, w, h);
|
||||||
g.drawText(item.getName(), GROUP_HEADER_FONT, Color.WHITE, x + PADDING, y + PADDING, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, false);
|
g.drawText(item.getName(), GROUP_HEADER_FONT, Color.WHITE, x + PADDING, y + PADDING, w - 2 * PADDING, h - 2 * PADDING, true, Align.center, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -628,7 +628,8 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
|
|||||||
}
|
}
|
||||||
if (Forge.gameInProgress) {
|
if (Forge.gameInProgress) {
|
||||||
/*preload deck to cache*/
|
/*preload deck to cache*/
|
||||||
ImageCache.preloadCache(decks[i]);
|
if(slot.getType() == LobbySlotType.LOCAL)
|
||||||
|
ImageCache.preloadCache(decks[i]);
|
||||||
}
|
}
|
||||||
Gdx.graphics.requestRendering();
|
Gdx.graphics.requestRendering();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,10 +358,10 @@ public class MatchScreen extends FScreen {
|
|||||||
if(gameMenu!=null) {
|
if(gameMenu!=null) {
|
||||||
if(gameMenu.getChildCount()>3){
|
if(gameMenu.getChildCount()>3){
|
||||||
if(viewWinLose == null) {
|
if(viewWinLose == null) {
|
||||||
gameMenu.getChildAt(0).setEnabled(true);
|
gameMenu.getChildAt(0).setEnabled(!game.isMulligan());
|
||||||
gameMenu.getChildAt(1).setEnabled(true);
|
gameMenu.getChildAt(1).setEnabled(!game.isMulligan());
|
||||||
gameMenu.getChildAt(2).setEnabled(true);
|
gameMenu.getChildAt(2).setEnabled(!game.isMulligan());
|
||||||
gameMenu.getChildAt(3).setEnabled(true);
|
gameMenu.getChildAt(3).setEnabled(!game.isMulligan());
|
||||||
gameMenu.getChildAt(4).setEnabled(false);
|
gameMenu.getChildAt(4).setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
gameMenu.getChildAt(0).setEnabled(false);
|
gameMenu.getChildAt(0).setEnabled(false);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color;
|
|||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.Align;
|
import com.badlogic.gdx.utils.Align;
|
||||||
|
|
||||||
|
import forge.Forge;
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.assets.FImage;
|
import forge.assets.FImage;
|
||||||
import forge.assets.FSkinFont;
|
import forge.assets.FSkinFont;
|
||||||
@@ -69,6 +70,9 @@ public class VAvatar extends FDisplayObject {
|
|||||||
float h = getHeight();
|
float h = getHeight();
|
||||||
g.drawImage(image, 0, 0, w, h);
|
g.drawImage(image, 0, 0, w, h);
|
||||||
|
|
||||||
|
if (Forge.altPlayerLayout && Forge.isLandscapeMode())
|
||||||
|
return;
|
||||||
|
|
||||||
//display XP in lower right corner of avatar
|
//display XP in lower right corner of avatar
|
||||||
int xp = player.getCounters(CounterEnumType.EXPERIENCE);
|
int xp = player.getCounters(CounterEnumType.EXPERIENCE);
|
||||||
if (xp > 0) {
|
if (xp > 0) {
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ public class VField extends FContainer {
|
|||||||
if (!c.hasCardAttachments() &&
|
if (!c.hasCardAttachments() &&
|
||||||
cardName.equals(c.getCurrentState().getName()) &&
|
cardName.equals(c.getCurrentState().getName()) &&
|
||||||
card.hasSameCounters(c) &&
|
card.hasSameCounters(c) &&
|
||||||
|
card.hasSamePT(c) && //don't stack token with different PT
|
||||||
card.getCurrentState().getKeywordKey().equals(c.getCurrentState().getKeywordKey()) &&
|
card.getCurrentState().getKeywordKey().equals(c.getCurrentState().getKeywordKey()) &&
|
||||||
card.isTapped() == c.isTapped() && // don't stack tapped tokens on untapped tokens
|
card.isTapped() == c.isTapped() && // don't stack tapped tokens on untapped tokens
|
||||||
card.isSick() == c.isSick() && //don't stack sick tokens on non sick
|
card.isSick() == c.isSick() && //don't stack sick tokens on non sick
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ public class VGameMenu extends FDropDownMenu {
|
|||||||
addItem(new FMenuItem(localizer.getMessage("lblDeckList"), FSkinImage.DECKLIST, new FEventHandler() {
|
addItem(new FMenuItem(localizer.getMessage("lblDeckList"), FSkinImage.DECKLIST, new FEventHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(FEvent e) {
|
public void handleEvent(FEvent e) {
|
||||||
|
//pause game when spectating AI Match
|
||||||
|
if (!MatchController.instance.hasLocalPlayers()) {
|
||||||
|
if(!MatchController.instance.isGamePaused())
|
||||||
|
MatchController.instance.pauseMatch();
|
||||||
|
}
|
||||||
|
|
||||||
final Player player = MatchController.getHostedMatch().getGame().getPhaseHandler().getPlayerTurn();
|
final Player player = MatchController.getHostedMatch().getGame().getPhaseHandler().getPlayerTurn();
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
final Deck deck = player.getRegisteredPlayer().getDeck();
|
final Deck deck = player.getRegisteredPlayer().getDeck();
|
||||||
@@ -89,6 +95,11 @@ public class VGameMenu extends FDropDownMenu {
|
|||||||
addItem(new FMenuItem(localizer.getMessage("lblSettings"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, new FEventHandler() {
|
addItem(new FMenuItem(localizer.getMessage("lblSettings"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, new FEventHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(FEvent e) {
|
public void handleEvent(FEvent e) {
|
||||||
|
//pause game when spectating AI Match
|
||||||
|
if (!MatchController.instance.hasLocalPlayers()) {
|
||||||
|
if(!MatchController.instance.isGamePaused())
|
||||||
|
MatchController.instance.pauseMatch();
|
||||||
|
}
|
||||||
SettingsScreen.show(false);
|
SettingsScreen.show(false);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.utils.Align;
|
import com.badlogic.gdx.utils.Align;
|
||||||
|
|
||||||
import forge.Forge;
|
import forge.Forge;
|
||||||
@@ -30,8 +31,10 @@ import forge.util.Utils;
|
|||||||
public class VPlayerPanel extends FContainer {
|
public class VPlayerPanel extends FContainer {
|
||||||
private static final FSkinFont LIFE_FONT = FSkinFont.get(18);
|
private static final FSkinFont LIFE_FONT = FSkinFont.get(18);
|
||||||
private static final FSkinFont INFO_FONT = FSkinFont.get(12);
|
private static final FSkinFont INFO_FONT = FSkinFont.get(12);
|
||||||
|
private static final FSkinFont INFO2_FONT = FSkinFont.get(14);
|
||||||
private static final FSkinColor INFO_FORE_COLOR = FSkinColor.get(Colors.CLR_TEXT);
|
private static final FSkinColor INFO_FORE_COLOR = FSkinColor.get(Colors.CLR_TEXT);
|
||||||
private static final FSkinColor DISPLAY_AREA_BACK_COLOR = FSkinColor.get(Colors.CLR_INACTIVE).alphaColor(0.5f);
|
private static final FSkinColor DISPLAY_AREA_BACK_COLOR = FSkinColor.get(Colors.CLR_INACTIVE).alphaColor(0.5f);
|
||||||
|
private static final FSkinColor DELIRIUM_HIGHLIGHT = FSkinColor.get(Colors.CLR_PHASE_ACTIVE_ENABLED).alphaColor(0.5f);
|
||||||
private static final float INFO_TAB_PADDING_X = Utils.scale(2);
|
private static final float INFO_TAB_PADDING_X = Utils.scale(2);
|
||||||
private static final float INFO_TAB_PADDING_Y = Utils.scale(2);
|
private static final float INFO_TAB_PADDING_Y = Utils.scale(2);
|
||||||
|
|
||||||
@@ -48,6 +51,7 @@ public class VPlayerPanel extends FContainer {
|
|||||||
private float avatarHeight = VAvatar.HEIGHT;
|
private float avatarHeight = VAvatar.HEIGHT;
|
||||||
private float displayAreaHeightFactor = 1.0f;
|
private float displayAreaHeightFactor = 1.0f;
|
||||||
private boolean forMultiPlayer = false;
|
private boolean forMultiPlayer = false;
|
||||||
|
public int adjustHeight = 1;
|
||||||
|
|
||||||
public VPlayerPanel(PlayerView player0, boolean showHand, int playerCount) {
|
public VPlayerPanel(PlayerView player0, boolean showHand, int playerCount) {
|
||||||
player = player0;
|
player = player0;
|
||||||
@@ -284,8 +288,14 @@ public class VPlayerPanel extends FContainer {
|
|||||||
float y = 0;
|
float y = 0;
|
||||||
avatar.setPosition(x, y);
|
avatar.setPosition(x, y);
|
||||||
y += avatar.getHeight();
|
y += avatar.getHeight();
|
||||||
lblLife.setBounds(x, y, avatar.getWidth(), LIFE_FONT.getLineHeight());
|
|
||||||
y += lblLife.getHeight();
|
lblLife.setBounds(x, Forge.altPlayerLayout ? 0 : y, avatar.getWidth(), Forge.altPlayerLayout ? INFO_FONT.getLineHeight() : LIFE_FONT.getLineHeight());
|
||||||
|
if (Forge.altPlayerLayout) {
|
||||||
|
if (adjustHeight > 2)
|
||||||
|
y += INFO_FONT.getLineHeight()/2;
|
||||||
|
} else
|
||||||
|
y += lblLife.getHeight();
|
||||||
|
|
||||||
float infoTabWidth = avatar.getWidth();
|
float infoTabWidth = avatar.getWidth();
|
||||||
float infoTabHeight = (height - y) / tabs.size();
|
float infoTabHeight = (height - y) / tabs.size();
|
||||||
for (InfoTab tab : tabs) {
|
for (InfoTab tab : tabs) {
|
||||||
@@ -361,6 +371,7 @@ public class VPlayerPanel extends FContainer {
|
|||||||
private int life = player.getLife();
|
private int life = player.getLife();
|
||||||
private int poisonCounters = player.getCounters(CounterEnumType.POISON);
|
private int poisonCounters = player.getCounters(CounterEnumType.POISON);
|
||||||
private int energyCounters = player.getCounters(CounterEnumType.ENERGY);
|
private int energyCounters = player.getCounters(CounterEnumType.ENERGY);
|
||||||
|
private int experienceCounters = player.getCounters(CounterEnumType.EXPERIENCE);
|
||||||
private String lifeStr = String.valueOf(life);
|
private String lifeStr = String.valueOf(life);
|
||||||
|
|
||||||
private LifeLabel() {
|
private LifeLabel() {
|
||||||
@@ -388,6 +399,7 @@ public class VPlayerPanel extends FContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
energyCounters = player.getCounters(CounterEnumType.ENERGY);
|
energyCounters = player.getCounters(CounterEnumType.ENERGY);
|
||||||
|
experienceCounters = player.getCounters(CounterEnumType.EXPERIENCE);
|
||||||
|
|
||||||
//when gui player loses life, vibrate device for a length of time based on amount of life lost
|
//when gui player loses life, vibrate device for a length of time based on amount of life lost
|
||||||
if (vibrateDuration > 0 && MatchController.instance.isLocalPlayer(player) &&
|
if (vibrateDuration > 0 && MatchController.instance.isLocalPlayer(player) &&
|
||||||
@@ -405,22 +417,50 @@ public class VPlayerPanel extends FContainer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Graphics g) {
|
public void draw(Graphics g) {
|
||||||
if (poisonCounters == 0 && energyCounters == 0) {
|
adjustHeight = 1;
|
||||||
g.drawText(lifeStr, LIFE_FONT, INFO_FORE_COLOR, 0, 0, getWidth(), getHeight(), false, Align.center, true);
|
if(Forge.altPlayerLayout && Forge.isLandscapeMode()) {
|
||||||
}
|
if (poisonCounters == 0 && energyCounters == 0 && experienceCounters == 0) {
|
||||||
else {
|
g.drawOutlinedText(lifeStr, INFO2_FONT, INFO_FORE_COLOR.getColor(), Color.BLACK, 0, 0, getWidth(), getHeight(), false, Align.left, false);
|
||||||
float halfHeight = getHeight() / 2;
|
} else {
|
||||||
float textStart = halfHeight + Utils.scale(1);
|
float halfHeight = getHeight() / 2;
|
||||||
float textWidth = getWidth() - textStart;
|
float textStart = halfHeight + Utils.scale(1);
|
||||||
g.drawImage(FSkinImage.QUEST_LIFE, 0, 0, halfHeight, halfHeight);
|
float textWidth = getWidth() - textStart;
|
||||||
g.drawText(lifeStr, INFO_FONT, INFO_FORE_COLOR, textStart, 0, textWidth, halfHeight, false, Align.center, true);
|
int mod = 1;
|
||||||
if (poisonCounters > 0) { //prioritize showing poison counters over energy counters
|
g.drawImage(FSkinImage.QUEST_LIFE, 0, 0, halfHeight, halfHeight);
|
||||||
g.drawImage(FSkinImage.POISON, 0, halfHeight, halfHeight, halfHeight);
|
g.drawOutlinedText(lifeStr, INFO_FONT, INFO_FORE_COLOR.getColor(), Color.BLACK, textStart, 0, textWidth, halfHeight, false, Align.left, false);
|
||||||
g.drawText(String.valueOf(poisonCounters), INFO_FONT, INFO_FORE_COLOR, textStart, halfHeight, textWidth, halfHeight, false, Align.center, true);
|
if (poisonCounters > 0) {
|
||||||
|
g.drawImage(FSkinImage.POISON, 0, halfHeight+2, halfHeight, halfHeight);
|
||||||
|
g.drawOutlinedText(String.valueOf(poisonCounters), INFO_FONT, INFO_FORE_COLOR.getColor(), Color.BLACK, textStart, halfHeight+2, textWidth, halfHeight, false, Align.left, false);
|
||||||
|
mod+=1;
|
||||||
|
}
|
||||||
|
if (energyCounters > 0) {
|
||||||
|
g.drawImage(FSkinImage.ENERGY, 0, (halfHeight*mod)+2, halfHeight, halfHeight);
|
||||||
|
g.drawOutlinedText(String.valueOf(energyCounters), INFO_FONT, INFO_FORE_COLOR.getColor(), Color.BLACK, textStart, (halfHeight*mod)+2, textWidth, halfHeight, false, Align.left, false);
|
||||||
|
mod+=1;
|
||||||
|
}
|
||||||
|
if (experienceCounters > 0) {
|
||||||
|
g.drawImage(FSkinImage.COMMANDER, 0, (halfHeight*mod)+2, halfHeight, halfHeight);
|
||||||
|
g.drawOutlinedText(String.valueOf(experienceCounters), INFO_FONT, INFO_FORE_COLOR.getColor(), Color.BLACK, textStart, (halfHeight*mod)+2, textWidth, halfHeight, false, Align.left, false);
|
||||||
|
mod+=1;
|
||||||
|
}
|
||||||
|
adjustHeight = (mod > 2) && (avatar.getHeight() < halfHeight*mod)? mod : 1;
|
||||||
}
|
}
|
||||||
else {
|
} else {
|
||||||
g.drawImage(FSkinImage.ENERGY, 0, halfHeight, halfHeight, halfHeight);
|
if (poisonCounters == 0 && energyCounters == 0) {
|
||||||
g.drawText(String.valueOf(energyCounters), INFO_FONT, INFO_FORE_COLOR, textStart, halfHeight, textWidth, halfHeight, false, Align.center, true);
|
g.drawText(lifeStr, LIFE_FONT, INFO_FORE_COLOR, 0, 0, getWidth(), getHeight(), false, Align.center, true);
|
||||||
|
} else {
|
||||||
|
float halfHeight = getHeight() / 2;
|
||||||
|
float textStart = halfHeight + Utils.scale(1);
|
||||||
|
float textWidth = getWidth() - textStart;
|
||||||
|
g.drawImage(FSkinImage.QUEST_LIFE, 0, 0, halfHeight, halfHeight);
|
||||||
|
g.drawText(lifeStr, INFO_FONT, INFO_FORE_COLOR, textStart, 0, textWidth, halfHeight, false, Align.center, true);
|
||||||
|
if (poisonCounters > 0) { //prioritize showing poison counters over energy counters
|
||||||
|
g.drawImage(FSkinImage.POISON, 0, halfHeight, halfHeight, halfHeight);
|
||||||
|
g.drawText(String.valueOf(poisonCounters), INFO_FONT, INFO_FORE_COLOR, textStart, halfHeight, textWidth, halfHeight, false, Align.center, true);
|
||||||
|
} else {
|
||||||
|
g.drawImage(FSkinImage.ENERGY, 0, halfHeight, halfHeight, halfHeight);
|
||||||
|
g.drawText(String.valueOf(energyCounters), INFO_FONT, INFO_FORE_COLOR, textStart, halfHeight, textWidth, halfHeight, false, Align.center, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,19 +513,22 @@ public class VPlayerPanel extends FContainer {
|
|||||||
yAcross = y;
|
yAcross = y;
|
||||||
y--;
|
y--;
|
||||||
h++;
|
h++;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
h -= INFO_TAB_PADDING_Y;
|
h -= INFO_TAB_PADDING_Y;
|
||||||
yAcross = h;
|
yAcross = h;
|
||||||
y--;
|
y--;
|
||||||
h += 2;
|
h += 2;
|
||||||
}
|
}
|
||||||
g.fillRect(DISPLAY_AREA_BACK_COLOR, 0, isFlipped() ? INFO_TAB_PADDING_Y : 0, w, getHeight() - INFO_TAB_PADDING_Y);
|
//change the graveyard tab selection color to active phase color to indicate the player has delirium
|
||||||
|
if ((icon == FSkinImage.HDGRAVEYARD || icon == FSkinImage.GRAVEYARD) && player.hasDelirium()) {
|
||||||
|
g.fillRect(DELIRIUM_HIGHLIGHT, 0 ,isFlipped() ? INFO_TAB_PADDING_Y : 0, w, getHeight() - INFO_TAB_PADDING_Y);
|
||||||
|
} else {
|
||||||
|
g.fillRect(DISPLAY_AREA_BACK_COLOR, 0, isFlipped() ? INFO_TAB_PADDING_Y : 0, w, getHeight() - INFO_TAB_PADDING_Y);
|
||||||
|
}
|
||||||
if (!Forge.isLandscapeMode()) {
|
if (!Forge.isLandscapeMode()) {
|
||||||
if (isFlipped()) { //use clip to ensure all corners connect
|
if (isFlipped()) { //use clip to ensure all corners connect
|
||||||
g.startClip(-1, y, w + 2, h);
|
g.startClip(-1, y, w + 2, h);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
g.startClip(-1, y, w + 2, yAcross - y);
|
g.startClip(-1, y, w + 2, yAcross - y);
|
||||||
}
|
}
|
||||||
if (forMultiPlayer) {
|
if (forMultiPlayer) {
|
||||||
@@ -533,8 +576,7 @@ public class VPlayerPanel extends FContainer {
|
|||||||
if (lblLife.getRotate180()) {
|
if (lblLife.getRotate180()) {
|
||||||
g.endTransform();
|
g.endTransform();
|
||||||
}
|
}
|
||||||
}
|
} else { //show image above text if taller than wide
|
||||||
else { //show image above text if taller than wide
|
|
||||||
if (lblLife.getRotate180()) {
|
if (lblLife.getRotate180()) {
|
||||||
g.startRotateTransform(getWidth() / 2, getHeight() / 2, 180);
|
g.startRotateTransform(getWidth() / 2, getHeight() / 2, 180);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -364,6 +364,7 @@ public class VStack extends FDropDown {
|
|||||||
float y = 0;
|
float y = 0;
|
||||||
float w = getWidth();
|
float w = getWidth();
|
||||||
float h = preferredHeight;
|
float h = preferredHeight;
|
||||||
|
CardView sourceCard = stackInstance.getSourceCard();
|
||||||
|
|
||||||
boolean needAlpha = (activeStackInstance != stackInstance);
|
boolean needAlpha = (activeStackInstance != stackInstance);
|
||||||
if (needAlpha) { //use alpha for non-active items on stack
|
if (needAlpha) { //use alpha for non-active items on stack
|
||||||
@@ -382,16 +383,17 @@ public class VStack extends FDropDown {
|
|||||||
|
|
||||||
x += PADDING;
|
x += PADDING;
|
||||||
y += PADDING;
|
y += PADDING;
|
||||||
CardRenderer.drawCardWithOverlays(g, stackInstance.getSourceCard(), x, y, CARD_WIDTH, CARD_HEIGHT, CardStackPosition.Top, true, false);
|
CardRenderer.drawCardWithOverlays(g, sourceCard, x, y, CARD_WIDTH, CARD_HEIGHT, CardStackPosition.Top, true, false, false);
|
||||||
|
|
||||||
x += CARD_WIDTH + PADDING;
|
x += CARD_WIDTH + PADDING;
|
||||||
w -= x + PADDING - BORDER_THICKNESS;
|
w -= x + PADDING - BORDER_THICKNESS;
|
||||||
h -= y + PADDING - BORDER_THICKNESS;
|
h -= y + PADDING - BORDER_THICKNESS;
|
||||||
|
|
||||||
String name = stackInstance.getSourceCard().getName();
|
String name = sourceCard.getName();
|
||||||
int index = text.indexOf(name);
|
int index = text.indexOf(name);
|
||||||
String newtext = "";
|
String newtext = "";
|
||||||
String cId = "(" + stackInstance.getSourceCard().getId() + ")";
|
String cId = "(" + sourceCard.getId() + ")";
|
||||||
|
String optionalCostString = !stackInstance.getOptionalCostString().equals("") ? " ("+ stackInstance.getOptionalCostString() + ")" : "";
|
||||||
|
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
newtext = TextUtil.fastReplace(TextUtil.fastReplace(text.trim(),"--","-"),"- -","-");
|
newtext = TextUtil.fastReplace(TextUtil.fastReplace(text.trim(),"--","-"),"- -","-");
|
||||||
@@ -405,7 +407,7 @@ public class VStack extends FDropDown {
|
|||||||
newtext = TextUtil.fastReplace(trimSecond, " "+cId, name);
|
newtext = TextUtil.fastReplace(trimSecond, " "+cId, name);
|
||||||
|
|
||||||
if(newtext.equals("\n"+name))
|
if(newtext.equals("\n"+name))
|
||||||
textRenderer.drawText(g, name + " " + cId, FONT, foreColor, x, y, w, h, y, h, true, Align.left, true);
|
textRenderer.drawText(g, name + " " + cId + optionalCostString, FONT, foreColor, x, y, w, h, y, h, true, Align.left, true);
|
||||||
else {
|
else {
|
||||||
newtext = TextUtil.fastReplace(TextUtil.fastReplace(newtext,name+" -","-"), "\n ", "\n");
|
newtext = TextUtil.fastReplace(TextUtil.fastReplace(newtext,name+" -","-"), "\n ", "\n");
|
||||||
newtext = "\n"+ TextUtil.fastReplace(newtext.trim(),"--","-");
|
newtext = "\n"+ TextUtil.fastReplace(newtext.trim(),"--","-");
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import forge.FThreads;
|
|||||||
import forge.assets.FSkinProp;
|
import forge.assets.FSkinProp;
|
||||||
import forge.game.GameView;
|
import forge.game.GameView;
|
||||||
import forge.gauntlet.GauntletWinLoseController;
|
import forge.gauntlet.GauntletWinLoseController;
|
||||||
|
import forge.util.Localizer;
|
||||||
import forge.util.gui.SOptionPane;
|
import forge.util.gui.SOptionPane;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -70,7 +71,7 @@ public class GauntletWinLose extends ControlWinLose {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SOptionPane.showMessageDialog(sb.toString(), "Gauntlet Progress", icon);
|
SOptionPane.showMessageDialog(sb.toString(), Localizer.getInstance().getMessage("lblGauntletProgress"), icon);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,17 +101,22 @@ public class ConquestRewardDialog extends FScrollPane {
|
|||||||
|
|
||||||
float startX = x;
|
float startX = x;
|
||||||
int cardCount = cardRevealers.size();
|
int cardCount = cardRevealers.size();
|
||||||
cardRevealers.get(0).setBounds(x, y, cardWidth, cardHeight);
|
try {
|
||||||
for (int i = 1; i < cardCount; i++) {
|
cardRevealers.get(0).setBounds(x, y, cardWidth, cardHeight);
|
||||||
if (i % columnCount == 0) {
|
for (int i = 1; i < cardCount; i++) {
|
||||||
x = startX;
|
if (i % columnCount == 0) {
|
||||||
y += cardHeight + PADDING;
|
x = startX;
|
||||||
|
y += cardHeight + PADDING;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x += cardWidth + PADDING;
|
||||||
|
}
|
||||||
|
cardRevealers.get(i).setBounds(x, y, cardWidth, cardHeight);
|
||||||
}
|
}
|
||||||
else {
|
} catch (Exception ex) {
|
||||||
x += cardWidth + PADDING;
|
System.err.println(ex.getMessage());
|
||||||
}
|
|
||||||
cardRevealers.get(i).setBounds(x, y, cardWidth, cardHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ScrollBounds(visibleWidth, y + cardHeight + PADDING);
|
return new ScrollBounds(visibleWidth, y + cardHeight + PADDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,6 +190,16 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
localizer.getMessage("cbEscapeEndsTurn"),
|
localizer.getMessage("cbEscapeEndsTurn"),
|
||||||
localizer.getMessage("nlEscapeEndsTurn")),
|
localizer.getMessage("nlEscapeEndsTurn")),
|
||||||
1);
|
1);
|
||||||
|
lstSettings.addItem(new BooleanSetting(FPref.UI_ALT_PLAYERINFOLAYOUT,
|
||||||
|
localizer.getMessage("lblAltLifeDisplay"),
|
||||||
|
localizer.getMessage("nlAltLifeDisplay")){
|
||||||
|
@Override
|
||||||
|
public void select() {
|
||||||
|
super.select();
|
||||||
|
//update
|
||||||
|
Forge.altPlayerLayout = FModel.getPreferences().getPrefBoolean(FPref.UI_ALT_PLAYERINFOLAYOUT);
|
||||||
|
}
|
||||||
|
},1);
|
||||||
|
|
||||||
//Random Deck Generation
|
//Random Deck Generation
|
||||||
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_NOSMALL,
|
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_NOSMALL,
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ import forge.assets.FSkinProp;
|
|||||||
import forge.assets.IHasSkinProp;
|
import forge.assets.IHasSkinProp;
|
||||||
import forge.assets.TextRenderer;
|
import forge.assets.TextRenderer;
|
||||||
import forge.assets.FSkinColor.Colors;
|
import forge.assets.FSkinColor.Colors;
|
||||||
|
import forge.card.CardFaceSymbols;
|
||||||
import forge.card.CardRenderer;
|
import forge.card.CardRenderer;
|
||||||
import forge.card.CardZoom;
|
import forge.card.CardZoom;
|
||||||
import forge.card.CardRenderer.CardStackPosition;
|
import forge.card.CardRenderer.CardStackPosition;
|
||||||
import forge.card.CardZoom.ActivateHandler;
|
import forge.card.CardZoom.ActivateHandler;
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
import forge.card.mana.ManaCostParser;
|
||||||
import forge.game.card.CardView;
|
import forge.game.card.CardView;
|
||||||
import forge.game.card.IHasCardView;
|
import forge.game.card.IHasCardView;
|
||||||
import forge.game.player.PlayerView;
|
import forge.game.player.PlayerView;
|
||||||
@@ -29,8 +32,11 @@ import forge.itemmanager.filters.ItemFilter;
|
|||||||
import forge.screens.match.MatchController;
|
import forge.screens.match.MatchController;
|
||||||
import forge.screens.match.views.VAvatar;
|
import forge.screens.match.views.VAvatar;
|
||||||
import forge.screens.match.views.VStack;
|
import forge.screens.match.views.VStack;
|
||||||
|
import forge.util.TextUtil;
|
||||||
import forge.util.Utils;
|
import forge.util.Utils;
|
||||||
|
|
||||||
|
import static forge.card.CardRenderer.MANA_SYMBOL_SIZE;
|
||||||
|
|
||||||
public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
||||||
public static final FSkinColor ITEM_COLOR = FSkinColor.get(Colors.CLR_ZEBRA);
|
public static final FSkinColor ITEM_COLOR = FSkinColor.get(Colors.CLR_ZEBRA);
|
||||||
public static final FSkinColor ALT_ITEM_COLOR = ITEM_COLOR.getContrastColor(-20);
|
public static final FSkinColor ALT_ITEM_COLOR = ITEM_COLOR.getContrastColor(-20);
|
||||||
@@ -337,7 +343,17 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
|
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
|
||||||
g.drawText(getChoiceText(value), font, foreColor, x, y, w, h, allowDefaultItemWrap(), Align.left, true);
|
//update manacost text to draw symbols instead
|
||||||
|
if (value.toString().contains(" {")){
|
||||||
|
String[] values = value.toString().split(" ");
|
||||||
|
String cost = TextUtil.fastReplace(values[1],"}{", " ");
|
||||||
|
cost = TextUtil.fastReplace(TextUtil.fastReplace(cost,"{", ""),"}", "");
|
||||||
|
ManaCost manaCost = new ManaCost(new ManaCostParser(cost));
|
||||||
|
CardFaceSymbols.drawManaCost(g, manaCost, x + font.getBounds(values[0]+" ").width, y + (h - MANA_SYMBOL_SIZE) / 2, MANA_SYMBOL_SIZE);
|
||||||
|
g.drawText(values[0], font, foreColor, x, y, w, h, allowDefaultItemWrap(), Align.left, true);
|
||||||
|
} else {
|
||||||
|
g.drawText(getChoiceText(value), font, foreColor, x, y, w, h, allowDefaultItemWrap(), Align.left, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected class NumberRenderer extends DefaultItemRenderer {
|
protected class NumberRenderer extends DefaultItemRenderer {
|
||||||
@@ -500,7 +516,7 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
|
|||||||
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
|
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
|
||||||
CardView cv = ((IHasCardView)value).getCardView();
|
CardView cv = ((IHasCardView)value).getCardView();
|
||||||
boolean showAlternate = showAlternate(cv, value.toString());
|
boolean showAlternate = showAlternate(cv, value.toString());
|
||||||
CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate);
|
CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate, true);
|
||||||
|
|
||||||
float dx = VStack.CARD_WIDTH + FList.PADDING;
|
float dx = VStack.CARD_WIDTH + FList.PADDING;
|
||||||
x += dx;
|
x += dx;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import forge.screens.match.views.VPrompt;
|
|||||||
import forge.toolbox.FButton.Corner;
|
import forge.toolbox.FButton.Corner;
|
||||||
import forge.toolbox.FEvent.FEventHandler;
|
import forge.toolbox.FEvent.FEventHandler;
|
||||||
import forge.util.PhysicsObject;
|
import forge.util.PhysicsObject;
|
||||||
|
import forge.util.TextUtil;
|
||||||
import forge.util.Utils;
|
import forge.util.Utils;
|
||||||
|
|
||||||
public abstract class FDialog extends FOverlay {
|
public abstract class FDialog extends FOverlay {
|
||||||
@@ -48,7 +49,7 @@ public abstract class FDialog extends FOverlay {
|
|||||||
buttonCount = buttonCount0;
|
buttonCount = buttonCount0;
|
||||||
prompt = add(new VPrompt("", "", null, null));
|
prompt = add(new VPrompt("", "", null, null));
|
||||||
if (buttonCount < 3) {
|
if (buttonCount < 3) {
|
||||||
title0 = title0.replaceAll(" - ", "\n"); //breakup into lines
|
title0 = TextUtil.fastReplace(title0," - ", "\n"); //breakup into lines
|
||||||
btnMiddle = null;
|
btnMiddle = null;
|
||||||
prompt.setMessage(title0); //only put title in message if no third button
|
prompt.setMessage(title0); //only put title in message if no third button
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD=500
|
|||||||
PREDICT_SPELLS_FOR_MAIN2=true
|
PREDICT_SPELLS_FOR_MAIN2=true
|
||||||
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
||||||
|
|
||||||
|
# If enabled, the AI will protect its permanents against curse auras with Hexproof, Shroud, and Protection
|
||||||
|
ACTIVELY_PROTECT_VS_CURSE_AURAS=true
|
||||||
|
|
||||||
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
||||||
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD=400
|
|||||||
PREDICT_SPELLS_FOR_MAIN2=true
|
PREDICT_SPELLS_FOR_MAIN2=true
|
||||||
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
||||||
|
|
||||||
|
# If enabled, the AI will protect its permanents against curse auras with Hexproof, Shroud, and Protection
|
||||||
|
ACTIVELY_PROTECT_VS_CURSE_AURAS=false
|
||||||
|
|
||||||
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
||||||
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD=350
|
|||||||
PREDICT_SPELLS_FOR_MAIN2=true
|
PREDICT_SPELLS_FOR_MAIN2=true
|
||||||
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
||||||
|
|
||||||
|
# If enabled, the AI will protect its permanents against curse auras with Hexproof, Shroud, and Protection
|
||||||
|
ACTIVELY_PROTECT_VS_CURSE_AURAS=true
|
||||||
|
|
||||||
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
||||||
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD=300
|
|||||||
PREDICT_SPELLS_FOR_MAIN2=true
|
PREDICT_SPELLS_FOR_MAIN2=true
|
||||||
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
RESERVE_MANA_FOR_MAIN2_CHANCE=100
|
||||||
|
|
||||||
|
# If enabled, the AI will protect its permanents against curse auras with Hexproof, Shroud, and Protection
|
||||||
|
ACTIVELY_PROTECT_VS_CURSE_AURAS=false
|
||||||
|
|
||||||
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
# If enabled, the AI will target artifacts and non-aura enchantments with removal aggressively
|
||||||
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS=true
|
||||||
|
|
||||||
|
|||||||
10
forge-gui/res/blockdata/chaosdraftthemes.txt
Normal file
10
forge-gui/res/blockdata/chaosdraftthemes.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Order, Tag, Label
|
||||||
|
1, DEFAULT, All sets (default)
|
||||||
|
11, MODERN, Modern legal expansions
|
||||||
|
12, PIONEER, Pioneer legal expansions
|
||||||
|
13, STANDARD, Standard legal expansions
|
||||||
|
21, CORE_SET, Core Sets
|
||||||
|
22, MASTERS_SET, Masters Sets (paper only)
|
||||||
|
30, MIRRODIN, Mirrodin (Plane)
|
||||||
|
30, RAVNICA, Ravnica (Plane)
|
||||||
|
40, GRAVEYARD_MATTERS, Graveyard matters
|
||||||
@@ -4,7 +4,7 @@ Types:Enchantment
|
|||||||
S:Mode$ Continuous | Affected$ Land.YouCtrl | AddType$ Plains | RemoveLandTypes$ True | RemoveIntrinsicAbilities$ True | Description$ Lands you control are Plains.
|
S:Mode$ Continuous | Affected$ Land.YouCtrl | AddType$ Plains | RemoveLandTypes$ True | RemoveIntrinsicAbilities$ True | Description$ Lands you control are Plains.
|
||||||
S:Mode$ Continuous | Affected$ Card.YouOwn+nonLand | SetColor$ White | AffectedZone$ Hand,Library,Graveyard,Exile,Command | Description$ Nonland permanents you control are white. The same is true for spells you control and nonland cards you own that aren't on the battlefield.
|
S:Mode$ Continuous | Affected$ Card.YouOwn+nonLand | SetColor$ White | AffectedZone$ Hand,Library,Graveyard,Exile,Command | Description$ Nonland permanents you control are white. The same is true for spells you control and nonland cards you own that aren't on the battlefield.
|
||||||
S:Mode$ Continuous | Affected$ Card.YouCtrl+nonLand | SetColor$ White | AffectedZone$ Battlefield,Stack
|
S:Mode$ Continuous | Affected$ Card.YouCtrl+nonLand | SetColor$ White | AffectedZone$ Battlefield,Stack
|
||||||
S:Mode$ Continuous | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ All | Description$ You may spend white mana as though it were mana of any color.
|
S:Mode$ Continuous | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ Color | Description$ You may spend white mana as though it were mana of any color.
|
||||||
S:Mode$ Continuous | Affected$ You | ManaColorConversion$ Restrictive | BlueConversion$ Colorless | BlackConversion$ Colorless | RedConversion$ Colorless | GreenConversion$ Colorless | ColorlessConversion$ Colorless | Description$ You may spend other mana only as though it were colorless mana.
|
S:Mode$ Continuous | Affected$ You | ManaColorConversion$ Restrictive | BlueConversion$ Colorless | BlackConversion$ Colorless | RedConversion$ Colorless | GreenConversion$ Colorless | ColorlessConversion$ Colorless | Description$ You may spend other mana only as though it were colorless mana.
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Chromatic Orrery
|
Name:Chromatic Orrery
|
||||||
ManaCost:7
|
ManaCost:7
|
||||||
Types:Legendary Artifact
|
Types:Legendary Artifact
|
||||||
S:Mode$ Continuous | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ All | BlueConversion$ All | BlackConversion$ All | RedConversion$ All | GreenConversion$ All | ColorlessConversion$ All | Description$ You may spend mana as though it were mana of any color.
|
S:Mode$ Continuous | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ Color | BlueConversion$ Color | BlackConversion$ Color | RedConversion$ Color | GreenConversion$ Color | ColorlessConversion$ Color | Description$ You may spend mana as though it were mana of any color.
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
A:AB$ Mana | Cost$ T | Produced$ C | Amount$ 5 | SpellDescription$ Add {C}{C}{C}{C}{C}.
|
A:AB$ Mana | Cost$ T | Produced$ C | Amount$ 5 | SpellDescription$ Add {C}{C}{C}{C}{C}.
|
||||||
A:AB$ Draw | Cost$ 5 T | NumCards$ X | References$ X | SpellDescription$ Draw a card for each color among permanents you control.
|
A:AB$ Draw | Cost$ 5 T | NumCards$ X | References$ X | SpellDescription$ Draw a card for each color among permanents you control.
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ Types:Creature Djinn
|
|||||||
PT:4/4
|
PT:4/4
|
||||||
K:Flying
|
K:Flying
|
||||||
K:etbCounter:WISH:3
|
K:etbCounter:WISH:3
|
||||||
A:AB$ Dig | Cost$ 2 U U SubCounter<1/WISH> | DigNum$ 1 | Reveal$ True | NoMove$ True | RememberRevealed$ True | SubAbility$ DBPlayIT | SpellDescription$ Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it.
|
A:AB$ Dig | Cost$ 2 U U SubCounter<1/WISH> | DigNum$ 1 | Reveal$ True | NoMove$ True | ImprintRevealed$ True | SubAbility$ DBPlay | StackDescription$ {p:You} reveals the top card of their library. {p:You} may play that card without paying its mana cost or exile it. | SpellDescription$ Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it.
|
||||||
SVar:DBPlayIT:DB$ Play | Defined$ Remembered | Controller$ You | WithoutManaCost$ True | Optional$ True | RememberPlayed$ True | SubAbility$ DBExileIfNotPlayed
|
SVar:DBPlay:DB$ Play | Defined$ Imprinted | Controller$ You | WithoutManaCost$ True | Optional$ True | RememberPlayed$ True | SubAbility$ DBExileIfNotPlayed | StackDescription$ None
|
||||||
SVar:DBExileIfNotPlayed:DB$ ChangeZone | Origin$ Library | Destination$ Exile | Defined$ Remembered | DefinedPlayer$ You | ConditionCheckSVar$ DjinnX | ConditionSVarCompare$ EQ1 | SubAbility$ DBDjinnCleanup | References$ DjinnX
|
SVar:DBExileIfNotPlayed:DB$ ChangeZone | Origin$ Library | Destination$ Exile | Defined$ Imprinted | DefinedPlayer$ You | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBCleanup | StackDescription$ None
|
||||||
SVar:DBDjinnCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
|
||||||
SVar:DjinnX:Remembered$Valid Card.IsRemembered
|
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/djinn_of_wishes.jpg
|
|
||||||
Oracle:Flying\nDjinn of Wishes enters the battlefield with three wish counters on it.\n{2}{U}{U}, Remove a wish counter from Djinn of Wishes: Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it.
|
Oracle:Flying\nDjinn of Wishes enters the battlefield with three wish counters on it.\n{2}{U}{U}, Remove a wish counter from Djinn of Wishes: Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it.
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ A:SP$ Effect | Cost$ 1 W | ReplacementEffects$ FDRep | StaticAbilities$ FDManaCo
|
|||||||
SVar:DBDraw:DB$ Draw | NumCards$ 1
|
SVar:DBDraw:DB$ Draw | NumCards$ 1
|
||||||
SVar:FDRep:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Card.YouCtrl | NoTapCheck$ True | ManaReplacement$ ProduceW | Description$ Spells and abilities you control that would add colored mana add that much white mana instead.
|
SVar:FDRep:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Card.YouCtrl | NoTapCheck$ True | ManaReplacement$ ProduceW | Description$ Spells and abilities you control that would add colored mana add that much white mana instead.
|
||||||
SVar:ProduceW:R->W & B->W & U->W & G->W
|
SVar:ProduceW:R->W & B->W & U->W & G->W
|
||||||
SVar:FDManaConvertion:Mode$ Continuous | EffectZone$ Command | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ All | Description$ You may spend white mana as though it were mana of any color.
|
SVar:FDManaConvertion:Mode$ Continuous | EffectZone$ Command | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ Color | Description$ You may spend white mana as though it were mana of any color.
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
AI:RemoveDeck:Random
|
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/false_dawn.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/false_dawn.jpg
|
||||||
Oracle:Until end of turn, spells and abilities you control that would add colored mana add that much white mana instead. Until end of turn, you may spend white mana as though it were mana of any color.\nDraw a card.
|
Oracle:Until end of turn, spells and abilities you control that would add colored mana add that much white mana instead. Until end of turn, you may spend white mana as though it were mana of any color.\nDraw a card.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Inscription of Abundance
|
|||||||
ManaCost:1 G
|
ManaCost:1 G
|
||||||
Types:Instant
|
Types:Instant
|
||||||
K:Kicker:2 G
|
K:Kicker:2 G
|
||||||
A:SP$ Charm | Cost$ 1 G | MinCharmNum$ X | CharmNum$ Y | References$ X,Y | Choices$ DBPutCounter,DBGainLife,DBPump | AdditionalDescription$ If this spell was kicked, choose any number instead.
|
A:SP$ Charm | Cost$ 1 G | MinCharmNum$ X | CharmNum$ Y | References$ X,Y | Choices$ DBPutCounter,DBGainLife,DBPump | AdditionalDescription$ . If this spell was kicked, choose any number instead.
|
||||||
SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 2 | SpellDescription$ Put two +1/+1 counters on target creature.
|
SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 2 | SpellDescription$ Put two +1/+1 counters on target creature.
|
||||||
SVar:DBGainLife:DB$ GainLife | ValidTgts$ Player | TgtPrompt$ Select target player | LifeAmount$ Z | References$ Z | SpellDescription$ Target player gains X life, where X is the greatest power among creatures they control.
|
SVar:DBGainLife:DB$ GainLife | ValidTgts$ Player | TgtPrompt$ Select target player | LifeAmount$ Z | References$ Z | SpellDescription$ Target player gains X life, where X is the greatest power among creatures they control.
|
||||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | AILogic$ Fight | SubAbility$ DBFight | SpellDescription$ Target creature you control fights target creature you don't control.
|
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | AILogic$ Fight | SubAbility$ DBFight | SpellDescription$ Target creature you control fights target creature you don't control.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Inscription of Insight
|
|||||||
ManaCost:3 U
|
ManaCost:3 U
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
K:Kicker:2 U U
|
K:Kicker:2 U U
|
||||||
A:SP$ Charm | Cost$ 3 U | MinCharmNum$ X | CharmNum$ Y | References$ X,Y | Choices$ DBReturn,DBScry,DBToken | AdditionalDescription$ If this spell was kicked, choose any number instead.
|
A:SP$ Charm | Cost$ 3 U | MinCharmNum$ X | CharmNum$ Y | References$ X,Y | Choices$ DBReturn,DBScry,DBToken | AdditionalDescription$ . If this spell was kicked, choose any number instead.
|
||||||
SVar:DBReturn:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature | TgtPrompt$ Select up to two target creatures | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return up to two target creatures to their owners' hands.
|
SVar:DBReturn:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature | TgtPrompt$ Select up to two target creatures | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return up to two target creatures to their owners' hands.
|
||||||
SVar:DBScry:DB$ Scry | ScryNum$ 2 | SubAbility$ DBDraw | SpellDescription$ Scry 2, then draw two cards.
|
SVar:DBScry:DB$ Scry | ScryNum$ 2 | SubAbility$ DBDraw | SpellDescription$ Scry 2, then draw two cards.
|
||||||
SVar:DBDraw:DB$ Draw | NumCards$ 2
|
SVar:DBDraw:DB$ Draw | NumCards$ 2
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Inscription of Ruin
|
|||||||
ManaCost:2 B
|
ManaCost:2 B
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
K:Kicker:2 B B
|
K:Kicker:2 B B
|
||||||
A:SP$ Charm | Cost$ 2 B | MinCharmNum$ X | CharmNum$ Y | References$ X,Y | Choices$ DBDiscard,DBReturn,DBDestroy | AdditionalDescription$ If this spell was kicked, choose any number instead.
|
A:SP$ Charm | Cost$ 2 B | MinCharmNum$ X | CharmNum$ Y | References$ X,Y | Choices$ DBDiscard,DBReturn,DBDestroy | AdditionalDescription$ . If this spell was kicked, choose any number instead.
|
||||||
SVar:DBDiscard:DB$ Discard | ValidTgts$ Opponent | NumCards$ 2 | Mode$ TgtChoose | SpellDescription$ Target opponent discards two cards.
|
SVar:DBDiscard:DB$ Discard | ValidTgts$ Opponent | NumCards$ 2 | Mode$ TgtChoose | SpellDescription$ Target opponent discards two cards.
|
||||||
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Card.Creature+cmcLE2+YouOwn | TgtPrompt$ Select target creature card with converted mana cost 2 or less | SpellDescription$ Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield.
|
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Card.Creature+cmcLE2+YouOwn | TgtPrompt$ Select target creature card with converted mana cost 2 or less | SpellDescription$ Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield.
|
||||||
SVar:DBDestroy:DB$ Destroy | ValidTgts$ Creature.cmcLE3 | TgtPrompt$ Select target creature with converted mana cost 3 or less | SpellDescription$ Destroy target creature with converted mana cost 3 or less.
|
SVar:DBDestroy:DB$ Destroy | ValidTgts$ Creature.cmcLE3 | TgtPrompt$ Select target creature with converted mana cost 3 or less | SpellDescription$ Destroy target creature with converted mana cost 3 or less.
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ ManaCost:1 U U
|
|||||||
Types:Legendary Planeswalker Jace
|
Types:Legendary Planeswalker Jace
|
||||||
Loyalty:4
|
Loyalty:4
|
||||||
K:Kicker:2
|
K:Kicker:2
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.Self+kicked | Origin$ Any | Destination$ Battlefield | Execute$ TrigCopy | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, create a token that's a copy of CARDNAME, except it's not legendary and its starting loyalty is 1.
|
T:Mode$ ChangesZone | ValidCard$ Card.Self+kicked | Origin$ Any | Destination$ Battlefield | Execute$ TrigCopy | TriggerDescription$ When CARDNAME enters the battlefield, if NICKNAME was kicked, create a token that's a copy of CARDNAME, except it's not legendary and its starting loyalty is 1.
|
||||||
SVar:TrigCopy:DB$ CopyPermanent | Defined$ Self | NonLegendary$ True | SetLoyalty$ 1
|
SVar:TrigCopy:DB$ CopyPermanent | Defined$ Self | NonLegendary$ True | SetLoyalty$ 1
|
||||||
A:AB$ Scry | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ScryNum$ 1 | SpellDescription$ Scry 1.
|
A:AB$ Scry | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ScryNum$ 2 | SpellDescription$ Scry 2.
|
||||||
A:AB$ Draw | Cost$ AddCounter<0/LOYALTY> | Planeswalker$ True | NumCards$ 1 | Reveal$ True | RememberDrawn$ True | SubAbility$ DBRemoveCounters | SpellDescription$ Draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from CARDNAME.
|
A:AB$ Draw | Cost$ AddCounter<0/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 1 | Reveal$ True | RememberDrawn$ True | SubAbility$ DBRemoveCounters | SpellDescription$ Draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from CARDNAME.
|
||||||
SVar:DBRemoveCounters:DB$ RemoveCounter | Defined$ Self | CounterType$ LOYALTY | CounterNum$ X | References$ X | SubAbility$ DBCleanup
|
SVar:DBRemoveCounters:DB$ RemoveCounter | Defined$ Self | CounterType$ LOYALTY | CounterNum$ X | References$ X | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:X:Remembered$CardManaCost
|
SVar:X:Remembered$CardManaCost
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
Oracle:Kicker {2}\nWhen Jace, Mirror Mage enters the battlefield, if Jace was kicked, create a token that's a copy of Jace, Mirror Mage, except it's not legendary and its starting loyalty is 1.\n[+1]: Scry 1.\n[0]: Draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from Jace, Mirror Mage.
|
Oracle:Kicker {2}\nWhen Jace, Mirror Mage enters the battlefield, if Jace was kicked, create a token that's a copy of Jace, Mirror Mage, except it's not legendary and its starting loyalty is 1.\n[+1]: Scry 2.\n[0]: Draw a card and reveal it. Remove a number of loyalty counters equal to that card's converted mana cost from Jace, Mirror Mage.
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ ManaCost:2 W
|
|||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
K:Enchant creature
|
K:Enchant creature
|
||||||
A:SP$ Attach | Cost$ 2 W | ValidTgts$ Creature
|
A:SP$ Attach | Cost$ 2 W | ValidTgts$ Creature
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddTrigger$ LightOfPromiseTrig | AddSVar$ LightOfPromisePutCounter & X | Description$ Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddTrigger$ LightOfPromiseTrig | AddSVar$ LightOfPromisePutCounter & LightOfPromiseAmount | Description$ Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."
|
||||||
SVar:LightOfPromiseTrig:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ LightOfPromisePutCounter | TriggerDescription$ Whenever you gain life, put that many +1/+1 counters on CARDNAME.
|
SVar:LightOfPromiseTrig:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ LightOfPromisePutCounter | TriggerDescription$ Whenever you gain life, put that many +1/+1 counters on this creature.
|
||||||
SVar:LightOfPromisePutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | References$ X
|
SVar:LightOfPromisePutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ LightOfPromiseAmount | References$ LightOfPromiseAmount
|
||||||
SVar:X:TriggerCount$LifeAmount
|
SVar:LightOfPromiseAmount:TriggerCount$LifeAmount
|
||||||
DeckHints:Ability$LifeGain
|
DeckNeeds:Ability$LifeGain
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
Oracle:Enchant creature\nEnchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."
|
Oracle:Enchant creature\nEnchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:6
|
|||||||
Types:Artifact
|
Types:Artifact
|
||||||
S:Mode$ Continuous | Affected$ Permanent | AddType$ Artifact | Description$ All permanents are artifact in addition to their other types.
|
S:Mode$ Continuous | Affected$ Permanent | AddType$ Artifact | Description$ All permanents are artifact in addition to their other types.
|
||||||
S:Mode$ Continuous| Affected$ Card | SetColor$ Colorless | AffectedZone$ Battlefield,Hand,Library,Graveyard,Exile,Stack,Command | Description$ All cards that aren't on the battlefield, spells, and permanents are colorless.
|
S:Mode$ Continuous| Affected$ Card | SetColor$ Colorless | AffectedZone$ Battlefield,Hand,Library,Graveyard,Exile,Stack,Command | Description$ All cards that aren't on the battlefield, spells, and permanents are colorless.
|
||||||
S:Mode$ Continuous | Affected$ Player | ManaColorConversion$ Additive | WhiteConversion$ All | BlueConversion$ All | BlackConversion$ All | RedConversion$ All | GreenConversion$ All | ColorlessConversion$ All | Description$ Players may spend mana as though it were mana of any color.
|
S:Mode$ Continuous | Affected$ Player | ManaColorConversion$ Additive | WhiteConversion$ Color | BlueConversion$ Color | BlackConversion$ Color | RedConversion$ Color | GreenConversion$ Color | ColorlessConversion$ Color | Description$ Players may spend mana as though it were mana of any color.
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/mycosynth_lattice.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/mycosynth_lattice.jpg
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ SVar:X:Count$xPaid
|
|||||||
SVar:Y:Count$RememberedSize
|
SVar:Y:Count$RememberedSize
|
||||||
SVar:XLands:Number$0
|
SVar:XLands:Number$0
|
||||||
DeckHas:Ability$Sacrifice
|
DeckHas:Ability$Sacrifice
|
||||||
|
AI:RemoveDeck:All
|
||||||
Oracle:Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter the battlefield tapped this turn.
|
Oracle:Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter the battlefield tapped this turn.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Kor Cleric
|
|||||||
PT:3/3
|
PT:3/3
|
||||||
K:Lifelink
|
K:Lifelink
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ Whenever CARDNAME or another Cleric you control dies, return target Cleric with lesser converted mana cost from your graveyard to the battlefield.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ Whenever CARDNAME or another Cleric you control dies, return target Cleric with lesser converted mana cost from your graveyard to the battlefield.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZone$ Battlefield | ValidCard$ Cleric.YouCtrl+Other | Execute$ TrigReturn | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another Cleric you control dies, return target Cleric card with lesser converted mana cost from your graveyard to the battlefield.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Cleric.YouCtrl+Other | Execute$ TrigReturn | Secondary$ True | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME or another Cleric you control dies, return target Cleric card with lesser converted mana cost from your graveyard to the battlefield.
|
||||||
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Cleric.cmcLTX+YouOwn | TgtPrompt$ Choose target Cleric card with lesser converted mana cost | References$ X | Origin$ Graveyard | Destination$ Battlefield
|
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Cleric.cmcLTX+YouOwn | TgtPrompt$ Choose target Cleric card with lesser converted mana cost | References$ X | Origin$ Graveyard | Destination$ Battlefield
|
||||||
SVar:X:TriggeredCard$CardManaCost
|
SVar:X:TriggeredCard$CardManaCost
|
||||||
DeckHas:Ability$Graveyard
|
DeckHas:Ability$Graveyard
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ Name:Rushing River
|
|||||||
ManaCost:2 U
|
ManaCost:2 U
|
||||||
Types:Instant
|
Types:Instant
|
||||||
K:Kicker:Sac<1/Land>
|
K:Kicker:Sac<1/Land>
|
||||||
A:SP$ ChangeZone | Cost$ 2 U | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | TargetMin$ X | TargetMax$ X | References$ X | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target nonland permanent to its owner's hand. If CARDNAME was kicked, return another target nonland permanent to its owner's hand.
|
A:SP$ ChangeZone | Cost$ 2 U | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | TargetMin$ X | TargetMax$ X | References$ X,Z | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target nonland permanent to its owner's hand. If CARDNAME was kicked, return another target nonland permanent to its owner's hand.
|
||||||
SVar:X:Count$Kicked.2.1
|
SVar:X:Count$Kicked.2.1
|
||||||
SVar:NeedsToPlayKickedVar:Z GE2
|
SVar:NeedsToPlayKickedVar:Z GE2
|
||||||
SVar:Z:Count$Valid Permanent.nonLand+OppCtrl
|
SVar:Z:Count$Valid Permanent.nonLand+OppCtrl
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/rushing_river.jpg
|
|
||||||
Oracle:Kicker—Sacrifice a land. (You may sacrifice a land in addition to any other costs as you cast this spell.)\nReturn target nonland permanent to its owner's hand. If Rushing River was kicked, return another target nonland permanent to its owner's hand.
|
Oracle:Kicker—Sacrifice a land. (You may sacrifice a land in addition to any other costs as you cast this spell.)\nReturn target nonland permanent to its owner's hand. If Rushing River was kicked, return another target nonland permanent to its owner's hand.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Trinisphere
|
Name:Trinisphere
|
||||||
ManaCost:3
|
ManaCost:3
|
||||||
Types:Artifact
|
Types:Artifact
|
||||||
S:Mode$ SetCost | ValidCard$ Card | Type$ Spell | Amount$ Min3 | CheckSVar$ X | SVarCompare$ EQ1 | Description$ As long as CARDNAME is untapped, each spell that would cost less than three mana to cast costs three mana to cast.
|
S:Mode$ SetCost | ValidCard$ Card | Type$ Spell | Amount$ 3 | RaiseTo$ True | CheckSVar$ X | SVarCompare$ EQ1 | Description$ As long as CARDNAME is untapped, each spell that would cost less than three mana to cast costs three mana to cast.
|
||||||
SVar:X:Count$Valid Card.Self+untapped
|
SVar:X:Count$Valid Card.Self+untapped
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
|
|||||||
8
forge-gui/res/cardsfolder/upcoming/amareth_the_lustrous.txt
Executable file
8
forge-gui/res/cardsfolder/upcoming/amareth_the_lustrous.txt
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Amareth, the Lustrous
|
||||||
|
ManaCost:3 G U W
|
||||||
|
Types:Legendary Creature Dragon
|
||||||
|
PT:6/6
|
||||||
|
K:Flying
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Permanent.YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ Whenever another permanent enters the battlefield under your control, look at the top card of your library. If it shares a card type with that permanent, you may reveal that card and put it into your hand.
|
||||||
|
SVar:TrigDig:DB$ Dig | DigNum$ 1 | ForceRevealToController$ True | ChangeNum$ 1 | Optional$ True | LibraryPosition$ 0 | LibraryPosition2$ 0 | ChangeValid$ Card.sharesCardTypeWith TriggeredCard
|
||||||
|
Oracle:Flying\nWhenever another permanent enters the battlefield under your control, look at the top card of your library. If it shares a card type with that permanent, you may reveal that card and put it into your hand.
|
||||||
9
forge-gui/res/cardsfolder/upcoming/amphin_mutineer.txt
Executable file
9
forge-gui/res/cardsfolder/upcoming/amphin_mutineer.txt
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Amphin Mutineer
|
||||||
|
ManaCost:3 U
|
||||||
|
Types:Creature Salamander Pirate
|
||||||
|
PT:3/3
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile up to one target non-Salamander creature. That creature's controller creates a 4/3 blue Salamander Warrior creature token.
|
||||||
|
SVar:TrigExile:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.nonSalamander | TgtPrompt$ Select up to one target non-Salamander creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBToken
|
||||||
|
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_4_3_salamander_warrior | TokenOwner$ TargetedController
|
||||||
|
K:Encore:4 U U
|
||||||
|
Oracle:When Amphin Mutineer enters the battlefield, exile up to one target non-Salamander creature. That creature's controller creates a 4/3 blue Salamander Warrior creature token.\nEncore {4}{U}{U} ({4}{U}{U}, Exile this card from your graveyard: For each opponent, create a token copy that attacks that opponent this turn if able. They gain haste. Sacrifice them at the beginning of the next end step. Activate only as a sorcery.)
|
||||||
10
forge-gui/res/cardsfolder/upcoming/apex_devastator.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/apex_devastator.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Apex Devastator
|
||||||
|
ManaCost:8 G G
|
||||||
|
Types:Creature Chimera Hydra
|
||||||
|
PT:10/10
|
||||||
|
K:Cascade
|
||||||
|
K:Cascade
|
||||||
|
K:Cascade
|
||||||
|
K:Cascade
|
||||||
|
SVar:PlayMain1:True
|
||||||
|
Oracle:Cascade, cascade, cascade, cascade (When you cast this spell, exile cards from the top of your library until you exile a nonland card that costs less. You may cast it without paying its mana cost. Put the exiled cards on the bottom in a random order. Then do it again.)
|
||||||
9
forge-gui/res/cardsfolder/upcoming/archon_of_coronation.txt
Executable file
9
forge-gui/res/cardsfolder/upcoming/archon_of_coronation.txt
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Archon of Coronation
|
||||||
|
ManaCost:4 W W
|
||||||
|
Types:Creature Archon
|
||||||
|
PT:5/5
|
||||||
|
K:Flying
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
|
||||||
|
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
|
||||||
|
S:Mode$ Continuous | Affected$ You | Condition$ You.isMonarch | AddKeyword$ Damage doesn't cause you to lose life. | Description$ As long as you're the monarch, damage doesn't cause you to lose life.
|
||||||
|
Oracle:Flying\nWhen Archon of Coronation enters the battlefield, you become the monarch.\nAs long as you're the monarch, damage doesn't cause you to lose life.
|
||||||
9
forge-gui/res/cardsfolder/upcoming/aurora_phoenix.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/aurora_phoenix.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Aurora Phoenix
|
||||||
|
ManaCost:4 R R
|
||||||
|
Types:Creature Phoenix
|
||||||
|
PT:5/3
|
||||||
|
K:Flying
|
||||||
|
K:Cascade
|
||||||
|
T:Mode$ SpellCast | ValidCard$ Card.withCascade | ValidActivatingPlayer$ You | TriggerZones$ Graveyard | Execute$ TrigReturn | TriggerDescription$ Whenever you cast a spell with cascade, return CARDNAME from your graveyard to your hand.
|
||||||
|
SVar:TrigReturn:DB$ ChangeZone | Defined$ Self | Origin$ Graveyard | Destination$ Hand
|
||||||
|
Oracle:Flying\nCascade (When you cast this spell, exile cards from the top of your library until you exile a nonland card that costs less. You may cast it without paying its mana cost. Put the exiled cards on the bottom of your library in a random order.)\nWhenever you cast a spell with cascade, return Aurora Phoenix from your graveyard to your hand.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Belbe, Corrupted Observer
|
||||||
|
ManaCost:B G
|
||||||
|
Types:Legendary Creature Elf Zombie
|
||||||
|
PT:2/2
|
||||||
|
T:Mode$ Phase | Phase$ Main2 | TriggerZones$ Battlefield | Execute$ TrigMana | TriggerDescription$ At the beginning of each player's postcombat main phase, that player adds {C}{C} for each of your opponents that lost life this turn.
|
||||||
|
SVar:TrigMana:DB$ Mana | Produced$ C | Amount$ X | References$ X | Defined$ TriggeredPlayer
|
||||||
|
SVar:X:PlayerCountOpponents$HasPropertyLostLifeThisTurn/Twice
|
||||||
|
Oracle:At the beginning of each player's postcombat main phase, that player adds {C}{C} for each of your opponents that lost life this turn.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Bladegriff Prototype
|
||||||
|
ManaCost:5
|
||||||
|
Types:Artifact Creature Griffin
|
||||||
|
PT:3/2
|
||||||
|
K:Flying
|
||||||
|
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDestroy | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, destroy target nonland permanent of that player's choice that one of your opponents controls.
|
||||||
|
SVar:TrigDestroy:DB$ Destroy | TargetingPlayer$ TriggeredTarget | ValidTgts$ Permanent.nonLand+OppCtrl
|
||||||
|
Oracle:Flying\nWhenever Bladegriff Prototype deals combat damage to a player, destroy target nonland permanent of that player's choice that one of your opponents controls.
|
||||||
16
forge-gui/res/cardsfolder/upcoming/blim_comedic_genius.txt
Normal file
16
forge-gui/res/cardsfolder/upcoming/blim_comedic_genius.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Name:Blim, Comedic Genius
|
||||||
|
ManaCost:2 B R
|
||||||
|
Types:Legendary Creature Imp
|
||||||
|
PT:4/3
|
||||||
|
K:Flying
|
||||||
|
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | CombatDamage$ True | TriggerZone$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player gains control of a permanent you control of your choice. Then each player loses life and discards cards equal to the number of permanents they control but don't own.
|
||||||
|
SVar:TrigPump:DB$ ChooseCard | Choices$ Permanent.YouCtrl | ChoiceTitle$ Choose a permanent you control for damaged player to gain control of | Defined$ You | SubAbility$ DBControl
|
||||||
|
SVar:DBControl:DB$ GainControl | Defined$ ChosenCard | NewController$ TriggeredTarget | SubAbility$ DBRepeatEach
|
||||||
|
SVar:DBRepeatEach:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBLoseLife | SubAbility$ DBCleanup
|
||||||
|
SVar:DBLoseLife:DB$ LoseLife | Defined$ Player.IsRemembered | LifeAmount$ X | References$ X | SubAbility$ DBDiscard
|
||||||
|
SVar:DBDiscard:DB$ Discard | Defined$ Player.IsRemembered | NumCards$ X | References$ X | Mode$ TgtChoose
|
||||||
|
SVar:X:Count$Valid Permanent.RememberedPlayerCtrl/Minus.Y
|
||||||
|
SVar:Y:Count$Valid Permanent.RememberedPlayerCtrl+RememberedPlayerOwn
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
|
||||||
|
DeckHints:Ability$Token
|
||||||
|
Oracle:Flying\nWhenever Blim, Comedic Genius deals combat damage to a player, that player gains control of a permanent you control of your choice. Then each player loses life and discards cards equal to the number of permanents they control but don't own.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
Name:Breeches, Brazen Plunderer
|
||||||
|
ManaCost:3 R
|
||||||
|
Types:Legendary Creature Goblin Pirate
|
||||||
|
PT:3/3
|
||||||
|
K:Menace
|
||||||
|
K:Partner
|
||||||
|
T:Mode$ DamageAll | ValidSource$ Pirate.YouCtrl | ValidTarget$ Opponent | TriggerZones$ Battlefield | Execute$ TrigExileTop | TriggerDescription$ Whenever one or more Pirates you control deal damage to your opponents, exile the top card of each of those opponents' libraries. You may play those cards this turn, and you may spend mana as though it were mana of any color to cast those spells.
|
||||||
|
SVar:TrigExileTop:DB$ Dig | DigNum$ 1 | ChangeNum$ All | Defined$ TriggeredTargets | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect
|
||||||
|
SVar:DBEffect:DB$ Effect | StaticAbilities$ EffPlay | EffectOwner$ You | RememberObjects$ Remembered | ForgetOnMoved$ Exile | SubAbility$ DBCleanup
|
||||||
|
SVar:EffPlay:Mode$ Continuous | MayPlay$ True | MayPlayIgnoreType$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play those cards this turn, and you may spend mana as though it were mana of any color to cast those spells.
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
DeckHints:Type$Pirate
|
||||||
|
Oracle:Menace\nWhenever one or more Pirates you control deal damage to your opponents, exile the top card of each of those opponents' libraries. You may play those cards this turn, and you may spend mana as though it were mana of any color to cast those spells.\nPartner (You can have two commanders if both have partner.)
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Brinelin, the Moon Kraken
|
||||||
|
ManaCost:6 U U
|
||||||
|
Types:Legendary Creature Kraken
|
||||||
|
PT:6/8
|
||||||
|
K:Partner
|
||||||
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigReturn | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield or whenever you cast a spell with converted mana cost 6 or greater, you may return target nonland permanent to its owner's hand.
|
||||||
|
T:Mode$ SpellCast | ValidCard$ Card.cmcGE6 | ValidActivatingPlayer$ You | Execute$ TrigReturn | TriggerZones$ Battlefield | OptionalDecider$ You | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or whenever you cast a spell with converted mana cost 6 or greater, you may return target nonland permanent to its owner's hand.
|
||||||
|
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Hand
|
||||||
|
Oracle:When Brinelin, the Moon Kraken enters the battlefield or whenever you cast a spell with converted mana cost 6 or greater, you may return target nonland permanent to its owner's hand.\nPartner (You can have two commanders if both have partner.)
|
||||||
13
forge-gui/res/cardsfolder/upcoming/colfenor_the_last_yew.txt
Executable file
13
forge-gui/res/cardsfolder/upcoming/colfenor_the_last_yew.txt
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
Name:Colfenor, the Last Yew
|
||||||
|
ManaCost:3 W B G
|
||||||
|
Types:Legendary Creature Treefolk Shaman
|
||||||
|
PT:3/7
|
||||||
|
K:Vigilance
|
||||||
|
K:Reach
|
||||||
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.Other+YouCtrl | RememberTriggeringCard$ True | TriggerZones$ Battlefield | Execute$ TrigChange | TriggerDescription$ Whenever CARDNAME or another creature you control dies, return up to one other target creature card with lesser toughness from your graveyard to your hand.
|
||||||
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChange | Secondary$ True | TriggerController$ TriggeredCardController | TriggerDescription$ Whenever CARDNAME or another creature you control dies, return up to one other target creature card with lesser toughness from your graveyard to your hand.
|
||||||
|
SVar:TrigChange:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 1 | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Creature.Other+IsNotRemembered+toughnessLTX+YouOwn | TgtPrompt$ Select up to one other target creature card with lesser toughness from your graveyard to return to your hand | References$ X | SubAbility$ DBCleanup
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
SVar:X:TriggeredCard$CardToughness
|
||||||
|
DeckHas:Ability$Graveyard
|
||||||
|
Oracle:Vigilance, reach\nWhenever Colfenor, the Last Yew or another creature you control dies, return up to one other target creature card with lesser toughness from your graveyard to your hand.
|
||||||
11
forge-gui/res/cardsfolder/upcoming/court_of_ambition.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/court_of_ambition.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Name:Court of Ambition
|
||||||
|
ManaCost:2 B B
|
||||||
|
Types:Enchantment
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
|
||||||
|
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
|
||||||
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRepeat | TriggerDescription$ At the beginning of your upkeep, each opponent loses 3 life unless they discard a card. If you’re the monarch, instead each opponent loses 6 life unless they discard two cards.
|
||||||
|
SVar:TrigRepeat:DB$ RepeatEach | RepeatPlayers$ Opponent | RepeatSubAbility$ DBDrain
|
||||||
|
SVar:DBDrain:DB$ LoseLife | Defined$ Player.IsRemembered | LifeAmount$ X | References$ X,Y | UnlessCost$ Discard<Y/Card> | UnlessPayer$ Player.IsRemembered
|
||||||
|
SVar:X:Count$Monarch.6.3
|
||||||
|
SVar:Y:Count$Monarch.2.1
|
||||||
|
Oracle:When Court of Ambition enters the battlefield, you become the monarch. \n At the beginning of your upkeep, each opponent loses 3 life unless they discard a card. If you’re the monarch, instead each opponent loses 6 life unless they discard two cards.
|
||||||
10
forge-gui/res/cardsfolder/upcoming/court_of_cunning.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/court_of_cunning.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Court of Cunning
|
||||||
|
ManaCost:1 U U
|
||||||
|
Types:Enchantment
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
|
||||||
|
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
|
||||||
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ At the beginning of your upkeep, any number of target players each mill two cards. If you're the monarch, each of those players mills ten cards instead. (To mill a card, a player puts the top card of their library into their graveyard.)
|
||||||
|
SVar:TrigMill:DB$ Mill | ValidTgts$ Player | TgtPrompt$ Select target player | TargetMin$ 0 | TargetMax$ MaxTgt | NumCards$ X | References$ X,MaxTgt
|
||||||
|
SVar:X:Count$Monarch.10.2
|
||||||
|
SVar:MaxTgt:PlayerCountPlayers$Amount
|
||||||
|
Oracle:When Court of Cunning enters the battlefield, you become the monarch.\nAt the beginning of your upkeep, any number of target players each mill two cards. If you're the monarch, each of those players mills ten cards instead. (To mill a card, a player puts the top card of their library into their graveyard.)
|
||||||
12
forge-gui/res/cardsfolder/upcoming/emberwilde_captain.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/emberwilde_captain.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name:Emberwilde Captain
|
||||||
|
ManaCost:3 R
|
||||||
|
Types:Creature Djinn Pirate
|
||||||
|
PT:4/2
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
|
||||||
|
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
|
||||||
|
T:Mode$ AttackersDeclared | AttackingPlayer$ Player.Opponent | AttackedTarget$ You | NoResolvingCheck$ True | CheckDefinedPlayer$ You.isMonarch | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever an opponent attacks you while you're the monarch, CARDNAME deals damage to that player equal to the number of cards in their hand.
|
||||||
|
SVar:TrigPump:DB$ Pump | RememberObjects$ TriggeredAttackingPlayer | SubAbility$ DBDmg
|
||||||
|
SVar:DBDmg:DB$ DealDamage | Defined$ Remembered | NumDmg$ X | References$ X | SubAbility$ DBCleanup
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
SVar:X:Count$ValidHand Card.RememberedPlayerCtrl
|
||||||
|
Oracle:When Emberwilde Captain enters the battlefield, you become the monarch.\nWhenever an opponent attacks you while you're the monarch, Emberwilde Captain deals damage to that player equal to the number of cards in their hand.
|
||||||
9
forge-gui/res/cardsfolder/upcoming/eyeblight_cullers.txt
Executable file
9
forge-gui/res/cardsfolder/upcoming/eyeblight_cullers.txt
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Eyeblight Cullers
|
||||||
|
ManaCost:4 B
|
||||||
|
Types:Creature Elf Warrior
|
||||||
|
PT:3/3
|
||||||
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, create three 1/1 green Elf Warrior creature tokens, then mill three cards. (Put the top three cards of your library into your graveyard.)
|
||||||
|
SVar:TrigToken:DB$ Token | TokenAmount$ 3 | TokenScript$ g_1_1_elf_warrior | TokenOwner$ You | SubAbility$ DBMill
|
||||||
|
SVar:DBMill:DB$ Mill | NumCards$ 3 | Defined$ You
|
||||||
|
DeckHas:Ability$Graveyard & Ability$Token
|
||||||
|
Oracle:When Eyeblight Cullers dies, create three 1/1 green Elf Warrior creature tokens, then mill three cards. (Put the top three cards of your library into your graveyard.)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user