Merge branch 'master' into questMode_wildOpponents

# Conflicts:
#	forge-gui/res/languages/en-US.properties
This commit is contained in:
Alessandro Coli
2020-09-27 16:01:56 +02:00
321 changed files with 1531 additions and 238 deletions

View File

@@ -300,4 +300,10 @@ public class TextUtil {
sb.append( str, idx1, str.length() ); sb.append( str, idx1, str.length() );
return sb.toString(); return sb.toString();
} }
//Convert to Mana String
public static String toManaString(String ManaProduced){
if (ManaProduced == "mana"|| ManaProduced.contains("Combo")|| ManaProduced.contains("Any"))
return "mana";//fix manamorphose stack description and probably others..
return "{"+TextUtil.fastReplace(ManaProduced," ","}{")+"}";
}
} }

View File

@@ -121,7 +121,14 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
final String destination = sa.getParam("Destination"); final String destination = sa.getParam("Destination");
final String type = sa.hasParam("ChangeType") ? sa.getParam("ChangeType") : "Card"; String type = "card";
if (sa.hasParam("ChangeTypeDesc")) {
type = sa.getParam("ChangeTypeDesc");
}
else if (sa.hasParam("ChangeType")) {
type = sa.getParam("ChangeType");
}
final int num = sa.hasParam("ChangeNum") ? AbilityUtils.calculateAmount(host, final int num = sa.hasParam("ChangeNum") ? AbilityUtils.calculateAmount(host,
sa.getParam("ChangeNum"), sa) : 1; sa.getParam("ChangeNum"), sa) : 1;
@@ -140,8 +147,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} else if (origin.equals("Library")) { } else if (origin.equals("Library")) {
sb.append(chooserNames); sb.append(chooserNames);
sb.append(" search").append(choosers.size() > 1 ? " " : "es "); sb.append(" search").append(choosers.size() > 1 ? " " : "es ");
sb.append(fetchPlayer); sb.append(fetchPlayer).append(fetchPlayer.equals(chooserNames) ? "'s " : " ");
sb.append("'s library for ").append(num).append(" ").append(type).append(" and "); sb.append("library for ").append(num).append(" ");
sb.append(type).append(num > 1 ? "s" : "");
if (!sa.hasParam("NoReveal")) {
if (choosers.size() == 1) {
sb.append(num > 1 ? ", reveals those cards," : ", reveals that card,");
} else {
sb.append(num > 1 ? ", reveal those cards," : ", reveal that card,");
}
}
sb.append(" and ");
if (destination.equals("Exile")) { if (destination.equals("Exile")) {
if (num == 1) { if (num == 1) {
@@ -169,10 +185,19 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
if (destination.equals("Hand")) { if (destination.equals("Hand")) {
if (num == 1) {
sb.append("into its owner's hand."); sb.append("into its owner's hand.");
} else {
sb.append("into their owner's hand.");
}
} }
if (destination.equals("Graveyard")) { if (destination.equals("Graveyard")) {
sb.append("into its owners's graveyard."); if (num == 1) {
sb.append("into its owner's graveyard.");
} else {
sb.append("into their owner's graveyard.");
}
} }
} }
sb.append(" Then shuffle that library."); sb.append(" Then shuffle that library.");
@@ -297,7 +322,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (ZoneType.Graveyard.equals(origin)) { if (ZoneType.Graveyard.equals(origin)) {
sb.append(fromGraveyard); sb.append(fromGraveyard);
} }
sb.append(" to").append(pronoun).append("owners hand."); sb.append(" to").append(pronoun).append("owner's hand.");
} }
if (destination.equals(ZoneType.Library)) { if (destination.equals(ZoneType.Library)) {

View File

@@ -49,6 +49,13 @@ public class CountersPutEffect extends SpellAbilityEffect {
final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose"); final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility); final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
//skip the StringBuilder if no targets are chosen ("up to" scenario)
if (spellAbility.usesTargeting()) {
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
if (targetCards.size() == 0) {
return stringBuilder.toString();
}
}
if (spellAbility.hasParam("Bolster")) { if (spellAbility.hasParam("Bolster")) {
stringBuilder.append("Bolster ").append(amount); stringBuilder.append("Bolster ").append(amount);
return stringBuilder.toString(); return stringBuilder.toString();

View File

@@ -6,26 +6,24 @@ import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
public class GoadEffect extends SpellAbilityEffect { public class GoadEffect extends SpellAbilityEffect {
@Override @Override
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player player = sa.getActivatingPlayer(); final Player player = sa.getActivatingPlayer();
final Game game = player.getGame(); final Game game = player.getGame();
final long timestamp = game.getNextTimestamp(); final long timestamp = game.getNextTimestamp();
for (final Card tgtC : getTargetCards(sa)) { for (final Card tgtC : getDefinedCardsOrTargeted(sa)) {
// only pump things in PumpZone // only pump things in PumpZone
if (!game.getCardsIn(ZoneType.Battlefield).contains(tgtC)) { if (!game.getCardsIn(ZoneType.Battlefield).contains(tgtC)) {
continue; continue;
} }
// if pump is a target, make sure we can still target now // if pump is a target, make sure we can still target now
if ((tgt != null) && !tgtC.canBeTargetedBy(sa)) { if (sa.usesTargeting() && !sa.getTargetRestrictions().canTgtPlayer() && !tgtC.canBeTargetedBy(sa)) {
continue; continue;
} }

View File

@@ -24,6 +24,8 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import java.util.List; import java.util.List;
import static forge.util.TextUtil.toManaString;
public class ManaEffect extends SpellAbilityEffect { public class ManaEffect extends SpellAbilityEffect {
@Override @Override
@@ -252,7 +254,7 @@ public class ManaEffect extends SpellAbilityEffect {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
String mana = !sa.hasParam("Amount") || StringUtils.isNumeric(sa.getParam("Amount")) String mana = !sa.hasParam("Amount") || StringUtils.isNumeric(sa.getParam("Amount"))
? GameActionUtil.generatedMana(sa) : "mana"; ? GameActionUtil.generatedMana(sa) : "mana";
sb.append("Add ").append(mana).append("."); sb.append("Add ").append(toManaString(mana)).append(".");
return sb.toString(); return sb.toString();
} }
} }

View File

@@ -105,9 +105,7 @@ public class MillEffect extends SpellAbilityEffect {
if (numCards != 1) { if (numCards != 1) {
sb.append("s"); sb.append("s");
} }
final String millPosition = sa.hasParam("FromBottom") ? "bottom" : "top"; sb.append(".");
sb.append(" from the ").append(millPosition).append(" of their library.");
return sb.toString(); return sb.toString();
} }

View File

@@ -6174,6 +6174,12 @@ public class Card extends GameEntity implements Comparable<Card> {
source.forceTurnFaceUp(); source.forceTurnFaceUp();
} }
// the modal state is not copied with lki, need to copy it extra
if (!source.hasState(CardStateName.Modal)) {
source.addAlternateState(CardStateName.Modal, false);
source.getState(CardStateName.Modal).copyFrom(this.getState(CardStateName.Modal), true);
}
source.setSplitStateToPlayAbility(la); source.setSplitStateToPlayAbility(la);
if (la.canPlay(source)) { if (la.canPlay(source)) {

View File

@@ -43,6 +43,7 @@ import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface; import forge.game.keyword.KeywordInterface;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerCollection;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler; import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer; import forge.game.replacement.ReplacementLayer;
@@ -80,10 +81,9 @@ public class CardFactoryUtil {
public static SpellAbility buildBasicLandAbility(final CardState state, byte color) { public static SpellAbility buildBasicLandAbility(final CardState state, byte color) {
String strcolor = MagicColor.toShortString(color); String strcolor = MagicColor.toShortString(color);
String abString = "AB$ Mana | Cost$ T | Produced$ " + strcolor + String abString = "AB$ Mana | Cost$ T | Produced$ " + strcolor +
" | SpellDescription$ Add {" + strcolor + "}."; " | Secondary$ True | SpellDescription$ Add {" + strcolor + "}.";
SpellAbility sa = AbilityFactory.getAbility(abString, state); SpellAbility sa = AbilityFactory.getAbility(abString, state);
sa.setIntrinsic(true); // always intristic sa.setIntrinsic(true); // always intristic
sa.setBasicLandAbility(true); // to exclude it from other suspress effects
return sa; return sa;
} }
@@ -1294,6 +1294,9 @@ public class CardFactoryUtil {
if (sq[0].contains("CardNumColors")) { if (sq[0].contains("CardNumColors")) {
return doXMath(CardUtil.getColors(c).countColors(), m, c); return doXMath(CardUtil.getColors(c).countColors(), m, c);
} }
if (sq[0].contains("CardNumAttacksThisTurn")) {
return doXMath(c.getDamageHistory().getCreatureAttacksThisTurn(), m, c);
}
if (sq[0].contains("ChosenNumber")) { if (sq[0].contains("ChosenNumber")) {
Integer i = c.getChosenNumber(); Integer i = c.getChosenNumber();
return doXMath(i == null ? 0 : i, m, c); return doXMath(i == null ? 0 : i, m, c);
@@ -1336,6 +1339,13 @@ public class CardFactoryUtil {
return doXMath(getCardTypesFromList(game.getCardsIn(ZoneType.smartValueOf(sq[1]))), m, c); return doXMath(getCardTypesFromList(game.getCardsIn(ZoneType.smartValueOf(sq[1]))), m, c);
} }
if (sq[0].contains("OppTypesInGrave")) {
final PlayerCollection opponents = cc.getOpponents();
CardCollection oppCards = new CardCollection();
oppCards.addAll(opponents.getCardsIn(ZoneType.Graveyard));
return doXMath(getCardTypesFromList(oppCards), m, c);
}
if (sq[0].contains("BushidoPoint")) { if (sq[0].contains("BushidoPoint")) {
return doXMath(c.getKeywordMagnitude(Keyword.BUSHIDO), m, c); return doXMath(c.getKeywordMagnitude(Keyword.BUSHIDO), m, c);
} }

View File

@@ -456,6 +456,19 @@ public final class CardPredicates {
}; };
} }
public static final Predicate<Card> castSA(final Predicate<SpellAbility> predSA) {
return new Predicate<Card>() {
@Override
public boolean apply(final Card c)
{
if (c.getCastSA() == null) {
return false;
}
return predSA.apply(c.getCastSA());
}
};
}
public static class Presets { public static class Presets {
/** /**

View File

@@ -173,23 +173,12 @@ public final class CardUtil {
} }
public static List<Card> getThisTurnCast(final String valid, final Card src) { public static List<Card> getThisTurnCast(final String valid, final Card src) {
List<Card> res = Lists.newArrayList(); return CardLists.getValidCardsAsList(src.getGame().getStack().getSpellsCastThisTurn(), valid, src.getController(), src);
final Game game = src.getGame();
res.addAll(game.getStack().getSpellsCastThisTurn());
res = CardLists.getValidCardsAsList(res, valid, src.getController(), src);
return res;
} }
public static List<Card> getLastTurnCast(final String valid, final Card src) { public static List<Card> getLastTurnCast(final String valid, final Card src) {
List<Card> res = Lists.newArrayList(); return CardLists.getValidCardsAsList(src.getGame().getStack().getSpellsCastLastTurn(), valid, src.getController(), src);
final Game game = src.getGame();
res.addAll(game.getStack().getSpellsCastLastTurn());
res = CardLists.getValidCardsAsList(res, valid, src.getController(), src);
return res;
} }
/** /**

View File

@@ -14,6 +14,7 @@ import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.AbilityActivated; import forge.game.spellability.AbilityActivated;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityPredicates;
import forge.game.spellability.TargetChoices; import forge.game.spellability.TargetChoices;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.zone.Zone; import forge.game.zone.Zone;
@@ -423,6 +424,7 @@ public class CostAdjustment {
final Player controller = hostCard.getController(); final Player controller = hostCard.getController();
final Player activator = sa.getActivatingPlayer(); final Player activator = sa.getActivatingPlayer();
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
final Game game = hostCard.getGame();
if (st.hasParam("ValidCard") if (st.hasParam("ValidCard")
&& !card.isValid(st.getParam("ValidCard").split(","), controller, hostCard, sa)) { && !card.isValid(st.getParam("ValidCard").split(","), controller, hostCard, sa)) {
@@ -452,7 +454,19 @@ public class CostAdjustment {
if (activator == null ) { if (activator == null ) {
return false; return false;
} }
List<Card> list = CardUtil.getThisTurnCast(st.getParam("ValidCard"), hostCard); List<Card> list;
if (st.hasParam("ValidCard")) {
list = CardUtil.getThisTurnCast(st.getParam("ValidCard"), hostCard);
} else {
list = game.getStack().getSpellsCastThisTurn();
}
if (st.hasParam("ValidSpell")) {
list = CardLists.filterAsList(list, CardPredicates.castSA(
SpellAbilityPredicates.isValid(st.getParam("ValidSpell").split(","), controller, hostCard, sa))
);
}
if (CardLists.filterControlledBy(list, activator).size() > 0) { if (CardLists.filterControlledBy(list, activator).size() > 0) {
return false; return false;
} }

View File

@@ -1,8 +1,16 @@
package forge.game.player; package forge.game.player;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import forge.LobbyPlayer; import forge.LobbyPlayer;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.ICardFace; import forge.card.ICardFace;
@@ -14,7 +22,11 @@ import forge.game.GameEntity;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.GameOutcome.AnteResult; import forge.game.GameOutcome.AnteResult;
import forge.game.GameType; import forge.game.GameType;
import forge.game.card.*; import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardView;
import forge.game.card.CounterType;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostPart; import forge.game.cost.CostPart;
@@ -23,18 +35,17 @@ import forge.game.keyword.KeywordInterface;
import forge.game.mana.Mana; import forge.game.mana.Mana;
import forge.game.mana.ManaConversionMatrix; import forge.game.mana.ManaConversionMatrix;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*; import forge.game.spellability.AbilitySub;
import forge.game.spellability.OptionalCostValue;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetChoices;
import forge.game.trigger.WrappedAbility; import forge.game.trigger.WrappedAbility;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/** /**
* A prototype for player controller class * A prototype for player controller class
@@ -278,4 +289,7 @@ public abstract class PlayerController {
public abstract CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, public abstract CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap,
SpellAbility sa, String title, boolean isOptional); SpellAbility sa, String title, boolean isOptional);
public void handleLandPlayed(Card land, Zone zone) {
}
} }

View File

@@ -113,8 +113,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private boolean blessing = false; private boolean blessing = false;
private Integer chapter = null; private Integer chapter = null;
private boolean basicLandAbility = false;
private CardStateName stateName = null; private CardStateName stateName = null;
private int totalManaSpent = 0; private int totalManaSpent = 0;
@@ -789,13 +787,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return this.isAlternativeCost(AlternativeCost.Flashback); return this.isAlternativeCost(AlternativeCost.Flashback);
} }
public void setBasicLandAbility(final boolean basicLandAbility0) {
basicLandAbility = basicLandAbility0;
}
public boolean isBasicLandAbility() {
return basicLandAbility && isIntrinsic();
}
/** /**
* @return the aftermath * @return the aftermath
*/ */

View File

@@ -4,6 +4,8 @@ import com.google.common.base.Predicate;
import forge.game.CardTraitPredicates; import forge.game.CardTraitPredicates;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.player.Player;
public final class SpellAbilityPredicates extends CardTraitPredicates { public final class SpellAbilityPredicates extends CardTraitPredicates {
public static final Predicate<SpellAbility> isApi(final ApiType type) { public static final Predicate<SpellAbility> isApi(final ApiType type) {
@@ -59,4 +61,13 @@ public final class SpellAbilityPredicates extends CardTraitPredicates {
} }
}; };
} }
public static final Predicate<SpellAbility> isValid(String[] restrictions, Player sourceController, Card source, SpellAbility spellAbility) {
return new Predicate<SpellAbility>() {
@Override
public boolean apply(final SpellAbility sa) {
return sa.isValid(restrictions, sourceController, source, spellAbility);
}
};
}
} }

View File

@@ -26,6 +26,8 @@ import forge.util.Localizer;
import java.util.Map; import java.util.Map;
import static forge.util.TextUtil.toManaString;
/** /**
* <p> * <p>
* Trigger_TapsForMana class. * Trigger_TapsForMana class.
@@ -99,6 +101,9 @@ public class TriggerTapsForMana extends Trigger {
if (!this.getHostCard().hasChosenColor() || !produced.contains(MagicColor.toShortString(this.getHostCard().getChosenColor()))) { if (!this.getHostCard().hasChosenColor() || !produced.contains(MagicColor.toShortString(this.getHostCard().getChosenColor()))) {
return false; return false;
} }
if (!produced.contains(MagicColor.toShortString(this.getParam("Produced")))) {
return false;
}
} }
} }
@@ -116,7 +121,7 @@ public class TriggerTapsForMana extends Trigger {
public String getImportantStackObjects(SpellAbility sa) { public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblTappedForMana")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card)); sb.append(Localizer.getInstance().getMessage("lblTappedForMana")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card));
sb.append(Localizer.getInstance().getMessage("lblProduced")).append(": ").append(sa.getTriggeringObject(AbilityKey.Produced)); sb.append(Localizer.getInstance().getMessage("lblProduced")).append(": ").append(toManaString(sa.getTriggeringObject(AbilityKey.Produced).toString()));
return sb.toString(); return sb.toString();
} }

View File

@@ -28,6 +28,8 @@ import forge.game.card.CardUtil;
import forge.game.event.EventValueChangeType; import forge.game.event.EventValueChangeType;
import forge.game.event.GameEventZone; import forge.game.event.GameEventZone;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerCollection;
import forge.game.player.PlayerController;
import forge.util.CollectionSuppliers; import forge.util.CollectionSuppliers;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.maps.EnumMapOfLists; import forge.util.maps.EnumMapOfLists;
@@ -123,7 +125,21 @@ public class Zone implements java.io.Serializable, Iterable<Card> {
} }
} }
onChanged(); onChanged();
if(zoneType == ZoneType.Battlefield && c.isLand()) {
PlayerCollection playerCollection = game.getPlayers();
int numPlayers = playerCollection.size();
for (int i = 0; i < numPlayers; i++) {
Player player = playerCollection.get(i);
if(!player.isAI()) {
PlayerController playerControllerHuman = player.getController();
playerControllerHuman.handleLandPlayed(c,this);
}
}
}
game.fireEvent(new GameEventZone(zoneType, getPlayer(), EventValueChangeType.Added, c)); game.fireEvent(new GameEventZone(zoneType, getPlayer(), EventValueChangeType.Added, c));
} }
public final boolean contains(final Card c) { public final boolean contains(final Card c) {

View File

@@ -246,6 +246,7 @@ public enum CSubmenuPreferences implements ICDoc {
initializeMulliganRuleComboBox(); initializeMulliganRuleComboBox();
initializeAiProfilesComboBox(); initializeAiProfilesComboBox();
initializeStackAdditionsComboBox(); initializeStackAdditionsComboBox();
initializeLandPlayedComboBox();
initializeColorIdentityCombobox(); initializeColorIdentityCombobox();
initializeAutoYieldModeComboBox(); initializeAutoYieldModeComboBox();
initializeCounterDisplayTypeComboBox(); initializeCounterDisplayTypeComboBox();
@@ -448,6 +449,17 @@ public enum CSubmenuPreferences implements ICDoc {
panel.setComboBox(comboBox, selectedItem); panel.setComboBox(comboBox, selectedItem);
} }
private void initializeLandPlayedComboBox() {
final String[] elems = {ForgeConstants.LAND_PLAYED_NOTIFICATION_NEVER, ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS,
ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS_FOR_NONBASIC_LANDS, ForgeConstants.LAND_PLAYED_NOTIFICATION_AI,
ForgeConstants.LAND_PLAYED_NOTIFICATION_AI_FOR_NONBASIC_LANDS};
final FPref userSetting = FPref.UI_LAND_PLAYED_NOTIFICATION_POLICY;
final FComboBoxPanel<String> panel = this.view.getCbpLandPlayedComboBoxPanel();
final FComboBox<String> comboBox = createComboBox(elems, userSetting);
final String selectedItem = this.prefs.getPref(userSetting);
panel.setComboBox(comboBox, selectedItem);
}
private void initializeColorIdentityCombobox() { private void initializeColorIdentityCombobox() {
final String[] elems = {ForgeConstants.DISP_CURRENT_COLORS_NEVER, ForgeConstants.DISP_CURRENT_COLORS_CHANGED, final String[] elems = {ForgeConstants.DISP_CURRENT_COLORS_NEVER, ForgeConstants.DISP_CURRENT_COLORS_CHANGED,
ForgeConstants.DISP_CURRENT_COLORS_MULTICOLOR, ForgeConstants.DISP_CURRENT_COLORS_MULTI_OR_CHANGED, ForgeConstants.DISP_CURRENT_COLORS_MULTICOLOR, ForgeConstants.DISP_CURRENT_COLORS_MULTI_OR_CHANGED,

View File

@@ -120,6 +120,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":"); private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":");
private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":"); private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":");
private final FComboBoxPanel<String> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":"); private final FComboBoxPanel<String> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":");
private final FComboBoxPanel<String> cbpLandPlayed = new FComboBoxPanel<>(localizer.getMessage("cbpLandPlayed")+":");
private final FComboBoxPanel<String> cbpDisplayCurrentCardColors = new FComboBoxPanel<>(localizer.getMessage("cbpDisplayCurrentCardColors")+":"); private final FComboBoxPanel<String> cbpDisplayCurrentCardColors = new FComboBoxPanel<>(localizer.getMessage("cbpDisplayCurrentCardColors")+":");
private final FComboBoxPanel<String> cbpAutoYieldMode = new FComboBoxPanel<>(localizer.getMessage("cbpAutoYieldMode")+":"); private final FComboBoxPanel<String> cbpAutoYieldMode = new FComboBoxPanel<>(localizer.getMessage("cbpAutoYieldMode")+":");
private final FComboBoxPanel<String> cbpCounterDisplayType = new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayType")+":"); private final FComboBoxPanel<String> cbpCounterDisplayType = new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayType")+":");
@@ -206,6 +207,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbpStackAdditions, comboBoxConstraints); pnlPrefs.add(cbpStackAdditions, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpStackAdditions")), descriptionConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpStackAdditions")), descriptionConstraints);
pnlPrefs.add(cbpLandPlayed, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpLandPlayed")), descriptionConstraints);
pnlPrefs.add(cbEnforceDeckLegality, titleConstraints); pnlPrefs.add(cbEnforceDeckLegality, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnforceDeckLegality")), descriptionConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnforceDeckLegality")), descriptionConstraints);
@@ -685,6 +689,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbpStackAdditions; return cbpStackAdditions;
} }
public FComboBoxPanel<String> getCbpLandPlayedComboBoxPanel() {
return cbpLandPlayed;
}
public FComboBoxPanel<GameLogEntryType> getGameLogVerbosityComboBoxPanel() { public FComboBoxPanel<GameLogEntryType> getGameLogVerbosityComboBoxPanel() {
return cbpGameLogEntryType; return cbpGameLogEntryType;
} }

View File

@@ -17,11 +17,40 @@
*/ */
package forge.screens.match; package forge.screens.match;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.*;
import forge.FThreads;
import forge.GuiBase;
import forge.ImageCache;
import forge.LobbyPlayer;
import forge.Singletons;
import forge.StaticData;
import forge.assets.FSkinProp; import forge.assets.FSkinProp;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.control.KeyboardShortcuts; import forge.control.KeyboardShortcuts;
@@ -42,11 +71,28 @@ import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal; import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon; import forge.game.player.IHasIcon;
import forge.game.player.Player;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.spellability.*; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.StackItemView;
import forge.game.spellability.TargetChoices;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.*; import forge.gui.FNetOverlay;
import forge.gui.framework.*; import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
import forge.gui.GuiUtils;
import forge.gui.SOverlayUtils;
import forge.gui.framework.DragCell;
import forge.gui.framework.EDocID;
import forge.gui.framework.FScreen;
import forge.gui.framework.ICDoc;
import forge.gui.framework.IVDoc;
import forge.gui.framework.SDisplayUtil;
import forge.gui.framework.SLayoutIO;
import forge.gui.framework.VEmptyDoc;
import forge.item.InventoryItem; import forge.item.InventoryItem;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.match.AbstractGuiGame; import forge.match.AbstractGuiGame;
@@ -57,12 +103,23 @@ import forge.player.PlayerZoneUpdates;
import forge.properties.ForgeConstants; import forge.properties.ForgeConstants;
import forge.properties.ForgePreferences; import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
import forge.screens.match.controllers.*; import forge.screens.match.controllers.CAntes;
import forge.screens.match.controllers.CCombat;
import forge.screens.match.controllers.CDetailPicture;
import forge.screens.match.controllers.CDev;
import forge.screens.match.controllers.CDock;
import forge.screens.match.controllers.CLog;
import forge.screens.match.controllers.CPrompt;
import forge.screens.match.controllers.CStack;
import forge.screens.match.menus.CMatchUIMenus; import forge.screens.match.menus.CMatchUIMenus;
import forge.screens.match.views.VField; import forge.screens.match.views.VField;
import forge.screens.match.views.VHand; import forge.screens.match.views.VHand;
import forge.toolbox.*; import forge.toolbox.FButton;
import forge.toolbox.FLabel;
import forge.toolbox.FOptionPane;
import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinImage; import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.FTextArea;
import forge.toolbox.imaging.FImagePanel; import forge.toolbox.imaging.FImagePanel;
import forge.toolbox.imaging.FImagePanel.AutoSizeImageMode; import forge.toolbox.imaging.FImagePanel.AutoSizeImageMode;
import forge.toolbox.imaging.FImageUtil; import forge.toolbox.imaging.FImageUtil;
@@ -79,17 +136,6 @@ import forge.view.arcane.CardPanel;
import forge.view.arcane.FloatingZone; import forge.view.arcane.FloatingZone;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* Constructs instance of match UI controller, used as a single point of * Constructs instance of match UI controller, used as a single point of
* top-level control for child UIs. Tasks targeting the view of individual * top-level control for child UIs. Tasks targeting the view of individual
@@ -1392,4 +1438,57 @@ public final class CMatchUI
nextNotifiableStackIndex--; nextNotifiableStackIndex--;
} }
@Override
public void handleLandPlayed(Card land, Zone zone) {
Runnable createPopupThread = new Runnable() {
@Override
public void run() {
createLandPopupPanel(land,zone);
}
};
GuiBase.getInterface().invokeInEdtAndWait(createPopupThread);
}
private void createLandPopupPanel(Card land, Zone zone) {
String landPlayedNotificationPolicy = FModel.getPreferences().getPref(FPref.UI_LAND_PLAYED_NOTIFICATION_POLICY);
Player cardController = land.getController();
boolean isAi = cardController.isAI();
if(ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS.equals(landPlayedNotificationPolicy)
|| (ForgeConstants.LAND_PLAYED_NOTIFICATION_AI.equals(landPlayedNotificationPolicy) && (isAi))
|| (ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS_FOR_NONBASIC_LANDS.equals(landPlayedNotificationPolicy) && !land.isBasicLand())
|| (ForgeConstants.LAND_PLAYED_NOTIFICATION_AI_FOR_NONBASIC_LANDS.equals(landPlayedNotificationPolicy) && !land.isBasicLand()) && (isAi)) {
String title = "Forge";
List<String> options = ImmutableList.of(Localizer.getInstance().getMessage("lblOK"));
MigLayout migLayout = new MigLayout("insets 15, left, gap 30, fill");
JPanel mainPanel = new JPanel(migLayout);
final Dimension parentSize = JOptionPane.getRootFrame().getSize();
Dimension maxSize = new Dimension(1400, parentSize.height - 100);
mainPanel.setMaximumSize(maxSize);
mainPanel.setOpaque(false);
int rotation = getRotation(land.getView());
FImagePanel imagePanel = new FImagePanel();
BufferedImage bufferedImage = FImageUtil.getImage(land.getCurrentState().getView());
imagePanel.setImage(bufferedImage, rotation, AutoSizeImageMode.SOURCE);
int imageWidth = 433;
int imageHeight = 600;
Dimension imagePanelDimension = new Dimension(imageWidth,imageHeight);
imagePanel.setMinimumSize(imagePanelDimension);
mainPanel.add(imagePanel, "cell 0 0, spany 3");
String msg = cardController.toString() + " puts " + land.toString() + " into play into " + zone.toString() + ".";
final FTextArea prompt1 = new FTextArea(msg);
prompt1.setFont(FSkin.getFont(21));
prompt1.setAutoSize(true);
prompt1.setMinimumSize(new Dimension(475,200));
mainPanel.add(prompt1, "cell 1 0, aligny top");
FOptionPane.showOptionDialog(null, title, null, mainPanel, options);
}
}
} }

View File

@@ -36,7 +36,7 @@ public class VCombat implements IVDoc<CCombat> {
// Fields used with interface IVDoc // Fields used with interface IVDoc
private DragCell parentCell; private DragCell parentCell;
private final DragTab tab = new DragTab(Localizer.getInstance().getMessage("lblCombat")); private final DragTab tab = new DragTab(Localizer.getInstance().getMessage("lblCombatTab"));
private final SkinnedTextArea tar = new SkinnedTextArea(); private final SkinnedTextArea tar = new SkinnedTextArea();
@@ -109,7 +109,7 @@ public class VCombat implements IVDoc<CCombat> {
// No need to update this unless it's showing // No need to update this unless it's showing
if (!this.equals(parentCell.getSelected())) { return; } if (!this.equals(parentCell.getSelected())) { return; }
tab.setText(cntAttackers > 0 ? (Localizer.getInstance().getMessage("lblCombat") + " : " + cntAttackers) : Localizer.getInstance().getMessage("lblCombat")); tab.setText(cntAttackers > 0 ? (Localizer.getInstance().getMessage("lblCombatTab") + " : " + cntAttackers) : Localizer.getInstance().getMessage("lblCombatTab"));
tar.setText(desc); tar.setText(desc);
} }
} }

View File

@@ -39,7 +39,9 @@ import forge.properties.ForgeConstants;
import forge.util.ImageUtil; import forge.util.ImageUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -77,6 +79,7 @@ public class ImageCache {
public static final Texture defaultImage; public static final Texture defaultImage;
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK; public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE; public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
private static final Map<Texture, Boolean> Borders = new HashMap<>();
private static boolean imageLoaded, delayLoadRequested; private static boolean imageLoaded, delayLoadRequested;
public static void allowSingleLoad() { public static void allowSingleLoad() {
@@ -99,6 +102,7 @@ public class ImageCache {
cache.invalidateAll(); cache.invalidateAll();
cache.cleanUp(); cache.cleanUp();
missingIconKeys.clear(); missingIconKeys.clear();
Borders.clear();
} }
public static void disposeTexture(){ public static void disposeTexture(){
@@ -181,6 +185,8 @@ public class ImageCache {
if (useDefaultIfNotFound) { if (useDefaultIfNotFound) {
image = defaultImage; image = defaultImage;
cache.put(imageKey, defaultImage); cache.put(imageKey, defaultImage);
if (Borders.get(image) == null)
Borders.put(image, false); //black border
} }
} }
return image; return image;
@@ -192,8 +198,8 @@ public class ImageCache {
e.printStackTrace(); e.printStackTrace();
} }
} }
public static TextureRegion croppedBorderImage(Texture image, boolean fullborder) { public static TextureRegion croppedBorderImage(Texture image) {
if (!fullborder) if (!image.toString().contains(".fullborder."))
return new TextureRegion(image); return new TextureRegion(image);
float rscale = 0.96f; float rscale = 0.96f;
int rw = Math.round(image.getWidth()*rscale); int rw = Math.round(image.getWidth()*rscale);
@@ -202,25 +208,6 @@ public class ImageCache {
int ry = Math.round((image.getHeight() - rh)/2f)-2; int ry = Math.round((image.getHeight() - rh)/2f)-2;
return new TextureRegion(image, rx, ry, rw, rh); return new TextureRegion(image, rx, ry, rw, rh);
} }
public static boolean isWhiteBordered(IPaperCard c) {
if (c == null)
return false;
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
if (ed != null && ed.isWhiteBorder())
return true;
return false;
}
public static boolean isWhiteBordered(CardView c) {
if (c == null)
return false;
CardView.CardStateView state = c.getCurrentState();
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
if (ed != null && ed.isWhiteBorder() && state.getFoilIndex() == 0)
return true;
return false;
}
public static Color borderColor(IPaperCard c) { public static Color borderColor(IPaperCard c) {
if (c == null) if (c == null)
return Color.valueOf("#171717"); return Color.valueOf("#171717");
@@ -250,38 +237,24 @@ public class ImageCache {
return 1; return 1;
return 0; return 0;
} }
public static boolean isExtendedArt(CardView c) { public static boolean isBorderlessCardArt(Texture t) {
if (c == null) return ImageLoader.isBorderless(t);
return false;
CardView.CardStateView state = c.getCurrentState();
if (state.getSetCode().contains("MPS_"))
return true;
if (state.getSetCode().equalsIgnoreCase("UST"))
return true;
return false;
} }
public static boolean isExtendedArt(IPaperCard c) { public static void updateBorders(Texture t, boolean val){
if (c == null) Borders.put(t, val);
return false;
if (c.getEdition().contains("MPS_"))
return true;
if (c.getEdition().equalsIgnoreCase("UST"))
return true;
return false;
} }
public static FImage getBorderImage(CardView c, boolean canshow) { public static FImage getBorder(Texture t) {
if (Borders.get(t) == null)
return BlackBorder;
return Borders.get(t) ? WhiteBorder : BlackBorder;
}
public static FImage getBorderImage(Texture t, boolean canshow) {
if (!canshow) if (!canshow)
return BlackBorder; return BlackBorder;
if (isWhiteBordered(c)) return getBorder(t);
return WhiteBorder;
return BlackBorder;
} }
public static FImage getBorderImage(IPaperCard c) { public static FImage getBorderImage(Texture t) {
if (isWhiteBordered(c)) return getBorder(t);
return WhiteBorder;
return BlackBorder;
} }
public static Color getTint(CardView c) { public static Color getTint(CardView c) {
if (c == null) if (c == null)

View File

@@ -1,6 +1,7 @@
package forge.assets; package forge.assets;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
@@ -13,24 +14,155 @@ import forge.FThreads;
import forge.Forge; import forge.Forge;
import forge.ImageKeys; import forge.ImageKeys;
import forge.util.TextUtil;
import static forge.assets.ImageCache.croppedBorderImage;
final class ImageLoader extends CacheLoader<String, Texture> { final class ImageLoader extends CacheLoader<String, Texture> {
private static ArrayList<String> borderlessCardlistKey = new ArrayList<String>() {
{//TODO: load the values from text list instead of hardcoded
add("2XM/Academy Ruins2.fullborder");
add("2XM/Atraxa, Praetors' Voice2.fullborder");
add("2XM/Avacyn, Angel of Hope2.fullborder");
add("2XM/Batterskull2.fullborder");
add("2XM/Blightsteel Colossus2.fullborder");
add("2XM/Blood Moon2.fullborder");
add("2XM/Brainstorm2.fullborder");
add("2XM/Chrome Mox2.fullborder");
add("2XM/Council's Judgment2.fullborder");
add("2XM/Crop Rotation2.fullborder");
add("2XM/Cyclonic Rift2.fullborder");
add("2XM/Dark Confidant2.fullborder");
add("2XM/Doubling Season2.fullborder");
add("2XM/Expedition Map2.fullborder");
add("2XM/Exploration2.fullborder");
add("2XM/Fatal Push2.fullborder");
add("2XM/Force of Will2.fullborder");
add("2XM/Goblin Guide2.fullborder");
add("2XM/Jace, the Mind Sculptor2.fullborder");
add("2XM/Kaalia of the Vast2.fullborder");
add("2XM/Karn Liberated2.fullborder");
add("2XM/Lightning Greaves2.fullborder");
add("2XM/Mana Crypt2.fullborder");
add("2XM/Meddling Mage2.fullborder");
add("2XM/Mox Opal2.fullborder");
add("2XM/Noble Hierarch2.fullborder");
add("2XM/Phyrexian Metamorph2.fullborder");
add("2XM/Sneak Attack2.fullborder");
add("2XM/Stoneforge Mystic2.fullborder");
add("2XM/Sword of Body and Mind2.fullborder");
add("2XM/Sword of Feast and Famine2.fullborder");
add("2XM/Sword of Fire and Ice2.fullborder");
add("2XM/Sword of Light and Shadow2.fullborder");
add("2XM/Sword of War and Peace2.fullborder");
add("2XM/Thoughtseize2.fullborder");
add("2XM/Toxic Deluge2.fullborder");
add("2XM/Urza's Mine2.fullborder");
add("2XM/Urza's Power Plant2.fullborder");
add("2XM/Urza's Tower2.fullborder");
add("2XM/Wurmcoil Engine2.fullborder");
add("ELD/Garruk, Cursed Huntsman2.fullborder");
add("ELD/Oko, Thief of Crowns2.fullborder");
add("ELD/The Royal Scions2.fullborder");
add("IKO/Brokkos, Apex of Forever2.fullborder");
add("IKO/Brokkos, Apex of Forever3.fullborder");
add("IKO/Crystalline Giant3.fullborder");
add("IKO/Cubwarden2.fullborder");
add("IKO/Dirge Bat2.fullborder");
add("IKO/Dirge Bat3.fullborder");
add("IKO/Everquill Phoenix2.fullborder");
add("IKO/Everquill Phoenix3.fullborder");
add("IKO/Gemrazer2.fullborder");
add("IKO/Gemrazer3.fullborder");
add("IKO/Gyruda, Doom of Depths3.fullborder");
add("IKO/Huntmaster Liger2.fullborder");
add("IKO/Huntmaster Liger3.fullborder");
add("IKO/Illuna, Apex of Wishes2.fullborder");
add("IKO/Illuna, Apex of Wishes3.fullborder");
add("IKO/Indatha Triome2.fullborder");
add("IKO/Ketria Triome2.fullborder");
add("IKO/Lukka, Coppercoat Outcast2.fullborder");
add("IKO/Luminous Broodmoth3.fullborder");
add("IKO/Mysterious Egg2.fullborder");
add("IKO/Narset of the Ancient Way2.fullborder");
add("IKO/Nethroi, Apex of Death2.fullborder");
add("IKO/Nethroi, Apex of Death3.fullborder");
add("IKO/Pollywog Symbiote2.fullborder");
add("IKO/Raugrin Triome2.fullborder");
add("IKO/Savai Triome2.fullborder");
add("IKO/Sea-Dasher Octopus2.fullborder");
add("IKO/Snapdax, Apex of the Hunt2.fullborder");
add("IKO/Snapdax, Apex of the Hunt3.fullborder");
add("IKO/Sprite Dragon3.fullborder");
add("IKO/Titanoth Rex2.fullborder");
add("IKO/Vadrok, Apex of Thunder2.fullborder");
add("IKO/Vadrok, Apex of Thunder3.fullborder");
add("IKO/Vivien, Monsters' Advocate2.fullborder");
add("IKO/Void Beckoner2.fullborder");
add("IKO/Yidaro, Wandering Monster3.fullborder");
add("IKO/Zagoth Triome2.fullborder");
add("IKO/Zilortha, Strength Incarnate.fullborder");
add("M21/Basri Ket2.fullborder");
add("M21/Chandra, Heart of Fire2.fullborder");
add("M21/Containment Priest2.fullborder");
add("M21/Cultivate2.fullborder");
add("M21/Garruk, Unleashed2.fullborder");
add("M21/Grim Tutor2.fullborder");
add("M21/Liliana, Waker of the Dead2.fullborder");
add("M21/Massacre Wurm2.fullborder");
add("M21/Scavenging Ooze2.fullborder");
add("M21/Solemn Simulacrum2.fullborder");
add("M21/Teferi, Master of Time2.fullborder");
add("M21/Ugin, the Spirit Dragon2.fullborder");
add("M21/Ugin, the Spirit Dragon3.fullborder");
add("PLGS/Hangarback Walker.fullborder");
add("SLD/Acidic Slime.fullborder");
add("SLD/Captain Sisay.fullborder");
add("SLD/Meren of Clan Nel Toth.fullborder");
add("SLD/Narset, Enlightened Master.fullborder");
add("SLD/Necrotic Ooze.fullborder");
add("SLD/Oona, Queen of the Fae.fullborder");
add("SLD/Saskia the Unyielding.fullborder");
add("SLD/The Mimeoplasm.fullborder");
add("SLD/Voidslime.fullborder");
add("THB/Ashiok, Nightmare Muse2.fullborder");
add("THB/Calix, Destiny's Hand2.fullborder");
add("THB/Elspeth, Sun's Nemesis2.fullborder");
add("UST/Forest.fullborder");
add("UST/Island.fullborder");
add("UST/Mountain.fullborder");
add("UST/Plains.fullborder");
add("UST/Swamp.fullborder");
add("ZNR/Boulderloft Pathway2.fullborder");
add("ZNR/Branchloft Pathway2.fullborder");
add("ZNR/Brightclimb Pathway2.fullborder");
add("ZNR/Clearwater Pathway2.fullborder");
add("ZNR/Cragcrown Pathway2.fullborder");
add("ZNR/Grimclimb Pathway2.fullborder");
add("ZNR/Jace, Mirror Mage2.fullborder");
add("ZNR/Lavaglide Pathway2.fullborder");
add("ZNR/Murkwater Pathway2.fullborder");
add("ZNR/Nahiri, Heir of the Ancients2.fullborder");
add("ZNR/Needleverge Pathway2.fullborder");
add("ZNR/Nissa of Shadowed Boughs2.fullborder");
add("ZNR/Pillarverge Pathway2.fullborder");
add("ZNR/Riverglide Pathway2.fullborder");
add("ZNR/Timbercrown Pathway2.fullborder");
}
};
Texture n; Texture n;
@Override @Override
public Texture load(String key) { public Texture load(String key) {
boolean extendedArt = false; boolean extendedArt = isBorderless(key);
boolean textureFilter = Forge.isTextureFilteringEnabled(); boolean textureFilter = Forge.isTextureFilteringEnabled();
if (key.length() > 4){
if ((key.substring(0,4).contains("MPS_"))) //MPS_ sets
extendedArt = true;
else if ((key.substring(0,3).contains("UST"))) //Unstable Set
extendedArt = true;
}
File file = ImageKeys.getImageFile(key); File file = ImageKeys.getImageFile(key);
if (file != null) { if (file != null) {
FileHandle fh = new FileHandle(file); FileHandle fh = new FileHandle(file);
try { try {
Texture t = new Texture(fh, textureFilter); Texture t = new Texture(fh, textureFilter);
//update
ImageCache.updateBorders(t, extendedArt ? false: isCloserToWhite(getpixelColor(t)));
if (textureFilter) if (textureFilter)
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear); t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
if (extendedArt) if (extendedArt)
@@ -106,4 +238,44 @@ final class ImageLoader extends CacheLoader<String, Texture> {
} }
} }
} }
public boolean isBorderless(String imagekey) {
if (imagekey.length() > 7) {
if ((!imagekey.substring(0, 7).contains("MPS_KLD"))&&(imagekey.substring(0, 4).contains("MPS_"))) //MPS_ sets except MPD_KLD
return true;
}
return borderlessCardlistKey.contains(TextUtil.fastReplace(imagekey,".full",".fullborder"));
}
public static boolean isBorderless(Texture t) {
//generated texture/pixmap?
if (t.toString().contains("com.badlogic.gdx.graphics.Texture@"))
return true;
for (String key : borderlessCardlistKey) {
if (t.toString().contains(key))
return true;
}
return false;
}
public static String getpixelColor(Texture i) {
if (!i.getTextureData().isPrepared()) {
i.getTextureData().prepare(); //prepare texture
}
//get pixmap from texture data
Pixmap pixmap = i.getTextureData().consumePixmap();
//get pixel color from x,y texture coordinate based on the image fullborder or not
Color color = new Color(pixmap.getPixel(croppedBorderImage(i).getRegionX()+1, croppedBorderImage(i).getRegionY()+1));
pixmap.dispose();
return color.toString();
}
public static boolean isCloserToWhite(String c){
if (c == null || c == "")
return false;
int c_r = Integer.parseInt(c.substring(0,2),16);
int c_g = Integer.parseInt(c.substring(2,4),16);
int c_b = Integer.parseInt(c.substring(4,6),16);
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
return brightness > 155;
}
} }

View File

@@ -49,13 +49,12 @@ public class CardImage implements FImage {
} }
else { else {
if (Forge.enableUIMask) { if (Forge.enableUIMask) {
boolean fullborder = image.toString().contains(".fullborder."); if (ImageCache.isBorderlessCardArt(image))
if (ImageCache.isExtendedArt(card))
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
else { else {
float radius = (h - w)/8; float radius = (h - w)/8;
g.drawfillBorder(3, ImageCache.borderColor(card), x, y, w, h, radius); g.drawfillBorder(3, ImageCache.borderColor(card), x, y, w, h, radius);
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.2f, y+radius/2, w*0.96f, h*0.96f); g.drawImage(ImageCache.croppedBorderImage(image), x+radius/2.2f, y+radius/2, w*0.96f, h*0.96f);
} }
} }
else else

View File

@@ -348,7 +348,6 @@ public class CardImageRenderer {
if (image == ImageCache.defaultImage) { //support drawing card image manually if card image not found if (image == ImageCache.defaultImage) { //support drawing card image manually if card image not found
drawCardImage(g, card, altState, x, y, w, h, CardStackPosition.Top); drawCardImage(g, card, altState, x, y, w, h, CardStackPosition.Top);
} else { } else {
boolean fullborder = image.toString().contains(".fullborder.");
float radius = (h - w)/8; float radius = (h - w)/8;
float wh_Adj = ForgeConstants.isGdxPortLandscape && isCurrentCard ? 1.38f:1.0f; float wh_Adj = ForgeConstants.isGdxPortLandscape && isCurrentCard ? 1.38f:1.0f;
float new_w = w*wh_Adj; float new_w = w*wh_Adj;
@@ -367,32 +366,32 @@ public class CardImageRenderer {
} }
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) { if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane())) {
if (Forge.enableUIMask){ if (Forge.enableUIMask){
if (ImageCache.isExtendedArt(card)) if (ImageCache.isBorderlessCardArt(image))
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
else { else {
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x+radius/2-minusxy, new_y+radius/2-minusxy, new_w*croppedArea, new_h*croppedArea, (new_x+radius/2-minusxy) + (new_w*croppedArea) / 2, (new_y+radius/2-minusxy) + (new_h*croppedArea) / 2, -90); g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x+radius/2-minusxy, new_y+radius/2-minusxy, new_w*croppedArea, new_h*croppedArea, (new_x+radius/2-minusxy) + (new_w*croppedArea) / 2, (new_y+radius/2-minusxy) + (new_h*croppedArea) / 2, -90);
} }
} else } else
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow) { } else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow) {
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath"); boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
if (Forge.enableUIMask) { if (Forge.enableUIMask) {
if (ImageCache.isExtendedArt(card)) if (ImageCache.isBorderlessCardArt(image))
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
else { else {
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), new_x + radius / 2-minusxy, new_y + radius / 2-minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2-minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2-minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90); g.drawRotatedImage(ImageCache.croppedBorderImage(image), new_x + radius / 2-minusxy, new_y + radius / 2-minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2-minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2-minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90);
} }
} else } else
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
} else { } else {
if (Forge.enableUIMask && canshow) { if (Forge.enableUIMask && canshow) {
if (ImageCache.isExtendedArt(card)) if (ImageCache.isBorderlessCardArt(image))
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
else { else {
g.drawImage(ImageCache.getBorderImage(card, canshow), x, y, w, h); g.drawImage(ImageCache.getBorderImage(image, canshow), x, y, w, h);
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea); g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
} }
} else { } else {
if (canshow) if (canshow)

View File

@@ -459,13 +459,12 @@ public class CardRenderer {
if (image == ImageCache.defaultImage) { if (image == ImageCache.defaultImage) {
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos); CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
} else { } else {
boolean fullborder = image.toString().contains(".fullborder.");
if (Forge.enableUIMask) { if (Forge.enableUIMask) {
if (ImageCache.isExtendedArt(pc)) if (ImageCache.isBorderlessCardArt(image))
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
else { else {
g.drawImage(ImageCache.getBorderImage(pc), x, y, w, h); g.drawImage(ImageCache.getBorderImage(image), x, y, w, h);
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea); g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
} }
} else } else
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
@@ -499,26 +498,25 @@ public class CardRenderer {
if (image == ImageCache.defaultImage) { if (image == ImageCache.defaultImage) {
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos); CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
} else { } else {
boolean fullborder = image.toString().contains(".fullborder.");
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON) if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON)
&& (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane()) && rotate){ && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane()) && rotate){
if (Forge.enableUIMask) { if (Forge.enableUIMask) {
if (ImageCache.isExtendedArt(card)) if (ImageCache.isBorderlessCardArt(image))
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90); g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
else { else {
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90); g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
g.drawRotatedImage(ImageCache.croppedBorderImage(image, fullborder), x+radius/2.3f-minusxy, y+radius/2-minusxy, w*croppedArea, h*croppedArea, (x+radius/2.3f-minusxy) + (w*croppedArea) / 2, (y+radius/2-minusxy) + (h*croppedArea) / 2, -90); g.drawRotatedImage(ImageCache.croppedBorderImage(image), x+radius/2.3f-minusxy, y+radius/2-minusxy, w*croppedArea, h*croppedArea, (x+radius/2.3f-minusxy) + (w*croppedArea) / 2, (y+radius/2-minusxy) + (h*croppedArea) / 2, -90);
} }
} else } else
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90); g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
} else { } else {
if (Forge.enableUIMask && canshow) { if (Forge.enableUIMask && canshow) {
if (ImageCache.isExtendedArt(card)) if (ImageCache.isBorderlessCardArt(image))
g.drawImage(image, x, y, w, h); g.drawImage(image, x, y, w, h);
else { else {
boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors(); boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
g.drawBorderImage(ImageCache.getBorderImage(card, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors g.drawBorderImage(ImageCache.getBorderImage(image, canshow), ImageCache.getTint(card), x, y, w, h, t); //tint check for changed colors
g.drawImage(ImageCache.croppedBorderImage(image, fullborder), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea); g.drawImage(ImageCache.croppedBorderImage(image), x + radius / 2.4f-minusxy, y + radius / 2-minusxy, w * croppedArea, h * croppedArea);
} }
} else { } else {
if (canshow) if (canshow)

View File

@@ -90,3 +90,4 @@ Core Set 2021, 3/6/M21, M21
Jumpstart, -/2/JMP, Meta-Choose(S(JMP Above the Clouds 1)Above the Clouds 1;S(JMP Above the Clouds 2)Above the Clouds 2;S(JMP Above the Clouds 3)Above the Clouds 3;S(JMP Above the Clouds 4)Above the Clouds 4;S(JMP Angels 1)Angels 1;S(JMP Angels 2)Angels 2;S(JMP Archaeology 1)Archaeology 1;S(JMP Archaeology 2)Archaeology 2;S(JMP Archaeology 3)Archaeology 3;S(JMP Archaeology 4)Archaeology 4;;S(JMP Basri)Basri;S(JMP Cats 1)Cats 1;S(JMP Cats 2)Cats 2;S(JMP Chandra)Chandra;S(JMP Devilish 1)Devilish 1;S(JMP Devilish 2)Devilish 2;S(JMP Devilish 3)Devilish 3;S(JMP Devilish 4)Devilish 4;S(JMP Dinosaurs 1)Dinosaurs 1;S(JMP Dinosaurs 2)Dinosaurs 2;S(JMP Dinosaurs 3)Dinosaurs 3;S(JMP Dinosaurs 4)Dinosaurs 4;S(JMP Discarding 1)Discarding 1;S(JMP Discarding 2)Discarding 2;S(JMP Doctor 1)Doctor 1;S(JMP Doctor 2)Doctor 2;S(JMP Doctor 3)Doctor 3;S(JMP Doctor 4)Doctor 4;S(JMP Dogs 1)Dogs 1;S(JMP Dogs 2)Dogs 2;S(JMP Dragons 1)Dragons 1;S(JMP Dragons 2)Dragons 2;S(JMP Elves 1)Elves 1;S(JMP Elves 2)Elves 2;S(JMP Enchanted 1)Enchanted 1;S(JMP Enchanted 2)Enchanted 2;S(JMP Feathered Friends 1)Feathered Friends 1;S(JMP Feathered Friends 2)Feathered Friends 2;S(JMP Feathered Friends 3)Feathered Friends 3;S(JMP Feathered Friends 4)Feathered Friends 4;S(JMP Garruk)Garruk;S(JMP Goblins 1)Goblins 1;S(JMP Goblins 2)Goblins 2;S(JMP Goblins 3)Goblins 3;S(JMP Goblins 4)Goblins 4;S(JMP Heavily Armored 1)Heavily Armored 1;S(JMP Heavily Armored 2)Heavily Armored 2;S(JMP Heavily Armored 3)Heavily Armored 3;S(JMP Heavily Armored 4)Heavily Armored 4;S(JMP Lands 1)Lands 1;S(JMP Lands 2)Lands 2;S(JMP Legion 1)Legion 1;S(JMP Legion 2)Legion 2;S(JMP Legion 3)Legion 3;S(JMP Legion 4)Legion 4;S(JMP Lightning 1)Lightning 1;S(JMP Lightning 2)Lightning 2;S(JMP Liliana)Liliana;S(JMP Milling)Milling;S(JMP Minions 1)Minions 1;S(JMP Minions 2)Minions 2;S(JMP Minions 3)Minions 3;S(JMP Minions 4)Minions 4;S(JMP Minotaurs 1)Minotaurs 1;S(JMP Minotaurs 2)Minotaurs 2;S(JMP Phyrexian)Phyrexian;S(JMP Pirates 1)Pirates 1;S(JMP Pirates 2)Pirates 2;S(JMP Plus One 1)Plus One 1;S(JMP Plus One 2)Plus One 2;S(JMP Plus One 3)Plus One 3;S(JMP Plus One 4)Plus One 4;S(JMP Predatory 1)Predatory 1;S(JMP Predatory 2)Predatory 2;S(JMP Predatory 3)Predatory 3;S(JMP Predatory 4)Predatory 4;S(JMP Rainbow)Rainbow;S(JMP Reanimated 1)Reanimated 1;S(JMP Reanimated 2)Reanimated 2;S(JMP Reanimated 3)Reanimated 3;S(JMP Reanimated 4)Reanimated 4;S(JMP Rogues 1)Rogues 1;S(JMP Rogues 2)Rogues 2;S(JMP Seismic)Seismic;S(JMP Smashing 1)Smashing 1;S(JMP Smashing 2)Smashing 2;S(JMP Smashing 3)Smashing 3;S(JMP Smashing 4)Smashing 4;S(JMP Spellcasting 1)Spellcasting 1;S(JMP Spellcasting 2)Spellcasting 2;S(JMP Spellcasting 3)Spellcasting 3;S(JMP Spellcasting 4)Spellcasting 4;S(JMP Spirits 1)Spirits 1;S(JMP Spirits 2)Spirits 2;S(JMP Spooky 1)Spooky 1;S(JMP Spooky 2)Spooky 2;S(JMP Spooky 3)Spooky 3;S(JMP Spooky 4)Spooky 4;S(JMP Teferi)Teferi;S(JMP Tree Hugging 1)Tree Hugging 1;S(JMP Tree Hugging 2)Tree Hugging 2;S(JMP Tree Hugging 3)Tree Hugging 3;S(JMP Tree Hugging 4)Tree Hugging 4;S(JMP Under the Sea 1)Under the Sea 1;S(JMP Under the Sea 2)Under the Sea 2;S(JMP Unicorns)Unicorns;S(JMP Vampires 1)Vampires 1;S(JMP Vampires 2)Vampires 2;S(JMP Vampires 3)Vampires 3;S(JMP Vampires 4)Vampires 4;S(JMP Walls)Walls;S(JMP Well-Read 1)Well-Read 1;S(JMP Well-Read 2)Well-Read 2;S(JMP Well-Read 3)Well-Read 3;S(JMP Well-Read 4)Well-Read 4;S(JMP Witchcraft 1)Witchcraft 1;S(JMP Witchcraft 2)Witchcraft 2;S(JMP Wizards 1)Wizards 1;S(JMP Wizards 2)Wizards 2;S(JMP Wizards 3)Wizards 3;S(JMP Wizards 4)Wizards 4)Themes Jumpstart, -/2/JMP, Meta-Choose(S(JMP Above the Clouds 1)Above the Clouds 1;S(JMP Above the Clouds 2)Above the Clouds 2;S(JMP Above the Clouds 3)Above the Clouds 3;S(JMP Above the Clouds 4)Above the Clouds 4;S(JMP Angels 1)Angels 1;S(JMP Angels 2)Angels 2;S(JMP Archaeology 1)Archaeology 1;S(JMP Archaeology 2)Archaeology 2;S(JMP Archaeology 3)Archaeology 3;S(JMP Archaeology 4)Archaeology 4;;S(JMP Basri)Basri;S(JMP Cats 1)Cats 1;S(JMP Cats 2)Cats 2;S(JMP Chandra)Chandra;S(JMP Devilish 1)Devilish 1;S(JMP Devilish 2)Devilish 2;S(JMP Devilish 3)Devilish 3;S(JMP Devilish 4)Devilish 4;S(JMP Dinosaurs 1)Dinosaurs 1;S(JMP Dinosaurs 2)Dinosaurs 2;S(JMP Dinosaurs 3)Dinosaurs 3;S(JMP Dinosaurs 4)Dinosaurs 4;S(JMP Discarding 1)Discarding 1;S(JMP Discarding 2)Discarding 2;S(JMP Doctor 1)Doctor 1;S(JMP Doctor 2)Doctor 2;S(JMP Doctor 3)Doctor 3;S(JMP Doctor 4)Doctor 4;S(JMP Dogs 1)Dogs 1;S(JMP Dogs 2)Dogs 2;S(JMP Dragons 1)Dragons 1;S(JMP Dragons 2)Dragons 2;S(JMP Elves 1)Elves 1;S(JMP Elves 2)Elves 2;S(JMP Enchanted 1)Enchanted 1;S(JMP Enchanted 2)Enchanted 2;S(JMP Feathered Friends 1)Feathered Friends 1;S(JMP Feathered Friends 2)Feathered Friends 2;S(JMP Feathered Friends 3)Feathered Friends 3;S(JMP Feathered Friends 4)Feathered Friends 4;S(JMP Garruk)Garruk;S(JMP Goblins 1)Goblins 1;S(JMP Goblins 2)Goblins 2;S(JMP Goblins 3)Goblins 3;S(JMP Goblins 4)Goblins 4;S(JMP Heavily Armored 1)Heavily Armored 1;S(JMP Heavily Armored 2)Heavily Armored 2;S(JMP Heavily Armored 3)Heavily Armored 3;S(JMP Heavily Armored 4)Heavily Armored 4;S(JMP Lands 1)Lands 1;S(JMP Lands 2)Lands 2;S(JMP Legion 1)Legion 1;S(JMP Legion 2)Legion 2;S(JMP Legion 3)Legion 3;S(JMP Legion 4)Legion 4;S(JMP Lightning 1)Lightning 1;S(JMP Lightning 2)Lightning 2;S(JMP Liliana)Liliana;S(JMP Milling)Milling;S(JMP Minions 1)Minions 1;S(JMP Minions 2)Minions 2;S(JMP Minions 3)Minions 3;S(JMP Minions 4)Minions 4;S(JMP Minotaurs 1)Minotaurs 1;S(JMP Minotaurs 2)Minotaurs 2;S(JMP Phyrexian)Phyrexian;S(JMP Pirates 1)Pirates 1;S(JMP Pirates 2)Pirates 2;S(JMP Plus One 1)Plus One 1;S(JMP Plus One 2)Plus One 2;S(JMP Plus One 3)Plus One 3;S(JMP Plus One 4)Plus One 4;S(JMP Predatory 1)Predatory 1;S(JMP Predatory 2)Predatory 2;S(JMP Predatory 3)Predatory 3;S(JMP Predatory 4)Predatory 4;S(JMP Rainbow)Rainbow;S(JMP Reanimated 1)Reanimated 1;S(JMP Reanimated 2)Reanimated 2;S(JMP Reanimated 3)Reanimated 3;S(JMP Reanimated 4)Reanimated 4;S(JMP Rogues 1)Rogues 1;S(JMP Rogues 2)Rogues 2;S(JMP Seismic)Seismic;S(JMP Smashing 1)Smashing 1;S(JMP Smashing 2)Smashing 2;S(JMP Smashing 3)Smashing 3;S(JMP Smashing 4)Smashing 4;S(JMP Spellcasting 1)Spellcasting 1;S(JMP Spellcasting 2)Spellcasting 2;S(JMP Spellcasting 3)Spellcasting 3;S(JMP Spellcasting 4)Spellcasting 4;S(JMP Spirits 1)Spirits 1;S(JMP Spirits 2)Spirits 2;S(JMP Spooky 1)Spooky 1;S(JMP Spooky 2)Spooky 2;S(JMP Spooky 3)Spooky 3;S(JMP Spooky 4)Spooky 4;S(JMP Teferi)Teferi;S(JMP Tree Hugging 1)Tree Hugging 1;S(JMP Tree Hugging 2)Tree Hugging 2;S(JMP Tree Hugging 3)Tree Hugging 3;S(JMP Tree Hugging 4)Tree Hugging 4;S(JMP Under the Sea 1)Under the Sea 1;S(JMP Under the Sea 2)Under the Sea 2;S(JMP Unicorns)Unicorns;S(JMP Vampires 1)Vampires 1;S(JMP Vampires 2)Vampires 2;S(JMP Vampires 3)Vampires 3;S(JMP Vampires 4)Vampires 4;S(JMP Walls)Walls;S(JMP Well-Read 1)Well-Read 1;S(JMP Well-Read 2)Well-Read 2;S(JMP Well-Read 3)Well-Read 3;S(JMP Well-Read 4)Well-Read 4;S(JMP Witchcraft 1)Witchcraft 1;S(JMP Witchcraft 2)Witchcraft 2;S(JMP Wizards 1)Wizards 1;S(JMP Wizards 2)Wizards 2;S(JMP Wizards 3)Wizards 3;S(JMP Wizards 4)Wizards 4)Themes
Amonkhet Remastered, 3/6/AKR, AKR Amonkhet Remastered, 3/6/AKR, AKR
Double Masters, 3/6/2XM, 2XM Double Masters, 3/6/2XM, 2XM
Zendikar Rising, 3/6/ZNR, ZNR

View File

@@ -114,3 +114,4 @@ THB: 36 Boosters
MB1: 24 Boosters MB1: 24 Boosters
IKO: 36 Boosters IKO: 36 Boosters
M21: 36 Boosters M21: 36 Boosters
ZNR: 36 Boosters

View File

@@ -80,3 +80,5 @@ IKO: 10 Boosters, 40 BasicLands
#needs to be 20 BasicLands, 20 Foil BasicLands, 1 Colossification+|IKO|3 #needs to be 20 BasicLands, 20 Foil BasicLands, 1 Colossification+|IKO|3
M21: 10 Boosters, 40 BasicLands M21: 10 Boosters, 40 BasicLands
#needs to be 20 BasicLands, 20 Foil BasicLands, 1 Pack Leader+|M21|3 #needs to be 20 BasicLands, 20 Foil BasicLands, 1 Pack Leader+|M21|3
ZNR: 10 Boosters, 40 BasicLands
#needs to be 20 BasicLands, 20 Foil BasicLands, 1 Charix, the Raging Isle+|ZNR|3

View File

@@ -0,0 +1,16 @@
Name:Akoum Warrior
ManaCost:5 R
Types:Creature Minotaur Warrior
PT:4/5
K:Trample
AlternateMode:Modal
Oracle:Trample
ALTERNATE
Name:Akoum Teeth
ManaCost:no cost
Types:Land
K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}.
Oracle:Akoum Teeth enters the battlefield tapped.\n{T}: Add {R}.

View File

@@ -0,0 +1,8 @@
Name:Ancient Greenwarden
ManaCost:4 G G
Types:Creature Elemental
PT:5/7
K:Reach
S:Mode$ Continuous | Affected$ Land.YouCtrl | MayPlay$ True | AffectedZone$ Graveyard | Description$ You may play lands from your graveyard.
K:Panharmonicon:Land:If a land entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
Oracle:Reach\nYou may play lands from your graveyard.\nIf a land entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.

View File

@@ -0,0 +1,13 @@
Name:Anowon, the Ruin Thief
ManaCost:2 U B
Types:Legendary Creature Vampire Rogue
PT:2/4
S:Mode$ Continuous | Affected$ Rogue.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Rogues you control get +1/+1.
T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Rogue.YouCtrl | ValidTarget$ Player | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ Whenever one or more Rogues you control deal combat damage to a player, that player mills a card for each 1 damage dealt to them. If the player mills at least one creature card this way, you draw a card. (To mill a card, a player puts the top card of their library into their graveyard.)
SVar:TrigMill:DB$ Mill | Defined$ TriggeredTarget | NumCards$ X | References$ X | RememberMilled$ True | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Creature | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:TriggerCount$DamageAmount
DeckNeeds:Type$Rogue
DeckHas:Ability$Mill
Oracle:Other Rogues you control get +1/+1.\nWhenever one or more Rogues you control deal combat damage to a player, that player mills a card for each 1 damage dealt to them. If the player mills at least one creature card this way, you draw a card. (To mill a card, a player puts the top card of their library into their graveyard.)

View File

@@ -0,0 +1,12 @@
Name:Ashaya, Soul of the Wild
ManaCost:3 G G
Types:Legendary Creature Elemental
PT:*/*
S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of lands you control.
S:Mode$ Continuous | Affected$ Creature.nonToken+YouCtrl | AddType$ Forest & Land | Description$ Nontoken creatures you control are Forest lands in addition to their other types. (They're still affected by summoning sickness.)
SVar:X:Count$Valid Land.YouCtrl
SVar:Y:Count$Valid Creature.nonToken+YouCtrl
SVar:Z:SVar$X/Plus.Y
SVar:NeedsToPlayVar:Z GE4
SVar:BuffedBy:Land,Creature
Oracle:Ashaya, Soul of the Wild's power and toughness are each equal to the number of lands you control.\nNontoken creatures you control are Forest lands in addition to their other types. (They're still affected by summoning sickness.)

View File

@@ -3,7 +3,6 @@ ManaCost:2 W W
Types:Creature Human Wizard Types:Creature Human Wizard
PT:2/2 PT:2/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Detain | TriggerDescription$ When CARDNAME enters the battlefield, detain up to two target creatures your opponents control. (Until your next turn, those creatures can't attack or block and their activated abilities can't be activated.) T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Detain | TriggerDescription$ When CARDNAME enters the battlefield, detain up to two target creatures your opponents control. (Until your next turn, those creatures can't attack or block and their activated abilities can't be activated.)
SVar:Detain:DB$ Pump | TargetMin$ 0 | TargetMax$ 2 | KW$ HIDDEN CARDNAME can't attack or block. & HIDDEN CARDNAME's activated abilities can't be activated. | IsCurse$ True | UntilYourNextTurn$ True | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature your opponent controls to detain. SVar:Detain:DB$ Pump | TargetMin$ 0 | TargetMax$ 2 | KW$ HIDDEN CARDNAME can't attack or block. & HIDDEN CARDNAME's activated abilities can't be activated. | IsCurse$ True | UntilYourNextTurn$ True | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature your opponent controls to detain. | AILogic$ DetainNonLand
SVar:PlayMain1:TRUE SVar:PlayMain1:TRUE
SVar:Picture:http://www.wizards.com/global/images/magic/general/azorius_justiciar.jpg
Oracle:When Azorius Justiciar enters the battlefield, detain up to two target creatures your opponents control. (Until your next turn, those creatures can't attack or block and their activated abilities can't be activated.) Oracle:When Azorius Justiciar enters the battlefield, detain up to two target creatures your opponents control. (Until your next turn, those creatures can't attack or block and their activated abilities can't be activated.)

View File

@@ -0,0 +1,16 @@
Name:Bala Ged Recovery
ManaCost:2 G
Types:Sorcery
A:SP$ ChangeZone | Cost$ 2 G | ValidTgts$ Card.YouOwn | TgtPrompt$ Select target card | Origin$ Graveyard | Destination$ Hand | SpellDescription$ Return target card from your graveyard to your hand.
DeckHas:Ability$Graveyard
AlternateMode:Modal
Oracle:Return target card from your graveyard to your hand.
ALTERNATE
Name:Bala Ged Sanctuary
ManaCost:no cost
Types:Land
K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}.
Oracle:Bala Ged Sanctuary enters the battlefield tapped.\n{T}: Add {G}.

View File

@@ -0,0 +1,9 @@
Name:Base Camp
ManaCost:no cost
Types:Land
K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ Mana | Cost$ T | Produced$ Any | RestrictValid$ Cleric,Rogue,Warrior,Wizard | SpellDescription$ Add one mana of any color. Spend this mana only to cast a Cleric, Rogue, Warrior, or Wizard spell or to activate an ability of a Cleric, Rogue, Warrior, or Wizard.
DeckHas:Ability$Party
DeckHints:Type$Cleric|Rogue|Warrior|Wizard
Oracle:Base Camp enters the battlefield tapped.\n{T}: Add {C}.\n{T}: Add one mana of any color. Spend this mana only to cast a Cleric, Rogue, Warrior, or Wizard spell or to activate an ability of a Cleric, Rogue, Warrior, or Wizard.

View File

@@ -6,7 +6,7 @@ K:Vigilance
K:Protection from multicolored K:Protection from multicolored
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on target creature you control. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on target creature you control.
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+counters_GE1_P1P1 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ Whenever CARDNAME or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+counters_GE1_P1P1 | Execute$ TrigToken | TriggerController$ TriggeredCardController | TriggerDescription$ Whenever CARDNAME or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.Other+YouCtrl+counters_GE1_P1P1 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerController$ TriggeredCardController | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.Other+YouCtrl+counters_GE1_P1P1 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerController$ TriggeredCardController | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_2_2_knight_vigilance | TokenOwner$ You SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_2_2_knight_vigilance | TokenOwner$ You
DeckHas:Ability$Counters & Ability$Token DeckHas:Ability$Counters & Ability$Token

View File

@@ -6,7 +6,7 @@ K:Menace
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 3 | CheckSVar$ X | SVarCompare$ GE8 | Description$ CARDNAME gets +3/+0 as long as an opponent has eight or more cards in their graveyard. S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 3 | CheckSVar$ X | SVarCompare$ GE8 | Description$ CARDNAME gets +3/+0 as long as an opponent has eight or more cards in their graveyard.
SVar:X:PlayerCountOpponents$HighestCardsInGraveyard SVar:X:PlayerCountOpponents$HighestCardsInGraveyard
AlternateMode:Modal AlternateMode:Modal
Oracle:Blackbloom Rogue gets +3/+0 as long as an opponent has eight or more cards in their graveyard. Oracle:Menace\nBlackbloom Rogue gets +3/+0 as long as an opponent has eight or more cards in their graveyard.
ALTERNATE ALTERNATE

View File

@@ -1,8 +1,7 @@
Name:Cellar Door Name:Cellar Door
ManaCost:2 ManaCost:2
Types:Artifact Types:Artifact
A:AB$ Mill | Cost$ 3 T | NumCards$ 1 | ValidTgts$ Player | TgtPrompt$ Choose a player | FromBottom$ True | RememberMilled$ True | SubAbility$ DBToken | SpellDescription$ Target player puts the bottom card of their library into their graveyard. If it's a creature card, you create a 2/2 black Zombie creature token. A:AB$ Mill | Cost$ 3 T | NumCards$ 1 | ValidTgts$ Player | TgtPrompt$ Choose a player | FromBottom$ True | RememberMilled$ True | SubAbility$ DBToken | StackDescription$ {p:Targeted} puts the bottom card of their library into their graveyard. | SpellDescription$ Target player puts the bottom card of their library into their graveyard. If it's a creature card, you create a 2/2 black Zombie creature token.
SVar:DBToken:DB$ Token | LegacyImage$ b 2 2 zombie isd | TokenScript$ b_2_2_zombie | TokenOwner$ You | TokenAmount$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Creature | ConditionCompare$ EQ1 | SubAbility$ DBCleanup SVar:DBToken:DB$ Token | TokenScript$ b_2_2_zombie | TokenOwner$ You | TokenAmount$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Creature | ConditionCompare$ EQ1 | SubAbility$ DBCleanup | StackDescription$ If it's a creature card, {p:You} creates a 2/2 black Zombie creature token. | SpellDescription$ If it's a creature card, you create a 2/2 black Zombie creature token.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/cellar_door.jpg
Oracle:{3}, {T}: Target player puts the bottom card of their library into their graveyard. If it's a creature card, create a 2/2 black Zombie creature token. Oracle:{3}, {T}: Target player puts the bottom card of their library into their graveyard. If it's a creature card, create a 2/2 black Zombie creature token.

View File

@@ -0,0 +1,9 @@
Name:Cliffhaven Kitesail
ManaCost:1
Types:Artifact Equipment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAttach | TriggerDescription$ When CARDNAME enters the battlefield, attach it to target creature you control.
SVar:TrigAttach:DB$ Attach | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control
SVar:NeedsToPlay:Creature.YouCtrl+inZoneBattlefield
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddKeyword$ Flying | Description$ Equipped creature has flying.
K:Equip:2
Oracle:When Cliffhaven Kitesail enters the battlefield, attach it to target creature you control.\nEquipped creature has flying.\nEquip {2} ({2}: Attach to target creature you control. Equip only as a sorcery.)

View File

@@ -0,0 +1,10 @@
Name:Enigma Thief
ManaCost:5 U U
Types:Creature Sphinx Rogue
PT:5/5
K:Prowl:3 U
K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ ReturnOneEach | TriggerDescription$ When CARDNAME enters the battlefield, for each opponent, return up to one target nonland permanent that player controls to its owners hand.
SVar:ReturnOneEach:DB$ ChangeZone | Origin$ Battlefield | Destination$ Hand | ValidTgts$ Permanent.nonLand+OppCtrl | TargetMin$ 0 | TargetMax$ OneEach | References$ OneEach | TargetsWithDifferentControllers$ True | TgtPrompt$ Select up to one target nonland permanent each opponent controls
SVar:OneEach:PlayerCountOpponents$Amount
Oracle:Prowl {3}{U} (You may cast this spell for its prowl cost if you dealt combat damage to a player this turn with a Sphinx or a Rogue.)\nFlying\nWhen Enigma Thief enters the battlefield, for each opponent, return up to one target nonland permanent that player controls to its owners hand.

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