Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master

This commit is contained in:
Michael Kamensky
2021-11-19 21:29:13 +03:00
481 changed files with 1519 additions and 660 deletions

View File

@@ -11,5 +11,6 @@ public class DifficultyData {
public int staringMoney=10;
public float enemyLifeFactor=1;
public boolean startingDifficulty;
public float sellFactor=0.2f;
}

View File

@@ -0,0 +1,805 @@
package forge.adventure.scene;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align;
import com.google.common.base.Function;
import forge.Forge;
import forge.Graphics;
import forge.adventure.AdventureApplicationAdapter;
import forge.adventure.world.AdventurePlayer;
import forge.assets.FImage;
import forge.assets.FSkinFont;
import forge.assets.FSkinImage;
import forge.deck.*;
import forge.gui.FThreads;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.itemmanager.*;
import forge.itemmanager.filters.ItemFilter;
import forge.localinstance.properties.ForgePreferences;
import forge.menu.FCheckBoxMenuItem;
import forge.menu.FDropDownMenu;
import forge.menu.FMenuItem;
import forge.menu.FPopupMenu;
import forge.model.FModel;
import forge.screens.FScreen;
import forge.screens.LoadingOverlay;
import forge.screens.TabPageScreen;
import forge.toolbox.FContainer;
import forge.toolbox.FEvent;
import forge.toolbox.FLabel;
import forge.toolbox.GuiChoose;
import forge.util.Callback;
import forge.util.ItemPool;
import forge.util.Localizer;
import forge.util.Utils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
public static FSkinImage MAIN_DECK_ICON = Forge.hdbuttons ? FSkinImage.HDLIBRARY :FSkinImage.DECKLIST;
public static FSkinImage SIDEBOARD_ICON = Forge.hdbuttons ? FSkinImage.HDSIDEBOARD : FSkinImage.FLASHBACK;
private static final float HEADER_HEIGHT = Math.round(Utils.AVG_FINGER_HEIGHT * 0.8f);
private static ItemPool<InventoryItem> decksUsingMyCards=new ItemPool<>(InventoryItem.class);
@Override
public void onActivate() {
decksUsingMyCards = new ItemPool<>(InventoryItem.class);
for (int i=0;i<AdventurePlayer.NUMBER_OF_DECKS;i++)
{
final Deck deck = AdventurePlayer.current().getDeck(i);
CardPool main = deck.getMain();
for (final Map.Entry<PaperCard, Integer> e : main) {
decksUsingMyCards.add(e.getKey());
}
if (deck.has(DeckSection.Sideboard)) {
for (final Map.Entry<PaperCard, Integer> e : deck.get(DeckSection.Sideboard)) {
// only add card if we haven't already encountered it in main
if (!main.contains(e.getKey())) {
decksUsingMyCards.add(e.getKey());
}
}
}
}
}
public void refresh() {
for(TabPage<AdventureDeckEditor> page:tabPages)
{
if(page instanceof CardManagerPage)
((CardManagerPage)page).refresh();
}
for (TabPage<AdventureDeckEditor> tabPage : tabPages) {
((AdventureDeckEditor.DeckEditorPage)tabPage).initialize();
}
}
private static AdventureDeckEditor.DeckEditorPage[] getPages() {
final Localizer localizer = Localizer.getInstance();
return new AdventureDeckEditor.DeckEditorPage[] {
new AdventureDeckEditor.CatalogPage(ItemManagerConfig.QUEST_EDITOR_POOL, localizer.getMessage("lblInventory"), FSkinImage.QUEST_BOX),
new AdventureDeckEditor.DeckSectionPage(DeckSection.Main, ItemManagerConfig.QUEST_DECK_EDITOR),
new AdventureDeckEditor.DeckSectionPage(DeckSection.Sideboard, ItemManagerConfig.QUEST_DECK_EDITOR)
};
}
private AdventureDeckEditor.CatalogPage catalogPage;
private AdventureDeckEditor.DeckSectionPage mainDeckPage;
private AdventureDeckEditor.DeckSectionPage sideboardPage;
private AdventureDeckEditor.DeckSectionPage commanderPage;
protected final AdventureDeckEditor.DeckHeader deckHeader = add(new AdventureDeckEditor.DeckHeader());
protected final FLabel lblName = deckHeader.add(new FLabel.Builder().font(FSkinFont.get(16)).insets(new Vector2(Utils.scale(5), 0)).build());
private final FLabel btnMoreOptions = deckHeader.add(new FLabel.Builder().text("...").font(FSkinFont.get(20)).align(Align.center).pressedColor(Header.BTN_PRESSED_COLOR).build());
boolean isShop=false;
public AdventureDeckEditor(boolean createAsShop) {
super(e -> {AdventurePlayer.current().getNewCards().clear();AdventureApplicationAdapter.instance.switchToLast();},getPages());
isShop=createAsShop;
//cache specific pages
for (TabPage<AdventureDeckEditor> tabPage : tabPages) {
if (tabPage instanceof AdventureDeckEditor.CatalogPage) {
catalogPage = (AdventureDeckEditor.CatalogPage) tabPage;
}
else if (tabPage instanceof AdventureDeckEditor.DeckSectionPage) {
AdventureDeckEditor.DeckSectionPage deckSectionPage = (AdventureDeckEditor.DeckSectionPage) tabPage;
switch (deckSectionPage.deckSection) {
case Main:
case Schemes:
case Planes:
mainDeckPage = deckSectionPage;
break;
case Sideboard:
sideboardPage = deckSectionPage;
break;
case Commander:
commanderPage = deckSectionPage;
break;
default:
break;
}
}
}
btnMoreOptions.setCommand(new FEvent.FEventHandler() {
@Override
public void handleEvent(FEvent e) {
FPopupMenu menu = new FPopupMenu() {
@Override
protected void buildMenu() {
final Localizer localizer = Localizer.getInstance();
addItem(new FMenuItem(localizer.getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> FDeckViewer.copyDeckToClipboard(getDeck())));
((AdventureDeckEditor.DeckEditorPage)getSelectedPage()).buildDeckMenu(this);
}
};
menu.show(btnMoreOptions, 0, btnMoreOptions.getHeight());
}
});
}
@Override
protected void doLayout(float startY, float width, float height) {
if (deckHeader.isVisible()) {
deckHeader.setBounds(0, startY, width, HEADER_HEIGHT);
startY += HEADER_HEIGHT;
}
super.doLayout(startY, width, height);
}
public Deck getDeck() {
return AdventurePlayer.current().getSelectedDeck();
}
protected AdventureDeckEditor.CatalogPage getCatalogPage() {
return catalogPage;
}
protected AdventureDeckEditor.DeckSectionPage getMainDeckPage() {
return mainDeckPage;
}
protected AdventureDeckEditor.DeckSectionPage getSideboardPage() {
return sideboardPage;
}
protected AdventureDeckEditor.DeckSectionPage getCommanderPage() {
return commanderPage;
}
@Override
public void onClose(final Callback<Boolean> canCloseCallback) {
}
@Override
public FScreen getLandscapeBackdropScreen() {
return null; //never use backdrop for editor
}
protected class DeckHeader extends FContainer {
private DeckHeader() {
setHeight(HEADER_HEIGHT);
}
@Override
public void drawBackground(Graphics g) {
g.fillRect(Header.BACK_COLOR, 0, 0, getWidth(), HEADER_HEIGHT);
}
@Override
public void drawOverlay(Graphics g) {
float y = HEADER_HEIGHT - Header.LINE_THICKNESS / 2;
g.drawLine(Header.LINE_THICKNESS, Header.LINE_COLOR, 0, y, getWidth(), y);
}
@Override
protected void doLayout(float width, float height) {
float x = 0;
lblName.setBounds(0, 0, width - 2 * height, height);
x += lblName.getWidth();
//noinspection SuspiciousNameCombination
x += height;
//noinspection SuspiciousNameCombination
btnMoreOptions.setBounds(x, 0, height, height);
}
}
protected static abstract class DeckEditorPage extends TabPage<AdventureDeckEditor> {
protected DeckEditorPage(String caption0, FImage icon0) {
super(caption0, icon0);
}
protected void buildDeckMenu(FPopupMenu menu) {
}
protected abstract void initialize();
@Override
public boolean fling(float velocityX, float velocityY) {
return false; //prevent left/right swipe to change tabs since it doesn't play nice with item managers
}
}
protected static abstract class CardManagerPage extends AdventureDeckEditor.DeckEditorPage {
private final ItemManagerConfig config;
protected final CardManager cardManager = add(new CardManager(false));
protected CardManagerPage(ItemManagerConfig config0, String caption0, FImage icon0) {
super(caption0, icon0);
config = config0;
cardManager.setItemActivateHandler(e -> onCardActivated(cardManager.getSelectedItem()));
cardManager.setContextMenuBuilder(new ItemManager.ContextMenuBuilder<PaperCard>() {
@Override
public void buildMenu(final FDropDownMenu menu, final PaperCard card) {
AdventureDeckEditor.CardManagerPage.this.buildMenu(menu, card);
}
});
}
private final Function<Map.Entry<InventoryItem, Integer>, Comparable<?>> fnNewCompare = from -> AdventurePlayer.current().getNewCards().contains(from.getKey()) ? Integer.valueOf(1) : Integer.valueOf(0);
private final Function<Map.Entry<? extends InventoryItem, Integer>, Object> fnNewGet = from -> AdventurePlayer.current().getNewCards().contains(from.getKey()) ? "NEW" : "";
public static final Function<Map.Entry<InventoryItem, Integer>, Comparable<?>> fnDeckCompare = from -> decksUsingMyCards.count(from.getKey());
public static final Function<Map.Entry<? extends InventoryItem, Integer>, Object> fnDeckGet = from -> Integer.valueOf(decksUsingMyCards.count(from.getKey())).toString();
protected void initialize() {
Map<ColumnDef, ItemColumn> colOverrides = new HashMap<>();
ItemColumn.addColOverride(config, colOverrides, ColumnDef.NEW, fnNewCompare, fnNewGet);
ItemColumn.addColOverride(config, colOverrides, ColumnDef.DECKS, fnDeckCompare, fnDeckGet);
cardManager.setup(config, colOverrides);
}
protected boolean canAddCards() {
return true;
}
public void addCard(PaperCard card) {
addCard(card, 1);
}
public void addCard(PaperCard card, int qty) {
if (canAddCards()) {
cardManager.addItem(card, qty);
updateCaption();
}
}
public void removeCard(PaperCard card) {
removeCard(card, 1);
}
public void removeCard(PaperCard card, int qty) {
cardManager.removeItem(card, qty);
updateCaption();
}
public void setCards(CardPool cards) {
cardManager.setItems(cards);
updateCaption();
}
protected void updateCaption() {
}
protected abstract void onCardActivated(PaperCard card);
protected abstract void buildMenu(final FDropDownMenu menu, final PaperCard card);
private ItemPool<PaperCard> getAllowedAdditions(Iterable<Map.Entry<PaperCard, Integer>> itemsToAdd, boolean isAddSource) {
ItemPool<PaperCard> additions = new ItemPool<>(cardManager.getGenericType());
Deck deck = parentScreen.getDeck();
for (Map.Entry<PaperCard, Integer> itemEntry : itemsToAdd) {
PaperCard card = itemEntry.getKey();
int max;
if (deck == null || card == null) {
max = Integer.MAX_VALUE;
}
else if (DeckFormat.canHaveAnyNumberOf(card)) {
max = Integer.MAX_VALUE;
}
else {
max = FModel.getPreferences().getPrefInt(ForgePreferences.FPref.DECK_DEFAULT_CARD_LIMIT);
Integer cardCopies = DeckFormat.canHaveSpecificNumberInDeck(card);
if (cardCopies != null) {
max = cardCopies;
}
max -= deck.getMain().count(card);
if (deck.has(DeckSection.Sideboard)) {
max -= deck.get(DeckSection.Sideboard).count(card);
}
if (deck.has(DeckSection.Commander)) {
max -= deck.get(DeckSection.Commander).count(card);
}
if (deck.has(DeckSection.Planes)) {
max -= deck.get(DeckSection.Planes).count(card);
}
if (deck.has(DeckSection.Schemes)) {
max -= deck.get(DeckSection.Schemes).count(card);
}
}
int qty;
if (isAddSource) {
qty = itemEntry.getValue();
}
else {
try {
qty = parentScreen.getCatalogPage().cardManager.getItemCount(card);
} catch (Exception e) {
//prevent NPE
qty = 0;
}
}
if (qty > max) {
qty = max;
}
if (qty > 0) {
additions.add(card, qty);
}
}
return additions;
}
protected int getMaxMoveQuantity(boolean isAddMenu, boolean isAddSource) {
ItemPool<PaperCard> selectedItemPool = cardManager.getSelectedItemPool();
if (isAddMenu) {
selectedItemPool = getAllowedAdditions(selectedItemPool, isAddSource);
}
if (selectedItemPool.isEmpty()) {
return 0;
}
int max = Integer.MAX_VALUE;
for (Map.Entry<PaperCard, Integer> itemEntry : selectedItemPool) {
if (itemEntry.getValue() < max) {
max = itemEntry.getValue();
}
}
return max;
}
protected void addItem(FDropDownMenu menu, final String verb, String dest, FImage icon, boolean isAddMenu, boolean isAddSource, final Callback<Integer> callback) {
final int max = getMaxMoveQuantity(isAddMenu, isAddSource);
if (max == 0) { return; }
String label = verb;
if (!StringUtils.isEmpty(dest)) {
label += " " + dest;
}
menu.addItem(new FMenuItem(label, icon, e -> {
if (max == 1) {
callback.run(max);
} else {
final Localizer localizer = Localizer.getInstance();
GuiChoose.getInteger(cardManager.getSelectedItem() + " - " + verb + " " + localizer.getMessage("lblHowMany"), 1, max, 20, callback);
}
}));
}
protected void addCommanderItems(final FDropDownMenu menu, final PaperCard card, boolean isAddMenu, boolean isAddSource) {
final Localizer localizer = Localizer.getInstance();
if (parentScreen.getCommanderPage() == null) {
return;
}
boolean isLegalCommander;
String captionSuffix = localizer.getMessage("lblCommander");
isLegalCommander = DeckFormat.Commander.isLegalCommander(card.getRules());
if (isLegalCommander && !parentScreen.getCommanderPage().cardManager.getPool().contains(card)) {
addItem(menu, "Set", "as " + captionSuffix, parentScreen.getCommanderPage().getIcon(), isAddMenu, isAddSource, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
setCommander(card);
}
});
}
if (canHavePartnerCommander() && card.getRules().canBePartnerCommander()) {
addItem(menu, "Set", "as Partner " + captionSuffix, parentScreen.getCommanderPage().getIcon(), isAddMenu, isAddSource, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
setPartnerCommander(card);
}
});
}
if (canHaveSignatureSpell() && card.getRules().canBeSignatureSpell()) {
addItem(menu, "Set", "as Signature Spell", FSkinImage.SORCERY, isAddMenu, isAddSource, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
setSignatureSpell(card);
}
});
}
}
protected boolean needsCommander() {
return parentScreen.getCommanderPage() != null && parentScreen.getDeck().getCommanders().isEmpty();
}
protected boolean canHavePartnerCommander() {
return parentScreen.getCommanderPage() != null && parentScreen.getDeck().getCommanders().size() == 1
&& parentScreen.getDeck().getCommanders().get(0).getRules().canBePartnerCommander();
}
protected boolean canOnlyBePartnerCommander(final PaperCard card) {
if (parentScreen.getCommanderPage() == null) {
return false;
}
byte cmdCI = 0;
for (final PaperCard p : parentScreen.getDeck().getCommanders()) {
cmdCI |= p.getRules().getColorIdentity().getColor();
}
return !card.getRules().getColorIdentity().hasNoColorsExcept(cmdCI);
}
protected boolean canHaveSignatureSpell() {
return parentScreen.getDeck().getOathbreaker() != null;
}
protected void setCommander(PaperCard card) {
if (!cardManager.isInfinite()) {
removeCard(card);
}
CardPool newPool = new CardPool();
newPool.add(card);
parentScreen.getCommanderPage().setCards(newPool);
refresh(); //refresh so cards shown that match commander's color identity
}
protected void setPartnerCommander(PaperCard card) {
if (!cardManager.isInfinite()) {
removeCard(card);
}
parentScreen.getCommanderPage().addCard(card);
refresh(); //refresh so cards shown that match commander's color identity
}
protected void setSignatureSpell(PaperCard card) {
if (!cardManager.isInfinite()) {
removeCard(card);
}
PaperCard signatureSpell = parentScreen.getDeck().getSignatureSpell();
if (signatureSpell != null) {
parentScreen.getCommanderPage().removeCard(signatureSpell); //remove existing signature spell if any
}
parentScreen.getCommanderPage().addCard(card);
//refreshing isn't needed since color identity won't change from signature spell
}
public void refresh() {
//not needed by default
}
@Override
protected void doLayout(float width, float height) {
float x = 0;
if (Forge.isLandscapeMode()) { //add some horizontal padding in landscape mode
x = ItemFilter.PADDING;
width -= 2 * x;
}
cardManager.setBounds(x, 0, width, height);
}
}
protected static class CatalogPage extends AdventureDeckEditor.CardManagerPage {
private boolean initialized, needRefreshWhenShown;
protected CatalogPage(ItemManagerConfig config, String caption0, FImage icon0) {
super(config, caption0, icon0);
}
@Override
protected void initialize() {
if (initialized) { return; } //prevent initializing more than once if deck changes
initialized = true;
super.initialize();
cardManager.setCaption(getItemManagerCaption());
if (!isVisible() ) {
needRefreshWhenShown = true;
return;
}
refresh();
}
@Override
protected boolean canAddCards() {
if (needRefreshWhenShown) { //ensure refreshed before cards added if hasn't been refreshed yet
needRefreshWhenShown = false;
refresh();
}
return !cardManager.isInfinite();
}
protected String getItemManagerCaption() {
final Localizer localizer = Localizer.getInstance();
return localizer.getMessage("lblCards");
}
@Override
public void setVisible(boolean visible0) {
if (isVisible() == visible0) { return; }
super.setVisible(visible0);
if (visible0 && needRefreshWhenShown) {
needRefreshWhenShown = false;
refresh();
}
}
@Override
public void refresh() {
FThreads.invokeInEdtLater(() -> LoadingOverlay.show(Localizer.getInstance().getMessage("lblLoading"), () -> {
final ItemPool<PaperCard> adventurePool = new ItemPool<>(PaperCard.class);
adventurePool.addAll(AdventurePlayer.current().getCards());
// remove bottom cards that are in the deck from the card pool
adventurePool.removeAll(AdventurePlayer.current().getSelectedDeck().getMain());
// remove sideboard cards from the catalog
adventurePool.removeAll(AdventurePlayer.current().getSelectedDeck().getOrCreate(DeckSection.Sideboard));
cardManager.setPool(adventurePool);
}));
}
@Override
protected void onCardActivated(PaperCard card) {
if (getMaxMoveQuantity(true, true) == 0) {
return; //don't add card if maximum copies of card already in deck
}
if (needsCommander()) {
setCommander(card); //handle special case of setting commander
return;
}
if (canOnlyBePartnerCommander(card)) {
return; //don't auto-change commander unexpectedly
}
if (!cardManager.isInfinite()) {
removeCard(card);
}
parentScreen.getMainDeckPage().addCard(card);
}
@Override
protected void buildMenu(final FDropDownMenu menu, final PaperCard card) {
final Localizer localizer = Localizer.getInstance();
if (!needsCommander() && !canOnlyBePartnerCommander(card)) {
addItem(menu, localizer.getMessage("lblAdd"), localizer.getMessage("lblTo") + " " + parentScreen.getMainDeckPage().cardManager.getCaption(), parentScreen.getMainDeckPage().getIcon(), true, true, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
if (!cardManager.isInfinite()) {
removeCard(card, result);
}
parentScreen.getMainDeckPage().addCard(card, result);
}
});
if (parentScreen.getSideboardPage() != null) {
addItem(menu, localizer.getMessage("lblAdd"), localizer.getMessage("lbltosideboard"), parentScreen.getSideboardPage().getIcon(), true, true, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
if (!cardManager.isInfinite()) {
removeCard(card, result);
}
parentScreen.getSideboardPage().addCard(card, result);
}
});
}
if(parentScreen.isShop)
{
addItem(menu, "Sell for ", String.valueOf(AdventurePlayer.current().cardSellPrice(card)), parentScreen.getSideboardPage().getIcon(), true, true, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
if (!cardManager.isInfinite()) {
removeCard(card, result);
}
AdventurePlayer.current().sellCard(card,result);
}
});
}
}
addCommanderItems(menu, card, true, true);
}
@Override
protected void buildDeckMenu(FPopupMenu menu) {
if (cardManager.getConfig().getShowUniqueCardsOption()) {
final Localizer localizer = Localizer.getInstance();
menu.addItem(new FCheckBoxMenuItem(localizer.getMessage("lblUniqueCardsOnly"), cardManager.getWantUnique(), e -> {
boolean wantUnique = !cardManager.getWantUnique();
cardManager.setWantUnique(wantUnique);
refresh();
cardManager.getConfig().setUniqueCardsOnly(wantUnique);
}));
}
}
}
protected static class DeckSectionPage extends AdventureDeckEditor.CardManagerPage {
private final String captionPrefix;
private final DeckSection deckSection;
protected DeckSectionPage(DeckSection deckSection0, ItemManagerConfig config) {
super(config, null, null);
final Localizer localizer = Localizer.getInstance();
deckSection = deckSection0;
switch (deckSection) {
default:
case Main:
captionPrefix = localizer.getMessage("lblMain");
cardManager.setCaption(localizer.getMessage("ttMain"));
icon = MAIN_DECK_ICON;
break;
case Sideboard:
captionPrefix = localizer.getMessage("lblSide");
cardManager.setCaption(localizer.getMessage("lblSideboard"));
icon = SIDEBOARD_ICON;
break;
case Commander:
captionPrefix = localizer.getMessage("lblCommander");
cardManager.setCaption(localizer.getMessage("lblCommander"));
icon = FSkinImage.COMMANDER;
break;
}
}
@Override
protected void initialize() {
super.initialize();
cardManager.setPool(parentScreen.getDeck().getOrCreate(deckSection));
updateCaption();
}
@Override
protected void updateCaption() {
if (deckSection == DeckSection.Commander) {
caption = captionPrefix; //don't display count for commander section since it won't be more than 1
} else {
caption = captionPrefix + " (" + parentScreen.getDeck().get(deckSection).countAll() + ")";
}
}
@Override
protected void onCardActivated(PaperCard card) {
switch (deckSection) {
case Main:
case Planes:
case Schemes:
removeCard(card);
if (parentScreen.getCatalogPage() != null) {
parentScreen.getCatalogPage().addCard(card);
}
break;
case Sideboard:
removeCard(card);
parentScreen.getMainDeckPage().addCard(card);
break;
default:
break;
}
}
@Override
protected void buildMenu(final FDropDownMenu menu, final PaperCard card) {
final Localizer localizer = Localizer.getInstance();
switch (deckSection) {
default:
case Main:
addItem(menu, localizer.getMessage("lblAdd"), null, Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS, true, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
parentScreen.getCatalogPage().removeCard(card, result);
addCard(card, result);
}
});
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
removeCard(card, result);
if (parentScreen.getCatalogPage() != null) {
parentScreen.getCatalogPage().addCard(card, result);
}
}
});
if (parentScreen.getSideboardPage() != null) {
addItem(menu, localizer.getMessage("lblMove"), localizer.getMessage("lbltosideboard"), parentScreen.getSideboardPage().getIcon(), false, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
removeCard(card, result);
parentScreen.getSideboardPage().addCard(card, result);
}
});
}
addCommanderItems(menu, card, false, false);
break;
case Sideboard:
addItem(menu, localizer.getMessage("lblAdd"), null, Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS, true, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
parentScreen.getCatalogPage().removeCard(card, result);
addCard(card, result);
}
});
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
removeCard(card, result);
if (parentScreen.getCatalogPage() != null) {
parentScreen.getCatalogPage().addCard(card, result);
}
}
});
addItem(menu, localizer.getMessage("lblMove"), localizer.getMessage("lblToMainDeck"), parentScreen.getMainDeckPage().getIcon(), false, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) { return; }
removeCard(card, result);
parentScreen.getMainDeckPage().addCard(card, result);
}
});
addCommanderItems(menu, card, false, false);
break;
case Commander:
if ( isPartnerCommander(card)) {
addItem(menu, localizer.getMessage("lblRemove"), null, Forge.hdbuttons ? FSkinImage.HDMINUS : FSkinImage.MINUS, false, false, new Callback<Integer>() {
@Override
public void run(Integer result) {
if (result == null || result <= 0) {
return;
}
removeCard(card, result);
parentScreen.getCatalogPage().refresh(); //refresh so commander options shown again
parentScreen.setSelectedPage(parentScreen.getCatalogPage());
}
});
}
break;
}
}
private boolean isPartnerCommander(final PaperCard card) {
if (parentScreen.getCommanderPage() == null || parentScreen.getDeck().getCommanders().isEmpty()) {
return false;
}
PaperCard firstCmdr = parentScreen.getDeck().getCommanders().get(0);
return !card.getName().equals(firstCmdr.getName());
}
}
}

