mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge branch 'master' into Attractions
# Conflicts: # forge-core/src/main/java/forge/deck/DeckFormat.java
This commit is contained in:
@@ -52,6 +52,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
private ColorSet deckbuildingColors;
|
||||
private String meldWith;
|
||||
private String partnerWith;
|
||||
private boolean addsWildCardColor;
|
||||
private boolean custom;
|
||||
|
||||
public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
|
||||
@@ -70,6 +71,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
aiHints = cah;
|
||||
meldWith = "";
|
||||
partnerWith = "";
|
||||
addsWildCardColor = false;
|
||||
|
||||
//calculate color identity
|
||||
byte colMask = calculateColorIdentity(mainPart);
|
||||
@@ -92,6 +94,8 @@ public final class CardRules implements ICardCharacteristics {
|
||||
colorIdentity = newRules.colorIdentity;
|
||||
meldWith = newRules.meldWith;
|
||||
partnerWith = newRules.partnerWith;
|
||||
addsWildCardColor = newRules.addsWildCardColor;
|
||||
tokens = newRules.tokens;
|
||||
}
|
||||
|
||||
private static byte calculateColorIdentity(final ICardFace face) {
|
||||
@@ -203,20 +207,20 @@ public final class CardRules implements ICardCharacteristics {
|
||||
@Override
|
||||
public ManaCost getManaCost() {
|
||||
switch (splitType.getAggregationMethod()) {
|
||||
case COMBINE:
|
||||
return ManaCost.combine(mainPart.getManaCost(), otherPart.getManaCost());
|
||||
default:
|
||||
return mainPart.getManaCost();
|
||||
case COMBINE:
|
||||
return ManaCost.combine(mainPart.getManaCost(), otherPart.getManaCost());
|
||||
default:
|
||||
return mainPart.getManaCost();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorSet getColor() {
|
||||
switch (splitType.getAggregationMethod()) {
|
||||
case COMBINE:
|
||||
return ColorSet.fromMask(mainPart.getColor().getColor() | otherPart.getColor().getColor());
|
||||
default:
|
||||
return mainPart.getColor();
|
||||
case COMBINE:
|
||||
return ColorSet.fromMask(mainPart.getColor().getColor() | otherPart.getColor().getColor());
|
||||
default:
|
||||
return mainPart.getColor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,10 +234,10 @@ public final class CardRules implements ICardCharacteristics {
|
||||
|
||||
public boolean canCastWithAvailable(byte colorCode) {
|
||||
switch (splitType.getAggregationMethod()) {
|
||||
case COMBINE:
|
||||
return canCastFace(mainPart, colorCode) || canCastFace(otherPart, colorCode);
|
||||
default:
|
||||
return canCastFace(mainPart, colorCode);
|
||||
case COMBINE:
|
||||
return canCastFace(mainPart, colorCode) || canCastFace(otherPart, colorCode);
|
||||
default:
|
||||
return canCastFace(mainPart, colorCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,10 +257,10 @@ public final class CardRules implements ICardCharacteristics {
|
||||
@Override
|
||||
public String getOracleText() {
|
||||
switch (splitType.getAggregationMethod()) {
|
||||
case COMBINE:
|
||||
return mainPart.getOracleText() + "\r\n\r\n" + otherPart.getOracleText();
|
||||
default:
|
||||
return mainPart.getOracleText();
|
||||
case COMBINE:
|
||||
return mainPart.getOracleText() + "\r\n\r\n" + otherPart.getOracleText();
|
||||
default:
|
||||
return mainPart.getOracleText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +272,9 @@ public final class CardRules implements ICardCharacteristics {
|
||||
}
|
||||
|
||||
public boolean canBeCommander() {
|
||||
if (mainPart.getOracleText().contains(" is your commander, choose a color before the game begins.")) {
|
||||
addsWildCardColor = true;
|
||||
}
|
||||
if (mainPart.getOracleText().contains("can be your commander") || canBeBackground()) {
|
||||
return true;
|
||||
}
|
||||
@@ -385,11 +392,15 @@ public final class CardRules implements ICardCharacteristics {
|
||||
return partnerWith;
|
||||
}
|
||||
|
||||
public boolean getAddsWildCardColor() {
|
||||
return addsWildCardColor;
|
||||
}
|
||||
|
||||
// vanguard card fields, they don't use sides.
|
||||
private int deltaHand;
|
||||
private int deltaLife;
|
||||
|
||||
private List<String> tokens;
|
||||
private List<String> tokens = Collections.emptyList();
|
||||
|
||||
public List<String> getTokens() {
|
||||
return tokens;
|
||||
@@ -435,6 +446,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
private CardSplitType altMode = CardSplitType.None;
|
||||
private String meldWith = "";
|
||||
private String partnerWith = "";
|
||||
private boolean addsWildCardColor = false;
|
||||
private String handLife = null;
|
||||
private String normalizedName = "";
|
||||
private Set<String> supportedFunctionalVariants = null;
|
||||
@@ -473,6 +485,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
this.has = null;
|
||||
this.meldWith = "";
|
||||
this.partnerWith = "";
|
||||
this.addsWildCardColor = false;
|
||||
this.normalizedName = "";
|
||||
this.supportedFunctionalVariants = null;
|
||||
this.tokens = Lists.newArrayList();
|
||||
@@ -497,7 +510,10 @@ public final class CardRules implements ICardCharacteristics {
|
||||
result.setNormalizedName(this.normalizedName);
|
||||
result.meldWith = this.meldWith;
|
||||
result.partnerWith = this.partnerWith;
|
||||
result.tokens = tokens.isEmpty() ? Collections.emptyList() : tokens;
|
||||
result.addsWildCardColor = this.addsWildCardColor;
|
||||
if (!tokens.isEmpty()) {
|
||||
result.tokens = tokens;
|
||||
}
|
||||
if (StringUtils.isNotBlank(handLife))
|
||||
result.setVanguardProperties(handLife);
|
||||
result.supportedFunctionalVariants = this.supportedFunctionalVariants;
|
||||
@@ -567,7 +583,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
} else if ("AltName".equals(key)) {
|
||||
face.setAltName(value);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
if ("Colors".equals(key)) {
|
||||
@@ -777,7 +793,10 @@ public final class CardRules implements ICardCharacteristics {
|
||||
}
|
||||
|
||||
public boolean hasStartOfKeyword(final String k) {
|
||||
for (final String inst : mainPart.getKeywords()) {
|
||||
return hasStartOfKeyword(k, mainPart);
|
||||
}
|
||||
public boolean hasStartOfKeyword(final String k, ICardFace cf) {
|
||||
for (final String inst : cf.getKeywords()) {
|
||||
final String[] parts = inst.split(":");
|
||||
if (parts[0].equals(k)) {
|
||||
return true;
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.ComparableOp;
|
||||
@@ -186,7 +187,7 @@ public final class CardRulesPredicates {
|
||||
return new Predicate<CardRules>() {
|
||||
@Override
|
||||
public boolean apply(final CardRules card) {
|
||||
return card.hasStartOfKeyword(keyword);
|
||||
return Iterables.any(card.getAllFaces(), cf -> cf != null && card.hasStartOfKeyword(keyword, cf));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,6 +131,11 @@ public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte>, Ser
|
||||
return (this.myColor & ~colormask) == 0;
|
||||
}
|
||||
|
||||
/** This returns the colors that colormask contains that are not in color */
|
||||
public ColorSet getMissingColors(final byte colormask) {
|
||||
return fromMask(this.myColor & ~colormask);
|
||||
}
|
||||
|
||||
/** Operand has no other colors except defined by this. */
|
||||
public boolean containsAllColorsFrom(final int colorProfile) {
|
||||
return (~this.myColor & colorProfile) == 0;
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.StaticData;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.CardType;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.ICardFace;
|
||||
import forge.deck.generation.DeckGenPool;
|
||||
import forge.deck.generation.DeckGeneratorBase.FilterCMC;
|
||||
@@ -126,7 +127,7 @@ public enum DeckFormat {
|
||||
private final Predicate<CardRules> cardPoolFilter;
|
||||
private final Predicate<PaperCard> paperCardPoolFilter;
|
||||
private final static String ADVPROCLAMATION = "Advantageous Proclamation";
|
||||
private final static String SOVREALM = "Sovereign's Realm";
|
||||
// private final static String SOVREALM = "Sovereign's Realm";
|
||||
|
||||
DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0, Predicate<CardRules> cardPoolFilter0, Predicate<PaperCard> paperCardPoolFilter0) {
|
||||
mainRange = mainRange0;
|
||||
@@ -212,17 +213,19 @@ public enum DeckFormat {
|
||||
|
||||
int min = getMainRange().getMinimum();
|
||||
int max = getMainRange().getMaximum();
|
||||
boolean noBasicLands = false;
|
||||
// boolean noBasicLands = false;
|
||||
|
||||
// Adjust minimum base on number of Advantageous Proclamation or similar cards
|
||||
CardPool conspiracies = deck.get(DeckSection.Conspiracy);
|
||||
if (conspiracies != null) {
|
||||
min -= (5 * conspiracies.countByName(ADVPROCLAMATION));
|
||||
noBasicLands = conspiracies.countByName(SOVREALM) > 0;
|
||||
// Commented out to remove warnings from the code.
|
||||
// noBasicLands = conspiracies.countByName(SOVREALM) > 0;
|
||||
}
|
||||
|
||||
if (hasCommander()) {
|
||||
byte cmdCI = 0;
|
||||
int wildColors = 0;
|
||||
if (equals(DeckFormat.Oathbreaker)) { // 1 Oathbreaker and 1 Signature Spell
|
||||
PaperCard oathbreaker = deck.getOathbreaker();
|
||||
if (oathbreaker == null) {
|
||||
@@ -235,8 +238,7 @@ public enum DeckFormat {
|
||||
return "has too many commanders";
|
||||
}
|
||||
cmdCI = oathbreaker.getRules().getColorIdentity().getColor();
|
||||
}
|
||||
else { // 1 Commander or 2 Partner Commanders
|
||||
} else { // 1 Commander or 2 Partner Commanders
|
||||
final List<PaperCard> commanders = deck.getCommanders();
|
||||
|
||||
if (commanders.isEmpty()) {
|
||||
@@ -252,6 +254,7 @@ public enum DeckFormat {
|
||||
return "has an illegal commander";
|
||||
}
|
||||
cmdCI |= pc.getRules().getColorIdentity().getColor();
|
||||
wildColors += pc.getRules().getAddsWildCardColor() ? 1 : 0;
|
||||
}
|
||||
|
||||
// special check for Partner
|
||||
@@ -280,8 +283,14 @@ public enum DeckFormat {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!cp.getKey().getRules().getColorIdentity().hasNoColorsExcept(cmdCI)) {
|
||||
erroneousCI.add(cp.getKey());
|
||||
ColorSet missingColors = cp.getKey().getRules().getColorIdentity().getMissingColors(cmdCI);
|
||||
if (missingColors.countColors() > 0) {
|
||||
if (missingColors.countColors() <= wildColors) {
|
||||
wildColors -= missingColors.countColors();
|
||||
cmdCI |= missingColors.getColor();
|
||||
} else {
|
||||
erroneousCI.add(cp.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deck.has(DeckSection.Sideboard)) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade).
|
||||
@@ -38,7 +37,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* @author Forge
|
||||
*/
|
||||
public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard, Serializable {
|
||||
public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
|
||||
private static final long serialVersionUID = 2942081982620691205L;
|
||||
|
||||
// Reference to rules
|
||||
|
||||
@@ -50,7 +50,6 @@ import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Visitor;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -327,7 +326,9 @@ public class Game {
|
||||
int plId = 0;
|
||||
for (RegisteredPlayer psc : players0) {
|
||||
IGameEntitiesFactory factory = (IGameEntitiesFactory)psc.getPlayer();
|
||||
Player pl = factory.createIngamePlayer(this, plId++);
|
||||
// If the Registered Player already has a pre-assigned ID, use that. Otherwise, assign a new one.
|
||||
Integer id = psc.getId();
|
||||
Player pl = factory.createIngamePlayer(this, id == null ? plId++ : id);
|
||||
allPlayers.add(pl);
|
||||
ingamePlayers.add(pl);
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ public final class GameActionUtil {
|
||||
// do only non intrinsic
|
||||
if (iSa.isSpell() && !iSa.isIntrinsic()) {
|
||||
alternatives.add(iSa);
|
||||
alternatives.addAll(getMayPlaySpellOptions(iSa, source, activator, altCostOnly));
|
||||
alternatives.addAll(getMayPlaySpellOptions(iSa, stackCopy, activator, altCostOnly));
|
||||
// currently only AltCost get added this way
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +84,11 @@ public abstract class SpellAbilityEffect {
|
||||
String spellDesc = CardTranslation.translateSingleDescriptionText(rawSDesc,
|
||||
sa.getHostCard().getName());
|
||||
|
||||
int idx = spellDesc.indexOf("(");
|
||||
if (idx > 0) { //trim reminder text from StackDesc
|
||||
spellDesc = spellDesc.substring(0, spellDesc.indexOf("(") - 1);
|
||||
//trim reminder text from StackDesc
|
||||
int idxL = spellDesc.indexOf(" (");
|
||||
int idxR = spellDesc.indexOf(")");
|
||||
if (idxL > 0 && idxR > idxL) {
|
||||
spellDesc = spellDesc.replace(spellDesc.substring(idxL, idxR + 1), "");
|
||||
}
|
||||
|
||||
if (reps != null) {
|
||||
|
||||
@@ -26,9 +26,10 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(Lang.joinHomogenous(getDefinedPlayersOrTargeted(sa)));
|
||||
sb.append("chooses from a list.");
|
||||
final List<Player> players = getDefinedPlayersOrTargeted(sa);
|
||||
|
||||
sb.append(Lang.joinHomogenous(players));
|
||||
sb.append(players.size() == 1 ? " chooses" : " choose").append(" from a list.");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -120,8 +120,8 @@ public class DebuffEffect extends SpellAbilityEffect {
|
||||
ProtectionFromColor = true;
|
||||
}
|
||||
if (ProtectionFromColor) {
|
||||
// Split "Protection from all colors" into extra Protection from <color>
|
||||
String allColors = "Protection from all colors";
|
||||
// Split "Protection from each color" into extra Protection from <color>
|
||||
String allColors = "Protection from each color";
|
||||
if (tgtC.hasKeyword(allColors)) {
|
||||
final List<String> allColorsProtect = Lists.newArrayList();
|
||||
|
||||
@@ -134,7 +134,7 @@ public class DebuffEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// Extra for Spectra Ward
|
||||
allColors = "Protection:Card.nonColorless:all colors:Aura";
|
||||
allColors = "Protection:Card.nonColorless:each color:Aura";
|
||||
if (tgtC.hasKeyword(allColors)) {
|
||||
final List<String> allColorsProtect = Lists.newArrayList();
|
||||
|
||||
|
||||
@@ -180,12 +180,8 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
for (final Player p : repeatPlayers) {
|
||||
if (optional) {
|
||||
if (!p.getController().confirmAction(repeat, null, sa.getParam("RepeatOptionalMessage"), null)) {
|
||||
continue;
|
||||
} else if (sa.hasParam("RememberDeciders")) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
if (optional && !p.getController().confirmAction(repeat, null, sa.getParam("RepeatOptionalMessage"), null)) {
|
||||
continue;
|
||||
}
|
||||
if (nextTurn) {
|
||||
game.getCleanup().addUntil(p, new GameCommand() {
|
||||
|
||||
@@ -2258,7 +2258,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
// If no colon exists in Madness keyword, it must have been granted and assumed the cost from host
|
||||
sbLong.append("Madness ").append(this.getManaCost()).append(" (").append(inst.getReminderText());
|
||||
sbLong.append(")").append("\r\n");
|
||||
} else if (keyword.startsWith("Emerge") || keyword.startsWith("Reflect")) {
|
||||
} else if (keyword.startsWith("Reflect")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1]));
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
@@ -2325,6 +2325,16 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
sbLong.append("\r\n");
|
||||
} else if (keyword.startsWith("Emerge")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]);
|
||||
if (k.length > 2) {
|
||||
sbLong.append(" from ").append(k[2].toLowerCase());
|
||||
}
|
||||
sbLong.append(" ").append(ManaCostParser.parse(k[1]));
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
sbLong.append("\r\n");
|
||||
|
||||
} else if (inst.getKeyword().equals(Keyword.COMPANION)) {
|
||||
sbLong.append("Companion — ");
|
||||
sbLong.append(((Companion)inst).getDescription());
|
||||
@@ -2484,7 +2494,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|| keyword.equals("Undaunted") || keyword.startsWith("Monstrosity")
|
||||
|| keyword.startsWith("Embalm") || keyword.equals("Prowess")
|
||||
|| keyword.startsWith("Eternalize") || keyword.startsWith("Reinforce")
|
||||
|| keyword.startsWith("Champion") || keyword.startsWith("Prowl") || keyword.startsWith("Adapt")
|
||||
|| keyword.startsWith("Champion") || keyword.startsWith("Freerunning") || keyword.startsWith("Prowl") || keyword.startsWith("Adapt")
|
||||
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Chapter")
|
||||
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|
||||
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")
|
||||
@@ -3002,7 +3012,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
} else if (keyword.startsWith("Starting intensity")) {
|
||||
sbAfter.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
|
||||
} else if (keyword.startsWith("Escalate") || keyword.startsWith("Buyback")
|
||||
|| keyword.startsWith("Prowl")) {
|
||||
|| keyword.startsWith("Freerunning") || keyword.startsWith("Prowl")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String manacost = k[1];
|
||||
final Cost cost = new Cost(manacost, false);
|
||||
@@ -6757,7 +6767,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
pW = true;
|
||||
protectKey += "W";
|
||||
}
|
||||
} else if (kw.contains("all colors")) {
|
||||
} else if (kw.contains("each color")) {
|
||||
protectKey += "allcolors:";
|
||||
} else if (kw.equals("Protection from everything")) {
|
||||
protectKey += "everything:";
|
||||
|
||||
@@ -676,7 +676,7 @@ public class CardFactoryUtil {
|
||||
validSource = "Green" + (damage ? "Source" : "");
|
||||
} else if (protectType.equals("colorless")) {
|
||||
validSource = "Colorless" + (damage ? "Source" : "");
|
||||
} else if (protectType.equals("all colors")) {
|
||||
} else if (protectType.equals("each color")) {
|
||||
validSource = "nonColorless" + (damage ? "Source" : "");
|
||||
} else if (protectType.equals("everything")) {
|
||||
return "";
|
||||
@@ -2830,16 +2830,22 @@ public class CardFactoryUtil {
|
||||
} else if (keyword.startsWith("Emerge")) {
|
||||
final String[] kw = keyword.split(":");
|
||||
String costStr = kw[1];
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
String validStr = kw.length > 2 ? kw[2] : "Creature";
|
||||
String desc = "(Emerge";
|
||||
if (kw.length > 2) {
|
||||
desc += " from " + kw[2].toLowerCase();
|
||||
}
|
||||
desc += ")";
|
||||
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(new Cost(costStr, false));
|
||||
|
||||
newSA.getRestrictions().setIsPresent("Creature.YouCtrl+CanBeSacrificedBy");
|
||||
newSA.getRestrictions().setIsPresent(validStr + ".YouCtrl+CanBeSacrificedBy");
|
||||
newSA.putParam("Secondary", "True");
|
||||
newSA.setAlternativeCost(AlternativeCost.Emerge);
|
||||
|
||||
newSA.setDescription(sa.getDescription() + " (Emerge)");
|
||||
newSA.putParam("AfterDescription", "(Emerge)");
|
||||
newSA.setDescription(sa.getDescription() + " " + desc);
|
||||
newSA.putParam("AfterDescription", desc);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Embalm")) {
|
||||
@@ -3105,6 +3111,27 @@ public class CardFactoryUtil {
|
||||
// instantiate attach ability
|
||||
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Freerunning")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost freerunningCost = new Cost(k[1], false);
|
||||
final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(freerunningCost);
|
||||
|
||||
if (host.isInstant() || host.isSorcery()) {
|
||||
newSA.putParam("Secondary", "True");
|
||||
}
|
||||
newSA.putParam("PrecostDesc", "Freerunning");
|
||||
newSA.putParam("CostDesc", ManaCostParser.parse(k[1]));
|
||||
|
||||
// makes new SpellDescription
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(newSA.getCostDescription());
|
||||
sb.append("(").append(inst.getReminderText()).append(")");
|
||||
newSA.setDescription(sb.toString());
|
||||
|
||||
newSA.setAlternativeCost(AlternativeCost.Freerunning);
|
||||
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Fuse") && card.getStateName().equals(CardStateName.Original)) {
|
||||
final SpellAbility sa = AbilityFactory.buildFusedAbility(card.getCard());
|
||||
card.addSpellAbility(sa);
|
||||
@@ -3900,7 +3927,8 @@ public class CardFactoryUtil {
|
||||
final String[] k = keyword.split(":");
|
||||
|
||||
sbDesc.append(" from ").append(k[2]);
|
||||
sbValid.append("| ValidSource$ ").append(k[1]);
|
||||
final String param = k[2].contains("abilities") ? "ValidSA$ " : "ValidSource$ ";
|
||||
sbValid.append("| ").append(param).append(k[1]);
|
||||
}
|
||||
|
||||
String effect = "Mode$ CantTarget | ValidCard$ Card.Self | Secondary$ True"
|
||||
|
||||
@@ -2120,6 +2120,14 @@ public class CardProperty {
|
||||
}
|
||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||
|
||||
return nameList.contains(card.getName());
|
||||
} else if (property.equals("NotedNameAetherSearcher")) {
|
||||
String names = sourceController.getDraftNotes().get("Aether Searcher");
|
||||
if (names == null || names.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||
|
||||
return nameList.contains(card.getName());
|
||||
} else if (property.equals("NotedTypes")) {
|
||||
// Should Paliano Vanguard be hardcoded here or part of the property?
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class CardUtil {
|
||||
"Cycling", "Echo", "Kicker", "Flashback", "Madness", "Morph",
|
||||
"Affinity", "Entwine", "Splice", "Ninjutsu",
|
||||
"Transmute", "Replicate", "Recover", "Squad", "Suspend", "Aura swap",
|
||||
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl",
|
||||
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "Freerunning",
|
||||
"Reinforce", "Unearth", "Level up", "Miracle", "Overload", "Cleave",
|
||||
"Scavenge", "Encore", "Bestow", "Outlast", "Dash", "Surge", "Emerge", "Hexproof:",
|
||||
"etbCounter", "Reflect", "Ward").build();
|
||||
|
||||
@@ -326,8 +326,28 @@ public class CardView extends GameEntityView {
|
||||
void updateDamage(Card c) {
|
||||
set(TrackableProperty.Damage, c.getDamage());
|
||||
updateLethalDamage(c);
|
||||
//update CrackOverlay (currently 16 overlays)
|
||||
set(TrackableProperty.CrackOverlay, c.getDamage() > 0 ? MyRandom.getRandom().nextInt(16) : 0);
|
||||
//get crackoverlay by level of damage light 0, medium 1, heavy 2, max 3
|
||||
int randCrackLevel = 0;
|
||||
if (c.getDamage() > 0) {
|
||||
switch (c.getDamage()) {
|
||||
case 1:
|
||||
case 2:
|
||||
randCrackLevel = 0;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
randCrackLevel = 1;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
randCrackLevel = 2;
|
||||
break;
|
||||
default:
|
||||
randCrackLevel = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
set(TrackableProperty.CrackOverlay, randCrackLevel);
|
||||
}
|
||||
|
||||
public int getAssignedDamage() {
|
||||
@@ -1466,6 +1486,7 @@ public class CardView extends GameEntityView {
|
||||
public String getKeywordKey() { return get(TrackableProperty.KeywordKey); }
|
||||
public String getProtectionKey() { return get(TrackableProperty.ProtectionKey); }
|
||||
public String getHexproofKey() { return get(TrackableProperty.HexproofKey); }
|
||||
public boolean hasAnnihilator() { return get(TrackableProperty.HasAnnihilator); }
|
||||
public boolean hasDeathtouch() { return get(TrackableProperty.HasDeathtouch); }
|
||||
public boolean hasToxic() { return get(TrackableProperty.HasToxic); }
|
||||
public boolean hasDevoid() { return get(TrackableProperty.HasDevoid); }
|
||||
@@ -1473,6 +1494,7 @@ public class CardView extends GameEntityView {
|
||||
public boolean hasDivideDamage() { return get(TrackableProperty.HasDivideDamage); }
|
||||
public boolean hasDoubleStrike() { return get(TrackableProperty.HasDoubleStrike); }
|
||||
public boolean hasDoubleTeam() { return get(TrackableProperty.HasDoubleTeam); }
|
||||
public boolean hasExalted() { return get(TrackableProperty.HasExalted); }
|
||||
public boolean hasFirstStrike() { return get(TrackableProperty.HasFirstStrike); }
|
||||
public boolean hasFlying() { return get(TrackableProperty.HasFlying); }
|
||||
public boolean hasFear() { return get(TrackableProperty.HasFear); }
|
||||
@@ -1542,6 +1564,7 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
void updateKeywords(Card c, CardState state) {
|
||||
c.updateKeywordsCache(state);
|
||||
set(TrackableProperty.HasAnnihilator, c.hasKeyword(Keyword.ANNIHILATOR, state));
|
||||
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
||||
set(TrackableProperty.HasToxic, c.hasKeyword(Keyword.TOXIC, state));
|
||||
set(TrackableProperty.HasDevoid, c.hasKeyword(Keyword.DEVOID, state));
|
||||
@@ -1549,6 +1572,7 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.HasDivideDamage, c.hasKeyword("You may assign CARDNAME's combat damage divided as " +
|
||||
"you choose among defending player and/or any number of creatures they control."));
|
||||
set(TrackableProperty.HasDoubleStrike, c.hasKeyword(Keyword.DOUBLE_STRIKE, state));
|
||||
set(TrackableProperty.HasExalted, c.hasKeyword(Keyword.EXALTED, state));
|
||||
set(TrackableProperty.HasFirstStrike, c.hasKeyword(Keyword.FIRST_STRIKE, state));
|
||||
set(TrackableProperty.HasFlying, c.hasKeyword(Keyword.FLYING, state));
|
||||
set(TrackableProperty.HasFear, c.hasKeyword(Keyword.FEAR, state));
|
||||
|
||||
@@ -223,7 +223,15 @@ public class CostAdjustment {
|
||||
// Reduce cost
|
||||
int sumGeneric = 0;
|
||||
if (sa.hasParam("ReduceCost")) {
|
||||
sumGeneric += AbilityUtils.calculateAmount(originalCard, sa.getParam("ReduceCost"), sa);
|
||||
String cst = sa.getParam("ReduceCost");
|
||||
String amt = sa.getParamOrDefault("ReduceAmount", cst);
|
||||
int num = AbilityUtils.calculateAmount(originalCard, amt, sa);
|
||||
|
||||
if (sa.hasParam("ReduceAmount") && num > 0) {
|
||||
cost.subtractManaCost(new ManaCost(new ManaCostParser(Strings.repeat(cst + " ", num))));
|
||||
} else {
|
||||
sumGeneric += num;
|
||||
}
|
||||
}
|
||||
|
||||
while (!reduceAbilities.isEmpty()) {
|
||||
@@ -379,9 +387,15 @@ public class CostAdjustment {
|
||||
}
|
||||
|
||||
private static void adjustCostByEmerge(final ManaCostBeingPaid cost, final SpellAbility sa) {
|
||||
CardCollectionView canEmerge = CardLists.filter(sa.getActivatingPlayer().getCreaturesInPlay(), CardPredicates.canBeSacrificedBy(sa, false));
|
||||
String kw = sa.getKeyword().getOriginal();
|
||||
String k[] = kw.split(":");
|
||||
String validStr = k.length > 2 ? k[2] : "Creature";
|
||||
Player p = sa.getActivatingPlayer();
|
||||
CardCollectionView canEmerge = CardLists.filter(p.getCardsIn(ZoneType.Battlefield),
|
||||
CardPredicates.restriction(validStr, p, sa.getHostCard(), sa),
|
||||
CardPredicates.canBeSacrificedBy(sa, false));
|
||||
|
||||
final CardCollectionView toSacList = sa.getHostCard().getController().getController().choosePermanentsToSacrifice(sa, 0, 1, canEmerge, "Creature");
|
||||
final CardCollectionView toSacList = p.getController().choosePermanentsToSacrifice(sa, 0, 1, canEmerge, validStr);
|
||||
|
||||
if (toSacList.isEmpty()) {
|
||||
return;
|
||||
|
||||
13
forge-game/src/main/java/forge/game/keyword/Emerge.java
Normal file
13
forge-game/src/main/java/forge/game/keyword/Emerge.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package forge.game.keyword;
|
||||
|
||||
public class Emerge extends KeywordWithCostAndType {
|
||||
protected void parse(String details) {
|
||||
final String[] k = details.split(":");
|
||||
if (k.length < 2) {
|
||||
super.parse("Creature:" + k[0]);
|
||||
} else {
|
||||
// Flip parameters
|
||||
super.parse(k[1] + ":" + k[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public enum Keyword {
|
||||
DREDGE("Dredge", KeywordWithAmount.class, false, "If you would draw a card, instead you may put exactly {%d:card} from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card."),
|
||||
ECHO("Echo", KeywordWithCost.class, false, "At the beginning of your upkeep, if this permanent came under your control since the beginning of your last upkeep, sacrifice it unless you pay %s."),
|
||||
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 mana value."),
|
||||
EMERGE("Emerge", Emerge.class, false, "You may cast this spell by sacrificing {1:%2$s} and paying the emerge cost reduced by that %2$s's mana value."),
|
||||
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."),
|
||||
ENLIST("Enlist", SimpleKeyword.class, false, "As this creature attacks, you may tap a nonattacking creature you control without summoning sickness. When you do, add its power to this creature’s until end of turn."),
|
||||
@@ -95,6 +95,7 @@ public enum Keyword {
|
||||
FOR_MIRRODIN("For Mirrodin", SimpleKeyword.class, false, "When this Equipment enters the battlefield, create a 2/2 red Rebel creature token, then attach this to it."),
|
||||
FORETELL("Foretell", KeywordWithCost.class, false, "During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost."),
|
||||
FORTIFY("Fortify", KeywordWithCost.class, false, "%s: Attach to target land you control. Fortify only as a sorcery."),
|
||||
FREERUNNING("Freerunning", KeywordWithCost.class, false, "You may cast this spell for its freerunning cost if you dealt combat damage to a player this turn with an Assassin or commander."),
|
||||
FRENZY("Frenzy", KeywordWithAmount.class, false, "Whenever this creature attacks and isn't blocked, it gets +%d/+0 until end of turn."),
|
||||
FRIENDS_FOREVER("Friends forever", Partner.class, true, "You can have two commanders if both have friends forever."),
|
||||
FUSE("Fuse", SimpleKeyword.class, true, "You may cast one or both halves of this card from your hand."),
|
||||
|
||||
@@ -2150,6 +2150,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return !game.getDamageDoneThisTurn(true, true, sb.toString(), "Player", null, this, null).isEmpty();
|
||||
}
|
||||
|
||||
public final boolean hasFreerunning() {
|
||||
return !game.getDamageDoneThisTurn(true, true, "Card.Assassin+YouCtrl,Card.IsCommander+YouCtrl", "Player", null, this, null).isEmpty();
|
||||
}
|
||||
|
||||
public final void setLibrarySearched(final int l) {
|
||||
numLibrarySearchedOwn = l;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package forge.game.player;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -15,6 +14,7 @@ import forge.util.Expressions;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -264,6 +264,10 @@ public class PlayerProperty {
|
||||
if (source.getChosenPlayer() == null || !source.getChosenPlayer().equals(player)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("NotedDefender")) {
|
||||
String tracker = player.getDraftNotes().getOrDefault("Cogwork Tracker", "");
|
||||
|
||||
return Iterables.contains(Arrays.asList(tracker.split(",")), String.valueOf(player));
|
||||
} else if (property.startsWith("life")) {
|
||||
int life = player.getLife();
|
||||
int amount = AbilityUtils.calculateAmount(source, property.substring(6), spellAbility);
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package forge.game.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
@@ -16,6 +10,11 @@ import forge.game.GameType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RegisteredPlayer {
|
||||
private final Deck originalDeck; // never return or modify this instance (it's a reference to game resources)
|
||||
private Deck currentDeck;
|
||||
@@ -38,6 +37,7 @@ public class RegisteredPlayer {
|
||||
private List<PaperCard> vanguardAvatars = null;
|
||||
private PaperCard planeswalker = null;
|
||||
private int teamNumber = -1; // members of teams with negative id will play FFA.
|
||||
private Integer id = null;
|
||||
private boolean randomFoil = false;
|
||||
private boolean enableETBCountersEffect = false;
|
||||
|
||||
@@ -46,6 +46,14 @@ public class RegisteredPlayer {
|
||||
restoreDeck();
|
||||
}
|
||||
|
||||
public final Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public final void setId(Integer id0) {
|
||||
id = id0;
|
||||
}
|
||||
|
||||
public final Deck getDeck() {
|
||||
return currentDeck;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public enum AlternativeCost {
|
||||
Evoke,
|
||||
Flashback,
|
||||
Foretold,
|
||||
Freerunning,
|
||||
Madness,
|
||||
MTMtE, // More Than Meets the Eye (Transformers Universes Beyond)
|
||||
Mutate,
|
||||
|
||||
@@ -1549,6 +1549,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return isAlternativeCost(AlternativeCost.Evoke);
|
||||
}
|
||||
|
||||
public final boolean isFreerunning() {
|
||||
return isAlternativeCost(AlternativeCost.Freerunning);
|
||||
}
|
||||
|
||||
public final boolean isMadness() {
|
||||
return isAlternativeCost(AlternativeCost.Madness);
|
||||
}
|
||||
|
||||
@@ -444,6 +444,11 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (sa.isFreerunning()) {
|
||||
if (!activator.hasFreerunning()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.getIsPresent() != null) {
|
||||
FCollection<GameObject> list;
|
||||
if (getPresentDefined() != null) {
|
||||
|
||||
@@ -70,7 +70,8 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
}
|
||||
|
||||
/** {@inheritDoc}
|
||||
* @param runParams*/
|
||||
* @param runParams
|
||||
**/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
final SpellAbility spellAbility = (SpellAbility) runParams.get(AbilityKey.SpellAbility);
|
||||
|
||||
@@ -144,6 +144,7 @@ public enum TrackableProperty {
|
||||
CountBasicLandTypes(TrackableTypes.IntegerType),
|
||||
|
||||
KeywordKey(TrackableTypes.StringType),
|
||||
HasAnnihilator(TrackableTypes.BooleanType),
|
||||
HasDeathtouch(TrackableTypes.BooleanType),
|
||||
HasToxic(TrackableTypes.BooleanType),
|
||||
HasDevoid(TrackableTypes.BooleanType),
|
||||
@@ -151,6 +152,7 @@ public enum TrackableProperty {
|
||||
HasDivideDamage(TrackableTypes.BooleanType),
|
||||
HasDoubleStrike(TrackableTypes.BooleanType),
|
||||
HasDoubleTeam(TrackableTypes.BooleanType),
|
||||
HasExalted(TrackableTypes.BooleanType),
|
||||
HasFirstStrike(TrackableTypes.BooleanType),
|
||||
HasFlying(TrackableTypes.BooleanType),
|
||||
HasFear(TrackableTypes.BooleanType),
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
package forge.screens.home.sanctioned;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
@@ -38,6 +28,14 @@ import forge.screens.deckeditor.views.VStatistics;
|
||||
import forge.toolbox.FOptionPane;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controls the draft submenu in the home UI.
|
||||
*
|
||||
@@ -142,6 +140,12 @@ public enum CSubmenuDraft implements ICDoc {
|
||||
|
||||
FModel.getGauntletMini().resetGauntletDraft();
|
||||
String duelType = (String)VSubmenuDraft.SINGLETON_INSTANCE.getCbOpponent().getSelectedItem();
|
||||
|
||||
if (duelType == null) {
|
||||
FOptionPane.showErrorDialog("Please select duel types for the draft match.", "Missing opponent items");
|
||||
return;
|
||||
}
|
||||
|
||||
final DeckGroup opponentDecks = FModel.getDecks().getDraft().get(humanDeck.getName());
|
||||
if (gauntlet) {
|
||||
if ("Gauntlet".equals(duelType)) {
|
||||
@@ -161,7 +165,7 @@ public enum CSubmenuDraft implements ICDoc {
|
||||
}
|
||||
});
|
||||
|
||||
List<Deck> aiDecks = Lists.newArrayList();
|
||||
Map<Integer, Deck> aiMap = Maps.newHashMap();
|
||||
if (VSubmenuDraft.SINGLETON_INSTANCE.isSingleSelected()) {
|
||||
// Restore Zero Indexing
|
||||
final int aiIndex = Integer.parseInt(duelType)-1;
|
||||
@@ -169,28 +173,43 @@ public enum CSubmenuDraft implements ICDoc {
|
||||
if (aiDeck == null) {
|
||||
throw new IllegalStateException("Draft: Computer deck is null!");
|
||||
}
|
||||
aiDecks.add(aiDeck);
|
||||
|
||||
aiMap.put(aiIndex, aiDeck);
|
||||
} else {
|
||||
final int numOpponents = Integer.parseInt(duelType);
|
||||
|
||||
List<Deck> randomOpponents = Lists.newArrayList(opponentDecks.getAiDecks());
|
||||
Collections.shuffle(randomOpponents);
|
||||
aiDecks = randomOpponents.subList(0, numOpponents);
|
||||
for(Deck d : aiDecks) {
|
||||
if (d == null) {
|
||||
int maxDecks = opponentDecks.getAiDecks().size();
|
||||
if (numOpponents > maxDecks) {
|
||||
throw new IllegalStateException("Draft: Not enough decks for the number of opponents!");
|
||||
}
|
||||
|
||||
List<Integer> aiIndices = Lists.newArrayList();
|
||||
for(int i = 0; i < maxDecks; i++) {
|
||||
aiIndices.add(i);
|
||||
}
|
||||
Collections.shuffle(aiIndices);
|
||||
aiIndices = aiIndices.subList(0, numOpponents);
|
||||
|
||||
for(int i : aiIndices) {
|
||||
final Deck aiDeck = opponentDecks.getAiDecks().get(i);
|
||||
if (aiDeck == null) {
|
||||
throw new IllegalStateException("Draft: Computer deck is null!");
|
||||
}
|
||||
|
||||
aiMap.put(i + 1, aiDeck);
|
||||
}
|
||||
}
|
||||
|
||||
final List<RegisteredPlayer> starter = new ArrayList<>();
|
||||
// Human is 0
|
||||
final RegisteredPlayer human = new RegisteredPlayer(humanDeck.getDeck()).setPlayer(GamePlayerUtil.getGuiPlayer());
|
||||
starter.add(human);
|
||||
for(Deck aiDeck : aiDecks) {
|
||||
starter.add(new RegisteredPlayer(aiDeck).setPlayer(GamePlayerUtil.createAiPlayer()));
|
||||
}
|
||||
for (final RegisteredPlayer pl : starter) {
|
||||
pl.assignConspiracies();
|
||||
human.setId(0);
|
||||
for(Map.Entry<Integer, Deck> aiDeck : aiMap.entrySet()) {
|
||||
RegisteredPlayer aiPlayer = new RegisteredPlayer(aiDeck.getValue()).setPlayer(GamePlayerUtil.createAiPlayer());
|
||||
aiPlayer.setId(aiDeck.getKey());
|
||||
starter.add(aiPlayer);
|
||||
aiPlayer.assignConspiracies();
|
||||
}
|
||||
|
||||
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
|
||||
|
||||
@@ -94,10 +94,12 @@ public class CardFaceSymbols {
|
||||
//ability icons
|
||||
MANA_IMAGES.put("commander", FSkin.getImage(FSkinProp.IMG_ABILITY_COMMANDER));
|
||||
MANA_IMAGES.put("ringbearer", FSkin.getImage(FSkinProp.IMG_ABILITY_RINGBEARER));
|
||||
MANA_IMAGES.put("annihilator", FSkin.getImage(FSkinProp.IMG_ABILITY_ANNIHILATOR));
|
||||
MANA_IMAGES.put("toxic", FSkin.getImage(FSkinProp.IMG_ABILITY_TOXIC));
|
||||
MANA_IMAGES.put("deathtouch", FSkin.getImage(FSkinProp.IMG_ABILITY_DEATHTOUCH));
|
||||
MANA_IMAGES.put("defender", FSkin.getImage(FSkinProp.IMG_ABILITY_DEFENDER));
|
||||
MANA_IMAGES.put("doublestrike", FSkin.getImage(FSkinProp.IMG_ABILITY_DOUBLE_STRIKE));
|
||||
MANA_IMAGES.put("exalted", FSkin.getImage(FSkinProp.IMG_ABILITY_EXALTED));
|
||||
MANA_IMAGES.put("firststrike", FSkin.getImage(FSkinProp.IMG_ABILITY_FIRST_STRIKE));
|
||||
MANA_IMAGES.put("fear", FSkin.getImage(FSkinProp.IMG_ABILITY_FEAR));
|
||||
MANA_IMAGES.put("flash", FSkin.getImage(FSkinProp.IMG_ABILITY_FLASH));
|
||||
|
||||
@@ -575,6 +575,14 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
|
||||
CardFaceSymbols.drawAbilitySymbol("firststrike", g, abiX, abiY, abiScale, abiScale);
|
||||
abiY += abiSpace;
|
||||
}
|
||||
if (card.getCurrentState().hasAnnihilator()) {
|
||||
CardFaceSymbols.drawAbilitySymbol("annihilator", g, abiX, abiY, abiScale, abiScale);
|
||||
abiY += abiSpace;
|
||||
}
|
||||
if (card.getCurrentState().hasExalted()) {
|
||||
CardFaceSymbols.drawAbilitySymbol("exalted", g, abiX, abiY, abiScale, abiScale);
|
||||
abiY += abiSpace;
|
||||
}
|
||||
if (card.getCurrentState().hasDeathtouch()) {
|
||||
CardFaceSymbols.drawAbilitySymbol("deathtouch", g, abiX, abiY, abiScale, abiScale);
|
||||
abiY += abiSpace;
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.List;
|
||||
public class BoosterDraftTest implements IBoosterDraft {
|
||||
|
||||
private int n = 3;
|
||||
private int round = 1;
|
||||
|
||||
@Override
|
||||
@Test(timeOut = 1000)
|
||||
@@ -43,6 +44,11 @@ public class BoosterDraftTest implements IBoosterDraft {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRound() {
|
||||
return round;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardPool nextChoice() {
|
||||
this.n--;
|
||||
@@ -95,4 +101,9 @@ public class BoosterDraftTest implements IBoosterDraft {
|
||||
public LimitedPlayer getNeighbor(LimitedPlayer p, boolean left) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitedPlayer getPlayer(int i) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class RewardSprite extends CharacterSprite {
|
||||
if (data != null) {
|
||||
rewards = JSONStringLoader.parse(RewardData[].class, data, default_reward);
|
||||
} else { //Shouldn't happen, but make sure it doesn't fly by.
|
||||
System.err.printf("Reward data is null. Using a default reward.");
|
||||
System.err.print("Reward data is null. Using a default reward.");
|
||||
rewards = JSONStringLoader.parse(RewardData[].class, default_reward, default_reward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ public class AdventureEventData implements Serializable {
|
||||
} else {
|
||||
description += "\n";
|
||||
}
|
||||
description += String.format("Prizes\n3 round wins: 500 gold\n2 round wins: 200 gold\n1 round win: 100 gold\n");
|
||||
description += "Prizes\n3 round wins: 500 gold\n2 round wins: 200 gold\n1 round win: 100 gold\n";
|
||||
description += "Finishing event will award an unsellable copy of each card in your Jumpstart deck.";
|
||||
}
|
||||
return description;
|
||||
|
||||
@@ -1106,10 +1106,13 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
if (canOnlyBePartnerCommander(card)) {
|
||||
return; //don't auto-change commander unexpectedly
|
||||
}
|
||||
DeckSectionPage main = getMainDeckPage();
|
||||
if (main == null)
|
||||
return;
|
||||
if (!cardManager.isInfinite()) {
|
||||
removeCard(card);
|
||||
}
|
||||
getMainDeckPage().addCard(card);
|
||||
main.addCard(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -238,12 +238,18 @@ public class ArenaScene extends UIScene implements IAfterMatch {
|
||||
arenaPlane.addActor(lost);
|
||||
}
|
||||
|
||||
boolean started = false;
|
||||
|
||||
private void startRound() {
|
||||
if (started)
|
||||
return;
|
||||
started = true;
|
||||
DuelScene duelScene = DuelScene.instance();
|
||||
EnemySprite enemy = enemies.get(enemies.size - 1);
|
||||
FThreads.invokeInEdtNowOrLater(() -> {
|
||||
Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
duelScene.initDuels(WorldStage.getInstance().getPlayerSprite(), enemy);
|
||||
started = false;
|
||||
duelScene.initDuels(WorldStage.getInstance().getPlayerSprite(), enemy, true, null);
|
||||
Forge.switchScene(duelScene);
|
||||
}, Forge.takeScreenshot(), true, false, false, false, "", Current.player().avatar(), enemy.getAtlasPath(), Current.player().getName(), enemy.getName()));
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ public class NewGameScene extends MenuScene {
|
||||
modeNames[i] = modes.get(i).getName();
|
||||
mode.setTextList(modeNames);
|
||||
|
||||
gender.setTextList(new String[]{Forge.getLocalizer().getInstance().getMessage("lblMale"), Forge.getLocalizer().getInstance().getMessage("lblFemale")});
|
||||
gender.setTextList(new String[]{Forge.getLocalizer().getMessage("lblMale"), Forge.getLocalizer().getMessage("lblFemale")});
|
||||
gender.addListener(new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
@@ -217,7 +217,7 @@ public class NewGameScene extends MenuScene {
|
||||
}
|
||||
Forge.switchScene(GameScene.instance());
|
||||
};
|
||||
Forge.setTransitionScreen(new TransitionScreen(runnable, null, false, true, "Generating World..."));
|
||||
Forge.setTransitionScreen(new TransitionScreen(runnable, null, false, true, Forge.getLocalizer().getMessage("lblGeneratingWorld")));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +273,7 @@ public class PlayerStatisticScene extends UIScene {
|
||||
if (g != null) //skip variants
|
||||
continue;
|
||||
}
|
||||
a.updateTrophyImage();
|
||||
TextureRegion textureRegion = new TextureRegion(((FBufferedImage) a.getImage()).getTexture());
|
||||
textureRegion.flip(false, true);
|
||||
Image image = new Image(textureRegion);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package forge.adventure.scene;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.*;
|
||||
@@ -99,7 +98,6 @@ public class SaveLoadScene extends UIScene {
|
||||
ui.onButtonPress("return", SaveLoadScene.this::back);
|
||||
difficulty.setSelectedIndex(1);
|
||||
difficulty.setAlignment(Align.center);
|
||||
difficulty.getStyle().fontColor = Color.GOLD;
|
||||
difficulty.setX(scrollPane.getWidth() - difficulty.getWidth() + 5);
|
||||
difficulty.setY(scrollPane.getTop() - difficulty.getHeight() - 5);
|
||||
}
|
||||
@@ -196,14 +194,19 @@ public class SaveLoadScene extends UIScene {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean loaded = false;
|
||||
|
||||
public void loadSave() {
|
||||
if (loaded)
|
||||
return;
|
||||
loaded = true;
|
||||
switch (mode) {
|
||||
case Save:
|
||||
if (TileMapScene.instance().currentMap().isInMap()) {
|
||||
//Access to screen should be disabled, but stop the process just in case.
|
||||
//Saving needs to be disabled inside maps until we can capture and load exact map state
|
||||
//Otherwise location based events for quests can be skipped by saving and then loading outside the map
|
||||
Dialog noSave = createGenericDialog("", "!!GAME NOT SAVED!!\nManual saving is only available on the world map","OK",null, null, null);
|
||||
Dialog noSave = createGenericDialog("", Forge.getLocalizer().getMessage("lblGameNotSaved"), Forge.getLocalizer().getMessage("lblOK"),null, null, null);
|
||||
showDialog(noSave);
|
||||
return;
|
||||
}
|
||||
@@ -226,17 +229,19 @@ public class SaveLoadScene extends UIScene {
|
||||
showDialog(saveDialog);
|
||||
stage.setKeyboardFocus(textInput);
|
||||
}
|
||||
loaded = false;
|
||||
break;
|
||||
case Load:
|
||||
try {
|
||||
Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
loaded = false;
|
||||
if (WorldSave.load(currentSlot)) {
|
||||
SoundSystem.instance.changeBackgroundTrack();
|
||||
Forge.switchScene(GameScene.instance());
|
||||
} else {
|
||||
Forge.clearTransitionScreen();
|
||||
}
|
||||
}, null, false, true, "Loading World..."));
|
||||
}, null, false, true, Forge.getLocalizer().getMessage("lblLoadingWorld")));
|
||||
} catch (Exception e) {
|
||||
Forge.clearTransitionScreen();
|
||||
}
|
||||
@@ -244,6 +249,7 @@ public class SaveLoadScene extends UIScene {
|
||||
case NewGamePlus:
|
||||
try {
|
||||
Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
loaded = false;
|
||||
if (WorldSave.load(currentSlot)) {
|
||||
WorldSave.getCurrentSave().clearChanges();
|
||||
WorldSave.getCurrentSave().getWorld().generateNew(0);
|
||||
@@ -261,8 +267,9 @@ public class SaveLoadScene extends UIScene {
|
||||
} else {
|
||||
Forge.clearTransitionScreen();
|
||||
}
|
||||
}, null, false, true, "Generating World..."));
|
||||
}, null, false, true, Forge.getLocalizer().getMessage("lblGeneratingWorld")));
|
||||
} catch (Exception e) {
|
||||
loaded = false;
|
||||
Forge.clearTransitionScreen();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
package forge.adventure.scene;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
|
||||
import com.badlogic.gdx.utils.Timer;
|
||||
import com.github.tommyettinger.textra.TextraButton;
|
||||
import com.github.tommyettinger.textra.TypingLabel;
|
||||
import forge.Forge;
|
||||
import forge.adventure.stage.GameHUD;
|
||||
import forge.adventure.stage.GameStage;
|
||||
import forge.adventure.stage.MapStage;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Controls;
|
||||
import forge.adventure.world.WorldSave;
|
||||
import forge.localinstance.properties.ForgeProfileProperties;
|
||||
import forge.screens.TransitionScreen;
|
||||
import forge.sound.SoundSystem;
|
||||
import forge.util.ZipUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* First scene after the splash screen
|
||||
*/
|
||||
public class StartScene extends UIScene {
|
||||
|
||||
private static StartScene object;
|
||||
Dialog exitDialog;
|
||||
Dialog exitDialog, backupDialog, zipDialog, unzipDialog;
|
||||
TextraButton saveButton, resumeButton, continueButton;
|
||||
TypingLabel version = Controls.newTypingLabel("{GRADIENT}[%80]" + Forge.CURRENT_VERSION + "{ENDGRADIENT}");
|
||||
|
||||
|
||||
public StartScene() {
|
||||
@@ -30,6 +39,7 @@ public class StartScene extends UIScene {
|
||||
ui.onButtonPress("Resume", StartScene.this::Resume);
|
||||
ui.onButtonPress("Continue", StartScene.this::Continue);
|
||||
ui.onButtonPress("Settings", StartScene.this::settings);
|
||||
ui.onButtonPress("Backup", StartScene.this::backup);
|
||||
ui.onButtonPress("Exit", StartScene.this::Exit);
|
||||
ui.onButtonPress("Switch", StartScene.this::switchToClassic);
|
||||
|
||||
@@ -40,6 +50,9 @@ public class StartScene extends UIScene {
|
||||
|
||||
saveButton.setVisible(false);
|
||||
resumeButton.setVisible(false);
|
||||
version.setHeight(5);
|
||||
version.skipToTheEnd();
|
||||
ui.addActor(version);
|
||||
}
|
||||
|
||||
public static StartScene instance() {
|
||||
@@ -55,7 +68,7 @@ public class StartScene extends UIScene {
|
||||
|
||||
public boolean Save() {
|
||||
if (TileMapScene.instance().currentMap().isInMap()) {
|
||||
Dialog noSave = createGenericDialog("", "!!GAME NOT SAVED!!\nManual saving is only available on the world map","OK",null, null, null);
|
||||
Dialog noSave = createGenericDialog("", Forge.getLocalizer().getMessage("lblGameNotSaved"), Forge.getLocalizer().getMessage("lblOK"),null, null, null);
|
||||
showDialog(noSave);
|
||||
} else {
|
||||
SaveLoadScene.instance().setMode(SaveLoadScene.Modes.Save);
|
||||
@@ -79,20 +92,27 @@ public class StartScene extends UIScene {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean loaded = false;
|
||||
|
||||
public boolean Continue() {
|
||||
final String lastActiveSave = Config.instance().getSettingData().lastActiveSave;
|
||||
|
||||
if (WorldSave.isSafeFile(lastActiveSave)) {
|
||||
if (loaded)
|
||||
return true;
|
||||
loaded = true;
|
||||
try {
|
||||
Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
loaded = false;
|
||||
if (WorldSave.load(WorldSave.filenameToSlot(lastActiveSave))) {
|
||||
SoundSystem.instance.changeBackgroundTrack();
|
||||
Forge.switchScene(GameScene.instance());
|
||||
} else {
|
||||
Forge.clearTransitionScreen();
|
||||
}
|
||||
}, null, false, true, "Loading World..."));
|
||||
}, null, false, true, Forge.getLocalizer().getMessage("lblLoadingWorld")));
|
||||
} catch (Exception e) {
|
||||
loaded = false;
|
||||
Forge.clearTransitionScreen();
|
||||
}
|
||||
}
|
||||
@@ -105,6 +125,86 @@ public class StartScene extends UIScene {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean backup() {
|
||||
if (backupDialog == null) {
|
||||
backupDialog = createGenericDialog(Forge.getLocalizer().getMessage("lblData"),
|
||||
null, Forge.getLocalizer().getMessage("lblBackup"),
|
||||
Forge.getLocalizer().getMessage("lblRestore"),
|
||||
() -> {
|
||||
removeDialog();
|
||||
Timer.schedule(new Timer.Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
generateBackup();
|
||||
}
|
||||
}, 0.2f);
|
||||
},
|
||||
() -> {
|
||||
removeDialog();
|
||||
Timer.schedule(new Timer.Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
restoreBackup();
|
||||
}
|
||||
}, 0.2f);
|
||||
}, true, Forge.getLocalizer().getMessage("lblCancel"));
|
||||
}
|
||||
showDialog(backupDialog);
|
||||
return true;
|
||||
}
|
||||
public boolean generateBackup() {
|
||||
try {
|
||||
File source = new FileHandle(ForgeProfileProperties.getUserDir() + "/adventure/Shandalar").file();
|
||||
File target = new FileHandle(Forge.getDeviceAdapter().getDownloadsDir()).file();
|
||||
ZipUtil.zip(source, target, ZipUtil.backupAdvFile);
|
||||
zipDialog = createGenericDialog("",
|
||||
Forge.getLocalizer().getMessage("lblSaveLocation") + "\n" + target.getAbsolutePath() + File.separator + ZipUtil.backupAdvFile,
|
||||
Forge.getLocalizer().getMessage("lblOK"), null, this::removeDialog, null);
|
||||
} catch (IOException e) {
|
||||
zipDialog = createGenericDialog("",
|
||||
Forge.getLocalizer().getMessage("lblErrorSavingFile") + "\n\n" + e.getMessage(),
|
||||
Forge.getLocalizer().getMessage("lblOK"), null, this::removeDialog, null);
|
||||
} finally {
|
||||
showDialog(zipDialog);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public boolean restoreBackup() {
|
||||
File source = new FileHandle(Forge.getDeviceAdapter().getDownloadsDir() + ZipUtil.backupAdvFile).file();
|
||||
File target = new FileHandle(ForgeProfileProperties.getUserDir() + "/adventure/Shandalar").file().getParentFile();
|
||||
if (unzipDialog == null) {
|
||||
unzipDialog = createGenericDialog("",
|
||||
Forge.getLocalizer().getMessage("lblDoYouWantToRestoreBackup"),
|
||||
Forge.getLocalizer().getMessage("lblYes"), Forge.getLocalizer().getMessage("lblNo"),
|
||||
() -> {
|
||||
removeDialog();
|
||||
Timer.schedule(new Timer.Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
extract(source, target);
|
||||
}
|
||||
}, 0.2f);
|
||||
}, this::removeDialog);
|
||||
}
|
||||
showDialog(unzipDialog);
|
||||
return true;
|
||||
}
|
||||
public boolean extract(File source, File target) {
|
||||
String title = "", val = "";
|
||||
try {
|
||||
val = Forge.getLocalizer().getMessage("lblFiles") + ":\n" + ZipUtil.unzip(source, target);
|
||||
} catch (IOException e) {
|
||||
title = Forge.getLocalizer().getMessage("lblError");
|
||||
val = e.getMessage();
|
||||
} finally {
|
||||
Config.instance().getSettingData().lastActiveSave = null;
|
||||
Config.instance().saveSettings();
|
||||
showDialog(createGenericDialog(title, val,
|
||||
Forge.getLocalizer().getMessage("lblOK"), null, this::removeDialog, null));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean Exit() {
|
||||
if (exitDialog == null) {
|
||||
exitDialog = createGenericDialog(Forge.getLocalizer().getMessage("lblExitForge"),
|
||||
@@ -123,16 +223,7 @@ public class StartScene extends UIScene {
|
||||
Forge.switchToClassic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
boolean hasSaveButton = WorldSave.getCurrentSave().getWorld().getData() != null;
|
||||
if (hasSaveButton) {
|
||||
TileMapScene scene = TileMapScene.instance();
|
||||
hasSaveButton = !scene.currentMap().isInMap() || scene.isAutoHealLocation();
|
||||
}
|
||||
saveButton.setVisible(hasSaveButton);
|
||||
saveButton.setDisabled(TileMapScene.instance().currentMap().isInMap());
|
||||
|
||||
public void updateResumeContinue() {
|
||||
boolean hasResumeButton = WorldSave.getCurrentSave().getWorld().getData() != null;
|
||||
resumeButton.setVisible(hasResumeButton);
|
||||
|
||||
@@ -146,7 +237,18 @@ public class StartScene extends UIScene {
|
||||
} else {
|
||||
continueButton.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
boolean hasSaveButton = WorldSave.getCurrentSave().getWorld().getData() != null;
|
||||
if (hasSaveButton) {
|
||||
TileMapScene scene = TileMapScene.instance();
|
||||
hasSaveButton = !scene.currentMap().isInMap() || scene.isAutoHealLocation();
|
||||
}
|
||||
saveButton.setVisible(hasSaveButton);
|
||||
saveButton.setDisabled(TileMapScene.instance().currentMap().isInMap());
|
||||
updateResumeContinue();
|
||||
|
||||
if (Forge.createNewAdventureMap) {
|
||||
this.NewGame();
|
||||
|
||||
@@ -239,6 +239,9 @@ public class UIScene extends Scene {
|
||||
}
|
||||
|
||||
public Dialog createGenericDialog(String title, String label, String stringYes, String stringNo, Runnable runnableYes, Runnable runnableNo) {
|
||||
return createGenericDialog(title, label, stringYes, stringNo, runnableYes, runnableNo, false, "");
|
||||
}
|
||||
public Dialog createGenericDialog(String title, String label, String stringYes, String stringNo, Runnable runnableYes, Runnable runnableNo, boolean cancelButton, String stringCancel) {
|
||||
Dialog dialog = new Dialog(title == null ? "" : title, Controls.getSkin());
|
||||
if (label != null)
|
||||
dialog.text(label);
|
||||
@@ -248,6 +251,10 @@ public class UIScene extends Scene {
|
||||
TextraButton no = Controls.newTextButton(stringNo, runnableNo);
|
||||
dialog.button(no);
|
||||
}
|
||||
if (cancelButton) {
|
||||
TextraButton cancel = Controls.newTextButton(stringCancel, this::removeDialog);
|
||||
dialog.button(cancel);
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ConsoleCommandInterpreter {
|
||||
public String complete(String text) {
|
||||
String[] words = splitOnSpace(text);
|
||||
Command currentCommand = root;
|
||||
String completionString = "";
|
||||
StringBuilder completionString = new StringBuilder();
|
||||
for (String name : words) {
|
||||
if (!currentCommand.children.containsKey(name)) {
|
||||
for (String key : currentCommand.children.keySet()) {
|
||||
@@ -49,7 +49,7 @@ public class ConsoleCommandInterpreter {
|
||||
}
|
||||
break;
|
||||
}
|
||||
completionString += name + " ";
|
||||
completionString.append(name).append(" ");
|
||||
currentCommand = currentCommand.children.get(name);
|
||||
}
|
||||
return text;
|
||||
|
||||
@@ -1039,6 +1039,7 @@ public class MapStage extends GameStage {
|
||||
}
|
||||
}
|
||||
|
||||
boolean started = false;
|
||||
public void beginDuel(EnemySprite mob) {
|
||||
if (mob == null) return;
|
||||
mob.clearCollisionHeight();
|
||||
@@ -1054,6 +1055,9 @@ public class MapStage extends GameStage {
|
||||
Forge.restrictAdvMenus = true;
|
||||
player.clearCollisionHeight();
|
||||
startPause(0.8f, () -> {
|
||||
if (started)
|
||||
return;
|
||||
started = true;
|
||||
Forge.setCursor(null, Forge.magnifyToggle ? "1" : "2");
|
||||
SoundSystem.instance.play(SoundEffectType.ManaBurn, false);
|
||||
DuelScene duelScene = DuelScene.instance();
|
||||
@@ -1061,6 +1065,7 @@ public class MapStage extends GameStage {
|
||||
if (!isLoadingMatch) {
|
||||
isLoadingMatch = true;
|
||||
Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
started = false;
|
||||
duelScene.initDuels(player, mob);
|
||||
if (isInMap && effect != null && !mob.ignoreDungeonEffect)
|
||||
duelScene.setDungeonEffect(effect);
|
||||
|
||||
@@ -64,6 +64,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
final Rectangle tempBoundingRect = new Rectangle();
|
||||
final Vector2 enemyMoveVector = new Vector2();
|
||||
|
||||
boolean collided = false;
|
||||
@Override
|
||||
protected void onActing(float delta) {
|
||||
if (isPaused() || MapStage.getInstance().isDialogOnlyInput())
|
||||
@@ -110,6 +111,9 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
}
|
||||
|
||||
if (player.collideWith(mob)) {
|
||||
if (collided)
|
||||
return;
|
||||
collided = true;
|
||||
player.setAnimation(CharacterSprite.AnimationTypes.Attack);
|
||||
player.playEffect(Paths.EFFECT_SPARKS, 0.5f);
|
||||
mob.setAnimation(CharacterSprite.AnimationTypes.Attack);
|
||||
@@ -126,6 +130,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
DuelScene duelScene = DuelScene.instance();
|
||||
FThreads.invokeInEdtNowOrLater(() -> {
|
||||
Forge.setTransitionScreen(new TransitionScreen(() -> {
|
||||
collided = false;
|
||||
duelScene.initDuels(player, mob);
|
||||
Forge.switchScene(duelScene);
|
||||
}, Forge.takeScreenshot(), true, false, false, false, "", Current.player().avatar(), mob.getAtlasPath(), Current.player().getName(), mob.getName()));
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.badlogic.gdx.utils.Timer;
|
||||
import com.github.tommyettinger.textra.Font;
|
||||
import com.github.tommyettinger.textra.TextraButton;
|
||||
import com.github.tommyettinger.textra.TextraLabel;
|
||||
import com.github.tommyettinger.textra.TypingButton;
|
||||
import com.github.tommyettinger.textra.TypingLabel;
|
||||
import forge.Forge;
|
||||
import forge.adventure.player.AdventurePlayer;
|
||||
@@ -84,12 +85,53 @@ public class Controls {
|
||||
|
||||
}
|
||||
|
||||
static class TypingButtonFix extends TypingButton {
|
||||
public TypingButtonFix(@Null String text) {
|
||||
super(text == null ? "NULL" : text, Controls.getSkin(), Controls.getTextraFont());
|
||||
addListener(new ClickListener(){
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
super.clicked(event, x, y);
|
||||
SoundSystem.instance.play(SoundEffectType.ButtonPress, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(Button.ButtonStyle style, boolean makeGridGlyphs) {
|
||||
super.setStyle(style, makeGridGlyphs);
|
||||
this.getTextraLabel().setFont(Controls.getTextraFont());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return this.getTextraLabel().storedText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(@Null String text) {
|
||||
getTextraLabel().storedText = text;
|
||||
getTextraLabel().layout.setTargetWidth(getTextraLabel().getMaxWidth());
|
||||
getTextraLabel().getFont().markup(text, getTextraLabel().layout.clear());
|
||||
getTextraLabel().setWidth(getTextraLabel().layout.getWidth() + (getTextraLabel().style != null && getTextraLabel().style.background != null ? getTextraLabel().style.background.getLeftWidth() + getTextraLabel().style.background.getRightWidth() : 0.0F));
|
||||
layout();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static public TextraButton newTextButton(String text) {
|
||||
TextraButton button = new TextButtonFix(text);
|
||||
button.getTextraLabel().setWrap(false);
|
||||
return button;
|
||||
}
|
||||
|
||||
static public TypingButton newTypingButton(String text) {
|
||||
TypingButton button = new TypingButtonFix(text);
|
||||
button.getTextraLabel().setWrap(false);
|
||||
return button;
|
||||
}
|
||||
|
||||
static public Rectangle getBoundingRect(Actor actor) {
|
||||
return new Rectangle(actor.getX(), actor.getY(), actor.getWidth(), actor.getHeight());
|
||||
}
|
||||
|
||||
@@ -354,11 +354,10 @@ public class SaveFileData extends HashMap<String,byte[]>
|
||||
final long localSUID = localClassDescriptor.getSerialVersionUID();
|
||||
final long streamSUID = resultClassDescriptor.getSerialVersionUID();
|
||||
if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
|
||||
final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
|
||||
s.append("local serialVersionUID = ").append(localSUID);
|
||||
s.append(" stream serialVersionUID = ").append(streamSUID);
|
||||
String s = "Overriding serialized class version mismatch: " + "local serialVersionUID = " + localSUID +
|
||||
" stream serialVersionUID = " + streamSUID;
|
||||
|
||||
System.err.println("[Invalid Class Exception]\n"+s);
|
||||
System.err.println("[Invalid Class Exception]\n"+ s);
|
||||
resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ public class TemplateTmxMapLoader extends TmxMapLoader {
|
||||
|
||||
this.root = xml.parse(tmxFile);
|
||||
parameter.generateMipMaps=true;
|
||||
parameter.textureMinFilter = Texture.TextureFilter.Nearest;
|
||||
parameter.textureMagFilter = Texture.TextureFilter.Nearest;
|
||||
final Array<FileHandle> textureFiles = getDependencyFileHandles(tmxFile);
|
||||
for (FileHandle textureFile : textureFiles) {
|
||||
Texture texture = new Texture(textureFile, parameter.generateMipMaps);
|
||||
|
||||
@@ -23,13 +23,10 @@ public class NavigationMap {
|
||||
this.half = spriteSize / 2;
|
||||
}
|
||||
|
||||
RayCastCallback callback = new RayCastCallback() {
|
||||
@Override
|
||||
public float reportRayFixture(Fixture fixture, Vector2 vector2, Vector2 vector21, float v) {
|
||||
if (v < 1.0)
|
||||
rayCollided = true;
|
||||
return 0;
|
||||
}
|
||||
RayCastCallback callback = (fixture, vector2, vector21, v) -> {
|
||||
if (v < 1.0)
|
||||
rayCollided = true;
|
||||
return 0;
|
||||
};
|
||||
|
||||
// public void initializeOverworldGeometryGraph() {
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.ArrayList;
|
||||
public class BiomeTexture implements Serializable {
|
||||
private final BiomeData data;
|
||||
private final int tileSize;
|
||||
public static Pixmap emptyPixmap = null;
|
||||
public Pixmap emptyPixmap = null;
|
||||
ArrayList<ArrayList<Pixmap>> images = new ArrayList<>();
|
||||
ArrayList<ArrayList<Pixmap>> smallImages = new ArrayList<>();
|
||||
ArrayList<IntMap<Pixmap>> edgeImages = new ArrayList<>();
|
||||
|
||||
@@ -25,7 +25,8 @@ public enum AbilityEffect {
|
||||
if (soundClip == null) {
|
||||
soundClip = AudioClip.createClip(ForgeConstants.EFFECTS_DIR + wav);
|
||||
}
|
||||
soundClip.play(FModel.getPreferences().getPrefInt(ForgePreferences.FPref.UI_VOL_SOUNDS)/100f);
|
||||
if (soundClip != null)
|
||||
soundClip.play(FModel.getPreferences().getPrefInt(ForgePreferences.FPref.UI_VOL_SOUNDS)/100f);
|
||||
animation.start();
|
||||
}
|
||||
|
||||
|
||||
@@ -446,10 +446,7 @@ public class FSkin {
|
||||
int crackCount = 0;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
int x = j * 200;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
int y = i * 279;
|
||||
Forge.getAssets().cracks().put(crackCount++, new TextureRegion(Forge.getAssets().getTexture(f17), x, y, 200, 279));
|
||||
}
|
||||
Forge.getAssets().cracks().put(crackCount++, new TextureRegion(Forge.getAssets().getTexture(f17), x, 0, 200, 279));
|
||||
}
|
||||
|
||||
//borders
|
||||
|
||||
@@ -350,13 +350,15 @@ public enum FSkinImage implements FSkinImageInterface {
|
||||
//COMMANDER
|
||||
IMG_ABILITY_COMMANDER (FSkinProp.IMG_ABILITY_COMMANDER),
|
||||
IMG_ABILITY_RINGBEARER (FSkinProp.IMG_ABILITY_RINGBEARER),
|
||||
|
||||
//ANNIHILATOR
|
||||
IMG_ABILITY_ANNIHILATOR (FSkinProp.IMG_ABILITY_ANNIHILATOR),
|
||||
//TOXIC
|
||||
IMG_ABILITY_TOXIC (FSkinProp.IMG_ABILITY_TOXIC),
|
||||
//ABILITY ICONS
|
||||
IMG_ABILITY_DEATHTOUCH (FSkinProp.IMG_ABILITY_DEATHTOUCH),
|
||||
IMG_ABILITY_DEFENDER (FSkinProp.IMG_ABILITY_DEFENDER),
|
||||
IMG_ABILITY_DOUBLE_STRIKE (FSkinProp.IMG_ABILITY_DOUBLE_STRIKE),
|
||||
IMG_ABILITY_EXALTED (FSkinProp.IMG_ABILITY_EXALTED),
|
||||
IMG_ABILITY_FIRST_STRIKE (FSkinProp.IMG_ABILITY_FIRST_STRIKE),
|
||||
IMG_ABILITY_FEAR (FSkinProp.IMG_ABILITY_FEAR),
|
||||
IMG_ABILITY_FLASH (FSkinProp.IMG_ABILITY_FLASH),
|
||||
|
||||
@@ -75,10 +75,12 @@ public class CardFaceSymbols {
|
||||
|
||||
Forge.getAssets().manaImages().put("commander", FSkinImage.IMG_ABILITY_COMMANDER);
|
||||
Forge.getAssets().manaImages().put("ringbearer", FSkinImage.IMG_ABILITY_RINGBEARER);
|
||||
Forge.getAssets().manaImages().put("annihilator", FSkinImage.IMG_ABILITY_ANNIHILATOR);
|
||||
Forge.getAssets().manaImages().put("toxic", FSkinImage.IMG_ABILITY_TOXIC);
|
||||
Forge.getAssets().manaImages().put("deathtouch", FSkinImage.IMG_ABILITY_DEATHTOUCH);
|
||||
Forge.getAssets().manaImages().put("defender", FSkinImage.IMG_ABILITY_DEFENDER);
|
||||
Forge.getAssets().manaImages().put("doublestrike", FSkinImage.IMG_ABILITY_DOUBLE_STRIKE);
|
||||
Forge.getAssets().manaImages().put("exalted", FSkinImage.IMG_ABILITY_EXALTED);
|
||||
Forge.getAssets().manaImages().put("firststrike", FSkinImage.IMG_ABILITY_FIRST_STRIKE);
|
||||
Forge.getAssets().manaImages().put("fear", FSkinImage.IMG_ABILITY_FEAR);
|
||||
Forge.getAssets().manaImages().put("flash", FSkinImage.IMG_ABILITY_FLASH);
|
||||
|
||||
@@ -910,6 +910,24 @@ public class CardRenderer {
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasAnnihilator()) {
|
||||
if (abiCount > 5) {
|
||||
abiY = cy + (abiSpace * (abiCount - 6));
|
||||
abiX = cx + ((cw * 2) / 1.92f);
|
||||
}
|
||||
CardFaceSymbols.drawSymbol("annihilator", g, abiX, abiY, abiScale, abiScale);
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasExalted()) {
|
||||
if (abiCount > 5) {
|
||||
abiY = cy + (abiSpace * (abiCount - 6));
|
||||
abiX = cx + ((cw * 2) / 1.92f);
|
||||
}
|
||||
CardFaceSymbols.drawSymbol("exalted", g, abiX, abiY, abiScale, abiScale);
|
||||
abiY += abiSpace;
|
||||
abiCount += 1;
|
||||
}
|
||||
if (card.getCurrentState().hasDeathtouch()) {
|
||||
if (abiCount > 5) {
|
||||
abiY = cy + (abiSpace * (abiCount - 6));
|
||||
|
||||
@@ -35,7 +35,21 @@
|
||||
"Stinging Study",
|
||||
"Study Hall",
|
||||
"Witch's Clinic",
|
||||
"Time Vault"
|
||||
"Time Vault",
|
||||
"Sol Ring",
|
||||
"Mana Crypt",
|
||||
"Tolarian Academy",
|
||||
"Tinker",
|
||||
"Demonic Tutor",
|
||||
"Vampiric Tutor",
|
||||
"Bazaar of Baghdad",
|
||||
"Library of Alexandria",
|
||||
"Mana Vault",
|
||||
"Fastbond",
|
||||
"Mishra's Workshop",
|
||||
"Yawgmoth's Bargain",
|
||||
"Gaea's Cradle",
|
||||
"Commander's Sphere"
|
||||
],
|
||||
"restrictedEditions": [
|
||||
"HTR",
|
||||
@@ -47,12 +61,12 @@
|
||||
"DS0",
|
||||
"HHO",
|
||||
"CMB1",
|
||||
"UNF",
|
||||
"UST",
|
||||
"UGL",
|
||||
"UNH",
|
||||
"UNH",
|
||||
"PPC1",
|
||||
"UND",
|
||||
"PUST",
|
||||
"UST"
|
||||
"PUST"
|
||||
],
|
||||
"difficulties": [
|
||||
{
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 120 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
@@ -1,12 +1,13 @@
|
||||
Name:Fifth Head of the Hydra
|
||||
ManaCost:no cost
|
||||
Types:Creature Hydra
|
||||
Colors:green,blue
|
||||
PT:1/4
|
||||
K:Defender
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | Description$ You can't lose the game and your opponents can't win the game.
|
||||
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ You can't win the game. | Secondary$ True | Description$ You can't lose the game and your opponents can't win the game.
|
||||
T:Mode$ Phase | Phase$ End Of Turn | ValidPlayer$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ At the beginning of each opponents end step, that player exiles 4 cards.
|
||||
SVar:TrigDig:DB$ Dig | Defined$ TriggeredPlayer | DigNum$ 4 | DestinationZone$ Exile
|
||||
T:Mode$ Phase | Phase$ End Of Turn | ValidPlayer$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ At the beginning of each opponents end step, that player exiles the top 4 cards from their library
|
||||
SVar:TrigDig:DB$ Dig | Defined$ ActivePlayer | DigNum$ 4 | DestinationZone$ Exile | ChangeNum$ All
|
||||
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | ValidCard$ Card.Self | ReplaceWith$ Exile | Description$ If CARDNAME would leave the battlefield, instead exile it with three time counters on it. It gains suspend.
|
||||
SVar:Exile:DB$ ChangeZone | Hidden$ True | WithCountersType$ TIME | WithCountersAmount$ 3 | Origin$ All | Destination$ Exile | Defined$ ReplacedCard | SubAbility$ GiveSuspend
|
||||
SVar:GiveSuspend:DB$ PumpAll | ValidCards$ Card.withoutSuspend+YouOwn | KW$ Suspend | PumpZone$ Exile | Duration$ Permanent
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Name:First Head of the Hydra
|
||||
ManaCost:no cost
|
||||
Types:Creature Hydra
|
||||
Colors:green,blue
|
||||
PT:1/4
|
||||
K:Defender
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | Description$ You can't lose the game and your opponents can't win the game.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Name:Fourth Head of the Hydra
|
||||
ManaCost:no cost
|
||||
Types:Creature Hydra
|
||||
Colors:green,blue
|
||||
PT:1/4
|
||||
K:Defender
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | Description$ You can't lose the game and your opponents can't win the game.
|
||||
|
||||
@@ -2,7 +2,9 @@ Name:Fungus of Slimefoot's Boss Effect
|
||||
ManaCost:no cost
|
||||
Colors:black,green
|
||||
Types:Enchantment
|
||||
S:Mode$ Continuous | Affected$ Saproling | EffectZone$ Command | AddType$ Land & Forest | SetColor$ Green | Description$ Saprolings you control are Forest lands in addition to their other types.
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | Execute$ TrigAnimateAll | TriggerDescription$ At the beginning of your upkeep, Saprolings you control perpetually become Forest lands in addition to their other types.
|
||||
SVar:TrigAnimateAll:DB$ AnimateAll | Duration$ Perpetual | Abilities$ ABMana | Types$ Land,Forest | SetColor$ Green | ValidCards$ Saproling.YouCtrl+nonForest
|
||||
SVar:ABMana:AB$Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}.
|
||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Command | CheckSVar$ X | SVarCompare$ GE2 | Execute$ TrigConjure | TriggerDescription$ At the beginning of each end step, if two or more creatures died this turn, conjure a random Fungus onto the battlefield.
|
||||
SVar:TrigConjure:DB$ MakeCard | Conjure$ True | AtRandom$ True | Spellbook$ Cankerbloom,Corpsejack Menace,Undercellar Myconid,Utopia Mycon,Sporemound,Deathspore Thallid,Sporoloth Ancient,Thallid Shell-Dweller,Psychotrope Thallid,Sporecrown Thallid,Sporesower Thallid,Thallid | Zone$ Battlefield
|
||||
SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Creature.YouCtrl
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Name:Second Head of the Hydra
|
||||
ManaCost:no cost
|
||||
Types:Creature Hydra
|
||||
Colors:green,blue
|
||||
PT:1/4
|
||||
K:Defender
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | Description$ You can't lose the game and your opponents can't win the game.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Name:Sixth Head of the Hydra
|
||||
ManaCost:no cost
|
||||
Types:Creature Hydra
|
||||
Colors:green,blue
|
||||
PT:1/4
|
||||
K:Defender
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | Description$ You can't lose the game and your opponents can't win the game.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:Sorin's Amulet
|
||||
ManaCost:no cost
|
||||
Types:Enchantment
|
||||
A:AB$ MakeCard | Cost$ B B PayShards<4> PayLife<4> | IsPresent$ Vampire.YouCtrl | PresentZone$ Library | PresentCompare$ GE20 | ActivationZone$ Command | GameActivationLimit$ 1 | Conjure$ True | AtRandom$ True | Spellbook$ Sorin; Grim Nemesis,Sorin; Imperious Bloodlord,Sorin; Lord of Innistrad,Sorin Markov,Sorin; Solemn Visitor,Sorin the Mirthless,Sorin; Vampire Lord,Sorin; Vengeful Bloodlord,| WithCounter$ TIME | WithCounterNum$ 3 | Zone$ Exile | RememberMade$ True | SubAbility$ GiveSuspend | SpellDescription$ Conjure a Sorin planeswalker into exile with three time counters, it gains suspend. Activate this ability only if your library contains 20 or more vampires and only once each game.
|
||||
SVar:GiveSuspend:DB$ Pump | Defined$ Remembered | KW$ Suspend | PumpZone$ Exile | Duration$ Permanent | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
S:Mode$ Continuous | EffectZone$ Command | Affected$ Vampire.YouCtrl | AddPower$ 1 | AddToughness$ 1 | CheckSVar$ YourLife | SVarCompare$ LT10 | AddKeyword$ Lifelink | Description$ As long as your life total is lower than 10, Vampires you control have +1/+1 and have lifelink.
|
||||
SVar:YourLife:Count$YourLifeTotal
|
||||
Oracle:{B}{B},{M}{M}{M}{M},Pay 4 life: Conjure a Sorin planeswalker into exile with three time counters, it gains suspend. Activate this ability only if your library contains 20 or more vampires and only once each game. \nAs long as your life total is lower than 10, Vampires you control have +1/+1 and have lifelink.
|
||||
@@ -0,0 +1,17 @@
|
||||
Name:Sorin's Boss Effect
|
||||
ManaCost:no cost
|
||||
Types:Enchantment
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | CheckSVar$ YourLife | SVarCompare$ GE40 | Execute$ TrigSeek | TriggerDescription$ As long as Sorin's life total is 40 or more, Sorin seeks two nonland cards every upkeep and can't lose the game and his opponents can't win the game.
|
||||
SVar:TrigSeek:DB$ Seek | Num$ 2 | Type$ Card.nonLand
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | CheckSVar$ YourLife | EffectZone$ Command | SVarCompare$ GE40 | Secondary$ True | Description$ You can't lose the game and your opponents can't win the game.
|
||||
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ You can't win the game. | Secondary$ True | EffectZone$ Command | CheckSVar$ YourLife | Secondary$ True | SVarCompare$ GE40 | Description$ You can't lose the game and your opponents can't win the game.
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | CheckSVar$ YourLifeCompare | SVarCompare$ EQ2 | Execute$ TrigConjure | TriggerDescription$ As long as Sorin's life total is between 20 and 40, at Sorin's upkeep, conjure a card from Sorin's Spellbook into exile with 2 time counters on it, it gains suspend.
|
||||
SVar:TrigConjure:DB$ MakeCard | Conjure$ True | AtRandom$ True | Spellbook$ Sorin; Grim Nemesis,Sorin; Imperious Bloodlord,Sorin; Lord of Innistrad,Sorin Markov,Sorin; Solemn Visitor,Sorin the Mirthless,Sorin; Vampire Lord,Sorin; Vengeful Bloodlord,Timothar; Baron of Bats,Olivia Voldaren,Patriarch's Bidding,Licia; Sanguine Tribune,Astarion; the Decadent,Strefan; Maurer Progenitor,Evelyn; the Covetous,Anje; Maid of Dishonor,Edgar Markov | WithCounter$ TIME | WithCounterNum$ 2 | Zone$ Exile | RememberMade$ True | SubAbility$ GiveSuspend
|
||||
SVar:GiveSuspend:DB$ Pump | Defined$ Remembered | KW$ Suspend | PumpZone$ Exile | Duration$ Permanent | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
S:Mode$ Continuous | EffectZone$ Command | Affected$ Vampire.YouCtrl | AddPower$ 2 | AddToughness$ 2 | CheckSVar$ YourLife | SVarCompare$ LT20 | AddKeyword$ Lifelink | Description$ As long as Sorin's life total is lower than 20, Sorin's Vampires get +2/+2 and have lifelink.
|
||||
SVar:Y:Count$Compare YourLife GE20.1.0
|
||||
SVar:X:Count$Compare YourLife LE40.1.0
|
||||
SVar:YourLifeCompare:SVar$X/Plus.Y
|
||||
SVar:YourLife:Count$YourLifeTotal
|
||||
Oracle:As long as Sorin's life total is 40 or more, Sorin seeks two nonland cards every upkeep and can't lose the game and his opponents can't win the game. \nAs long as Sorin's life total is between 20 and 40, at Sorin's upkeep, conjure a card from Sorin's Spellbook into exile with 2 time counters on it, it gains suspend.\nAs long as Sorin's life total is lower than 20, Sorin's Vampires get +2/+2 and have lifelink.
|
||||
@@ -2,6 +2,7 @@ Name:Third Head of the Hydra
|
||||
ManaCost:no cost
|
||||
Types:Creature Hydra
|
||||
PT:1/4
|
||||
Colors:green,blue
|
||||
K:Defender
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | Description$ You can't lose the game and your opponents can't win the game.
|
||||
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ You can't win the game. | Secondary$ True | Description$ You can't lose the game and your opponents can't win the game.
|
||||
|
||||
@@ -4,55 +4,79 @@ Name=slimefoot
|
||||
|
||||
[Main]
|
||||
4 Abrupt Decay|GK1|1
|
||||
3 Akawalli, the Seething Tower|LCI|1
|
||||
4 Bayou|30A|1
|
||||
2 Binding the Old Gods|KHM|1
|
||||
2 Blood Artist|J22|1
|
||||
4 Bojuka Bog|WOC|1
|
||||
2 Casualties of War|KHC|1
|
||||
4 Corpsejack Menace|CM2|1
|
||||
4 Cultivate|AFC|1
|
||||
1 Damnation|TSR|1
|
||||
2 Deathbloom Thallid|J22|1
|
||||
4 Deathbonnet Sprout|MID|1
|
||||
4 Deathcap Glade|VOW|1
|
||||
4 Deathcap Marionette|LCI|1
|
||||
3 Deathsprout|C20|1
|
||||
2 Doubling Season|CMM|1
|
||||
2 Fists of Ironwood|RVR|1
|
||||
11 Forest|DMU|1
|
||||
2 Forest|DMU|2
|
||||
3 Forest|DMU|3
|
||||
4 Fungal Infection|J22|1
|
||||
2 Ghave, Guru of Spores|PLIST|1
|
||||
3 Golgari Germination|DDJ|1
|
||||
2 Golgari Germination|DDJ|1
|
||||
4 Golgari Rot Farm|C20|1
|
||||
2 Grave Pact|8ED|1
|
||||
2 Grim Backwoods|C18|1
|
||||
2 Growing Rites of Itlimoc|P22|1
|
||||
4 Haunted Mire|DMU|1
|
||||
4 Indatha Triome|IKO|1
|
||||
2 Llanowar Wastes|BRO|1
|
||||
4 Mold Shambler|DDM|1
|
||||
3 Moldervine Reclamation|KHC|1
|
||||
4 Mortality Spear|STX|1
|
||||
4 Mycoid Shepherd|PM10|1
|
||||
4 Mycoloth|MB1|1
|
||||
2 Mycoid Shepherd|ARB|1
|
||||
2 Mycoloth|MB1|1
|
||||
4 Myconid Spore Tender|CLB|1
|
||||
4 Nature's Lore|DMR|1
|
||||
2 Nemata, Grove Guardian|CMM|1
|
||||
2 Nemata, Primeval Warden|DMU|1
|
||||
4 Overgrown Tomb|GRN|1
|
||||
2 Pallid Mycoderm|MMA|1
|
||||
3 Plains|DMU|1
|
||||
4 Propagator Primordium|YLCI|1
|
||||
2 Restless Cottage|WOE|1
|
||||
2 Rot Shambler|BFZ|1
|
||||
2 Rot Shambler|CMM|1
|
||||
2 Saproling Symbiosis|DMR|1
|
||||
4 Savannah|30A|1
|
||||
1 Slimefoot's Survey|DMU|1
|
||||
4 Slimefoot, Thallid Transplant|YDMU|1
|
||||
4 Slimefoot, the Stowaway|DOM|1
|
||||
3 Sporecrown Thallid|DOM|1
|
||||
4 Sproutback Trudge|C21|1
|
||||
3 Sporemound|SCD|1
|
||||
2 Sproutback Trudge|C21|1
|
||||
4 Swamp|DMU|1
|
||||
1 Swamp|DMU|2
|
||||
1 Swamp|DMU|3
|
||||
4 Swarm Shambler|J22|1
|
||||
4 Tendershoot Dryad|RIX|1
|
||||
2 Tendershoot Dryad|RIX|1
|
||||
2 Tendril of the Mycotyrant|LCI|1
|
||||
4 Thallid|MMA|1
|
||||
4 The Mycotyrant|LCI|1
|
||||
3 The Skullspore Nexus|LCI|1
|
||||
2 Thelon of Havenwood|TSR|1
|
||||
4 Trudge Garden|C21|1
|
||||
4 Underground Mortuary|MKM|1
|
||||
1 Urborg, Tomb of Yawgmoth|M15|1
|
||||
4 Verdant Catacombs|MM3|1
|
||||
4 Verdant Force|C15|1
|
||||
2 Verdant Force|C15|1
|
||||
2 Verdeloth the Ancient|CMM|1
|
||||
4 Village Rites|GN3|1
|
||||
4 Woodland Cemetery|DOM|1
|
||||
1 Yavimaya Sapherd|DOM|1
|
||||
1 Yavimaya, Cradle of Growth|MH2|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
75
forge-gui/res/adventure/common/decks/miniboss/sorin.dck
Normal file
75
forge-gui/res/adventure/common/decks/miniboss/sorin.dck
Normal file
@@ -0,0 +1,75 @@
|
||||
[metadata]
|
||||
Name=sorin
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Ancient Tomb|UMA|1
|
||||
4 Anguished Unmaking|SOI|1
|
||||
2 Anowon, the Ruin Sage|VOC|1
|
||||
4 Blood Artist|J21|1
|
||||
4 Bloodline Keeper|V17|1
|
||||
2 Bloodlord of Vaasgoth|VOC|1
|
||||
4 Bojuka Bog|WOC|1
|
||||
2 Butcher of Malakir|VOC|1
|
||||
2 Call the Bloodline|F16|1
|
||||
2 Champion of Dusk|VOC|1
|
||||
2 Charismatic Conqueror|LCC|2
|
||||
4 Clavileño, First of the Blessed|LCC|3
|
||||
4 Cordial Vampire|MH1|1
|
||||
4 Cruel Celebrant|LCC|1
|
||||
4 Dark Ritual|A25|1
|
||||
2 Echo of Dusk|LCI|1
|
||||
2 Edgar, Charmed Groom|VOW|1
|
||||
4 Feast of Blood|PIDW|1
|
||||
3 Gifted Aetherborn|AER|1
|
||||
4 Godless Shrine|RVR|1
|
||||
4 Guul Draz Assassin|PLIST|1
|
||||
4 Indulgent Aristocrat|VOC|1
|
||||
4 Isolated Chapel|LCC|1
|
||||
2 Legion's Landing|XLN|1
|
||||
4 Marsh Flats|MH2|1
|
||||
4 Mortify|E02|1
|
||||
2 Nullpriest of Oblivion|J21|1
|
||||
4 Olivia's Wrath|VOC|1
|
||||
4 Orzhov Basilica|2X2|1
|
||||
5 Plains|XLN|1
|
||||
3 Plains|XLN|2
|
||||
4 Plains|XLN|3
|
||||
2 Plains|XLN|4
|
||||
4 Quag Vampires|CNS|1
|
||||
4 Sanctum Seeker|LCC|1
|
||||
4 Scrubland|VMA|1
|
||||
4 Shattered Sanctum|VOW|1
|
||||
4 Shineshadow Snarl|STX|1
|
||||
3 Sorin Markov|PLIST|1
|
||||
2 Sorin of House Markov|MH3|1
|
||||
3 Sorin the Mirthless|VOW|1
|
||||
4 Sorin's Thirst|M20|1
|
||||
4 Sorin's Vengeance|PLIST|1
|
||||
2 Sorin, Grim Nemesis|SOI|1
|
||||
3 Sorin, Imperious Bloodlord|M20|1
|
||||
2 Sorin, Lord of Innistrad|LCC|1
|
||||
4 Sorin, Solemn Visitor|KTK|1
|
||||
1 Sorin, Vampire Lord|M20|1
|
||||
2 Sorin, Vengeful Bloodlord|WAR|1
|
||||
4 Sunlit Marsh|DMU|1
|
||||
21 Swamp|XLN|1
|
||||
4 Swords to Plowshares|CMM|1
|
||||
2 Tithe Drinker|C17|1
|
||||
2 Vampire Nocturnus|PDP13|1
|
||||
4 Vampire of the Dire Moon|M20|1
|
||||
2 Vanquisher's Banner|XLN|1
|
||||
4 Vindicate|EMA|1
|
||||
4 Vito, Thorn of the Dusk Rose|M21|1
|
||||
2 Vona, Butcher of Magan|MOC|1
|
||||
4 Welcoming Vampire|LCC|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=aerie_guard
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
2 Battlefield Raptor|KHM|1
|
||||
2 Bident of Thassa|A25|1
|
||||
@@ -40,14 +38,4 @@ Name=aerie_guard
|
||||
2 Watcher of the Spheres|M21|1
|
||||
2 Welcoming Vampire|VOW|1
|
||||
1 Winged Words|J21|1
|
||||
1 Wrath of God|PZ1|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
1 Wrath of God|PZ1|1
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=Amonkhet - Aven U1
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Alrund's Epiphany|KHM|1
|
||||
2 Aven Eternal|WAR|1
|
||||
@@ -17,13 +15,3 @@ Name=Amonkhet - Aven U1
|
||||
4 Silent Departure|EMA|1
|
||||
4 Silver Raven|AFR|1
|
||||
4 Winged Words|M20|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=Amonkhet - Aven W1
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Aven Battle Priest|ORI|1
|
||||
1 Aven Brigadier|ONS|1
|
||||
@@ -16,12 +14,4 @@ Name=Amonkhet - Aven W1
|
||||
3 Union of the Third Path|BRO|1
|
||||
1 Windbrisk Raptor|SHM|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=amonkhet_minotaur
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Ahn-Crop Crasher|AKH|1
|
||||
2 Bloodrage Brawler|AKH|1
|
||||
@@ -42,13 +40,3 @@ Name=amonkhet_minotaur
|
||||
1 Trial of Zeal|AKH|1
|
||||
2 Warfire Javelineer|AKH|1
|
||||
2 Zealot of the God-Pharaoh|HOU|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=salamander
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Aetherspouts|C21|1
|
||||
4 Amphin Cutthroat|M12|1
|
||||
@@ -26,13 +24,3 @@ Name=salamander
|
||||
3 Vineglimmer Snarl|LTC|1
|
||||
1 Vronos, Masked Inquisitor|CMM|1
|
||||
1 Wormfang Newt|JUD|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=ancientvampire
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Bloodline Keeper|V17|1
|
||||
1 Bloodlord of Vaasgoth|C17|1
|
||||
@@ -30,13 +28,3 @@ Name=ancientvampire
|
||||
2 Vito, Thorn of the Dusk Rose|PRES|1
|
||||
1 Vraan, Executioner Thane|ONE|1
|
||||
1 Westgate Regent|AFR|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=angelwarrior
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Admonition Angel|ZNC|1
|
||||
1 Akroma, Angel of Wrath|V15|1
|
||||
@@ -26,13 +24,3 @@ Name=angelwarrior
|
||||
1 Serra's Emissary|MH2|1
|
||||
4 Starnheim Aspirant|KHM|1
|
||||
4 Take Vengeance|M20|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=archaeologist
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Ancient Den|C21|1
|
||||
2 Angel of the Ruins|MOC|1
|
||||
@@ -27,13 +25,3 @@ Name=archaeologist
|
||||
2 Tempered Steel|2XM|1
|
||||
2 Teshar, Ancestor's Apostle|DOM|1
|
||||
1 Traxos, Scourge of Kroog|DMC|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=archerelite
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
2 Arbalest Elite|M12|1
|
||||
2 Arcus Acolyte|J21|1
|
||||
@@ -25,13 +23,3 @@ Name=archerelite
|
||||
2 Tadeas, Juniper Ascendant|SLX|1
|
||||
1 Titania's Chosen|C14|1
|
||||
2 Titania's Chosen|CMA|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=archmage
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Aether Channeler|DMU|1
|
||||
1 Azami, Lady of Scrolls|C17|1
|
||||
@@ -19,13 +17,3 @@ Name=archmage
|
||||
2 Time Warp|E02|1
|
||||
2 Venser, Shaper Savant|TSR|1
|
||||
4 Wizard Class|PLIST|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Prince Valiant 3
|
||||
Title=Prince Valiant
|
||||
Difficulty=hard
|
||||
Description=GW Knight of the Reliquary deck with Terravore and Worm Harvest
|
||||
Icon=Prince Valiant.jpg
|
||||
Deck Type=constructed
|
||||
[main]
|
||||
4 Savannah
|
||||
2 Forest
|
||||
@@ -34,4 +28,3 @@ Deck Type=constructed
|
||||
1 Courser of Kruphix
|
||||
2 Valorous Stance
|
||||
1 Naturalize
|
||||
[sideboard]
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=armored_knight
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Acclaimed Contender|ELD|1
|
||||
1 Adeline, Resplendent Cathar|MID|1
|
||||
@@ -39,13 +37,3 @@ Name=armored_knight
|
||||
1 Trailblazer's Boots|ONC|1
|
||||
1 Valiant Endeavor|AFC|1
|
||||
1 Varchild, Betrayer of Kjeldor|C18|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=Karn
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Ancient Tomb|UMA|1
|
||||
2 Automatic Librarian|DMU|1
|
||||
@@ -22,13 +20,3 @@ Name=Karn
|
||||
4 Voltaic Servant|DOM|1
|
||||
4 Wasteland|SLD|1
|
||||
2 Weatherlight|DOM|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=artificer
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
1 Aether Chaser|KLR|1
|
||||
2 Aether Hub|KLD|1
|
||||
@@ -38,13 +36,3 @@ Name=artificer
|
||||
4 Welder Automaton|GNT|1
|
||||
1 Whirler Virtuoso|KLR|1
|
||||
2 Whirlermaker|KLR|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=axgard_dwarf
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Alpine Meadow|KHM|1
|
||||
2 Axgard Armory|KHM|1
|
||||
@@ -30,13 +28,3 @@ Name=axgard_dwarf
|
||||
2 Vault Robber|KHM|1
|
||||
2 Warchanter Skald|KHM|1
|
||||
1 Warhorn Blast|KHM|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
[metadata]
|
||||
Name=azoriusangel
|
||||
[Main]
|
||||
4 Adarkar Wastes|DMU|1
|
||||
2 Angel of Mercy|JMP|1
|
||||
2 Benevolent Bodyguard|EMA|1
|
||||
2 Celestial Regulator|SNC|1
|
||||
2 Condemn|RVR|1
|
||||
1 Errant and Giada|MOM|1
|
||||
4 Faithful Disciple|YMID|1
|
||||
2 Firemane Commando|MOC|1
|
||||
2 Fragment Reality|YNEO|1
|
||||
1 Geist of Saint Traft|PRM|1
|
||||
2 Glacial Fortress|XLN|1
|
||||
2 Holy Armor|30A|1
|
||||
3 Holy Strength|30A|1
|
||||
2 Invasion of Xerex|MOM|1
|
||||
2 Invocation of Saint Traft|SOI|1
|
||||
2 Iridescent Angel|V15|1
|
||||
1 Island|SOI|1
|
||||
2 Island|SOI|2
|
||||
1 Island|SOI|3
|
||||
2 Migratory Route|KHC|1
|
||||
1 Plains|SOI|1
|
||||
4 Plains|SOI|2
|
||||
7 Plains|SOI|3
|
||||
2 Port Town|WHO|4
|
||||
3 Revitalize|M19|1
|
||||
2 Seraph of Dawn|J21|1
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=badger
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Beast Within|DMC|1
|
||||
4 Charging Badger|ANB|1
|
||||
@@ -15,13 +13,3 @@ Name=badger
|
||||
4 Nimble Mongoose|MB1|1
|
||||
4 Prismatic Vista|MH1|1
|
||||
4 Rysorian Badger|HML|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Carnage 2
|
||||
Title=Carnage
|
||||
Difficulty=medium
|
||||
Description=Mono R Grand Melee deck with Circle of Flame
|
||||
Icon=Carnage.jpg
|
||||
Deck Type=constructed
|
||||
[main]
|
||||
23 Mountain
|
||||
4 Grand Melee
|
||||
@@ -32,4 +26,3 @@ Deck Type=constructed
|
||||
1 Thran War Machine
|
||||
1 Ulamog's Crusher
|
||||
1 Utvara Scalper
|
||||
[sideboard]
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Freddy's Nightmares 2
|
||||
Title=Freddy Krueger
|
||||
Difficulty=medium
|
||||
Description=RB nightmare creature theme with mutate abilities, life drain, and some burn
|
||||
Icon=Freddy Krueger 2.jpg
|
||||
Deck Type=constructed
|
||||
[Main]
|
||||
2 Banefire
|
||||
3 Blood Crypt
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Frodo 2
|
||||
Title=Frodo
|
||||
Difficulty=medium
|
||||
Description=WRG Zoo deck
|
||||
Icon=Frodo.jpg
|
||||
Deck Type=constructed
|
||||
Profile=Reckless
|
||||
[main]
|
||||
3 Plateau
|
||||
3 Taiga
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Cuthbert's Ka-tet 2
|
||||
Title=Cuthbert Allgood of Roland's Ka-tet
|
||||
Difficulty=medium
|
||||
Description=WGR flicker and direct damage theme deck
|
||||
Icon=Cuthbert Allgood 2.jpg
|
||||
Deck Type=constructed
|
||||
[Main]
|
||||
2 Acidic Slime
|
||||
2 Ajani's Welcome
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Hellboy 2
|
||||
Title=Hellboy
|
||||
Difficulty=medium
|
||||
Description=BR direct damage deck
|
||||
Icon=Hellboy.jpg
|
||||
Deck Type=constructed
|
||||
[main]
|
||||
5 Swamp
|
||||
7 Mountain
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Bolas's Infernal Reign 3
|
||||
Title=Nicol Bolas, the Deceiver
|
||||
Difficulty=hard
|
||||
Description=URB dragons, counter, burn, and reanimate theme with Bolas planeswalkers and legends
|
||||
Icon=Nicol Bolas, the Deceiver 3.jpg
|
||||
Deck Type=constructed
|
||||
[Main]
|
||||
1 Ancient Brass Dragon
|
||||
2 Avatar of Discord
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Diablo 3
|
||||
Title=Diablo
|
||||
Difficulty=hard
|
||||
Description=BR Hellbent deck
|
||||
Icon=Diablo.jpg
|
||||
Deck Type=constructed
|
||||
[main]
|
||||
4 Badlands
|
||||
4 Bloodstained Mire
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Deadpool 2
|
||||
Title=Deadpool
|
||||
Difficulty=medium
|
||||
Description=BR deck with Ashenmoor Liege and Grixis Grimblade
|
||||
Icon=Deadpool.jpg
|
||||
Deck Type=constructed
|
||||
Profile=Reckless
|
||||
[main]
|
||||
2 Vicious Kavu
|
||||
2 Ashenmoor Gouger
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Abraham Lincoln 2
|
||||
Title=Abraham Lincoln
|
||||
Difficulty=medium
|
||||
Description=WUR flying creatures deck with Flamebreak and Earthquake
|
||||
Icon=Abraham Lincoln.jpg
|
||||
Deck Type=constructed
|
||||
[main]
|
||||
1 Plateau
|
||||
1 Flooded Strand
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Blaine the Pain 2
|
||||
Title=Blaine the Pain
|
||||
Difficulty=medium
|
||||
Description=RB vehicles strategic aggro theme deck
|
||||
Icon=Blaine the Pain 2.jpg
|
||||
Deck Type=constructed
|
||||
[Main]
|
||||
2 Aradara Express
|
||||
1 Beacon of Unrest
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=bandittrapper_hazezon
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
2 Ancient Greenwarden|ZNR|1
|
||||
2 Argoth, Sanctum of Nature|BRO|1
|
||||
@@ -30,13 +28,3 @@ Name=bandittrapper_hazezon
|
||||
1 Titania, Voice of Gaea|BRO|1
|
||||
2 Wayward Swordtooth|RIX|1
|
||||
2 Wooded Foothills|KTK|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=barbarian
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Balduvian Horde|A25|1
|
||||
2 Balthor the Stout|TOR|1
|
||||
@@ -20,13 +18,3 @@ Name=barbarian
|
||||
4 Plundering Barbarian|AFR|1
|
||||
4 Sardian Cliffstomper|BRO|1
|
||||
1 Zalto, Fire Giant Duke|AFR|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duel]
|
||||
[metadata]
|
||||
Name=Baltrice's Burning Light 3
|
||||
Title=Baltrice
|
||||
Difficulty=hard
|
||||
Description=RW burn and lifegain themed deck with Spitemare and Boros Reckoner
|
||||
Icon=Baltrice 3.jpg
|
||||
Deck Type=constructed
|
||||
[Main]
|
||||
2 Akroma's Memorial
|
||||
2 Archaeomancer's Map
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=barronlevilain
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Archon of Coronation|CMR|1
|
||||
4 Beloved Princess|ELD|1
|
||||
@@ -23,12 +21,3 @@ Name=barronlevilain
|
||||
4 Throne of the High City|CN2|1
|
||||
4 Throne Warden|PZ2|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
[metadata]
|
||||
Name=bat
|
||||
[Avatar]
|
||||
|
||||
[Main]
|
||||
4 Basilica Screecher|GTC|1
|
||||
4 Blight Keeper|J22|1
|
||||
@@ -15,13 +13,3 @@ Name=bat
|
||||
8 Swamp|J22|2
|
||||
10 Swamp|J22|3
|
||||
4 Unholy Strength|30A|1
|
||||
[Sideboard]
|
||||
|
||||
[Planes]
|
||||
|
||||
[Schemes]
|
||||
|
||||
[Conspiracy]
|
||||
|
||||
[Dungeon]
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user