Compare commits

..

1 Commits

Author SHA1 Message Date
Hans Mackowiak
71676a2781 CantTarget: return the StaticAbility 2025-09-07 10:03:53 +02:00
511 changed files with 1287 additions and 2768 deletions

View File

@@ -442,9 +442,11 @@ public class CountersPutAi extends CountersAi {
}
sa.addDividedAllocation(c, amount);
return decision;
} else if (!hasSacCost) {
// for Sacrifice costs, evaluate further to see if it's worth using the ability before the card dies
return decision;
} else {
if (!hasSacCost) {
// for Sacrifice costs, evaluate further to see if it's worth using the ability before the card dies
return decision;
}
}
}
}
@@ -682,12 +684,14 @@ public class CountersPutAi extends CountersAi {
|| (sa.getRootAbility().isTrigger() && !sa.getRootAbility().isOptionalTrigger());
if (sa.usesTargeting()) {
CardCollection list;
CardCollection list = null;
if (sa.isCurse()) {
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
} else {
list = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
}
list = CardLists.getTargetableCards(list, sa);
if (list.isEmpty() && isMandatoryTrigger) {
@@ -703,8 +707,9 @@ public class CountersPutAi extends CountersAi {
|| sa.getTargets().isEmpty()) {
sa.resetTargets();
return new AiAbilityDecision(0, AiPlayDecision.TargetingFailed);
} else {
break;
}
break;
}
if (sa.isCurse()) {
@@ -747,6 +752,8 @@ public class CountersPutAi extends CountersAi {
final SpellAbility root = sa.getRootAbility();
final Card source = sa.getHostCard();
final String aiLogic = sa.getParamOrDefault("AILogic", "");
boolean preferred = true;
CardCollection list;
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
final boolean divided = sa.isDividedAsYouChoose();
final int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
@@ -768,11 +775,11 @@ public class CountersPutAi extends CountersAi {
AiAbilityDecision decision = doChargeToCMCLogic(ai, sa);
if (decision.willingToPlay()) {
return decision;
}
if (mandatory) {
} else if (mandatory) {
return new AiAbilityDecision(50, AiPlayDecision.MandatoryPlay);
} else {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
if (!sa.usesTargeting()) {
@@ -823,19 +830,19 @@ public class CountersPutAi extends CountersAi {
}
}
sa.resetTargets();
Iterable<Card> filteredField;
if (sa.isCurse()) {
filteredField = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
} else {
filteredField = ai.getCardsIn(ZoneType.Battlefield);
list = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
}
CardCollection list = CardLists.getTargetableCards(filteredField, sa);
list = ComputerUtil.filterAITgts(sa, ai, list, false);
int totalTargets = list.size();
boolean preferred = true;
list = CardLists.getTargetableCards(list, sa);
// Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, list, false);
int totalTargets = list.size();
sa.resetTargets();
while (sa.canAddMoreTarget()) {
if (mandatory) {
// When things are mandatory, gotta handle a little differently
@@ -872,21 +879,27 @@ public class CountersPutAi extends CountersAi {
if (choice == null && mandatory) {
choice = Aggregates.random(list);
}
} else if (type.equals("M1M1")) {
choice = ComputerUtilCard.getWorstCreatureAI(list);
} else {
choice = Aggregates.random(list);
if (type.equals("M1M1")) {
choice = ComputerUtilCard.getWorstCreatureAI(list);
} else {
choice = Aggregates.random(list);
}
}
} else if (preferred) {
list = ComputerUtil.getSafeTargets(ai, sa, list);
choice = chooseBoonTarget(list, type);
if (choice == null && mandatory) {
choice = Aggregates.random(list);
}
} else if (type.equals("P1P1")) {
choice = ComputerUtilCard.getWorstCreatureAI(list);
} else {
choice = Aggregates.random(list);
if (preferred) {
list = ComputerUtil.getSafeTargets(ai, sa, list);
choice = chooseBoonTarget(list, type);
if (choice == null && mandatory) {
choice = Aggregates.random(list);
}
} else {
if (type.equals("P1P1")) {
choice = ComputerUtilCard.getWorstCreatureAI(list);
} else {
choice = Aggregates.random(list);
}
}
}
if (choice != null && divided) {
int alloc = Math.max(amount / totalTargets, 1);
@@ -1081,7 +1094,8 @@ public class CountersPutAi extends CountersAi {
Player ai = sa.getActivatingPlayer();
GameEntity e = (GameEntity) params.get("Target");
// for Card try to select not useless counter
if (e instanceof Card c) {
if (e instanceof Card) {
Card c = (Card) e;
if (c.getController().isOpponentOf(ai)) {
if (options.contains(CounterEnumType.M1M1) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterEnumType.M1M1;
@@ -1098,7 +1112,8 @@ public class CountersPutAi extends CountersAi {
}
}
}
} else if (e instanceof Player p) {
} else if (e instanceof Player) {
Player p = (Player) e;
if (p.isOpponentOf(ai)) {
if (options.contains(CounterEnumType.POISON)) {
return CounterEnumType.POISON;
@@ -1232,8 +1247,9 @@ public class CountersPutAi extends CountersAi {
if (numCtrs < optimalCMC) {
// If the AI has less counters than the optimal CMC, it should play the ability.
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
} else {
// If the AI has enough counters or more than the optimal CMC, it should not play the ability.
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
// If the AI has enough counters or more than the optimal CMC, it should not play the ability.
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
}

View File

@@ -878,7 +878,7 @@ public class StaticData {
}
}
}
// stream().toList() causes crash on Android 8-13, use Collectors.toList()
// stream().toList() causes crash on Android, use Collectors.toList()
List<String> NIF = new ArrayList<>(NIF_Q).stream().sorted().collect(Collectors.toList());
List<String> CNI = new ArrayList<>(CNI_Q).stream().sorted().collect(Collectors.toList());
List<String> TOK = new ArrayList<>(TOKEN_Q).stream().sorted().collect(Collectors.toList());

View File

@@ -639,7 +639,7 @@ public final class CardEdition implements Comparable<CardEdition> {
* name - grouping #3
* artist name - grouping #5
*/
"(^(.?[0-9A-Z-]+\\S?[A-Z]*)\\s)?([^@]*)( @(.*))?$"
"(^(.?[0-9A-Z-]+\\S?[A-Z]*)\\s)?([^@]*)( @(.*))?$"
);
ListMultimap<String, EditionEntry> cardMap = ArrayListMultimap.create();
@@ -1018,13 +1018,16 @@ public final class CardEdition implements Comparable<CardEdition> {
public static final Predicate<CardEdition> HAS_BOOSTER_BOX = edition -> edition.getBoosterBoxCount() > 0;
@Deprecated //Use CardEdition::hasBasicLands and a nonnull test.
public static final Predicate<CardEdition> hasBasicLands = ed -> {
if (ed == null) {
// Happens for new sets with "???" code
return false;
}
return ed.hasBasicLands();
for(String landName : MagicColor.Constant.BASIC_LANDS) {
if (null == StaticData.instance().getCommonCards().getCard(landName, ed.getCode(), 0))
return false;
}
return true;
};
}
@@ -1045,7 +1048,7 @@ public final class CardEdition implements Comparable<CardEdition> {
public boolean hasBasicLands() {
for(String landName : MagicColor.Constant.BASIC_LANDS) {
if (this.getCardInSet(landName).isEmpty())
if (null == StaticData.instance().getCommonCards().getCard(landName, this.getCode(), 0))
return false;
}
return true;

View File

@@ -49,16 +49,6 @@ public class DeckRecognizer {
LIMITED_CARD,
CARD_FROM_NOT_ALLOWED_SET,
CARD_FROM_INVALID_SET,
/**
* Valid card request, but can't be imported because the player does not have enough copies.
* Should be replaced with a different printing if possible.
*/
CARD_NOT_IN_INVENTORY,
/**
* Valid card request for a card that isn't in the player's inventory, but new copies can be acquired freely.
* Usually used for basic lands. Should be supplied to the import controller by the editor.
*/
FREE_CARD_NOT_IN_INVENTORY,
// Warning messages
WARNING_MESSAGE,
UNKNOWN_CARD,
@@ -73,14 +63,10 @@ public class DeckRecognizer {
CARD_TYPE,
CARD_RARITY,
CARD_CMC,
MANA_COLOUR;
public static final EnumSet<TokenType> CARD_TOKEN_TYPES = EnumSet.of(LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET, CARD_FROM_INVALID_SET, CARD_NOT_IN_INVENTORY, FREE_CARD_NOT_IN_INVENTORY);
public static final EnumSet<TokenType> IN_DECK_TOKEN_TYPES = EnumSet.of(LEGAL_CARD, LIMITED_CARD, DECK_NAME, FREE_CARD_NOT_IN_INVENTORY);
public static final EnumSet<TokenType> CARD_PLACEHOLDER_TOKEN_TYPES = EnumSet.of(CARD_TYPE, CARD_RARITY, CARD_CMC, MANA_COLOUR);
MANA_COLOUR
}
public enum LimitedCardType {
public enum LimitedCardType{
BANNED,
RESTRICTED,
}
@@ -122,10 +108,6 @@ public class DeckRecognizer {
return new Token(TokenType.CARD_FROM_INVALID_SET, count, card, cardRequestHasSetCode);
}
public static Token NotInInventoryFree(final PaperCard card, final int count, final DeckSection section) {
return new Token(TokenType.FREE_CARD_NOT_IN_INVENTORY, count, card, section, true);
}
// WARNING MESSAGES
// ================
public static Token UnknownCard(final String cardName, final String setCode, final int count) {
@@ -144,10 +126,6 @@ public class DeckRecognizer {
return new Token(TokenType.WARNING_MESSAGE, msg);
}
public static Token NotInInventory(final PaperCard card, final int count, final DeckSection section) {
return new Token(TokenType.CARD_NOT_IN_INVENTORY, count, card, section, false);
}
/* =================================
* DECK SECTIONS
* ================================= */
@@ -261,11 +239,14 @@ public class DeckRecognizer {
/**
* Filters all token types that have a PaperCard instance set (not null)
* @return true for tokens of type:
* LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET and CARD_FROM_INVALID_SET, CARD_NOT_IN_INVENTORY, FREE_CARD_NOT_IN_INVENTORY.
* LEGAL_CARD, LIMITED_CARD, CARD_FROM_NOT_ALLOWED_SET and CARD_FROM_INVALID_SET.
* False otherwise.
*/
public boolean isCardToken() {
return TokenType.CARD_TOKEN_TYPES.contains(this.type);
return (this.type == TokenType.LEGAL_CARD ||
this.type == TokenType.LIMITED_CARD ||
this.type == TokenType.CARD_FROM_NOT_ALLOWED_SET ||
this.type == TokenType.CARD_FROM_INVALID_SET);
}
/**
@@ -274,7 +255,9 @@ public class DeckRecognizer {
* LEGAL_CARD, LIMITED_CARD, DECK_NAME; false otherwise.
*/
public boolean isTokenForDeck() {
return TokenType.IN_DECK_TOKEN_TYPES.contains(this.type);
return (this.type == TokenType.LEGAL_CARD ||
this.type == TokenType.LIMITED_CARD ||
this.type == TokenType.DECK_NAME);
}
/**
@@ -283,7 +266,7 @@ public class DeckRecognizer {
* False otherwise.
*/
public boolean isCardTokenForDeck() {
return isCardToken() && isTokenForDeck();
return (this.type == TokenType.LEGAL_CARD || this.type == TokenType.LIMITED_CARD);
}
/**
@@ -293,7 +276,10 @@ public class DeckRecognizer {
* CARD_RARITY, CARD_CMC, CARD_TYPE, MANA_COLOUR
*/
public boolean isCardPlaceholder(){
return TokenType.CARD_PLACEHOLDER_TOKEN_TYPES.contains(this.type);
return (this.type == TokenType.CARD_RARITY ||
this.type == TokenType.CARD_CMC ||
this.type == TokenType.MANA_COLOUR ||
this.type == TokenType.CARD_TYPE);
}
/** Determines if current token is a Deck Section token
@@ -550,7 +536,7 @@ public class DeckRecognizer {
PaperCard tokenCard = token.getCard();
if (isAllowed(tokenSection)) {
if (tokenSection != referenceDeckSectionInParsing) {
if (!tokenSection.equals(referenceDeckSectionInParsing)) {
Token sectionToken = Token.DeckSection(tokenSection.name(), this.allowedDeckSections);
// just check that last token is stack is a card placeholder.
// In that case, add the new section token before the placeholder
@@ -589,7 +575,7 @@ public class DeckRecognizer {
refLine = purgeAllLinks(refLine);
String line;
if (refLine.startsWith(LINE_COMMENT_DELIMITER_OR_MD_HEADER))
if (StringUtils.startsWith(refLine, LINE_COMMENT_DELIMITER_OR_MD_HEADER))
line = refLine.replaceAll(LINE_COMMENT_DELIMITER_OR_MD_HEADER, "");
else
line = refLine.trim(); // Remove any trailing formatting
@@ -598,7 +584,7 @@ public class DeckRecognizer {
// Final fantasy cards like Summon: Choco/Mog should be ommited to be recognized. TODO: fix maybe for future cards
if (!line.contains("Summon:"))
line = SEARCH_SINGLE_SLASH.matcher(line).replaceFirst(" // ");
if (line.startsWith(ASTERISK)) // Markdown lists (tappedout md export)
if (StringUtils.startsWith(line, ASTERISK)) // markdown lists (tappedout md export)
line = line.substring(2);
// == Patches to Corner Cases
@@ -614,8 +600,8 @@ public class DeckRecognizer {
Token result = recogniseCardToken(line, referenceSection);
if (result == null)
result = recogniseNonCardToken(line);
return result != null ? result : refLine.startsWith(DOUBLE_SLASH) ||
refLine.startsWith(LINE_COMMENT_DELIMITER_OR_MD_HEADER) ?
return result != null ? result : StringUtils.startsWith(refLine, DOUBLE_SLASH) ||
StringUtils.startsWith(refLine, LINE_COMMENT_DELIMITER_OR_MD_HEADER) ?
new Token(TokenType.COMMENT, 0, refLine) : new Token(TokenType.UNKNOWN_TEXT, 0, refLine);
}
@@ -627,7 +613,7 @@ public class DeckRecognizer {
while (m.find()) {
line = line.replaceAll(m.group(), "").trim();
}
if (line.endsWith("()"))
if (StringUtils.endsWith(line, "()"))
return line.substring(0, line.length()-2);
return line;
}
@@ -755,12 +741,21 @@ public class DeckRecognizer {
// This would save tons of time in parsing Input + would also allow to return UnsupportedCardTokens beforehand
private DeckSection getTokenSection(String deckSec, DeckSection currentDeckSection, PaperCard card){
if (deckSec != null) {
DeckSection cardSection = switch (deckSec.toUpperCase().trim()) {
case "MB" -> DeckSection.Main;
case "SB" -> DeckSection.Sideboard;
case "CM" -> DeckSection.Commander;
default -> DeckSection.matchingSection(card);
};
DeckSection cardSection;
switch (deckSec.toUpperCase().trim()) {
case "MB":
cardSection = DeckSection.Main;
break;
case "SB":
cardSection = DeckSection.Sideboard;
break;
case "CM":
cardSection = DeckSection.Commander;
break;
default:
cardSection = DeckSection.matchingSection(card);
break;
}
if (cardSection.validate(card))
return cardSection;
}
@@ -1022,21 +1017,51 @@ public class DeckRecognizer {
private static MagicColor.Color getMagicColor(String colorName){
if (colorName.toLowerCase().startsWith("multi") || colorName.equalsIgnoreCase("m"))
return null; // will be handled separately
return MagicColor.Color.fromByte(MagicColor.fromName(colorName.toLowerCase()));
byte color = MagicColor.fromName(colorName.toLowerCase());
switch (color) {
case MagicColor.WHITE:
return MagicColor.Color.WHITE;
case MagicColor.BLUE:
return MagicColor.Color.BLUE;
case MagicColor.BLACK:
return MagicColor.Color.BLACK;
case MagicColor.RED:
return MagicColor.Color.RED;
case MagicColor.GREEN:
return MagicColor.Color.GREEN;
default:
return MagicColor.Color.COLORLESS;
}
}
public static String getLocalisedMagicColorName(String colorName){
Localizer localizer = Localizer.getInstance();
return switch (colorName.toLowerCase()) {
case MagicColor.Constant.WHITE -> localizer.getMessage("lblWhite");
case MagicColor.Constant.BLUE -> localizer.getMessage("lblBlue");
case MagicColor.Constant.BLACK -> localizer.getMessage("lblBlack");
case MagicColor.Constant.RED -> localizer.getMessage("lblRed");
case MagicColor.Constant.GREEN -> localizer.getMessage("lblGreen");
case MagicColor.Constant.COLORLESS -> localizer.getMessage("lblColorless");
case "multicolour", "multicolor" -> localizer.getMessage("lblMulticolor");
default -> "";
};
switch(colorName.toLowerCase()){
case MagicColor.Constant.WHITE:
return localizer.getMessage("lblWhite");
case MagicColor.Constant.BLUE:
return localizer.getMessage("lblBlue");
case MagicColor.Constant.BLACK:
return localizer.getMessage("lblBlack");
case MagicColor.Constant.RED:
return localizer.getMessage("lblRed");
case MagicColor.Constant.GREEN:
return localizer.getMessage("lblGreen");
case MagicColor.Constant.COLORLESS:
return localizer.getMessage("lblColorless");
case "multicolour":
case "multicolor":
return localizer.getMessage("lblMulticolor");
default:
return "";
}
}
/**
@@ -1055,6 +1080,37 @@ public class DeckRecognizer {
return "";
}
private static Pair<String, String> getManaNameAndSymbol(String matchedMana) {
if (matchedMana == null)
return null;
Localizer localizer = Localizer.getInstance();
switch (matchedMana.toLowerCase()) {
case MagicColor.Constant.WHITE:
case "w":
return Pair.of(localizer.getMessage("lblWhite"), MagicColor.Color.WHITE.getSymbol());
case MagicColor.Constant.BLUE:
case "u":
return Pair.of(localizer.getMessage("lblBlue"), MagicColor.Color.BLUE.getSymbol());
case MagicColor.Constant.BLACK:
case "b":
return Pair.of(localizer.getMessage("lblBlack"), MagicColor.Color.BLACK.getSymbol());
case MagicColor.Constant.RED:
case "r":
return Pair.of(localizer.getMessage("lblRed"), MagicColor.Color.RED.getSymbol());
case MagicColor.Constant.GREEN:
case "g":
return Pair.of(localizer.getMessage("lblGreen"), MagicColor.Color.GREEN.getSymbol());
case MagicColor.Constant.COLORLESS:
case "c":
return Pair.of(localizer.getMessage("lblColorless"), MagicColor.Color.COLORLESS.getSymbol());
default: // Multicolour
return Pair.of(localizer.getMessage("lblMulticolor"), "");
}
}
public static boolean isDeckName(final String lineAsIs) {
if (lineAsIs == null)
return false;

View File

@@ -367,7 +367,7 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
public final FCollectionView<SpellAbility> getManaAbilities() {
FCollection<SpellAbility> newCol = new FCollection<>();
updateSpellAbilities(newCol, true);
// stream().toList() causes crash on Android 8-13, use Collectors.toList()
// stream().toList() causes crash on Android, use Collectors.toList()
newCol.addAll(abilities.stream().filter(SpellAbility::isManaAbility).collect(Collectors.toList()));
card.updateSpellAbilities(newCol, this, true);
return newCol;
@@ -375,7 +375,7 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
public final FCollectionView<SpellAbility> getNonManaAbilities() {
FCollection<SpellAbility> newCol = new FCollection<>();
updateSpellAbilities(newCol, false);
// stream().toList() causes crash on Android 8-13, use Collectors.toList()
// stream().toList() causes crash on Android, use Collectors.toList()
newCol.addAll(abilities.stream().filter(Predicate.not(SpellAbility::isManaAbility)).collect(Collectors.toList()));
card.updateSpellAbilities(newCol, this, false);
return newCol;
@@ -390,7 +390,7 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
if (null != mana) {
leftAbilities = leftAbilities.stream()
.filter(mana ? SpellAbility::isManaAbility : Predicate.not(SpellAbility::isManaAbility))
// stream().toList() causes crash on Android 8-13, use Collectors.toList()
// stream().toList() causes crash on Android, use Collectors.toList()
.collect(Collectors.toList());
}
newCol.addAll(leftAbilities);
@@ -402,7 +402,7 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
if (null != mana) {
rightAbilities = rightAbilities.stream()
.filter(mana ? SpellAbility::isManaAbility : Predicate.not(SpellAbility::isManaAbility))
// stream().toList() causes crash on Android 8-13, use Collectors.toList()
// stream().toList() causes crash on Android, use Collectors.toList()
.collect(Collectors.toList());
}
newCol.addAll(rightAbilities);

View File

@@ -1,8 +1,8 @@
package forge.game.keyword;
import forge.card.CardSplitType;
import forge.StaticData;
import forge.game.card.Card;
import forge.item.PaperCard;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
@@ -223,7 +223,7 @@ public enum Keyword {
displayName = displayName0;
}
private static Pair<Keyword, String> getKeywordDetails(String k) {
public static KeywordInterface getInstance(String k) {
Keyword keyword = Keyword.UNDEFINED;
String details = k;
// try to get real part
@@ -255,20 +255,15 @@ public enum Keyword {
keyword = smartValueOf(k);
details = "";
}
return Pair.of(keyword, details);
}
public static KeywordInterface getInstance(String k) {
Pair<Keyword, String> p = getKeywordDetails(k);
KeywordInstance<?> inst;
try {
inst = p.getKey().type.getConstructor().newInstance();
inst = keyword.type.getConstructor().newInstance();
}
catch (Exception e) {
inst = new UndefinedKeyword();
}
inst.initialize(k, p.getKey(), p.getValue());
inst.initialize(k, keyword, details);
return inst;
}
@@ -283,44 +278,36 @@ public enum Keyword {
return keywords;
}
private static Keyword get(String k) {
if (k == null || k.isEmpty())
return Keyword.UNDEFINED;
return getKeywordDetails(k).getKey();
}
private static final Map<String, Set<Keyword>> cardKeywordSetLookup = new HashMap<>();
public static Set<Keyword> getKeywordSet(PaperCard card) {
String name = card.getName();
Set<Keyword> keywordSet = cardKeywordSetLookup.get(name);
String key = card.getName();
Set<Keyword> keywordSet = cardKeywordSetLookup.get(key);
if (keywordSet == null) {
CardSplitType cardSplitType = card.getRules().getSplitType();
keywordSet = EnumSet.noneOf(Keyword.class);
if (cardSplitType != CardSplitType.None && cardSplitType != CardSplitType.Split) {
if (card.getRules().getOtherPart() != null) {
if (card.getRules().getOtherPart().getKeywords() != null) {
for (String key : card.getRules().getOtherPart().getKeywords()) {
Keyword keyword = get(key);
if (!Keyword.UNDEFINED.equals(keyword))
keywordSet.add(keyword);
}
}
keywordSet = new HashSet<>();
for (KeywordInterface inst : Card.getCardForUi(card).getKeywords()) {
final Keyword keyword = inst.getKeyword();
if (keyword != Keyword.UNDEFINED) {
keywordSet.add(keyword);
}
}
if (card.getRules().getMainPart().getKeywords() != null) {
for (String key : card.getRules().getMainPart().getKeywords()) {
Keyword keyword = get(key);
if (!Keyword.UNDEFINED.equals(keyword))
keywordSet.add(keyword);
}
}
cardKeywordSetLookup.put(name, keywordSet);
cardKeywordSetLookup.put(card.getName(), keywordSet);
}
return keywordSet;
}
public static Runnable getPreloadTask() {
if (cardKeywordSetLookup.size() < 10000) { //allow preloading even if some but not all cards loaded
return () -> {
final Collection<PaperCard> cards = StaticData.instance().getCommonCards().getUniqueCards();
for (PaperCard card : cards) {
getKeywordSet(card);
}
};
}
return null;
}
public static Keyword smartValueOf(String value) {
for (final Keyword v : Keyword.values()) {
if (v.displayName.equalsIgnoreCase(value)) {

View File

@@ -577,6 +577,9 @@ public class Player extends GameEntity implements Comparable<Player> {
}
public final boolean payLife(final int lifePayment, final SpellAbility cause, final boolean effect) {
return payLife(lifePayment, cause, effect, null);
}
public final boolean payLife(final int lifePayment, final SpellAbility cause, final boolean effect, Map<AbilityKey, Object> params) {
// fast check for pay zero life
if (lifePayment <= 0) {
cause.setPaidLife(0);
@@ -596,6 +599,9 @@ public class Player extends GameEntity implements Comparable<Player> {
if (cause.isReplacementAbility() && effect) {
replaceParams.putAll(cause.getReplacingObjects());
}
if (params != null) {
replaceParams.putAll(params);
}
switch (getGame().getReplacementHandler().run(ReplacementType.PayLife, replaceParams)) {
case Replaced:
return true;

View File

@@ -83,8 +83,6 @@ public class StaticAbilityCantTarget {
return false;
}
}
} else if (stAb.hasParam("AffectedZone")) {
return false;
}
final Card source = spellAbility.getHostCard();

View File

@@ -22,6 +22,7 @@ import java.util.Map;
import com.google.common.collect.Iterables;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
@@ -59,8 +60,17 @@ public class TriggerAttackerBlocked extends Trigger {
return false;
}
if (!matchesValidParam("ValidBlocker", runParams.get(AbilityKey.Blockers))) {
return false;
if (hasParam("ValidBlocker")) {
@SuppressWarnings("unchecked")
int count = CardLists.getValidCardCount(
(Iterable<Card>) runParams.get(AbilityKey.Blockers),
getParam("ValidBlocker"),
getHostCard().getController(), getHostCard(), this
);
if (count == 0) {
return false;
}
}
return true;

View File

@@ -1,770 +0,0 @@
{
"a32x":{
"CPU":"2x Cortex-A76 @ 2GHz 6x Cortex-A55 @ 2GHz",
"SoC":"MediaTek Dimensity 720 (MT6853)"
},
"angler":{
"CPU":"4x Cortex-A57 @ 1.95GHz 4x Cortex-A53 @ 1.55GHz",
"SoC":"Snapdragon 810 MSM8994"
},
"ane":{
"CPU":"4x Cortex-A54 @ 2.3GHz 4x Cortex-A54 @ 1.7GHz",
"SoC":"HiSilicon Kirin 659"
},
"amar_row_wifi":{
"CPU":"8x Cortex-A53 @ 1.8GHz",
"SoC":"Mediatek MT8768"
},
"atlas":{
"CPU":"-",
"SoC":"Intel(R) Core(TM) i5-8200Y @ 1.3GHz"
},
"apq8084":{
"CPU":"4x Krait 450 @ 2.65GHz",
"SoC":"Snapdragon 805 APQ8084"
},
"atoll":{
"CPU":"2x Kryo 465 Gold @ 2.3GHz 6x Kryo 465 Silver @ 1.8GHz",
"SoC":"Snapdragon 720G (SM7125)"
},
"art-l29":{
"CPU":"4x Cortex-A73 @ 2.2GHz 4x Cortex-A53 @ 1.7GHz",
"SoC":"HiSilicon Kirin 710F"
},
"baylake":{
"CPU":"Atom Z3745 @ 1.3GHz",
"SoC":"Intel Atom Z3745"
},
"begonia":{
"CPU":"2x Cortex-176 @ 2GHz 6x Cortex-A55 @ 2GHz",
"SoC":"MediaTek Helios G90T MT6785T"
},
"blueline":{
"CPU":"4x Kryo 385 Gold @ 2.8GHz 4x Kryo 385 Silver @ 1.75GHz",
"SoC":"Snapdragon 845"
},
"bullhead":{
"CPU":"4x Cortex-A57 @ 1.8GHz 4x Cortex-A53 @ 1.44GHz",
"SoC":"Snapdragon 808"
},
"capri":{
"CPU":"2x Cortex-A9 @ 1.2GHz",
"SoC":"Broadcom BCM28155"
},
"cepheus":{
"CPU":"1x Kryo 485 Gold @ 2.8GHz 3x Kryo 485 Gold @ 2.4GHz 4x Kryo 485 Silver @ 1.7GHz",
"SoC":"Snapdragon 855 SM8150"
},
"cheryl":{
"CPU":"4x Kryo 280 HP @ 2.45GHz 4x Kryo 280 LP @ 1.9GHz",
"SoC":"Snapdragon 835 MSM8998"
},
"cheryl2":{
"CPU":"4x Kryo 385 Gold @ 2.8GHz 4x Kryo 385 Silver @ 1.8GHz",
"SoC":"Snapdragon 845 SDM845"
},
"cheetah":{
"CPU":"2x Cortex-X1 @ 2.8GHz 2x Cortex-A78 @ 2.3GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Google Tensor G2 (GS201)"
},
"comet":{
"CPU":"1x Cortex-X4 @ 31.GHz 3x Cortex-A720 @ 2.6GHz 4x Cortex-A520 @ 1.95GHz",
"SoC":"Google Tensor G4 (GS401)"
},
"eureka":{
"CPU":"6x Cortex-A78C @ 2.3 GHz",
"SoC":"Snapdragon XR2 Gen 2 (SM8550)"
},
"felix":{
"CPU":"2x Cortex-X1 @ 2.8GHz 2x Cortex-A78 @ 2.3GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Google Tensor G2 (GS201)"
},
"tangorpro":{
"CPU":"2x Cortex-X1 @ 2.8GHz 2x Cortex-A78 @ 2.3GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Google Tensor G2 (GS201)"
},
"codina":{
"CPU":"2x Cortex-A9 @ 1.0GHz",
"SoC":"NovaThor U8500"
},
"clovertrail":{
"CPU":"2x Atom Z2560 @ 1.6GHz",
"SoC":"Intel Atom Z2560"
},
"clt":{
"CPU":"4x Cortex-A73 @ 2.36GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"HiSilicon Kirin 970"
},
"els":{
"CPU":"4x Cortex-A76 @ 2.8GHz 4x Cortex-A55 @ 1.9GHz",
"SoC":"HiSilicon Kirin 990 5G"
},
"dandelion":{
"CPU":"8x Cortex-A54 @ 1.5GHz",
"SoC":"MediaTek Helio G25 (MT6762G)"
},
"darcy":{
"CPU":"4x ARM Cortex-A53 4x ARM Cortex-A57",
"SoC":"nVIDIA Tegra X1 T210"
},
"db8520h":{
"CPU":"2x Cortex-A9 @ 1.0GHz",
"SoC":"NovaThor U8500"
},
"dragon":{
"CPU":"4x Cortex-A57 @ 1.9GHz 4x Cortex-A53 @ 1.3GHz",
"SoC":"nVIDIA Tegra X1 T210"
},
"douglas":{
"CPU":"4x Cortex-A53 @ 1.3GHz",
"SoC":"MediaTek MT8163"
},
"eeepad":{
"CPU":"4x Atom Z2520 @ 1.2GHz",
"SoC":"Intel Atom Z2520"
},
"endeavoru":{
"CPU":"4x Cortex-A9 @ 1.5GHz 1x Cortex-A9 @ 0.5GHz",
"SoC":"nVIDIA Tegra 3 AP33"
},
"eml":{
"CPU":"4x Cortex-A73 @ 2.36GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"HiSilicon Kirin 970"
},
"eve":{
"CPU":"i5-7Y56 @ 1.2GHz",
"SoC":"Ambel Lake-Y / Kaby Lake-U/Y"
},
"eva-l19":{
"CPU":"4x Cortex-A72 @ 2.5GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"HiSilicon Kirin 955"
},
"exynos990":{
"CPU":"4x Cortex-A55 @ 2GHz 2x Cortex-A76 @ 2.5GHz 2x Exynos M5 @ 2.7GHz",
"SoC":"Exynos 990"
},
"exynos9611":{
"CPU":"4x Cortex-A73 @ 2.3GHz 4x Cortex-A53 @ 1.7GHz",
"SoC":"Exynos 7 Octa (9611)"
},
"exynos2100":{
"CPU":"1x Cortex-X1 @ 2.9GHz 3x Cortex-A78 @ 2.8GHz 4x Cortex-A55 @ 2.2GHz",
"SoC":"Exynos 2100"
},
"exynos9810":{
"CPU":"4x Exynos M3 @ 2.7GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Exynos 9 (9810)"
},
"exynos9820":{
"CPU":"2 Exynos M4 @ 2.7GHz 2x Cortex-A75 @ 2.3GHz 4x Cortex-A55 @ 1.9GHz",
"SoC":"Exynos 9 (9820)"
},
"ford":{
"CPU":"4x Cortex-A7 @ 1.3GHz",
"SoC":"MediaTek MT8127"
},
"flo":{
"CPU":"4x Krait 300 @ 1.5GHz",
"SoC":"Snapdragon 600 APQ8064-FLO"
},
"flame":{
"CPU":"1x Kryo 485 Gold @ 2.8GHz 3x Kryo 485 Gold @ 2.4GHz 4x Kryo 485 Silver @ 1.7GHz",
"SoC":"Snapdragon 855 SM8150"
},
"fleur":{
"CPU":"2x Cortex-A76 @ 2GHz 6x Cortex-A55 @ 2GHz",
"SoC":"MediaTek Helio G96 (MT6781)"
},
"flounder":{
"CPU":"2x nVidia Denver @ 2.5GHz",
"SoC":"nVIDIA Tegra K1 T132"
},
"g3u":{
"CPU":"2x Cortex-A5 @ 1.0GHz",
"SoC":"Snapdragon S4 Play MSM8225"
},
"gee":{
"CPU":"4x Krait 300 @ 1.5GHz",
"SoC":"Snapdragon S4 Pro APQ8064"
},
"grouper":{
"CPU":"4x Cortex-A9 @ 1.3GHz 1x Cortex-A9 @ 0.5GHz",
"SoC":"nVIDIA Tegra 3 T30L"
},
"hammerhead":{
"CPU":"4x Krait 400 @ 2.26GHz",
"SoC":"Snapdragon 800 MSM8974"
},
"hawaii_ss_kylepro":{
"CPU":"2x Cortex-A9 @ 1.2GHz",
"SoC":"Broadcom BCM21664T"
},
"herring":{
"CPU":"1x Cortex-A8 @ 1.0GHz",
"SoC":"Exynos 3 Single 3110"
},
"hollywood":{
"CPU":"4x Kryo 280 HP @ 2.4GHz 4x Kryo 280 LP @ 1.9GHz",
"SoC":"Snapdragon XR2"
},
"k6853v1_64_titan":{
"CPU":"2x Cortex-A76 @ 2Ghz 6x Cortex-A55 @ 2GHz",
"SoC":"MediaTek Dimensity 720 (MT6853)"
},
"kalama":{
"CPU":"1x Cortex-X3 @ 3.3GHz 2x Cortex-A710 @ 2.8GHz 2x Cortex-A715 @ 2.8GHz 3x Cortex-A510 @ 2.0GHz",
"SoC":"Snapdragon 8 Gen 2 (SM8550)"
},
"kona":{
"CPU":"1x Cortex-A77 @ 3.1GHz 3x Cortex-A77 @ 2.4GHz 4x Kryo 585 Silver @ 1.8GHz",
"SoC":"Snapdragon 865 SM8250"
},
"kohaku":{
"CPU":"i5-10210U @ 1.6GHz",
"SoC":"Comet Lake-U"
},
"lahaina":{
"CPU":"1x Cortex-X1 @ 2.8GHz 3x Cortex-A78 @ 2.4GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Snapdragon 888"
},
"liara":{
"CPU":"5 Compute cores 2C+3G",
"SoC":"AMD A4-9120C Radeon R4"
},
"lito":{
"CPU":"2x Cortex-A77 @ 2.1GHz 6x Kryo 560 Silver @ 1.7GHz",
"SoC":"Snapdragon 690 SM6350"
},
"lya":{
"CPU":"2x Cortex-A76 @ 2.6GHz 2x Cortex-A76 @ 1.9GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"HiSilicon Kirin 980"
},
"lyo-l01":{
"CPU":"4x Cortex-153 @ 1.3GHz",
"SoC":"MediaTek MT6735"
},
"mako":{
"CPU":"4x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 Pro APQ8064"
},
"marlin":{
"CPU":"2x Kryo HP @ 2.15GHz 2x Kryo @ 1.6GHz",
"SoC":"Snapdragon 821 MSM8996 Pro"
},
"med":{
"CPU":"8x Cortex-A54 @ 1.5GHz",
"SoC":"MediaTek MT6762R"
},
"mocha":{
"CPU":"4x Cortex-A15 @2.2GHz",
"SoC":"nVIDIA Tegra K1 T124"
},
"msm8225":{
"CPU":"2x Cortex-A5 @ 1.2GHz",
"SoC":"Snapdragon S4 MSM8225"
},
"msm8226":{
"CPU":"4x Cortex-A7 @ 1.19GHz",
"SoC":"Snapdragon 400 MSM8226"
},
"msm8625":{
"CPU":"2x Cortex-A5 @ 1.2GHz",
"SoC":"Snapdragon S4 MSM8625"
},
"MSM8227":{
"CPU":"2x Krait @ 1GHz",
"SoC":"Snapdragon S4 Plus MSM8227"
},
"msm8627":{
"CPU":"2x Krait @ 1GHz",
"SoC":"Snapdragon S4 Plus MSM8627"
},
"apq8030":{
"CPU":"2x Krait @ 1.2GHz",
"SoC":"Snapdragon S4 Plus APQ8030"
},
"msm8230":{
"CPU":"2x Krait @ 1.2GHz",
"SoC":"Snapdragon S4 Plus MSM8230"
},
"msm8660_surf":{
"CPU":"2x Scorpion @ 1.5GHz",
"SoC":"Snapdragon S3 MSM8260"
},
"msm8630":{
"CPU":"2x Krait @ 1.2GHz",
"SoC":"Snapdragon S4 Plus MSM8630"
},
"msm8930":{
"CPU":"2x Krait @ 1.2GHz",
"SoC":"Snapdragon S4 Plus MSM8930"
},
"msm8937":{
"CPU":"4x Cortex-A53 @ 1.4GHz",
"SoC":"Snapdragon 425 MSM8917"
},
"apq8060a":{
"CPU":"2x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 Plus APQ8060A"
},
"msm8260a":{
"CPU":"2x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 Plus MSM8260A"
},
"msm8660a":{
"CPU":"2x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 Plus MSM8660A"
},
"msm8960":{
"CPU":"2x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 Plus MSM8960"
},
"msm8260a-pro":{
"CPU":"2x Krait 300 @ 1.7GHz",
"SoC":"Snapdragon S4 Pro MSM8260A Pro"
},
"msm8960t":{
"CPU":"2x Krait 300 @ 1.7GHz",
"SoC":"Snapdragon S4 Pro MSM8960T"
},
"msm8960t-pro":{
"CPU":"2x Krait 300 @ 1.7GHz",
"SoC":"Snapdragon S4 Pro MSM8960T Pro"
},
"msm8960ab":{
"CPU":"2x Krait 300 @ 1.7GHz",
"SoC":"Snapdragon S4 Pro MSM8960AB"
},
"msm8960dt":{
"CPU":"2x Krait 300 @ 1.7GHz",
"SoC":"Snapdragon S4 Pro MSM8960DT"
},
"apq8064":{
"CPU":"4x Krait 300 @ 1.5GHz",
"SoC":"Snapdragon 600 APQ8064"
},
"msm8916":{
"CPU":"4x Cortex-A53 @ 1.2GHz",
"SoC":"Snapdragon 410 MSM8916"
},
"msm8953":{
"CPU":"8x Cortex-A53 @ 2.0GHz",
"SoC":"Snapdragon 625 MSM8953"
},
"msm8952":{
"CPU":"8x Cortex-A53 @ 1.2GHz",
"SoC":"Snapdragon 617 MSM8952"
},
"msm8956":{
"CPU":"2x Cortex-A72 @ 1.8GHz 4x Cortex-A53 @ 1.4GHz",
"SoC":"Snapdragon 650 MSM8956"
},
"msm8974":{
"CPU":"4x Krait 400 @ 2.15GHz",
"SoC":"Snapdragon 800 MSM8974"
},
"msm8974pro-ab":{
"CPU":"4x Krait 400 @ 2.26GHz",
"SoC":"Snapdragon 801 MSM8974PRO-AB"
},
"msm8974pro-ac":{
"CPU":"4x Krait 400 @ 2.45GHz",
"SoC":"Snapdragon 801 MSM8974AC"
},
"msm8976":{
"CPU":"4x Cortex-A53 @ 1.4GHz 4x Cortex-A72 @ 1.8GHz",
"SoC":"Snapdragon 652 MSM8976"
},
"msm8976pro":{
"CPU":"4x Cortex-A53 @ 1.4GHz 4x Cortex-A72 @ 1.9GHz",
"SoC":"Snapdragon 653 MSM8976 Pro"
},
"msm8992":{
"CPU":"4x Cortex-A57 @ 1.8GHz 4x Cortex-A53 @ 1.4GHz",
"SoC":"Snapdragon 808 MSM8992"
},
"msm8994":{
"CPU":"4x Cortex-A57 @ 1.95GHz 4x Cortex-A53 @ 1.5GHz",
"SoC":"Snapdragon 810 MSM8994"
},
"msm8996":{
"CPU":"2x Kryo HP @ 1.8GHz 2x Kryo LP @ 1.36GHz",
"SoC":"Snapdragon 820 MSM8996"
},
"msm8996pro":{
"CPU":"2x Kryo HP @ 2.34GHz 2x Kryo LP @ 2.18GHz",
"SoC":"Snapdragon 821 MSM8996 Pro"
},
"msm8998":{
"CPU":"4x Kryo 280 HP @ 2.4GHz 4x Kryo 280 LP @ 1.9GHz",
"SoC":"Snapdragon 835 MSM8998"
},
"msmnile":{
"CPU":"1x Kryo 485 Gold @ 2.8GHz 3x Kryo 485 Gold @ 2.4GHz 4x Kryo 485 Silver @ 1.8GHz",
"SoC":"Snapdragon 855 SM8150"
},
"mt6795t":{
"CPU":"8x Cortex-A53 @ 2.1GHz",
"SoC":"MediaTek Helio X10 MT6795T"
},
"mt6797m":{
"CPU":"2x Cortex-A72 @ 2.1GHz 4x Cortex-A53 @ 1.85GHz 4x Cortex-A53 @ 1.4GHz",
"SoC":"MediaTek Helio X20 MT6797M"
},
"mt6750t":{
"CPU":"8x Cortex-A53 @ 1.5GHz",
"SoC":"MediaTek MT6750T"
},
"mx5":{
"CPU":"8x Cortex-A53 @ 2.1GHz",
"SoC":"MediaTek Helio X10 MT6795T"
},
"mtk6575":{
"CPU":"1x Cortex-A9 @ 1.0GHz",
"SoC":"MediaTek MT6575"
},
"noh":{
"CPU":"4x Cortex-A77 @ 3.1GHz 4x Cortex-A55 @ 2.0GHz",
"SoC":"HiSilicon Kirin 9000"
},
"sdm710":{
"CPU":"2x Kryo 385 Gold @ 2.2GHz 6x Kryo 385 Silver @ 1.7GHz",
"SoC":"Snapdragon 710 SDM710"
},
"sdm845":{
"CPU":"4x Kryo 385 Gold @ 2.8GHz 4x Kryo 385 Silver @ 1.7GHz",
"SoC":"Snapdragon 845 SDM845"
},
"monterey":{
"CPU":"4x Kryo 280 HP @ 2.45GHz 4x Kryo 280 LP @ 1.9GHz",
"SoC":"Snapdragon 835 MSM8998"
},
"oriole":{
"CPU":"2x Cortex-X1 @ 2.8GHz 2x Cortex-A76 @ 2.2GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Google Tensor (Whitechapel)"
},
"oppo6779_18073":{
"CPU":"2x Cortex-A75 @ 2.2GHz 6x Cortex-A55 @ 2GHz",
"SoC":"Mediatek MT6779 Helio P90"
},
"gt-p7510":{
"CPU":"2x Cortex-A9 @ 1.0GHz",
"SoC":"nVIDIA Tegra 2 T20"
},
"pacific":{
"CPU":"2x @ 2.1GHz 2x @ 1.6GHz",
"SoC":"Snapdragon 820E Embedded"
},
"piranha":{
"CPU":"2x Cortex-A9 @ 1.0GHz",
"SoC":"Texas Instruments OMAP4430"
},
"pro5":{
"CPU":"4xCortex-56 @ 2.1GHz 4x Cortex-53 @ 1.5GHz",
"SoC":"Exynos 7 Octa 7420"
},
"pro7plus":{
"CPU":"2x Cortex-A73 @ 2.6GHz 4x Cortex-A53 @ 2.2GHz 4x Cortex-A35 @ 1.9GHz",
"SoC":"MediaTek Helio X30 MT6799"
},
"pxa986":{
"CPU":"2x Cortex-A9 @ 1.2GHz",
"SoC":"Marvell PXA988"
},
"pxa19xx":{
"CPU":"4x Cortex-A53 @ 1.25GHz",
"SoC":"Maxvell Armada PXA1908"
},
"rhea_ss_corsicass":{
"CPU":"1x Cortex-A9 @ 0.9GHz",
"SoC":"Broadcom BCM21654"
},
"panther":{
"CPU":"1x Cortex-X1 @ 2.8GHz 2x Cortex-A78 @ 2.3GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Google Tensor G2"
},
"prada":{
"CPU":"4x Cortex-A53 @ 1.4 GHz 4x Cortex-A53 @ 1.1GHz",
"SoC":"Snapdragon 430 MSM8937"
},
"pro6plus":{
"CPU":"4x Exynos M1 @ 1.97GHz 4x Cortex-A53 @ 1.48GHz",
"SoC":"Exynos 8 Octa 8890"
},
"universal3110":{
"CPU":"1x Cortex-A8 @ 1.2GHz",
"SoC":"Exynos 3 Single 3110"
},
"universal9820":{
"CPU":"2x Exynos M4 @ 2.7GHz 2x Cortex-A75 @ 2.3GHz 4x Cortex-A55 @ 1.95GHz",
"SoC":"Exynos 9 9820"
},
"redfin":{
"CPU":"2x Kryo 475 Gold @ 2.4GHz 2x Kryo 475 Gold 2.2GHz 6x Kryo 475 Silver @ 1.8GHz ",
"SoC":"Snapdragon 765/765G"
},
"s5e9925":{
"CPU":"1x Cortex-X2 @ 2.8GHZ 3x Cortex-A710 @ 2.5GHz 4x Cortex-A510 @ 1.8GHz",
"SoC":"Exynos 2200"
},
"saturn":{
"CPU":"4x Krait 450 @ 2.45GHz",
"SoC":"Snapdragon 805 APQ8084"
},
"sailfish":{
"CPU":"2x Kryo HP @ 2.15GHz 2x Kryo @ 1.6GHz",
"SoC":"Snapdragon 821 MSM8996 Pro"
},
"sdm660":{
"CPU":"4x Kryo 260 HP @ 2.2GHz 4x Kryo 260 LP @ 1.8GHz",
"SoC":"Snapdragon 660"
},
"sc-02b":{
"CPU":"1x Cortex-A8 @ 1.2GHz",
"SoC":"Exynos 3 Single 3110"
},
"sch-i905":{
"CPU":"2x Cortex-A9 @ 1.0GHz",
"SoC":"nVIDIA Tegra 2 T20"
},
"sc7730s":{
"CPU":"4x Cortex-A7 @ 1.3GHz",
"SoC":"Spreadtrum SC7730S"
},
"shamu":{
"CPU":"4x Krai 450 @ 2.65GHz",
"SoC":"Snapdragon 805 APQ8084AB"
},
"shiba":{
"CPU":"1x Cortex-X3 @ 2.9GHz 4x Cortex-A715 @ 2.3GHz 4x Cortex-A510 @ 1.7GHz",
"SoC":"Google Tensor G3 (GS301)"
},
"sm6150":{
"CPU":"2x Kryo 460 Gold @ 2GHz 6x 460 Kryo Silver @ 1.7GHz",
"SoC":"Snapdragon 675 SM6150"
},
"smdkc110":{
"CPU":"1x Cortex-A8 @ 1.2GHz",
"SoC":"Exynos 3 Single 3110"
},
"sun":{
"CPU":"2x Oryon @ 4.5GHz 6x Oryon @ 3.5GHz",
"SoC":"Snapdragon 9 Elite (SM8750)"
},
"ums512_25c10": {
"CPU":"2 Cortex-A75 @2GHz 6x Cortex-A55 @ 2 GHz",
"SoC":"Unisoc Tiger T618"
},
"universal3250":{
"CPU":"2x Cortex-A7 @ 1.0GHz",
"SoC":"Exynos 2 Dual 3250"
},
"universal3470":{
"CPU":"4x Cortex-A7 @ 1.4GHz",
"SoC":"Exynos 3 Quad 3470"
},
"universal3475":{
"CPU":"4x Cortex-A7 @ 1.3GHz",
"SoC":"Exynos 3 Quad 3475"
},
"universal4210":{
"CPU":"2x Cortex-A9 @ 1.4GHz",
"SoC":"Exynos 4 Dual 4210"
},
"universal4212":{
"CPU":"2x Cortex-A9 @ 1.5GHz",
"SoC":"Exynos 4 Dual 4212"
},
"universal4412":{
"CPU":"4x Cortex-A9 @ 1.4GHz",
"SoC":"Exynos 4 Quad 4412"
},
"smdk4x12":{
"CPU":"4x Cortex-A9 @ 1.4GHz",
"SoC":"Exynos 4 Quad 4412"
},
"universal4415":{
"CPU":"4x Cortex-A9 @ 1.5GHz",
"SoC":"Exynos 4 Quad 4415"
},
"universal5250":{
"CPU":"2x Cortex-A15 @ 1.7GHz",
"SoC":"Exynos 5 Dual 5250"
},
"universal5260":{
"CPU":"2x Cortex-A15 @ 1.7GHz 4x Cortex-A7 @ 1.3GHz",
"SoC":"Exynos 5 Hexa 5260"
},
"universal5410":{
"CPU":"4x Cortex-A15 @ 1.6GHz 4x Cortex-A7 @ 1.2GHz",
"SoC":"Exynos 5 Octa 5410"
},
"universal5420":{
"CPU":"4x Cortex-A15 @ 1.9GHz 4x Cortex-A7 @ 1.3GHz",
"SoC":"Exynos 5 Octa 5420"
},
"universal5422":{
"CPU":"4x Cortex-A15 @ 2.1GHz 4x Cortex-A7 @ 1.5GHz",
"SoC":"Exynos 5 Octa 5422"
},
"universal5430":{
"CPU":"4x Cortex-A15 @ 1.8GHz 4x Cortex-A7 @ 1.3GHz",
"SoC":"Exynos 5 Octa 5430"
},
"universal5800":{
"CPU":"4x Cortex-A15 @ 2.0GHz 4x Cortex-A7 @ 1.3GHz",
"SoC":"Exynos 5 Octa 5800"
},
"universal5433":{
"CPU":"4x Cortex-A57 @ 1.9GHz 4x Cortex-A53 @ 1.3GHz",
"SoC":"Exynos 7 Octa 5433"
},
"universal7420":{
"CPU":"4x Cortex-A57 @ 2.1GHz 4x Cortex-A53 @ 1.5GHz",
"SoC":"Exynos 7 Octa 7420"
},
"universal7570":{
"CPU":"8x Cortex-A53 @ 1.4GHz",
"SoC":"Exynos 7 Quad 7570"
},
"universal7580":{
"CPU":"8x Cortex-A53 @ 1.6GHz",
"SoC":"Exynos 7 Octa 7580"
},
"universal7870":{
"CPU":"8x Cortex-A53 @ 1.6GHz",
"SoC":"Exynos 7 Octa 7870"
},
"universal7880":{
"CPU":"8x Cortex-A53 @ 1.9GHz",
"SoC":"Exynos 7 Octa 7880"
},
"universal7872":{
"CPU":"2x Cortex-A73 @ 2.0GHz 4x Cortex-A53 @ 1.6GHz",
"SoC":"Exynos 7 Hexa 7872"
},
"universal7885":{
"CPU":"2x Cortex-A73 @ 2.2GHz 6x Cortex-A53 @ 1.6GHz",
"SoC":"Exynos 7 Octa 7885"
},
"universal8890":{
"CPU":"4x Cortex-A53 @ 1.6GHz 4x Samsung Exynos M1 @ 2.6GHz",
"SoC":"Exynos 8 Octa 8890"
},
"universal8895":{
"CPU":"4x Samsung Exynos M1 @ 2.3GHz 4x Cortex-A53 @ 1.6GHz",
"SoC":"Exynos 9 Octa 8895"
},
"universal9810":{
"CPU":"4x Samsung Exynos M3 @ 2.8GHz 4x Cortex-A55 @ 1.7GHz",
"SoC":"Exynos 9 Series 9810"
},
"universal9825":{
"CPU":"2x Samsung Exynos M4 @ 2.7GHz 2x Cortex-A75 @ 2.4GHz 4x Cortex-A55 @ 1.9GHz",
"SoC":"Exynos 9 Series 9825"
},
"ville":{
"CPU":"2x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 MSM8290"
},
"vog":{
"CPU":"2x Cortex-A76 @ 2.6GHz 2x Cortex-A76 @ 1.9GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"HiSilicon Kirin 980"
},
"venus":{
"CPU":"1x Cortex-X1 @ 2.8GHz 3x Cortex-A78 @ 2.4GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Snapdragon 888 SM8350"
},
"vtr":{
"CPU":"4x Cortex-A73 @ 2.4GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"HiSilicon Kirin 960"
},
"taimen":{
"CPU":"4x Kryo 280 HP @ 2.45GHz 4x Kryo 280 LP @ 1.9GHz",
"SoC":"Snapdragon 835 MSM8998"
},
"tn8":{
"CPU":"4x Cortex-A15 @2.2GHz",
"SoC":"nVIDIA Tegra K1 T124"
},
"taro":{
"CPU":"1x Cortex-X2 @ 3GHz 3x Cortex-A710 @ 2.5GHz 4x Cortex-A510 @ 1.8GHz",
"SoC":"Snapdragon 8 Gen 1 (SM8450)"
},
"thebes":{
"CPU":"2x Cortex-A15 @ 1.5GHz 2x Cortex-A7 @ 1.2GHz",
"SoC":"MediaTek MT8135"
},
"trinket":{
"CPU":"4x Kryo 260 HP @ 2GHz 4x Kryo 260 LP @ 1.8GHz",
"SoC":"Snapdragon 665 SM6125"
},
"tuna":{
"CPU":"2x Cortex-A9 @ 1.2GHz",
"SoC":"TI OMAP 4460"
},
"vu2kt":{
"CPU":"2x Krait @ 1.5GHz",
"SoC":"Snapdragon S4 Plus MSM8960"
},
"walleye":{
"CPU":"4x Kryo 280 HP @ 2.45GHz 4x Kryo 280 LP @ 1.9GHz",
"SoC":"Snapdragon 835 MSM8998"
},
"wikipad":{
"CPU":"4x Cortex-A9 @ 1.3GHz 1x Cortex-A9 @ 0.5GHz",
"SoC":"nVIDIA Tegra 3 T30L"
},
"qc_reference_phone":{
"CPU":"2x Kryo HP @ 2.15GHz 2x Kryo LP @ 1.36GHz",
"SoC":"Snapdragon 820 MSM8996"
},
"z4u":{
"CPU":"4x Cortex-A5 @ 1.2GHz",
"SoC":"Snapdragon 200 MSM8225Q"
},
"zs600kl":{
"CPU":"4x Kryo 385 Gold @ 3GHz 4x Kryo 385 Silver @ 1.8GHz",
"SoC":"Snapdragon 845"
},
"mt6765v":{
"CPU":"4x Cortex-A53 @ 2.3GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"Mediatek MT6765G Helio G35 (12 nm)"
},
"k65v1_64_bsp_titan_rat":{
"CPU":"4x Cortex-A53 @ 2.3GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"Mediatek MT6765 Helio P35 (12nm)"
},
"careena":{
"CPU":"AMD 670F00h",
"SoC":"AMD A4-9120C RADEON R4, 5 COMPUTE CORES 2C+3G"
},
"sion":{
"CPU":"i3-8130U CPU 2.2GHz SoC",
"SoC":"i3-8130U CPU 2,2 GHz soc"
},
"biloba":{
"CPU":"2x Cortex-A75 @ 2.0GHz 6x Cortex-A55 @ 1.8GHz",
"SoC":"Mediatek MT6769Z Helio G85 (12nm)"
},
"ele":{
"CPU":"2x Cortex-A76 @ 2.6GHz 2x Cortex-A76 @ 1.9GHz 4x Cortex-A55 @ 1.8GHz",
"SoC":"Kirin 980 (7 nm)"
},
"krane":{
"CPU":"4x Cortex-A73 @ 2.0GHz + 4x Cortex-A53 @ 2.0GHz",
"SoC":"MediaTekHelio P60T (12nm)"
},
"pineapple":{
"CPU":"1x Cortex-X4 @ 3.3GHz 3x Cortex-A720 @ 3.2GHz 2x Cortex-A720 @ 2.6GHz 4x Cortex-A520 @ 2.3GHz",
"SoC":"Qualcomm SM8650-AB Snapdragon 8 Gen 3"
},
"s5e9945":{
"CPU":"1x Cortex-X4 @ 3.2GHz 2x Cortex-A720 @ 2.9GHz 3x Cortex-A720 @ 2.6GHz 4x Cortex-A520 @ 2.0GHz",
"SoC":"Exynos 2400"
},
"a12":{
"CPU":"4x Cortex-A53 @ 2.35GHz 4x Cortex-A53 @ 1.8GHz",
"SoC":"MediaTek Helio P35 (MT6765)"
}
}

View File

@@ -67,12 +67,10 @@ import forge.util.ThreadUtil;
import io.sentry.protocol.Device;
import io.sentry.protocol.OperatingSystem;
import org.apache.commons.lang3.tuple.Pair;
import org.json.JSONObject;
import org.jupnp.DefaultUpnpServiceConfiguration;
import org.jupnp.android.AndroidUpnpServiceConfiguration;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
@@ -190,26 +188,7 @@ public class Main extends AndroidApplication {
boolean permissiongranted = checkPermission();
Gadapter = new AndroidAdapter(getContext());
String cpu = "";
String soc = "";
boolean getChipset = false;
// database.json source: https://github.com/xTheEc0/Android-Device-Hardware-Specs-Database
try {
InputStream is = getAssets().open("database.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
JSONObject db = new JSONObject(new String(buffer, StandardCharsets.UTF_8));
JSONObject board = db.getJSONObject(Build.BOARD);
cpu = board.get("CPU").toString();
soc = board.get("SoC").toString();
getChipset = true;
} catch (Exception e) {
cpu = getCpuName();
soc = Build.BOARD;
getChipset = false;
}
// Device Info
Device device = new Device();
device.setId(Build.ID);
@@ -218,8 +197,8 @@ public class Main extends AndroidApplication {
device.setBrand(Build.BRAND);
device.setManufacturer(Build.MANUFACTURER);
device.setMemorySize(memInfo.totalMem);
device.setCpuDescription(cpu);
device.setChipset(soc);
device.setCpuDescription(getCpuName());
device.setChipset(Build.HARDWARE + " " + Build.BOARD);
// OS Info
OperatingSystem os = new OperatingSystem();
os.setName("Android");
@@ -227,7 +206,7 @@ public class Main extends AndroidApplication {
os.setBuild(Build.DISPLAY);
os.setRawDescription(getAndroidOSName());
initForge(Gadapter, new HWInfo(device, os, getChipset), permissiongranted, totalMemory, isTabletDevice(getContext()));
initForge(Gadapter, new HWInfo(device, os), permissiongranted, totalMemory, isTabletDevice(getContext()));
}
private void crossfade(View contentView, View previousView) {

View File

@@ -68,7 +68,7 @@ public class AddBasicLandsDialog {
private static final int LAND_PANEL_PADDING = 3;
private final FComboBoxPanel<CardEdition> cbLandSet = new FComboBoxPanel<>(Localizer.getInstance().getMessage("lblLandSet") + ":", FlowLayout.CENTER,
IterableUtil.filter(StaticData.instance().getSortedEditions(), CardEdition::hasBasicLands));
IterableUtil.filter(StaticData.instance().getSortedEditions(), CardEdition.Predicates.hasBasicLands));
private final MainPanel panel = new MainPanel();
private final LandPanel pnlPlains = new LandPanel("Plains");

View File

@@ -46,6 +46,7 @@ import forge.toolbox.*;
import forge.util.Localizer;
import forge.view.FDialog;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;
import static forge.deck.DeckRecognizer.TokenType.*;
@@ -522,7 +523,7 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
else
deck.setName(currentDeckName);
}
host.getDeckController().loadDeck(deck, controller.getImportBehavior() != DeckImportController.ImportBehavior.MERGE);
host.getDeckController().loadDeck(deck, controller.getCreateNewDeck());
processWindowEvent(new WindowEvent(DeckImport.this, WindowEvent.WINDOW_CLOSING));
});
@@ -530,7 +531,7 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
this.createNewDeckCheckbox.setSelected(false);
this.createNewDeckCheckbox.addActionListener(e -> {
boolean createNewDeck = createNewDeckCheckbox.isSelected();
controller.setImportBehavior(createNewDeck ? DeckImportController.ImportBehavior.CREATE_NEW : DeckImportController.ImportBehavior.MERGE);
controller.setCreateNewDeck(createNewDeck);
String cmdAcceptLabel = createNewDeck ? CREATE_NEW_DECK_CMD_LABEL : IMPORT_CARDS_CMD_LABEL;
cmdAcceptButton.setText(cmdAcceptLabel);
String smartCardArtChboxTooltip = createNewDeck ? SMART_CARDART_TT_NO_DECK : SMART_CARDART_TT_WITH_DECK;
@@ -599,7 +600,7 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
if (token.getType() == LIMITED_CARD)
cssClass = WARN_MSG_CLASS;
String statusMsg = String.format("<span class=\"%s\" style=\"font-size: 9px;\">%s</span>", cssClass,
controller.getTokenStatusMessage(token));
getTokenStatusMessage(token));
statusLbl.append(statusMsg);
}
@@ -739,12 +740,12 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
private String toHTML(final DeckRecognizer.Token token) {
if (token == null)
return "";
String tokenMsg = controller.getTokenMessage(token);
String tokenMsg = getTokenMessage(token);
if (tokenMsg == null)
return "";
String tokenStatus = controller.getTokenStatusMessage(token);
String tokenStatus = getTokenStatusMessage(token);
String cssClass = getTokenCSSClass(token.getType());
if (tokenStatus.isEmpty())
if (tokenStatus.length() == 0)
tokenMsg = padEndWithHTMLSpaces(tokenMsg, 2*PADDING_TOKEN_MSG_LENGTH+10);
else {
tokenMsg = padEndWithHTMLSpaces(tokenMsg, PADDING_TOKEN_MSG_LENGTH);
@@ -754,6 +755,11 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
tokenMsg = String.format("<a class=\"%s\" href=\"%s\">%s</a>", cssClass,
token.getKey().toString(), tokenMsg);
if (tokenStatus == null) {
String tokenTag = String.format("<td colspan=\"2\" class=\"%s\">%s</td>", cssClass, tokenMsg);
return String.format("<tr>%s</tr>", tokenTag);
}
String tokenTag = "<td class=\"%s\">%s</td>";
String tokenMsgTag = String.format(tokenTag, cssClass, tokenMsg);
String tokenStatusTag;
@@ -770,6 +776,97 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
return String.format("%s%s", targetMsg, spacer);
}
private String getTokenMessage(DeckRecognizer.Token token) {
switch (token.getType()) {
case LEGAL_CARD:
case LIMITED_CARD:
case CARD_FROM_NOT_ALLOWED_SET:
case CARD_FROM_INVALID_SET:
return String.format("%s x %s %s", token.getQuantity(), token.getText(), getTokenFoilLabel(token));
// Card Warning Msgs
case UNKNOWN_CARD:
case UNSUPPORTED_CARD:
return token.getQuantity() > 0 ? String.format("%s x %s", token.getQuantity(), token.getText())
: token.getText();
case UNSUPPORTED_DECK_SECTION:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance()
.getMessage("lblWarnDeckSectionNotAllowedInEditor", token.getText(),
this.currentGameType));
// Special Case of Card moved into another section (e.g. Commander from Sideboard)
case WARNING_MESSAGE:
return String.format("%s: %s", Localizer.getInstance()
.getMessage("lblWarningMsgPrefix"), token.getText());
// Placeholders
case DECK_SECTION_NAME:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblDeckSection"),
token.getText());
case CARD_RARITY:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblRarity"),
token.getText());
case CARD_TYPE:
case CARD_CMC:
case MANA_COLOUR:
case COMMENT:
return token.getText();
case DECK_NAME:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblDeckName"),
token.getText());
case UNKNOWN_TEXT:
default:
return null;
}
}
private String getTokenStatusMessage(DeckRecognizer.Token token){
if (token == null)
return "";
switch (token.getType()) {
case LIMITED_CARD:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance().getMessage("lblWarnLimitedCard",
StringUtils.capitalize(token.getLimitedCardType().name()), getGameFormatLabel()));
case CARD_FROM_NOT_ALLOWED_SET:
return Localizer.getInstance().getMessage("lblErrNotAllowedCard", getGameFormatLabel());
case CARD_FROM_INVALID_SET:
return Localizer.getInstance().getMessage("lblErrCardEditionDate");
case UNSUPPORTED_CARD:
return Localizer.getInstance().getMessage("lblErrUnsupportedCard", this.currentGameType);
case UNKNOWN_CARD:
return String.format("%s: %s", Localizer.getInstance().getMessage("lblWarningMsgPrefix"),
Localizer.getInstance().getMessage("lblWarnUnknownCardMsg"));
case UNSUPPORTED_DECK_SECTION:
case WARNING_MESSAGE:
case COMMENT:
case CARD_CMC:
case MANA_COLOUR:
case CARD_TYPE:
case DECK_SECTION_NAME:
case CARD_RARITY:
case DECK_NAME:
case LEGAL_CARD:
case UNKNOWN_TEXT:
default:
return "";
}
}
private String getTokenCSSClass(DeckRecognizer.TokenType tokenType){
switch (tokenType){
case LEGAL_CARD:
@@ -802,6 +899,17 @@ public class DeckImport<TModel extends DeckBase> extends FDialog {
return "";
}
}
private String getTokenFoilLabel(DeckRecognizer.Token token) {
if (!token.isCardToken())
return "";
final String foilMarker = "- (Foil)";
return token.getCard().isFoil() ? foilMarker : "";
}
private String getGameFormatLabel() {
return String.format("\"%s\"", this.controller.getCurrentGameFormatName());
}
}
class GameFormatDropdownRenderer extends JLabel implements ListCellRenderer<GameFormat> {

View File

@@ -51,7 +51,7 @@ public class GameLauncher {
os.setBuild(si.getOperatingSystem().getVersionInfo().getBuildNumber());
os.setRawDescription(si.getOperatingSystem() + " x" + si.getOperatingSystem().getBitness());
totalRAM = Math.round(si.getHardware().getMemory().getTotal() / 1024f / 1024f);
hw = new HWInfo(device, os, false);
hw = new HWInfo(device, os);
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -43,7 +43,7 @@ public class EntryActor extends MapActor{
{
if(targetMap==null||targetMap.isEmpty())
{
stage.exitDungeon(false, false);
stage.exitDungeon(false);
}
else
{

View File

@@ -39,7 +39,7 @@ public class PortalActor extends EntryActor {
}
if (currentAnimationType == PortalAnimationTypes.Active) {
if (targetMap == null || targetMap.isEmpty()) {
stage.exitDungeon(false, false);
stage.exitDungeon(false);
} else {
if (targetMap.equals(currentMap)) {
stage.spawn(entryTargetObject);

View File

@@ -1077,25 +1077,6 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
return items.random();
}
public boolean hasEquippedItem() {
for (Long id : equippedItems.values()) {
ItemData item = getEquippedItem(id);
if (item == null)
continue;
if (isHardorInsaneDifficulty()) {
return true;
} else {
switch (item.equipmentSlot) {
// limit to these for easy and normal
case "Boots", "Body", "Neck" -> {
return true;
}
}
}
}
return false;
}
public ItemData getEquippedAbility1() {
for (Long id : equippedItems.values()) {
ItemData data = getEquippedItem(id);

View File

@@ -27,6 +27,8 @@ import forge.item.PaperCard;
import forge.itemmanager.*;
import forge.itemmanager.filters.CardColorFilter;
import forge.itemmanager.filters.CardTypeFilter;
import forge.localinstance.properties.ForgePreferences;
import forge.menu.FCheckBoxMenuItem;
import forge.menu.FDropDownMenu;
import forge.menu.FMenuItem;
import forge.menu.FPopupMenu;
@@ -39,7 +41,6 @@ import forge.util.Utils;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class AdventureDeckEditor extends FDeckEditor {
protected static class AdventureEditorConfig extends DeckEditorConfig {
@@ -145,8 +146,7 @@ public class AdventureDeckEditor extends FDeckEditor {
if(event.cardBlock != null) {
if(event.cardBlock.getLandSet() != null)
return List.of(event.cardBlock.getLandSet());
List<CardEdition> eventSets = new ArrayList<>(event.cardBlock.getSets());
eventSets.removeIf(Predicate.not(CardEdition::hasBasicLands));
List<CardEdition> eventSets = event.cardBlock.getSets();
if(!eventSets.isEmpty())
return eventSets;
}
@@ -558,7 +558,7 @@ public class AdventureDeckEditor extends FDeckEditor {
currentEvent.participants[i].setDeck(opponentDecks[i]);
}
currentEvent.draftedDeck = (Deck) currentEvent.registeredDeck.copyTo("Draft Deck");
if (allowAddBasic()) {
if (allowsAddBasic()) {
showAddBasicLandsDialog();
//Might be annoying if you haven't pruned your deck yet, but best to remind player that
//this probably needs to be done since it's there since it's not normally part of Adventure
@@ -713,6 +713,27 @@ public class AdventureDeckEditor extends FDeckEditor {
return this.deckHeader;
}
@Override
protected FPopupMenu createMoreOptionsMenu() {
return new FPopupMenu() {
@Override
protected void buildMenu() {
Localizer localizer = Forge.getLocalizer();
addItem(new FMenuItem(localizer.getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> FDeckViewer.copyDeckToClipboard(getDeck())));
if (allowsAddBasic()) {
FMenuItem addBasic = new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e1 -> showAddBasicLandsDialog());
addItem(addBasic);
}
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.DEV_MODE_ENABLED)) {
addItem(new FCheckBoxMenuItem(localizer.getMessage("cbEnforceDeckLegality"), shouldEnforceConformity(), e -> toggleConformity()));
String devSuffix = " (" + localizer.getMessage("lblDev") + ")";
addItem(new FMenuItem(localizer.getMessage("lblAddcard") + devSuffix, FSkinImage.HDPLUS, e -> showDevAddCardDialog()));
}
((DeckEditorPage) getSelectedPage()).buildDeckMenu(this);
}
};
}
@Override
protected void addChosenBasicLands(CardPool landsToAdd) {
if(isLimitedEditor())
@@ -744,12 +765,6 @@ public class AdventureDeckEditor extends FDeckEditor {
catalog.moveCards(landsToMove, getMainDeckPage());
}
@Override
protected PaperCard supplyPrintForImporter(PaperCard missingCard) {
PaperCard out = super.supplyPrintForImporter(missingCard);
return out == null ? null : out.getNoSellVersion();
}
@Override
protected void cacheTabPages() {
super.cacheTabPages();
@@ -760,9 +775,7 @@ public class AdventureDeckEditor extends FDeckEditor {
}
@Override
protected boolean allowAddBasic() {
if(getEditorConfig() instanceof DeckPreviewConfig)
return false;
protected boolean allowsAddBasic() {
AdventureEventData currentEvent = getCurrentEvent();
if (currentEvent == null)
return true;

View File

@@ -157,7 +157,7 @@ public class ArenaScene extends UIScene implements IAfterMatch {
}
@Override
public void setWinner(boolean winner, boolean isArena) {
public void setWinner(boolean winner) {
enable = false;
Array<ArenaRecord> winners = new Array<>();
Array<EnemySprite> winnersEnemies = new Array<>();

View File

@@ -142,7 +142,7 @@ public class DuelScene extends ForgeScene {
Current.player().getStatistic().setResult(enemyName, winner);
if (last instanceof IAfterMatch) {
((IAfterMatch) last).setWinner(winner, isArena);
((IAfterMatch) last).setWinner(winner);
}
});
}

View File

@@ -541,7 +541,7 @@ public class EventScene extends MenuScene implements IAfterMatch {
AdventureEventData.AdventureEventMatch humanMatch = null;
public void setWinner(boolean winner, boolean isArena) {
public void setWinner(boolean winner) {
if (winner) {
humanMatch.winner = humanMatch.p1;
humanMatch.p1.wins++;

View File

@@ -181,8 +181,8 @@ public abstract class HudScene extends Scene implements InputProcessor, IAfterMa
}
@Override
public void setWinner(boolean winner, boolean isArena) {
stage.setWinner(winner, isArena);
public void setWinner(boolean winner) {
stage.setWinner(winner);
}
public boolean isInHudOnlyMode() {

View File

@@ -203,7 +203,7 @@ public class ConsoleCommandInterpreter {
});
registerCommand(new String[]{"leave"}, s -> {
if (!MapStage.getInstance().isInMap()) return "not on a map";
MapStage.getInstance().exitDungeon(false, false);
MapStage.getInstance().exitDungeon(false);
return "Got out";
});
registerCommand(new String[]{"debug", "collision"}, s -> {

View File

@@ -893,7 +893,7 @@ public class GameHUD extends Stage {
@Override
public boolean act(float v) {
if (exitDungeon) {
MapStage.getInstance().exitDungeon(false, false);
MapStage.getInstance().exitDungeon(false);
setDisabled(exitToWorldMapActor, true, "[%120][+ExitToWorldMap]", "\u2613");
setDisabled(bookmarkActor, true, "[%120][+Bookmark]", "\u2613");
}

View File

@@ -74,8 +74,8 @@ public abstract class GameStage extends Stage {
private float touchY = -1;
private final float timer = 0;
private float animationTimeout = 0;
public static float maximumScrollDistance = 1.5f;
public static float minimumScrollDistance = 0.3f;
public static float maximumScrollDistance=1.5f;
public static float minimumScrollDistance=0.3f;
private String extraAnnouncement = "";
@@ -97,9 +97,8 @@ public abstract class GameStage extends Stage {
public void setDialogStage(Stage dialogStage) {
this.dialogStage = dialogStage;
}
public void showDialog() {
if (dialogStage == null) {
if (dialogStage == null){
setDialogStage(GameHUD.getInstance());
}
GameHUD.getInstance().playerIdle();
@@ -124,7 +123,6 @@ public abstract class GameStage extends Stage {
/**
* Triggered when the hud is showing a dialog, which is tracked separately
*
* @param isShowing Whether a dialog is currently showing
*/
public void hudIsShowingDialog(boolean isShowing) {
@@ -250,21 +248,24 @@ public abstract class GameStage extends Stage {
showDialog();
}
public boolean axisMoved(Controller controller, int axisIndex, float value) {
if (MapStage.getInstance().isDialogOnlyInput() || isPaused()) {
if (MapStage.getInstance().isDialogOnlyInput()||isPaused()) {
return true;
}
player.getMovementDirection().x = controller.getAxis(0);
player.getMovementDirection().y = -controller.getAxis(1);
if (player.getMovementDirection().len() < 0.2) {
if(player.getMovementDirection().len()<0.2)
{
player.stop();
}
return true;
}
enum PlayerModification {
enum PlayerModification
{
Sprint,
Hide,
Fly
@@ -272,32 +273,29 @@ public abstract class GameStage extends Stage {
}
HashMap<PlayerModification, Float> currentModifications = new HashMap<>();
public void modifyPlayer(PlayerModification mod, float value) {
float currentValue = 0;
if (currentModifications.containsKey(mod)) {
currentValue = currentModifications.get(mod);
HashMap<PlayerModification,Float> currentModifications=new HashMap<>();
public void modifyPlayer(PlayerModification mod,float value) {
float currentValue=0;
if(currentModifications.containsKey(mod))
{
currentValue=currentModifications.get(mod);
}
currentModifications.put(mod, currentValue + value);
currentModifications.put(mod,currentValue+value);
}
public void flyFor(float value) {
modifyPlayer(PlayerModification.Fly, value);
modifyPlayer(PlayerModification.Fly,value);
player.playEffect(Paths.EFFECT_FLY);
}
public void hideFor(float value) {
modifyPlayer(PlayerModification.Hide, value);
player.setColor(player.getColor().r, player.getColor().g, player.getColor().b, 0.5f);
modifyPlayer(PlayerModification.Hide,value);
player.setColor(player.getColor().r,player.getColor().g,player.getColor().b,0.5f);
player.playEffect(Paths.EFFECT_HIDE);
}
public void sprintFor(float value) {
modifyPlayer(PlayerModification.Sprint, value);
modifyPlayer(PlayerModification.Sprint,value);
player.playEffect(Paths.EFFECT_SPRINT);
}
public void startPause(float i) {
startPause(i, null);
}
@@ -307,7 +305,6 @@ public abstract class GameStage extends Stage {
animationTimeout = i;
player.setMovementDirection(Vector2.Zero);
}
public boolean isPaused() {
return animationTimeout > 0;
}
@@ -333,7 +330,7 @@ public abstract class GameStage extends Stage {
dialog = Controls.newDialog("");
}
public void setWinner(boolean b, boolean a) {
public void setWinner(boolean b) {
}
public void setBounds(float width, float height) {
@@ -368,13 +365,15 @@ public abstract class GameStage extends Stage {
animationTimeout -= delta;
return;
}
Array<PlayerModification> modsToRemove = new Array<>();
for (Map.Entry<PlayerModification, Float> mod : currentModifications.entrySet()) {
mod.setValue(mod.getValue() - delta);
if (mod.getValue() < 0)
Array<PlayerModification> modsToRemove=new Array<>();
for(Map.Entry<PlayerModification, Float> mod:currentModifications.entrySet())
{
mod.setValue(mod.getValue()-delta);
if(mod.getValue()<0)
modsToRemove.add(mod.getKey());
}
for (PlayerModification mod : modsToRemove) {
for(PlayerModification mod:modsToRemove)
{
currentModifications.remove(mod);
onRemoveEffect(mod);
}
@@ -409,9 +408,10 @@ public abstract class GameStage extends Stage {
}
private void onRemoveEffect(PlayerModification mod) {
switch (mod) {
switch (mod)
{
case Hide:
player.setColor(player.getColor().r, player.getColor().g, player.getColor().b, 1f);
player.setColor(player.getColor().r,player.getColor().g,player.getColor().b,1f);
break;
case Fly:
player.removeEffect(Paths.EFFECT_FLY);
@@ -430,16 +430,20 @@ public abstract class GameStage extends Stage {
super.keyDown(keycode);
if (isPaused())
return true;
if (KeyBinding.Left.isPressed(keycode)) {
if (KeyBinding.Left.isPressed(keycode))
{
player.getMovementDirection().x = -1;
}
if (KeyBinding.Right.isPressed(keycode)) {
if (KeyBinding.Right.isPressed(keycode) )
{
player.getMovementDirection().x = +1;
}
if (KeyBinding.Up.isPressed(keycode)) {
if (KeyBinding.Up.isPressed(keycode))
{
player.getMovementDirection().y = +1;
}
if (KeyBinding.Down.isPressed(keycode)) {
if (KeyBinding.Down.isPressed(keycode))
{
player.getMovementDirection().y = -1;
}
if (keycode == Input.Keys.F5)//todo config
@@ -479,8 +483,9 @@ public abstract class GameStage extends Stage {
if (GameHUD.getInstance().isDebugMap()) {
TileMapScene S = TileMapScene.instance();
PointOfInterestData P = PointOfInterestData.getPointOfInterest("DEBUGZONE");
if (P != null) {
PointOfInterest PoI = new PointOfInterest(P, new Vector2(0, 0), MyRandom.getRandom());
if( P != null)
{
PointOfInterest PoI = new PointOfInterest(P,new Vector2(0,0), MyRandom.getRandom());
S.load(PoI);
Forge.switchScene(S);
}
@@ -566,12 +571,14 @@ public abstract class GameStage extends Stage {
public boolean keyUp(int keycode) {
if (isPaused())
return true;
if (KeyBinding.Left.isPressed(keycode) || KeyBinding.Right.isPressed(keycode)) {
if (KeyBinding.Left.isPressed(keycode)||KeyBinding.Right.isPressed(keycode))
{
player.getMovementDirection().x = 0;
if (!player.isMoving())
stop();
}
if (KeyBinding.Down.isPressed(keycode) || KeyBinding.Up.isPressed(keycode)) {
if (KeyBinding.Down.isPressed(keycode)||KeyBinding.Up.isPressed(keycode))
{
player.getMovementDirection().y = 0;
if (!player.isMoving())
stop();
@@ -651,16 +658,17 @@ public abstract class GameStage extends Stage {
return Vector2.Zero.cpy();
}
protected void teleported(Vector2 position) {
protected void teleported(Vector2 position)
{
}
public void setPosition(Vector2 position) {
getPlayerSprite().setPosition(position);
teleported(position);
}
public void resetPlayerLocation() {
public void resetPlayerLocation()
{
PointOfInterest poi = Current.world().findPointsOfInterest("Spawn");
if (poi != null) {
Forge.advFreezePlayerControls = true;
@@ -669,7 +677,7 @@ public abstract class GameStage extends Stage {
Timer.schedule(new Timer.Task() {
@Override
public void run() {
showImageDialog(Current.generateDefeatMessage(true), getDefeatBadge(),
showImageDialog(Current.generateDefeatMessage(), getDefeatBadge(),
() -> FThreads.invokeInEdtNowOrLater(() -> Forge.setTransitionScreen(new CoverScreen(() -> {
Forge.advFreezePlayerControls = false;
WorldStage.getInstance().setPosition(new Vector2(poi.getPosition().x - 16f, poi.getPosition().y + 16f));
@@ -681,21 +689,6 @@ public abstract class GameStage extends Stage {
}, 1f);
}//Spawn shouldn't be null
}
public void defeatedFromBoss() {
if (!Current.player().hasEquippedItem())
return;
Forge.advFreezePlayerControls = true;
getPlayerSprite().setAnimation(CharacterSprite.AnimationTypes.Hit);
getPlayerSprite().playEffect(Paths.EFFECT_BLOOD, 0.5f);
Timer.schedule(new Timer.Task() {
@Override
public void run() {
showImageDialog(Current.generateDefeatMessage(false), getDefeatBadge(), () -> Forge.advFreezePlayerControls = false);
}
}, 1f);
}
private FBufferedImage getDefeatBadge() {
FileHandle defeat = Config.instance().getFile("ui/defeat.png");
if (defeat.exists()) {

View File

@@ -1,5 +1,5 @@
package forge.adventure.stage;
public interface IAfterMatch {
void setWinner(boolean winner, boolean isArena);
void setWinner(boolean winner);
}

View File

@@ -606,7 +606,7 @@ public class MapStage extends GameStage {
}));
break;
case "exit":
addMapActor(obj, new OnCollide(() -> MapStage.this.exitDungeon(false, false)));
addMapActor(obj, new OnCollide(() -> MapStage.this.exitDungeon(false)));
break;
case "dialog":
if (obj instanceof TiledMapTileMapObject) {
@@ -750,7 +750,7 @@ public class MapStage extends GameStage {
}
}
public boolean exitDungeon(boolean defeated, boolean defeatedByBoss) {
public boolean exitDungeon(boolean defeated) {
AdventureQuestController.instance().updateQuestsLeave();
clearIsInMap();
AdventureQuestController.instance().showQuestDialogs(this);
@@ -758,8 +758,6 @@ public class MapStage extends GameStage {
effect = null; //Reset dungeon effects.
if (defeated)
WorldStage.getInstance().resetPlayerLocation();
else if (defeatedByBoss)
WorldStage.getInstance().defeatedFromBoss();
Forge.switchScene(GameScene.instance());
isPlayerLeavingDungeon = false;
dialogOnlyInput = false;
@@ -768,7 +766,7 @@ public class MapStage extends GameStage {
@Override
public void setWinner(boolean playerWins, boolean isArena) {
public void setWinner(boolean playerWins) {
isLoadingMatch = false;
freezeAllEnemyBehaviors = true;
if (playerWins) {
@@ -807,16 +805,16 @@ public class MapStage extends GameStage {
boolean defeated = Current.player().defeated();
//If hardcore mode is added, check and redirect to game over screen here
if (canFailDungeon && !defeated)
dungeonFailedDialog(true, currentMob.getData().boss && !isArena);
dungeonFailedDialog(true);
else
exitDungeon(defeated, currentMob.getData().boss && !isArena);
exitDungeon(defeated);
MapStage.this.stop();
currentMob = null;
});
}
}
private void dungeonFailedDialog(boolean exit, boolean defeatedByBoss) {
private void dungeonFailedDialog(boolean exit) {
dialog.getButtonTable().clear();
dialog.getContentTable().clear();
dialog.clearListeners();
@@ -836,7 +834,7 @@ public class MapStage extends GameStage {
L.skipToTheEnd();
super.clicked(event, x, y);
if (exit)
exitDungeon(false, defeatedByBoss);
exitDungeon(false);
}
});
dialog.getButtonTable().add(ok).width(240f);

View File

@@ -162,7 +162,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
}
@Override
public void setWinner(boolean playerIsWinner, boolean isArena) {
public void setWinner(boolean playerIsWinner) {
if (playerIsWinner) {
currentMob.clearCollisionHeight();
Current.player().win();
@@ -192,13 +192,10 @@ public class WorldStage extends GameStage implements SaveFileContent {
boolean defeated = Current.player().defeated();
AdventureQuestController.instance().updateQuestsLose(currentMob);
AdventureQuestController.instance().showQuestDialogs(MapStage.getInstance());
boolean defeatedFromBoss = currentMob.getData().boss && !isArena;
WorldStage.this.removeEnemy(currentMob);
currentMob = null;
if (defeated) {
WorldStage.getInstance().resetPlayerLocation();
} else if (defeatedFromBoss) {
WorldStage.getInstance().defeatedFromBoss();
}
});
}

View File

@@ -809,11 +809,6 @@ public class CardUtil {
return generateBoosterPackAsDeck(edition);
}
private static PaperCard getReplacement(String missingCard, String replacementCard) {
System.err.println(missingCard + " : Not found in the database.\nReplacement card: " + replacementCard);
return FModel.getMagicDb().getCommonCards().getCard(replacementCard);
}
public static PaperCard getCardByName(String cardName) {
List<PaperCard> validCards;
//Faster to ask the CardDB for a card name than it is to search the pool.
@@ -822,10 +817,6 @@ public class CardUtil {
else
validCards = FModel.getMagicDb().getCommonCards().getUniqueCardsNoAlt(cardName);
if (validCards.isEmpty()) {
return getReplacement(cardName, "Wastes");
}
return validCards.get(Current.world().getRandom().nextInt(validCards.size()));
}
@@ -837,7 +828,7 @@ public class CardUtil {
.filter(input -> input.getEdition().equals(edition)).collect(Collectors.toList());
if (validCards.isEmpty()) {
System.err.println("Unexpected behavior: tried to call getCardByNameAndEdition for card " + cardName + " from the edition " + edition + ", but didn't find it in the DB. A random existing instance will be returned if found.");
System.err.println("Unexpected behavior: tried to call getCardByNameAndEdition for card " + cardName + " from the edition " + edition + ", but didn't find it in the DB. A random existing instance will be returned.");
return getCardByName(cardName);
}

View File

@@ -27,9 +27,8 @@ public class Current {
public static void setLatestDeck(Deck generateDeck) {
deck=generateDeck;
}
public static String generateDefeatMessage(boolean hasDied) {
String key = hasDied ? "lblYouDied" : "lblYouLostTheLastGame";
String message = Forge.getLocalizer().getMessage(key, player().getName());
public static String generateDefeatMessage() {
String message = Forge.getLocalizer().getMessage("lblYouDied", player().getName());
ItemData itemData = player().getRandomEquippedItem();
if (itemData != null) {
itemData.isCracked = true;

View File

@@ -20,7 +20,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
captureException(e, key, subData);
}
}
@@ -34,7 +33,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -47,7 +45,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -60,7 +57,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -73,7 +69,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -86,7 +81,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -101,7 +95,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
stream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -114,7 +107,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
captureException(e, key, subData);
}
}
@@ -127,7 +119,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
captureException(e, key, subData);
}
}
@@ -141,7 +132,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}
@@ -157,7 +147,6 @@ public class SaveFileData extends HashMap<String, byte[]> {
objStream.flush();
put(key, stream.toByteArray());
} catch (IOException e) {
put("IOException", e.toString().getBytes());
e.printStackTrace();
}
}

View File

@@ -26,50 +26,53 @@ import java.util.zip.InflaterInputStream;
/**
* Represents everything that will be saved, like the player and the world.
*/
public class WorldSave {
public class WorldSave {
static final public int AUTO_SAVE_SLOT = -1;
static final public int QUICK_SAVE_SLOT = -2;
static final public int INVALID_SAVE_SLOT = -3;
static final WorldSave currentSave = new WorldSave();
static final public int AUTO_SAVE_SLOT =-1;
static final public int QUICK_SAVE_SLOT =-2;
static final public int INVALID_SAVE_SLOT =-3;
static final WorldSave currentSave=new WorldSave();
public WorldSaveHeader header = new WorldSaveHeader();
private final AdventurePlayer player = new AdventurePlayer();
private final World world = new World();
private final PointOfInterestChanges.Map pointOfInterestChanges = new PointOfInterestChanges.Map();
private final AdventurePlayer player=new AdventurePlayer();
private final World world=new World();
private final PointOfInterestChanges.Map pointOfInterestChanges= new PointOfInterestChanges.Map();
private final SignalList onLoadList = new SignalList();
private final SignalList onLoadList=new SignalList();
public final World getWorld() {
public final World getWorld()
{
return world;
}
public AdventurePlayer getPlayer() {
public AdventurePlayer getPlayer()
{
return player;
}
public void onLoad(Runnable run) {
public void onLoad(Runnable run)
{
onLoadList.add(run);
}
public PointOfInterestChanges getPointOfInterestChanges(String id) {
if (!pointOfInterestChanges.containsKey(id))
pointOfInterestChanges.put(id, new PointOfInterestChanges());
public PointOfInterestChanges getPointOfInterestChanges(String id)
{
if(!pointOfInterestChanges.containsKey(id))
pointOfInterestChanges.put(id,new PointOfInterestChanges());
return pointOfInterestChanges.get(id);
}
static public boolean load(int currentSlot) {
String fileName = WorldSave.getSaveFile(currentSlot);
if (!new File(fileName).exists())
if(!new File(fileName).exists())
return false;
new File(getSaveDir()).mkdirs();
try {
try (FileInputStream fos = new FileInputStream(fileName);
InflaterInputStream inf = new InflaterInputStream(fos);
ObjectInputStream oos = new ObjectInputStream(inf)) {
try(FileInputStream fos = new FileInputStream(fileName);
InflaterInputStream inf = new InflaterInputStream(fos);
ObjectInputStream oos = new ObjectInputStream(inf))
{
currentSave.header = (WorldSaveHeader) oos.readObject();
SaveFileData mainData = (SaveFileData) oos.readObject();
SaveFileData mainData=(SaveFileData)oos.readObject();
currentSave.player.load(mainData.readSubData("player"));
GamePlayerUtil.getGuiPlayer().setName(currentSave.player.getName());
try {
@@ -92,11 +95,9 @@ public class WorldSave {
}
return true;
}
public static boolean isSafeFile(String name) {
return filenameToSlot(name) != INVALID_SAVE_SLOT;
return filenameToSlot(name)!= INVALID_SAVE_SLOT;
}
static public int filenameToSlot(String name) {
if (name.equals("auto_save.sav"))
return AUTO_SAVE_SLOT;
@@ -130,10 +131,10 @@ public class WorldSave {
public static WorldSave generateNewWorld(String name, boolean male, int race, int avatarIndex, ColorSet startingColorIdentity, DifficultyData diff, AdventureModes mode, int customDeckIndex, CardEdition starterEdition, long seed) {
currentSave.world.generateNew(seed);
currentSave.pointOfInterestChanges.clear();
boolean chaos = mode == AdventureModes.Chaos;
boolean custom = mode == AdventureModes.Custom;
Deck starterDeck = Config.instance().starterDeck(startingColorIdentity, diff, mode, customDeckIndex, starterEdition);
currentSave.player.create(name, starterDeck, male, race, avatarIndex, chaos, custom, diff);
boolean chaos=mode==AdventureModes.Chaos;
boolean custom=mode==AdventureModes.Custom;
Deck starterDeck = Config.instance().starterDeck(startingColorIdentity,diff,mode,customDeckIndex,starterEdition);
currentSave.player.create(name, starterDeck, male, race, avatarIndex, chaos, custom, diff);
currentSave.player.setWorldPosY((int) (currentSave.world.getData().playerStartPosY * currentSave.world.getData().height * currentSave.world.getTileSize()));
currentSave.player.setWorldPosX((int) (currentSave.world.getData().playerStartPosX * currentSave.world.getData().width * currentSave.world.getTileSize()));
currentSave.onLoadList.emit();
@@ -141,103 +142,46 @@ public class WorldSave {
}
public boolean autoSave() {
return save("auto save" + SaveLoadScene.instance().getSaveFileSuffix(), AUTO_SAVE_SLOT);
return save("auto save"+ SaveLoadScene.instance().getSaveFileSuffix(),AUTO_SAVE_SLOT);
}
public boolean quickSave() {
return save("quick save" + SaveLoadScene.instance().getSaveFileSuffix(), QUICK_SAVE_SLOT);
return save("quick save"+ SaveLoadScene.instance().getSaveFileSuffix(),QUICK_SAVE_SLOT);
}
public boolean quickLoad() {
return load(QUICK_SAVE_SLOT);
}
public boolean save(String text, int currentSlot) {
header.name = text;
String fileName = WorldSave.getSaveFile(currentSlot);
String oldFileName = fileName.replace(".sav", ".old");
new File(getSaveDir()).mkdirs();
File currentFile = new File(fileName);
File backupFile = new File(oldFileName);
if (currentFile.exists())
currentFile.renameTo(backupFile);
try {
try (FileOutputStream fos = new FileOutputStream(fileName);
DeflaterOutputStream def = new DeflaterOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(def)) {
SaveFileData player = currentSave.player.save();
SaveFileData world = currentSave.world.save();
SaveFileData worldStage = WorldStage.getInstance().save();
SaveFileData poiChanges = currentSave.pointOfInterestChanges.save();
String message = getExceptionMessage(player, world, worldStage, poiChanges);
if (!message.isEmpty()) {
oos.close();
fos.close();
restoreBackup(oldFileName, fileName);
announceError(message);
return true;
}
SaveFileData mainData = new SaveFileData();
mainData.store("player", player);
mainData.store("world", world);
mainData.store("worldStage", worldStage);
mainData.store("pointOfInterestChanges", poiChanges);
if (mainData.readString("IOException") != null) {
oos.close();
fos.close();
restoreBackup(oldFileName, fileName);
announceError("Please check forge.log for errors.");
return true;
}
header.saveDate = new Date();
try(FileOutputStream fos = new FileOutputStream(fileName);
DeflaterOutputStream def= new DeflaterOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(def))
{
header.saveDate= new Date();
oos.writeObject(header);
SaveFileData mainData=new SaveFileData();
mainData.store("player",currentSave.player.save());
mainData.store("world",currentSave.world.save());
mainData.store("worldStage", WorldStage.getInstance().save());
mainData.store("pointOfInterestChanges",currentSave.pointOfInterestChanges.save());
oos.writeObject(mainData);
}
} catch (IOException e) {
restoreBackup(oldFileName, fileName);
announceError("Please check forge.log for errors.");
return true;
e.printStackTrace();
return false;
}
Config.instance().getSettingData().lastActiveSave = WorldSave.filename(currentSlot);
Config.instance().saveSettings();
if (backupFile.exists())
backupFile.delete();
return true;
}
public void restoreBackup(String oldFilename, String currentFilename) {
File f = new File(currentFilename);
if (f.exists())
f.delete();
File b = new File(oldFilename);
if (b.exists())
b.renameTo(new File(currentFilename));
}
public String getExceptionMessage(SaveFileData... datas) {
StringBuilder message = new StringBuilder();
for (SaveFileData data : datas) {
String s = data.readString("IOException");
if (s != null)
message.append(s).append("\n");
}
return message.toString();
}
private void announceError(String message) {
currentSave.player.getCurrentGameStage().setExtraAnnouncement("Error Saving File!\n" + message);
}
public void clearChanges() {
pointOfInterestChanges.clear();
}

View File

@@ -59,7 +59,7 @@ public class AddBasicLandsDialog extends FDialog {
private final Consumer<CardPool> callback;
private final FLabel lblLandSet = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblLandSet") + ":").font(FSkinFont.get(12)).textColor(FLabel.getInlineLabelColor()).build());
private final FComboBox<CardEdition> cbLandSet = add(new FComboBox<>(IterableUtil.filter(StaticData.instance().getSortedEditions(), CardEdition::hasBasicLands)));
private final FComboBox<CardEdition> cbLandSet = add(new FComboBox<>(IterableUtil.filter(StaticData.instance().getEditions(), CardEdition.Predicates.hasBasicLands)));
private final FScrollPane scroller = add(new FScrollPane() {
@Override

View File

@@ -81,21 +81,13 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
public boolean allowsCardReplacement() { return hasInfiniteCardPool() || usePlayerInventory(); }
public List<CardEdition> getBasicLandSets(Deck currentDeck) {
if(hasInfiniteCardPool())
return FModel.getMagicDb().getSortedEditions().stream().filter(CardEdition::hasBasicLands).collect(Collectors.toList());
return List.of(DeckProxy.getDefaultLandSet(currentDeck));
}
protected abstract IDeckController getController();
protected abstract DeckEditorPage[] getInitialPages();
public DeckSection[] getPrimarySections() {
if(getGameType() != null)
return getGameType().getPrimaryDeckSections().toArray(new DeckSection[0]);
return new DeckSection[]{DeckSection.Main, DeckSection.Sideboard};
}
public DeckSection[] getExtraSections() {
protected DeckSection[] getExtraSections() {
if(getGameType() != null)
return getGameType().getSupplimentalDeckSections().toArray(new DeckSection[0]);
return new DeckSection[]{DeckSection.Attractions, DeckSection.Contraptions};
@@ -152,7 +144,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
ItemManagerConfig catalogConfig = null;
ItemManagerConfig mainSectionConfig = null;
ItemManagerConfig sideboardConfig = null;
Function<Deck, Collection<CardEdition>> fnGetBasicLandSet = null;
Function<Deck, CardEdition> fnGetBasicLandSet = null;
Supplier<ItemPool<PaperCard>> itemPoolSupplier = null;
String catalogCaption = null;
@@ -204,7 +196,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
this.sideboardConfig = sideboardConfig;
return this;
}
public GameTypeDeckEditorConfig setBasicLandSetFunction(Function<Deck, Collection<CardEdition>> fnGetBasicLandSet) {
public GameTypeDeckEditorConfig setBasicLandSetFunction(Function<Deck, CardEdition> fnGetBasicLandSet) {
this.fnGetBasicLandSet = fnGetBasicLandSet;
return this;
}
@@ -304,21 +296,9 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
}
@Override
public DeckSection[] getPrimarySections() {
return gameType.getPrimaryDeckSections().toArray(new DeckSection[0]);
}
@Override
public DeckSection[] getExtraSections() {
protected DeckSection[] getExtraSections() {
return gameType.getSupplimentalDeckSections().toArray(new DeckSection[0]);
}
@Override
public List<CardEdition> getBasicLandSets(Deck currentDeck) {
if(this.fnGetBasicLandSet != null)
return List.copyOf(fnGetBasicLandSet.apply(currentDeck));
return super.getBasicLandSets(currentDeck);
}
}
public static DeckEditorConfig EditorConfigConstructed = new GameTypeDeckEditorConfig(GameType.Constructed,
@@ -368,19 +348,18 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
.setMainSectionConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setSideboardConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setPlayerInventorySupplier(() -> FModel.getQuest().getCards().getCardpool())
.setBasicLandSetFunction(d -> FModel.getQuest().getAvailableLandSets());
.setBasicLandSetFunction(d -> FModel.getQuest().getDefaultLandSet());
public static DeckEditorConfig EditorConfigQuestCommander = new GameTypeDeckEditorConfig(GameType.QuestCommander, DECK_CONTROLLER_QUEST)
.setCatalogConfig(ItemManagerConfig.QUEST_EDITOR_POOL)
.setMainSectionConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setSideboardConfig(ItemManagerConfig.QUEST_DECK_EDITOR)
.setPlayerInventorySupplier(() -> FModel.getQuest().getCards().getCardpool())
.setBasicLandSetFunction(d -> FModel.getQuest().getAvailableLandSets());
.setBasicLandSetFunction(d -> FModel.getQuest().getDefaultLandSet());
public static DeckEditorConfig EditorConfigQuestDraft = new GameTypeDeckEditorConfig(GameType.QuestDraft, DECK_CONTROLLER_QUEST_DRAFT);
public static DeckEditorConfig EditorConfigPlanarConquest = new GameTypeDeckEditorConfig(GameType.PlanarConquest, DECK_CONTROLLER_PLANAR_CONQUEST)
.setCatalogConfig(ItemManagerConfig.CONQUEST_COLLECTION)
.setMainSectionConfig(ItemManagerConfig.CONQUEST_DECK_EDITOR)
.setPlayerInventorySupplier(ConquestUtil::getAvailablePool)
.setBasicLandSetFunction(ConquestUtil::getBasicLandSets);
.setPlayerInventorySupplier(ConquestUtil::getAvailablePool);
protected static DeckSectionPage createPageForExtraSection(DeckSection deckSection, DeckEditorConfig editorConfig) {
CardManager cm = new CardManager(false);
@@ -563,7 +542,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
@Override
protected void buildMenu() {
final Localizer localizer = Forge.getLocalizer();
if (allowAddBasic())
if (allowsAddBasic())
addItem(new FMenuItem(localizer.getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e -> showAddBasicLandsDialog()));
if (showAddExtraSectionOption()) {
addItem(new FMenuItem(localizer.getMessage("lblAddDeckSection"), FSkinImage.CHAOS, e -> {
@@ -579,41 +558,28 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
});
}));
}
if (editorConfig.hasInfiniteCardPool() || editorConfig.usePlayerInventory()) {
if (editorConfig.getGameType() != null && editorConfig.hasInfiniteCardPool()) {
addItem(new FMenuItem(localizer.getMessage("lblImportFromClipboard"), Forge.hdbuttons ? FSkinImage.HDIMPORT : FSkinImage.OPEN, e -> {
FDeckImportDialog dialog = new FDeckImportDialog(deck, FDeckEditor.this.editorConfig);
if(editorConfig.usePlayerInventory())
dialog.setFreePrintConverter(FDeckEditor.this::supplyPrintForImporter);
dialog.setImportBannedCards(!FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY));
FDeckImportDialog dialog = new FDeckImportDialog(!deck.isEmpty(), FDeckEditor.this.editorConfig);
dialog.setCallback(importedDeck -> {
if (deck != null && importedDeck.hasName()) {
deck.setName(importedDeck.getName());
setHeaderText(importedDeck.getName());
}
switch (dialog.getImportBehavior()) {
case REPLACE_CURRENT:
for(DeckSectionPage page : pagesBySection.values()) {
if(importedDeck.has(page.deckSection)) {
page.setCards(importedDeck.get(page.deckSection));
if(hiddenExtraSections.contains(page.deckSection))
showExtraSectionTab(page.deckSection);
}
else
page.setCards(new CardPool());
}
break;
case CREATE_NEW:
deckController.setDeck(importedDeck);
break;
case MERGE:
for (Entry<DeckSection, CardPool> section : importedDeck) {
DeckSectionPage page = getPageForSection(section.getKey());
if (page != null)
page.addCards(section.getValue());
}
if (dialog.createNewDeck()) {
for (Entry<DeckSection, CardPool> section : importedDeck) {
DeckSectionPage page = getPageForSection(section.getKey());
if (page != null)
page.setCards(section.getValue());
}
} else {
for (Entry<DeckSection, CardPool> section : importedDeck) {
DeckSectionPage page = getPageForSection(section.getKey());
if (page != null)
page.addCards(section.getValue());
}
}
});
dialog.initParse();
dialog.show();
setSelectedPage(getMainDeckPage()); //select main deck page if needed so main deck if visible below dialog
}));
@@ -677,20 +643,6 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
getMainDeckPage().addCards(landsToAdd);
}
/**
* If a card is missing from a player's inventory while importing a deck, it gets run through here.
* Returning a PaperCard will let unlimited copies of that card be used as a substitute. Returning null
* will leave the card missing from the import.
*/
protected PaperCard supplyPrintForImporter(PaperCard missingCard) {
//Could support dungeons here too? Not that we really use them in the editor...
if(!missingCard.isVeryBasicLand())
return null;
List<CardEdition> basicSets = editorConfig.getBasicLandSets(deck);
String setCode = basicSets.isEmpty() ? "JMP" : basicSets.get(0).getCode();
return FModel.getMagicDb().fetchCard(missingCard.getCardName(), setCode, null);
}
protected boolean shouldEnforceConformity() {
if(FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY))
return true;
@@ -743,9 +695,6 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
showExtraSectionTab(section);
if(pagesBySection.containsKey(section))
setSelectedPage(pagesBySection.get(section));
else if(section == DeckSection.Main && pagesBySection.containsKey(mainDeckPage.deckSection))
//Tried to switch to the Main page in a Planar or Scheme deck.
setSelectedPage(pagesBySection.get(mainDeckPage.deckSection));
}
public void notifyNewControllerModel() {
@@ -1078,7 +1027,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
protected boolean allowSaveAs() {
return allowSave() && allowRename();
}
protected boolean allowAddBasic() {
protected boolean allowsAddBasic() {
return !isDrafting();
}

View File

@@ -18,12 +18,11 @@
package forge.deck;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import forge.Forge;
import forge.Graphics;
@@ -32,15 +31,11 @@ import forge.deck.DeckRecognizer.TokenType;
import forge.game.GameType;
import forge.gui.FThreads;
import forge.gui.util.SOptionPane;
import forge.item.PaperCard;
import forge.toolbox.FCheckBox;
import forge.toolbox.FComboBox;
import forge.toolbox.FDialog;
import forge.toolbox.FOptionPane;
import forge.toolbox.FTextArea;
import forge.util.ItemPool;
import forge.util.Localizer;
import org.apache.commons.lang3.StringUtils;
public class FDeckImportDialog extends FDialog {
@@ -50,7 +45,7 @@ public class FDeckImportDialog extends FDialog {
private final FCheckBox newEditionCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblImportLatestVersionCard"), false));
private final FCheckBox dateTimeCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblUseOnlySetsReleasedBefore"), false));
private final FCheckBox smartCardArtCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblUseSmartCardArt"), false));
private final FCheckBox createNewDeckCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblReplaceDeckCheckbox"), false));
private final FCheckBox createNewDeckCheck = add(new FCheckBox(Forge.getLocalizer().getMessage("lblNewDeckCheckbox"), false));
// private final FCheckBox importInDeck = add(new FCheckBox()
/*setting onlyCoreExpCheck to false allow the copied cards to pass the check of deck contents
forge-core\src\main\java\forge\deck\Deck.javaDeck.java starting @ Line 320 which is called by
@@ -62,60 +57,100 @@ public class FDeckImportDialog extends FDialog {
private final FComboBox<String> monthDropdown = add(new FComboBox<>()); //don't need wrappers since skin can't change while this dialog is open
private final FComboBox<Integer> yearDropdown = add(new FComboBox<>());
private boolean showOptions;
private final Deck currentDeck;
private final boolean showOptions;
private final boolean currentDeckIsEmpty;
private boolean createNewDeckControl;
private final DeckImportController controller;
private final FDeckEditor.DeckEditorConfig editorConfig;
private final static ImmutableList<String> importOrCancel = ImmutableList.of(Forge.getLocalizer().getMessage("lblImport"), Forge.getLocalizer().getMessage("lblCancel"));
public FDeckImportDialog(final Deck currentDeck, final FDeckEditor.DeckEditorConfig editorConfig) {
public FDeckImportDialog(final boolean replacingDeck, final FDeckEditor.DeckEditorConfig editorConfig) {
super(Forge.getLocalizer().getMessage("lblImportFromClipboard"), 2);
boolean usingInventory = editorConfig.usePlayerInventory();
boolean replacingDeck = !currentDeck.isEmpty() || usingInventory;
this.currentDeck = currentDeck;
this.editorConfig = editorConfig;
ItemPool<PaperCard> cardPool = editorConfig.getCardPool(false);
controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, replacingDeck);
String contents = Forge.getClipboard().getContents();
if (contents == null)
contents = ""; //prevent NPE
txtInput.setText(contents);
GameType gameType = editorConfig.getGameType();
controller.setGameFormat(gameType);
List<DeckSection> supportedSections = new ArrayList<>();
supportedSections.addAll(List.of(editorConfig.getPrimarySections()));
supportedSections.addAll(List.of(editorConfig.getExtraSections()));
controller.setAllowedSections(supportedSections);
controller.setCurrentDeckInEditor(currentDeck);
if(usingInventory)
controller.setPlayerInventory(cardPool);
if (editorConfig.allowsCardReplacement()) {
GameType gameType = editorConfig.getGameType();
controller.setGameFormat(gameType);
List<DeckSection> supportedSections = new ArrayList<>();
supportedSections.add(DeckSection.Main);
supportedSections.add(DeckSection.Sideboard);
if (editorConfig.hasCommander())
supportedSections.add(DeckSection.Commander);
supportedSections.addAll(Lists.newArrayList(editorConfig.getExtraSections()));
controller.setAllowedSections(supportedSections);
}
onlyCoreExpCheck.setSelected(StaticData.instance().isCoreExpansionOnlyFilterSet());
newEditionCheck.setSelected(StaticData.instance().cardArtPreferenceIsLatest());
smartCardArtCheck.setSelected(StaticData.instance().isEnabledCardArtSmartSelection());
createNewDeckCheck.setSelected(replacingDeck);
this.currentDeckIsEmpty = !replacingDeck;
this.createNewDeckControl = replacingDeck;
if(usingInventory)
controller.setImportBehavior(DeckImportController.ImportBehavior.REPLACE_CURRENT);
else
controller.setImportBehavior(createNewDeckControl ? DeckImportController.ImportBehavior.CREATE_NEW : DeckImportController.ImportBehavior.MERGE);
initButton(0, Forge.getLocalizer().getMessage("lblImport"), e -> FThreads.invokeInBackgroundThread(() -> {
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText()); //ensure deck updated based on any changes to options
initButton(0, Forge.getLocalizer().getMessage("lblImport"), e -> FThreads.invokeInBackgroundThread(this::performImport));
if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//if there are any cards that cannot be imported, let user know this and give them the option to cancel
StringBuilder sb = new StringBuilder();
for (DeckRecognizer.Token token : tokens) {
if (token.getType() == TokenType.CARD_FROM_NOT_ALLOWED_SET
|| token.getType() == TokenType.CARD_FROM_INVALID_SET
|| token.getType() == TokenType.UNKNOWN_CARD
|| token.getType() == TokenType.UNSUPPORTED_CARD) {
if (sb.length() > 0)
sb.append("\n");
sb.append(token.getQuantity()).append(" ").append(token.getText());
}
}
if (sb.length() > 0) {
if (SOptionPane.showOptionDialog(Forge.getLocalizer().getMessage("lblFollowingCardsCannotBeImported") + "\n\n" + sb, Forge.getLocalizer().getMessage("lblImportRemainingCards"), SOptionPane.INFORMATION_ICON, importOrCancel) == 1) {
return;
}
}
final Deck deck = controller.accept(); //must accept in background thread in case a dialog is shown
if (deck == null) { return; }
FThreads.invokeInEdtLater(() -> {
hide();
if (callback != null)
callback.accept(deck);
});
}));
initButton(1, Forge.getLocalizer().getMessage("lblCancel"), e -> hide());
dateTimeCheck.setCommand(e -> updateDropDownEnabled());
newEditionCheck.setCommand(e -> setArtPreferenceInController());
onlyCoreExpCheck.setCommand(e -> setArtPreferenceInController());
smartCardArtCheck.setCommand(e -> controller.setSmartCardArtOptimisation(smartCardArtCheck.isSelected()));
createNewDeckCheck.setCommand(e -> {
createNewDeckControl = createNewDeckCheck.isSelected();
controller.setImportBehavior(createNewDeckControl ? DeckImportController.ImportBehavior.CREATE_NEW : DeckImportController.ImportBehavior.MERGE);
});
setShowOptions(false);
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText());
if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//ensure at least one known card found on clipboard
for (DeckRecognizer.Token token : tokens) {
if (token.getType() == TokenType.LEGAL_CARD) {
showOptions = true;
dateTimeCheck.setCommand(e -> updateDropDownEnabled());
newEditionCheck.setCommand(e -> setArtPreferenceInController());
onlyCoreExpCheck.setCommand(e -> setArtPreferenceInController());
smartCardArtCheck.setCommand(e -> controller.setSmartCardArtOptimisation(smartCardArtCheck.isSelected()));
createNewDeckCheck.setCommand(e -> {
createNewDeckControl = createNewDeckCheck.isSelected();
controller.setCreateNewDeck(createNewDeckControl);
});
updateDropDownEnabled();
setArtPreferenceInController();
return;
}
}
showOptions = false;
setButtonEnabled(0, false);
txtInput.setText(Forge.getLocalizer().getMessage("lblNoKnownCardsOnClipboard"));
}
private void setArtPreferenceInController() {
@@ -125,66 +160,16 @@ public class FDeckImportDialog extends FDialog {
}
private void updateDropDownEnabled() {
boolean enabled = dateTimeCheck.isSelected() && this.showOptions;
boolean enabled = dateTimeCheck.isSelected();
monthDropdown.setEnabled(enabled);
yearDropdown.setEnabled(enabled);
}
private void setShowOptions(boolean showOptions) {
this.showOptions = showOptions;
dateTimeCheck.setEnabled(showOptions);
newEditionCheck.setEnabled(showOptions);
onlyCoreExpCheck.setEnabled(showOptions);
newEditionCheck.setEnabled(showOptions);
smartCardArtCheck.setEnabled(showOptions);
createNewDeckCheck.setEnabled(showOptions);
updateDropDownEnabled();
}
public void setCallback(Consumer<Deck> callback0){
callback = callback0;
}
public void setFreePrintConverter(Function<PaperCard, PaperCard> freePrintConverter) {
this.controller.setFreePrintConverter(freePrintConverter);
}
public DeckImportController.ImportBehavior getImportBehavior() {
return controller.getImportBehavior();
}
public void setImportBannedCards(boolean importBannedCards) {
controller.importBannedAndRestrictedCards(importBannedCards);
}
public void initParse() {
boolean usingInventory = editorConfig.usePlayerInventory();
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText());
if (usingInventory)
tokens = controller.constrainTokensToInventory();
else if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//ensure at least one known card found on clipboard
for (DeckRecognizer.Token token : tokens) {
if (token.getType() == TokenType.LEGAL_CARD || token.getType() == TokenType.FREE_CARD_NOT_IN_INVENTORY) {
if(usingInventory) {
//Settings aren't compatible with player inventories.
setShowOptions(false);
return;
}
setShowOptions(true);
updateDropDownEnabled();
setArtPreferenceInController();
return;
}
}
setButtonEnabled(0, false);
txtInput.setText(Forge.getLocalizer().getMessage("lblNoKnownCardsOnClipboard"));
}
public boolean createNewDeck(){ return this.createNewDeckControl; }
@Override
public void drawOverlay(Graphics g) {
@@ -217,7 +202,7 @@ public class FDeckImportDialog extends FDialog {
yearDropdown.setBounds(x + dropDownWidth + fieldPadding, y, dropDownWidth, h);
y += h + fieldPadding;
if (!this.currentDeck.isEmpty()){
if (!this.currentDeckIsEmpty){
smartCardArtCheck.setBounds(x, y, w/2, h);
createNewDeckCheck.setBounds(x + w/2, y, w/2, h);
} else
@@ -237,50 +222,4 @@ public class FDeckImportDialog extends FDialog {
}
return y;
}
private static final EnumSet<TokenType> MISSING_TOKENS = EnumSet.of(TokenType.CARD_FROM_NOT_ALLOWED_SET,
TokenType.CARD_FROM_INVALID_SET, TokenType.UNKNOWN_CARD, TokenType.UNSUPPORTED_CARD,
TokenType.WARNING_MESSAGE, TokenType.CARD_NOT_IN_INVENTORY);
private void performImport() {
List<DeckRecognizer.Token> tokens = controller.parseInput(txtInput.getText()); //ensure deck updated based on any changes to options
if (editorConfig.usePlayerInventory())
tokens = controller.constrainTokensToInventory();
else if (controller.isSmartCardArtEnabled())
tokens = controller.optimiseCardArtInTokens();
//if there are any cards that cannot be imported, let user know this and give them the option to cancel
// Android API StringBuilder isEmpty() is unavailable. https://developer.android.com/reference/java/lang/StringBuilder
StringBuilder sb = new StringBuilder();
for (DeckRecognizer.Token token : tokens) {
if (MISSING_TOKENS.contains(token.getType())) {
if (sb.length() != 0)
sb.append("\n");
String message = controller.getTokenMessage(token);
String statusMessage = controller.getTokenStatusMessage(token);
if(!StringUtils.isBlank(statusMessage))
sb.append(String.format("%s - (%s)", message, statusMessage));
else
sb.append(statusMessage);
}
}
if (sb.length() != 0) {
Localizer localizer = Forge.getLocalizer();
if (SOptionPane.showOptionDialog(localizer.getMessage("lblFollowingCardsCannotBeImported") + "\n\n" + sb, localizer.getMessage("lblImportRemainingCards"), SOptionPane.WARNING_ICON, importOrCancel) == 1) {
return;
}
}
final Deck deck = controller.accept(currentDeck.getName()); //must accept in background thread in case a dialog is shown
if (deck == null) {
return;
}
FThreads.invokeInEdtLater(() -> {
hide();
if (callback != null)
callback.accept(deck);
});
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

View File

@@ -1,10 +0,0 @@
Name:Emrakul's Presence
ManaCost:no cost
Types:Artifact
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Command | CheckSVar$ X | SVarCompare$ LT8 | Execute$ TrigDraw | TriggerDescription$ At the beginning of your end step, if you have fewer than eight cards in hand, draw cards equal to the difference.
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ Difference
SVar:X:Count$ValidHand Card.YouOwn
SVar:Difference:Number$8/Minus.X
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | Execute$ TrigWish | TriggerDescription$ At the beginning of your upkeep, put a random permanent card from your graveyard or exile onto the battlefield.
SVar:TrigWish:DB$ ChangeZone | Origin$ Graveyard,Exile | Destination$ Battlefield | ChangeType$ Permanent.YouCtrl | Hidden$ True | AtRandom$ True
Oracle:At the beginning of your upkeep, put a random permanent card from your graveyard or exile onto the battlefield.\nAt the beginning of your end step, if you have fewer than eight cards in hand, draw cards equal to the difference.

View File

@@ -1,25 +1,19 @@
[metadata]
Name=Emrakul
[Main]
1 Black Lotus
2 Vesuva
4 Black Lotus
4 Crystal Vein
4 Eldrazi Temple
3 Forsaken Monument
2 It That Betrays
2 Pathrazer of Ulamog
2 Void Winnower
2 Emrakul, the Aeons Torn
2 Emrakul, the Promised End
3 Emrakul, the Aeons Torn
3 Emrakul, the Promised End
4 Expedition Map
2 Eye of Ugin
4 Ancient Tomb
1 Strip Mine
4 All is Dust
4 Mana Vault
2 Kozilek, Butcher of Truth
2 Kozilek, the Great Distortion
1 Mana Crypt
1 Sol Ring
3 Titan's Presence
4 Mana Crypt
4 Sol Ring
4 Titan's Presence
2 Ulamog, the Ceaseless Hunger
2 Ulamog, the Infinite Gyre
4 Urza's Mine

View File

@@ -3,10 +3,6 @@
<editorsettings>
<export target="wastetown..tmx" format="tmx"/>
</editorsettings>
<properties>
<property name="dungeonEffect">{&quot;startBattleWithCardInCommandZone&quot;: [ &quot;Emrakul's Presence&quot; ]
}</property>
</properties>
<tileset firstgid="1" source="../../tileset/main.tsx"/>
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
<layer id="1" name="Background" width="30" height="30">

View File

@@ -144,4 +144,3 @@ Tarkir Dragonstorm, 3/6/TDM, TDM
Final Fantasy, 3/6/FIN, FIN
Alchemy: Innistrad, 3/6/ISD, YMID
Edge of Eternities, 3/6/EOE, EOE
Marvel's Spider-Man, 3/6/SPM, SPM

View File

@@ -2,7 +2,7 @@ Name:Coal Hill School
ManaCost:no cost
Types:Plane Earth
T:Mode$ SpellCast | ValidCard$ Card.Historic | Execute$ TrigDraw | TriggerZones$ Command | TriggerDescription$ Whenever a player casts a historic spell, that player draws a card. (Artifacts, legendaries, and Sagas are historic.)
SVar:TrigDraw:DB$ Draw | Defined$ TriggeredActivator | NumCards$ 1
SVar:TrigDraw:DB$ Draw | Defined$ TriggeredPlayer | NumCards$ 1
T:Mode$ ChaosEnsues | TriggerZones$ Command | Execute$ TrigReturn | TriggerDescription$ Whenever chaos ensues, return target historic card from your graveyard to your hand.
SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Card.Historic+YouOwn | TgtPrompt$ Select target historic card
DeckHints:Type$Artifact|Saga|Legendary

View File

@@ -2,6 +2,6 @@ Name:Phyrexian Reaper
ManaCost:4 B
Types:Creature Phyrexian Zombie
PT:3/3
T:Mode$ AttackerBlockedByCreature | ValidCard$ Card.Self | ValidBlocker$ Creature.Green | Execute$ TrigDestroyBlocker | TriggerDescription$ Whenever CARDNAME becomes blocked by a green creature, destroy that creature. It can't be regenerated.
T:Mode$ AttackerBlocked | ValidCard$ Card.Self | ValidBlocker$ Creature.Green | Execute$ TrigDestroyBlocker | TriggerDescription$ Whenever CARDNAME becomes blocked by a green creature, destroy that creature. It can't be regenerated.
SVar:TrigDestroyBlocker:DB$ Destroy | Defined$ TriggeredBlockerLKICopy | NoRegen$ True
Oracle:Whenever Phyrexian Reaper becomes blocked by a green creature, destroy that creature. It can't be regenerated.

View File

@@ -3,7 +3,7 @@ ManaCost:3 B
Types:Creature Phyrexian Minion
PT:2/2
K:Flying
T:Mode$ AttackerBlockedByCreature | ValidCard$ Card.Self | ValidBlocker$ Creature.White | Execute$ TrigDestroyBlocker | TriggerDescription$ Whenever CARDNAME becomes blocked by a white creature, destroy that creature. It can't be regenerated.
T:Mode$ AttackerBlocked | ValidCard$ Card.Self | ValidBlocker$ Creature.White | Execute$ TrigDestroyBlocker | TriggerDescription$ Whenever CARDNAME becomes blocked by a white creature, destroy that creature. It can't be regenerated.
SVar:TrigDestroyBlocker:DB$ Destroy | Defined$ TriggeredBlockerLKICopy | NoRegen$ True
AI:RemoveDeck:Random
Oracle:Flying\nWhenever Phyrexian Slayer becomes blocked by a white creature, destroy that creature. It can't be regenerated.

View File

@@ -2,7 +2,7 @@ Name:Recruit Instructor
ManaCost:R W
Types:Creature Mouse Warrior
PT:1/1
T:Mode$ AttackersDeclared | ValidAttackers$ Card.Self | Execute$ TrigDraft | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature attacks, draft a card from CARDNAME's spellbook.
T:Mode$ AttackersDeclared | ValidAttackers$ Self | Execute$ TrigDraft | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature attacks, draft a card from CARDNAME's spellbook.
SVar:TrigDraft:DB$ Draft | Spellbook$ Angelfire Ignition,Barge In,Become Brutes,Boon of Safety,Cheeky House-Mouse,Crumb and Get It,Defiant Strike,Embercleave,Feather of Flight,Mabel's Mettle,Might of the Meek,Moment of Heroism,Unleash Fury,War Squeak
T:Mode$ BecomesTarget | ValidTarget$ Card.Self | ValidSource$ SpellAbility.YouCtrl | TriggerZones$ Battlefield | FirstTime$ True | Execute$ TrigToken | TriggerDescription$ Valiant — Whenever CARDNAME becomes the target of a spell or ability you control for the first time each turn, create a 1/1 white Mouse creature token.
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_mouse | TokenAmount$ 1 | TokenOwner$ You

View File

@@ -3,7 +3,7 @@ ManaCost:2 U
Types:Creature Human Wizard
PT:3/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters, look at the top three cards of your library. You may put one of those cards back on top of your library. Put the rest into your graveyard.
SVar:TrigDig:DB$ Dig | DigNum$ 3 | ChangeNum$ 1 | ChangeValid$ Card | DestinationZone2$ Graveyard | DestinationZone$ Library | LibraryPosition$ 0 | Optional$ True | NoReveal$ True
SVar:TrigDig:DB$ Dig | DigNum$ 3 | ChangeNum$ 1 | ChangeValid$ Card | DestinationZone2$ Graveyard | DestinationZone$ Library | LibraryPosition$ 0 | Optional$ True
DeckHas:Ability$Graveyard
DeckHints:Ability$Graveyard
Oracle:When Sage of Days enters, look at the top three cards of your library. You may put one of those cards back on top of your library. Put the rest into your graveyard.

View File

@@ -2,8 +2,8 @@ Name:The Reaver Cleaver
ManaCost:2 R
Types:Legendary Artifact Equipment
K:Equip:3
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Trample | AddTrigger$ TrigDamageDone | Description$ Equipped creature gets +1/+1 and has trample and "Whenever this creature deals combat damage to a player or planeswalker, create that many Treasure tokens."
SVar:TrigDamageDone:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player,Planeswalker | CombatDamage$ True | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature deals combat damage to a player or planeswalker, create that many Treasure tokens.
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Trample | Description$ Equipped creature gets +1/+1 and has trample.
T:Mode$ DamageDone | ValidSource$ Creature.EquippedBy | ValidTarget$ Player,Planeswalker | CombatDamage$ True | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature deals combat damage to a player or planeswalker, create that many Treasure tokens.
SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ c_a_treasure_sac | TokenOwner$ You
SVar:X:TriggerCount$DamageAmount
DeckHas:Ability$Token & Type$Treasure|Artifact

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