View File

@@ -1,83 +1,13 @@
package forge.adventure.scene;
import com.badlogic.gdx.scenes.scene2d.Stage;
import forge.adventure.AdventureApplicationAdapter;
import forge.adventure.world.AdventurePlayer;
import forge.deck.Deck;
import forge.deck.FDeckEditor;
import forge.gamemodes.quest.QuestMode;
import forge.gamemodes.quest.QuestSpellShop;
import forge.gamemodes.quest.data.DeckConstructionRules;
import forge.gamemodes.quest.data.QuestData;
import forge.item.PaperCard;
import forge.itemmanager.ColumnDef;
import forge.itemmanager.ItemColumn;
import forge.itemmanager.ItemManagerConfig;
import forge.model.FModel;
import forge.screens.FScreen;
import forge.toolbox.FEvent;
import java.util.HashMap;
import java.util.Map;
/**
* DeckEditScene
* scene class that contains the Deck editor
*/
public class DeckEditScene extends ForgeScene {
public class AdventureDeckEditor extends FDeckEditor {
public AdventureDeckEditor( ) {
super( EditorType.Quest, "", false, new FEvent.FEventHandler() {
@Override
public void handleEvent(FEvent e) {
AdventureApplicationAdapter.instance.switchToLast();
}
});
}
@Override
public void onActivate() {
super.onActivate();
QuestSpellShop.updateDecksForEachCard();
}
@Override
protected boolean allowDelete() {
return false;
}
@Override
protected boolean allowsSave() {
return false;
}
@Override
protected boolean allowsAddBasic() {
return false;
}
@Override
protected boolean allowRename() {
return false;
}
@Override
protected boolean isLimitedEditor() {
return true;
}
@Override
protected Map<ColumnDef, ItemColumn> getColOverrides(ItemManagerConfig config) {
Map<ColumnDef, ItemColumn> colOverrides = new HashMap<>();
ItemColumn.addColOverride(config, colOverrides, ColumnDef.NEW, FModel.getQuest().getCards().getFnNewCompare(), FModel.getQuest().getCards().getFnNewGet());
ItemColumn.addColOverride(config, colOverrides, ColumnDef.DECKS, QuestSpellShop.fnDeckCompare, QuestSpellShop.fnDeckGet);
return colOverrides;
}
public void refresh() {
for(TabPage page:tabPages)
{
if(page instanceof CardManagerPage)
((CardManagerPage)page).refresh();
}
}
}
AdventureDeckEditor screen;
Stage stage;
@@ -96,35 +26,14 @@ public class DeckEditScene extends ForgeScene {
@Override
public void enter() {
QuestData data = new QuestData("", 0, QuestMode.Classic, null, false, "", DeckConstructionRules.Commander);
FModel.getQuest().load(data);
FModel.getQuest().getCards().getCardpool().clear();
for (Map.Entry<PaperCard, Integer> card : AdventurePlayer.current().getCards())
FModel.getQuest().getCards().addSingleCard(card.getKey(), card.getValue());
Deck deck = AdventurePlayer.current().getSelectedDeck();
getScreen();
screen.getEditorType().getController().setDeck(deck);
screen.refresh();
super.enter();
}
@Override
public FScreen getScreen() {
return screen==null?screen = new AdventureDeckEditor():screen;
return screen==null?screen = new AdventureDeckEditor(true):screen;
}
}

View File

@@ -35,9 +35,14 @@ public class InnScene extends UIScene {
super.resLoaded();
ui.onButtonPress("done",()->done());
ui.onButtonPress("heal",()->heal());
ui.onButtonPress("sell",()->sell());
TextButton doneButton = ui.findActor("done");
}
private void sell() {
AdventureApplicationAdapter.instance.switchScene(SceneType.ShopScene.instance);
}
@Override
public boolean keyPressed(int keycode)
{

View File

@@ -14,7 +14,8 @@ public enum SceneType {
TileMapScene(new forge.adventure.scene.TileMapScene()),
RewardScene(new forge.adventure.scene.RewardScene()),
InnScene(new forge.adventure.scene.InnScene()),
DeckSelectScene(new forge.adventure.scene.DeckSelectScene());
DeckSelectScene(new forge.adventure.scene.DeckSelectScene()),
ShopScene(new forge.adventure.scene.ShopScene());
public final forge.adventure.scene.Scene instance;

View File

@@ -0,0 +1,39 @@
package forge.adventure.scene;
import com.badlogic.gdx.scenes.scene2d.Stage;
import forge.screens.FScreen;
/**
* DeckEditScene
* scene class that contains the Deck editor
*/
public class ShopScene extends ForgeScene {
AdventureDeckEditor screen;
Stage stage;
public ShopScene() {
}
@Override
public void dispose() {
if (stage != null)
stage.dispose();
}
@Override
public void enter() {
getScreen();
screen.refresh();
super.enter();
}
@Override
public FScreen getScreen() {
return screen==null?screen = new AdventureDeckEditor(true):screen;
}
}

View File

@@ -9,6 +9,9 @@ import forge.adventure.util.*;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.util.ItemPool;
import java.io.Serializable;
@@ -43,6 +46,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
return WorldSave.currentSave.getPlayer();
}
private final CardPool cards=new CardPool();
private final ItemPool<InventoryItem> newCards=new ItemPool<>(InventoryItem.class);
public void create(String n, Deck startingDeck, boolean male, int race, int avatar,DifficultyData difficultyData) {
@@ -57,11 +61,13 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
this.difficultyData.startingDifficulty=difficultyData.startingDifficulty;
this.difficultyData.name=difficultyData.name;
this.difficultyData.enemyLifeFactor=difficultyData.enemyLifeFactor;
this.difficultyData.sellFactor=difficultyData.sellFactor;
life=maxLife;
avatarIndex = avatar;
heroRace = race;
isFemale = !male;
name = n;
newCards.clear();
onGoldChangeList.emit();
onLifeTotalChangeList.emit();
}
@@ -117,6 +123,9 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
this.difficultyData.startingDifficulty=data.readBool("startingDifficulty");
this.difficultyData.name=data.readString("difficultyName");
this.difficultyData.enemyLifeFactor=data.readFloat("enemyLifeFactor");
this.difficultyData.sellFactor=data.readFloat("sellFactor");
if(this.difficultyData.sellFactor==0)
this.difficultyData.sellFactor=0.2f;
name = data.readString("name");
@@ -155,6 +164,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
cards.clear();
cards.addAll(CardPool.fromCardList(Lists.newArrayList((String[])data.readObject("cards"))));
newCards.clear();
onLifeTotalChangeList.emit();
onGoldChangeList.emit();
}
@@ -169,6 +179,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
data.store("startingDifficulty",this.difficultyData.startingDifficulty);
data.store("difficultyName",this.difficultyData.name);
data.store("enemyLifeFactor",this.difficultyData.enemyLifeFactor);
data.store("sellFactor",this.difficultyData.sellFactor);
data.store("name",name);
@@ -220,6 +231,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
{
case Card:
cards.add(reward.getCard());
newCards.add(reward.getCard());
break;
case Gold:
addGold(reward.getCount());
@@ -297,4 +309,19 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
deck = (Deck)deck.copyTo(text);
decks[selectedDeckIndex]=deck;
}
public ItemPool<InventoryItem> getNewCards() {
return newCards;
}
public int cardSellPrice(PaperCard card)
{
return (int) (CardUtil.getCardPrice(card)*difficultyData.sellFactor);
}
public void sellCard(PaperCard card, Integer result) {
float price= CardUtil.getCardPrice(card)*result;
price=difficultyData.sellFactor*price;
cards.remove(card, result);
addGold((int) price);
}
}

