mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Merge remote-tracking branch 'upstream/master' into deck-importer-decks-file-format
This commit is contained in:
@@ -208,7 +208,7 @@ public class AiAttackController {
|
||||
*/
|
||||
public final boolean isEffectiveAttacker(final Player ai, final Card attacker, final Combat combat, final GameEntity defender) {
|
||||
// if the attacker will die when attacking don't attack
|
||||
if ((attacker.getNetToughness() + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, null, combat, true)) <= 0) {
|
||||
if (attacker.getNetToughness() + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, null, combat, true) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,19 +17,11 @@
|
||||
*/
|
||||
package forge.ai;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ai.ability.ChangeZoneAi;
|
||||
import forge.ai.ability.ExploreAi;
|
||||
import forge.ai.ability.LearnAi;
|
||||
@@ -40,39 +32,17 @@ import forge.card.mana.ManaCost;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.CardTraitPredicates;
|
||||
import forge.game.Direction;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.*;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.SpellApiBased;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPlayOption;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Accessors;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostAdjustment;
|
||||
import forge.game.cost.CostDiscard;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPayEnergy;
|
||||
import forge.game.cost.CostPayLife;
|
||||
import forge.game.cost.CostPutCounter;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.cost.CostSacrifice;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -82,15 +52,7 @@ import forge.game.replacement.ReplaceMoved;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.LandAbility;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.OptionalCostValue;
|
||||
import forge.game.spellability.Spell;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityCondition;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.spellability.SpellPermanent;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.staticability.StaticAbilityMustTarget;
|
||||
import forge.game.trigger.Trigger;
|
||||
@@ -106,6 +68,9 @@ import forge.util.collect.FCollectionView;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.event.BreadcrumbBuilder;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* AiController class.
|
||||
@@ -1968,6 +1933,38 @@ public class AiController {
|
||||
// Whims of the Fates {all, 0, 0}
|
||||
result.addAll(pool);
|
||||
break;
|
||||
case FlipOntoBattlefield:
|
||||
if ("DamageCreatures".equals(sa.getParam("AILogic"))) {
|
||||
int maxToughness = Integer.valueOf(sa.getSubAbility().getParam("NumDmg"));
|
||||
CardCollectionView rightToughness = CardLists.filter(pool, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
return card.getController().isOpponentOf(sa.getActivatingPlayer())
|
||||
&& card.getNetToughness() <= maxToughness
|
||||
&& card.canBeDestroyed();
|
||||
}
|
||||
});
|
||||
Card bestCreature = ComputerUtilCard.getBestCreatureAI(rightToughness.isEmpty() ? pool : rightToughness);
|
||||
if (bestCreature != null) {
|
||||
result.add(bestCreature);
|
||||
} else {
|
||||
result.add(Aggregates.random(pool)); // should ideally never get here
|
||||
}
|
||||
} else {
|
||||
CardCollectionView viableOptions = CardLists.filter(pool, Predicates.and(CardPredicates.isControlledByAnyOf(sa.getActivatingPlayer().getOpponents())),
|
||||
new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
return card.canBeDestroyed();
|
||||
}
|
||||
});
|
||||
Card best = ComputerUtilCard.getBestAI(viableOptions);
|
||||
if (best == null) {
|
||||
best = Aggregates.random(pool); // should ideally never get here either
|
||||
}
|
||||
result.add(best);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CardCollection editablePool = new CardCollection(pool);
|
||||
for (int i = 0; i < max; i++) {
|
||||
|
||||
@@ -40,7 +40,6 @@ import forge.game.card.CardFactory;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.combat.Combat;
|
||||
@@ -905,7 +904,7 @@ public class ComputerUtilCard {
|
||||
}
|
||||
|
||||
for (final Card crd : list) {
|
||||
ColorSet color = CardUtil.getColors(crd);
|
||||
ColorSet color = crd.determineColor();
|
||||
if (color.hasWhite()) map.get(0).setValue(Integer.valueOf(map.get(0).getValue()+1));
|
||||
if (color.hasBlue()) map.get(1).setValue(Integer.valueOf(map.get(1).getValue()+1));
|
||||
if (color.hasBlack()) map.get(2).setValue(Integer.valueOf(map.get(2).getValue()+1));
|
||||
|
||||
@@ -1478,7 +1478,7 @@ public class ComputerUtilCombat {
|
||||
|
||||
// DealDamage triggers
|
||||
if (ApiType.DealDamage.equals(sa.getApi())) {
|
||||
if ("TriggeredAttacker".equals(sa.getParam("Defined"))) {
|
||||
if (!sa.hasParam("Defined") || !sa.getParam("Defined").startsWith("TriggeredAttacker")) {
|
||||
continue;
|
||||
}
|
||||
int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa);
|
||||
|
||||
@@ -87,6 +87,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.Explore, ExploreAi.class)
|
||||
.put(ApiType.Fight, FightAi.class)
|
||||
.put(ApiType.FlipACoin, FlipACoinAi.class)
|
||||
.put(ApiType.FlipOntoBattlefield, FlipOntoBattlefieldAi.class)
|
||||
.put(ApiType.Fog, FogAi.class)
|
||||
.put(ApiType.GainControl, ControlGainAi.class)
|
||||
.put(ApiType.GainControlVariant, ControlGainVariantAi.class)
|
||||
@@ -140,6 +141,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.RemoveCounterAll, CannotPlayAi.class)
|
||||
.put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class)
|
||||
.put(ApiType.RemoveFromGame, AlwaysPlayAi.class)
|
||||
.put(ApiType.RemoveFromMatch, AlwaysPlayAi.class)
|
||||
.put(ApiType.ReorderZone, AlwaysPlayAi.class)
|
||||
.put(ApiType.Repeat, RepeatAi.class)
|
||||
.put(ApiType.RepeatEach, RepeatEachAi.class)
|
||||
|
||||
@@ -260,8 +260,9 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
|
||||
// minimum card advantage unless the hand will be fully reloaded
|
||||
int minAdv = logic.contains(".minAdv") ? Integer.parseInt(logic.substring(logic.indexOf(".minAdv") + 7)) : 0;
|
||||
boolean noDiscard = logic.contains(".noDiscard");
|
||||
|
||||
if (numExiledWithSrc > curHandSize) {
|
||||
if (numExiledWithSrc > curHandSize || (noDiscard && numExiledWithSrc > 0)) {
|
||||
if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(source)) {
|
||||
// Try to gain some card advantage if the card will die anyway
|
||||
// TODO: ideally, should evaluate the hand value and not discard good hands to it
|
||||
@@ -269,7 +270,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize());
|
||||
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (!noDiscard && numExiledWithSrc >= ai.getMaxHandSize());
|
||||
}
|
||||
} else if (origin.equals(ZoneType.Stack)) {
|
||||
// time stop can do something like this:
|
||||
|
||||
@@ -244,7 +244,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
|
||||
//if Iona does prevent from casting, allow it to draw
|
||||
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
|
||||
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
|
||||
if (imprinted.determineColor().hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
|
||||
return allow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +303,8 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
|| ai.getLife() <= 5)) {
|
||||
// Basic ai logic for Lethal Vapors
|
||||
return false;
|
||||
} else if ("Always".equals(logic)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (list.isEmpty()
|
||||
|
||||
@@ -51,8 +51,8 @@ public class DigUntilAi extends SpellAbilityAi {
|
||||
// until opponent's end of turn phase!
|
||||
// But we still want more (and want to fill grave) if nothing better to do then
|
||||
// This is important for Replenish/Living Death type decks
|
||||
if (!((ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN))
|
||||
&& (!ai.getGame().getPhaseHandler().isPlayerTurn(ai)))) {
|
||||
if (!ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)
|
||||
&& !ai.getGame().getPhaseHandler().isPlayerTurn(ai)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class DigUntilAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
final String num = sa.getParam("Amount");
|
||||
if ((num != null) && num.equals("X") && sa.getSVar(num).equals("Count$xPaid")) {
|
||||
if (num != null && num.equals("X") && sa.getSVar(num).equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
SpellAbility root = sa.getRootAbility();
|
||||
if (root.getXManaCostPaid() == null) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class FlipOntoBattlefieldAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||
PhaseHandler ph = sa.getHostCard().getGame().getPhaseHandler();
|
||||
String logic = sa.getParamOrDefault("AILogic", "");
|
||||
|
||||
if (!SpellAbilityAi.isSorcerySpeed(sa) && sa.getPayCosts().hasManaCost()) {
|
||||
return ph.is(PhaseType.END_OF_TURN);
|
||||
}
|
||||
|
||||
if ("DamageCreatures".equals(logic)) {
|
||||
int maxToughness = Integer.valueOf(sa.getSubAbility().getParam("NumDmg"));
|
||||
CardCollectionView rightToughness = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
return card.getNetToughness() <= maxToughness && card.canBeDestroyed();
|
||||
}
|
||||
});
|
||||
return !rightToughness.isEmpty();
|
||||
}
|
||||
|
||||
return !aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||
return canPlayAI(aiPlayer, sa) || mandatory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -165,11 +165,10 @@ public class StaticData {
|
||||
return this.editions;
|
||||
}
|
||||
|
||||
public final CardEdition.Collection getCustomEditions(){
|
||||
public final CardEdition.Collection getCustomEditions() {
|
||||
return this.customEditions;
|
||||
}
|
||||
|
||||
|
||||
private List<CardEdition> sortedEditions;
|
||||
public final List<CardEdition> getSortedEditions() {
|
||||
if (sortedEditions == null) {
|
||||
@@ -189,13 +188,13 @@ public class StaticData {
|
||||
}
|
||||
|
||||
private TreeMap<CardEdition.Type, List<CardEdition>> editionsTypeMap;
|
||||
public final Map<CardEdition.Type, List<CardEdition>> getEditionsTypeMap(){
|
||||
if (editionsTypeMap == null){
|
||||
public final Map<CardEdition.Type, List<CardEdition>> getEditionsTypeMap() {
|
||||
if (editionsTypeMap == null) {
|
||||
editionsTypeMap = new TreeMap<>();
|
||||
for (CardEdition.Type editionType : CardEdition.Type.values()){
|
||||
for (CardEdition.Type editionType : CardEdition.Type.values()) {
|
||||
editionsTypeMap.put(editionType, new ArrayList<>());
|
||||
}
|
||||
for (CardEdition edition : this.getSortedEditions()){
|
||||
for (CardEdition edition : this.getSortedEditions()) {
|
||||
CardEdition.Type key = edition.getType();
|
||||
List<CardEdition> editionsOfType = editionsTypeMap.get(key);
|
||||
editionsOfType.add(edition);
|
||||
@@ -204,9 +203,9 @@ public class StaticData {
|
||||
return editionsTypeMap;
|
||||
}
|
||||
|
||||
public CardEdition getCardEdition(String setCode){
|
||||
public CardEdition getCardEdition(String setCode) {
|
||||
CardEdition edition = this.editions.get(setCode);
|
||||
if (edition == null) // try custom editions
|
||||
if (edition == null) // try custom editions
|
||||
edition = this.customEditions.get(setCode);
|
||||
return edition;
|
||||
}
|
||||
@@ -231,8 +230,7 @@ public class StaticData {
|
||||
public void attemptToLoadCard(String cardName){
|
||||
this.attemptToLoadCard(cardName, null);
|
||||
}
|
||||
|
||||
public void attemptToLoadCard(String cardName, String setCode){
|
||||
public void attemptToLoadCard(String cardName, String setCode) {
|
||||
CardRules rules = cardReader.attemptToLoadCard(cardName);
|
||||
CardRules customRules = null;
|
||||
if (customCardReader != null) {
|
||||
@@ -257,7 +255,7 @@ public class StaticData {
|
||||
* @param collectorNumber Card's collector Number
|
||||
* @return PaperCard instance found in one of the available CardDb databases, or <code>null</code> if not found.
|
||||
*/
|
||||
public PaperCard fetchCard(final String cardName, final String setCode, final String collectorNumber){
|
||||
public PaperCard fetchCard(final String cardName, final String setCode, final String collectorNumber) {
|
||||
PaperCard card = null;
|
||||
for (CardDb db : this.getAvailableDatabases().values()) {
|
||||
card = db.getCard(cardName, setCode, collectorNumber);
|
||||
@@ -345,7 +343,7 @@ public class StaticData {
|
||||
return result;
|
||||
}
|
||||
|
||||
private CardDb matchTargetCardDb(final String cardName){
|
||||
private CardDb matchTargetCardDb(final String cardName) {
|
||||
// NOTE: any foil request in cardName is NOT taken into account here.
|
||||
// It's a private method, so it's a fair assumption.
|
||||
for (CardDb targetDb : this.getAvailableDatabases().values()){
|
||||
@@ -360,7 +358,7 @@ public class StaticData {
|
||||
* @param cardName Name of the Card to verify (CASE SENSITIVE)
|
||||
* @return True if a card with the given input string can be found. False otherwise.
|
||||
*/
|
||||
public boolean isMTGCard(final String cardName){
|
||||
public boolean isMTGCard(final String cardName) {
|
||||
if (cardName == null || cardName.trim().length() == 0)
|
||||
return false;
|
||||
CardDb.CardRequest cr = CardDb.CardRequest.fromString(cardName); // accounts for any foil request ending with +
|
||||
@@ -450,10 +448,6 @@ public class StaticData {
|
||||
|
||||
public Predicate<PaperCard> getBrawlPredicate() { return brawlPredicate; }
|
||||
|
||||
public void setFilteredHandsEnabled(boolean filteredHandsEnabled){
|
||||
this.filteredHandsEnabled = filteredHandsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an alternative card print for the given card wrt. the input setReleaseDate.
|
||||
* The reference release date will be used to retrieve the alternative art, according
|
||||
@@ -501,7 +495,7 @@ public class StaticData {
|
||||
*/
|
||||
public PaperCard getAlternativeCardPrint(PaperCard card, Date setReleaseDate,
|
||||
boolean isCardArtPreferenceLatestArt,
|
||||
boolean cardArtPreferenceHasFilter){
|
||||
boolean cardArtPreferenceHasFilter) {
|
||||
Date searchReferenceDate = getReferenceDate(setReleaseDate, isCardArtPreferenceLatestArt);
|
||||
CardDb.CardArtPreference searchCardArtStrategy = getSearchStrategyForAlternativeCardArt(isCardArtPreferenceLatestArt,
|
||||
cardArtPreferenceHasFilter);
|
||||
@@ -537,7 +531,6 @@ public class StaticData {
|
||||
public PaperCard getAlternativeCardPrint(PaperCard card, Date setReleaseDate, boolean isCardArtPreferenceLatestArt,
|
||||
boolean cardArtPreferenceHasFilter,
|
||||
boolean preferCandidatesFromExpansionSets, boolean preferModernFrame) {
|
||||
|
||||
PaperCard altCard = this.getAlternativeCardPrint(card, setReleaseDate, isCardArtPreferenceLatestArt,
|
||||
cardArtPreferenceHasFilter);
|
||||
if (altCard == null)
|
||||
@@ -618,7 +611,7 @@ public class StaticData {
|
||||
|
||||
private PaperCard tryToGetCardPrintFromExpansionSet(PaperCard altCard,
|
||||
boolean isCardArtPreferenceLatestArt,
|
||||
boolean preferModernFrame){
|
||||
boolean preferModernFrame) {
|
||||
CardEdition altCardEdition = editions.get(altCard.getEdition());
|
||||
if (altCardEdition.getType() == CardEdition.Type.EXPANSION)
|
||||
return null; // Nothing to do here!
|
||||
@@ -628,7 +621,7 @@ public class StaticData {
|
||||
CardDb.CardArtPreference searchStrategy = getSearchStrategyForAlternativeCardArt(searchStrategyFlag,
|
||||
true);
|
||||
PaperCard altCandidate = altCard;
|
||||
while (altCandidate != null){
|
||||
while (altCandidate != null) {
|
||||
Date referenceDate = editions.get(altCandidate.getEdition()).getDate();
|
||||
altCandidate = this.searchAlternativeCardCandidate(altCandidate, preferModernFrame,
|
||||
referenceDate, searchStrategy);
|
||||
@@ -645,7 +638,7 @@ public class StaticData {
|
||||
private PaperCard tryToGetCardPrintWithMatchingFrame(PaperCard altCard,
|
||||
boolean isCardArtPreferenceLatestArt,
|
||||
boolean cardArtHasFilter,
|
||||
boolean preferModernFrame){
|
||||
boolean preferModernFrame) {
|
||||
CardEdition altCardEdition = editions.get(altCard.getEdition());
|
||||
boolean frameIsCompliantAlready = (altCardEdition.isModern() == preferModernFrame);
|
||||
if (frameIsCompliantAlready)
|
||||
@@ -654,7 +647,7 @@ public class StaticData {
|
||||
CardDb.CardArtPreference searchStrategy = getSearchStrategyForAlternativeCardArt(searchStrategyFlag,
|
||||
cardArtHasFilter);
|
||||
PaperCard altCandidate = altCard;
|
||||
while (altCandidate != null){
|
||||
while (altCandidate != null) {
|
||||
Date referenceDate = editions.get(altCandidate.getEdition()).getDate();
|
||||
altCandidate = this.searchAlternativeCardCandidate(altCandidate, preferModernFrame,
|
||||
referenceDate, searchStrategy);
|
||||
@@ -677,7 +670,7 @@ public class StaticData {
|
||||
* @param card Instance of target <code>PaperCard</code>
|
||||
* @return The number of available arts for the given card in the corresponding set, or 0 if not found.
|
||||
*/
|
||||
public int getCardArtCount(PaperCard card){
|
||||
public int getCardArtCount(PaperCard card) {
|
||||
Collection<CardDb> databases = this.getAvailableDatabases().values();
|
||||
for (CardDb db: databases){
|
||||
int artCount = db.getArtCount(card.getName(), card.getEdition());
|
||||
@@ -687,9 +680,12 @@ public class StaticData {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean getFilteredHandsEnabled(){
|
||||
public boolean getFilteredHandsEnabled() {
|
||||
return filteredHandsEnabled;
|
||||
}
|
||||
public void setFilteredHandsEnabled(boolean filteredHandsEnabled) {
|
||||
this.filteredHandsEnabled = filteredHandsEnabled;
|
||||
}
|
||||
|
||||
public void setMulliganRule(MulliganDefs.MulliganRule rule) {
|
||||
mulliganRule = rule;
|
||||
@@ -699,21 +695,21 @@ public class StaticData {
|
||||
return mulliganRule;
|
||||
}
|
||||
|
||||
public void setCardArtPreference(boolean latestArt, boolean coreExpansionOnly){
|
||||
public void setCardArtPreference(boolean latestArt, boolean coreExpansionOnly) {
|
||||
this.commonCards.setCardArtPreference(latestArt, coreExpansionOnly);
|
||||
this.variantCards.setCardArtPreference(latestArt, coreExpansionOnly);
|
||||
this.customCards.setCardArtPreference(latestArt, coreExpansionOnly);
|
||||
}
|
||||
|
||||
public String getCardArtPreferenceName(){
|
||||
public String getCardArtPreferenceName() {
|
||||
return this.commonCards.getCardArtPreference().toString();
|
||||
}
|
||||
|
||||
public CardDb.CardArtPreference getCardArtPreference(){
|
||||
public CardDb.CardArtPreference getCardArtPreference() {
|
||||
return this.commonCards.getCardArtPreference();
|
||||
}
|
||||
|
||||
public CardDb.CardArtPreference getCardArtPreference(boolean latestArt, boolean coreExpansionOnly){
|
||||
public CardDb.CardArtPreference getCardArtPreference(boolean latestArt, boolean coreExpansionOnly) {
|
||||
if (latestArt){
|
||||
return coreExpansionOnly ? CardDb.CardArtPreference.LATEST_ART_CORE_EXPANSIONS_REPRINT_ONLY : CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS;
|
||||
}
|
||||
@@ -721,15 +717,15 @@ public class StaticData {
|
||||
}
|
||||
|
||||
|
||||
public boolean isCoreExpansionOnlyFilterSet(){ return this.commonCards.getCardArtPreference().filterSets; }
|
||||
public boolean isCoreExpansionOnlyFilterSet() { return this.commonCards.getCardArtPreference().filterSets; }
|
||||
|
||||
public boolean cardArtPreferenceIsLatest(){
|
||||
public boolean cardArtPreferenceIsLatest() {
|
||||
return this.commonCards.getCardArtPreference().latestFirst;
|
||||
}
|
||||
|
||||
// === MOBILE APP Alternative Methods (using String Labels, not yet localised!!) ===
|
||||
// Note: only used in mobile
|
||||
public String[] getCardArtAvailablePreferences(){
|
||||
public String[] getCardArtAvailablePreferences() {
|
||||
CardDb.CardArtPreference[] preferences = CardDb.CardArtPreference.values();
|
||||
String[] preferences_avails = new String[preferences.length];
|
||||
for (int i = 0; i < preferences.length; i++)
|
||||
@@ -745,17 +741,16 @@ public class StaticData {
|
||||
return label.toString().trim();
|
||||
}
|
||||
|
||||
public void setCardArtPreference(String artPreference){
|
||||
public void setCardArtPreference(String artPreference) {
|
||||
this.commonCards.setCardArtPreference(artPreference);
|
||||
this.variantCards.setCardArtPreference(artPreference);
|
||||
this.customCards.setCardArtPreference(artPreference);
|
||||
}
|
||||
|
||||
//
|
||||
public boolean isEnabledCardArtSmartSelection(){
|
||||
public boolean isEnabledCardArtSmartSelection() {
|
||||
return this.enableSmartCardArtSelection;
|
||||
}
|
||||
public void setEnableSmartCardArtSelection(boolean isEnabled){
|
||||
public void setEnableSmartCardArtSelection(boolean isEnabled) {
|
||||
this.enableSmartCardArtSelection = isEnabled;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
|
||||
public void add(final String cardRequest, final int amount) {
|
||||
CardDb.CardRequest request = CardDb.CardRequest.fromString(cardRequest);
|
||||
this.add(request.cardName, request.edition, request.artIndex, amount);
|
||||
this.add(CardDb.CardRequest.compose(request.cardName, request.isFoil), request.edition, request.artIndex, amount);
|
||||
}
|
||||
|
||||
public void add(final String cardName, final String setCode) {
|
||||
@@ -100,7 +100,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
paperCard = StaticData.instance().getCommonCards().getCard(cardName);
|
||||
selectedDbName = "Common";
|
||||
}
|
||||
if (paperCard == null){
|
||||
if (paperCard == null) {
|
||||
// after all still null
|
||||
System.err.println("An unsupported card was requested: \"" + cardName + "\" from \"" + setCode + "\". \n");
|
||||
paperCard = StaticData.instance().getCommonCards().createUnsupportedCard(cardName);
|
||||
@@ -130,7 +130,6 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add all from a List of CardPrinted.
|
||||
*
|
||||
@@ -222,10 +221,10 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
*
|
||||
* @see CardPool#getCardEditionStatistics(boolean)
|
||||
*/
|
||||
public Map<CardEdition.Type, Integer> getCardEditionTypeStatistics(boolean includeBasicLands){
|
||||
public Map<CardEdition.Type, Integer> getCardEditionTypeStatistics(boolean includeBasicLands) {
|
||||
Map<CardEdition.Type, Integer> editionTypeStats = new HashMap<>();
|
||||
Map<CardEdition, Integer> editionStatistics = this.getCardEditionStatistics(includeBasicLands);
|
||||
for(Entry<CardEdition, Integer> entry : editionStatistics.entrySet()) {
|
||||
for (Entry<CardEdition, Integer> entry : editionStatistics.entrySet()) {
|
||||
CardEdition edition = entry.getKey();
|
||||
int count = entry.getValue();
|
||||
CardEdition.Type key = edition.getType();
|
||||
@@ -242,11 +241,11 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
*
|
||||
* @return The most frequent CardEdition.Type in the pool, or null if the Pool is empty
|
||||
*/
|
||||
public CardEdition.Type getTheMostFrequentEditionType(){
|
||||
public CardEdition.Type getTheMostFrequentEditionType() {
|
||||
Map<CardEdition.Type, Integer> editionTypeStats = this.getCardEditionTypeStatistics(false);
|
||||
Integer mostFrequentType = 0;
|
||||
List<CardEdition.Type> mostFrequentEditionTypes = new ArrayList<>();
|
||||
for (Map.Entry<CardEdition.Type, Integer> entry : editionTypeStats.entrySet()){
|
||||
for (Map.Entry<CardEdition.Type, Integer> entry : editionTypeStats.entrySet()) {
|
||||
if (entry.getValue() > mostFrequentType) {
|
||||
mostFrequentType = entry.getValue();
|
||||
mostFrequentEditionTypes.add(entry.getKey());
|
||||
@@ -271,11 +270,11 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
* If the count of Modern and PreModern cards is tied, the return value is determined
|
||||
* by the preferred Card Art Preference settings, namely True if Latest Art, False otherwise.
|
||||
*/
|
||||
public boolean isModern(){
|
||||
public boolean isModern() {
|
||||
int modernEditionsCount = 0;
|
||||
int preModernEditionsCount = 0;
|
||||
Map<CardEdition, Integer> editionStats = this.getCardEditionStatistics(false);
|
||||
for (Map.Entry<CardEdition, Integer> entry: editionStats.entrySet()){
|
||||
for (Map.Entry<CardEdition, Integer> entry: editionStats.entrySet()) {
|
||||
CardEdition edition = entry.getKey();
|
||||
if (edition.isModern())
|
||||
modernEditionsCount += entry.getValue();
|
||||
@@ -364,7 +363,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
private static int getMedianFrequency(List<Integer> frequencyValues, float meanFrequency) {
|
||||
int medianFrequency = frequencyValues.get(0);
|
||||
float refDelta = Math.abs(meanFrequency - medianFrequency);
|
||||
for (int i = 1; i < frequencyValues.size(); i++){
|
||||
for (int i = 1; i < frequencyValues.size(); i++) {
|
||||
int currentFrequency = frequencyValues.get(i);
|
||||
float delta = Math.abs(meanFrequency - currentFrequency);
|
||||
if (delta < refDelta) {
|
||||
@@ -411,7 +410,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
return pool;
|
||||
}
|
||||
|
||||
public static List<Pair<String, Integer>> processCardList(final Iterable<String> lines){
|
||||
public static List<Pair<String, Integer>> processCardList(final Iterable<String> lines) {
|
||||
List<Pair<String, Integer>> cardRequests = new ArrayList<>();
|
||||
if (lines == null)
|
||||
return cardRequests; // empty list
|
||||
@@ -466,7 +465,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
public CardPool getFilteredPool(Predicate<PaperCard> predicate) {
|
||||
CardPool filteredPool = new CardPool();
|
||||
Iterator<PaperCard> cardsInPool = this.items.keySet().iterator();
|
||||
while (cardsInPool.hasNext()){
|
||||
while (cardsInPool.hasNext()) {
|
||||
PaperCard c = cardsInPool.next();
|
||||
if (predicate.apply(c))
|
||||
filteredPool.add(c, this.items.get(c));
|
||||
@@ -479,12 +478,12 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
* @param predicate the Predicate to apply to this CardPool
|
||||
* @return a new CardPool made from this CardPool with only the cards that agree with the provided Predicate
|
||||
*/
|
||||
public CardPool getFilteredPoolWithCardsCount(Predicate<PaperCard> predicate){
|
||||
public CardPool getFilteredPoolWithCardsCount(Predicate<PaperCard> predicate) {
|
||||
CardPool filteredPool = new CardPool();
|
||||
for(Entry<PaperCard, Integer> entry : this.items.entrySet()){
|
||||
for (Entry<PaperCard, Integer> entry : this.items.entrySet()) {
|
||||
PaperCard pc = entry.getKey();
|
||||
int count = entry.getValue();
|
||||
if(predicate.apply(pc))
|
||||
if (predicate.apply(pc))
|
||||
filteredPool.add(pc, count);
|
||||
}
|
||||
return filteredPool;
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.card.CardDb;
|
||||
import forge.card.CardEdition;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.*;
|
||||
@@ -216,7 +217,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
}
|
||||
|
||||
private void loadDeferredSections() {
|
||||
if ((deferredSections == null) && (loadedSections == null))
|
||||
if (deferredSections == null && loadedSections == null)
|
||||
return;
|
||||
|
||||
if (loadedSections != null && !includeCardsFromUnspecifiedSet)
|
||||
@@ -236,8 +237,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
if (deferredSections != null) {
|
||||
this.validateDeferredSections();
|
||||
referenceDeckLoadingMap = new HashMap<>(this.deferredSections);
|
||||
}
|
||||
else
|
||||
} else
|
||||
referenceDeckLoadingMap = new HashMap<>(loadedSections);
|
||||
|
||||
loadedSections = new HashMap<>();
|
||||
@@ -255,7 +255,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
continue;
|
||||
final List<String> cardsInSection = s.getValue();
|
||||
ArrayList<String> cardNamesWithNoEdition = getAllCardNamesWithNoSpecifiedEdition(cardsInSection);
|
||||
if (cardNamesWithNoEdition.size() > 0){
|
||||
if (cardNamesWithNoEdition.size() > 0) {
|
||||
includeCardsFromUnspecifiedSet = true;
|
||||
if (smartCardArtSelection)
|
||||
cardsWithNoEdition.put(sec, cardNamesWithNoEdition);
|
||||
@@ -267,10 +267,9 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
deferredSections = null; // set to null, just in case!
|
||||
if (includeCardsFromUnspecifiedSet && smartCardArtSelection)
|
||||
optimiseCardArtSelectionInDeckSections(cardsWithNoEdition);
|
||||
|
||||
}
|
||||
|
||||
private void validateDeferredSections(){
|
||||
private void validateDeferredSections() {
|
||||
/*
|
||||
Construct a temporary (DeckSection, CardPool) Maps, to be sanitised and finalised
|
||||
before copying into `this.parts`. This sanitisation is applied because of the
|
||||
@@ -285,6 +284,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
}
|
||||
|
||||
final List<String> cardsInSection = s.getValue();
|
||||
List<Pair<String, Integer>> originalCardRequests = CardPool.processCardList(cardsInSection);
|
||||
CardPool pool = CardPool.fromCardList(cardsInSection);
|
||||
if (pool.countDistinct() == 0)
|
||||
continue; // pool empty, no card has been found!
|
||||
@@ -301,13 +301,12 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
if (whiteList == null)
|
||||
whiteList = new ArrayList<>();
|
||||
for (Entry<PaperCard, Integer> entry : filteredPool) {
|
||||
String poolRequest = getPoolRequest(entry);
|
||||
String poolRequest = getPoolRequest(entry, originalCardRequests);
|
||||
whiteList.add(poolRequest);
|
||||
}
|
||||
validatedSections.put(s.getKey(), whiteList);
|
||||
|
||||
if (filteredPool.countDistinct() != pool.countDistinct()){
|
||||
|
||||
if (filteredPool.countDistinct() != pool.countDistinct()) {
|
||||
CardPool blackList = pool.getFilteredPoolWithCardsCount(new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard input) {
|
||||
@@ -317,7 +316,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
|
||||
for (Entry<PaperCard, Integer> entry : blackList) {
|
||||
DeckSection cardSection = DeckSection.matchingSection(entry.getKey());
|
||||
String poolRequest = getPoolRequest(entry);
|
||||
String poolRequest = getPoolRequest(entry, originalCardRequests);
|
||||
List<String> sectionCardList = validatedSections.getOrDefault(cardSection.name(), null);
|
||||
if (sectionCardList == null)
|
||||
sectionCardList = new ArrayList<>();
|
||||
@@ -331,11 +330,26 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
this.deferredSections = validatedSections;
|
||||
}
|
||||
|
||||
private String getPoolRequest(Entry<PaperCard, Integer> entry) {
|
||||
private String getPoolRequest(Entry<PaperCard, Integer> entry, List<Pair<String, Integer>> originalCardRequests) {
|
||||
PaperCard card = entry.getKey();
|
||||
int amount = entry.getValue();
|
||||
String cardRequest = CardDb.CardRequest.compose(card.getName(), card.getEdition(), card.getArtIndex());
|
||||
return String.format("%d %s", amount, cardRequest);
|
||||
String poolCardRequest = CardDb.CardRequest.compose(
|
||||
card.isFoil() ? CardDb.CardRequest.compose(card.getName(), true) : card.getName(),
|
||||
card.getEdition(), card.getArtIndex());
|
||||
String originalRequestCandidate = null;
|
||||
for (Pair<String, Integer> originalRequest : originalCardRequests){
|
||||
String cardRequest = originalRequest.getLeft();
|
||||
if (!StringUtils.startsWithIgnoreCase(poolCardRequest, cardRequest))
|
||||
continue;
|
||||
originalRequestCandidate = cardRequest;
|
||||
int cardAmount = originalRequest.getRight();
|
||||
if (amount == cardAmount)
|
||||
return String.format("%d %s", cardAmount, cardRequest);
|
||||
}
|
||||
// This is just in case, it should never happen as we're
|
||||
if (originalRequestCandidate != null)
|
||||
return String.format("%d %s", amount, originalRequestCandidate);
|
||||
return String.format("%d %s", amount, poolCardRequest);
|
||||
}
|
||||
|
||||
private ArrayList<String> getAllCardNamesWithNoSpecifiedEdition(List<String> cardsInSection) {
|
||||
@@ -356,9 +370,9 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
boolean isCardArtPreferenceLatestArt = data.cardArtPreferenceIsLatest();
|
||||
boolean cardArtPreferenceHasFilter = data.isCoreExpansionOnlyFilterSet();
|
||||
|
||||
for(Entry<DeckSection, CardPool> part : parts.entrySet()) {
|
||||
for (Entry<DeckSection, CardPool> part : parts.entrySet()) {
|
||||
DeckSection deckSection = part.getKey();
|
||||
if(deckSection == DeckSection.Planes || deckSection == DeckSection.Schemes || deckSection == DeckSection.Avatar)
|
||||
if (deckSection != DeckSection.Main && deckSection != DeckSection.Sideboard && deckSection != DeckSection.Commander)
|
||||
continue;
|
||||
|
||||
// == 0. First Off, check if there is anything at all to do for the current section
|
||||
@@ -432,7 +446,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
cardsPerArtIndex = Math.max(1, cardsPerArtIndex); // make sure is never zero
|
||||
int cardsAdded = 0;
|
||||
PaperCard alternativeCardArt = null;
|
||||
for (int artIndex = 1; artIndex <= nrOfAvailableArts; artIndex++){
|
||||
for (int artIndex = 1; artIndex <= nrOfAvailableArts; artIndex++) {
|
||||
alternativeCardArt = data.getOrLoadCommonCard(cardName, setCode, artIndex, isFoil);
|
||||
cardsAdded += cardsPerArtIndex;
|
||||
pool.add(alternativeCardArt, cardsPerArtIndex);
|
||||
@@ -461,7 +475,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
|
||||
if (card.getRules().isVariant())
|
||||
return false; // skip variant cards
|
||||
if (StaticData.instance().getCommonCards().hasPreferredArt(card.getName())){
|
||||
if (StaticData.instance().getCommonCards().hasPreferredArt(card.getName())) {
|
||||
// if there is any preferred art, never update it!
|
||||
CardDb.CardRequest request = CardDb.CardRequest.fromString(card.getName());
|
||||
if (request.edition.equals(card.getEdition()) && request.artIndex == card.getArtIndex())
|
||||
@@ -478,8 +492,6 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
return releaseDate.compareTo(referenceReleaseDate) < 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final Function<Deck, String> FN_NAME_SELECTOR = new Function<Deck, String>() {
|
||||
@Override
|
||||
public String apply(Deck arg1) {
|
||||
|
||||
@@ -123,6 +123,14 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
}
|
||||
return this.foiledVersion;
|
||||
}
|
||||
public PaperCard getUnFoiled() {
|
||||
if (!this.foil)
|
||||
return this;
|
||||
|
||||
PaperCard unFoiledVersion = new PaperCard(this.rules, this.edition, this.rarity,
|
||||
this.artIndex, false, String.valueOf(collectorNumber), this.artist);
|
||||
return unFoiledVersion;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String getImageKey() {
|
||||
@@ -260,7 +268,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
return sortableCNKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(final IPaperCard o) {
|
||||
final int nameCmp = name.compareToIgnoreCase(o.getName());
|
||||
|
||||
@@ -830,7 +830,7 @@ public class Game {
|
||||
c.removeTempController(p);
|
||||
// return stolen spells
|
||||
if (c.isInZone(ZoneType.Stack)) {
|
||||
SpellAbilityStackInstance si = getStack().getInstanceFromSpellAbility(c.getCastSA());
|
||||
SpellAbilityStackInstance si = getStack().getInstanceMatchingSpellAbilityID(c.getCastSA());
|
||||
si.setActivatingPlayer(c.getController());
|
||||
}
|
||||
if (c.getController().equals(p)) {
|
||||
@@ -854,7 +854,7 @@ public class Game {
|
||||
|
||||
if (p != null && p.isMonarch()) {
|
||||
// if the player who lost was the Monarch, someone else will be the monarch
|
||||
if(p.equals(getPhaseHandler().getPlayerTurn())) {
|
||||
if (p.equals(getPhaseHandler().getPlayerTurn())) {
|
||||
getAction().becomeMonarch(getNextPlayerAfter(p), null);
|
||||
} else {
|
||||
getAction().becomeMonarch(getPhaseHandler().getPlayerTurn(), null);
|
||||
|
||||
@@ -997,6 +997,9 @@ public class GameAction {
|
||||
}
|
||||
|
||||
public void ceaseToExist(Card c, boolean skipTrig) {
|
||||
if (c.isInZone(ZoneType.Stack)) {
|
||||
c.getGame().getStack().remove(c);
|
||||
}
|
||||
c.getZone().remove(c);
|
||||
|
||||
// CR 603.6c other players LTB triggers should work
|
||||
|
||||
@@ -1,24 +1,7 @@
|
||||
package forge.game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
@@ -40,7 +23,11 @@ import forge.util.Localizer;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.collect.FCollectionView;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class Match {
|
||||
private static List<PaperCard> removedCards = Lists.newArrayList();
|
||||
private final List<RegisteredPlayer> players;
|
||||
private final GameRules rules;
|
||||
private final String title;
|
||||
@@ -198,6 +185,12 @@ public class Match {
|
||||
return myRemovedAnteCards;
|
||||
}
|
||||
|
||||
public static List<PaperCard> getRemovedCards() { return removedCards; }
|
||||
|
||||
public void removeCard(PaperCard c) {
|
||||
removedCards.add(c);
|
||||
}
|
||||
|
||||
private static void preparePlayerZone(Player player, final ZoneType zoneType, CardPool section, boolean canRandomFoil) {
|
||||
PlayerZone library = player.getZone(zoneType);
|
||||
List<Card> newLibrary = new ArrayList<>();
|
||||
@@ -257,6 +250,23 @@ public class Match {
|
||||
}
|
||||
|
||||
Deck toChange = psc.getDeck();
|
||||
if (!getRemovedCards().isEmpty()) {
|
||||
CardPool main = new CardPool();
|
||||
main.addAll(toChange.get(DeckSection.Main));
|
||||
CardPool sideboard = new CardPool();
|
||||
sideboard.addAll(toChange.getOrCreate(DeckSection.Sideboard));
|
||||
for (PaperCard c : removedCards) {
|
||||
if (main.contains(c)) {
|
||||
main.remove(c, 1);
|
||||
} else if (sideboard.contains(c)) {
|
||||
sideboard.remove(c, 1);
|
||||
}
|
||||
}
|
||||
toChange.getMain().clear();
|
||||
toChange.getMain().addAll(main);
|
||||
toChange.get(DeckSection.Sideboard).clear();
|
||||
toChange.get(DeckSection.Sideboard).addAll(sideboard);
|
||||
}
|
||||
List<PaperCard> newMain = person.sideboard(toChange, rules.getGameType(), player.getName());
|
||||
if (null != newMain) {
|
||||
CardPool allCards = new CardPool();
|
||||
|
||||
@@ -1789,7 +1789,7 @@ public class AbilityUtils {
|
||||
if (sq[0].contains("HasNumChosenColors")) {
|
||||
int sum = 0;
|
||||
for (Card card : getDefinedCards(c, sq[1], sa)) {
|
||||
sum += CardUtil.getColors(card).getSharedColors(ColorSet.fromNames(c.getChosenColors())).countColors();
|
||||
sum += card.determineColor().getSharedColors(ColorSet.fromNames(c.getChosenColors())).countColors();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -1990,7 +1990,7 @@ public class AbilityUtils {
|
||||
|
||||
// Count$CardMulticolor.<numMC>.<numNotMC>
|
||||
if (sq[0].contains("CardMulticolor")) {
|
||||
final boolean isMulti = CardUtil.getColors(c).isMulticolor();
|
||||
final boolean isMulti = c.determineColor().isMulticolor();
|
||||
return doXMath(Integer.parseInt(sq[isMulti ? 1 : 2]), expr, c, ctb);
|
||||
}
|
||||
// Count$Madness.<True>.<False>
|
||||
@@ -2046,7 +2046,7 @@ public class AbilityUtils {
|
||||
}
|
||||
|
||||
if (sq[0].contains("CardNumColors")) {
|
||||
return doXMath(CardUtil.getColors(c).countColors(), expr, c, ctb);
|
||||
return doXMath(c.determineColor().countColors(), expr, c, ctb);
|
||||
}
|
||||
if (sq[0].contains("CardNumAttacksThisTurn")) {
|
||||
return doXMath(c.getDamageHistory().getCreatureAttacksThisTurn(), expr, c, ctb);
|
||||
@@ -3766,7 +3766,7 @@ public class AbilityUtils {
|
||||
someCards = CardLists.filter(someCards, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CardUtil.getColors(c).isMulticolor();
|
||||
return c.determineColor().isMulticolor();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3775,7 +3775,7 @@ public class AbilityUtils {
|
||||
someCards = CardLists.filter(someCards, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CardUtil.getColors(c).isMonoColor();
|
||||
return c.determineColor().isMonoColor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ public enum ApiType {
|
||||
Explore (ExploreEffect.class),
|
||||
Fight (FightEffect.class),
|
||||
FlipACoin (FlipCoinEffect.class),
|
||||
FlipOntoBattlefield (FlipOntoBattlefieldEffect.class),
|
||||
Fog (FogEffect.class),
|
||||
GainControl (ControlGainEffect.class),
|
||||
GainControlVariant (ControlGainVariantEffect.class),
|
||||
@@ -139,6 +140,7 @@ public enum ApiType {
|
||||
RemoveCounterAll (CountersRemoveAllEffect.class),
|
||||
RemoveFromCombat (RemoveFromCombatEffect.class),
|
||||
RemoveFromGame (RemoveFromGameEffect.class),
|
||||
RemoveFromMatch (RemoveFromMatchEffect.class),
|
||||
ReorderZone (ReorderZoneEffect.class),
|
||||
Repeat (RepeatEffect.class),
|
||||
RepeatEach (RepeatEachEffect.class),
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.google.common.collect.Lists;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.event.GameEventCardModeChosen;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -81,6 +82,19 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
chosenSAs = p.getController().chooseSpellAbilitiesForEffect(abilities, sa, prompt, amount, ImmutableMap.of());
|
||||
}
|
||||
|
||||
for (SpellAbility chosenSA : chosenSAs) {
|
||||
if (sa.hasParam("AtRandom") && sa.getParam("AtRandom").equals("Urza") && chosenSA.usesTargeting()) {
|
||||
List<Card> validTargets = CardUtil.getValidCardsToTarget(chosenSA.getTargetRestrictions(), sa);
|
||||
if (validTargets.isEmpty()) {
|
||||
List <SpellAbility> newChosenSAs = Lists.newArrayList();
|
||||
Aggregates.random(abilities, amount, newChosenSAs);
|
||||
chosenSAs = newChosenSAs;
|
||||
} else {
|
||||
p.getController().chooseTargetsFor(chosenSA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!chosenSAs.isEmpty()) {
|
||||
for (SpellAbility chosenSA : chosenSAs) {
|
||||
String chosenValue = chosenSA.getDescription();
|
||||
|
||||
@@ -42,8 +42,9 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
final int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa);
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
|
||||
sb.append(host.getController()).append(" looks at the top ");
|
||||
sb.append(Lang.nounWithAmount(numToDig, "card")).append(" of ");
|
||||
sb.append(host.getController()).append(sa.hasParam("Reveal") && sa.getParam("Reveal").equals("True")
|
||||
? " reveals " : " looks at ").append("the top ");
|
||||
sb.append(numToDig == 1 ? "card" : (Lang.getNumeral(numToDig) + " cards")).append(" of ");
|
||||
|
||||
if (tgtPlayers.contains(host.getController())) {
|
||||
sb.append("their ");
|
||||
|
||||
@@ -179,12 +179,12 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()))) {
|
||||
continue;
|
||||
}
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
Card m = null;
|
||||
if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
|
||||
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa, moveParams);
|
||||
if (sa.hasParam("Tapped")) {
|
||||
c.setTapped(true);
|
||||
@@ -195,7 +195,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
} else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
|
||||
//Don't do anything
|
||||
} else {
|
||||
m = game.getAction().moveTo(foundDest, c, foundLibPos, sa);
|
||||
m = game.getAction().moveTo(foundDest, c, foundLibPos, sa, moveParams);
|
||||
}
|
||||
revealed.remove(c);
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class FlipOntoBattlefieldEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
// Basic parameters defining the chances
|
||||
final float chanceToFlip = 0.85f;
|
||||
final int maxFlipTimes = 2;
|
||||
final float chanceToHit = 0.70f;
|
||||
final float chanceToHitTwoCards = 0.20f;
|
||||
|
||||
final Card host = sa.getHostCard();
|
||||
final Player p = sa.getActivatingPlayer();
|
||||
final Game game = host.getGame();
|
||||
boolean flippedOnce = false;
|
||||
|
||||
// TODO: allow to make a bounding box of sorts somehow, ideally - upgrade to a full system allowing to actually target by location
|
||||
CardCollectionView tgtBox = p.getController().chooseCardsForEffect(game.getCardsIn(ZoneType.Battlefield), sa, Localizer.getInstance().getMessage("lblChooseDesiredLocation"), 1, 1, sa.hasParam("AllowRandom"), null);
|
||||
|
||||
Card tgtLoc = tgtBox.getFirst();
|
||||
|
||||
Card lhsNeighbor = getNeighboringCard(tgtLoc, -1);
|
||||
Card rhsNeighbor = getNeighboringCard(tgtLoc, 1);
|
||||
|
||||
CardCollection randChoices = new CardCollection();
|
||||
randChoices.add(tgtLoc);
|
||||
if (lhsNeighbor != null) {
|
||||
randChoices.add(lhsNeighbor);
|
||||
} else if (rhsNeighbor != null) {
|
||||
randChoices.add(rhsNeighbor);
|
||||
}
|
||||
|
||||
// TODO: would be fun to add a small chance (e.g. 3-5%) to land unpredictably on some random target?
|
||||
|
||||
flippedOnce = MyRandom.getRandom().nextFloat() <= chanceToFlip; // 20% chance that the card won't flip even once
|
||||
if (!flippedOnce) {
|
||||
sa.setSVar("TimesFlipped", "0");
|
||||
game.getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblDidNotFlipOver"), null);
|
||||
return;
|
||||
} else {
|
||||
int flippedTimes = MyRandom.getRandom().nextInt(maxFlipTimes) + 1;
|
||||
sa.setSVar("TimesFlipped", String.valueOf(flippedTimes)); // Currently the exact # of times is unused
|
||||
game.getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblFlippedOver", flippedTimes), null);
|
||||
}
|
||||
|
||||
// Choose what was hit
|
||||
CardCollection hit = new CardCollection();
|
||||
float outcome = MyRandom.getRandom().nextFloat();
|
||||
if (outcome <= chanceToHitTwoCards) {
|
||||
hit.addAll(Aggregates.random(randChoices, randChoices.size() > 1 ? 2 : 1));
|
||||
if (hit.size() == 2) {
|
||||
game.getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblLandedOnTwoCards", hit.getFirst(), hit.getLast()), null);
|
||||
} else {
|
||||
game.getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblLandedOnOneCard", hit.getFirst()), null);
|
||||
}
|
||||
}
|
||||
else if (outcome <= chanceToHit) {
|
||||
hit.add(Aggregates.random(randChoices));
|
||||
game.getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblLandedOnOneCard", hit.getFirst()), null);
|
||||
} else {
|
||||
game.getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblDidNotLandOnCards"), null);
|
||||
}
|
||||
|
||||
// Remember whatever was hit
|
||||
for (Card c : hit) {
|
||||
host.addRemembered(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Card host = sa.getHostCard();
|
||||
final Player p = sa.getActivatingPlayer();
|
||||
|
||||
sb.append("Flip ");
|
||||
sb.append(host.toString());
|
||||
sb.append(" onto the battlefield from a height of at least one foot.");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Card getNeighboringCard(Card c, int direction) {
|
||||
// Currently gets the nearest (in zone order) card to the left or to the right of the designated one by type
|
||||
Player controller = c.getController();
|
||||
ArrayList<Card> cardsOTB = Lists.newArrayList(CardLists.filter(
|
||||
controller.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
if (c.isCreature()) {
|
||||
return card.isCreature();
|
||||
} else if (c.isPlaneswalker() || c.isArtifact() || (c.isEnchantment() && !c.isAura())) {
|
||||
return card.isPlaneswalker() || card.isArtifact() || (c.isEnchantment() && !c.isAura());
|
||||
} else if (c.isLand()) {
|
||||
return card.isLand();
|
||||
} else if (c.isAttachedToEntity()) {
|
||||
return card.isAttachedToEntity(c.getEntityAttachedTo()) || c.equals(card.getAttachedTo());
|
||||
}
|
||||
return card.sharesCardTypeWith(c);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
int loc = cardsOTB.indexOf(c);
|
||||
if (direction < 0 && loc > 0) {
|
||||
return cardsOTB.get(loc - 1);
|
||||
} else if (loc < cardsOTB.size() - 1) {
|
||||
return cardsOTB.get(loc + 1);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -99,12 +99,8 @@ public class MillEffect extends SpellAbilityEffect {
|
||||
} else if (dest.equals(ZoneType.Ante)) {
|
||||
sb.append("antes ");
|
||||
}
|
||||
sb.append(numCards);
|
||||
sb.append(" card");
|
||||
if (numCards != 1) {
|
||||
sb.append("s");
|
||||
}
|
||||
sb.append(".");
|
||||
|
||||
sb.append(numCards == 1 ? "a card" : (Lang.getNumeral(numCards) + " cards")).append(".");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardFactory;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.item.PaperCard;
|
||||
@@ -35,7 +34,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
|
||||
cards = Lists.newArrayList(Iterables.filter(cards, cpp));
|
||||
}
|
||||
// current color of source card
|
||||
final ColorSet color = CardUtil.getColors(source);
|
||||
final ColorSet color = source.determineColor();
|
||||
if (color.isColorless()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -65,7 +64,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
} else if (sa.getParam("Gains").equals("TargetedCardColor")) {
|
||||
for (final Card c : sa.getSATargetingCard().getTargets().getTargetCards()) {
|
||||
ColorSet cs = CardUtil.getColors(c);
|
||||
ColorSet cs = c.determineColor();
|
||||
for (byte col : MagicColor.WUBRG) {
|
||||
if (cs.hasAnyColor(col))
|
||||
gains.add(MagicColor.toLongString(col).toLowerCase());
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.event.GameEventRandomLog;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
public class RemoveFromMatchEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
CardCollection toRemove;
|
||||
|
||||
if (sa.hasParam("RemoveType")) {
|
||||
CardCollection cards = (CardCollection) host.getOwner().getGame().getCardsInGame();
|
||||
if (sa.hasParam("IncludeSideboard")) {
|
||||
CardCollection sideboard = (CardCollection) host.getGame().getCardsIn(ZoneType.Sideboard);
|
||||
cards.addAll(sideboard);
|
||||
}
|
||||
toRemove = (CardCollection) AbilityUtils.filterListByType(cards, sa.getParam("RemoveType"), sa);
|
||||
} else {
|
||||
toRemove = getTargetCards(sa);
|
||||
}
|
||||
String logMessage = sa.getParamOrDefault("LogMessage", "Removed from match");
|
||||
String remove = toRemove.toString().replace("[","").replace("]","");
|
||||
host.getController().getGame().fireEvent(new GameEventRandomLog(logMessage + ": " + remove));
|
||||
for (final Card tgtC : toRemove) {
|
||||
tgtC.getGame().getAction().ceaseToExist(tgtC, true);
|
||||
PaperCard rem = (PaperCard) tgtC.getPaperCard();
|
||||
host.getGame().getMatch().removeCard(rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class TwoPilesEffect extends SpellAbilityEffect {
|
||||
zone = ZoneType.smartValueOf(sa.getParam("Zone"));
|
||||
}
|
||||
|
||||
final String valid = sa.getParamOrDefault("ValidCards", "");
|
||||
final String valid = sa.getParamOrDefault("ValidCards", "Card");
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
|
||||
@@ -4937,17 +4937,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
view.updateEmblem(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* there are easy checkers for Color. The CardUtil functions should be made
|
||||
* part of the Card class, so calling out is not necessary
|
||||
*/
|
||||
public final boolean isOfColor(final String col) { return CardUtil.getColors(this).hasAnyColor(MagicColor.fromName(col)); }
|
||||
public final boolean isBlack() { return CardUtil.getColors(this).hasBlack(); }
|
||||
public final boolean isBlue() { return CardUtil.getColors(this).hasBlue(); }
|
||||
public final boolean isRed() { return CardUtil.getColors(this).hasRed(); }
|
||||
public final boolean isGreen() { return CardUtil.getColors(this).hasGreen(); }
|
||||
public final boolean isWhite() { return CardUtil.getColors(this).hasWhite(); }
|
||||
public final boolean isColorless() { return CardUtil.getColors(this).isColorless(); }
|
||||
public final boolean isOfColor(final String col) { return determineColor().hasAnyColor(MagicColor.fromName(col)); }
|
||||
public final boolean isBlack() { return determineColor().hasBlack(); }
|
||||
public final boolean isBlue() { return determineColor().hasBlue(); }
|
||||
public final boolean isRed() { return determineColor().hasRed(); }
|
||||
public final boolean isGreen() { return determineColor().hasGreen(); }
|
||||
public final boolean isWhite() { return determineColor().hasWhite(); }
|
||||
public final boolean isColorless() { return determineColor().isColorless(); }
|
||||
|
||||
public final boolean sharesNameWith(final Card c1) {
|
||||
// in a corner case where c1 is null, there is no name to share with.
|
||||
@@ -5891,6 +5887,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
@Override
|
||||
public final boolean canBeTargetedBy(final SpellAbility sa) {
|
||||
if (getOwner().hasLost()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sa == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,11 @@ public class CardFactory {
|
||||
out.setCommander(in.isRealCommander());
|
||||
//out.setFaceDown(in.isFaceDown());
|
||||
|
||||
int foil = in.getCurrentState().getFoil();
|
||||
if (foil > 0) {
|
||||
out.setFoil(foil);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ public class CardFactoryUtil {
|
||||
}
|
||||
|
||||
for (final Card crd : list) {
|
||||
ColorSet color = CardUtil.getColors(crd);
|
||||
ColorSet color = crd.determineColor();
|
||||
for (int i = 0; i < cntColors; i++) {
|
||||
if (color.hasAnyColor(MagicColor.WUBRG[i]))
|
||||
map[i]++;
|
||||
@@ -376,7 +376,7 @@ public class CardFactoryUtil {
|
||||
}
|
||||
|
||||
for (final Card crd : list) {
|
||||
ColorSet color = CardUtil.getColors(crd);
|
||||
ColorSet color = crd.determineColor();
|
||||
for (int i = 0; i < cntColors; i++) {
|
||||
if (color.hasAnyColor(MagicColor.WUBRG[i]))
|
||||
map[i]++;
|
||||
@@ -407,7 +407,7 @@ public class CardFactoryUtil {
|
||||
}
|
||||
|
||||
for (final Card crd : list) {
|
||||
ColorSet color = CardUtil.getColors(crd);
|
||||
ColorSet color = crd.determineColor();
|
||||
for (int i = 0; i < cntColors; i++) {
|
||||
if (color.hasAnyColor(colorRestrictions.get(i))) {
|
||||
map[i]++;
|
||||
|
||||
@@ -264,7 +264,7 @@ public final class CardPredicates {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CardUtil.getColors(c).hasAnyColor(color);
|
||||
return c.determineColor().hasAnyColor(color);
|
||||
}
|
||||
};
|
||||
} // getColor()
|
||||
@@ -273,7 +273,7 @@ public final class CardPredicates {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CardUtil.getColors(c).hasExactlyColor(color);
|
||||
return c.determineColor().hasExactlyColor(color);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -282,7 +282,7 @@ public final class CardPredicates {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return CardUtil.getColors(c).isColorless();
|
||||
return c.determineColor().isColorless();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -689,7 +689,7 @@ public class CardProperty {
|
||||
break;
|
||||
case "MostProminentColor":
|
||||
byte mask = CardFactoryUtil.getMostProminentColors(game.getCardsIn(ZoneType.Battlefield));
|
||||
if (!CardUtil.getColors(card).hasAnyColor(mask))
|
||||
if (!card.determineColor().hasAnyColor(mask))
|
||||
return false;
|
||||
break;
|
||||
case "LastCastThisTurn":
|
||||
@@ -703,7 +703,7 @@ public class CardProperty {
|
||||
if (castSA == null) {
|
||||
return false;
|
||||
}
|
||||
if (!CardUtil.getColors(card).hasAnyColor(castSA.getPayingColors().getColor())) {
|
||||
if (!card.determineColor().hasAnyColor(castSA.getPayingColors().getColor())) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -85,10 +85,6 @@ public final class CardUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ColorSet getColors(final Card c) {
|
||||
return c.determineColor();
|
||||
}
|
||||
|
||||
public static boolean isStackingKeyword(final String keyword) {
|
||||
String kw = keyword;
|
||||
if (kw.startsWith("HIDDEN")) {
|
||||
@@ -158,7 +154,6 @@ public final class CardUtil {
|
||||
|
||||
public static List<Card> getLastTurnCast(final String valid, final Card src, final CardTraitBase ctb) {
|
||||
return CardLists.getValidCardsAsList(src.getGame().getStack().getSpellsCastLastTurn(), valid, src.getController(), src, ctb);
|
||||
|
||||
}
|
||||
|
||||
public static List<Card> getLKICopyList(final Iterable<Card> in, Map<Integer, Card> cachedMap) {
|
||||
@@ -343,7 +338,7 @@ public final class CardUtil {
|
||||
|
||||
byte combinedColor = 0;
|
||||
for (Card tgt : tgts) {
|
||||
ColorSet cs = CardUtil.getColors(tgt);
|
||||
ColorSet cs = tgt.determineColor();
|
||||
for (byte color : MagicColor.WUBRG) {
|
||||
if(!cs.hasAnyColor(color))
|
||||
continue;
|
||||
|
||||
@@ -158,7 +158,7 @@ public class TokenInfo {
|
||||
if (!colorMap.isEmpty()) {
|
||||
if (!result.isColorless()) {
|
||||
// change Token Colors
|
||||
byte color = CardUtil.getColors(result).getColor();
|
||||
byte color = result.determineColor().getColor();
|
||||
|
||||
for (final Map.Entry<String, String> e : colorMap.entrySet()) {
|
||||
byte v = MagicColor.fromName(e.getValue());
|
||||
|
||||
@@ -34,7 +34,6 @@ import forge.game.GameObjectPredicates;
|
||||
import forge.game.GameType;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -300,9 +299,9 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
|
||||
if (first == null) {
|
||||
return false;
|
||||
}
|
||||
byte firstColor = CardUtil.getColors(first).getColor();
|
||||
byte firstColor = first.determineColor().getColor();
|
||||
for (Card c : tgts) {
|
||||
if (CardUtil.getColors(c).getColor() != firstColor) {
|
||||
if (c.determineColor().getColor() != firstColor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,52 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# returns the JDK version.
|
||||
# 8 for 1.8.0_nn, 9 for 9-ea etc, and "no_java" for undetected
|
||||
# Based on the code from this source: https://eed3si9n.com/detecting-java-version-bash
|
||||
jdk_version() {
|
||||
local result
|
||||
local java_cmd
|
||||
if [[ -n $(type -p java) ]]
|
||||
then
|
||||
java_cmd=java
|
||||
elif [[ (-n "$JAVA_HOME") && (-x "$JAVA_HOME/bin/java") ]]
|
||||
then
|
||||
java_cmd="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
local IFS=$'\n'
|
||||
# remove \r for Cygwin
|
||||
local lines=$("$java_cmd" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n')
|
||||
if [[ -z $java_cmd ]]
|
||||
then
|
||||
result=no_java
|
||||
else
|
||||
for line in $lines; do
|
||||
if [[ (-z $result) && ($line = *"version \""*) ]]
|
||||
then
|
||||
local ver=$(echo $line | sed -e 's/.*version "\(.*\)"\(.*\)/\1/; 1q')
|
||||
# on macOS, sed doesn't support '?'
|
||||
if [[ $ver = "1."* ]]
|
||||
then
|
||||
result=$(echo $ver | sed -e 's/1\.\([0-9]*\)\(.*\)/\1/; 1q')
|
||||
else
|
||||
result=$(echo $ver | sed -e 's/\([0-9]*\)\(.*\)/\1/; 1q')
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo "$result"
|
||||
}
|
||||
v="$(jdk_version)"
|
||||
|
||||
SHAREDPARAMS='-Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$'
|
||||
cd $(dirname "${0}")
|
||||
java -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
|
||||
|
||||
if [[ $v -ge 17 ]]
|
||||
then
|
||||
java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED $SHAREDPARAMS
|
||||
elif [[ $v -ge 11 ]]
|
||||
then
|
||||
java --illegal-access=permit $SHAREDPARAMS
|
||||
else
|
||||
java $SHAREDPARAMS
|
||||
fi
|
||||
|
||||
@@ -371,7 +371,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
@Override
|
||||
public void deckTypeSelected(final DecksComboBoxEvent ev) {
|
||||
if (ev.getDeckType() == DeckType.NET_ARCHIVE_STANDARD_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -398,7 +398,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
return;
|
||||
|
||||
} else if (ev.getDeckType() == DeckType.NET_ARCHIVE_PIONEER_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -424,7 +424,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
return;
|
||||
|
||||
} else if (ev.getDeckType() == DeckType.NET_ARCHIVE_MODERN_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -450,7 +450,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
return;
|
||||
|
||||
} else if (ev.getDeckType() == DeckType.NET_ARCHIVE_PAUPER_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -476,7 +476,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
return;
|
||||
|
||||
} else if (ev.getDeckType() == DeckType.NET_ARCHIVE_LEGACY_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -502,7 +502,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
return;
|
||||
|
||||
} else if (ev.getDeckType() == DeckType.NET_ARCHIVE_VINTAGE_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -528,7 +528,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
return;
|
||||
|
||||
} else if (ev.getDeckType() == DeckType.NET_ARCHIVE_BLOCK_DECK && !refreshingDeckType) {
|
||||
if(lstDecks.getGameType() != GameType.Constructed)
|
||||
if (lstDecks.getGameType() != GameType.Constructed)
|
||||
return;
|
||||
FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks
|
||||
@Override
|
||||
@@ -712,7 +712,6 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
prefs.save();
|
||||
}
|
||||
|
||||
|
||||
private String getState() {
|
||||
final StringBuilder state = new StringBuilder();
|
||||
DeckType selectedDeckType = this.selectedDeckType; // decksComboBox.getDeckType()
|
||||
@@ -827,7 +826,6 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<String> getSelectedDecksFromSavedState(final String savedState) {
|
||||
try {
|
||||
if (StringUtils.isBlank(savedState)) {
|
||||
|
||||
@@ -216,7 +216,6 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
|
||||
}
|
||||
|
||||
for (final Entry<TItem, Integer> itemEntry : itemsToAdd) {
|
||||
|
||||
final TItem item = itemEntry.getKey();
|
||||
final PaperCard card = item instanceof PaperCard ? (PaperCard)item : null;
|
||||
int qty = itemEntry.getValue();
|
||||
@@ -224,8 +223,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
|
||||
int max;
|
||||
if (deck == null || card == null || limit == CardLimit.None || DeckFormat.canHaveAnyNumberOf(card)) {
|
||||
max = Integer.MAX_VALUE;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
max = (limit == CardLimit.Singleton ? 1 : FModel.getPreferences().getPrefInt(FPref.DECK_DEFAULT_CARD_LIMIT));
|
||||
|
||||
Integer cardCopies = DeckFormat.canHaveSpecificNumberInDeck(card);
|
||||
@@ -538,16 +536,16 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
|
||||
CardManager cardManager = (CardManager) CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckManager();
|
||||
PaperCard existingCard = cardManager.getSelectedItem();
|
||||
// make a foiled version based on the original
|
||||
PaperCard foiledCard = existingCard.getFoiled();
|
||||
PaperCard foiledCard = existingCard.isFoil() ? existingCard.getUnFoiled() : existingCard.getFoiled();
|
||||
// remove *quantity* instances of existing card
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, quantity);
|
||||
// add *quantity* into the deck and set them as selected
|
||||
cardManager.addItem(foiledCard, quantity);
|
||||
cardManager.setSelectedItem(foiledCard);
|
||||
}
|
||||
}, true, true);
|
||||
}, true, true);
|
||||
}
|
||||
//TODO: need to translate getItemDisplayString
|
||||
//TODO: need to translate getItemDisplayString
|
||||
private void addItem(final String verb, final String dest, final boolean toAlternate, final int qty, final int shortcutModifiers) {
|
||||
String label = verb + " " + SItemManagerUtil.getItemDisplayString(getItemManager().getSelectedItems(), qty, false);
|
||||
if (dest != null && !dest.isEmpty()) {
|
||||
@@ -563,8 +561,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
|
||||
}
|
||||
if (isAddContextMenu) {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.addSelectedCards(toAlternate, quantity);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(toAlternate, quantity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import forge.gui.framework.FScreen;
|
||||
import forge.item.PaperCard;
|
||||
import forge.itemmanager.CardManager;
|
||||
import forge.itemmanager.ItemManagerConfig;
|
||||
import forge.itemmanager.filters.DeckSetFilter;
|
||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||
import forge.model.FModel;
|
||||
import forge.screens.deckeditor.AddBasicLandsDialog;
|
||||
|
||||
@@ -86,10 +86,9 @@ public class DeckController<T extends DeckBase> {
|
||||
/**
|
||||
* Load deck from file or clipboard
|
||||
*/
|
||||
public void loadDeck(Deck deck){
|
||||
public void loadDeck(Deck deck) {
|
||||
this.loadDeck(deck, true);
|
||||
}
|
||||
|
||||
public void loadDeck(Deck deck, boolean substituteCurrentDeck) {
|
||||
boolean isStored;
|
||||
if (view.getCatalogManager().isInfinite()) {
|
||||
@@ -159,7 +158,6 @@ public class DeckController<T extends DeckBase> {
|
||||
}
|
||||
|
||||
private void pickFromCatalog(Map<String, Integer> countByName, CardPool catalog, CardPool targetSection) {
|
||||
|
||||
CardPool catalogClone = new CardPool(catalog); // clone to iterate modified collection
|
||||
for (Map.Entry<PaperCard, Integer> entry : catalogClone) {
|
||||
PaperCard availableCard = entry.getKey();
|
||||
@@ -218,7 +216,6 @@ public class DeckController<T extends DeckBase> {
|
||||
public void setModel(final T document) {
|
||||
setModel(document, false);
|
||||
}
|
||||
|
||||
private void setModel(final T document, final boolean isStored) {
|
||||
model = document;
|
||||
onModelChanged(isStored);
|
||||
@@ -234,8 +231,7 @@ public class DeckController<T extends DeckBase> {
|
||||
if (isStored) {
|
||||
if (isModelInSyncWithFolder()) {
|
||||
setSaved(true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
notifyModelChanged();
|
||||
}
|
||||
} else { //TODO: Make this smarter
|
||||
@@ -317,8 +313,7 @@ public class DeckController<T extends DeckBase> {
|
||||
final T newModel = currentFolder.get(name);
|
||||
if (newModel != null) {
|
||||
setModel((T) newModel.copyTo(name), true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setSaved(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@@ -96,7 +95,6 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
pnlContent.setOpaque(false);
|
||||
|
||||
if (javaRecentEnough()) {
|
||||
|
||||
// With Blacksmith we would upload the releases and the /latest would redirect to the right URL
|
||||
// That currently doesn't happen so lets comment out this button for now
|
||||
// pnlContent.add(btnCheckForUpdates, constraintsBTN);
|
||||
@@ -122,9 +120,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
|
||||
pnlContent.add(btnDownloadSkins, constraintsBTN);
|
||||
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadSkins")), constraintsLBL);
|
||||
|
||||
} else {
|
||||
|
||||
String text = localizer.getMessage("lblYourVersionOfJavaIsTooOld");
|
||||
FLabel label = new FLabel.Builder().fontAlign(SwingConstants.CENTER).text(text).fontStyle(Font.BOLD).fontSize(18).build();
|
||||
pnlContent.add(label, "w 90%!, h 25px!, center, gap 0 0 30px 3px");
|
||||
@@ -137,7 +133,6 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
text = text + " . " + localizer.getMessage("lblYouNeedAtLeastJavaVersion") ;
|
||||
label = new FLabel.Builder().fontAlign(SwingConstants.CENTER).text(text).fontStyle(Font.BOLD).fontSize(18).build();
|
||||
pnlContent.add(label, "w 90%!, h 25px!, center, gap 0 0 0 36px");
|
||||
|
||||
}
|
||||
|
||||
pnlContent.add(btnListImageData, constraintsBTN);
|
||||
@@ -154,15 +149,12 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
|
||||
pnlContent.add(btnLicensing, constraintsBTN);
|
||||
pnlContent.add(_makeLabel(localizer.getMessage("lblLicensing")), constraintsLBL);
|
||||
|
||||
}
|
||||
|
||||
private boolean javaRecentEnough() {
|
||||
|
||||
RuntimeVersion javaVersion = RuntimeVersion.of(System.getProperty("java.version"));
|
||||
|
||||
return javaVersion.getMajor() >= 9 || (javaVersion.getMajor() >= 1 && (javaVersion.getMinor() > 8 || (javaVersion.getMinor() == 8 && javaVersion.getUpdate() >= 101)));
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -199,7 +191,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
public void setHowToPlayCommand(UiCommand command) { btnHowToPlay.setCommand(command); }
|
||||
public void setDownloadPricesCommand(UiCommand command) { btnDownloadPrices.setCommand(command); }
|
||||
public void setLicensingCommand(UiCommand command) { btnLicensing.setCommand(command); }
|
||||
public void setDownloadSkinsCommand(UiCommand command) { btnDownloadSkins.setCommand(command); }
|
||||
public void setDownloadSkinsCommand(UiCommand command) { btnDownloadSkins.setCommand(command); }
|
||||
|
||||
public void focusTopButton() {
|
||||
btnDownloadPics.requestFocusInWindow();
|
||||
@@ -269,7 +261,6 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
|
||||
String imagePath;
|
||||
int artIndex = 1;
|
||||
ArrayList<String> cis = new ArrayList<>();
|
||||
|
||||
HashMap<String, Pair<Boolean, Integer>> cardCount = new HashMap<>();
|
||||
for (CardInSet c : e.getAllCardsInSet()) {
|
||||
@@ -302,10 +293,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// check the front image
|
||||
//
|
||||
imagePath = ImageUtil.getImageRelativePath(cp, false, true, false);
|
||||
if (imagePath != null) {
|
||||
File file = ImageKeys.getImageFile(imagePath);
|
||||
@@ -319,9 +307,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// check the back face
|
||||
//
|
||||
if (cp.hasBackFace()) {
|
||||
imagePath = ImageUtil.getImageRelativePath(cp, true, true, false);
|
||||
if (imagePath != null) {
|
||||
@@ -372,7 +358,6 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
nifSB.append("\n");
|
||||
}
|
||||
|
||||
|
||||
String totalStats = "Missing images: " + missingCount + "\nUnimplemented cards: " + notImplementedCount + "\n";
|
||||
cniSB.append("\n-----------\n");
|
||||
cniSB.append(totalStats);
|
||||
@@ -425,7 +410,6 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void showLicensing() {
|
||||
String license = "<html>Forge License Information<br><br>"
|
||||
+ "This program is free software : you can redistribute it and/or modify "
|
||||
|
||||
@@ -23,7 +23,7 @@ public class IntegerConstraint {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if( min == max ) {
|
||||
if (min == max) {
|
||||
return String.valueOf( min );
|
||||
}
|
||||
return "between " + min + " and " + max;
|
||||
|
||||
@@ -339,17 +339,14 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
//hide deck header on while drafting
|
||||
setDeck(new Deck());
|
||||
deckHeader.setVisible(false);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (newDeck == null) {
|
||||
editorType.getController().newModel();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
editorType.getController().setDeck(newDeck);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (editorType == EditorType.Draft || editorType == EditorType.QuestDraft) {
|
||||
tabPages[0].hideTab(); //hide Draft Pack page if editing existing draft deck
|
||||
}
|
||||
@@ -897,8 +894,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
public void handleEvent(FEvent e) {
|
||||
if (max == 1) {
|
||||
callback.run(max);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
GuiChoose.getInteger(cardManager.getSelectedItem() + " - " + verb + " " + localizer.getMessage("lblHowMany"), 1, max, 20, callback);
|
||||
}
|
||||
@@ -1150,8 +1146,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
//if a commander has been set, only show cards that match its color identity
|
||||
switch (editorType) {
|
||||
case Commander:
|
||||
@@ -1176,8 +1171,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
default:
|
||||
if (cardManager.getWantUnique()) {
|
||||
cardManager.setPool(editorType.applyCardFilter(FModel.getUniqueCardsNoAlt(), additionalFilter), true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
cardManager.setPool(editorType.applyCardFilter(FModel.getAllCardsNoAlt(), additionalFilter), true);
|
||||
}
|
||||
break;
|
||||
@@ -1250,8 +1244,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
CardPreferences.save();
|
||||
}
|
||||
}));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
menu.addItem(new FMenuItem(localizer.getMessage("lblRemoveFavorites"), Forge.hdbuttons ? FSkinImage.HDSTAR_OUTLINE : FSkinImage.STAR_OUTLINE, new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
@@ -1376,8 +1369,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
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 {
|
||||
} else {
|
||||
caption = captionPrefix + " (" + parentScreen.getDeck().get(deckSection).countAll() + ")";
|
||||
}
|
||||
}
|
||||
@@ -1767,8 +1759,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
if (isStored) {
|
||||
if (isModelInSyncWithFolder()) {
|
||||
setSaved(true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
notifyModelChanged();
|
||||
}
|
||||
}
|
||||
@@ -1779,8 +1770,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
}
|
||||
if (model != null) {
|
||||
editor.setDeck(model.getHumanDeck());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
editor.setDeck(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -525,6 +525,9 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
|
||||
}
|
||||
|
||||
public void addItem(final T item, int qty) {
|
||||
if (pool == null) {
|
||||
return;
|
||||
}
|
||||
pool.add(item, qty);
|
||||
if (isUnfiltered()) {
|
||||
model.addItem(item, qty);
|
||||
|
||||
@@ -6,6 +6,6 @@ SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Des
|
||||
A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
|
||||
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
||||
AI:RemoveDeck:All
|
||||
A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.minAdv2 | SpellDescription$ Return all cards exiled with CARDNAME to their owner's hand.
|
||||
A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.noDiscard.minAdv2 | SpellDescription$ Return all cards exiled with CARDNAME to their owner's hand.
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Whenever you discard a card, exile that card from your graveyard.\n{2}, {T}: Draw a card, then discard a card.\n{4}, {T}, Sacrifice Bag of Holding: Return all cards exiled with Bag of Holding to their owner's hand.
|
||||
|
||||
6
forge-gui/res/cardsfolder/b/blacker_lotus.txt
Normal file
6
forge-gui/res/cardsfolder/b/blacker_lotus.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Blacker Lotus
|
||||
ManaCost:0
|
||||
Types:Artifact
|
||||
A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 4 | AILogic$ BlackLotus | SubAbility$ DBTearUpAndRemove | SpellDescription$ Tear CARDNAME into pieces. Add four mana of any one color. Remove the pieces from the game.
|
||||
SVar:DBTearUpAndRemove:DB$ RemoveFromMatch | Defined$ Self | LogMessage$ Torn to pieces and removed
|
||||
Oracle:{T}: Tear Blacker Lotus into pieces. Add four mana of any one color. Remove the pieces from the game.
|
||||
8
forge-gui/res/cardsfolder/c/chaos_orb.txt
Normal file
8
forge-gui/res/cardsfolder/c/chaos_orb.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Chaos Orb
|
||||
ManaCost:2
|
||||
Types:Artifact
|
||||
A:AB$ FlipOntoBattlefield | Cost$ 1 T | SubAbility$ DBDestroyTouched | ActivationZone$ Battlefield | SpellDescription$ If CARDNAME is on the battlefield, flip CARDNAME onto the battlefield from a height of at least one foot. If CARDNAME turns over completely at least once during the flip, destroy all nontoken permanents it touches. Then destroy CARDNAME. | StackDescription$ SpellDescription
|
||||
SVar:DBDestroyTouched:DB$ DestroyAll | ValidCards$ Card.IsRemembered+nonToken | SubAbility$ DBDestroyChaosOrb | AILogic$ Always | StackDescription$ None
|
||||
SVar:DBDestroyChaosOrb:DB$ Destroy | Defined$ Self | SubAbility$ DBCleanup | AILogic$ Always | StackDescription$ None
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:{1}, {T}: If Chaos Orb is on the battlefield, flip Chaos Orb onto the battlefield from a height of at least one foot. If Chaos Orb turns over completely at least once during the flip, destroy all nontoken permanents it touches. Then destroy Chaos Orb.
|
||||
@@ -1,10 +1,9 @@
|
||||
Name:Fact or Fiction
|
||||
ManaCost:3 U
|
||||
Types:Instant
|
||||
A:SP$ Dig | Cost$ 3 U | DigNum$ 5 | Reveal$ True | RememberRevealed$ True | NoMove$ True | SubAbility$ DBTwoPiles | SpellDescription$ Reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard.
|
||||
SVar:DBTwoPiles:DB$ TwoPiles | Defined$ You | DefinedCards$ Remembered | Separator$ Opponent | ChosenPile$ DBHand | UnchosenPile$ DBGrave
|
||||
A:SP$ Dig | DigNum$ 5 | Reveal$ True | RememberRevealed$ True | NoMove$ True | SubAbility$ DBTwoPiles | SpellDescription$ Reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard.
|
||||
SVar:DBTwoPiles:DB$ TwoPiles | Defined$ You | DefinedCards$ Remembered | Separator$ Opponent | ChosenPile$ DBHand | UnchosenPile$ DBGrave | StackDescription$ An opponent separates those cards into two piles. {p:You} puts one pile into their hand and the other into their graveyard.
|
||||
SVar:DBHand:DB$ ChangeZone | Defined$ Remembered | Origin$ Library | Destination$ Hand | SubAbility$ DBCleanup
|
||||
SVar:DBGrave:DB$ ChangeZone | Defined$ Remembered | Origin$ Library | Destination$ Graveyard | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/fact_or_fiction.jpg
|
||||
Oracle:Reveal the top five cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard.
|
||||
|
||||
7
forge-gui/res/cardsfolder/f/falling_star.txt
Normal file
7
forge-gui/res/cardsfolder/f/falling_star.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Falling Star
|
||||
ManaCost:2 R
|
||||
Types:Sorcery
|
||||
A:SP$ FlipOntoBattlefield | SubAbility$ DBDamageTouched | AILogic$ DamageCreatures | SpellDescription$ Flip CARDNAME onto the playing area from a height of at least one foot. CARDNAME deals 3 damage to each creature it lands on. Tap all creatures dealt damage by CARDNAME. If CARDNAME doesn't turn completely over at least once during the flip, it has no effect. | StackDescription$ SpellDescription
|
||||
SVar:DBDamageTouched:DB$ DamageAll | ValidCards$ Creature.IsRemembered | ValidDescription$ each creature touched. | NumDmg$ 3 | SubAbility$ DBCleanup | StackDescription$ None
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:Flip Falling Star onto the playing area from a height of at least one foot. Falling Star deals 3 damage to each creature it lands on. Tap all creatures dealt damage by Falling Star. If Falling Star doesn't turn completely over at least once during the flip, it has no effect.
|
||||
@@ -5,5 +5,5 @@ PT:3/3
|
||||
K:Double Strike
|
||||
K:Evoke:ExileFromHand<1/Card.Red+Other/red card>
|
||||
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerDescription$ When CARDNAME enters the battlefield, it deals 4 damage divided as you choose among any number of target creatures and/or planeswalkers.
|
||||
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker to distribute damage to | NumDmg$ 4 | TargetMin$ 1 | TargetMax$ 4 | DividedAsYouChoose$ 4
|
||||
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker to distribute damage to | NumDmg$ 4 | TargetMin$ 0 | TargetMax$ 4 | DividedAsYouChoose$ 4
|
||||
Oracle:Double strike\nWhen Fury enters the battlefield, it deals 4 damage divided as you choose among any number of target creatures and/or planeswalkers.\nEvoke—Exile a red card from your hand.
|
||||
|
||||
@@ -7,4 +7,5 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
|
||||
SVar:TrigEffect:DB$ Effect | Triggers$ TrigDamageDone
|
||||
SVar:TrigDamageDone:Mode$ DamageDone | ValidTarget$ Creature.OppCtrl | Execute$ TrigDestroy | TriggerDescription$ Poison Breath — Until end of turn, whenever a creature an opponent controls is dealt damage, destroy it.
|
||||
SVar:TrigDestroy:DB$ Destroy | Defined$ TriggeredTarget
|
||||
SVar:PlayMain1:TRUE
|
||||
Oracle:Flying\nPoison Breath — When Green Dragon enters the battlefield, until end of turn, whenever a creature an opponent controls is dealt damage, destroy it.
|
||||
|
||||
@@ -14,5 +14,5 @@ ManaCost:1 G
|
||||
Types:Legendary Artifact
|
||||
K:ETBReplacement:Other:ChooseCT
|
||||
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
|
||||
A:AB$ Mana | Cost$ T | Produced$ G | RestrictValid$ Creature.ChosenType,Creature.Legendary | SpellDescription$ {T}: Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell.
|
||||
A:AB$ Mana | Cost$ T | Produced$ G | RestrictValid$ Creature.ChosenType,Creature.Legendary | SpellDescription$ Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell.
|
||||
Oracle:As The Ringhart Crest enters the battlefield, choose a creature type.\n{T}: Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell.
|
||||
|
||||
6
forge-gui/res/cardsfolder/l/look_at_me_im_the_dci.txt
Normal file
6
forge-gui/res/cardsfolder/l/look_at_me_im_the_dci.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Look at Me, I'm the DCI
|
||||
ManaCost:5 W W
|
||||
Types:Sorcery
|
||||
A:SP$ NameCard | Defined$ You | ValidCards$ Card.nonBasic | SelectPrompt$ Choose a card other than a basic land card to ban | SubAbility$ DBRemove | AILogic$ MostProminentInHumanDeck | StackDescription$ None | SpellDescription$ Ban a card other than a basic land card for the rest of the match. (All cards with that name in any zone or sideboard are removed from the match.)
|
||||
SVar:DBRemove:DB$ RemoveFromMatch | RemoveType$ Card.NamedCard | IncludeSideboard$ True | StackDescription$ Ban a card other than a basic land card for the rest of the match. (All cards with that name in any zone or sideboard are removed from the match.)
|
||||
Oracle:Ban a card other than a basic land card for the rest of the match. (All cards with that name in any zone or sideboard are removed from the match.)
|
||||
@@ -3,6 +3,6 @@ ManaCost:1 U R
|
||||
Types:Legendary Creature Human Wizard
|
||||
PT:1/4
|
||||
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | Execute$ DrawDamageOther | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of each player's end step, that player may draw a card if a player other than you lost life this turn.
|
||||
SVar:DrawDamageOther:DB$ Draw | Defined$ TriggeredPlayer | NumCards$ 1 | ConditionPlayerDefined$ Player.Other | ConditionPlayerContains$ Player.wasDealtDamageThisTurn | OptionalDecider$ True
|
||||
SVar:DrawDamageOther:DB$ Draw | Defined$ TriggeredPlayer | NumCards$ 1 | ConditionPlayerDefined$ Player.Other | ConditionPlayerContains$ Player.LostLifeThisTurn | OptionalDecider$ True
|
||||
K:Partner
|
||||
Oracle:At the beginning of each player's end step, that player may draw a card if a player other than you lost life this turn.\nPartner (You can have two commanders if both have partner.)
|
||||
|
||||
@@ -2,10 +2,10 @@ Name:Sorin, Lord of Innistrad
|
||||
ManaCost:2 W B
|
||||
Types:Legendary Planeswalker Sorin
|
||||
Loyalty:3
|
||||
A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | TokenAmount$ 1 | TokenScript$ b_1_1_vampire_lifelink | TokenOwner$ You | LegacyImage$ b 1 1 vampire lifelink dka | Planeswalker$ True | SpellDescription$ Create a 1/1 black Vampire creature token with lifelink.
|
||||
A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | TokenAmount$ 1 | TokenScript$ b_1_1_vampire_lifelink | Planeswalker$ True | SpellDescription$ Create a 1/1 black Vampire creature token with lifelink.
|
||||
A:AB$ Effect | Cost$ SubCounter<2/LOYALTY> | Name$ Emblem - Sorin, Lord of Innistrad | Image$ emblem_sorin_lord_of_innistrad | StaticAbilities$ STPump | Planeswalker$ True | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "Creatures you control get +1/+0."
|
||||
SVar:STPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddPower$ 1
|
||||
A:AB$ Destroy | Cost$ SubCounter<6/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Creature,Planeswalker.Other | TargetMin$ 0 | TargetMax$ 3 | TgtPrompt$ Select target creatures or other planeswalkers | RememberTargets$ True | ForgetOtherTargets$ True | SubAbility$ DBChangeZoneAll | SpellDescription$ Destroy up to 3 target creatures and/or other planeswalkers. Return each card put into a graveyard this way to the battlefield under your control.
|
||||
A:AB$ Destroy | Cost$ SubCounter<6/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Creature,Planeswalker.Other | TargetMin$ 0 | TargetMax$ 3 | TgtPrompt$ Select target creatures or other planeswalkers | RememberTargets$ True | ForgetOtherTargets$ True | SubAbility$ DBChangeZoneAll | SpellDescription$ Destroy up to three target creatures and/or other planeswalkers. Return each card put into a graveyard this way to the battlefield under your control.
|
||||
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/sorin_lord_of_innistrad.jpg
|
||||
|
||||
113
forge-gui/res/cardsfolder/u/urza_academy_headmaster.txt
Normal file
113
forge-gui/res/cardsfolder/u/urza_academy_headmaster.txt
Normal file
@@ -0,0 +1,113 @@
|
||||
Name:Urza, Academy Headmaster
|
||||
ManaCost:W U B R G
|
||||
Types:Legendary Planeswalker Urza
|
||||
Loyalty:4
|
||||
A:AB$ GenericChoice | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | AtRandom$ Urza | ShowChoice$ Description | Choices$ Pump1,PutCounter2,Destroy3,Token4,Token5,Token6,Dig7,MustAttack8,PutCounter9,Effect10,Exile11,Dig12,Animate13,Draw14,Animate15,PumpAll16,Dig17,DealDamage18,Animate19,Mana20 | ShowChoice$ Description | StackDescription$ SpellDescription | SpellDescription$ Head to AskUrza.com and click +1.
|
||||
SVar:Pump1:DB$ Pump | TargetMin$ 0 | TargetMax$ 1 | KW$ First Strike & Vigilance & Lifelink | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | NumDef$ +1 | KW$ First Strike & Vigilance & Lifelink | SpellDescription$ Until end of turn, up to one target creature gets +1/+1 and gains first strike, vigilance, and lifelink.
|
||||
SVar:PutCounter2:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control to distribute counters to | CounterType$ P1P1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | SpellDescription$ Distribute three +1/+1 counters among one, two, or three target creatures you control.
|
||||
SVar:Destroy3:DB$ Destroy | ValidTgts$ Permanent.nonCreature | TgtPrompt$ Select a noncreature permanent | SpellDescription$ Destroy target noncreature permanent.
|
||||
SVar:Token4:DB$ Token | TokenAmount$ 2 | TokenScript$ r_3_1_elemental_haste | AtEOT$ Exile | SpellDescription$ Create two 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step.
|
||||
SVar:Token5:DB$ Token | TokenAmount$ 3 | TokenScript$ w_1_1_soldier | SpellDescription$ Create three 1/1 white Soldier creature tokens.
|
||||
SVar:Token6:DB$ Token | TokenScript$ b_3_3_beast_deathtouch | SpellDescription$ Create a 3/3 black Beast creature token with deathtouch.
|
||||
SVar:Dig7:DB$ Dig | DigNum$ 5 | Reveal$ True | ChangeNum$ All | ChangeValid$ Creature | SpellDescription$ Reveal the top five cards of your library. Put all creature cards revealed this way into your hand and the rest on the bottom of your library in any order.
|
||||
SVar:MustAttack8:DB$ MustAttack | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Defender$ Self | SpellDescription$ During target opponent's next turn, creatures that player controls attack NICKNAME if able.
|
||||
SVar:PutCounter9:DB$ PutCounter | Defined$ Self | CounterType$ LOYALTY | CounterNum$ X9 | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | StackDescription$ SpellDescription | SpellDescription$ Put a loyalty counter on NICKNAME for each creature target opponent controls.
|
||||
SVar:X9:TargetedPlayer$CreaturesInPlay
|
||||
SVar:Effect10:DB$ Effect | Triggers$ TrigAttack10 | Duration$ UntilYourNextTurn | SpellDescription$ Until your next turn, whenever a creature an opponent controls attacks, it gets -1/-0 until end of turn.
|
||||
SVar:TrigAttack10:Mode$ Attacks | ValidCard$ Creature.OppCtrl | TriggerZones$ Command | Execute$ DebuffAttacker10 | TriggerDescription$ Whenever a creature an opponent controls attacks, it gets -1/-0 until end of turn.
|
||||
SVar:DebuffAttacker10:DB$ Pump | Defined$ TriggeredAttacker | NumAtt$ -1
|
||||
SVar:Exile11:DB$ ChangeZone | ValidTgts$ Player | TgtPrompt$ Select target player | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | Chooser$ Targeted | Hidden$ True | IsCurse$ True | Mandatory$ True | SpellDescription$ Target player exiles a card from their hand.
|
||||
SVar:Dig12:DB$ Dig | DigNum$ 1 | Reveal$ True | ChangeNum$ All | ChangeValid$ Land | DestinationZone$ Battlefield | DestinationZone2$ Hand | SpellDescription$ Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand.
|
||||
SVar:Animate13:DB$ Animate | ValidTgts$ Land.YouCtrl | TgtPrompt$ Select target land you control | Power$ 4 | Toughness$ 4 | Types$ Creature,Elemental | Duration$ Permanent | Keywords$ Trample | SpellDescription$ Target land you control becomes a 4/4 Elemental creature with trample. It's still a land.
|
||||
SVar:Draw14:DB$ Draw | Defined$ You | SubAbility$ DBMana14 | SpellDescription$ Draw a card, then add one mana of any color.
|
||||
SVar:DBMana14:DB$ Mana | Produced$ Any | AILogic$ MostProminentInComputerHand
|
||||
SVar:Animate15:DB$ Animate | Power$ 4 | Toughness$ 4 | Types$ Creature,Legendary,Dragon | Colors$ Red | OverwriteColors$ True | RemoveCardTypes$ True | Keywords$ Flying & Indestructible & Haste | SpellDescription$ Until end of turn, NICKNAME becomes a legendary 4/4 red Dragon creature with flying, indestructible, and haste. (He doesn't lose loyalty while he's not a planeswalker.)
|
||||
SVar:PumpAll16:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | KW$ Lifelink | Duration$ UntilYourNextTurn | SpellDescription$ Until your next turn, creatures you control get +1/+0 and gain lifelink.
|
||||
SVar:Dig17:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Artifact | SpellDescription$ Look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in any order.
|
||||
SVar:DealDamage18:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SpellDescription$ NICKNAME deals 3 damage to any target.
|
||||
SVar:Animate19:DB$ Animate | Defined$ Self | Triggers$ DamageTrig19 | Duration$ UntilYourNextTurn | SpellDescription$ Until your next turn, whenever a creature deals combat damage to NICKNAME, destroy that creature. | StackDescription$ SpellDescription
|
||||
SVar:DamageTrig19:Mode$ DamageDone | ValidSource$ Creature | ValidTarget$ Card.Self | TriggerZones$ Battlefield | CombatDamage$ True | Execute$ TrigDestroy19 | TriggerDescription$ Whenever a creature deals combat damage to NICKNAME, destroy that creature.
|
||||
SVar:TrigDestroy19:DB$ Destroy | Defined$ TriggeredSourceLKICopy
|
||||
SVar:Mana20:DB$ Mana | Produced$ Combo Any | Amount$ X20 | SpellDescription$ Add X mana in any combination of colors, where X is the number of creatures you control.
|
||||
SVar:X20:Count$Valid Creature.YouCtrl
|
||||
A:AB$ GenericChoice | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | AtRandom$ Urza | ShowChoice$ Description | Choices$ DamageAll1M,GainControl2M,DealDamage3M,Destroy4M,Effect5M,ChangeZone6M,Draw7M,Mill8M,Dig9M,Exile10M,Reveal11M,Tutor12M,Sacrifice13M,Token14M,Token15M,SetLife16M,Destroy17M,Return18M,Token19M,Draw20M | ShowChoice$ Description | StackDescription$ SpellDescription | SpellDescription$ Head to AskUrza.com and click -1.
|
||||
SVar:DamageAll1M:DB$ DamageAll | NumDmg$ 3 | ValidCards$ Creature | SpellDescription$ NICKNAME deals 3 damage to each creature.
|
||||
SVar:GainControl2M:DB$ GainControl | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Gain control of target creature.
|
||||
SVar:DealDamage3M:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 4 | SubAbility$ DBGainLife3M | SpellDescription$ NICKNAME deals 4 damage to any target and you gain 4 life.
|
||||
SVar:DBGainLife3M:DB$ GainLife | Defined$ You | LifeAmount$ 4
|
||||
SVar:Destroy4M:DB$ Destroy | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBGainLife4M | SpellDescription$ Destroy target creature. You gain life equal to its toughness.
|
||||
SVar:DBGainLife4M:DB$ GainLife | Defined$ You | LifeAmount$ X4M
|
||||
SVar:X4M:TargetedLKI$CardToughness
|
||||
SVar:Effect5M:DB$ Effect | Name$ Emblem - Urza, Academy Headmaster (5M) | StaticAbilities$ STPump5M | Duration$ Permanent | SpellDescription$ You get an emblem with "Creatures you control get +1/+1."
|
||||
SVar:STPump5M:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control get +1/+1.
|
||||
SVar:ChangeZone6M:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature | ChangeNum$ 1 | SpellDescription$ You may put a creature card from your hand onto the battlefield.
|
||||
SVar:Draw7M:DB$ Draw | NumCards$ 3 | SubAbility$ DBChangeZone7M | SpellDescription$ Draw three cards, then put a card from your hand on top of your library.
|
||||
SVar:DBChangeZone7M:DB$ ChangeZone | Origin$ Hand | Destination$ Library | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SelectPrompt$ Select a card from your hand to put on top of your library
|
||||
SVar:Mill8M:DB$ Mill | ValidTgts$ Player | TgtPrompt$ Select target player | NumCards$ 10 | SpellDescription$ Target player puts the top ten cards of their library into their graveyard.
|
||||
SVar:Dig9M:DB$ Dig | DigNum$ 5 | Reveal$ True | RememberRevealed$ True | NoMove$ True | SubAbility$ DBTwoPiles9M | SpellDescription$ Reveal the top five cards of your library. An opponent separates them into two piles. Put one pile into your hand and the other on the bottom of your library in any order.
|
||||
SVar:DBTwoPiles9M:DB$ TwoPiles | Defined$ You | DefinedCards$ Remembered | Separator$ Opponent | ChosenPile$ DBHand9M | UnchosenPile$ DBLibraryBottom9M
|
||||
SVar:DBHand9M:DB$ ChangeZone | Defined$ Remembered | Origin$ Library | Destination$ Hand
|
||||
SVar:DBLibraryBottom9M:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Library | Destination$ Library | LibraryPosition$ -1 | SubAbility$ DBCleanup
|
||||
SVar:Exile10M:DB$ ChangeZone | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target permanent.
|
||||
SVar:Reveal11M:DB$ Dig | DigNum$ 5 | Reveal$ True | RememberRevealed$ True | NoMove$ True | SubAbility$ DBChangeCreatures11M | SpellDescription$ Reveal the top five cards of your library. You may put all creature cards and/or land cards from among them into your hand. Put the rest into your graveyard.
|
||||
SVar:DBChangeCreatures11M:DB$ ChangeZoneAll | ChangeType$ Card.Creature+IsRemembered | Origin$ Library | Destination$ Hand | Optional$ True | OptionQuestion$ Put all creature cards into your hand? | ForgetChanged$ True | SubAbility$ DBChangeLands11M
|
||||
SVar:DBChangeLands11M:DB$ ChangeZoneAll | ChangeType$ Card.Land+IsRemembered | Origin$ Library | Destination$ Hand | Optional$ True | OptionQuestion$ Put all land cards into your hand? | ForgetChanged$ True | SubAbility$ DBChangeRest11M
|
||||
SVar:DBChangeRest11M:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Library | Destination$ Graveyard | ForgetChanged$ True
|
||||
SVar:Tutor12M:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SpellDescription$ Search your library for a card and put that card into your hand. Then shuffle your library.
|
||||
SVar:Sacrifice13M:DB$ Sacrifice | ValidTgts$ Player | Amount$ 2 | SacValid$ Creature | SpellDescription$ Target player sacrifices two creatures.
|
||||
SVar:Token14M:DB$ Token | TokenScript$ b_5_5_demon_flying | SubAbility$ DBLoselife14M | SpellDescription$ Create a 5/5 black Demon creature token with flying. You lose 2 life.
|
||||
SVar:DBLoselife14M:DB$ LoseLife | LifeAmount$ 2
|
||||
SVar:Token15M:DB$ Token | TokenScript$ c_4_4_dragon_flying | SpellDescription$ Create a 4/4 gold Dragon creature token with flying.
|
||||
SVar:SetLife16M:DB$ SetLife | ValidTgts$ Player | TgtPrompt$ Select target player | LifeAmount$ 10 | SpellDescription$ Target player's life total becomes 10.
|
||||
SVar:Destroy17M:DB$ Destroy | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select a nonland permanent | SpellDescription$ Destroy target nonland permanent.
|
||||
SVar:Return18M:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | TgtPrompt$ Select target permanent in a graveyard | ValidTgts$ Permanent | SpellDescription$ Return target permanent from a graveyard to the battlefield under your control.
|
||||
SVar:Token19M:DB$ Token | TokenAmount$ 2 | TokenScript$ g_3_3_beast | SpellDescription$ Create two 3/3 green Beast creature tokens.
|
||||
SVar:Draw20M:DB$ Draw | NumCards$ 4 | SubAbility$ DBDiscard20M | SpellDescription$ Draw four cards, then discard two cards.
|
||||
SVar:DBDiscard20M:DB$ Discard | Defined$ You | NumCards$ 2 | Mode$ TgtChoose
|
||||
A:AB$ GenericChoice | Cost$ SubCounter<6/LOYALTY> | AtRandom$ Urza | ShowChoice$ Description | Choices$ DealDamage1U,Effect2U,DestroyAll3U,Token4U,LifeGain5U,Damage6U,Effect7U,Effect8U,Token9U,Shuffle10U,Destroy11U,Effect12U,Effect13U,ControlPlayer14U,ExileLibrary15U,Token16U,Raise17U,LifeDraw18U,FlipTurns19U | Planeswalker$ True | Ultimate$ True | ShowChoice$ Description | StackDescription$ SpellDescription | SpellDescription$ Head to AskUrza.com and click -6.
|
||||
SVar:DealDamage1U:DB$ DealDamage | ValidTgts$ Player,Planeswalker | SubAbility$ DBDiscard1U | SpellDescription$ NICKNAME deals 7 damage to target player or planeswalker. That player or that planeswalker's controller discards seven cards, then sacrifices seven permanents.
|
||||
SVar:DBDiscard1U:DB$ Discard | Defined$ TargetedOrController | NumCards$ 7 | Mode$ TgtChoose | SubAbility$ DBSac1U
|
||||
SVar:DBSac1U:DB$Sacrifice | Defined$ TargetedOrController | SacValid$ Permanent | Amount$ 7
|
||||
SVar:Effect2U:DB$ Effect | Name$ Emblem - Urza, Academy Headmaster (2U) | ReplacementEffects$ RPreventDamage | Stackable$ False | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "If a source would deal damage to you or a planeswalker you control, prevent all but 1 of that damage."
|
||||
SVar:RPreventDamage:Event$ DamageDone | ValidTarget$ You,Planeswalker.YouCtrl | ReplaceWith$ PreventDmg | PreventionEffect$ True | Description$ If a source would deal damage to you or a planeswalker you control, prevent all but 1 of that damage.
|
||||
SVar:PreventDmg:DB$ ReplaceDamage | Amount$ ShieldAmount
|
||||
SVar:ShieldAmount:ReplaceCount$DamageAmount/Minus.1
|
||||
SVar:DestroyAll3U:DB$ DestroyAll | ValidTgts$ Player | TgtPrompt$ Select target player | ValidCards$ Land | SpellDescription$ Destroy all lands target player controls.
|
||||
SVar:Token4U:DB$ Token | TokenAmount$ X4U | TokenScript$ w_2_2_cat | SpellDescription$ Create X 2/2 white Cat creature tokens, where X is your life total.
|
||||
SVar:X4U:Count$YourLifeTotal
|
||||
SVar:LifeGain5U:DB$ GainLife | LifeAmount$ 100 | SpellDescription$ You gain 100 life.
|
||||
SVar:Damage6U:DB$ DealDamage | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select a player or planeswalker | NumDmg$ 10 | SubAbility$ DmgAll6U | DamageMap$ True | SpellDescription$ NICKNAME deals 10 damage to target player or planeswalker and each creature that player or that planeswalker's controller controls.
|
||||
SVar:DmgAll6U:DB$ DamageAll | NumDmg$ 10 | ValidCards$ Creature.ControlledBy TargetedOrController | SubAbility$ DBDamageResolve6U
|
||||
SVar:DBDamageResolve6U:DB$ DamageResolve
|
||||
SVar:Effect7U:DB$ Effect | Name$ Emblem - Urza, Academy Headmaster (7U) | StaticAbilities$ ST7U | Stackable$ False | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "Creatures you control have double strike, trample, hexproof and haste."
|
||||
SVar:ST7U:Mode$ Continuous | EffectZone$ Command | Affected$ Creature.YouCtrl | AffectedZone$ Battlefield | AddKeyword$ Double Strike & Trample & Hexproof & Haste | Description$ Creatures you control have double strike, trample, hexproof and haste.
|
||||
SVar:Effect8U:DB$ Effect | Name$ Emblem - Urza, Academy Headmaster (8U) | StaticAbilities$ STIndestructible8U | Stackable$ False | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "Artifacts, creatures, enchantments, and lands you control have indestructible."
|
||||
SVar:STIndestructible8U:Mode$ Continuous | EffectZone$ Command | Affected$ Artifact.YouCtrl,Creature.YouCtrl,Enchantment.YouCtrl,Land.YouCtrl | AffectedZone$ Battlefield | AddKeyword$ Indestructible | Description$ Artifacts, creatures, enchantments, and lands you control are indestructible.
|
||||
SVar:Token9U:DB$ Token | TokenAmount$ Lands | TokenScript$ g_6_6_wurm | SpellDescription$ Create a 6/6 green Wurm creature token for each land you control.
|
||||
SVar:Lands:Count$Valid Land.YouCtrl
|
||||
SVar:Shuffle10U:DB$ ChangeZoneAll | ChangeType$ Card | Origin$ Hand,Graveyard | Destination$ Library | Shuffle$ True | SubAbility$ DBDraw10U | UseAllOriginZones$ True | SpellDescription$ Each player shuffles their hand and graveyard into their library. You draw seven cards.
|
||||
SVar:DBDraw10U:DB$ Draw | Defined$ You | NumCards$ 7
|
||||
SVar:Destroy11U:DB$ Destroy | ValidTgts$ Creature,Planeswalker.Other | TargetMin$ 0 | TargetMax$ 3 | TgtPrompt$ Select up to three target creatures and/or other planeswalkers | RememberTargets$ True | SubAbility$ DBChangeZoneAll11U | SpellDescription$ Destroy up to three target creatures and/or other planeswalkers. Return each card put into a graveyard this way to the battlefield under your control.
|
||||
SVar:DBChangeZoneAll11U:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | SubAbility$ DBCleanup
|
||||
SVar:Effect12U:DB$ Effect | Name$ Emblem - Urza, Academy Headmaster (12U) | Triggers$ TrigSpellCast12U | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "Whenever you cast a spell, exile target permanent."
|
||||
SVar:TrigSpellCast12U:Mode$ SpellCast | ValidActivatingPlayer$ You | Execute$ EffSpellCast12U | TriggerDescription$ Whenever you cast a spell, exile target permanent.
|
||||
SVar:EffSpellCast12U:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent
|
||||
SVar:Effect13U:DB$ Effect | Name$ Emblem - Urza, Academy Headmaster (13U) | Triggers$ TrigFight13U | Duration$ Permanent | AILogic$ Always | SubAbility$ DBToken13U | SpellDescription$ You get an emblem with "Whenever a creature enters the battlefield under your control, you may have it fight target creature." Then create three 8/8 blue Octopus creature tokens.
|
||||
SVar:TrigFight13U:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl | Execute$ DBFight13U | OptionalDecider$ You | TriggerZones$ Command | TriggerDescription$ Whenever a creature enters the battlefield under your control, you may have it fight target creature.
|
||||
SVar:DBFight13U:DB$ Fight | Defined$ TriggeredCardLKICopy | ValidTgts$ Creature | TgtPrompt$ Choose target creature
|
||||
SVar:DBToken13U:DB$ Token | TokenAmount$ 3 | TokenScript$ u_8_8_octopus
|
||||
SVar:ControlPlayer14U:DB$ ControlPlayer | ValidTgts$ Player | TgtPrompt$ Select target player | SpellDescription$ You control target player during that player's next turn.
|
||||
SVar:ExileLibrary15U:DB$ ChangeZoneAll | Origin$ Library | Destination$ Exile | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBChangeZone15U | Planeswalker$ True | Ultimate$ True | SpellDescription$ Exile all cards from target player's library, then that player shuffles their hand into their library.
|
||||
SVar:DBChangeZone15U:DB$ ChangeZoneAll | Origin$ Hand | Destination$ Library | Defined$ Targeted | ChangeType$ Card | Shuffle$ True
|
||||
SVar:Token16U:DB$ Token | TokenAmount$ 3 | TokenScript$ b_1_1_assassin_lose_con | SpellDescription$ Create three 1/1 black Assassin creature tokens with "Whenever this creature deals combat damage to a player, that player loses the game."
|
||||
SVar:Raise17U:DB$ ChangeZoneAll | ChangeType$ Creature | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | SpellDescription$ Put all creature cards in all graveyards onto the battlefield under your control.
|
||||
SVar:LifeDraw18U:DB$ GainLife | LifeAmount$ Lands | SubAbility$ DBDraw18U | SpellDescription$ You gain X life and draw X cards, where X is the number of lands you control.
|
||||
SVar:DBDraw18U:DB$ Draw | NumCards$ Lands
|
||||
SVar:FlipTurns19U:DB$ FlipACoin | Amount$ 5 | NoCall$ True | HeadsSubAbility$ DBAddTurn19U | StackDescription$ SpellDescription | SpellDescription$ Flip 5 coins. Take an extra turn after this one for each coin that comes up heads.
|
||||
SVar:DBAddTurn19U:DB$ AddTurn | Defined$ You | NumTurns$ 1
|
||||
SVar:LifeDrawPut20U:DB$ GainLife | LifeAmount$ 7 | SubAbility$ DBDraw20U | SpellDescription$ You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.
|
||||
SVar:DBDraw20U:DB$ Draw | NumCards$ 7 | SubAbility$ DBChangeZone20U
|
||||
SVar:DBChangeZone20U:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Permanent | ChangeNum$ 7
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
|
||||
DeckHas:Ability$Counters & Ability$Token & Ability$LifeGain & Ability$Discard
|
||||
Oracle:[+1]: Head to AskUrza.com and click +1.\n[-1]: Head to AskUrza.com and click -1.\n[-6]: Head to AskUrza.com and click -6.
|
||||
@@ -244,7 +244,7 @@ Eternal Warrior|LEG
|
||||
Eureka|LEG
|
||||
Evil Eye of Orms-by-Gore|LEG
|
||||
Fallen Angel|LEG
|
||||
#Falling Star|LEG
|
||||
Falling Star|LEG
|
||||
Feint|LEG
|
||||
Field of Dreams|LEG
|
||||
Fire Sprites|LEG
|
||||
@@ -504,7 +504,7 @@ Camouflage|LEB
|
||||
Castle|LEB
|
||||
Celestial Prism|LEB
|
||||
Channel|LEB
|
||||
#Chaos Orb|LEB
|
||||
Chaos Orb|LEB
|
||||
Chaoslace|LEB
|
||||
Circle of Protection: Black|LEB
|
||||
Circle of Protection: Blue|LEB
|
||||
|
||||
14
forge-gui/res/editions/Innistrad Crimson Vow Commander.txt
Normal file
14
forge-gui/res/editions/Innistrad Crimson Vow Commander.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
[metadata]
|
||||
Code=VOC
|
||||
Date=2021-11-19
|
||||
Name=Innistrad: Crimson Vow Commander
|
||||
Type=Commander
|
||||
ScryfallCode=VOC
|
||||
|
||||
[cards]
|
||||
1 M Millicent, Restless Revenant @
|
||||
2 M Strefan, Maurer Progenitor @
|
||||
32 M Wedding Ring @
|
||||
39 M Millicent, Restless Revenant @
|
||||
40 M Strefan, Maurer Progenitor @
|
||||
70 M Wedding Ring @
|
||||
90
forge-gui/res/editions/Innistrad Crimson Vow.txt
Normal file
90
forge-gui/res/editions/Innistrad Crimson Vow.txt
Normal file
@@ -0,0 +1,90 @@
|
||||
[metadata]
|
||||
Code=VOW
|
||||
Date=2021-11-19
|
||||
Name=Innistrad: Crimson Vow
|
||||
Code2=VOW
|
||||
MciCode=vow
|
||||
Type=Expansion
|
||||
ScryfallCode=VOW
|
||||
|
||||
[cards]
|
||||
5 R By Invitation Only @
|
||||
10 C Drogskol Infantry @
|
||||
15 C Gryff Rider @
|
||||
34 M Savior of Ollenbock @
|
||||
36 R Sigarda's Summons @
|
||||
38 R Thalia, Guardian of Thraben @
|
||||
45 R Wedding Announcement @
|
||||
60 U Geistlight Snare @
|
||||
71 R Overcharged Amalgam @
|
||||
103 R Demonic Bargain @
|
||||
112 U Fell Stinger @
|
||||
114 C Gluttonous Guest @
|
||||
129 C Rot-Tide Gargantua @
|
||||
131 M Sorin the Mirthless @
|
||||
137 R Voldaren Bloodcaster @
|
||||
154 R Dominating Vampire @
|
||||
184 C Weary Prisoner @
|
||||
185 C Apprentice Sharpshooter @
|
||||
197 R Dig Up @
|
||||
208 C Massive Might @
|
||||
235 R Dorothea, Vengeful Victim @
|
||||
238 R Grolnok, the Omnivore @
|
||||
245 M Olivia, Crimson Bride @
|
||||
260 C Wedding Invitation @
|
||||
261 R Deathcap Glade @
|
||||
262 R Dreamroot Cascade @
|
||||
264 R Shattered Sanctum @
|
||||
265 R Stormcarved Coast @
|
||||
266 R Sundown Pass @
|
||||
267 R Voldaren Estate @
|
||||
268 L Plains @
|
||||
269 L Plains @
|
||||
270 L Island @
|
||||
271 L Island @
|
||||
272 L Swamp @
|
||||
273 L Swamp @
|
||||
274 L Mountain @
|
||||
275 L Mountain @
|
||||
276 L Forest @
|
||||
277 L Forest @
|
||||
278 M Sorin the Mirthless @
|
||||
281 R Deathcap Glade @
|
||||
282 R Dreamroot Cascade @
|
||||
283 R Shattered Sanctum @
|
||||
284 R Stormcarved Coast @
|
||||
285 R Sundown Pass @
|
||||
292 C Gluttonous Guest @
|
||||
297 M Sorin the Mirthless @
|
||||
298 R Voldaren Bloodcaster @
|
||||
305 R Dominating Vampire @
|
||||
315 M Olivia, Crimson Bride @
|
||||
318 R Thalia, Guardian of Thraben @
|
||||
322 R Dorothea, Vengeful Victim @
|
||||
324 R Grolnok, the Omnivore @
|
||||
330 M Savior of Ollenbock @
|
||||
331 R Thalia, Guardian of Thraben @
|
||||
337 M Sorin the Mirthless @
|
||||
338 R Voldaren Bloodcaster @
|
||||
343 M Olivia, Crimson Bride @
|
||||
346 R By Invitation Only @
|
||||
352 M Savior of Ollenbock @
|
||||
353 R Sigarda's Summons @
|
||||
355 R Wedding Announcement @
|
||||
363 R Overcharged Amalgam @
|
||||
368 R Demonic Bargain @
|
||||
387 R Dig Up @
|
||||
397 R Voldaren Estate @
|
||||
398 L Plains @
|
||||
399 L Island @
|
||||
400 L Swamp @
|
||||
401 L Mountain @
|
||||
402 L Forest @
|
||||
403 R Voldaren Estate @
|
||||
404 R Sigarda's Summons @
|
||||
405 U Geistlight Snare @
|
||||
406 U Fell Stinger @
|
||||
407 R Dominating Vampire @
|
||||
|
||||
[tokens]
|
||||
c_a_blood_draw
|
||||
@@ -1938,6 +1938,13 @@ lblTails=Zahl
|
||||
lblCallCoinFlip=Kopf oder Zahl
|
||||
lblWin=Gewonnen
|
||||
lblLose=Verloren
|
||||
#FlipOntoBattlefieldEffect.java
|
||||
lblChooseDesiredLocation=Wähle eine Karte als Mittelpunkt der gewünschten Landezone.
|
||||
lblDidNotFlipOver=Die Karte hat sich NICHT überschlagen.
|
||||
lblFlippedOver=Die Karte überschlug sich {0} mal.
|
||||
lblDidNotLandOnCards=Die Karte landete auf keiner Karte.
|
||||
lblLandedOnOneCard=Die Karte landete auf {0}.
|
||||
lblLandedOnTwoCards=Die Karte landete auf {0} und {1}.
|
||||
#InvestigateEffect.java
|
||||
lblWouldYouLikeInvestigate=Möchtest du Nachforschungen anstellen?
|
||||
#LifeSetEffect.java
|
||||
|
||||
@@ -1939,6 +1939,13 @@ lblTails=tails
|
||||
lblCallCoinFlip=Call coin flip
|
||||
lblWin=win
|
||||
lblLose=lose
|
||||
#FlipOntoBattlefieldEffect.java
|
||||
lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location.
|
||||
lblDidNotFlipOver=The card did not flip over.
|
||||
lblFlippedOver=The card flipped over {0} time(s).
|
||||
lblDidNotLandOnCards=The card did not land on any cards.
|
||||
lblLandedOnOneCard=The card landed on {0}.
|
||||
lblLandedOnTwoCards=The card landed on {0} and {1}.
|
||||
#InvestigateEffect.java
|
||||
lblWouldYouLikeInvestigate=Do you want to investigate?
|
||||
#LifeSetEffect.java
|
||||
|
||||
@@ -1937,6 +1937,13 @@ lblTails=cruz
|
||||
lblCallCoinFlip=Llamar al lanzamiento de la moneda
|
||||
lblWin=gana
|
||||
lblLose=pierde
|
||||
#FlipOntoBattlefieldEffect.java
|
||||
lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location.
|
||||
lblDidNotFlipOver=The card did not flip over.
|
||||
lblFlippedOver=The card flipped over {0} time(s).
|
||||
lblDidNotLandOnCards=The card did not land on any cards.
|
||||
lblLandedOnOneCard=The card landed on {0}.
|
||||
lblLandedOnTwoCards=The card landed on {0} and {1}.
|
||||
#InvestigateEffect.java
|
||||
lblWouldYouLikeInvestigate=¿Quieres investigar?
|
||||
#LifeSetEffect.java
|
||||
|
||||
@@ -1936,6 +1936,13 @@ lblTails=croce
|
||||
lblCallCoinFlip=Scegli testa o croce
|
||||
lblWin=hai vinto
|
||||
lblLose=hai perso
|
||||
#FlipOntoBattlefieldEffect.java
|
||||
lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location.
|
||||
lblDidNotFlipOver=The card did not flip over.
|
||||
lblFlippedOver=The card flipped over {0} time(s).
|
||||
lblDidNotLandOnCards=The card did not land on any cards.
|
||||
lblLandedOnOneCard=The card landed on {0}.
|
||||
lblLandedOnTwoCards=The card landed on {0} and {1}.
|
||||
#InvestigateEffect.java
|
||||
lblWouldYouLikeInvestigate=Do you want to investigate?
|
||||
#LifeSetEffect.java
|
||||
|
||||
@@ -1936,6 +1936,13 @@ lblTails=裏
|
||||
lblCallCoinFlip=コイン投げを予想
|
||||
lblWin=勝ち
|
||||
lblLose=負け
|
||||
#FlipOntoBattlefieldEffect.java
|
||||
lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location.
|
||||
lblDidNotFlipOver=The card did not flip over.
|
||||
lblFlippedOver=The card flipped over {0} time(s).
|
||||
lblDidNotLandOnCards=The card did not land on any cards.
|
||||
lblLandedOnOneCard=The card landed on {0}.
|
||||
lblLandedOnTwoCards=The card landed on {0} and {1}.
|
||||
#InvestigateEffect.java
|
||||
lblWouldYouLikeInvestigate=Do you want to investigate?
|
||||
#LifeSetEffect.java
|
||||
|
||||
@@ -1940,6 +1940,13 @@ lblTails=背面
|
||||
lblCallCoinFlip=掷骰子
|
||||
lblWin=赢
|
||||
lblLose=输
|
||||
#FlipOntoBattlefieldEffect.java
|
||||
lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location.
|
||||
lblDidNotFlipOver=The card did not flip over.
|
||||
lblFlippedOver=The card flipped over {0} time(s).
|
||||
lblDidNotLandOnCards=The card did not land on any cards.
|
||||
lblLandedOnOneCard=The card landed on {0}.
|
||||
lblLandedOnTwoCards=The card landed on {0} and {1}.
|
||||
#InvestigateEffect.java
|
||||
lblWouldYouLikeInvestigate=你想要探查吗?
|
||||
#LifeSetEffect.java
|
||||
|
||||
@@ -374,6 +374,7 @@ Tezzeret
|
||||
Tibalt
|
||||
Tyvar
|
||||
Ugin
|
||||
Urza
|
||||
Venser
|
||||
Vivien
|
||||
Vraska
|
||||
|
||||
@@ -2130,3 +2130,11 @@
|
||||
2021-10-11 Legacy Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-11-legacy-challenge.zip
|
||||
2021-10-12 Legacy Preliminary (4 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-12-legacy-preliminary.zip
|
||||
2021-10-16 Legacy League (49 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-16-legacy-league.zip
|
||||
2021-10-18 Legacy Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-18-legacy-challenge.zip
|
||||
2021-10-20 Legacy Preliminary (4 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-20-legacy-preliminary.zip
|
||||
2021-10-22 Legacy Preliminary (5 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-22-legacy-preliminary.zip
|
||||
2021-10-23 Legacy League (49 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-23-legacy-league.zip
|
||||
2021-10-23 Legacy Preliminary (3 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-23-legacy-preliminary.zip
|
||||
2021-10-24 Legacy Premier (16 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-24-legacy-premier.zip
|
||||
2021-10-25 Legacy Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-25-legacy-challenge.zip
|
||||
2021-10-26 Legacy Preliminary (3 decks) | https://downloads.cardforge.org/decks/archive/legacy/2021-10-26-legacy-preliminary.zip
|
||||
|
||||
@@ -2798,3 +2798,17 @@
|
||||
2021-10-16 Modern Preliminary (6 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-16-modern-preliminary.zip
|
||||
2021-10-16 Modern Premier (16 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-16-modern-premier.zip
|
||||
2021-10-17 Modern Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-17-modern-challenge.zip
|
||||
2021-10-18 Modern Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-18-modern-challenge.zip
|
||||
2021-10-18 Modern Premier (16 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-18-modern-premier.zip
|
||||
2021-10-19 Modern League (56 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-19-modern-league.zip
|
||||
2021-10-19 Modern Preliminary (7 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-19-modern-preliminary.zip
|
||||
2021-10-20 Modern Preliminary (5 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-20-modern-preliminary.zip
|
||||
2021-10-21 Modern Preliminary (10 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-21-modern-preliminary.zip
|
||||
2021-10-22 Modern League (56 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-22-modern-league.zip
|
||||
2021-10-22 Modern Preliminary (5 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-22-modern-preliminary.zip
|
||||
2021-10-23 Modern Preliminary (7 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-23-modern-preliminary.zip
|
||||
2021-10-24 Modern Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-24-modern-challenge.zip
|
||||
2021-10-25 Modern Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-25-modern-challenge.zip
|
||||
2021-10-25 Modern Premier (16 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-25-modern-premier.zip
|
||||
2021-10-26 Modern League (65 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-26-modern-league.zip
|
||||
2021-10-26 Modern Preliminary (7 decks) | https://downloads.cardforge.org/decks/archive/modern/2021-10-26-modern-preliminary.zip
|
||||
|
||||
@@ -1815,3 +1815,8 @@
|
||||
2021-10-11 Pauper Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-11-pauper-challenge.zip
|
||||
2021-10-13 Pauper League (30 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-13-pauper-league.zip
|
||||
2021-10-17 Pauper Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-17-pauper-challenge.zip
|
||||
2021-10-18 Pauper Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-18-pauper-challenge.zip
|
||||
2021-10-20 Pauper League (25 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-20-pauper-league.zip
|
||||
2021-10-24 Pauper Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-24-pauper-challenge.zip
|
||||
2021-10-25 Pauper Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2021-10-25-pauper-challenge.zip
|
||||
Pauper League (20 decks) | https://downloads.cardforge.org/decks/archive/pauper/pauper-league.zip
|
||||
|
||||
@@ -755,3 +755,10 @@
|
||||
2021-10-11 Pioneer League (18 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-11-pioneer-league.zip
|
||||
2021-10-14 Pioneer League (15 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-14-pioneer-league.zip
|
||||
2021-10-17 Pioneer Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-17-pioneer-challenge.zip
|
||||
2021-10-18 Pioneer Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-18-pioneer-challenge.zip
|
||||
2021-10-18 Pioneer League (14 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-18-pioneer-league.zip
|
||||
2021-10-21 Pioneer League (17 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-21-pioneer-league.zip
|
||||
2021-10-23 Pioneer Preliminary (6 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-23-pioneer-preliminary.zip
|
||||
2021-10-24 Pioneer Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-24-pioneer-challenge.zip
|
||||
2021-10-25 Pioneer Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-25-pioneer-challenge.zip
|
||||
2021-10-25 Pioneer League (19 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2021-10-25-pioneer-league.zip
|
||||
|
||||
@@ -2515,3 +2515,10 @@
|
||||
2021-10-14 Standard League (12 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-14-standard-league.zip
|
||||
2021-10-15 Standard Preliminary (4 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-15-standard-preliminary.zip
|
||||
2021-10-17 Standard Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-17-standard-challenge.zip
|
||||
2021-10-18 Standard Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-18-standard-challenge.zip
|
||||
2021-10-18 Standard League (15 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-18-standard-league.zip
|
||||
2021-10-20 Standard Preliminary (3 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-20-standard-preliminary.zip
|
||||
2021-10-21 Standard League (5 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-21-standard-league.zip
|
||||
2021-10-24 Standard Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-24-standard-challenge.zip
|
||||
2021-10-25 Standard Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-25-standard-challenge.zip
|
||||
2021-10-25 Standard League (8 decks) | https://downloads.cardforge.org/decks/archive/standard/2021-10-25-standard-league.zip
|
||||
|
||||
@@ -1535,3 +1535,8 @@
|
||||
2021-10-11 Vintage Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-11-vintage-challenge.zip
|
||||
2021-10-17 Vintage Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-17-vintage-challenge.zip
|
||||
2021-10-17 Vintage League (12 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-17-vintage-league.zip
|
||||
2021-10-18 Vintage Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-18-vintage-challenge.zip
|
||||
2021-10-23 Vintage Preliminary (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-23-vintage-preliminary.zip
|
||||
2021-10-24 Vintage Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-24-vintage-challenge.zip
|
||||
2021-10-24 Vintage League (9 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-24-vintage-league.zip
|
||||
2021-10-25 Vintage Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2021-10-25-vintage-challenge.zip
|
||||
|
||||
@@ -13,7 +13,6 @@ import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -74,7 +73,7 @@ public final class InputSelectCardsForConvokeOrImprovise extends InputSelectMany
|
||||
if (improvise) {
|
||||
chosenColor = ManaCostShard.COLORLESS.getColorMask();
|
||||
} else {
|
||||
ColorSet colors = CardUtil.getColors(card);
|
||||
ColorSet colors = card.determineColor();
|
||||
if (colors.isMulticolor()) {
|
||||
//if card is multicolor, strip out any colors which can't be paid towards remaining cost
|
||||
colors = ColorSet.fromMask(colors.getColor() & remainingCost.getUnpaidColors());
|
||||
|
||||
@@ -30,8 +30,7 @@ public class FThreads {
|
||||
public static void invokeInEdtNowOrLater(final Runnable proc) {
|
||||
if (isGuiThread()) {
|
||||
GuiBase.getInterface().invokeInEdtNow(proc);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
GuiBase.getInterface().invokeInEdtLater(proc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +80,7 @@ public final class ItemManagerModel<T extends InventoryItem> {
|
||||
if (data.count(item0) > 0) {
|
||||
if (isInfinite()) {
|
||||
data.removeAll(item0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
data.remove(item0, qty);
|
||||
}
|
||||
isListInSync = false;
|
||||
|
||||
Reference in New Issue
Block a user