mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Merge remote-tracking branch 'upstream/master' into deck-importer-decks-file-format
This commit is contained in:
@@ -233,14 +233,12 @@ public class ComputerUtilCost {
|
||||
}
|
||||
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) {
|
||||
if (cost == null) {
|
||||
// TODO cheating via autopay can still happen, need to get the real ai player from controlledBy
|
||||
if (cost == null || !ai.isAI()) {
|
||||
return true;
|
||||
}
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostSacrifice) {
|
||||
if (!ai.isAI()) {
|
||||
return false;
|
||||
}
|
||||
CardCollection list = new CardCollection();
|
||||
final CardCollection exclude = new CardCollection();
|
||||
if (AiCardMemory.getMemorySet(ai, MemorySet.PAYS_SAC_COST) != null) {
|
||||
|
||||
@@ -1291,6 +1291,27 @@ public class PlayerControllerAi extends PlayerController {
|
||||
return SpellApiToAi.Converter.get(api).chooseCardName(player, sa, faces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card chooseDungeon(Player ai, List<PaperCard> dungeonCards, String message) {
|
||||
// TODO: improve the conditions that define which dungeon is a viable option to choose
|
||||
List<String> dungeonNames = Lists.newArrayList();
|
||||
for (PaperCard pc : dungeonCards) {
|
||||
dungeonNames.add(pc.getName());
|
||||
}
|
||||
|
||||
// Don't choose Tomb of Annihilation when life in danger unless we can win right away or can't lose for 0 life
|
||||
if (ai.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player?
|
||||
int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD));
|
||||
if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife())
|
||||
&& !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) {
|
||||
dungeonNames.remove("Tomb of Annihilation");
|
||||
}
|
||||
}
|
||||
|
||||
int i = MyRandom.getRandom().nextInt(dungeonNames.size());
|
||||
return Card.fromPaperCard(dungeonCards.get(i), ai);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards) {
|
||||
// sort from best to worst
|
||||
|
||||
@@ -2,10 +2,8 @@ package forge.ai.ability;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.ai.AiPlayDecision;
|
||||
import forge.ai.AiProps;
|
||||
import forge.ai.PlayerControllerAi;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.card.ICardFace;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -52,24 +50,4 @@ public class VentureAi extends SpellAbilityAi {
|
||||
return Aggregates.random(spells); // If we're here, we should choose at least something, so choose a random thing then
|
||||
}
|
||||
|
||||
// AI that chooses which dungeon to venture into
|
||||
@Override
|
||||
public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces) {
|
||||
// TODO: improve the conditions that define which dungeon is a viable option to choose
|
||||
List<String> dungeonNames = Lists.newArrayList();
|
||||
for (ICardFace face : faces) {
|
||||
dungeonNames.add(face.getName());
|
||||
}
|
||||
|
||||
// Don't choose Tomb of Annihilation when life in danger unless we can win right away or can't lose for 0 life
|
||||
if (ai.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player?
|
||||
int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD));
|
||||
if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife())
|
||||
&& !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) {
|
||||
dungeonNames.remove("Tomb of Annihilation");
|
||||
}
|
||||
}
|
||||
|
||||
return Aggregates.random(dungeonNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
System.out.println("[LOG]: (Lazy) Loading Card: " + cardName);
|
||||
rulesByName.put(cardName, cr);
|
||||
boolean reIndexNecessary = false;
|
||||
if ((setCode == null) || setCode.length() == 0 || setCode.equals(CardEdition.UNKNOWN.getCode())) {
|
||||
CardEdition ed = editions.get(setCode);
|
||||
if (ed == null || ed.equals(CardEdition.UNKNOWN)) {
|
||||
// look for all possible editions
|
||||
for (CardEdition e : editions) {
|
||||
List<CardInSet> cardsInSet = e.getCardInSet(cardName); // empty collection if not present
|
||||
@@ -249,10 +250,9 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CardEdition e = editions.get(setCode);
|
||||
List<CardInSet> cardsInSet = e.getCardInSet(cardName); // empty collection if not present
|
||||
List<CardInSet> cardsInSet = ed.getCardInSet(cardName); // empty collection if not present
|
||||
for (CardInSet cis : cardsInSet) {
|
||||
addSetCard(e, cis, cr);
|
||||
addSetCard(ed, cis, cr);
|
||||
reIndexNecessary = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,29 +199,36 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
* @param collectorNumber: Input collectorNumber tro transform in a Sorting Key
|
||||
* @return A 5-digits zero-padded collector number + any non-numerical parts attached.
|
||||
*/
|
||||
private static final Map<String, String> sortableCollNumberLookup = new HashMap<>();
|
||||
public static String getSortableCollectorNumber(final String collectorNumber){
|
||||
String sortableCollNr = collectorNumber;
|
||||
if (sortableCollNr == null || sortableCollNr.length() == 0)
|
||||
sortableCollNr = "50000"; // very big number of 5 digits to have them in last positions
|
||||
String inputCollNumber = collectorNumber;
|
||||
if (collectorNumber == null || collectorNumber.length() == 0)
|
||||
inputCollNumber = "50000"; // very big number of 5 digits to have them in last positions
|
||||
|
||||
String matchedCollNr = sortableCollNumberLookup.getOrDefault(inputCollNumber, null);
|
||||
if (matchedCollNr != null)
|
||||
return matchedCollNr;
|
||||
|
||||
// Now, for proper sorting, let's zero-pad the collector number (if integer)
|
||||
int collNr;
|
||||
String sortableCollNr;
|
||||
try {
|
||||
collNr = Integer.parseInt(sortableCollNr);
|
||||
collNr = Integer.parseInt(inputCollNumber);
|
||||
sortableCollNr = String.format("%05d", collNr);
|
||||
} catch (NumberFormatException ex) {
|
||||
String nonNumeric = sortableCollNr.replaceAll("[0-9]", "");
|
||||
String onlyNumeric = sortableCollNr.replaceAll("[^0-9]", "");
|
||||
String nonNumSub = inputCollNumber.replaceAll("[0-9]", "");
|
||||
String onlyNumSub = inputCollNumber.replaceAll("[^0-9]", "");
|
||||
try {
|
||||
collNr = Integer.parseInt(onlyNumeric);
|
||||
collNr = Integer.parseInt(onlyNumSub);
|
||||
} catch (NumberFormatException exon) {
|
||||
collNr = 0; // this is the case of ONLY-letters collector numbers
|
||||
}
|
||||
if ((collNr > 0) && (sortableCollNr.startsWith(onlyNumeric))) // e.g. 12a, 37+, 2018f,
|
||||
sortableCollNr = String.format("%05d", collNr) + nonNumeric;
|
||||
if ((collNr > 0) && (inputCollNumber.startsWith(onlyNumSub))) // e.g. 12a, 37+, 2018f,
|
||||
sortableCollNr = String.format("%05d", collNr) + nonNumSub;
|
||||
else // e.g. WS6, S1
|
||||
sortableCollNr = nonNumeric + String.format("%05d", collNr);
|
||||
sortableCollNr = nonNumSub + String.format("%05d", collNr);
|
||||
}
|
||||
sortableCollNumberLookup.put(inputCollNumber, sortableCollNr);
|
||||
return sortableCollNr;
|
||||
}
|
||||
|
||||
|
||||
@@ -463,8 +463,11 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
*/
|
||||
public CardPool getFilteredPool(Predicate<PaperCard> predicate) {
|
||||
CardPool filteredPool = new CardPool();
|
||||
for (PaperCard pc : this.items.keySet()) {
|
||||
if (predicate.apply(pc)) filteredPool.add(pc);
|
||||
Iterator<PaperCard> cardsInPool = this.items.keySet().iterator();
|
||||
while (cardsInPool.hasNext()){
|
||||
PaperCard c = cardsInPool.next();
|
||||
if (predicate.apply(c))
|
||||
filteredPool.add(c, this.items.get(c));
|
||||
}
|
||||
return filteredPool;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
(see getCollectorNumber())
|
||||
*/
|
||||
private String collectorNumber;
|
||||
private final String artist;
|
||||
private String artist;
|
||||
private final int artIndex;
|
||||
private final boolean foil;
|
||||
private Boolean hasImage;
|
||||
@@ -73,6 +73,8 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
|
||||
@Override
|
||||
public String getCollectorNumber() {
|
||||
if (collectorNumber == null)
|
||||
collectorNumber = IPaperCard.NO_COLLECTOR_NUMBER;
|
||||
return collectorNumber;
|
||||
}
|
||||
|
||||
@@ -103,6 +105,8 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
|
||||
@Override
|
||||
public String getArtist() {
|
||||
if (this.artist == null)
|
||||
artist = IPaperCard.NO_ARTIST_NAME;
|
||||
return artist;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.item.InventoryItem;
|
||||
|
||||
/**
|
||||
@@ -137,27 +137,26 @@ public class ItemPool<T extends InventoryItem> implements Iterable<Entry<T, Inte
|
||||
return count;
|
||||
}
|
||||
|
||||
public final int countAll(Predicate<T> condition) {
|
||||
public int countAll(Predicate<T> condition){
|
||||
int count = 0;
|
||||
for (Entry<T, Integer> e : this) {
|
||||
if (condition.apply(e.getKey())) {
|
||||
count += e.getValue();
|
||||
}
|
||||
}
|
||||
for (Integer v : Maps.filterKeys(this.items, condition).values())
|
||||
count += v;
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <U extends InventoryItem> int countAll(Predicate<U> condition, Class<U> cls) {
|
||||
int count = 0;
|
||||
Iterable<T> matchingKeys = Iterables.filter(this.items.keySet(), new Predicate<T>() {
|
||||
Map<T, Integer> matchingKeys = Maps.filterKeys(this.items, new Predicate<T>() {
|
||||
@Override
|
||||
public boolean apply(T item) {
|
||||
return cls.isInstance(item) && condition.apply((U)item);
|
||||
return cls.isInstance(item) && (condition.apply((U)item));
|
||||
}
|
||||
});
|
||||
for (T key : matchingKeys)
|
||||
count += this.items.get(key);
|
||||
for (Integer i : matchingKeys.values()) {
|
||||
count += i;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
public class ChooseTypeEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -63,8 +64,13 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
|
||||
|
||||
if (!validTypes.isEmpty()) {
|
||||
for (final Player p : tgtPlayers) {
|
||||
String choice;
|
||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||
String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes);
|
||||
if (sa.hasParam("AtRandom")) {
|
||||
choice = Aggregates.random(validTypes);
|
||||
} else {
|
||||
choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes);
|
||||
}
|
||||
if (!sa.hasParam("ChooseType2")) {
|
||||
card.setChosenType(choice);
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.google.common.base.Predicates;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.ICardFace;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
@@ -45,13 +44,9 @@ public class VentureEffect extends SpellAbilityEffect {
|
||||
// Create a new dungeon card chosen by player in command zone.
|
||||
List<PaperCard> dungeonCards = StaticData.instance().getVariantCards().getAllCards(
|
||||
Predicates.compose(CardRulesPredicates.Presets.IS_DUNGEON, PaperCard.FN_GET_RULES));
|
||||
List<ICardFace> faces = new ArrayList<>();
|
||||
for (PaperCard pc : dungeonCards) {
|
||||
faces.add(pc.getRules().getMainPart());
|
||||
}
|
||||
|
||||
String message = Localizer.getInstance().getMessage("lblChooseDungeon");
|
||||
String chosen = player.getController().chooseCardName(sa, faces, message);
|
||||
Card dungeon = Card.fromPaperCard(StaticData.instance().getVariantCards().getUniqueByName(chosen), player);
|
||||
Card dungeon = player.getController().chooseDungeon(player, dungeonCards, message);
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, dungeon, sa);
|
||||
|
||||
@@ -261,6 +261,8 @@ public abstract class PlayerController {
|
||||
public abstract String chooseCardName(SpellAbility sa, Predicate<ICardFace> cpp, String valid, String message);
|
||||
|
||||
public abstract String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message);
|
||||
|
||||
public abstract Card chooseDungeon(Player player, List<PaperCard> dungeonCards, String message);
|
||||
// better to have this odd method than those if playerType comparison in ChangeZone
|
||||
public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider);
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ public abstract class StatTypeFilter<T extends InventoryItem> extends ToggleButt
|
||||
buttonMap.get(statTypes).setText(String.valueOf(count));
|
||||
}
|
||||
}
|
||||
|
||||
getWidget().revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,4 +89,24 @@ public class CardDbTestLazyCardLoading extends ForgeCardMockTestCase {
|
||||
assertNotNull(aetherVialCard);
|
||||
assertEquals(aetherVialCard.getName(), expectedCardName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tesLoadAndGetUnsupportedCardHavingWrongSetCode(){
|
||||
String cardName = "Dominating Licid";
|
||||
String wrongSetCode = "AA";
|
||||
String expectedSetCode = "EXO"; // Exodus
|
||||
CardRarity expectedCardRarity = CardRarity.Rare;
|
||||
|
||||
PaperCard dominatingLycidCard = this.cardDb.getCard(cardName);
|
||||
assertNull(dominatingLycidCard);
|
||||
|
||||
// Load the Card (just card name
|
||||
FModel.getMagicDb().attemptToLoadCard(cardName, wrongSetCode);
|
||||
|
||||
dominatingLycidCard = this.cardDb.getCard(cardName);
|
||||
assertNotNull(dominatingLycidCard);
|
||||
assertEquals(dominatingLycidCard.getName(), cardName);
|
||||
assertEquals(dominatingLycidCard.getEdition(), expectedSetCode);
|
||||
assertEquals(dominatingLycidCard.getRarity(), expectedCardRarity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,6 +681,12 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card chooseDungeon(Player player, List<PaperCard> dungeonCards, String message) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards) {
|
||||
return Lists.newArrayList();
|
||||
|
||||
10
forge-gui/res/cardsfolder/a/arden_angel.txt
Normal file
10
forge-gui/res/cardsfolder/a/arden_angel.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Arden Angel
|
||||
ManaCost:4 W W
|
||||
Types:Creature Angel
|
||||
PT:4/4
|
||||
K:Flying
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Graveyard | IsPresent$ Card.StrictlySelf | PresentZone$ Graveyard | Execute$ TrigChooseNumber | TriggerDescription$ At the beginning of your upkeep, if CARDNAME is in your graveyard, choose a number from 1 to 4 at random. If the chosen number is 1, return CARDNAME from your graveyard to the battlefield.
|
||||
SVar:TrigChooseNumber:DB$ ChooseNumber | Defined$ You | Min$ 1 | Max$ 4 | Random$ True | SubAbility$ DBChangeZone
|
||||
SVar:DBChangeZone:DB$ ChangeZone | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ1 | Origin$ Graveyard | Destination$ Battlefield
|
||||
SVar:X:Count$ChosenNumber
|
||||
Oracle:Flying\nAt the beginning of your upkeep, if Arden Angel is in your graveyard, choose a number from 1 to 4 at random. If the chosen number is 1, return Arden Angel from your graveyard to the battlefield.
|
||||
7
forge-gui/res/cardsfolder/a/ashuzas_breath.txt
Normal file
7
forge-gui/res/cardsfolder/a/ashuzas_breath.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Ashuza's Breath
|
||||
ManaCost:1 R
|
||||
Types:Sorcery
|
||||
A:SP$ RepeatEach | RepeatCards$ Creature | Zone$ Battlefield | RepeatSubAbility$ DBDealDamage | SpellDescription$ For each creature, choose a number from 0 to 2 at random. CARDNAME deals that much damage to that creature.
|
||||
SVar:DBDealDamage:DB$ DealDamage | NumDmg$ X | Defined$ Remembered
|
||||
SVar:X:Count$Random.0.2
|
||||
Oracle:For each creature, choose a number from 0 to 2 at random. Ashuza's Breath deals that much damage to that creature.
|
||||
8
forge-gui/res/cardsfolder/c/camato_scout.txt
Normal file
8
forge-gui/res/cardsfolder/c/camato_scout.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Camato Scout
|
||||
ManaCost:1 U U
|
||||
Types:Creature Merfolk
|
||||
PT:2/3
|
||||
K:ETBReplacement:Other:ChooseLT
|
||||
SVar:ChooseLT:DB$ ChooseType | Defined$ You | AtRandom$ True | Type$ Basic Land | SpellDescription$ As CARDNAME enters the battlefield, choose a land type.
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ ChosenTypewalk | Description$ CARDNAME has landwalk of the chosen type.
|
||||
Oracle:As Camato Scout enters the battlefield, choose a basic land type at random. Camato Scout has landwalk of the chosen type.
|
||||
7
forge-gui/res/cardsfolder/h/hapatos_might.txt
Normal file
7
forge-gui/res/cardsfolder/h/hapatos_might.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Hapato's Might
|
||||
ManaCost:2 B
|
||||
Types:Instant
|
||||
A:SP$ ChooseNumber | Defined$ You | Min$ 0 | Max$ 6 | Random$ True | SubAbility$ DBPump | StackDescription$ None | SpellDescription$ Target creature gets +X/+0 until end of turn, where X is a number from 0 to 6 chosen at random.
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ X | StackDescription$ {c:Targeted} gets +X/+0 until end of turn, where X is a number from 0 to 6 chosen at random.
|
||||
SVar:X:Count$ChosenNumber
|
||||
Oracle:Target creature gets +X/+0 until end of turn, where X is a number from 0 to 6 chosen at random.
|
||||
10
forge-gui/res/cardsfolder/l/lydari_druid.txt
Normal file
10
forge-gui/res/cardsfolder/l/lydari_druid.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Lydari Druid
|
||||
ManaCost:2 G
|
||||
Types:Creature Druid
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRepeat | TriggerDescription$ When CARDNAME enters the battlefield, for each land on the battlefield, choose a basic land type at random. Those lands become the land types chosen this way. (This effect lasts indefinitely.)
|
||||
SVar:TrigRepeat:DB$ RepeatEach | RepeatCards$ Land | RepeatSubAbility$ DBChooseLT | SubAbility$ DBCleanup
|
||||
SVar:DBChooseLT:DB$ ChooseType | Defined$ You | AtRandom$ True | Type$ Basic Land | SubAbility$ DBAnimate
|
||||
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ ChosenType | RemoveLandTypes$ True | RemoveIntrinsicAbilities$ True | Duration$ Permanent
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenType$ True
|
||||
Oracle:When Lydari Druid enters the battlefield, for each land on the battlefield, choose a basic land type at random. Those lands become the land types chosen this way. (This effect lasts indefinitely.)
|
||||
8
forge-gui/res/cardsfolder/l/lydari_elephant.txt
Normal file
8
forge-gui/res/cardsfolder/l/lydari_elephant.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Lydari Elephant
|
||||
ManaCost:4 G
|
||||
Types:Creature Elephant
|
||||
PT:*/*
|
||||
K:ETBReplacement:Other:RandomPower
|
||||
SVar:RandomPower:DB$ Animate | Defined$ Self | Power$ X | Toughness$ X | Duration$ Permanent | SpellDescription$ As CARDNAME enters the battlefield, choose two numbers from 3 to 7 at random. CARDNAME's power is equal to the first number chosen and its toughness equal to the second number chosen.
|
||||
SVar:X:Count$Random.3.7
|
||||
Oracle:As Lydari Elephant enters the battlefield, choose two numbers from 3 to 7 at random. Lydari Elephant's power is equal to the first number chosen and its toughness equal to the second number chosen.
|
||||
9
forge-gui/res/cardsfolder/m/murgish_cemetery.txt
Normal file
9
forge-gui/res/cardsfolder/m/murgish_cemetery.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Murgish Cemetery
|
||||
ManaCost:4 B B
|
||||
Types:Enchantment
|
||||
A:AB$ ChooseNumber | Cost$ 3 B Discard<1/Card> | Min$ 2 | Max$ 6 | Defined$ You | Random$ True | SubAbility$ DBToken | SpellDescription$ Create an X/X black Zombie creature token, where X is a number from 2 to 6 chosen at random.
|
||||
SVar:DBToken:DB$ Token | TokenScript$ b_x_x_zombie | TokenPower$ X | TokenToughness$ X
|
||||
SVar:X:Count$Random.2.6
|
||||
DeckHas:Ability$Token & Ability$Discard
|
||||
DeckHints:Type$Zombie
|
||||
Oracle:{3}{B}, Discard a card: Create an X/X black Zombie creature token, where X is a number from 2 to 6 chosen at random.
|
||||
9
forge-gui/res/cardsfolder/s/sajis_torrent.txt
Normal file
9
forge-gui/res/cardsfolder/s/sajis_torrent.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Saji's Torrent
|
||||
ManaCost:1 U
|
||||
Types:Instant
|
||||
A:SP$ ChooseNumber | Min$ 0 | Max$ 5 | Defined$ You | Random$ True | SubAbility$ DBChoose | StackDescription$ SpellDescription | SpellDescription$ Tap X creatures, where X is a number from 0 to 5 chosen at random.
|
||||
SVar:DBChoose:DB$ ChooseCard | Amount$ X | Choices$ Creature | RememberChosen$ True | SubAbility$ DBTapAll | StackDescription$ None
|
||||
SVar:DBTapAll:DB$ TapAll | ValidCards$ Creature.IsRemembered | StackDescription$ None | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Count$ChosenNumber
|
||||
Oracle:Tap X creatures, where X is a number from 0 to 5 chosen at random.
|
||||
9
forge-gui/res/cardsfolder/t/tornellan_protector.txt
Normal file
9
forge-gui/res/cardsfolder/t/tornellan_protector.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Tornellan Protector
|
||||
ManaCost:2 W
|
||||
Types:Creature Cleric
|
||||
PT:1/2
|
||||
A:AB$ Effect | Cost$ T | ValidTgts$ Creature,Player | TgtPrompt$ Select target creature or player | RememberObjects$ Targeted | ReplacementEffects$ Protect | SpellDescription$ Until end of turn, each time damage is dealt to target creature or player, prevent X of that damage, where X is a number from 1 to 3 chosen at random each time.
|
||||
SVar:Protect:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ Creature.IsRemembered,Player.IsRemembered | ReplaceWith$ DBReplace | PreventionEffect$ True | Description$ Until end of turn, each time damage is dealt to target creature or player, prevent X of that damage, where X is a number from 1 to 3 chosen at random each time.
|
||||
SVar:DBReplace:DB$ ReplaceDamage | Amount$ X
|
||||
SVar:X:Count$Random.1.3
|
||||
Oracle:{T}: Until end of turn, each time damage is dealt to target creature or player, prevent X of that damage, where X is a number from 1 to 3 chosen at random each time.
|
||||
10
forge-gui/res/cardsfolder/v/velican_dragon.txt
Normal file
10
forge-gui/res/cardsfolder/v/velican_dragon.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Velican Dragon
|
||||
ManaCost:5 R R
|
||||
Types:Creature Dragon
|
||||
PT:5/5
|
||||
K:Flying
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks or blocks, it gets +X/+0 until end of turn, where X is a number from 0 to 5 chosen at random.
|
||||
T:Mode$ Blocks | ValidCard$ Card.Self | Execute$ TrigPump | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or blocks, +X/+0 until end of turn, where X is a number from 0 to 5 chosen at random.
|
||||
SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ X
|
||||
SVar:X:Count$Random.0.5
|
||||
Oracle:Flying\nWhenever Velican Dragon attacks or blocks, it gets +X/+0 until end of turn, where X is a number from 0 to 5 chosen at random.
|
||||
@@ -348,11 +348,14 @@ ScryfallCode=SLD
|
||||
579 L Forest @Johannes Voss
|
||||
581 M Lucille @Jason Felix
|
||||
582 R Brainstorm @Mark Poole
|
||||
585 R Terramorphic Expanse @
|
||||
589 R Arcane Signet @Dan Frazier
|
||||
591 R Crash Through @Tyler Walpole
|
||||
603 M Eldrazi Monument @Cosmin Podar
|
||||
604 R Ornithopter @Cosmin Podar
|
||||
605 R Panharmonicon @Cosmin Podar
|
||||
606 R Swiftfoot Boots @Cosmin Podar
|
||||
607 R Rogue's Passage @Cosmin Podar
|
||||
|
||||
[tokens]
|
||||
b_1_1_faerie_rogue_flying
|
||||
|
||||
17
forge-gui/res/editions/Sega Dreamcast Cards.txt
Normal file
17
forge-gui/res/editions/Sega Dreamcast Cards.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
[metadata]
|
||||
Code=PSDG
|
||||
Date=2001-06-28
|
||||
Name=Sega Dreamcast Cards
|
||||
Type=Promo
|
||||
|
||||
[cards]
|
||||
1 R Arden Angel @Greg Staples
|
||||
2 R Ashuza's Breath @Glen Angus
|
||||
3 R Camato Scout @Christopher Moeller
|
||||
4 R Hapato's Might @Jeff Easley
|
||||
5 R Lydari Druid @Kev Walker
|
||||
6 R Lydari Elephant @Heather Hudson
|
||||
7 R Murgish Cemetery @Daren Bader
|
||||
8 R Saji's Torrent @Matt Cavotta
|
||||
9 R Tornellan Protector @Eric Peterson
|
||||
10 R Velican Dragon @Daren Bader
|
||||
@@ -3235,6 +3235,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
return face == null ? "" : face.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card chooseDungeon(Player player, List<PaperCard> dungeonCards, String message) {
|
||||
PaperCard dungeon = getGui().one(message, dungeonCards);
|
||||
return Card.fromPaperCard(dungeon, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards) {
|
||||
GameEntityViewMap<Card, CardView> gameCacheSplice = GameEntityView.getMap(cards);
|
||||
|
||||
Reference in New Issue
Block a user