View File

@@ -737,7 +737,7 @@ public class AiController {
}
int oldCMC = -1;
boolean xCost = sa.getPayCosts().hasXInAnyCostPart() || sa.getHostCard().hasStartOfKeyword("Strive");
boolean xCost = sa.costHasX() || sa.getHostCard().hasStartOfKeyword("Strive");
if (!xCost) {
if (!ComputerUtilCost.canPayCost(sa, player)) {
// for most costs, it's OK to check if they can be paid early in order to avoid running a heavy API check

View File

@@ -722,7 +722,12 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
return CardType.isALandType(input);
}
};
public static Predicate<String> IS_BASIC_LAND_TYPE = new Predicate<String>() {
@Override
public boolean apply(String input) {
return CardType.isABasicLandType(input);
}
};
public static Predicate<String> IS_ARTIFACT_TYPE = new Predicate<String>() {
@Override
public boolean apply(String input) {

View File

@@ -28,7 +28,6 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
}
return "";
}
@Override

View File

@@ -5139,12 +5139,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
public final boolean hasABasicLandType() {
for (final String type : getType().getSubtypes()) {
if (forge.card.CardType.isABasicLandType(type)) {
return true;
}
}
return false;
return Iterables.any(getType().getSubtypes(), CardType.Predicates.IS_BASIC_LAND_TYPE);
}
public final boolean isUsedToPay() {

View File

@@ -185,11 +185,9 @@ public class TokenInfo {
}
}
if (!typeMap.isEmpty()) {
String oldName = result.getName();
CardType type = new CardType(result.getType());
String joinedName = StringUtils.join(type.getSubtypes(), " ");
final boolean nameGenerated = oldName.equals(joinedName);
final boolean nameGenerated = result.getName().endsWith(" Token");
boolean typeChanged = false;
if (!Iterables.isEmpty(type.getSubtypes())) {
@@ -207,7 +205,7 @@ public class TokenInfo {
// update generated Name
if (nameGenerated) {
result.setName(StringUtils.join(type.getSubtypes(), " "));
result.setName(StringUtils.join(type.getSubtypes(), " ") + " Token");
}
}
}

View File

@@ -48,7 +48,7 @@ public class GameSimulatorTest extends SimulationTestCase {
assertEquals(1, heraldCopy.getToughnessBonusFromCounters());
assertEquals(1, heraldCopy.getPowerBonusFromCounters());
Card warriorToken = findCardWithName(simGame, "Warrior");
Card warriorToken = findCardWithName(simGame, "Warrior Token");
assertNotNull(warriorToken);
assertTrue(warriorToken.isSick());
assertEquals(1, warriorToken.getCurrentPower());
@@ -233,7 +233,7 @@ public class GameSimulatorTest extends SimulationTestCase {
GameSimulator sim = createSimulator(game, p);
sim.simulateSpellAbility(minusTwo);
Game simGame = sim.getSimulatedGameState();
Card vampireToken = findCardWithName(simGame, "Vampire");
Card vampireToken = findCardWithName(simGame, "Vampire Token");
assertNotNull(vampireToken);
Player simP = simGame.getPlayers().get(1);
@@ -599,7 +599,7 @@ public class GameSimulatorTest extends SimulationTestCase {
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
Card scion = findCardWithName(simGame, "Eldrazi Scion");
Card scion = findCardWithName(simGame, "Eldrazi Scion Token");
assertNotNull(scion);
assertEquals(1, scion.getNetPower());
assertEquals(1, scion.getNetToughness());
@@ -608,7 +608,7 @@ public class GameSimulatorTest extends SimulationTestCase {
GameCopier copier = new GameCopier(simGame);
Game copy = copier.makeCopy();
Card scionCopy = findCardWithName(copy, "Eldrazi Scion");
Card scionCopy = findCardWithName(copy, "Eldrazi Scion Token");
assertNotNull(scionCopy);
assertEquals(1, scionCopy.getNetPower());
assertEquals(1, scionCopy.getNetToughness());
@@ -1288,7 +1288,7 @@ public class GameSimulatorTest extends SimulationTestCase {
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
int numZombies = countCardsWithName(simGame, "Zombie");
int numZombies = countCardsWithName(simGame, "Zombie Token");
assertEquals(2, numZombies);
}
@@ -1323,11 +1323,11 @@ public class GameSimulatorTest extends SimulationTestCase {
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(fatalPushSA).value;
assertTrue(score > 0);
assertEquals(2, countCardsWithName(sim.getSimulatedGameState(), "Zombie"));
assertEquals(2, countCardsWithName(sim.getSimulatedGameState(), "Zombie Token"));
score = sim.simulateSpellAbility(electrifySA).value;
assertTrue(score > 0);
assertEquals(countCardsWithName(sim.getSimulatedGameState(), "Zombie"), 4);
assertEquals(4, countCardsWithName(sim.getSimulatedGameState(), "Zombie Token"));
}
public void testPlayerXCount() {
@@ -1564,7 +1564,7 @@ public class GameSimulatorTest extends SimulationTestCase {
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
int numZombies = countCardsWithName(simGame, "Zombie");
int numZombies = countCardsWithName(simGame, "Zombie Token");
assertEquals(4, numZombies);
}
@@ -1592,7 +1592,7 @@ public class GameSimulatorTest extends SimulationTestCase {
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
int numZombies = countCardsWithName(simGame, "Zombie");
int numZombies = countCardsWithName(simGame, "Zombie Token");
assertEquals(3, numZombies);
}

View File

@@ -438,20 +438,21 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck if visible below dialog
}
}));
addItem(new FMenuItem(localizer.getMessage("lblSaveAs"), Forge.hdbuttons ? FSkinImage.HDSAVEAS : FSkinImage.SAVEAS, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
String defaultName = editorType.getController().getNextAvailableName();
FOptionPane.showInputDialog(localizer.getMessage("lblNameNewCopyDeck"), defaultName, new Callback<String>() {
@Override
public void run(String result) {
if (!StringUtils.isEmpty(result)) {
editorType.getController().saveAs(result);
if(allowsSave())
addItem(new FMenuItem(localizer.getMessage("lblSaveAs"), Forge.hdbuttons ? FSkinImage.HDSAVEAS : FSkinImage.SAVEAS, new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
String defaultName = editorType.getController().getNextAvailableName();
FOptionPane.showInputDialog(localizer.getMessage("lblNameNewCopyDeck"), defaultName, new Callback<String>() {
@Override
public void run(String result) {
if (!StringUtils.isEmpty(result)) {
editorType.getController().saveAs(result);
}
}
}
});
}
}));
});
}
}));
}
if (allowRename()) {
addItem(new FMenuItem(localizer.getMessage("lblRenameDeck"), Forge.hdbuttons ? FSkinImage.HDEDIT : FSkinImage.EDIT, new FEventHandler() {
@@ -767,7 +768,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
}
protected void initialize() {
cardManager.setup(config, parentScreen.getColOverrides(config));
cardManager.setup(config);
}
protected boolean canAddCards() {
@@ -1256,6 +1257,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
}
}
addCommanderItems(menu, card, true, true);
if (parentScreen.getEditorType() == EditorType.Constructed) {

View File

@@ -19,18 +19,21 @@
"name":"Easy",
"startingLife":16,
"staringMoney":200,
"enemyLifeFactor":0.8
"enemyLifeFactor":0.8 ,
"sellFactor":0.5
},{
"name":"Normal",
"startingLife":12,
"staringMoney":100,
"startingDifficulty":true,
"enemyLifeFactor":1.0
"enemyLifeFactor":1.0 ,
"sellFactor":0.2
},{
"name":"Hard",
"startingLife":8,
"staringMoney":10,
"enemyLifeFactor":1.5
"enemyLifeFactor":1.5 ,
"sellFactor":0.1
}
]
}

View File

@@ -20,6 +20,15 @@
"x": 420,
"y": 10
} ,
{
"type" : "TextButton",
"name" : "sell" ,
"text" : "Sell cards" ,
"width": 48,
"height": 16,
"x": 420,
"y": 34
} ,
{
"type" : "TextButton",
"name" : "done" ,
@@ -27,7 +36,7 @@
"width": 48,
"height": 16,
"x": 420,
"y": 34
"y": 58
}
]

View File

@@ -5,11 +5,11 @@ PT:4/4
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield or dies, exile another card from a graveyard.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or dies, exile another card from a graveyard.
SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Hidden$ True | RememberChanged$ True | ChangeType$ Card.Other | ChangeNum$ 1 | Mandatory$ True | AILogic$ ExilePreference:HighestCMC | SubAbility$ DBImmediateTrigger
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card | Execute$ TrigCharm | TriggerDescription$ When you do, ABILITY
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card | RememberObjects$ Remembered | SubAbility$ DBCleanup | Execute$ TrigCharm | TriggerDescription$ When you do, ABILITY
SVar:TrigCharm:DB$ Charm | Choices$ DBRemoveCounter,DBPump
SVar:DBRemoveCounter:DB$ RemoveCounter | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | CounterType$ Any | CounterNum$ X | SubAbility$ DBCleanup | SpellDescription$ Remove X counters from target permanent, where X is the mana value of the exiled card.
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | IsCurse$ True | NumAtt$ -X | NumDef$ -X | SubAbility$ DBCleanup | SpellDescription$ Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card.
SVar:DBRemoveCounter:DB$ RemoveCounter | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | CounterType$ Any | CounterNum$ X | SpellDescription$ Remove X counters from target permanent, where X is the mana value of the exiled card.
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | IsCurse$ True | NumAtt$ -X | NumDef$ -X | SpellDescription$ Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Remembered$CardManaCost
SVar:X:TriggerRemembered$CardManaCost
DeckHas:Ability$Graveyard
Oracle:Menace\nWhen Cemetery Desecrator enters the battlefield or dies, exile another card from a graveyard. When you do, choose one —\n• Remove X counters from target permanent, where X is the mana value of the exiled card.\n• Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card.

View File

@@ -5,5 +5,5 @@ K:Enchant player
A:SP$ Attach | Cost$ R | ValidTgts$ Player | AILogic$ Curse
T:Mode$ AttackersDeclared | ValidCard$ Creature | AttackedTarget$ Player.EnchantedBy | Execute$ TrigRepeat | TriggerZones$ Battlefield | TriggerDescription$ Whenever enchanted player is attacked, create a Gold token. Each opponent attacking that player does the same. (A Gold token is an artifact with "Sacrifice this artifact: Add one mana of any color.")
SVar:TrigRepeat:DB$ RepeatEach | RepeatPlayers$ TriggeredAttackingPlayerAndYou | RepeatSubAbility$ TrigToken | ChangeZoneTable$ True
SVar:TrigToken:DB$ Token | TokenScript$ gold | TokenOwner$ Player.IsRemembered | TokenAmount$ 1 | LegacyImage$ gold c17
SVar:TrigToken:DB$ Token | TokenScript$ c_a_gold_draw | TokenOwner$ Player.IsRemembered | TokenAmount$ 1
Oracle:Enchant player\nWhenever enchanted player is attacked, create a Gold token. Each opponent attacking that player does the same. (A Gold token is an artifact with "Sacrifice this artifact: Add one mana of any color.")

View File

@@ -2,5 +2,5 @@ Name:Gild
ManaCost:3 B
Types:Sorcery
A:SP$ ChangeZone | Cost$ 3 B | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBToken | SpellDescription$ Exile target creature. Create a Gold token. (It's an artifact with "Sacrifice this artifact: Add one mana of any color.")
SVar:DBToken:DB$ Token | TokenScript$ gold | TokenOwner$ You | LegacyImage$ gold bng
SVar:DBToken:DB$ Token | TokenScript$ c_a_gold_draw | TokenOwner$ You
Oracle:Exile target creature. Create a Gold token. (It's an artifact with "Sacrifice this artifact: Add one mana of any color.")

View File

@@ -2,11 +2,14 @@ Name:Goblin Welder
ManaCost:R
Types:Creature Goblin Artificer
PT:1/1
A:AB$ Pump | Cost$ T | ValidTgts$ Artifact | TgtPrompt$ Select target artifact a player controls | RememberObjects$ ThisTargetedCard | Condition$ AllTargetsLegal | SubAbility$ DBTargetYard | SpellDescription$ Choose target artifact a player controls and target artifact card in that player's graveyard. If both targets are still legal as this ability resolves, that player simultaneously sacrifices the artifact and returns the artifact card to the battlefield. | StackDescription$ None
SVar:DBTargetYard:DB$ Pump | ValidTgts$ Artifact | TgtPrompt$ Select target artifact card in that player's graveyard | TgtZone$ Graveyard | PumpZone$ Graveyard | TargetsWithSameController$ True | ImprintCards$ ThisTargetedCard | Condition$ AllTargetsLegal | StackDescription$ If both targets are still legal as this ability resolves, {p:TargetedController} simultaneously sacrifices {c:ParentTarget} and returns {c:Targeted} to the battlefield. | SubAbility$ DBSacrifice
SVar:DBSacrifice:DB$ SacrificeAll | ValidCards$ Card.IsRemembered | ConditionDefined$ Remembered | ConditionPresent$ Artifact | ConditionCompare$ GE1 | ConditionCheckSVar$ CheckImprint | ConditionSVarCompare$ GE1 | SubAbility$ DBReturn | StackDescription$ None
SVar:DBReturn:DB$ ChangeZone | Defined$ Imprinted | Origin$ Graveyard | Destination$ Battlefield | SubAbility$ DBCleanup | ConditionDefined$ Remembered | ConditionPresent$ Artifact | ConditionCompare$ GE1 | ConditionCheckSVar$ CheckImprint | ConditionSVarCompare$ GE1 | StackDescription$ None
A:AB$ Pump | Cost$ T | ValidTgts$ Artifact | TgtPrompt$ Select target artifact a player controls | RememberObjects$ ThisTargetedCard | Condition$ AllTargetsLegal | SubAbility$ DBTargetYard | StackDescription$ If both targets are still legal as this ability resolves, {p:TargetedController} simultaneously sacrifices {c:ThisTargetedCard} | SpellDescription$ Choose target artifact a player controls and target artifact card in that player's graveyard. If both targets are still legal as this ability resolves, that player simultaneously sacrifices the artifact and returns the artifact card to the battlefield.
SVar:DBTargetYard:DB$ Pump | ValidTgts$ Artifact | TargetsWithDefinedController$ ParentTargetedController | TgtPrompt$ Select target artifact card in that player's graveyard | TgtZone$ Graveyard | PumpZone$ Graveyard | ImprintCards$ ThisTargetedCard | Condition$ AllTargetsLegal | StackDescription$ and returns {c:ThisTargetedCard} to the battlefield. | SubAbility$ DBBranch
SVar:DBBranch:DB$ Branch | BranchConditionSVar$ TargetCheck | BranchConditionSVarCompare$ GE2 | TrueSubAbility$ DBSacrifice | FalseSubAbility$ DBCleanup
SVar:DBSacrifice:DB$ SacrificeAll | ValidCards$ Card.IsRemembered | SubAbility$ DBReturn
SVar:DBReturn:DB$ ChangeZone | Defined$ Imprinted | Origin$ Graveyard | Destination$ Battlefield | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
SVar:TargetCheck:SVar$CheckRemem/Plus.CheckImprint
SVar:CheckRemem:Remembered$Valid Artifact.sharesControllerWith Imprinted
SVar:CheckImprint:Imprinted$Valid Artifact.sharesControllerWith Remembered
AI:RemoveDeck:All
AI:RemoveDeck:Random

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Human Noble
PT:2/3
T:Mode$ Untaps | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigExile | OptionalDecider$ You | TriggerDescription$ Inspired — Whenever CARDNAME becomes untapped, you may exile target creature. If you do, create a Gold token. (It's an artifact with "Sacrifice this artifact: Add one mana of any color.")
SVar:TrigExile:DB$ ChangeZone | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenScript$ gold | TokenOwner$ You | TokenAmount$ 1 | LegacyImage$ gold jou | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBToken:DB$ Token | TokenScript$ c_a_gold_draw | TokenOwner$ You | TokenAmount$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Remembered$Amount
Oracle:Inspired — Whenever King Macar, the Gold-Cursed becomes untapped, you may exile target creature. If you do, create a Gold token. (It's an artifact with "Sacrifice this artifact: Add one mana of any color.")

View File

@@ -3,7 +3,7 @@ ManaCost:X W U U
Types:Legendary Planeswalker Niko
Loyalty:3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create X Shard tokens. (They're enchantments with "{2}, Sacrifice this enchantment: Scry 1, then draw a card.")
SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ shard | TokenOwner$ You
SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ c_e_shard_draw | TokenOwner$ You
SVar:X:Count$xPaid
A:AB$ Effect | Cost$ AddCounter<1/LOYALTY> | Name$ Niko Aris Effect | Planeswalker$ True | Triggers$ Trig | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | RememberObjects$ Targeted | SubAbility$ DBPump | SpellDescription$ Up to one target creature you control can't be blocked this turn. Whenever that creature deals damage this turn, return it to its owner's hand.
SVar:DBPump:DB$ Pump | KW$ HIDDEN Unblockable | Defined$ Targeted
@@ -11,6 +11,6 @@ SVar:Trig:Mode$ DamageDealtOnce | ValidSource$ Creature.IsRemembered | Execute$
SVar:Eff:DB$ ChangeZone | ValidTgts$ Creature.IsRemembered | Origin$ Battlefield | Destination$ Hand
A:AB$ DealDamage | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature.tapped | NumDmg$ Y | TgtPrompt$ Select target tapped creature | SpellDescription$ CARDNAME deals 2 damage to target tapped creature for each card you've drawn this turn.
SVar:Y:Count$YouDrewThisTurn/Twice
A:AB$ Token | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ shard | TokenOwner$ You | SpellDescription$ Create a Shard token.
A:AB$ Token | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ c_e_shard_draw | TokenOwner$ You | SpellDescription$ Create a Shard token.
DeckHas:Ability$Token & Ability$Sacrifice
Oracle:When Niko Aris enters the battlefield, create X Shard tokens. (They're enchantments with "{2}, Sacrifice this enchantment: Scry 1, then draw a card.")\n[+1]: Up to one target creature you control can't be blocked this turn. Whenever that creature deals damage this turn, return it to its owner's hand.\n[1]: Niko Aris deals 2 damage to target tapped creature for each card you've drawn this turn.\n[1]: Create a Shard token.

View File

@@ -5,6 +5,6 @@ K:Saga:4:TrigToken,TrigPutCounter,TrigDraw,TrigGold
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You | LegacyImage$ w 1 1 human soldier thb | SpellDescription$ Create a 1/1 white Human Soldier token.
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 3 | SpellDescription$ Put three +1/+1 counters on target creature you control.
SVar:TrigDraw:DB$ Draw | NumCards$ 2 | ConditionPresent$ Creature.YouCtrl+powerGE4 | SpellDescription$ If you control a creature with power 4 or greater, draw two cards.
SVar:TrigGold:DB$ Token | TokenAmount$ 1 | TokenScript$ gold | TokenOwner$ You | LegacyImage$ gold thb | SpellDescription$ Create a Gold token.
SVar:TrigGold:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_gold_draw | TokenOwner$ You | SpellDescription$ Create a Gold token.
DeckHas:Ability$Counters & Ability$Token
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)\nI — Create a 1/1 white Human Soldier creature token.\nII — Put three +1/+1 counters on target creature you control.\nIII — If you control a creature with power 4 or greater, draw two cards.\nIV — Create a Gold token. (It's an artifact with "Sacrifice this artifact: Add one mana of any color.")

View File

@@ -178,14 +178,14 @@ ScryfallCode=BNG
165 R Temple of Plenty @Noah Bradley
[tokens]
gold
b_2_2_e_zombie
c_a_gold_draw
g_2_2_wolf
g_3_3_e_centaur
r_3_1_e_elemental_haste
u_2_2_e_bird_flying
u_9_9_kraken
w_1_1_bird_flying
w_1_1_cat_soldier_vigilance
w_1_1_e_soldier
w_1_1_e_soldier
u_2_2_e_bird_flying
u_9_9_kraken
b_2_2_e_zombie
r_3_1_e_elemental_haste
g_3_3_e_centaur
g_2_2_wolf

View File

@@ -352,27 +352,27 @@ ScryfallCode=C15
342 L Forest @Jonas De Ro
[tokens]
gold
lightning_rager
c_1_1_shapeshifter_changeling
w_4_4_angel_flying
w_2_2_cat
w_2_2_knight_first_strike
w_2_2_knight_vigilance
u_2_2_drake_flying
b_0_0_phyrexian_germ
b_2_2_zombie
r_5_5_dragon_flying
r_3_1_elemental_shaman_haste
g_2_2_bear
g_4_4_phyrexian_beast
g_3_3_elephant
g_3_3_frog_lizard
c_1_1_shapeshifter_changeling
c_a_gold_draw
g_1_1_saproling
g_1_1_snake
g_1_2_spider_reach
g_2_2_bear
g_2_2_wolf
ur_5_5_elemental_flying
g_3_3_elephant
g_3_3_frog_lizard
g_4_4_phyrexian_beast
gu_1_1_snake
wb_x_x_e_spirit_experience
lightning_rager
r_3_1_elemental_shaman_haste
r_5_5_dragon_flying
u_2_2_drake_flying
ur_5_5_elemental_flying
w_2_2_cat
w_2_2_knight_first_strike
w_2_2_knight_vigilance
w_4_4_angel_flying
wb_1_1_spirit_flying
wb_x_x_e_spirit_experience

View File

@@ -319,14 +319,14 @@ ScryfallCode=C17
309 L Forest @Kev Walker
[tokens]
gold
c_0_1_eldrazi_spawn_sac
w_2_2_cat
b_1_1_bat_flying
b_1_1_rat_deathtouch
b_1_1_vampire
b_2_2_zombie
brg_3_3_cat_dragon_flying
c_0_1_eldrazi_spawn_sac
c_a_gold_draw
g_2_2_cat_warrior_forestwalk
r_4_4_dragon_flying
r_6_6_dragon_flying
g_2_2_cat_warrior_forestwalk
brg_3_3_cat_dragon_flying
w_2_2_cat

View File

@@ -8,4 +8,18 @@ ScryfallCode=HHO
[cards]
6 R Fruitcake Elemental @Darrell Riche
7 R Gifts Given @Jason Chan
8 R Evil Presents @Paul Bonner
9 R Season's Beatings @Kev Walker
10 R Snow Mercy @rk post
11 R Yule Ooze @Steve Prescott
12 R Naughty // Nice @Greg Staples
13 R Stocking Tiger @Terese Nielsen
14 M Mishra's Toy Workshop @Jung Park
15 M Goblin Sleigh Ride @Mark Zug
16 M Thopter Pie Network @Victor Adame Minguez
17 M Some Disassembly Required @Dmitry Burmak
18 M Bog Humbugs @Kieran Yanner
19 M Decorated Knight @Zoltan Boros
20 M Topdeck the Halls @Kieran Yanner
21 M Last-Minute Chopping @Marta Nael

View File

@@ -178,16 +178,16 @@ ScryfallCode=JOU
165 R Temple of Malady @James Paick
[tokens]
gold
w_1_1_soldier
w_1_1_soldier
w_1_1_soldier
b_x_x_zombie
bg_1_1_e_snake_deathtouch
c_a_gold_draw
g_1_3_e_spider_reach
g_x_x_hydra
r_2_3_minotaur_haste
r_2_3_minotaur_haste
rg_2_2_satyr_haste
u_2_2_e_bird_flying
u_4_4_sphinx_flying
b_x_x_zombie
r_2_3_minotaur_haste
r_2_3_minotaur_haste
g_x_x_hydra
g_1_3_e_spider_reach
rg_2_2_satyr_haste
bg_1_1_e_snake_deathtouch
w_1_1_soldier
w_1_1_soldier
w_1_1_soldier

View File

@@ -454,22 +454,22 @@ ScryfallCode=KHM
2 Woodland Chasm|KHM
[tokens]
w_4_4_angel_warrior_flying_vigilance
w_1_1_human_warrior
w_1_1_spirit_flying
u_1_1_bird_flying
u_4_4_giant_wizard
komas_coil
u_2_2_shapeshifter_changeling
b_2_2_zombie_berserker
r_2_3_demon_berserker_menace
r_5_5_dragon_flying
r_2_1_dwarf_berserker
c_a_treasure_sac
c_e_shard_draw
g_1_1_elf_warrior
g_2_2_bear
g_2_2_cat
g_1_1_elf_warrior
g_4_4_troll_warrior_trample
c_a_treasure_sac
icy_manalith
komas_coil
r_2_1_dwarf_berserker
r_2_3_demon_berserker_menace
r_5_5_dragon_flying
replicated_ring
shard
u_1_1_bird_flying
u_2_2_shapeshifter_changeling
u_4_4_giant_wizard
w_1_1_human_warrior
w_1_1_spirit_flying
w_4_4_angel_warrior_flying_vigilance

View File

@@ -184,6 +184,9 @@ ScryfallCode=SLD
203 R Commander's Sphere @Yosuke Ueno
204 R Darksteel Ingot @Theodoru
205 R Gilded Lotus @Mab Graves
206 R Exquisite Blood @Nils Hamm
207 R Night's Whisper @Tomas Duchek
208 R Phyrexian Tower @Nicholas Gregory
209 M Elesh Norn, Grand Cenobite @Igor Kieryluk
210 M Jin-Gitaxias, Core Augur @Eric Deschamps
211 M Sheoldred, Whispering One @Jana Schirmer & Johannes Voss
@@ -200,6 +203,11 @@ ScryfallCode=SLD
223 M Grave Titan @GodMachine
224 M Inferno Titan @Dibujante Nocturno
225 M Kroxa, Titan of Death's Hunger @DZO
226 R Path to Exile @Marija Tiurina
227 R Well of Lost Dreams @Marija Tiurina
228 R Frantic Search @Marija Tiurina
229 R Intruder Alarm @Marija Tiurina
230 R Shelldock Isle @Marija Tiurina
231 R Gravecrawler @Fay Dalton
232 M Liliana, Death's Majesty @Fay Dalton
233 M Rise of the Dark Realms @Fay Dalton
@@ -270,6 +278,11 @@ ScryfallCode=SLD
302 R Vault of Whispers @DXTR
303 R Great Furnace @DXTR
304 R Tree of Tales @DXTR
305 R Ravenous Chupacabra @Crocodile Jackson
306 R Managorger Hydra @Crocodile Jackson
307 R Pathbreaker Ibex @Crocodile Jackson
308 R Temur Sabertooth @Crocodile Jackson
309 R Winding Constrictor @Crocodile Jackson
310 R Unbreakable Formation @Tyler Walpole
311 R Whir of Invention @Tyler Walpole
312 R Hero's Downfall @Tyler Walpole
@@ -285,6 +298,16 @@ ScryfallCode=SLD
322 R Blasphemous Act @Martin Ansin
323 R Beast Within @Martin Ansin
324 R Grafdigger's Cage @Martin Ansin
325 R Snow-Covered Plains @Jubilee
326 R Snow-Covered Island @Jubilee
327 R Snow-Covered Swamp @Jubilee
328 R Snow-Covered Mountain @Jubilee
329 R Snow-Covered Forest @Jubilee
330 R Aether Gust @Mateus Manhanini
331 R Counterspell @Mateus Manhanini
332 R Fabricate @Mateus Manhanini
333 R Fact or Fiction @Mateus Manhanini
334 R Mystical Tutor @Mateus Manhanini
340 M Mind Flayer, the Shadow @Isis
341 R Chief Jim Hopper @Greg Staples
342 R Dustin, Gadget Genius @Colin Boyer
@@ -303,6 +326,11 @@ ScryfallCode=SLD
356 R Slayers' Stronghold @Alayna Danner
357 R Gavony Township @Robbie Trevino
358 R Alchemist's Refuge @DZO
359 R Plains @Donato Giancola
360 R Island @Yeong-Hao Han
361 R Swamp @Jonas De Ro
362 R Mountain @Grzegorz Rutkowski
363 R Forest @Andreas Rocha
364 R Swords to Plowshares @MSCHF
365 R Grim Tutor @MSCHF
366 R Blood Moon @MSCHF
@@ -318,15 +346,25 @@ ScryfallCode=SLD
376 M Craterhoof Behemoth @Kira
377 R Metalwork Colossus @Chris Rahn
378 R Metalwork Colossus @Hyan Tran
379 R Zndrsplt, Eye of Wisdom @Alexis Ziritt
379b R Zndrsplt, Eye of Wisdom @Alexis Ziritt
380 R Okaun, Eye of Chaos @Alexis Ziritt
380b R Okaun, Eye of Chaos @Alexis Ziritt
381 R Propaganda @Scott Balmer
381b R Propaganda @Scott Balmer
382 R Stitch in Time @Micha Huigen
382b R Stitch in Time @Micha Huigen
383 R Krark's Thumb @Wooden Cyclops
383b R Krark's Thumb @Wooden Cyclops
477 R Path to Exile @Riot Games
478 R Rhystic Study @Riot Games
482 R Counterflux @Riot Games
483 R Thran Dynamo @Riot Games
484 L Plains @Riot Games
485 L Island @Riot Games
486 L Swamp @Riot Games
487 L Mountain @Riot Games
488 L Forest @Riot Games
484 R Plains @Riot Games
485 R Island @Riot Games
486 R Swamp @Riot Games
487 R Mountain @Riot Games
488 R Forest @Riot Games
501 R Karn, the Great Creator @Wisnu Tan
502 R Ugin, the Ineffable @Daarken
503 M Gideon Blackblade @Kieran Yanner

View File

@@ -655,10 +655,11 @@ Unknown Shores|THB|1
[tokens]
b_2_2_zombie
c_0_4_a_wall_defender
c_a_gold_draw
g_1_2_spider_reach
g_2_2_wolf
r_x_1_elemental_trample_haste
r_1_1_satyr_noblock
r_x_1_elemental_trample_haste
u_1_1_tentacle
u_3_2_reflection
u_8_8_kraken_hexproof
@@ -666,4 +667,3 @@ ub_2_3_nightmare_mill
w_0_1_goat
w_1_1_human_soldier
w_2_2_pegasus_flying
gold

View File

@@ -300,6 +300,7 @@ Curse
Rune
Saga
Shrine
Shard
[ArtifactTypes]
Blood
Clue:Clues

View File

@@ -1,4 +1,4 @@
Name:Phyrexian Germ
Name:Phyrexian Germ Token
ManaCost:no cost
Types:Creature Phyrexian Germ
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie Army
Name:Zombie Army Token
ManaCost:no cost
Types:Creature Zombie Army
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Cleric
Name:Cleric Token
ManaCost:no cost
Types:Creature Cleric
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Insect
Name:Insect Token
ManaCost:no cost
Types:Creature Insect
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Serf
Name:Serf Token
ManaCost:no cost
Types:Creature Serf
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Thrull
Name:Thrull Token
ManaCost:no cost
Types:Creature Thrull
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Assassin
Name:Assassin Token
ManaCost:no cost
Types:Creature Assassin
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Assassin
Name:Assassin Token
ManaCost:no cost
Types:Creature Assassin
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Assassin
Name:Assassin Token
ManaCost:no cost
Types:Creature Assassin
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Bat
Name:Bat Token
ManaCost:no cost
Types:Creature Bat
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Faerie Rogue
Name:Faerie Rogue Token
ManaCost:no cost
Types:Creature Faerie Rogue
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Goblin Rogue
Name:Goblin Rogue Token
ManaCost:no cost
Types:Creature Goblin Rogue
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Harpy
Name:Harpy Token
ManaCost:no cost
Types:Creature Harpy
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Insect
Name:Insect Token
ManaCost:no cost
Types:Creature Insect
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Minion
Name:Minion Token
ManaCost:no cost
Types:Creature Minion
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Rat
Name:Rat Token
ManaCost:no cost
Types:Creature Rat
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Rat
Name:Rat Token
ManaCost:no cost
Types:Creature Rat
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Skeleton
Name:Skeleton Token
ManaCost:no cost
Types:Creature Skeleton
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Skeleton
Name:Skeleton Token
ManaCost:no cost
Types:Creature Minion
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Skeleton
Name:Skeleton Token
ManaCost:no cost
Colors:black
Types:Creature Skeleton

View File

@@ -1,4 +1,4 @@
Name:Slug
Name:Slug Token
ManaCost:no cost
Types:Creature Slug
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Snake
Name:Snake Token
ManaCost:no cost
Types:Creature Snake
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Thrull
Name:Thrull Token
ManaCost:no cost
Types:Creature Thrull
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Vampire
Name:Vampire Token
ManaCost:no cost
Types:Creature Vampire
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Vampire Knight
Name:Vampire Knight Token
ManaCost:no cost
Types:Creature Vampire Knight
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Vampire
Name:Vampire Token
ManaCost:no cost
Types:Creature Vampire
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Wolf
Name:Wolf Token
ManaCost:no cost
Types:Creature Wolf
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Bat
Name:Bat Token
ManaCost:no cost
Types:Creature Bat
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Cat
Name:Cat Token
ManaCost:no cost
Types:Creature Cat
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Spider
Name:Spider Token
ManaCost:no cost
Types:Creature Spider
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Warrior
Name:Warrior Token
ManaCost:no cost
Types:Creature Warrior
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie
Name:Zombie Token
ManaCost:no cost
Types:Enchantment Creature Zombie
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Knight
Name:Knight Token
ManaCost:no cost
Types:Creature Knight
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Phyrexian Zombie
Name:Phyrexian Zombie Token
ManaCost:no cost
Types:Creature Phyrexian Zombie
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Pirate
Name:Pirate Token
ManaCost:no cost
Types:Creature Pirate
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Rogue
Name:Rogue Token
ManaCost:no cost
Types:Creature Rogue
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Rogue
Name:Rogue Token
ManaCost:no cost
Types:Creature Rogue
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Vampire
Name:Vampire Token
ManaCost:no cost
Types:Creature Vampire
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie
Name:Zombie Token
ManaCost:no cost
Types:Creature Zombie
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie Berserker
Name:Zombie Berserker Token
ManaCost:no cost
Types:Creature Zombie Berserker
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie
Name:Zombie Token
ManaCost:no cost
Types:Creature Zombie
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie
Name:Zombie Token
ManaCost:no cost
Types:Creature Zombie
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie Knight
Name:Zombie Knight Token
ManaCost:no cost
Types:Creature Zombie Knight
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Vampire
Name:Vampire Token
ManaCost:no cost
Types:Creature Vampire
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Spider
Name:Spider Token
ManaCost:no cost
Types:Creature Spider
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Angel
Name:Angel Token
ManaCost:no cost
Types:Creature Angel
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Beast
Name:Beast Token
ManaCost:no cost
Types:Creature Beast
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Horror
Name:Horror Token
ManaCost:no cost
Types:Creature Horror
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Horror
Name:Horror Token
ManaCost:no cost
Types:Creature Horror
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Kavu
Name:Kavu Token
ManaCost:no cost
Types:Creature Kavu
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Horror
Name:Horror Token
ManaCost:no cost
Types:Creature Horror
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie Warrior
Name:Zombie Warrior Token
ManaCost:no cost
Types:Creature Zombie Warrior
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Demon
Name:Demon Token
ManaCost:no cost
Types:Creature Demon
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Zombie Giant
Name:Zombie Giant Token
ManaCost:no cost
Types:Creature Zombie Giant
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Demon
Name:Demon Token
ManaCost:no cost
Types:Creature Demon
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Wurm
Name:Wurm Token
ManaCost:no cost
Types:Creature Wurm
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Aura Curse
Name:Aura Curse Token
ManaCost:no cost
Types:Enchantment Aura Curse
Colors:black

View File

@@ -1,4 +1,4 @@
Name:Demon
Name:Demon Token
ManaCost:no cost
Colors:black
Types:Creature Demon

View File

@@ -1,4 +1,4 @@
Name:Horror
Name:Horror Token
ManaCost:no cost
Colors:black
Types:Creature Horror

View File

@@ -1,4 +1,4 @@
Name:Nightmare Horror
Name:Nightmare Horror Token
ManaCost:no cost
Colors:black
Types:Creature Nightmare Horror

View File

@@ -1,4 +1,4 @@
Name:Phyrexian Minion
Name:Phyrexian Minion Token
ManaCost:no cost
Types:Creature Phyrexian Minion
PT:*/*

View File

@@ -1,4 +1,4 @@
Name:Spirit
Name:Spirit Token
ManaCost:no cost
Colors:black
Types:Creature Spirit

View File

@@ -1,4 +1,4 @@
Name:Vampire
Name:Vampire Token
ManaCost:no cost
Colors:black
Types:Creature Vampire

View File

@@ -1,4 +1,4 @@
Name:Zombie
Name:Zombie Token
ManaCost:no cost
Colors:black
Types:Creature Zombie

View File

@@ -1,4 +1,4 @@
Name:Zombie Horror
Name:Zombie Horror Token
ManaCost:no cost
Colors:black
Types:Creature Zombie Horror

View File

@@ -1,4 +1,4 @@
Name:Snake
Name:Snake Token
ManaCost:no cost
Types:Enchantment Creature Snake
Colors:black,green

View File

@@ -1,4 +1,4 @@
Name:Insect
Name:Insect Token
ManaCost:no cost
Types:Creature Insect
Colors:black,green

View File

@@ -1,4 +1,4 @@
Name:Pest
Name:Pest Token
ManaCost:no cost
Types:Creature Pest
Colors:black,green

Some files were not shown because too many files have changed in this diff Show More